Решение на Трета задача от Диан Николов

Обратно към всички решения

Към профила на Диан Николов

Резултати

  • 6 точки от тестове
  • 0 бонус точки
  • 6 точки общо
  • 67 успешни тест(а)
  • 2 неуспешни тест(а)

Код

module Graphics
module Renderers
class Html
HTML_HEADER = "<!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"
HTML_FOOTER = " </div>
</body>
</html>\n"
def render(canvas)
HTML_HEADER + (0...canvas.height).to_a.map do |y|
(0...canvas.width).to_a.map do |x|
pixel_to_html canvas, x, y
end.join
end.join("<br>\n") + HTML_FOOTER
end
private
def pixel_to_html(canvas, x, y)
canvas.pixel_at?(x, y) ? "<b></b>" : "<i></i>"
end
end
class Ascii
def render(canvas)
(0...canvas.height).to_a.map do |y|
(0...canvas.width).to_a.map do |x|
pixel_to_ascii canvas, x, y
end.join
end.join "\n"
end
private
def pixel_to_ascii(canvas, x, y)
canvas.pixel_at?(x, y) ? "@" : "-"
end
end
end
class Point
attr_reader :x, :y, :plot
def initialize(x, y)
@x = x
@y = y
@plot = [self]
end
def ==(point)
@x == point.x and @y == point.y
end
alias_method :eql?, :==
def hash
[@x, @y].hash
end
end
class Line
attr_reader :from, :to, :plot
def initialize(point_a, point_b)
initialize_line_ends(point_a, point_b)
@plot = []
point_a.x != point_b.x ? plot_standard : plot_vertical
end
def ==(line)
@from == line.from and @to == line.to
end
alias_method :eql?, :==
def hash
[@from, @to].hash
end
private
def initialize_line_ends(point_a, point_b)
if point_a.x != point_b.x
@from = point_a.x <= point_b.x ? point_a : point_b
@to = point_a.x > point_b.x ? point_a : point_b
else
@from = point_a.y <= point_b.y ? point_a : point_b
@to = point_a.y > point_b.y ? point_a : point_b
end
end
def plot_standard
error_delta = ((@to.y - @from.y).to_f / (@to.x - @from.x).to_f).abs
error, y = 0, @from.y
(@from.x..@to.x).each do |x|
@plot << Point.new(x, y)
error, y = new_error_and_ordinate error, error_delta, y
end
@plot += Line.new(Point.new(@to.x, y), @to).plot
end
def new_error_and_ordinate(error, error_delta, y)
error += error_delta
if error >= 0.5
@from.y < @to.y ? y += 1 : y -= 1
error -= 1.0
end
return error, y
end
def plot_vertical
(@from.y..@to.y).each { |y| @plot << Point.new(@from.x, y) }
end
end
class Rectangle
attr_reader :left, :right, :plot
attr_reader :top_left, :top_right, :bottom_left, :bottom_right
def initialize(point_a, point_b)
@plot = []
initialize_ends point_a, point_b
create_ends
plot_pixels
end
def ==(rectangle)
@left == rectangle.left and @right == rectangle.right
end
alias_method :eql?, :==
def hash
[@top_left, @top_right, @bottom_left, @bottom_right].hash
end
private
def initialize_ends(point_a, point_b)
if point_a.x != point_b.x
@left = point_a.x <= point_b.x ? point_a : point_b
@right = point_a.x > point_b.x ? point_a : point_b
else
@left = point_a.y <= point_b.y ? point_a : point_b
@right = point_a.y > point_b.y ? point_a : point_b
end
end
def create_ends
left_x = Point.new(@left.x, @right.y)
right_x = Point.new(@right.x, @left.y)
@top_left = @left.y <= left_x.y ? @left : left_x
@bottom_left = @left.y > left_x.y ? @left : left_x
@top_right = @right.y <= right_x.y ? @right : right_x
@bottom_right = @right.y > right_x.y ? @right : right_x
end
def plot_pixels
@plot = [
@top_left, @top_right,
@top_right, @bottom_right,
@bottom_right, @bottom_left,
@bottom_left, @top_left,
].each_slice(2).map { |a, b| Line.new(a, b).plot }.flatten
end
end
class Canvas
attr_reader :width, :height
def initialize(width, height)
@width = width
@height = height
@pixel_matrix = Hash.new
end
def set_pixel(x, y)
@pixel_matrix[Point.new(x, y)] = true
end
def pixel_at?(x, y)
@pixel_matrix[Point.new(x, y)]
end
def draw(object)
object.plot.each { |pixel| set_pixel(pixel.x, pixel.y) }
end
def render_as(renderer_type)
renderer = renderer_type.new
renderer.render self
end
end
end

