Ангел обнови решението на 20.12.2013 18:02 (преди почти 11 години)
+module Graphics
+ class Canvas
+ attr_reader :width,:height
+
+ def initialize(width, height)
+ @width = width
+ @height = height
+ @canvas = 1.upto(height).map { |_| 1.upto(width).map { |_| false } }
+ end
+
+ def set_pixel(x, y)
+ @canvas[y][x] = true
+ end
+
+ def pixel_at?(x, y)
+ @canvas[y][x]
+ end
+
+ def draw(shape)
+ shape.points.each { |point| set_pixel(point.x,point.y) }
+ end
+
+ def render_as(renderer)
+ if renderer == Graphics::Renderers::Ascii
+ render_image({true => '@', false => '-'},"\n")
+ else renderer == Graphics::Renderers::Html
+ add_html render_image({true => "<b></b>", false => "<i></i>"}, "<br />")
+ end
+ end
+
+ private
+
+ def render_image(pixels, delimiter)
+ @canvas.each_with_object("") do |row, rendered|
+ row.each { |element| rendered << pixels[element] }
+ rendered << delimiter
+ end.chomp
+ end
+
+ def add_html(drawing)
+ "<!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\">#{drawing}</div> </body> </html>"
+ end
+ end
+
+ class Point
+ include Comparable
+
+ attr_reader :x,:y
+
+ def initialize(x, y)
+ @x = x
+ @y = y
+ end
+
+ alias_method :==, :eql?
+ def ==(other)
+ x == other.x and y == other.y
+ end
+
+ def <=>(other)
+ x == other.x ? y <=> other.y : x <=> other.x
+ end
+
+ def points
+ [self]
+ end
+ end
+
+ class Line
+ attr_reader :from,:to
+
+ def initialize(first_point, second_point)
+ @from, @to = [first_point,second_point].sort
+ end
+
+ alias_method :==, :eql?
+ def ==(other)
+ [@from,@to].sort == [other.from,other.to].sort
+ end
+
+ def points
+ bresenham_points = prepare_coordinates()
+ get_line_points(*bresenham_points)
+ end
+
+ private
+
+ def prepare_coordinates()
+ from_x,from_y,to_x,to_y = @from.x,@from.y,@to.x,@to.y
+ if steep_line?
+ from_x,from_y,to_x,to_y = from_y,from_x,to_y,to_x
+ end
+ if from_x > to_x
+ from_x,to_x,from_y,to_y = to_x,from_x,to_y,from_y
+ end
+ [Point.new(from_x,from_y),Point.new(to_x,to_y)]
+ end
+
+ def get_line_points(from_point, to_point)
+ error = (delta(from_point.x,to_point.x) / 2).to_i
+ y = from_point.y
+ ordinate_step = from_point.y < to_point.y ? 1 : -1
+ generate_points(error, y, ordinate_step,from_point, to_point)
+ end
+
+ def generate_points(error, y, ordinate_step, from_point, to_point)
+ (from_point.x..to_point.x).each_with_object([]) do |x,points|
+ points << (steep_line? ? Point.new(y,x) : Point.new(x,y))
+ error -= delta(from_point.y,to_point.y)
+ if error < 0
+ y += ordinate_step
+ error += delta(from_point.x,to_point.x)
+ end
+ end
+ end
+
+ def steep_line?
+ ((@to.y-@from.y).abs) > ((@to.x-@from.x).abs)
+ end
+
+ def delta(from_coordinate, to_coordinate)
+ (from_coordinate - to_coordinate).abs
+ end
+ end
+
+ class Rectangle
+ attr_reader :left,:right,:top_left,:top_right,:bottom_left,:bottom_right
+
+ def initialize(first_point, second_point)
+ @left, @right = [first_point,second_point].sort
+ @top_left, @bottom_right = @left, @right
+ @bottom_left = Point.new(@left.x,@right.y)
+ @top_right = Point.new(@right.x,@left.y)
+ end
+
+ alias_method :==, :eql?
+ def ==(other)
+ @left == other.left and @right == other.right
+ end
+
+ def points
+ [Line.new(@top_left,@top_right).points,
+ Line.new(@top_right,@bottom_right).points,
+ Line.new(@bottom_right,@bottom_left).points,
+ Line.new(@bottom_left,@top_left).points].flatten
+ end
+ end
+
+ module Renderers
+ class Ascii
+
+ end
+
+ class Html
+
+ end
+ end
+end
+
+module Graphics
+ canvas = Canvas.new 30, 30
+
+ # Door frame and window
+ canvas.draw Rectangle.new(Point.new(3, 3), Point.new(18, 12))
+ canvas.draw Rectangle.new(Point.new(1, 1), Point.new(20, 28))
+
+ # Door knob
+ canvas.draw Line.new(Point.new(4, 15), Point.new(7, 15))
+ canvas.draw Point.new(4, 16)
+
+ # Big "R"
+ canvas.draw Line.new(Point.new(8, 5), Point.new(8, 10))
+ canvas.draw Line.new(Point.new(9, 5), Point.new(12, 5))
+ canvas.draw Line.new(Point.new(9, 7), Point.new(12, 7))
+ canvas.draw Point.new(13, 6)
+ canvas.draw Line.new(Point.new(12, 8), Point.new(13, 10))
+
+ puts canvas.render_as(Renderers::Ascii)
+end