Илия обнови решението на 22.12.2013 17:07 (преди почти 11 години)
+module Graphics
+
+ class Canvas
+ attr_reader :width, :height
+
+ def initialize(width = 0, height = 0)
+ @width = width
+ @height = height
+ @pixels = {}
+ end
+
+ def set_pixel(x, y)
+ @pixels["[#{x},#{y}]"] = true
+ end
+
+ def pixel_at?(x, y)
+ @pixels.has_key? "[#{x},#{y}]"
+ end
+
+ def draw(figure)
+ figure.rasterization.each { |point| set_pixel *point }
+ end
+
+ def render_as(renderer)
+ renderer.new(@width, @height, @pixels).to_s
+ end
+ end
+
+ module Renderers
+
+ class Ascii
+ attr_reader :renderer
+
+ def initialize(width, height, pixels)
+ @renderer = rendering width, height, pixels
+ end
+
+ def to_s
+ @renderer.map { |y| y.join }.join("\n")
+ end
+
+ private
+
+ def replace(array, pixels, first_string, second_string)
+ array.map { |coordinates| pixels[coordinates] ? first_string : second_string }
+ end
+
+ def rendering(width, height, pixels)
+ all_pixels = 0.upto(height - 1).map do |y|
+ 0.upto(width - 1).map { |x| "[#{x},#{y}]" }
+ end
+
+ all_pixels.map { |row| replace row, pixels, "@", "-" }
+ end
+ end
+
+ class Html
+ def initialize(width, height, pixels)
+ @renderer = rendering width, height, pixels
+ end
+
+ def to_s
+ "<!DOCTYPE html>
+ <html>
+ <head>
+ <title>Rendered Canvas</title>
+ <style type=\"text/css\">
+ .canvas {
+ font-size: 1px;
+ line-height: 1px;
+ }
+ .canvas * {
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ border-radius: 5px;
+ }
+ .canvas i {
+ background-color: #eee;
+ }
+ .canvas b {
+ background-color: #333;
+ }
+ </style>
+ </head>
+ <body>
+ <div class=\"canvas\">#{@renderer.map { |y| y.join }.join("<br>\n")}</div>
+ </body>
+ </html>"
+ end
+
+ private
+
+ def replace(array, pixels, first_string, second_string)
+ array.map { |coordinates| pixels[coordinates] ? first_string : second_string }
+ end
+
+ def rendering(width, height, pixels)
+ all_pixels = 0.upto(height - 1).map do |y|
+ 0.upto(width - 1).map { |x| "[#{x},#{y}]" }
+ end
+
+ all_pixels.map { |row| replace row, pixels, "<b></b>", "<i></i>" }
+ end
+ end
+ end
+
+ class Point
+ attr_reader :x, :y
+
+ def initialize(x, y)
+ @x = x
+ @y = y
+ end
+
+ def <=>(other)
+ [@x, @y] <=> [other.x, other.y]
+ end
+
+ def ==(other)
+ [@x, @y] == [other.x, other.y]
+ end
+
+ def eql?(other)
+ [@x, @y] == [other.x, other.y]
+ end
+
+ def rasterization
+ [[@x, @y]]
+ end
+ end
+
+ class Line
+ def initialize(a, b)
+ @a = a
+ @b = b
+ end
+
+ def from
+ (@a <=> @b) == -1 ? @a : @b
+ end
+
+ def to
+ (@a <=> @b) == 1 ? @a : @b
+ end
+
+ def ==(other)
+ [from, to] == [other.from, other.to]
+ end
+
+ def eql?(other)
+ [from, to] == [other.from, other.to]
+ end
+
+ def line_points(x_1, y_1, x_2, y_2, steep)
+ error, ystep, y = (x_2 - x_1) / 2, y_1 < y_2 ? 1 : -1, y_1
+ x_1.upto(x_2).map do |x|
+ point, error = steep ? [y, x] : [x, y], error - (y_2 - y_1).abs
+ y += ystep and error += (x_2 - x_1) if error < 0
+ point
+ end
+ end
+
+ def rasterization
+ x_1, y_1, x_2, y_2 = from.x, from.y, to.x, to.y
+ x_1, y_1, x_2, y_2 = y_1, x_1, y_2, x_2 if steep = (y_2 - y_1).abs > (x_2 - x_1)
+ x_1, x_2, y_1, y_2 = x_2, x_1, y_2, y_1 if x_1 > x_2
+ line_points x_1, y_1, x_2, y_2, steep
+ end
+ end
+
+ class Rectangle
+ def initialize(a, b)
+ @a = a
+ @b = b
+ @c = Point.new a.x, b.y
+ @d = Point.new b.x, a.y
+ end
+
+ def left
+ (@a <=> @b) == -1 ? @a : @b
+ end
+
+ def right
+ (@a <=> @b) == 1 ? @a : @b
+ end
+
+ def vertices
+ [@a, @b, @c, @d].sort
+ end
+
+ def top_left
+ vertices[0]
+ end
+
+ def top_right
+ vertices[2]
+ end
+
+ def bottom_left
+ vertices[1]
+ end
+
+ def bottom_right
+ vertices[3]
+ end
+
+ def ==(other)
+ vertices == other.vertices
+ end
+
+ def eql?(other)
+ vertices == other.vertices
+ end
+
+ def rasterization
+ points = [@a, @b, @c, @d].sort
+ lines = [[0,1], [1, 3], [2, 0], [2, 3]]
+ pixels = []
+ lines.each { |x, y| pixels.concat Line.new(points[x], points[y]).rasterization }
+ pixels
+ end
+ end
+end