pixelart 1.2.3 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -3
- data/Manifest.txt +1 -0
- data/README.md +290 -290
- data/Rakefile +32 -32
- data/lib/pixelart/base.rb +87 -86
- data/lib/pixelart/blur.rb +19 -19
- data/lib/pixelart/circle.rb +46 -46
- data/lib/pixelart/color.rb +131 -131
- data/lib/pixelart/composite.rb +154 -154
- data/lib/pixelart/gradient.rb +106 -106
- data/lib/pixelart/image.rb +283 -283
- data/lib/pixelart/led.rb +37 -37
- data/lib/pixelart/misc.rb +66 -66
- data/lib/pixelart/palette.rb +72 -72
- data/lib/pixelart/pixelator.rb +165 -165
- data/lib/pixelart/silhouette.rb +35 -35
- data/lib/pixelart/sketch.rb +69 -69
- data/lib/pixelart/spots.rb +146 -146
- data/lib/pixelart/stripes.rb +116 -0
- data/lib/pixelart/transparent.rb +60 -60
- data/lib/pixelart/ukraine.rb +20 -33
- data/lib/pixelart/vector.rb +163 -163
- data/lib/pixelart/version.rb +22 -22
- data/lib/pixelart.rb +12 -12
- metadata +7 -6
data/lib/pixelart/composite.rb
CHANGED
@@ -1,154 +1,154 @@
|
|
1
|
-
module Pixelart
|
2
|
-
|
3
|
-
class ImageComposite < Image # check: (re)name to Collage, Sheet, Sprites, or such?
|
4
|
-
|
5
|
-
## default tile width / height in pixel -- check: (re)name to sprite or such? why? why not?
|
6
|
-
TILE_WIDTH = 24
|
7
|
-
TILE_HEIGHT = 24
|
8
|
-
|
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
|
-
|
17
|
-
def initialize( *args, **kwargs )
|
18
|
-
@tile_width = kwargs[:width] || kwargs[:tile_width] || TILE_WIDTH
|
19
|
-
@tile_height = kwargs[:height] || kwargs[:tile_height] || TILE_HEIGHT
|
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
|
-
|
35
|
-
## todo/fix: check type - args[0] is Image!!!
|
36
|
-
if args.size == 1 ## assume "copy" c'tor with passed in image
|
37
|
-
img = args[0] ## pass image through as-is
|
38
|
-
|
39
|
-
@tile_cols = img.width / @tile_width ## e.g. 2400/24 = 100
|
40
|
-
@tile_rows = img.height / @tile_height ## e.g. 2400/24 = 100
|
41
|
-
@tile_count = @tile_cols * @tile_rows ## ## 10000 = 100x100 (2400x2400 pixel)
|
42
|
-
elsif args.size == 2 || args.size == 0 ## cols, rows
|
43
|
-
## todo/fix: check type - args[0] & args[1] is Integer!!!!!
|
44
|
-
## todo/check - find a better name for cols/rows - why? why not?
|
45
|
-
@tile_cols = args[0] || 3
|
46
|
-
@tile_rows = args[1] || 3
|
47
|
-
@tile_count = 0 # (track) current index (of added images)
|
48
|
-
|
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
|
-
|
63
|
-
else
|
64
|
-
raise ArgumentError, "cols, rows or image arguments expected; got: #{args.inspect}"
|
65
|
-
end
|
66
|
-
|
67
|
-
|
68
|
-
puts " #{img.height}x#{img.width} (height x width)"
|
69
|
-
|
70
|
-
super( nil, nil, img )
|
71
|
-
end
|
72
|
-
|
73
|
-
|
74
|
-
def count() @tile_count; end
|
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
|
-
|
82
|
-
|
83
|
-
#####
|
84
|
-
# set / add tile
|
85
|
-
def _add( image )
|
86
|
-
y, x = @tile_count.divmod( @tile_cols )
|
87
|
-
|
88
|
-
puts " [#{@tile_count}] @ (#{x}/#{y}) #{image.width}x#{image.height} (height x width)"
|
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
|
-
|
102
|
-
## note: image.image - "unwrap" the "raw" ChunkyPNG::Image
|
103
|
-
@img.compose!( image.image, x*@tile_width, y*@tile_height )
|
104
|
-
@tile_count += 1
|
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
|
116
|
-
alias_method :<<, :add
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
######
|
121
|
-
# get tile
|
122
|
-
|
123
|
-
def tile( index )
|
124
|
-
y, x = index.divmod( @tile_cols )
|
125
|
-
img = @img.crop( x*@tile_width, y*@tile_height, @tile_width, @tile_height )
|
126
|
-
Image.new( img.width, img.height, img ) ## wrap in pixelart image
|
127
|
-
end
|
128
|
-
|
129
|
-
def []( *args ) ## overload - why? why not?
|
130
|
-
if args.size == 1
|
131
|
-
index = args[0]
|
132
|
-
tile( index )
|
133
|
-
else
|
134
|
-
super ## e.g [x,y] --- get pixel
|
135
|
-
end
|
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
|
-
|
153
|
-
end # class ImageComposite
|
154
|
-
end # module Pixelart
|
1
|
+
module Pixelart
|
2
|
+
|
3
|
+
class ImageComposite < Image # check: (re)name to Collage, Sheet, Sprites, or such?
|
4
|
+
|
5
|
+
## default tile width / height in pixel -- check: (re)name to sprite or such? why? why not?
|
6
|
+
TILE_WIDTH = 24
|
7
|
+
TILE_HEIGHT = 24
|
8
|
+
|
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
|
+
|
17
|
+
def initialize( *args, **kwargs )
|
18
|
+
@tile_width = kwargs[:width] || kwargs[:tile_width] || TILE_WIDTH
|
19
|
+
@tile_height = kwargs[:height] || kwargs[:tile_height] || TILE_HEIGHT
|
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
|
+
|
35
|
+
## todo/fix: check type - args[0] is Image!!!
|
36
|
+
if args.size == 1 ## assume "copy" c'tor with passed in image
|
37
|
+
img = args[0] ## pass image through as-is
|
38
|
+
|
39
|
+
@tile_cols = img.width / @tile_width ## e.g. 2400/24 = 100
|
40
|
+
@tile_rows = img.height / @tile_height ## e.g. 2400/24 = 100
|
41
|
+
@tile_count = @tile_cols * @tile_rows ## ## 10000 = 100x100 (2400x2400 pixel)
|
42
|
+
elsif args.size == 2 || args.size == 0 ## cols, rows
|
43
|
+
## todo/fix: check type - args[0] & args[1] is Integer!!!!!
|
44
|
+
## todo/check - find a better name for cols/rows - why? why not?
|
45
|
+
@tile_cols = args[0] || 3
|
46
|
+
@tile_rows = args[1] || 3
|
47
|
+
@tile_count = 0 # (track) current index (of added images)
|
48
|
+
|
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
|
+
|
63
|
+
else
|
64
|
+
raise ArgumentError, "cols, rows or image arguments expected; got: #{args.inspect}"
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
puts " #{img.height}x#{img.width} (height x width)"
|
69
|
+
|
70
|
+
super( nil, nil, img )
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
def count() @tile_count; end
|
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
|
+
|
82
|
+
|
83
|
+
#####
|
84
|
+
# set / add tile
|
85
|
+
def _add( image )
|
86
|
+
y, x = @tile_count.divmod( @tile_cols )
|
87
|
+
|
88
|
+
puts " [#{@tile_count}] @ (#{x}/#{y}) #{image.width}x#{image.height} (height x width)"
|
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
|
+
|
102
|
+
## note: image.image - "unwrap" the "raw" ChunkyPNG::Image
|
103
|
+
@img.compose!( image.image, x*@tile_width, y*@tile_height )
|
104
|
+
@tile_count += 1
|
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
|
116
|
+
alias_method :<<, :add
|
117
|
+
|
118
|
+
|
119
|
+
|
120
|
+
######
|
121
|
+
# get tile
|
122
|
+
|
123
|
+
def tile( index )
|
124
|
+
y, x = index.divmod( @tile_cols )
|
125
|
+
img = @img.crop( x*@tile_width, y*@tile_height, @tile_width, @tile_height )
|
126
|
+
Image.new( img.width, img.height, img ) ## wrap in pixelart image
|
127
|
+
end
|
128
|
+
|
129
|
+
def []( *args ) ## overload - why? why not?
|
130
|
+
if args.size == 1
|
131
|
+
index = args[0]
|
132
|
+
tile( index )
|
133
|
+
else
|
134
|
+
super ## e.g [x,y] --- get pixel
|
135
|
+
end
|
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
|
+
|
153
|
+
end # class ImageComposite
|
154
|
+
end # module Pixelart
|
data/lib/pixelart/gradient.rb
CHANGED
@@ -1,106 +1,106 @@
|
|
1
|
-
|
2
|
-
## inspired / helped by
|
3
|
-
## https://en.wikipedia.org/wiki/List_of_software_palettes#Color_gradient_palettes
|
4
|
-
## https://github.com/mistic100/tinygradient
|
5
|
-
## https://mistic100.github.io/tinygradient/
|
6
|
-
## https://bsouthga.dev/posts/color-gradients-with-python
|
7
|
-
|
8
|
-
|
9
|
-
module Pixelart
|
10
|
-
|
11
|
-
class Gradient
|
12
|
-
|
13
|
-
def initialize( *stops )
|
14
|
-
## note: convert stop colors to rgb triplets e.g.
|
15
|
-
## from #ffffff to [255,255,255]
|
16
|
-
## #000000 to [0,0,0] etc.
|
17
|
-
@stops = stops.map do |stop|
|
18
|
-
stop = Color.parse( stop )
|
19
|
-
[Color.r(stop), Color.g(stop), Color.b(stop)]
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
|
24
|
-
def colors( steps )
|
25
|
-
segments = @stops.size - 1
|
26
|
-
|
27
|
-
## note: gradient will include start (first)
|
28
|
-
## AND stop color (last) - stop color is NOT excluded for now!!
|
29
|
-
if segments == 1
|
30
|
-
start = @stops[0]
|
31
|
-
stop = @stops[1]
|
32
|
-
|
33
|
-
gradient = linear_gradient( start, stop, steps,
|
34
|
-
include_stop: true )
|
35
|
-
else
|
36
|
-
steps_per_segment, mod = steps.divmod( segments )
|
37
|
-
raise ArgumentError, "steps (#{steps}) must be divisible by # of segments (#{segments}); expected mod of 0 but got #{mod}" if mod != 0
|
38
|
-
|
39
|
-
gradient = []
|
40
|
-
segments.times do |segment|
|
41
|
-
start = @stops[segment]
|
42
|
-
stop = @stops[segment+1]
|
43
|
-
include_stop = (segment == segments-1) ## note: only include stop if last segment!!
|
44
|
-
|
45
|
-
# print " segment #{segment+1}/#{segments} #{steps_per_segment} color(s) - "
|
46
|
-
# print " start: #{start.inspect} "
|
47
|
-
# print include_stop ? 'include' : 'exclude'
|
48
|
-
# print " stop: #{stop.inspect}"
|
49
|
-
# print "\n"
|
50
|
-
|
51
|
-
gradient += linear_gradient( start, stop, steps_per_segment,
|
52
|
-
include_stop: include_stop )
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
## convert to color (Integer)
|
57
|
-
gradient.map do |color|
|
58
|
-
Color.rgb( *color )
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
def interpolate( start, stop, steps, n )
|
65
|
-
## note: n - expected to start with 1,2,3,etc.
|
66
|
-
color = []
|
67
|
-
3.times do |i|
|
68
|
-
stepize = Float(stop[i] - start[i]) / Float(steps-1)
|
69
|
-
value = stepize * n
|
70
|
-
## convert back to Integer from Float
|
71
|
-
## add 0.5 for rounding up (starting with 0.5) - why? why not?
|
72
|
-
value = (value+0.5).to_i
|
73
|
-
value = start[i] + value
|
74
|
-
|
75
|
-
color << value
|
76
|
-
end
|
77
|
-
color
|
78
|
-
end
|
79
|
-
|
80
|
-
|
81
|
-
def linear_gradient( start, stop, steps,
|
82
|
-
include_stop: true )
|
83
|
-
|
84
|
-
gradient = [start] ## auto-add start color (first color in gradient)
|
85
|
-
|
86
|
-
if include_stop
|
87
|
-
1.upto( steps-2 ).each do |n| ## sub two (-2), that is, start and stop color
|
88
|
-
gradient << interpolate( start, stop, steps, n )
|
89
|
-
end
|
90
|
-
# note: use original passed in stop color (should match calculated)
|
91
|
-
gradient << stop
|
92
|
-
else
|
93
|
-
1.upto( steps-1 ).each do |n| ## sub one (-1), that is, start color only
|
94
|
-
## note: add one (+1) to steps because stop color gets excluded (not included)!!
|
95
|
-
gradient << interpolate( start, stop, steps+1, n )
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
gradient
|
100
|
-
end
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
end # class Gradient
|
105
|
-
end # module Pixelart
|
106
|
-
|
1
|
+
|
2
|
+
## inspired / helped by
|
3
|
+
## https://en.wikipedia.org/wiki/List_of_software_palettes#Color_gradient_palettes
|
4
|
+
## https://github.com/mistic100/tinygradient
|
5
|
+
## https://mistic100.github.io/tinygradient/
|
6
|
+
## https://bsouthga.dev/posts/color-gradients-with-python
|
7
|
+
|
8
|
+
|
9
|
+
module Pixelart
|
10
|
+
|
11
|
+
class Gradient
|
12
|
+
|
13
|
+
def initialize( *stops )
|
14
|
+
## note: convert stop colors to rgb triplets e.g.
|
15
|
+
## from #ffffff to [255,255,255]
|
16
|
+
## #000000 to [0,0,0] etc.
|
17
|
+
@stops = stops.map do |stop|
|
18
|
+
stop = Color.parse( stop )
|
19
|
+
[Color.r(stop), Color.g(stop), Color.b(stop)]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def colors( steps )
|
25
|
+
segments = @stops.size - 1
|
26
|
+
|
27
|
+
## note: gradient will include start (first)
|
28
|
+
## AND stop color (last) - stop color is NOT excluded for now!!
|
29
|
+
if segments == 1
|
30
|
+
start = @stops[0]
|
31
|
+
stop = @stops[1]
|
32
|
+
|
33
|
+
gradient = linear_gradient( start, stop, steps,
|
34
|
+
include_stop: true )
|
35
|
+
else
|
36
|
+
steps_per_segment, mod = steps.divmod( segments )
|
37
|
+
raise ArgumentError, "steps (#{steps}) must be divisible by # of segments (#{segments}); expected mod of 0 but got #{mod}" if mod != 0
|
38
|
+
|
39
|
+
gradient = []
|
40
|
+
segments.times do |segment|
|
41
|
+
start = @stops[segment]
|
42
|
+
stop = @stops[segment+1]
|
43
|
+
include_stop = (segment == segments-1) ## note: only include stop if last segment!!
|
44
|
+
|
45
|
+
# print " segment #{segment+1}/#{segments} #{steps_per_segment} color(s) - "
|
46
|
+
# print " start: #{start.inspect} "
|
47
|
+
# print include_stop ? 'include' : 'exclude'
|
48
|
+
# print " stop: #{stop.inspect}"
|
49
|
+
# print "\n"
|
50
|
+
|
51
|
+
gradient += linear_gradient( start, stop, steps_per_segment,
|
52
|
+
include_stop: include_stop )
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
## convert to color (Integer)
|
57
|
+
gradient.map do |color|
|
58
|
+
Color.rgb( *color )
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
def interpolate( start, stop, steps, n )
|
65
|
+
## note: n - expected to start with 1,2,3,etc.
|
66
|
+
color = []
|
67
|
+
3.times do |i|
|
68
|
+
stepize = Float(stop[i] - start[i]) / Float(steps-1)
|
69
|
+
value = stepize * n
|
70
|
+
## convert back to Integer from Float
|
71
|
+
## add 0.5 for rounding up (starting with 0.5) - why? why not?
|
72
|
+
value = (value+0.5).to_i
|
73
|
+
value = start[i] + value
|
74
|
+
|
75
|
+
color << value
|
76
|
+
end
|
77
|
+
color
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
def linear_gradient( start, stop, steps,
|
82
|
+
include_stop: true )
|
83
|
+
|
84
|
+
gradient = [start] ## auto-add start color (first color in gradient)
|
85
|
+
|
86
|
+
if include_stop
|
87
|
+
1.upto( steps-2 ).each do |n| ## sub two (-2), that is, start and stop color
|
88
|
+
gradient << interpolate( start, stop, steps, n )
|
89
|
+
end
|
90
|
+
# note: use original passed in stop color (should match calculated)
|
91
|
+
gradient << stop
|
92
|
+
else
|
93
|
+
1.upto( steps-1 ).each do |n| ## sub one (-1), that is, start color only
|
94
|
+
## note: add one (+1) to steps because stop color gets excluded (not included)!!
|
95
|
+
gradient << interpolate( start, stop, steps+1, n )
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
gradient
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
end # class Gradient
|
105
|
+
end # module Pixelart
|
106
|
+
|