Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add avatars functionality #351

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ desc 'test => spec'
task test: [:spec]

# Useful for hooking up with SublimeText.
# e.g. rake sample[basic.rb]
# e.g. rake 'run[autoscale_font/_autoscale_font]'
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! I tend not to use this anymore.

desc 'Run a specific sample'
task :run, [:file] => :install do |t, args|
args.with_defaults(file: 'basic.rb')
Expand Down
113 changes: 113 additions & 0 deletions docs/dsl/avatar.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
avatar
======

Renders an avatar SVG image using using https://avatars.dicebear.com/. Uses the SVG-specified units and DPI to determine the pixel width and height. If neither data nor file are specified for a given card, this method does nothing.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to document that it'll save a file and only download if it's not saved already


Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also probably say that beyond the first few options, it's identical to svg.


Options
-------
.. include:: /args/expansion.rst

library
default: ``'avataaars'``

avatar library to use. Options are ``'male'``, ``'female'``, ``'human'``, ``'identicon'``, ``'initials'``, ``'bottts'``, ``'avataaars'``, ``'jdenticon'``, ``'gridy'`` or ``'micah'``.

.. include:: /args/xy.rst

seed
default: ``nil``

string to use as the randomizer for the avatar library. Or, in the case of ``'initials'``, the actual initials shown in the returned image.

width
default: ``native``
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
default: ``native``
default: ``:native``


the pixel width that the image should scale to. Setting this to ``:deck`` will scale to the deck height. ``:scale`` will use the width to scale and keep native the aspect ratio. SVG scaling is done with vectors, so the scaling should be smooth. When set to ``:native``, uses the DPI and units of the loaded SVG document. Supports :doc:`/units` and :doc:`/shorthands`.

height
default: ``:native``

the pixel width that the image should scale to. ``:deck`` will scale to the deck height. ``:scale`` will use the width to scale and keep native the aspect ratio. SVG scaling is done with vectors, so the scaling should be smooth. When set to ``:native``, uses the DPI and units of the loaded SVG document. Supports :doc:`/units` and :doc:`/shorthands`.

blend
default: ``:none``

the composite blend operator used when applying this image. See Blend Modes at http://cairographics.org/operators.
The possibilties include ``:none``, ``:multiply``, ``:screen``, ``:overlay``, ``:darken``, ``:lighten``, ``:color_dodge``, ``:color_burn``, ``:hard_light``, ``:soft_light``, ``:difference``, ``:exclusion``, ``:hsl_hue``, ``:hsl_saturation``, ``:hsl_color``, ``:hsl_luminosity``. String versions of these options are accepted too.

angle
default: ``0``

rotation of the image in radians. Note that this rotates around the upper-left corner, making the placement of x-y coordinates slightly tricky.

mask
default: ``nil``

if specified, the image will be used as a mask for the given color/gradient. Transparent pixels are ignored, opaque pixels are the given color. Note: the origin for gradient coordinates is at the given x,y, not at 0,0 as it is most other places.

.. warning::

For implementation reasons, your vector image will be rasterized when mask is applied. If you use this with, say, PDF, the images will be embedded as rasters, not vectors.

crop_x
default: ``0``

crop the loaded image at this x coordinate. Supports :doc:`/units`

crop_y
default: ``0``

crop the loaded image at this y coordinate. Supports :doc:`/units`

crop_corner_radius
default: ``0``

Radius for rounded corners, both x and y. When set, overrides crop_corner_x_radius and crop_corner_y_radius. Supports :doc:`/units`

crop_corner_x_radius
default: ``0``

x radius for rounded corners of cropped image. Supports :doc:`/units`

crop_corner_y_radius
default: ``0``

y radius for rounded corners of cropped image. Supports :doc:`/units`

crop_width
default: ``0``

width of the cropped image. Supports :doc:`/units`

crop_height
default: ``0``

ive): Height of the cropped image. Supports :doc:`/units`

flip_horizontal
default: ``false``

Flip this image about its center horizontally (i.e. left becomes right and vice versa).

flip_vertical
default: ``false``

Flip this image about its center verticall (i.e. top becomes bottom and vice versa).

.. include:: /args/range.rst
.. include:: /args/layout.rst


Examples
--------

These examples live here: https://github.com/andymeneely/squib/tree/dev/samples/avatars

.. literalinclude:: ../../samples/avatars/_avatars.rb
:linenos:

