pixelart 1.1.0 → 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb520f7fc94b0b7d62336829d92929bbeb27600f0042ab3f68d81b6e5b26d2b3
4
- data.tar.gz: ed4756f69b5f5d5ca696a1105aa348b5b86ab33653ff7c34d1b24f4dc75e244f
3
+ metadata.gz: 6be97cd0f30e95965b6ca21d69142283d1e2869ba99e37b84245b5d95c47fda0
4
+ data.tar.gz: 2bc68cf7a70d718e2d96b92f4401f44074b01282cb5ede85d548b36edac7d3d6
5
5
  SHA512:
6
- metadata.gz: caa3d9eeb12a4a59e4f6ec3191e432ae11d7cf44465e0b6722c69bda985af6caeea3a4390f773d4d535a38096f483c3d9467b03f63ed6547a168f79361db1991
7
- data.tar.gz: d7f6d039eddc337b62ca22b338712a26423781b2a57703429469d376c02af0fef6aadd9182f4fb2cb755dc83f27c6bcbb97519ee528c01169ba8070534c7c377
6
+ metadata.gz: c329166bd2144e1e440d01447c9babc6d999de0aed2d9eb3dedaa521b016b829f0039d8fdf5475afeffbc46f2f75f1efaca1a95f73a3c3e968d653041ff4656a
7
+ data.tar.gz: 44ab750a9bdec98770ff6ed325b8fac5883e693ae3356b08012184bdf72a20c1c88a023fb883dc437311df043d6a2d23012f54309d70dfa8e3402deb5683e945
data/Manifest.txt CHANGED
@@ -13,6 +13,9 @@ lib/pixelart/led.rb
13
13
  lib/pixelart/misc.rb
14
14
  lib/pixelart/palette.rb
15
15
  lib/pixelart/pixelator.rb
16
+ lib/pixelart/silhouette.rb
16
17
  lib/pixelart/sketch.rb
17
18
  lib/pixelart/spots.rb
19
+ lib/pixelart/transparent.rb
20
+ lib/pixelart/vector.rb
18
21
  lib/pixelart/version.rb
data/README.md CHANGED
@@ -3,8 +3,8 @@
3
3
  pixelart - mint your own pixel art images off chain using any design (in ascii text) in any colors; incl. 2x/4x/8x zoom for bigger sizes
4
4
 
5
5
 
