Николай обнови решението на 22.12.2013 22:48 (преди почти 11 години)
+module Graphics
+ class Canvas
+ attr_reader :width, :height
+
+ def initialize(width, height)
+ @width, @height = width, height
+ @pixels = Array.new(@height) { |index| Array.new(@width) }
+ end
+
+ def set_pixel(x, y)
+ @pixels[y][x] = true
+ end
+
+ def pixel_at?(x, y)
+ @pixels[y][x]
+ end
+
+ def draw(figure)
+ figure.draw_to self
+ end
+
+ def render_as(renderer)
+ renderer.render self
+ end
+ end
+
+ class Point
+ include Comparable
+
+ attr_reader :x, :y
+
+ def initialize(x, y)
+ @x, @y = x, y
+ end
+
+ def draw_to(canvas)
+ canvas.set_pixel(x, y)
+ end
+
+ def <=>(other)
+ return -1 if self < other
+ return 1 if self > other
+ 0
+ end
+
+ def <(other)
+ x < other.x or (x.eql? other.x and y < other.y)
+ end
+
+ def >(other)
+ other < self
+ end
+
+ def eql?(other)
+ x.eql? other.x and y.eql? other.y
+ end
+
+ alias :== :eql?
+ end
+
+ class Line
+ attr_reader :from, :to
+
+ def initialize(from, to)
+ @from, @to = [from, to].minmax
+ end
+
+ def draw_to(canvas)
+ x_1, y_1, x_2, y_2 = from.x, from.y, to.x, to.y
+
+ steep = (y_2 - y_1).abs > (x_2 - x_1).abs
+
+ x_1, y_1, x_2, y_2 = y_1, x_1, y_2, x_2 if steep
+ x_1, x_2, y_1, y_2 = x_2, x_1, y_2, y_1 if x_1 > x_2
+
+ bresenham(canvas, x_1, y_1, x_2, y_2, steep)
+ end
+
+ def eql?(other)
+ from.eql? other.from and to.eql? other.to
+ end
+
+ alias :== :eql?
+
+ private
+ def bresenham(canvas, x_1, y_1, x_2, y_2, steep)
+ y, error, step_y = y_1, ((x_2 - x_1) / 2), (y_1 < y_2 ? 1 : -1)
+ x_1.upto(x_2) do |x|
+ canvas.draw Point.new(steep ? y : x, steep ? x : y)
+ if (error -= (y_2 - y_1).abs) < 0
+ y += step_y
+ error += (x_2 - x_1)
+ end
+ end
+ end
+ end
+
+ class Rectangle
+ attr_reader :left, :right
+ attr_reader :top_left, :top_right, :bottom_left, :bottom_right
+
+ def initialize(left, right)
+ @left, @right = [left, right].minmax
+
+ @top_left = Point.new([left.x, right.x].min, [left.y, right.y].min)
+ @top_right = Point.new([left.x, right.x].min, [left.y, right.y].max)
+ @bottom_left = Point.new([left.x, right.x].max, [left.y, right.y].min)
+ @bottom_right = Point.new([left.x, right.x].max, [left.y, right.y].max)
+ end
+
+ def draw_to(canvas)
+ canvas.draw(Line.new(top_left, top_right))
+ canvas.draw(Line.new(bottom_left, bottom_right))
+ canvas.draw(Line.new(top_left, bottom_left))
+ canvas.draw(Line.new(top_right, bottom_right))
+ end
+
+ def eql?(other)
+ top_left.eql? other.top_left and bottom_right.eql? other.bottom_right
+ end
+
+ alias :== :eql?
+ end
+
+ module Renderers
+ class Renderer
+ def initialize(template, full_pixel, empty_pixel, line_break)
+ @template = template
+ @full_pixel = full_pixel
+ @empty_pixel = empty_pixel
+ @line_break = line_break
+ end
+
+ def render(canvas)
+ @template % [render_pixels(canvas)]
+ end
+
+ private
+ def render_pixels(canvas)
+ (0...(canvas.height)).map do |y|
+ (0...(canvas.width)).map do |x|
+ render_pixel(canvas.pixel_at?(x, y))
+ end.join
+ end.join(@line_break)
+ end
+
+ def render_pixel(pixel)
+ pixel ? @full_pixel : @empty_pixel
+ end
+ end
+
+ class Ascii
+ @@renderer = Renderer.new("%s", '@', '-', "\n")
+ def self.render(canvas)
+ @@renderer.render(canvas)
+ end
+ end
+
+ class Html
+ @@renderer = Renderer.new('
+<!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">
+ %s
+ </div>
+</body>
+</html>
+',
+"<b></b>", "<i></i>", "<br>")
+ def self.render(canvas)
+ @@renderer.render(canvas)
+ end
+ end
+ end
+end