Лог от изпълнението

..............F.............................................F........

Failures:

  1) Graphics Canvas drawing of shapes and rasterization of lines works with lines with a significant slope, with swapped ends
     Failure/Error: ascii.should eq rendering(expected)
       
       expected: "----------\n-@--------\n-@--------\n--@-------\n--@-------\n--@-------\n--@-------\n---@------\n---@------\n----------"
            got: "----------\n-@--------\n--@-------\n---@------\n---@------\n---@------\n---@------\n---@------\n---@------\n----------"
       
       (compared using ==)
       
       Diff:
       
       @@ -1,10 +1,10 @@
        ----------
        -@--------
       --@--------
        --@-------
       ---@-------
       ---@-------
       ---@-------
       +---@------
       +---@------
       +---@------
       +---@------
        ---@------
        ---@------
        ----------
     # /tmp/d20131223-4637-3p1yj3/spec.rb:624:in `check_rendering_of'
     # /tmp/d20131223-4637-3p1yj3/spec.rb:113:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  2) Graphics shapes Rectangle comparison for equality is true for rectangles defined with different diagonal corners
     Failure/Error: (a == b).should be_true
       expected: true value
            got: false
     # /tmp/d20131223-4637-3p1yj3/spec.rb:540:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

Finished in 0.0923 seconds
69 examples, 2 failures

Failed examples:

rspec /tmp/d20131223-4637-3p1yj3/spec.rb:109 # Graphics Canvas drawing of shapes and rasterization of lines works with lines with a significant slope, with swapped ends
rspec /tmp/d20131223-4637-3p1yj3/spec.rb:536 # Graphics shapes Rectangle comparison for equality is true for rectangles defined with different diagonal corners

История (2 версии и 2 коментара)

Диан обнови решението на 20.12.2013 21:19 (преди над 10 години)

