lib/chunky_png/canvas/drawing.rb in chunky_png-1.3.0 vs lib/chunky_png/canvas/drawing.rb in chunky_png-1.3.1
- old
+ new
@@ -1,70 +1,72 @@
module ChunkyPNG
class Canvas
-
+
# Module that adds some primitive drawing methods to {ChunkyPNG::Canvas}.
#
- # All of these methods change the current canvas instance and do not create a new one,
- # even though the method names do not end with a bang.
+ # All of these methods change the current canvas instance and do not create
+ # a new one, even though the method names do not end with a bang.
#
- # @note Drawing operations will not fail when something is drawn outside of the bounds
- # of the canvas; these pixels will simply be ignored.
+ # @note Drawing operations will not fail when something is drawn outside of
+ # the bounds of the canvas; these pixels will simply be ignored.
# @see ChunkyPNG::Canvas
module Drawing
-
- # Composes a pixel on the canvas by alpha blending a color with its background color.
+
+ # Composes a pixel on the canvas by alpha blending a color with its
+ # background color.
+ #
# @param [Integer] x The x-coordinate of the pixel to blend.
# @param [Integer] y The y-coordinate of the pixel to blend.
# @param [Integer] color The foreground color to blend with
# @return [Integer] The composed color.
def compose_pixel(x, y, color)
return unless include_xy?(x, y)
compose_pixel_unsafe(x, y, ChunkyPNG::Color.parse(color))
end
-
- # Composes a pixel on the canvas by alpha blending a color with its background color,
- # without bounds checking.
+
+ # Composes a pixel on the canvas by alpha blending a color with its
+ # background color, without bounds checking.
+ #
# @param (see #compose_pixel)
# @return [Integer] The composed color.
def compose_pixel_unsafe(x, y, color)
set_pixel(x, y, ChunkyPNG::Color.compose(color, get_pixel(x, y)))
end
-
+
# Draws a Bezier curve
# @param [Array, Point] A collection of control points
# @return [Chunky:PNG::Canvas] Itself, with the curve drawn
def bezier_curve(points, stroke_color = ChunkyPNG::Color::BLACK)
-
points = ChunkyPNG::Vector(*points)
case points.length
when 0, 1; return self
when 2; return line(points[0].x, points[0].y, points[1].x, points[1].y, stroke_color)
end
-
+
curve_points = Array.new
-
- t = 0
- n = points.length - 1
+
+ t = 0
+ n = points.length - 1
bicof = 0
-
+
while t <= 100
cur_p = ChunkyPNG::Point.new(0,0)
-
+
# Generate a float of t.
t_f = t / 100.00
-
+
cur_p.x += ((1 - t_f) ** n) * points[0].x
cur_p.y += ((1 - t_f) ** n) * points[0].y
-
+
for i in 1...points.length - 1
bicof = binomial_coefficient(n , i)
-
- cur_p.x += (bicof * (1 - t_f) ** (n - i)) * (t_f ** i) * points[i].x
- cur_p.y += (bicof * (1 - t_f) ** (n - i)) * (t_f ** i) * points[i].y
+
+ cur_p.x += (bicof * (1 - t_f) ** (n - i)) * (t_f ** i) * points[i].x
+ cur_p.y += (bicof * (1 - t_f) ** (n - i)) * (t_f ** i) * points[i].y
i += 1
end
-
+
cur_p.x += (t_f ** n) * points[n].x
cur_p.y += (t_f ** n) * points[n].y
curve_points << cur_p
@@ -74,96 +76,107 @@
curve_points.each_cons(2) do |p1, p2|
line_xiaolin_wu(p1.x.round, p1.y.round, p2.x.round, p2.y.round, stroke_color)
end
- return self
+ self
end
-
-
+
# Draws an anti-aliased line using Xiaolin Wu's algorithm.
#
# @param [Integer] x0 The x-coordinate of the first control point.
# @param [Integer] y0 The y-coordinate of the first control point.
# @param [Integer] x1 The x-coordinate of the second control point.
# @param [Integer] y1 The y-coordinate of the second control point.
# @param [Integer] stroke_color The color to use for this line.
- # @param [true, false] inclusive Whether to draw the last pixel.
- # Set to false when drawing multiple lines in a path.
+ # @param [true, false] inclusive Whether to draw the last pixel. Set to
+ # false when drawing multiple lines in a path.
# @return [ChunkyPNG::Canvas] Itself, with the line drawn.
def line_xiaolin_wu(x0, y0, x1, y1, stroke_color, inclusive = true)
-
stroke_color = ChunkyPNG::Color.parse(stroke_color)
-
+
dx = x1 - x0
sx = dx < 0 ? -1 : 1
dx *= sx
dy = y1 - y0
sy = dy < 0 ? -1 : 1
dy *= sy
-
+
if dy == 0 # vertical line
x0.step(inclusive ? x1 : x1 - sx, sx) do |x|
compose_pixel(x, y0, stroke_color)
end
-
+
elsif dx == 0 # horizontal line
y0.step(inclusive ? y1 : y1 - sy, sy) do |y|
compose_pixel(x0, y, stroke_color)
end
-
+
elsif dx == dy # diagonal
x0.step(inclusive ? x1 : x1 - sx, sx) do |x|
compose_pixel(x, y0, stroke_color)
y0 += sy
end
-
+
elsif dy > dx # vertical displacement
compose_pixel(x0, y0, stroke_color)
e_acc = 0
e = ((dx << 16) / dy.to_f).round
(dy - 1).downto(0) do |i|
e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xffff
- x0 += sx if (e_acc <= e_acc_temp)
+ x0 += sx if e_acc <= e_acc_temp
w = 0xff - (e_acc >> 8)
compose_pixel(x0, y0, ChunkyPNG::Color.fade(stroke_color, w))
- compose_pixel(x0 + sx, y0 + sy, ChunkyPNG::Color.fade(stroke_color, 0xff - w)) if inclusive || i > 0
+ if inclusive || i > 0
+ compose_pixel(x0 + sx,
+ y0 + sy,
+ ChunkyPNG::Color.fade(stroke_color, 0xff - w))
+ end
y0 += sy
end
compose_pixel(x1, y1, stroke_color) if inclusive
-
+
else # horizontal displacement
compose_pixel(x0, y0, stroke_color)
e_acc = 0
e = ((dy << 16) / dx.to_f).round
(dx - 1).downto(0) do |i|
e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xffff
- y0 += sy if (e_acc <= e_acc_temp)
+ y0 += sy if e_acc <= e_acc_temp
w = 0xff - (e_acc >> 8)
compose_pixel(x0, y0, ChunkyPNG::Color.fade(stroke_color, w))
- compose_pixel(x0 + sx, y0 + sy, ChunkyPNG::Color.fade(stroke_color, 0xff - w)) if inclusive || i > 0
+ if inclusive || i > 0
+ compose_pixel(x0 + sx,
+ y0 + sy,
+ ChunkyPNG::Color.fade(stroke_color, 0xff - w))
+ end
x0 += sx
end
compose_pixel(x1, y1, stroke_color) if inclusive
end
-
- return self
+
+ self
end
-
+
alias_method :line, :line_xiaolin_wu
-
-
- # Draws a polygon on the canvas using the stroke_color, filled using the fill_color if any.
+
+ # Draws a polygon on the canvas using the stroke_color, filled using the
+ # fill_color if any.
#
- # @param [Array, String] The control point vector. Accepts everything {ChunkyPNG.Vector} accepts.
+ # @param [Array, String] The control point vector. Accepts everything
+ # {ChunkyPNG.Vector} accepts.
# @param [Integer] stroke_color The stroke color to use for this polygon.
# @param [Integer] fill_color The fill color to use for this polygon.
# @return [ChunkyPNG::Canvas] Itself, with the polygon drawn.
- def polygon(path, stroke_color = ChunkyPNG::Color::BLACK, fill_color = ChunkyPNG::Color::TRANSPARENT)
-
+ def polygon(path,
+ stroke_color = ChunkyPNG::Color::BLACK,
+ fill_color = ChunkyPNG::Color::TRANSPARENT)
+
vector = ChunkyPNG::Vector(*path)
- raise ArgumentError, "A polygon requires at least 3 points" if path.length < 3
+ if path.length < 3
+ raise ArgumentError, 'A polygon requires at least 3 points'
+ end
stroke_color = ChunkyPNG::Color.parse(stroke_color)
fill_color = ChunkyPNG::Color.parse(fill_color)
# Fill
@@ -182,60 +195,64 @@
compose_pixel(x, y, fill_color)
end
end
end
end
-
+
# Stroke
vector.each_edge do |(from_x, from_y), (to_x, to_y)|
line(from_x, from_y, to_x, to_y, stroke_color, false)
end
- return self
+ self
end
-
+
# Draws a rectangle on the canvas, using two control points.
#
# @param [Integer] x0 The x-coordinate of the first control point.
# @param [Integer] y0 The y-coordinate of the first control point.
# @param [Integer] x1 The x-coordinate of the second control point.
# @param [Integer] y1 The y-coordinate of the second control point.
# @param [Integer] stroke_color The line color to use for this rectangle.
# @param [Integer] fill_color The fill color to use for this rectangle.
# @return [ChunkyPNG::Canvas] Itself, with the rectangle drawn.
- def rect(x0, y0, x1, y1, stroke_color = ChunkyPNG::Color::BLACK, fill_color = ChunkyPNG::Color::TRANSPARENT)
-
+ def rect(x0, y0, x1, y1,
+ stroke_color = ChunkyPNG::Color::BLACK,
+ fill_color = ChunkyPNG::Color::TRANSPARENT)
+
stroke_color = ChunkyPNG::Color.parse(stroke_color)
fill_color = ChunkyPNG::Color.parse(fill_color)
-
+
# Fill
unless fill_color == ChunkyPNG::Color::TRANSPARENT
[x0, x1].min.upto([x0, x1].max) do |x|
[y0, y1].min.upto([y0, y1].max) do |y|
compose_pixel(x, y, fill_color)
end
end
end
-
+
# Stroke
line(x0, y0, x0, y1, stroke_color, false)
line(x0, y1, x1, y1, stroke_color, false)
line(x1, y1, x1, y0, stroke_color, false)
line(x1, y0, x0, y0, stroke_color, false)
-
- return self
+
+ self
end
-
+
# Draws a circle on the canvas.
#
# @param [Integer] x0 The x-coordinate of the center of the circle.
# @param [Integer] y0 The y-coordinate of the center of the circle.
# @param [Integer] radius The radius of the circle from the center point.
# @param [Integer] stroke_color The color to use for the line.
# @param [Integer] fill_color The color to use that fills the circle.
# @return [ChunkyPNG::Canvas] Itself, with the circle drawn.
- def circle(x0, y0, radius, stroke_color = ChunkyPNG::Color::BLACK, fill_color = ChunkyPNG::Color::TRANSPARENT)
+ def circle(x0, y0, radius,
+ stroke_color = ChunkyPNG::Color::BLACK,
+ fill_color = ChunkyPNG::Color::TRANSPARENT)
stroke_color = ChunkyPNG::Color.parse(stroke_color)
fill_color = ChunkyPNG::Color.parse(fill_color)
f = 1 - radius
@@ -281,23 +298,29 @@
end
end
unless fill_color == ChunkyPNG::Color::TRANSPARENT
lines.each_with_index do |length, y|
- line(x0 - length, y0 - y, x0 + length, y0 - y, fill_color) if length > 0
- line(x0 - length, y0 + y, x0 + length, y0 + y, fill_color) if length > 0 && y > 0
+ if length > 0
+ line(x0 - length, y0 - y, x0 + length, y0 - y, fill_color)
+ end
+ if length > 0 && y > 0
+ line(x0 - length, y0 + y, x0 + length, y0 + y, fill_color)
+ end
end
end
- return self
+ self
end
-
+
private
-
+
# Calculates the binomial coefficient for n over k.
#
- # @param [Integer] n first parameter in coeffient (the number on top when looking at the mathematic formula)
- # @param [Integer] k k-element, second parameter in coeffient (the number on the bottom when looking at the mathematic formula)
+ # @param [Integer] n first parameter in coeffient (the number on top when
+ # looking at the mathematic formula)
+ # @param [Integer] k k-element, second parameter in coeffient (the number
+ # on the bottom when looking at the mathematic formula)
# @return [Integer] The binomial coeffcient of (n,k)
def binomial_coefficient(n, k)
return 1 if n == k || k == 0
return n if k == 1
return -1 if n < k