module ChunkyPNG::Canvas::Drawing
def bezier_curve(points, stroke_color = ChunkyPNG::Color::BLACK)
-
(Chunky:PNG::Canvas)
- Itself, with the curve drawn
Parameters:
-
A
(Array, Point
) -- collection of control points
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 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 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 bicof = 0 t += 1 end 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 self end
def binomial_coefficient(n, k)
-
(Integer)
- The binomial coeffcient of (n,k)
Parameters:
-
k
(Integer
) -- k-element, second parameter in coeffient (the number -
n
(Integer
) -- first parameter in coeffient (the number on top when
def binomial_coefficient(n, k) return 1 if n == k || k == 0 return n if k == 1 return -1 if n < k # calculate factorials fact_n = (2..n).inject(1) { |carry, i| carry * i } fact_k = (2..k).inject(1) { |carry, i| carry * i } fact_n_sub_k = (2..(n - k)).inject(1) { |carry, i| carry * i } fact_n / (fact_k * fact_n_sub_k) end
def circle(x0, y0, radius,
-
(ChunkyPNG::Canvas)
- Itself, with the circle drawn.
Parameters:
-
fill_color
(Integer
) -- The color to use that fills the circle. -
stroke_color
(Integer
) -- The color to use for the line. -
radius
(Integer
) -- The radius of the circle from the center point. -
y0
(Integer
) -- The y-coordinate of the center of the circle. -
x0
(Integer
) -- The x-coordinate of the center of the circle.
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 ddF_x = 1 ddF_y = -2 * radius x = 0 y = radius compose_pixel(x0, y0 + radius, stroke_color) compose_pixel(x0, y0 - radius, stroke_color) compose_pixel(x0 + radius, y0, stroke_color) compose_pixel(x0 - radius, y0, stroke_color) lines = [radius - 1] unless fill_color == ChunkyPNG::Color::TRANSPARENT while x < y if f >= 0 y -= 1 ddF_y += 2 f += ddF_y end x += 1 ddF_x += 2 f += ddF_x unless fill_color == ChunkyPNG::Color::TRANSPARENT lines[y] = lines[y] ? [lines[y], x - 1].min : x - 1 lines[x] = lines[x] ? [lines[x], y - 1].min : y - 1 end compose_pixel(x0 + x, y0 + y, stroke_color) compose_pixel(x0 - x, y0 + y, stroke_color) compose_pixel(x0 + x, y0 - y, stroke_color) compose_pixel(x0 - x, y0 - y, stroke_color) unless x == y compose_pixel(x0 + y, y0 + x, stroke_color) compose_pixel(x0 - y, y0 + x, stroke_color) compose_pixel(x0 + y, y0 - x, stroke_color) compose_pixel(x0 - y, y0 - x, stroke_color) end end unless fill_color == ChunkyPNG::Color::TRANSPARENT lines.each_with_index do |length, y_offset| if length > 0 line(x0 - length, y0 - y_offset, x0 + length, y0 - y_offset, fill_color) end if length > 0 && y_offset > 0 line(x0 - length, y0 + y_offset, x0 + length, y0 + y_offset, fill_color) end end end self end
def compose_pixel(x, y, color)
-
(Integer)
- The composed color.
Parameters:
-
color
(Integer
) -- The foreground color to blend with -
y
(Integer
) -- The y-coordinate of the pixel to blend. -
x
(Integer
) -- The x-coordinate of the pixel to blend.
def compose_pixel(x, y, color) return unless include_xy?(x, y) compose_pixel_unsafe(x, y, ChunkyPNG::Color.parse(color)) end
def compose_pixel_unsafe(x, y, color)
-
(Integer)
- The composed color.
Parameters:
-
(
) --
def compose_pixel_unsafe(x, y, color) set_pixel(x, y, ChunkyPNG::Color.compose(color, get_pixel(x, y))) end
def line_xiaolin_wu(x0, y0, x1, y1, stroke_color, inclusive = true)
-
(ChunkyPNG::Canvas)
- Itself, with the line drawn.
Parameters:
-
inclusive
(true, false
) -- Whether to draw the last pixel. Set to -
stroke_color
(Integer
) -- The color to use for this line. -
y1
(Integer
) -- The y-coordinate of the second control point. -
x1
(Integer
) -- The x-coordinate of the second control point. -
y0
(Integer
) -- The y-coordinate of the first control point. -
x0
(Integer
) -- The x-coordinate of the first control point.
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 w = 0xff - (e_acc >> 8) compose_pixel(x0, y0, ChunkyPNG::Color.fade(stroke_color, w)) 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 w = 0xff - (e_acc >> 8) compose_pixel(x0, y0, ChunkyPNG::Color.fade(stroke_color, w)) 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 self end
def polygon(path,
-
(ChunkyPNG::Canvas)
- Itself, with the polygon drawn.
Parameters:
-
fill_color
(Integer
) -- The fill color to use for this polygon. -
stroke_color
(Integer
) -- The stroke color to use for this polygon. -
The
(Array, String
) -- control point vector. Accepts everything
def polygon(path, stroke_color = ChunkyPNG::Color::BLACK, fill_color = ChunkyPNG::Color::TRANSPARENT) vector = ChunkyPNG::Vector(*path) 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 unless fill_color == ChunkyPNG::Color::TRANSPARENT vector.y_range.each do |y| intersections = [] vector.edges.each do |p1, p2| if (p1.y < y && p2.y >= y) || (p2.y < y && p1.y >= y) intersections << (p1.x + (y - p1.y).to_f / (p2.y - p1.y) * (p2.x - p1.x)).round end end intersections.sort! 0.step(intersections.length - 1, 2) do |i| intersections[i].upto(intersections[i + 1]) do |x| 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 self end
def rect(x0, y0, x1, y1,
-
(ChunkyPNG::Canvas)
- Itself, with the rectangle drawn.
Parameters:
-
fill_color
(Integer
) -- The fill color to use for this rectangle. -
stroke_color
(Integer
) -- The line color to use for this rectangle. -
y1
(Integer
) -- The y-coordinate of the second control point. -
x1
(Integer
) -- The x-coordinate of the second control point. -
y0
(Integer
) -- The y-coordinate of the first control point. -
x0
(Integer
) -- The x-coordinate of the first control point.
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) self end