.. raw:: html

<img src="../images/_avatars_00_expected.png" width=600 class="figure">

4 changes: 2 additions & 2 deletions docs/dsl/svg.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,12 @@ placeholder
crop_x
default: ``0``

rop the loaded image at this x coordinate. Supports :doc:`/units`
crop the loaded image at this x coordinate. Supports :doc:`/units`

crop_y
default: ``0``

rop the loaded image at this y coordinate. Supports :doc:`/units`
crop the loaded image at this y coordinate. Supports :doc:`/units`

crop_corner_radius
default: ``0``
Expand Down
1 change: 1 addition & 0 deletions lib/squib/deck.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ def show_info(config, layout)
###################
### DSL METHODS ###
###################
require_relative 'dsl/avatar'
require_relative 'dsl/background'
require_relative 'dsl/circle'
require_relative 'dsl/csv'
Expand Down
92 changes: 92 additions & 0 deletions lib/squib/dsl/avatar.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
require 'mechanize'
require_relative '../errors_warnings/warn_unexpected_params'
require_relative '../args/card_range'
require_relative '../args/paint'
require_relative '../args/scale_box'
require_relative '../args/transform'
require_relative '../args/svg_special'

module Squib
class Deck
def avatar(opts = {})
DSL::Avatar.new(self, __callee__).run(opts)
end
end

module DSL
# Add an avatar for placeholder art in your games
# using https://avatars.dicebear.com/. The image will
# be downloaded to your configured image directory if
# it doesn't already exist.
#
# Library can be male, female, human, identicon, initials,
# bottts, avataaars, jdenticon, gridy or micah.
#
# Seed can be any random string
#
# Example:
# avatar library: 'micah', seed: '1234'
class Avatar
include WarnUnexpectedParams
attr_reader :dsl_method, :deck

def initialize(deck, dsl_method)
@deck = deck
@dsl_method = dsl_method
end

def self.accepted_params
%i[
library seed
x y width height
blend mask
crop_x crop_y crop_width crop_height
crop_corner_radius crop_corner_x_radius crop_corner_y_radius
flip_horizontal flip_vertical angle
range layout
]
end

def run(opts)
warn_if_unexpected opts
Dir.chdir(deck.img_dir) do
defaults = { library: 'avataaars' }
Copy link
Owner

@andymeneely andymeneely Nov 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
defaults = { library: 'avataaars' }
defaults = { library: 'avataaars', seed: 'squibrocks' }

I like to have everything have a default so things work even if you forget something. This seemed most sensible

options = defaults.merge opts

# Extract the default svg options
range = Args.extract_range opts, deck
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think as it currently stands library and seed won't expand per-card. I'd be fine with a new args class args/avatar_special.rb or args/dicebear.rb or something that has library and seed.

It also allows us to specify all these: https://avatars.dicebear.com/docs/options.

svg_args = Args.extract_svg_special opts, deck
paint = Args.extract_paint opts, deck
box = Args.extract_scale_box opts, deck
trans = Args.extract_transform opts, deck

deck.progress_bar.start('Loading Avatar(s)', range.size) do |bar|
range.each do |i|
library = options[:library]
seed = options[:seed]
next if seed.nil?

