Христо обнови решението на 22.12.2013 20:18 (преди почти 11 години)
+module Graphics
+ class Canvas
+ attr_accessor :width, :height
+
+ def initialize width, height
+ @width = width
+ @height = height
+ @canvas = Array.new(height) { Array.new(width, '0') }
+ end
+
+ def set_pixel x, y
+ @canvas[y][x] = '1'
+ end
+
+ def pixel_at? x, y
+ @canvas[y][x].eql? '1'
+ end
+
+ def draw figure
+ return set_pixel(figure.x, figure.y) if figure.instance_of?(Point)
+ figure.points.each do |point|
+ set_pixel(point.x, point.y)
+ end
+ end
+
+ def render_as renderer
+ result = 'BEFORE'
+ result += @canvas.map { |row| row.join('') }.join("\n")
+ result += 'AFTER'
+ result.gsub(/(BEFORE|AFTER|1|0|\n)/, renderer.render_rules)
+ end
+ end
+
+ class Point
+ include Comparable
+ attr_accessor :x, :y
+
+ def initialize x, y
+ @x = x
+ @y = y
+ end
+
+ def <=>(other)
+ x == other.x ? y <=> other.y : x <=> other.x
+ end
+
+ def hash
+ [@x, @y].hash
+ end
+
+ def ==(other)
+ other.class == self.class and other.x == x and other.y == y
+ end
+ alias_method :eql?, :==
+ end
+
+ class Line
+ attr_accessor :from, :to
+
+ def initialize point_a, point_b
+ @from = [point_a, point_b].min
+ @to = [point_a, point_b].max
+ end
+
+ def points
+ algorithm = Bresenham.new(from, to)
+ algorithm.draw_line
+ end
+
+ def hash
+ [@from, @to].hash
+ end
+
+ def ==(other)
+ other.class == self.class and other.points == points
+ end
+ alias_method :eql?, :==
+ end
+
+ class Rectangle
+ def initialize point_a, point_b
+ @point_a = point_a
+ @point_b = point_b
+ end
+
+ def left
+ [@point_a, @point_b].min
+ end
+
+ def right
+ [@point_a, @point_b].max
+ end
+
+ def up
+ @point_a.y > @point_b.y ? @point_a : @point_b
+ end
+
+ def down
+ @point_a.y < @point_b.y ? @point_a : @point_b
+ end
+
+ def top_left
+ points.first
+ end
+
+ def top_right
+ points.group_by(&:x).max[1].first
+ end
+
+ def bottom_left
+ points.group_by(&:y).max[1].first
+ end
+
+ def bottom_right
+ points.last
+ end
+
+ def points
+ (
+ (left.x..right.x).map { |x| [Point.new(x, right.y), Point.new(x, left.y)] } +
+ (down.y..up.y) .map { |y| [Point.new(down.x, y), Point.new(up.x, y)] }
+ ).flatten.uniq.sort
+ end
+
+ def hash
+ [@point_a, @point_b].hash
+ end
+
+ def ==(other)
+ other.class == self.class and other.points.sort == points.sort
+ end
+ alias_method :eql?, :==
+
+ private :up, :down
+ end
+
+ class Bresenham
+ def initialize from, to
+ @start_x, @start_y = from.x, from.y
+ @end_x, @end_y = to.x, to.y
+ @delta_x, @delta_y = (to.x - from.x).abs, -(to.y - from.y).abs
+ @step_x, @step_y = from.x < to.x ? 1 : -1, from.y < to.y ? 1 : -1
+ @error = @delta_x + @delta_y
+ end
+
+ def go_x_axis
+ @error += @delta_y
+ @start_x += @step_x
+ end
+
+ def go_y_axis
+ @error += @delta_x
+ @start_y += @step_y
+ end
+
+ def draw_line
+ points = [Point.new(@start_x, @start_y)]
+ while @start_x != @end_x or @start_y != @end_y
+ go_x_axis if @error * 2 >= @delta_y
+ go_y_axis if @error * 2 <= @delta_x
+ points << Point.new(@start_x, @start_y)
+ end
+ points
+ end
+ end
+
+ module Renderers
+
+ class Ascii
+ def self.render_rules
+ {"0" => "-", "1" => "@", "\n" => "\n", "BEFORE" => '', "AFTER" => '' }
+ end
+ end
+
+ class Html
+ def self.render_rules
+ html = {"0" => "<i></i>", "1" => "<b></b>", "\n" => "<br>" }
+ html['BEFORE'] = <<-BEFORE
+<!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">
+BEFORE
+ html['AFTER'] = <<-AFTER
+ </div>
+</body>
+</html>
+AFTER
+ html
+ end
+ end
+ end
+end