+module Graphics
+ module Renderers
+ class Html
+ end
+
+ class Ascii
+ end
+
+ HTML_PREFIX = "<!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"
+ HTML_SUFFIX = " </div>
+ </body>
+ </html>\n"
+ end
+ class Point
+ attr_reader :x, :y, :pixel_container
+ def initialize(x, y)
+ @x = x
+ @y = y
+ @pixel_container = Array.new
+ @pixel_container << self
+ end
+
+ def ==(point)
+ @x == point.x and @y == point.y
+ end
+
+ alias_method :eql?, :==
+
+ def hash
+ [@x, @y].hash
+ end
+
+ def get_min_x(point)
+ if @x <= point.x then self else point end
+ end
+
+ def get_min_y(point)
+ if @y <= point.y then self else point end
+ end
+
+ def get_max_x(point)
+ if @x > point.x then self else point end
+ end
+
+ def get_max_y(point)
+ if @y > point.y then self else point end
+ end
+ end
+
+ class Line
+ attr_reader :from, :to, :pixel_container
+ def initialize(point_a, point_b)
+ initialize_line_ends(point_a, point_b)
+ @pixel_container = Array.new
+ if point_a.x != point_b.x then generate_standard else generate_vertical end
+ end
+
+ def ==(line)
+ @from == line.from and @to == line.to
+ end
+
+ alias_method :eql?, :==
+
+ def hash
+ [@from, @to].hash
+ end
+
+ private
+ def initialize_line_ends(point_a, point_b)
+ if point_a.x != point_b.x
+ @from = point_a.get_min_x point_b
+ @to = point_a.get_max_x point_b
+ else
+ @from = point_a.get_min_y point_b
+ @to = point_a.get_max_y point_b
+ end
+ end
+
+ def generate_standard
+ delta_error = (Float(@to.y - @from.y) / Float(@to.x - @from.x)).abs
+ error, y = 0, @from.y
+ (@from.x..@to.x).each do |x|
+ @pixel_container << Point.new(x, y)
+ error, y = get_new_error_and_ordinate error, delta_error, y
+ end
+ @pixel_container += Line.new(Point.new(@to.x, y), @to).pixel_container
+ end
+
+ def get_new_error_and_ordinate(error, delta_error, y)
+ error += delta_error
+ if error >= 0.5
+ if @from.y < @to.y then y += 1 else y -= 1 end
+ error -= 1.0
+ end
+ return error, y
+ end
+
+ def generate_vertical
+ (@from.y..@to.y).each do |y|
+ @pixel_container << Point.new(@from.x, y)
+ end
+ end
+ end
+
+ class Rectangle
+ attr_reader :left, :right, :pixel_container
+ attr_reader :top_left, :top_right, :bottom_left, :bottom_right
+ def initialize(point_a, point_b)
+ @pixel_container = Array.new
+ initialize_ends(point_a, point_b)
+ create_ends
+ generate_pixels
+ end
+
+ def ==(rectangle)
+ @top_left == rectangle.top_left and
+ @top_right == rectangle.top_right and
+ @bottom_left == rectangle.bottom_left and
+ @bottom_right == rectangle.bottom_right
+ end
+
+ alias_method :eql?, :==
+
+ def hash
+ [@top_left, @top_right, @bottom_left, @bottom_right].hash
+ end
+
+ private
+ def initialize_ends(point_a, point_b)
+ if point_a.x != point_b.x
+ @left = point_a.get_min_x point_b
+ @right = point_a.get_max_x point_b
+ else
+ @left = point_a.get_min_y point_b
+ @right = point_a.get_max_y point_b
+ end
+ end
+
+ def create_ends
+ left_x = Point.new(@left.x, @right.y)
+ right_x = Point.new(@right.x, @left.y)
+
+ @top_left = @left.get_min_y left_x
+ @bottom_left = @left.get_max_y left_x
+
+ @top_right = @right.get_min_y right_x
+ @bottom_right = @right.get_max_y right_x
+ end
+
+ def generate_pixels
+ @pixel_container += Line.new(@top_left, @bottom_left).pixel_container
+ @pixel_container += Line.new(@bottom_left, @bottom_right).pixel_container
+ @pixel_container += Line.new(@bottom_right, @top_right).pixel_container
+ @pixel_container += Line.new(@top_right, @top_left).pixel_container
+ end
+ end
+
+ class ArrayTwoDimensional
+ def initialize(first_dimension, second_dimension)
+ @data = Array.new(first_dimension) { Array.new(second_dimension) }
+ end
+
+ def [](x, y)
+ @data[x][y]
+ end
+
+ def []=(x, y, value)
+ @data[x][y] = value
+ end
+ end
+
+ class Canvas
+ attr_reader :width, :height
+ def initialize(width, height)
+ @width = width
+ @height = height
+ @pixel_matrix = ArrayTwoDimensional.new(width, height)
+ end
+
+ def set_pixel(x, y)
+ @pixel_matrix[x, y] = 1
+ end
+
+ def pixel_at?(x, y)
+ nil != @pixel_matrix[x, y]
+ end
+
+ def draw(object)
+ object.pixel_container.each { |pixel| set_pixel(pixel.x, pixel.y) }
+ end
+
+ def render_as(renderer_type)
+ if renderer_type == Renderers::Ascii
+ render_as_ascii
+ else
+ render_as_html
+ end
+ end
+
+ private
+ def render_as_ascii
+ ascii_string = ""
+ (0...@height).each do |y|
+ (0...@width).each do |x|
+ ascii_string += ascii_pixel_check(x, y)
+ end
+ ascii_string += "\n" if y < @height - 1
+ end
+ ascii_string
+ end
+
+ def ascii_pixel_check(x, y)
+ if pixel_at?(x, y)
+ "@"
+ else
+ "-"
+ end
+ end
+
+ def render_as_html
+ html_string = Renderers::HTML_PREFIX
+ (0...@height).each do |y|
+ (0...@width).each do |x|
+ html_string += html_pixel_check(x, y)
+ end
+ html_string += "<br>\n" if y < @height - 1
+ end
+ html_string += Renderers::HTML_SUFFIX
+ end
+
+ def html_pixel_check(x, y)
+ if pixel_at?(x, y)
+ "<b></b>"
+ else
+ "<i></i>"
+ end
+ end
+ end
+end