file = "avatar-#{library}-#{seed}.svg"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should give people control over this, similar to prefix and suffix. Use the % on strings to format and make that a parameter (https://github.com/andymeneely/squib/blob/dev/lib/squib/args/save_batch.rb#L56-L58).

Something like an option called filename that defaults to `"avatar-%s-%s.svg". Like this:

irb(main):001:0> "avatar-%s-%s.svg" % ['a','b']
=> "avatar-a-b.svg"

We could also document a sample that allows you to put the avatar it is own folder:

irb(main):002:0> "avatars/%s-%s.svg" % ['a','b']
=> "avatars/a-b.svg"


# Check if we need to download the image
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like this chunk to be extracted out to a separate file - that way this class is only doing DSL method delegation stuff and we can test the dicebear thing separately. Maybe in a file like lib/squib/avatar_downloader.rb or something.

unless File.exist?(file)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should wrap this in a begin/rescue so we can report a more helpful error.

agent = Mechanize.new
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might also be good to do a log statement whenever we download. It'll be verbose the first time, but at least people would know when it's downloading and not.

agent.follow_meta_refresh = true
agent.keep_alive = false
agent.history.max_size = 10

response = agent.get_file("https://avatars.dicebear.com/api/#{library}/#{seed}.svg")
response = response.encode('ascii-8bit').force_encoding('utf-8')

File.open(file, 'w') { |f| f.write(response) }
end

deck.cards[i].svg(file, svg_args[i], box[i], paint[i], trans[i])
bar.increment
end
end
end
end
end
end
end
2 changes: 2 additions & 0 deletions samples/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
avatar-*.svg
avatars/*.svg
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this second line? Would be nice if we had some samples that had already downloaded an avatar, that would be nice for regression testing reasons.

58 changes: 58 additions & 0 deletions samples/avatars/_avatars.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
require 'squib'
require 'squib/sample_helpers'

Squib::Deck.new(width: 1000, height: 2050, config: 'avatar_config.yml') do
draw_graph_paper width, height

sample "Avatar library - 'male'." do |x, y|
avatar library: 'male', seed: 'abcde', x: x, y: y, width: 96, height: 96
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add an example showing different avatars on different cards?

avatar library: 'male', seed: 'fghij', x: x + 125, y: y, width: 96, height: 96
end

sample "Avatar library - 'female'." do |x, y|
avatar library: 'female', seed: 'abcde', x: x, y: y, width: 96, height: 96
avatar library: 'female', seed: 'fghij', x: x + 125, y: y, width: 96, height: 96
end

sample "Avatar library - 'human'." do |x, y|
avatar library: 'human', seed: 'abcde', x: x, y: y, width: 96, height: 96
avatar library: 'human', seed: 'fghij', x: x + 125, y: y, width: 96, height: 96
end

sample "Avatar library - 'identicon'." do |x, y|
avatar library: 'identicon', seed: 'abcde', x: x, y: y, width: 96, height: 96
avatar library: 'identicon', seed: 'fghij', x: x + 125, y: y, width: 96, height: 96
end

sample "Avatar library - 'initials'." do |x, y|
avatar library: 'initials', seed: 'Longer Name', x: x, y: y, width: 96, height: 96
avatar library: 'initials', seed: 'RN', x: x + 125, y: y, width: 96, height: 96
end

sample "Avatar library - 'bottts'." do |x, y|
avatar library: 'bottts', seed: 'abcde', x: x, y: y, width: 96, height: 96
avatar library: 'bottts', seed: 'fghij', x: x + 125, y: y, width: 96, height: 96
end

sample "Avatar library - 'avataaars'." do |x, y|
avatar library: 'avataaars', seed: 'abcde', x: x, y: y, width: 96, height: 96
avatar library: 'avataaars', seed: 'fghij', x: x + 125, y: y, width: 96, height: 96
end

sample "Avatar library - 'jdenticon'." do |x, y|
avatar library: 'jdenticon', seed: 'abcde', x: x, y: y, width: 96, height: 96
avatar library: 'jdenticon', seed: 'fghij', x: x + 125, y: y, width: 96, height: 96
end

sample "Avatar library - 'gridy'." do |x, y|
avatar library: 'gridy', seed: 'abcde', x: x, y: y, width: 96, height: 96
avatar library: 'gridy', seed: 'fghij', x: x + 125, y: y, width: 96, height: 96
end

sample "Avatar library - 'micah'." do |x, y|
avatar library: 'micah', seed: 'abcde', x: x, y: y, width: 96, height: 96
avatar library: 'micah', seed: 'fghij', x: x + 125, y: y, width: 96, height: 96
end

save_png prefix: '_avatars_'
end
Binary file added samples/avatars/_avatars_00_expected.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions samples/avatars/avatar_config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
img_dir: '.'
1 change: 1 addition & 0 deletions squib.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Gem::Specification.new do |spec|
spec.add_runtime_dependency 'gio2', '~> 3.4'
spec.add_runtime_dependency 'gobject-introspection', '~> 3.4'
spec.add_runtime_dependency 'highline', '2.0.3'
spec.add_runtime_dependency 'mechanize', '~> 2.7'
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mechanize might be overkill for us - I think we can download a file with Ruby's stdlib?

spec.add_runtime_dependency 'mercenary', '0.4.0'
spec.add_runtime_dependency 'nokogiri', '~> 1.11'
spec.add_runtime_dependency 'pango', '~> 3.4'
Expand Down