6
- * home :: [github.com/rubycoco/pixel](https://github.com/rubycoco/pixel)
7
- * bugs :: [github.com/rubycoco/pixel/issues](https://github.com/rubycoco/pixel/issues)
6
+ * home :: [github.com/pixelartexchange/pixel](https://github.com/pixelartexchange/pixel)
7
+ * bugs :: [github.com/pixelartexchange/pixel/issues](https://github.com/pixelartexchange/pixel/issues)
8
8
  * gem :: [rubygems.org/gems/pixelart](https://rubygems.org/gems/pixelart)
9
9
  * rdoc :: [rubydoc.info/gems/pixelart](http://rubydoc.info/gems/pixelart)
10
10
 
@@ -71,8 +71,8 @@ img3x.save( './i/mooncat_white-3x.png' )
71
71
 
72
72
  Voila!
73
73
 
74
- ![](https://github.com/rubycoco/pixel/raw/master/pixelart/i/mooncat_white.png)
75
- ![](https://github.com/rubycoco/pixel/raw/master/pixelart/i/mooncat_white-3x.png)
74
+ ![](https://github.com/pixelartexchange/pixel/raw/master/pixelart/i/mooncat_white.png)
75
+ ![](https://github.com/pixelartexchange/pixel/raw/master/pixelart/i/mooncat_white-3x.png)
76
76
 
77
77
 
78
78
 
@@ -100,8 +100,8 @@ img3x.save( './i/mooncat_black-3x.png' )
100
100
 
101
101
  Voila! Black is the new White!
102
102
 
103
- ![](https://github.com/rubycoco/pixel/raw/master/pixelart/i/mooncat_black.png)
104
- ![](https://github.com/rubycoco/pixel/raw/master/pixelart/i/mooncat_black-3x.png)
103
+ ![](https://github.com/pixelartexchange/pixel/raw/master/pixelart/i/mooncat_black.png)
104
+ ![](https://github.com/pixelartexchange/pixel/raw/master/pixelart/i/mooncat_black-3x.png)
105
105
 
106
106
 
107
107
 
@@ -227,8 +227,8 @@ img5x.save( './i/vader5x.png' )
227
227
 
228
228
  Voila!
229
229
 
230
- ![](https://github.com/rubycoco/pixel/raw/master/pixelart/i/vader.png)
231
- ![](https://github.com/rubycoco/pixel/raw/master/pixelart/i/vader5x.png)
230
+ ![](https://github.com/pixelartexchange/pixel/raw/master/pixelart/i/vader.png)
231
+ ![](https://github.com/pixelartexchange/pixel/raw/master/pixelart/i/vader5x.png)
232
232
 
233
233
 
234
234
 
@@ -288,4 +288,3 @@ Just install the gem:
288
288
 
289
289
  The scripts are dedicated to the public domain.
290
290
  Use it as you please with no restrictions whatsoever.
291
-
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ Hoe.spec 'pixelart' do
8
8
  self.summary = "pixelart - mint your own pixel art images off chain using any design (in ascii text) in any colors; incl. 2x/4x/8x zoom for bigger sizes"
9
9
  self.description = summary
10
10
 
11
- self.urls = { home: 'https://github.com/rubycoco/pixel' }
11
+ self.urls = { home: 'https://github.com/pixelartexchange/pixel' }
12
12
 
13
13
  self.author = 'Gerald Bauer'
14
14
  self.email = 'wwwmake@googlegroups.com'
@@ -20,6 +20,7 @@ Hoe.spec 'pixelart' do
20
20
  self.extra_deps = [
21
21
  ['chunky_png'],
22
22
  ['mini_magick'],
23
+ ['csvreader'],
23
24
  ]
24
25
 
25
26
  self.licenses = ['Public Domain']
data/lib/pixelart/base.rb CHANGED
@@ -7,6 +7,9 @@ require 'chunky_png'
7
7
  require 'mini_magick'
8
8
 
9
9
 
10
+ # bonus / prologue / convenience 3rd party
11
+ require 'csvreader'
12
+
10
13
 
11
14
  ## stdlib
12
15
  require 'pp'
@@ -28,14 +31,18 @@ require 'pixelart/palette'
28
31
  require 'pixelart/image'
29
32
  require 'pixelart/composite'
30
33
 
34
+
31
35
  require 'pixelart/pixelator'
32
36
 
33
37
  require 'pixelart/misc' ## misc helpers
34
38
 
35
39
  #########################
36
- # (special) effects / filters
40
+ # (special) effects / filters / etc
37
41
  require 'pixelart/led'
38
42
  require 'pixelart/sketch'
43
+ require 'pixelart/transparent'
44
+ require 'pixelart/silhouette'
45
+
39
46
 
40
47
  ## (special) effects / filters that require imagemagick
41
48
 
@@ -47,6 +54,9 @@ module Pixelart
47
54
  MAGICK_OUTPUT = './tmp/magick_output.png'
48
55
  end
49
56
 
57
+ require 'pixelart/vector' ## vector graphics helpers
58
+
59
+
50
60
  require 'pixelart/spots'
51
61
  require 'pixelart/blur'
52
62
 
@@ -7,10 +7,31 @@ class ImageComposite < Image # check: (re)name to Collage, Sheet, Sprites, or s
7
7
  TILE_HEIGHT = 24
8
8
 
9
9
 
10
+ def self.read( path, width: TILE_WIDTH, height: TILE_WIDTH ) ## convenience helper
11
+ img = ChunkyPNG::Image.from_file( path )
12
+ new( img, width: width,
13
+ height: width )
14
+ end
15
+
16
+
10
17
  def initialize( *args, **kwargs )
11
18
  @tile_width = kwargs[:width] || kwargs[:tile_width] || TILE_WIDTH
12
19
  @tile_height = kwargs[:height] || kwargs[:tile_height] || TILE_HEIGHT
13
20
 
21
+ ## check for background
22
+ background = kwargs[:background] || kwargs[:tile_background]
23
+
24
+ if background
25
+ ## wrap into an array if not already an array
26
+ ## and convert all colors to true rgba colors as integer numbers
27
+ background = [background] unless background.is_a?( Array )
28
+ @background_colors = background.map { |color| Color.parse( color ) }
29
+ else
30
+ ## todo/check: use empty array instead of nil - why? why not?
31
+ @background_colors = nil
32
+ end
33
+
34
+
14
35
  ## todo/fix: check type - args[0] is Image!!!
15
36
  if args.size == 1 ## assume "copy" c'tor with passed in image
16
37
  img = args[0] ## pass image through as-is
@@ -25,12 +46,25 @@ class ImageComposite < Image # check: (re)name to Collage, Sheet, Sprites, or s
25
46
  @tile_rows = args[1] || 3
26
47
  @tile_count = 0 # (track) current index (of added images)
27
48
 
28
- img = ChunkyPNG::Image.new( @tile_cols * @tile_width,
29
- @tile_rows * @tile_height )
49
+ background_color = if @background_colors && @background_colors.size == 1
50
+ @background_colors[0]
51
+ else
52
+ 0 # note: 0 is transparent (0) true color
53
+ end
54
+
55
+ ## todo/check - always auto-fill complete image (even if empty/no tiles)
56
+ ## with background color if only one background color
57
+ ## why? why not??? or always follow the "model"
58
+ ## with more than one background color???
59
+ img = ChunkyPNG::Image.new( @tile_cols * @tile_width,
60
+ @tile_rows * @tile_height,
61
+ background_color )
62
+
30
63
  else
31
64
  raise ArgumentError, "cols, rows or image arguments expected; got: #{args.inspect}"
32
65
  end
33
66
 
67
+
34
68
  puts " #{img.height}x#{img.width} (height x width)"
35
69
 
36
70
  super( nil, nil, img )
@@ -39,19 +73,46 @@ class ImageComposite < Image # check: (re)name to Collage, Sheet, Sprites, or s
39
73
 
40
74
  def count() @tile_count; end
41
75
  alias_method :size, :count ## add size alias (confusing if starting with 0?) - why? why not?
76
+ alias_method :tile_count, :count
77
+
78
+ def tile_width() @tile_width; end
79
+ def tile_height() @tile_height; end
80
+
81
+
42
82
 
43
83
  #####
44
84
  # set / add tile
45
-
46
- def add( image )
85
+ def _add( image )
47
86
  y, x = @tile_count.divmod( @tile_cols )
48
87
 
49
88
  puts " [#{@tile_count}] @ (#{x}/#{y}) #{image.width}x#{image.height} (height x width)"
50
89
 
90
+ ## note: only used if more than one background color specified
91
+ ## needs to cycle through
92
+ if @background_colors && @background_colors.size > 1
93
+ i = x + y*@tile_cols
94
+
95
+ ## note: cycle through background color for now
96
+ background_color = @background_colors[i % @background_colors.size]
97
+ background = Image.new( @tile_width, @tile_height, background_color ) ## todo/chekc: use "raw" ChunkyPNG:Image here - why? why not?
98
+ background.compose!( image )
99
+ image = background ## switch - make image with background new image
100
+ end
101
+
51
102
  ## note: image.image - "unwrap" the "raw" ChunkyPNG::Image
52
103
  @img.compose!( image.image, x*@tile_width, y*@tile_height )
53
104
  @tile_count += 1
54
105
  end
106
+
107
+ def add( image_or_images ) ## note: allow adding of image OR array of images
108
+ if image_or_images.is_a?( Array )
109
+ images = image_or_images
110
+ images.each { |image| _add( image ) }
111
+ else
112
+ image = image_or_images
113
+ _add( image )
114
+ end
115
+ end
55
116
  alias_method :<<, :add
56
117
 
57
118
 
@@ -73,5 +134,21 @@ class ImageComposite < Image # check: (re)name to Collage, Sheet, Sprites, or s
73
134
  super ## e.g [x,y] --- get pixel
74
135
  end
75
136
  end
137
+
138
+
139
+ ## convenience helpers to loop over composite
140
+ def each( &block )
141
+ count.times do |i|
142
+ block.call( tile( i ) )
143
+ end
144
+ end
145
+
146
+ def each_with_index( &block )
147
+ count.times do |i|
148
+ block.call( tile( i ), i )
149
+ end
150
+ end
151
+
152
+
76
153
  end # class ImageComposite
77
154
  end # module Pixelart
@@ -64,18 +64,21 @@ end
64
64
 
65
65
 
66
66
 
67
- def zoom( zoom=2 )
67
+ def zoom( zoom=2, spacing: 0 )
68
68
  ## create a new zoom factor x image (2x, 3x, etc.)
69
69
 
70
- img = Image.new( @img.width*zoom,
71
- @img.height*zoom )
70
+ width = @img.width*zoom+(@img.width-1)*spacing
71
+ height = @img.height*zoom+(@img.height-1)*spacing
72
72
 
73
- @img.height.times do |y|
74
- @img.width.times do |x|
73
+ img = Image.new( width, height )
74
+
75
+ @img.width.times do |x|
76
+ @img.height.times do |y|
75
77
  pixel = @img[x,y]
76
78
  zoom.times do |n|
77
79
  zoom.times do |m|
78
- img[n+zoom*x,m+zoom*y] = pixel
80
+ img[n+zoom*x+spacing*x,
81
+ m+zoom*y+spacing*y] = pixel
79
82
  end
80
83
  end
81
84
  end # each x
@@ -86,6 +89,12 @@ end
86
89
  alias_method :scale, :zoom
87
90
 
88
91
 
92
+ def crop( x, y, crop_width, crop_height )
93
+ Image.new( nil, nil,
94
+ image.crop( x,y, crop_width, crop_height ) )
95
+ end
96
+
97
+
89
98
 
90
99
  #######################
91
100
  ## filter / effects
@@ -94,12 +103,25 @@ def grayscale
94
103
  img = @img.grayscale
95
104
  Image.new( img.width, img.height, img )
96
105
  end
106
+ alias_method :greyscale, :grayscale
107
+
108
+
109
+
110
+ def flip
111
+ img = @img.flip
112
+ Image.new( img.width, img.height, img )
113
+ end
114
+ alias_method :flip_horizontally, :flip
97
115
 
98
116
  def mirror
99
117
  img = @img.mirror
100
118
  Image.new( img.width, img.height, img )
101
119
  end
102
120
  alias_method :flip_vertically, :mirror
121
+ alias_method :flop, :mirror
122
+
123
+
124
+
103
125
 
104
126
 
105
127
  ## add replace_colors alias too? - why? why not?
@@ -0,0 +1,35 @@
1
+ module Pixelart
2
+
3
+
4
+ ## todo/check:
5
+ ## use a different name for silhouette
6
+ ## - why not - outline ???
7
+ ## or - shadow ???
8
+ ## or - profile ???
9
+ ## or - figure ???
10
+ ## or - shape ???
11
+ ## or - form ???
12
+
13
+ class Image
14
+ def silhouette( color='#000000' )
15
+ color = Color.parse( color )
16
+
17
+ img = Image.new( @img.width, @img.height )
18
+
19
+ @img.width.times do |x|
20
+ @img.height.times do |y|
21
+ pixel = @img[x,y]
22
+
23
+ img[x,y] = if pixel == Color::TRANSPARENT # transparent (0)
24
+ Color::TRANSPARENT
25
+ else
26
+ color
27
+ end
28
+ end
29
+ end
30
+ img
31
+ end
32
+
33
+ end # class Image
34
+
35
+ end # module Pixelart
@@ -3,41 +3,67 @@ module Pixelart
3
3
  class Image
4
4
 
5
5
 
6
- def spots( spot=10,
7
- spacing: 0,
8
- center: nil,
9
- radius: nil,
10
- background: nil,
11
- lightness: nil,
12
- odd: false )
6
+ def spots_hidef( spot=10,
7
+ spacing: 0,
8
+ center: nil,
9
+ radius: nil,
10
+ background: nil,
11
+ lightness: nil,
12
+ odd: false )
13
+
14
+ width = @img.width*spot+(@img.width-1)*spacing
15
+ height = @img.height*spot+(@img.height-1)*spacing
13
16
 
14
- width = @img.width
15
- height = @img.height
16
17
  ## puts " #{width}x#{height}"
17
18
 
19
+ ## settings in a hash for "pretty printing" in comments
20
+ settings = { spot: spot
21
+ }
22
+
23
+ settings[ :spacing ] = spacing if spacing
24
+ settings[ :center ] = center if center
25
+ settings[ :radius ] = radius if radius
26
+ settings[ :background ] = background if background
27
+ settings[ :lightness ] = lightness if lightness
28
+ settings[ :odd ] = odd if odd
29
+
30
+
31
+ v = Vector.new( width, height, header: <<TXT )
32
+ generated by pixelart/v#{VERSION} on #{Time.now.utc}
33
+
34
+ spots_hidef with settings:
35
+ #{settings.to_json}
36
+ TXT
37
+
38
+
18
39
  min_center, max_center = center ? center : [0,0]
19
40
  min_radius, max_radius = radius ? radius : [0,0]
20
41
 
21
- background_color = background ? Color.parse( background ) : 0
42
+ ## note: allow passing in array of colors (will get randomally picked)
43
+ background_colors = if background
44
+ ## check for array; otherwise assume single color passed in
45
+ background_ary = background.is_a?( Array) ? background : [background]
46
+ background_ary.map { |background| Color.parse( background ) }
47
+ else
48
+ [0] # transparent (default - no background)
49
+ end
22
50
 
23
- min_lightness, max_lightness = lightness ? lightness : [0.0,0.0]
24
51
 
25
- ## note: magick command line might get way too long, thus,
26
- ## save commands to a script
27
- script = String.new('')
28
- script << "#{MAGICK_INPUT} \\\n"
52
+ min_lightness, max_lightness = lightness ? lightness : [0.0,0.0]
29
53
 
30
54
 
31
- width.times do |x|
32
- height.times do |y|
55
+ @img.width.times do |x|
56
+ @img.height.times do |y|
33
57
  color = @img[ x, y ]
34
58
 
35
- if color == 0 ## transparent
36
- if background ## change transparent to background
37
- color = background_color
38
- else
39
- next ## skip transparent
40
- end
59
+ if color == 0 ## transparent
60
+ next if background.nil?
61
+
62
+ color = if background_colors.size == 1
63
+ background_colors[0]
64
+ else ## pick random background color
65
+ background_colors[ rand( background_colors.size ) ]
66
+ end
41
67
  end
42
68
 
43
69
 
@@ -65,7 +91,6 @@ def spots( spot=10,
65
91
  # e.g. 0 becomes #00000000
66
92
  # and so on
67
93
  color_hex = Color.to_hex( color, include_alpha: true )
68
- script << "-fill '#{color_hex}' "
69
94
 
70
95
  cx_offset,
71
96
  cy_offset = if center ## randomize (offset off center +/-)
@@ -79,48 +104,40 @@ def spots( spot=10,
79
104
  cx = x*spot + x*spacing + cx_offset
80
105
  cy = y*spot + y*spacing + cx_offset
81
106
 
82
- px_offset = if radius ## randomize (radius +/-)
107
+ r = if radius ## randomize (radius +/-)
83
108
  min_radius + rand( max_radius-min_radius )
84
109
  else
85
110
  spot/2
86
111
  end
87
- px = cx + px_offset
88
- py = cy
89
112
 
113
+ cx += spot/2 if odd && (y % 2 == 1) ## add odd offset
90
114
 
91
- if odd && (y % 2 == 1) ## add odd offset
92
- cx += spot/2
93
- px += spot/2
94
- end
95
115
 
96
- ## circle
97
- ## give the center and any point on the perimeter (boundary)
98
- script << "-draw 'circle #{cx},#{cy},#{px},#{py}' \\\n"
116
+ v.circle( cx: cx, cy: cy, r: r, fill: color_hex)
99
117
  end
100
118
  end
119
+ v
120
+ end ## method spots_hidef
121
+ alias_method :spots_hd, :spots_hidef
101
122
 
102
- script << "-write #{MAGICK_OUTPUT}\n"
103
-
104
-
105
- ## use an empty image (canvas) with transparent background
106
- ## as magick input image
107
- canvas = Image.new( width*spot+(width-1)*spacing,
108
- height*spot+(height-1)*spacing )
109
- canvas.save( MAGICK_INPUT )
110
-
111
- ## note: save magick input first (see above) before save script
112
- ## will (auto-)create missing directories in path (if missing)
113
-
114
- File.open( MAGICK_SCRIPT, 'w:utf-8' ) do |f|
115
- f.write( script )
116
- end
117
123
 
124
+ def spots( spot=10,
125
+ spacing: 0,
126
+ center: nil,
127
+ radius: nil,
128
+ background: nil,
129
+ lightness: nil,
130
+ odd: false )
118
131
 
119
- MiniMagick::Tool::Magick.new do |magick|
120
- magick.script( MAGICK_SCRIPT )
121
- end
132
+ v = spots_hidef( spot,
133
+ spacing: spacing,
134
+ center: center,
135
+ radius: radius,
136
+ background: background,
137
+ lightness: lightness,
138
+ odd: odd )
122
139
 
123
- Image.read( MAGICK_OUTPUT )
140
+ v.to_image
124
141
  end
125
142
 
126
143
 
@@ -0,0 +1,60 @@
1
+
2
+ module Pixelart
3
+ class Image
4
+
5
+ def transparent( style = :solid, fuzzy: false )
6
+ img = Image.new( width, height )
7
+
8
+
9
+ background = self[0,0]
10
+
11
+ bh,bs,bl = Color.to_hsl( background )
12
+ bh = (bh % 360) ## might might negative degree (always make positive)
13
+
14
+ height.times do |y|
15
+ if style == :linear
16
+ background = self[0,y]
17
+
18
+ bh,bs,bl = Color.to_hsl( background )
19
+ bh = (bh % 360) ## might might negative degree (always make positive)
20
+ end
21
+ width.times do |x|
22
+ pixel = self[x,y]
23
+
24
+ if background == 0 ## special case if background is already transparent keep going
25
+ img[x,y] = pixel
26
+ elsif fuzzy
27
+ ## check for more transparents
28
+ ## not s is 0.0 to 0.99 (100%)
29
+ ## and l is 0.0 to 0.99 (100%)
30
+ h,s,l = Color.to_hsl( pixel )
31
+ h = (h % 360) ## might might negative degree (always make positive)
32
+
33
+ ## try some kind-of fuzzy "heuristic" match on background color
34
+ if ((h >= bh-5) && (h <= bh+5)) &&
35
+ ((s >= bs-0.07) && (s <= bs+0.07)) &&
36
+ ((l >= bl-0.07) && (l <= bl+0.07))
37
+ img[x,y] = 0 ## Color::TRANSPARENT
38
+
39
+ if h != bh || s != bs || l != bl
40
+ # report fuzzy background color
41
+ puts " #{x}/#{y} fuzzy background #{[h,s,l]} ~= #{[bh,bs,bl]}"
42
+ end
43
+ else
44
+ img[x,y] = pixel
45
+ end
46
+ else
47
+ if pixel == background
48
+ img[x,y] = 0 ## Color::TRANSPARENT
49
+ else
50
+ img[x,y] = pixel
51
+ end
52
+ end
53
+ end
54
+ end
55
+ img
56
+ end # method transparent
57
+
58
+
59
+ end # class Image
60
+ end # module Pixelart
@@ -0,0 +1,163 @@
1
+ module Pixelart
2
+
3
+
4
+ class Vector # holds a vector graphics image (in source)
5
+
6
+ class Shape; end
7
+ class Circle < Shape
8
+ def initialize( cx, cy, r, fill: )
9
+ @cx = cx
10
+ @cy = cy
11
+ @r = r
12
+ @fill = fill
13
+ end
14
+
15
+ def to_svg
16
+ %Q{<circle cx="#{@cx}" cy="#{@cy}" r="#{@r}" fill="#{@fill}" />}
17
+ end
18
+
19
+ def to_magick
20
+ ## circle
21
+ ## give the center and any point on the perimeter (boundary)
22
+ px = @cx+@r
23
+ py = @cy
24
+ "-fill '#{@fill}' -draw 'circle #{@cx},#{@cy},#{px},#{py}'"
25
+ end
26
+ end # class Circle
27
+
28
+ class Path < Shape
29
+ def initialize( fill:, stroke: )
30
+ @commands = []
31
+ @fill = fill
32
+ @stroke = stroke
33
+ end
34
+
35
+ def move_to( x, y ) ## add a move_to instruction
36
+ @commands << ['M', x, y]
37
+ self
38
+ end
39
+
40
+ def line_to( x, y )
41
+ @commands << ['L', x, y]
42
+ self
43
+ end
44
+
45
+ def line( *coords ) ## move_to/line_to all-in-one shortcut
46
+ ## todo/check - assert coords has at least two x/y pairs - why? why not?
47
+ move_to( *coords[0..1] )
48
+ coords[2..-1].each_slice( 2) do |coord|
49
+ line_to( *coord )
50
+ end
51
+ self
52
+ end
53
+
54
+
55
+
56
+ def to_svg
57
+ buf = %Q{<path style="stroke: #{@stroke}; fill: #{@fill || 'none'};" }
58
+ buf << %Q{d="}
59
+ last_command = ''
60
+ @commands.each_with_index do |command,i|
61
+ buf << " " if i > 0 # add space separator
62
+
63
+ ## "optimize" - that is, do not repead command if same as before
64
+ buf << command[0] if command[0] != last_command
65
+ buf << "#{command[1]} #{command[2]}"
66
+ last_command = command[0]
67
+ end
68
+
69
+ buf << %Q{"}
70
+ buf << "/>"
71
+ buf
72
+ end
73
+ end # class Path
74
+
75
+
76
+
77
+ def initialize( width, height, header: nil )
78
+ @width = width
79
+ @height = height
80
+
81
+ @header = header
82
+ @shapes = []
83
+ end
84
+
85
+ def circle( cx:, cy:, r:, fill: )
86
+ @shapes << Circle.new( cx, cy, r, fill: fill )
87
+ end
88
+
89
+ ## note: default stroke (color) to black (#000000) for now - why? why not?
90
+ def path( stroke: '#000000', fill: nil )
91
+ path = Path.new( stroke: stroke, fill: fill )
92
+ @shapes << path
93
+
94
+ path ## note: MUST return "inner" path shape for chaining "dsl-like" methods / commands
95
+ end
96
+
97
+
98
+
99
+ def to_image
100
+ ## use an empty image (canvas) with transparent background
101
+ ## as magick input image
102
+ canvas = Image.new( @width, @height )
103
+ canvas.save( MAGICK_INPUT )
104
+
105
+ ## note: magick command line might get way too long, thus,
106
+ ## save commands to a script
107
+ ## note: save magick input first (see above) before save script
108
+ ## will (auto-)create missing directories in path (if missing)
109
+
110
+ File.open( MAGICK_SCRIPT, 'w:utf-8' ) do |f|
111
+ f.write( "#{MAGICK_INPUT} \\\n" )
112
+ @shapes.each do |shape|
113
+ f.write( "#{shape.to_magick} \\\n" )
114
+ end
115
+ f.write( "-write #{MAGICK_OUTPUT}\n" )
116
+ end
117
+
118
+ MiniMagick::Tool::Magick.new do |magick|
119
+ magick.script( MAGICK_SCRIPT )
120
+ end
121
+
122
+ Image.read( MAGICK_OUTPUT )
123
+ end
124
+
125
+
126
+ def to_svg
127
+ buf = String.new('')
128
+
129
+ if @header
130
+ buf << "<!--\n"
131
+ ## auto-indent lines by five (5) spaces for now
132
+ @header.each_line do |line|
133
+ buf << " #{line}"
134
+ end
135
+ buf << "\n-->\n\n"
136
+ end
137
+
138
+ buf << %Q{<svg version="1.1" width="#{@width}" height="#{@height}" xmlns="http://www.w3.org/2000/svg">\n}
139
+ @shapes.each do |shape|
140
+ buf << " #{shape.to_svg}\n"
141
+ end
142
+ buf << "</svg>"
143
+ buf
144
+ end
145
+
146
+
147
+ def save( path, format: nil )
148
+ if format && format.downcase == 'png' ## support png with image magick
149
+ img = to_image
150
+ img.save( path )
151
+ else
152
+ # make sure outdir exits
153
+ outdir = File.dirname( path )
154
+ FileUtils.mkdir_p( outdir ) unless Dir.exist?( outdir )
155
+ File.open( path, 'w:utf-8' ) do |f|
156
+ f.write( to_svg )
157
+ end
158
+ end
159
+ end
160
+ alias_method :write, :save
161
+
162
+ end # class Vector
163
+ end # module Pixelart
@@ -2,8 +2,8 @@
2
2
  module Pixelart
3
3
 
4
4
  MAJOR = 1
5
- MINOR = 1
6
- PATCH = 0
5
+ MINOR = 2
6
+ PATCH = 2
7
7
  VERSION = [MAJOR,MINOR,PATCH].join('.')
8
8
 
9
9
  def self.version
data/lib/pixelart.rb CHANGED
@@ -8,3 +8,5 @@ require 'pixelart/base' # aka "strict(er)" version
8
8
  # make Image, Color, Palette8bit, etc top-level
9
9
  include Pixelart
10
10
 
11
+
12
+
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pixelart
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-24 00:00:00.000000000 Z
11
+ date: 2022-01-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chunky_png
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: csvreader
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rdoc
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -97,10 +111,13 @@ files:
97
111
  - lib/pixelart/misc.rb
98
112
  - lib/pixelart/palette.rb
99
113
  - lib/pixelart/pixelator.rb
114
+ - lib/pixelart/silhouette.rb
100
115
  - lib/pixelart/sketch.rb
101
116
  - lib/pixelart/spots.rb
117
+ - lib/pixelart/transparent.rb
118
+ - lib/pixelart/vector.rb
102
119
  - lib/pixelart/version.rb
103
- homepage: https://github.com/rubycoco/pixel
120
+ homepage: https://github.com/pixelartexchange/pixel
104
121
  licenses:
105
122
  - Public Domain
106
123
  metadata: {}