Бележки:

  • В Ruby няма практика да има думата get_ в името на методите; тази дума просто генерира шум и не носи информация.
  • Лош стил е да се създава списък с Array.new, когато може да се напише просто []. Пак там, спокойно можеш да напишеш @pixel_container = [self].
  • Трябва да има нов ред след attr_reader дефинициите, по конвенция.
  • Добре си се справил с реализацията на hash методите.
  • Използваш синоними на методи по подходящ начин в случая.
  • HTML кодът е добре сложен в константи, но тези константи е най-подходящо да се намират в класа Html.
  • "Prefix" и "suffix" са по-неподходящи имена, отколкото "header" и "footer" за HTML кода в тази задача.
  • pixel_container не ми се струва като добро име; потърси по-подходящо.
  • Не ми се струва добра идея да имаш методи Point#get_min_x, get_min_y и т.н. По-скоро ползвай елементарната им логика там, където ти трябва, директно, или намери друг начин да си свършиш работата (ternary оператор, [x, y].min/max, ...)
  • Според нашите конвенции, след private се оставя един празен ред и методите не се вкарват едно допълнително ниво на идентация навътре.
  • generate_pixels може да се напише така:

      [
        @top_left,     @top_right,
        @top_right,    @bottom_right,
        @bottom_right, @bottom_left,
        @bottom_left,  @top_left,
      ].each_slice(2).map { |a, b| Line.new(a, b).pixel_container }.flatten
    

    И след това да се кръсти направо pixel_container (или каквото ново име си измислил за това).

  • Рядко се ползва if then else end конструкцията. Предпочитай нормални if/else/end на няколко реда. В краен случай, за много прости изрази, може и ternary оператора condition ? foo : bar.
  • Вместо думата "generate", ми се струва, че ще е по-удачно да ползваш нещо друго. Например "rasterize". Имаш възможност да подобриш имена и на други места, въпреки, че виждам, че се стараеш да именуваш добре и някъде се е получило. Просто винаги може повече.
  • Мисля, че "delta_error" е по-лошо име от "error_delta". Не прави int -> float така. Ползвай to_f метода на числата.
  • Нека да напомня, че празните редове не се броят от skeptic и е добра практика да се слагат на места в методи, за да разделят блокове от код.
  • Има ли смисъл да проверяваш за съвпадение на всички ъгли в сравнението на правоъгълници?
  • Помисли дали няма друг по-оптимален начин в Ruby за реализация вътрешното представяне на пано от масив от масиви. Ruby не е C и има по-удобни структури от данни :) Тогава ще отпадне и нуждата от този странен ArrayTwoDimentional клас.
  • Също така, pixel_at? може да връща нещо, което се оценява като истина или лъжа; не е задължително да е true/false. От друга страна, смятам, че е по-добре да ползваш true, за да кажеш "има нещо", отколкото 1.
  • Пробвай да направиш методите за рендериране с map и join.
  • Името html_pixel_check не е добро. Не изразява точно какво прави методът. Потърси друго.
  • Помисли кой трбява да носи отговорността за "рендериране" на пано. Самото пано или рендерера? Помисли кой обект каква отговорност носи. Една основна идея на ОО-програмирането е да разпределиш различните отговорности между различни обекти. Стреми се всеки обект да отговаря за едно конкретно нещо и само за него. Напълно нормално е да имаш голям брой малки обекти в системата, с тясно специализирани отговорности. Казвам това в контекста на празните класове Html и Ascii.

Общото ми впечатление е, че си на правилна посока, но има нужда да подаботиш над стила си, следването на конвенциите, именуването на променливи и стремежа към по-идиоматичен Ruby код.

Диан обнови решението на 22.12.2013 20:26 (преди над 10 години)

