Permalink
Browse files

All functionality implemented

  • Loading branch information...
1 parent 1d88ef0 commit 3e5d38b9e1983b2def4f78a5ed7fd2e3dad9a261 @PavelTyk committed Sep 30, 2010
Showing with 308 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +5 −0 bin/editor
  3. +43 −0 lib/editor.rb
  4. +65 −0 lib/image.rb
  5. +60 −0 spec/editor_spec.rb
  6. +62 −0 spec/image_arrays.rb
  7. +69 −0 spec/image_spec.rb
  8. +2 −0 spec/spec.opts
  9. +1 −0 spec/spec_helper.rb
View
@@ -0,0 +1 @@
+.idea
View
@@ -0,0 +1,5 @@
+#!/usr/bin/env ruby
+$LOAD_PATH.unshift File.expand_path('../../lib' , __FILE__)
+require 'editor'
+editor = Editor.new(STDOUT)
+editor.start
View
@@ -0,0 +1,43 @@
+require 'image'
+require 'readline'
+
+class Editor
+ def initialize(output)
+ @output = output
+ end
+ def start
+ @output.puts 'Welcome to Image Editor!'
+ listen
+ end
+
+ def listen
+ loop do
+ cmd = Readline::readline '> ', true
+ execute(cmd)
+ end
+ end
+
+ def execute(cmd)
+ case cmd
+ when /^i (\d+)+ (\d+)+$/i
+ @image = Image.new($1.to_i, $2.to_i)
+ when /^c/i
+ @image.clear
+ when /^l (\d+) (\d+) ([a-z])$/i
+ @image.colorize($1.to_i, $2.to_i,$3)
+ when /^v (\d+) (\d+) (\d+) ([a-z])$/i
+ @image.draw_vertical_segment($1.to_i, $2.to_i, $3.to_i, $4)
+ when /^h (\d+) (\d+) (\d+) ([a-z])$/i
+ @image.draw_horizontal_segment($1.to_i, $2.to_i, $3.to_i, $4)
+ when /^f (\d+) (\d+) ([a-z])$/i
+ @image.fill($1.to_i, $2.to_i, $3)
+ when /^s$/i
+ @output.puts @image.to_s
+ when /^x$/i
+ exit
+ else
+ puts 'Unknown command'
+ end
+ end
+
+end
View
@@ -0,0 +1,65 @@
+class Image
+ DEFAULT_COLOR = 'O'
+
+ def initialize(m, n)
+ @image_arr = Array.new
+ n.times { @image_arr << Array.new(m, DEFAULT_COLOR) }
+ end
+
+ def clear
+ @image_arr.each { |row| row.fill DEFAULT_COLOR}
+ end
+
+ def colorize(x, y, color)
+ x, y = normalize_coords(x, y)
+ @image_arr[y][x] = color
+ end
+
+ def draw_vertical_segment(x, y1, y2, color)
+ x, y1, y2 = normalize_coords(x, y1, y2)
+ y1, y2 = y2, y1 if y1 > y2
+ @image_arr[y1..y2].each { |row| row[x] = color }
+ end
+
+ def draw_horizontal_segment(y, x1, x2, color)
+ y, x1, x2 = normalize_coords(y, x1, x2)
+ x1, x2 = x2, x1 if x1 > x2
+ @image_arr[y].fill(color, x1..x2)
+ end
+
+ def fill(x,y,color)
+ x, y = normalize_coords x, y
+ flood_fill(x, y, @image_arr[y][x], color)
+ end
+
+ def to_s
+ @image_arr.map{ |row| row.join(" ")}.join("\n")
+ end
+
+private
+
+ def normalize_coords *args
+ args.map! {|arg| arg - 1 }
+ end
+
+ def flood_fill(x, y, original_color, new_color)
+ return unless y >= 0 && y < @image_arr.size && x >= 0 && x < @image_arr[0].size
+
+ return if @image_arr[y][x] != original_color
+ return if @image_arr[y][x] == new_color
+
+ @image_arr[y][x] = new_color
+
+ flood_fill(x+1, y, original_color, new_color)
+ flood_fill(x-1, y, original_color, new_color)
+
+ flood_fill(x-1, y+1, original_color, new_color)
+ flood_fill(x , y+1, original_color, new_color)
+ flood_fill(x+1, y+1, original_color, new_color)
+
+ flood_fill(x-1, y-1, original_color, new_color)
+ flood_fill(x, y-1, original_color, new_color)
+ flood_fill(x+1, y-1, original_color, new_color)
+ end
+
+end
View
@@ -0,0 +1,60 @@
+require 'spec_helper'
+
+describe Editor do
+ let(:output) { double('output').as_null_object }
+ let(:editor) { Editor.new(output) }
+
+ describe '#start' do
+ before(:each) {editor.stub(:listen)}
+ it "should send welcome message" do
+ output.should_receive(:puts).with('Welcome to Image Editor!')
+ editor.start
+ end
+ it "should start listening" do
+ editor.should_receive(:listen)
+ editor.start
+ end
+ end
+
+ describe "#execute" do
+ it "should terminate program when 'X' cmd received" do
+ lambda { editor.execute('x') }.should raise_error(SystemExit)
+ end
+ it "should create new image when 'I m n' command received" do
+ Image.should_receive(:new).with(50, 20)
+ editor.execute('i 50 20')
+ end
+
+ context "with initialized image 10x10" do
+ let(:image) { double(Image.new(10, 10)) }
+ before(:each) {editor.instance_variable_set :@image, image}
+
+ it "should send :clear message to image when 'c' command received" do
+ image.should_receive(:clear)
+ editor.execute('c')
+ end
+ it "should send :colorize message to image with 5, 2 and 'C' when 'L 5 2 C' command received" do
+ image.should_receive(:colorize).with(5,2,'C')
+ editor.execute('L 5 2 C')
+ end
+ it "should send :draw_vertical_segment with 3, 2, 5 and 'C' to image when 'V 3 2 5 C' command received" do
+ image.should_receive(:draw_vertical_segment).with(3,2,5,'C')
+ editor.execute('V 3 2 5 C')
+ end
+ it "should send :draw_horizontal_segment with 2, 1, 6 and 'C' to image when 'H 2 1 6 C' command received" do
+ image.should_receive(:draw_horizontal_segment).with(2,1,6,'C')
+ editor.execute('H 2 1 6 C')
+ end
+ it "should send :fill with 2, 3 and 'C' to image when 'F 2 3 C' command received" do
+ image.should_receive(:fill).with(2, 3, 'C')
+ editor.execute('F 2 3 C')
+ end
+ it "should show the contents of the current image when 'S' command received" do
+ image.stub :to_s
+ output.should_receive(:puts)
+ editor.execute('s')
+ end
+ end
+ end
+
+end
View
@@ -0,0 +1,62 @@
+IMAGE_CLEAN = <<EOS.chomp
+O O O O O O
+O O O O O O
+O O O O O O
+O O O O O O
+O O O O O O
+O O O O O O
+EOS
+
+IMAGE_WITH_L_2_3_C = <<EOS.chomp
+O O O O O O
+O O O O O O
+O C O O O O
+O O O O O O
+O O O O O O
+O O O O O O
+EOS
+
+IMAGE_WITH_V_2_2_4_C = <<EOS.chomp
+O O O O O O
+O C O O O O
+O C O O O O
+O C O O O O
+O O O O O O
+O O O O O O
+EOS
+
+IMAGE_WITH_H_3_3_5_C = <<EOS.chomp
+O O O O O O
+O O O O O O
+O O C C C O
+O O O O O O
+O O O O O O
+O O O O O O
+EOS
+
+IMAGE_RECTANGLE_COLORED_WITH_C = <<EOS.chomp
+O O O O O O
+O C C C C O
+O C O O C O
+O C O O C O
+O C C C C O
+O O O O O O
+EOS
+
+IMAGE_RECTANGLE_COLORED_WITH_C_INNER_FILLED_WITH_F = <<EOS.chomp
+O O O O O O
+O C C C C O
+O C F F C O
+O C F F C O
+O C C C C O
+O O O O O O
+EOS
+
+IMAGE_RECTANGLE_COLORED_WITH_C_OUTER_FILLED_WITH_F = <<EOS.chomp
+F F F F F F
+F C C C C F
+F C O O C F
+F C O O C F
+F C C C C F
+F F F F F F
+EOS
View
@@ -0,0 +1,69 @@
+require 'spec_helper'
+require 'image_arrays'
+
+describe Image do
+ let(:image) {Image.new(6,6)}
+ describe "#initialize" do
+ it "should create 6x6 array with 'O' elements" do
+ image.instance_variable_get(:@image_arr).should eql(image_str_to_array IMAGE_CLEAN)
+ end
+ end
+ describe "#to_s" do
+ it "should return 'image string'" do
+ image.to_s.should eql IMAGE_CLEAN
+ end
+ end
+ describe "#colorize" do
+ it "should set 'pixel' color to 'C'" do
+ image.colorize(2, 3, 'C')
+ image.to_s.should eql IMAGE_WITH_L_2_3_C
+ end
+ end
+
+ describe "#draw_vertical_segment" do
+ it "should draw vertical line with 'C' color" do
+ image.draw_vertical_segment(2, 2, 4, 'C')
+ image.to_s.should eql IMAGE_WITH_V_2_2_4_C
+ end
+ it "should draw vertical line with 'C' color even if Y coordinates are in reverse order" do
+ image.draw_vertical_segment(2, 4, 2, 'C')
+ image.to_s.should eql IMAGE_WITH_V_2_2_4_C
+ end
+ end
+
+ describe "#draw_horizontal_segment" do
+ it "should draw horizontal line with 'C' color" do
+ image.draw_horizontal_segment(3, 3, 5, 'C')
+ image.to_s.should eql IMAGE_WITH_H_3_3_5_C
+ end
+ it "should draw horizontal line with 'C' color even if X coordinates are in reverse order" do
+ image.draw_horizontal_segment(3, 5, 3, 'C')
+ image.to_s.should eql IMAGE_WITH_H_3_3_5_C
+ end
+ end
+
+ describe "#fill" do
+ it "should fill inner area" do
+ image.instance_variable_set :@image_arr, image_str_to_array(IMAGE_RECTANGLE_COLORED_WITH_C)
+ image.fill(3, 3, 'F')
+ image.to_s.should eql IMAGE_RECTANGLE_COLORED_WITH_C_INNER_FILLED_WITH_F
+ end
+ it "should fill outer area" do
+ image.instance_variable_set :@image_arr, image_str_to_array(IMAGE_RECTANGLE_COLORED_WITH_C)
+ image.fill(1, 1, 'F')
+ image.to_s.should eql IMAGE_RECTANGLE_COLORED_WITH_C_OUTER_FILLED_WITH_F
+ end
+ end
+
+ describe "#clear" do
+ it "should clear image to deault color" do
+ image.instance_variable_set :@image_arr, image_str_to_array(IMAGE_CLEAN)
+ image.clear
+ image.to_s.should eql IMAGE_CLEAN
+ end
+ end
+end
+
+def image_str_to_array(image_str)
+ image_str.split("\n").map{ |row| row.split }
+end
View
@@ -0,0 +1,2 @@
+--colour
+--format nested
View
@@ -0,0 +1 @@
+require 'editor'

0 comments on commit 3e5d38b

Please sign in to comment.