Мария обнови решението на 22.12.2013 15:49 (преди почти 11 години)
+module Graphics
+ class Renderers
+ class Ascii
+ def self.render(canvas)
+ matrix = Array.new(canvas.width * canvas.height, "-")
+ canvas.set_pixels.each { |x, y| matrix[y * canvas.width + x] = "@" }
+ output = ""
+ matrix.each_slice(canvas.width) { |slice| output << slice.join("") << "\n" }
+ output.chomp("\n")
+ end
+ end
+
+ class Html
+ BEGINNING = <<-HTML_BEGINNING
+ <!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">
+ HTML_BEGINNING
+
+ ENDING = <<-HTML_ENDING
+ </div>
+ </body>
+ </html>
+ HTML_ENDING
+
+ def self.render(canvas)
+ matrix = Array.new(canvas.width * canvas.height, "<i></i>")
+ canvas.set_pixels.each { |x, y| matrix[y * canvas.width + x] = "<b></b>" }
+ output = ""
+ matrix.each_slice(canvas.width) { |slice| output << slice.join("") << "<br>" }
+ body = output.chomp("<br>")
+ [BEGINNING, body, ENDING].join("")
+ end
+ end
+ end
+
+ class Canvas
+ attr_reader :width, :height, :set_pixels
+
+ def initialize(width, height)
+ @width = width
+ @height = height
+ @set_pixels = []
+ end
+
+ def set_pixel(x, y)
+ @set_pixels << [x, y]
+ end
+
+ def pixel_at?(x, y)
+ @set_pixels.include? [x, y]
+ end
+
+ def draw(figure)
+ @set_pixels += figure.rasterize
+ end
+
+ def render_as(renderer)
+ renderer.render(self)
+ end
+ end
+
+ class Point
+ attr_reader :x, :y
+
+ def initialize(x, y)
+ @x = x
+ @y = y
+ end
+
+ def coordinates
+ [@x, @y]
+ end
+
+ def rasterize
+ [coordinates]
+ end
+
+ def hash
+ [@x, @y].hash
+ end
+
+ def ==(other)
+ @x == other.x and @y == other.y
+ end
+
+ alias_method :eql?, :==
+ end
+
+ class Bresenham
+ def initialize(line)
+ @x, @y, @line_end = line.from.x, line.from.y, [line.to.x, line.to.y]
+ @pixels = []
+ @dx, @dy = (line.to.x - @x).abs, (line.to.y - @y).abs
+ @signum_x, @signum_y = line.to.x <=> @x, line.to.y <=> @y
+ @error = 2 * @dy - @dx
+ @swap = false
+ end
+
+ def swap
+ if @dx < @dy
+ @dx, @dy = @dy, @dx
+ @swap = true
+ end
+ end
+
+ def next_pixel
+ if @error > 0
+ @swap ? @x += @signum_x : @y += @signum_y
+ @error -= 2 * @dx
+ end
+ @swap ? @y += @signum_y : @x += @signum_x
+ @error += 2 * @dy
+ end
+
+ def rasterize
+ swap
+ @dx.times do
+ @pixels << [@x, @y]
+ next_pixel
+ end
+ @pixels << @line_end
+ end
+ end
+
+ class Line
+ def initialize(from, to)
+ @from = from
+ @to = to
+ end
+
+ def from
+ if @from.x == @to.x
+ @from.y < @to.y ? @from : @to
+ else
+ @from.x < @to.x ? @from : @to
+ end
+ end
+
+ def to
+ if @from.x == @to.x
+ @from.y < @to.y ? @to : @from
+ else
+ @from.x < @to.x ? @to : @from
+ end
+ end
+
+ def hash
+ [from.hash, to.hash].hash
+ end
+
+ def ==(other)
+ from == other.from and to == other.to
+ end
+
+ def rasterize
+ bresenham = Bresenham.new(self)
+ bresenham.rasterize
+ end
+
+ alias_method :eql?, :==
+ end
+
+ class Rectangle
+ attr_reader :left, :right, :top_left
+
+ def initialize(left, right)
+ @left = Line.new(left, right).from
+ @right = Line.new(left, right).to
+ @top_left = Point.new([left.x, right.x].min, [left.y, right.y].min)
+ @width = (left.x - right.x).abs
+ @height = (left.y - right.y).abs
+ end
+
+ def hash
+ [top_left.hash, bottom_right.hash].hash
+ end
+
+ def ==(other)
+ top_left == other.top_left and bottom_right == other.bottom_right
+ end
+
+ def top_right
+ Point.new(@top_left.x + @width, @top_left.y)
+ end
+
+ def bottom_left
+ Point.new(@top_left.x, @top_left.y + @height)
+ end
+
+ def bottom_right
+ Point.new(@top_left.x + @width, @top_left.y + @height)
+ end
+
+ def border
+ lines = Line.new(top_left, top_right).rasterize
+ lines += Line.new(top_left, bottom_left).rasterize
+ lines += Line.new(bottom_left, bottom_right).rasterize
+ lines += Line.new(bottom_right, top_right).rasterize
+ end
+
+ def rasterize
+ if left == right
+ left.coordinates
+ elsif left.x == right.x or left.y == right.y
+ Line.new(left, right).rasterize
+ else
+ border
+ end
+ end
+
+ alias_method :eql?, :==
+ end
+end