module Graphics
module Renderers
class Html
+ HTML_HEADER = "<!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"
+ HTML_FOOTER = " </div>
+ </body>
+ </html>\n"
+
+ def render(canvas)
+ HTML_HEADER + (0...canvas.height).to_a.map do |y|
+ (0...canvas.width).to_a.map do |x|
+ pixel_to_html canvas, x, y
+ end.join
+ end.join("<br>\n") + HTML_FOOTER
+ end
+
+ private
+
+ def pixel_to_html(canvas, x, y)
+ canvas.pixel_at?(x, y) ? "<b></b>" : "<i></i>"
+ end
end
class Ascii
- end
+ def render(canvas)
+ (0...canvas.height).to_a.map do |y|
+ (0...canvas.width).to_a.map do |x|
+ pixel_to_ascii canvas, x, y
+ end.join
+ end.join "\n"
+ end
- HTML_PREFIX = "<!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"
- HTML_SUFFIX = " </div>
- </body>
- </html>\n"
+ private
+
+ def pixel_to_ascii(canvas, x, y)
+ canvas.pixel_at?(x, y) ? "@" : "-"
+ end
+ end
end
class Point
- attr_reader :x, :y, :pixel_container
+ attr_reader :x, :y, :plot
+
def initialize(x, y)
@x = x
@y = y
- @pixel_container = Array.new
- @pixel_container << self
+ @plot = [self]
end
def ==(point)
@x == point.x and @y == point.y
end
alias_method :eql?, :==
def hash
[@x, @y].hash
end
-
- def get_min_x(point)
- if @x <= point.x then self else point end
- end
-
- def get_min_y(point)
- if @y <= point.y then self else point end
- end
-
- def get_max_x(point)
- if @x > point.x then self else point end
- end
-
- def get_max_y(point)
- if @y > point.y then self else point end
- end
end
class Line
- attr_reader :from, :to, :pixel_container
+ attr_reader :from, :to, :plot
+
def initialize(point_a, point_b)
initialize_line_ends(point_a, point_b)
- @pixel_container = Array.new
- if point_a.x != point_b.x then generate_standard else generate_vertical end
+ @plot = []
+ point_a.x != point_b.x ? plot_standard : plot_vertical
end
def ==(line)
@from == line.from and @to == line.to
end
alias_method :eql?, :==
def hash
[@from, @to].hash
end
private
- def initialize_line_ends(point_a, point_b)
- if point_a.x != point_b.x
- @from = point_a.get_min_x point_b
- @to = point_a.get_max_x point_b
- else
- @from = point_a.get_min_y point_b
- @to = point_a.get_max_y point_b
- end
- end
- def generate_standard
- delta_error = (Float(@to.y - @from.y) / Float(@to.x - @from.x)).abs
- error, y = 0, @from.y
- (@from.x..@to.x).each do |x|
- @pixel_container << Point.new(x, y)
- error, y = get_new_error_and_ordinate error, delta_error, y
- end
- @pixel_container += Line.new(Point.new(@to.x, y), @to).pixel_container
+ def initialize_line_ends(point_a, point_b)
+ if point_a.x != point_b.x
+ @from = point_a.x <= point_b.x ? point_a : point_b
+ @to = point_a.x > point_b.x ? point_a : point_b
+ else
+ @from = point_a.y <= point_b.y ? point_a : point_b
+ @to = point_a.y > point_b.y ? point_a : point_b
end
+ end
- def get_new_error_and_ordinate(error, delta_error, y)
- error += delta_error
- if error >= 0.5
- if @from.y < @to.y then y += 1 else y -= 1 end
- error -= 1.0
- end
- return error, y
+ def plot_standard
+ error_delta = ((@to.y - @from.y).to_f / (@to.x - @from.x).to_f).abs
+ error, y = 0, @from.y
+
+ (@from.x..@to.x).each do |x|
+ @plot << Point.new(x, y)
+ error, y = new_error_and_ordinate error, error_delta, y
end
- def generate_vertical
- (@from.y..@to.y).each do |y|
- @pixel_container << Point.new(@from.x, y)
- end
+ @plot += Line.new(Point.new(@to.x, y), @to).plot
+ end
+
+ def new_error_and_ordinate(error, error_delta, y)
+ error += error_delta
+ if error >= 0.5
+ @from.y < @to.y ? y += 1 : y -= 1
+ error -= 1.0
end
+ return error, y
+ end
+
+ def plot_vertical
+ (@from.y..@to.y).each { |y| @plot << Point.new(@from.x, y) }
+ end
end
class Rectangle
- attr_reader :left, :right, :pixel_container
+ attr_reader :left, :right, :plot
attr_reader :top_left, :top_right, :bottom_left, :bottom_right
+
def initialize(point_a, point_b)
- @pixel_container = Array.new
- initialize_ends(point_a, point_b)
+ @plot = []
+ initialize_ends point_a, point_b
create_ends
- generate_pixels
+ plot_pixels
end
def ==(rectangle)
- @top_left == rectangle.top_left and
- @top_right == rectangle.top_right and
- @bottom_left == rectangle.bottom_left and
- @bottom_right == rectangle.bottom_right
+ @left == rectangle.left and @right == rectangle.right
end
alias_method :eql?, :==
def hash
[@top_left, @top_right, @bottom_left, @bottom_right].hash
end
private
- def initialize_ends(point_a, point_b)
- if point_a.x != point_b.x
- @left = point_a.get_min_x point_b
- @right = point_a.get_max_x point_b
- else
- @left = point_a.get_min_y point_b
- @right = point_a.get_max_y point_b
- end
- end
- def create_ends
- left_x = Point.new(@left.x, @right.y)
- right_x = Point.new(@right.x, @left.y)
-
- @top_left = @left.get_min_y left_x
- @bottom_left = @left.get_max_y left_x
-
- @top_right = @right.get_min_y right_x
- @bottom_right = @right.get_max_y right_x
+ def initialize_ends(point_a, point_b)
+ if point_a.x != point_b.x
+ @left = point_a.x <= point_b.x ? point_a : point_b
+ @right = point_a.x > point_b.x ? point_a : point_b
+ else
+ @left = point_a.y <= point_b.y ? point_a : point_b
+ @right = point_a.y > point_b.y ? point_a : point_b
end
+ end
- def generate_pixels
- @pixel_container += Line.new(@top_left, @bottom_left).pixel_container
- @pixel_container += Line.new(@bottom_left, @bottom_right).pixel_container
- @pixel_container += Line.new(@bottom_right, @top_right).pixel_container
- @pixel_container += Line.new(@top_right, @top_left).pixel_container
- end
- end
+ def create_ends
+ left_x = Point.new(@left.x, @right.y)
+ right_x = Point.new(@right.x, @left.y)
- class ArrayTwoDimensional
- def initialize(first_dimension, second_dimension)
- @data = Array.new(first_dimension) { Array.new(second_dimension) }
- end
+ @top_left = @left.y <= left_x.y ? @left : left_x
+ @bottom_left = @left.y > left_x.y ? @left : left_x
- def [](x, y)
- @data[x][y]
+ @top_right = @right.y <= right_x.y ? @right : right_x
+ @bottom_right = @right.y > right_x.y ? @right : right_x
end
- def []=(x, y, value)
- @data[x][y] = value
+ def plot_pixels
+ @plot = [
+ @top_left, @top_right,
+ @top_right, @bottom_right,
+ @bottom_right, @bottom_left,
+ @bottom_left, @top_left,
+ ].each_slice(2).map { |a, b| Line.new(a, b).plot }.flatten
end
end
class Canvas
attr_reader :width, :height
+
def initialize(width, height)
@width = width
@height = height
- @pixel_matrix = ArrayTwoDimensional.new(width, height)
+ @pixel_matrix = Hash.new
end
def set_pixel(x, y)
- @pixel_matrix[x, y] = 1
+ @pixel_matrix[Point.new(x, y)] = true
end
def pixel_at?(x, y)
- nil != @pixel_matrix[x, y]
+ @pixel_matrix[Point.new(x, y)]
end
def draw(object)
- object.pixel_container.each { |pixel| set_pixel(pixel.x, pixel.y) }
+ object.plot.each { |pixel| set_pixel(pixel.x, pixel.y) }
end
def render_as(renderer_type)
- if renderer_type == Renderers::Ascii
- render_as_ascii
- else
- render_as_html
- end
+ renderer = renderer_type.new
+ renderer.render self
end
-
- private
- def render_as_ascii
- ascii_string = ""
- (0...@height).each do |y|
- (0...@width).each do |x|
- ascii_string += ascii_pixel_check(x, y)
- end
- ascii_string += "\n" if y < @height - 1
- end
- ascii_string
- end
-
- def ascii_pixel_check(x, y)
- if pixel_at?(x, y)
- "@"
- else
- "-"
- end
- end
-
- def render_as_html
- html_string = Renderers::HTML_PREFIX
- (0...@height).each do |y|
- (0...@width).each do |x|
- html_string += html_pixel_check(x, y)
- end
- html_string += "<br>\n" if y < @height - 1
- end
- html_string += Renderers::HTML_SUFFIX
- end
-
- def html_pixel_check(x, y)
- if pixel_at?(x, y)
- "<b></b>"
- else
- "<i></i>"
- end
- end
end
end
  • Щеше да ти е по-удобно да ползваш други кавички за дефиницията на HTML header & footer-а, например единични.
  • Няма нужда да викаш to_a на Range обект, за да го map-неш.
  • Има нужда от празен ред м/у 63 и 64.
  • Никога не ползвай само Hash.new, след като може да се напише {}.

Като цяло, решението ти е прилично, но има още места, които могат да се подобрят. Виж моето решение за справка.