Антонио обнови решението на 18.12.2013 15:25 (преди почти 11 години)
+module Graphics
+ class Canvas
+ def initialize(width, height)
+ @width = width
+ @height = height
+ @pixels = []
+ end
+
+ attr_reader :width, :height
+
+ def set_pixel(x, y)
+ @pixels << [x, y] unless @pixels.include? [x, y]
+ end
+
+ def pixel_at?(x, y)
+ @pixels.include? [x, y]
+ end
+
+ def draw(form)
+ form.draw(self)
+ end
+
+ def render_as(renderer)
+ renderer.new.render_as(self)
+ end
+ end
+
+ module Renderers
+ class Ascii
+ def render_as(sail)
+ panel = ""
+ 0.upto(sail.width * sail.height - 1) do |x|
+ panel += "\n" if (x).remainder(sail.width).zero? and not x == 0
+ panel += '-'
+ panel[panel.length - 1] = '@' if sail.pixel_at? x % sail.width, x / sail.width
+ end
+ panel
+ end
+ end
+
+ class Html
+ def set_sail(sail, count, pixel = "")
+ pixel += "<br>\n" if (count).remainder(sail.width).zero? and not count == 0
+ if sail.pixel_at? count % sail.width, count / sail.width
+ pixel += "<b></b>"
+ else
+ pixel += "<i></i>"
+ end
+ pixel
+ end
+
+ def render_as(sail)
+ panel = "
+ <!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\">\n"
+ 0.upto(sail.width * sail.height - 1) do |x|
+ panel += set_sail(sail, x)
+ end
+ panel += "
+ </div>
+ </body>
+ </html>"
+ end
+ end
+ end
+
+ class Point
+ def initialize(x, y)
+ @x = x
+ @y = y
+ end
+
+ attr_reader :x, :y
+
+ def ==(point)
+ x == point.x and y == point.y
+ end
+
+ def eql?(point)
+ x.eql? point.x and y.eql? point.y
+ end
+
+ def draw(sail)
+ sail.set_pixel(x, y)
+ end
+
+ def hash
+ [x, y].hash
+ end
+ end
+
+ class Line
+ def initialize(from_point, to_point)
+ @from_point = from_point
+ @to_point = to_point
+ end
+
+ def from
+ if @from_point.x == @to_point.x
+ if @from_point.y < @to_point.y
+ return @from_point
+ else
+ return @to_point
+ end
+ end
+ @from_point.x < @to_point.x ? @from_point : @to_point
+ end
+
+ def to
+ if @from_point.x == @to_point.x
+ if @from_point.y > @to_point.y
+ return @from_point
+ else
+ return @to_point
+ end
+ end
+ @from_point.x > @to_point.x ? @from_point : @to_point
+ end
+
+ def ==(line)
+ from == line.from and to == line.to
+ end
+
+ def eql?(line)
+ from.eql? line.from and to.eql? line.to
+ end
+
+ def set_draw_y(sail, length_x, length_y)
+ from.y.upto(to.y) do |y|
+ x = (from.x + length_x * (y - from.y) / length_y).abs
+ sail.set_pixel(x, y)
+ end
+ end
+
+ def set_draw_x(sail, length_x, length_y)
+ from.x.upto(to.x) do |x|
+ y = (from.y + length_y * (x - from.x) / length_x).abs
+ sail.set_pixel(x, y)
+ end
+ end
+
+ def draw(sail)
+ length_x, length_y = to.x - from.x, to.y - from.y
+ if length_x == 0 || length_y >= length_x
+ set_draw_y(sail, length_x, length_y)
+ else
+ set_draw_x(sail, length_x, length_y)
+ end
+ end
+
+ def hash
+ [from.x, from.y, to.x, to.y].hash
+ end
+ end
+
+ class Rectangle
+ def initialize(left_point, right_point)
+ @left_point = left_point
+ @right_point = right_point
+ end
+
+ def left
+ if @left_point.x == @right_point.x
+ if @left_point.y < @right_point.y
+ return @left_point
+ else
+ return @right_point
+ end
+ end
+ @left_point.x < @right_point.x ? @left_point : @right_point
+ end
+
+ def right
+ if @left_point.x == @right_point.x
+ if @left_point.y > @right_point.y
+ return @left_point
+ else
+ return @right_point
+ end
+ end
+ @left_point.x > @right_point.x ? @left_point : @right_point
+ end
+
+ def top_left
+ if left.y < right.y
+ left
+ else
+ Point.new left.x, right.y
+ end
+ end
+
+ def top_right
+ if left.y < right.y
+ Point.new right.x, left.y
+ else
+ right
+ end
+ end
+
+ def bottom_left
+ if left.y < right.y
+ Point.new left.x, right.y
+ else
+ left
+ end
+ end
+
+ def bottom_right
+ if left.y < right.y
+ right
+ else
+ Point.new left.x, right.y
+ end
+ end
+
+ def ==(rectangle)
+ top_left == rectangle.top_left and bottom_right == rectangle.bottom_right
+ end
+
+ def eql?(rectangle)
+ top_left.eql? rectangle.top_left and bottom_right.eql? rectangle.bottom_right
+ end
+
+ def draw(sail)
+ sail.draw Line.new(top_left, top_right)
+ sail.draw Line.new(top_left, bottom_left)
+ sail.draw Line.new(bottom_left, bottom_right)
+ sail.draw Line.new(top_right, bottom_right)
+ end
+
+ def hash
+ [top_left.x, top_left.y, bottom_right.x, bottom_right.y, bottom_left.x].hash
+ end
+ end
+end
Бележки:
- Помисли дали няма друг по-оптимален начин в Ruby за реализация вътрешното представяне на пано от двумерен масив. Това линейно търсене за проверка дали пиксел е set-нат, е ненужно и лесно може да бъде избегнато.
-
form
не ми се струва като особено добро име; може би си искал да кажешshape
илиfigure
? - Мисля, че методът
render_as
на рендерер не е кръстен удачно. Потърси по-подходящо име. - Не мога да повярвам, че виждам платно (sail) в рендерерите :)) Какво ще го правиш това платно :) Ако си искал да кажеш "пано", можеш спокойно да ползваш или "canvas", което така или иначе е част от домейн-терминологията вече, или поне нещо от рода на
pane
. - Мисля, че ще е по-удачно ако обикаляш по височина и ширина на дадено пано, вместо да правиш акробатики с аритметиката, които, на всичкото отгоре, се дублират в двата рендерера. Рефакторирай и изсуши малко тази част. Пробвай да ползваш
map
иjoin
там, също така. - "To set sail" означава "да отплавам" :)
- По-оптимално (а и по-четимо) е да не дефинираш HTML низовете в
render_as
, а да ги изведеш като константи вHtml
. Ползвай други кавички/ограничител, за да ги дефинираш тези низове, за да не трябва да ги екранираш вътре. - Имаш дребни проблеми с идентацията на места. Оправи ги (напр. редове 92 и 93). Ако останат във финалното решение, ще ти взема точка за наказание.
- Имаш дублираща се логика в
==
иeql?
във фигурите; условието е такова, че позволява и прави удачно да се ползват синоними на методи тук. - Добре си се оправил с
Point#hash
. - Рефакторирай
Line#from
иto
. Не искам да виждамreturn
там. - В
Line#hash
може да се възползваш от факта, че имашPoint#hash
. - Поработи над имената на методите, които ти участват в растеризацията на линии. Не са достатъчно добри.
- Почти същите забележки имам и за
Rectangle
.
Иначе стилът ти е що-годе приличен и откъм дизайн си на прав път.