puzzlize 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/.gitignore +4 -0
  2. data/.rspec +1 -0
  3. data/.rvmrc +61 -0
  4. data/CHANGELOG +3 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +7 -0
  7. data/README.md +76 -0
  8. data/Rakefile +5 -0
  9. data/lib/puzzlize.rb +6 -0
  10. data/lib/puzzlize/action_view_helpers.rb +89 -0
  11. data/lib/puzzlize/cutter.rb +199 -0
  12. data/lib/puzzlize/model_additions.rb +18 -0
  13. data/lib/puzzlize/model_instance_methods.rb +125 -0
  14. data/lib/puzzlize/railtie.rb +17 -0
  15. data/lib/puzzlize/schema.rb +26 -0
  16. data/lib/puzzlize/version.rb +3 -0
  17. data/puzzlize.gemspec +26 -0
  18. data/spec/puzzlize/cutter_spec.rb +198 -0
  19. data/spec/puzzlize/model_additions_spec.rb +30 -0
  20. data/spec/puzzlize_spec.rb +50 -0
  21. data/spec/spec_helper.rb +3 -0
  22. data/spec/support/correct/regular_photo-0_0.gif +0 -0
  23. data/spec/support/correct/regular_photo-0_1.gif +0 -0
  24. data/spec/support/correct/regular_photo-1_0.gif +0 -0
  25. data/spec/support/correct/regular_photo-1_1.gif +0 -0
  26. data/spec/support/correct/regular_photo-2_0.gif +0 -0
  27. data/spec/support/correct/regular_photo-2_1.gif +0 -0
  28. data/spec/support/correct/regular_photo-3_0.gif +0 -0
  29. data/spec/support/correct/regular_photo-3_1.gif +0 -0
  30. data/spec/support/correct/regular_photo.jpg +0 -0
  31. data/spec/support/correct/simple_sample-0_0.gif +0 -0
  32. data/spec/support/correct/simple_sample-0_1.gif +0 -0
  33. data/spec/support/correct/simple_sample-0_2.gif +0 -0
  34. data/spec/support/correct/simple_sample-0_3.gif +0 -0
  35. data/spec/support/correct/simple_sample-1_0.gif +0 -0
  36. data/spec/support/correct/simple_sample-1_1.gif +0 -0
  37. data/spec/support/correct/simple_sample-1_2.gif +0 -0
  38. data/spec/support/correct/simple_sample-1_3.gif +0 -0
  39. data/spec/support/correct/simple_sample-2_0.gif +0 -0
  40. data/spec/support/correct/simple_sample-2_1.gif +0 -0
  41. data/spec/support/correct/simple_sample-2_2.gif +0 -0
  42. data/spec/support/correct/simple_sample-2_3.gif +0 -0
  43. data/spec/support/correct/simple_sample-3_0.gif +0 -0
  44. data/spec/support/correct/simple_sample-3_1.gif +0 -0
  45. data/spec/support/correct/simple_sample-3_2.gif +0 -0
  46. data/spec/support/correct/simple_sample-3_3.gif +0 -0
  47. data/spec/support/correct/simple_sample.jpg +0 -0
  48. data/spec/support/images/sample_image.png +0 -0
  49. data/spec/support/made/.DS_Store +0 -0
  50. data/spec/support/made/.gitignore +0 -0
  51. data/spec/support/made/regular_photo.jpg +0 -0
  52. data/spec/support/made/simple_sample.jpg +0 -0
  53. data/spec/support/regular_photo.jpg +0 -0
  54. data/spec/support/simple_sample.jpg +0 -0
  55. metadata +223 -0
@@ -0,0 +1,18 @@
1
+ require "puzzlize/model_instance_methods"
2
+ module Puzzlize
3
+ module ModelAdditions
4
+ def puzzlize(attribute)
5
+ include Puzzlize::ModelInstanceMethods
6
+ @@puzzle_attribute_name = attribute
7
+
8
+ # one of the main reason why this doesn't run off of after create is because paperclip callback hook is after_save.
9
+ # since I use paper's medium image, we need to run after save.
10
+ # Because I did not want to tightly integrate with paperclip, I did not use their callback hooks.
11
+ after_save :make_puzzle
12
+
13
+ def get_puzzlize_attribute_name
14
+ @@puzzle_attribute_name
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,125 @@
1
+ module Puzzlize
2
+ module ModelInstanceMethods
3
+ def default_puzzle_image_url
4
+ self.send(self.class.get_puzzlize_attribute_name).url(:medium)
5
+ end
6
+
7
+ def default_puzzle_image_path
8
+ self.send(self.class.get_puzzlize_attribute_name).path(:medium)
9
+ end
10
+
11
+ def puzzle_images_urls
12
+ output = []
13
+ self.vertical_pieces.to_i.times do |y_axis|
14
+ self.horizontal_pieces.to_i.times do |x_axis|
15
+ output << piece_file_name(x_axis, y_axis, self.default_puzzle_image_url)
16
+ end
17
+ end
18
+ output
19
+ end
20
+
21
+ def puzzle_pieces_names
22
+ output = []
23
+ self.vertical_pieces.to_i.times do |y_axis|
24
+ self.horizontal_pieces.to_i.times do |x_axis|
25
+ output << "puzzle_#{x_axis}-#{y_axis}"
26
+ end
27
+ end
28
+ output
29
+ end
30
+
31
+ def make_puzzle
32
+ if puzzle_created?
33
+ determine_parameters
34
+ make_all_piece
35
+ update_piece_information
36
+ end
37
+ end
38
+
39
+ def puzzle_created?
40
+ !self.vertical_pieces?
41
+ end
42
+
43
+ def determine_parameters
44
+ @file_location = self.default_puzzle_image_path
45
+ @source_file = Magick::Image.read(@file_location).first
46
+ @cutter = Puzzlize::Cutter.new
47
+ @cutter.piece_size = (@source_file.columns * 0.25).to_i
48
+ @cutter.image_width = @source_file.columns
49
+ @cutter.image_height = @source_file.rows
50
+ @source_file = @source_file.crop(*(@cutter.piece_fitting_diamentions + [true]))
51
+ end
52
+
53
+ # this is isolated because supermodel treats update_attributes the same as save
54
+ # ActiveRecord does not do this.
55
+ def update_piece_information
56
+ self.update_attributes({
57
+ :vertical_pieces => @cutter.vertical_pieces,
58
+ :horizontal_pieces => @cutter.horizontal_pieces,
59
+ :vertical_piece_size => @cutter.real_piece_size,
60
+ :horizontal_piece_size => @cutter.real_piece_size,
61
+ :image_width => @cutter.image_width,
62
+ :image_height => @cutter.image_height})
63
+ end
64
+
65
+ def make_single_piece(row_x, row_y)
66
+ # get the source image and crop it to the appropriate size.
67
+ bg = @source_file.crop(*(@cutter.piece_points(row_x, row_y) + [true]))
68
+
69
+ # This will be the final image output
70
+ img = Magick::Image.new(bg.columns,bg.rows)
71
+ img.compression = Magick::LZWCompression
72
+
73
+ # draw the image that is suppose to be invisible.
74
+ gc = Magick::Draw.new
75
+ gc.stroke_antialias(false)
76
+ gc.stroke_width(0)
77
+
78
+ # first start with black box.
79
+ gc.fill('#000000')
80
+ gc.rectangle(0,0,bg.columns, bg.rows)
81
+
82
+ # draw rectangles around the edges of the images aka "buffers"
83
+ # that will later be made transperant
84
+ gc.fill('#09f700')
85
+ @cutter.rectangle_locations(row_x, row_y).each do |a_rectangle|
86
+ gc.rectangle(*a_rectangle) unless a_rectangle.nil?
87
+ end
88
+
89
+ # draw the up to 4 connectors
90
+ connector_types = @cutter.connector_types(row_x, row_y)
91
+ @cutter.connector_locations(row_x, row_y).each_with_index do |connector, index|
92
+ unless connector.nil?
93
+ gc.fill('#09f700') if connector_types[index] == -1
94
+ gc.fill('#000000') if connector_types[index] == 1
95
+ points = connector
96
+ points << (connector[0] + @cutter.connector_radius)
97
+ points << connector[1]
98
+ gc.circle(*points)
99
+ end
100
+ end
101
+
102
+ # draw the image and composite the background image (bg) and set the transparency.
103
+ # Creating a cookie cutter affect.
104
+ gc.draw(img)
105
+ img = img.matte_replace((bg.columns / 2).to_i,(bg.rows / 2).to_i)
106
+ img = bg.composite(img, Magick::CenterGravity, Magick::OverCompositeOp)
107
+ img = img.transparent('#09f700')
108
+
109
+ Rails.logger.debug { "[puzzle_maker] writing image to #{@file_location.sub(/\..*$/, "-#{row_x}_#{row_y}.gif")}".inspect } if defined? Rails
110
+ img.write(piece_file_name(row_x, row_y, @file_location))
111
+ end
112
+
113
+ def piece_file_name(row_x, row_y, file_location)
114
+ file_location.sub(/\..*$/, "-#{row_x}_#{row_y}.gif")
115
+ end
116
+
117
+ def make_all_piece
118
+ @cutter.vertical_pieces.times do |y_axis|
119
+ @cutter.horizontal_pieces.times do |x_axis|
120
+ make_single_piece(x_axis, y_axis)
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,17 @@
1
+ module Puzzlize
2
+ class Railtie < Rails::Railtie
3
+ initializer 'puzzlize.model_additons' do
4
+ ActiveSupport.on_load :active_record do
5
+ extend ModelAdditions
6
+
7
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, Puzzlize::Schema)
8
+ ActiveRecord::ConnectionAdapters::Table.send(:include, Puzzlize::Schema)
9
+ ActiveRecord::ConnectionAdapters::TableDefinition.send(:include, Puzzlize::Schema)
10
+ end
11
+
12
+ ActiveSupport.on_load :action_view do
13
+ include ViewHelpers
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ module Puzzlize
2
+ module Schema
3
+ @@schema = [
4
+ :vertical_piece_size,
5
+ :horizontal_piece_size,
6
+ :vertical_pieces,
7
+ :horizontal_pieces,
8
+ :image_width,
9
+ :image_height
10
+ ]
11
+
12
+
13
+ def add_puzzlize_columns
14
+ @@schema.each do |column_name|
15
+ column(column_name, :integer)
16
+ end
17
+ end
18
+
19
+ def remove_puzzlize_columns(table_name)
20
+ @@schema.each do |column_name|
21
+ remove_column(table_name, column_name)
22
+ end
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,3 @@
1
+ module Puzzlize
2
+ VERSION = "0.0.1"
3
+ end
data/puzzlize.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "puzzlize/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "puzzlize"
7
+ s.version = Puzzlize::VERSION
8
+ s.authors = ["Ziemek Wolski"]
9
+ s.email = ["ziemek.wolski+gem@gmail.com"]
10
+ s.homepage = "https://github.com/ziemekwolski/puzzlize"
11
+ s.summary = %q{Gem created a puzzle using images from a predefined image on a model.}
12
+ s.description = %q{This gem is made up off two libraries. A cutter library - determines all the points to cut. Rmagick rapper, which actually cuts up the images.}
13
+
14
+ s.rubyforge_project = "puzzlize"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "rake"
22
+ s.add_development_dependency "rspec"
23
+ s.add_development_dependency "supermodel"
24
+ s.add_runtime_dependency "paperclip"
25
+ s.add_runtime_dependency "rmagick"
26
+ end
@@ -0,0 +1,198 @@
1
+ require "spec_helper"
2
+
3
+
4
+ describe "Cutter" do
5
+ it "test should store size of piece" do
6
+ cutter = Puzzlize::Cutter.new
7
+ cutter.piece_size = 20
8
+ cutter.piece_size.should == 20
9
+ end
10
+
11
+ it "test should store size of image" do
12
+ cutter = Puzzlize::Cutter.new
13
+ cutter.image_width = 20
14
+ cutter.image_height = 100
15
+ cutter.image_width.should == 20
16
+ cutter.image_height.should == 100
17
+ end
18
+
19
+ it "test determine number of horizontal pieces" do
20
+ cutter = Puzzlize::Cutter.new
21
+ cutter.piece_size = 20
22
+ cutter.image_width = 40
23
+ cutter.image_height = 100
24
+
25
+ cutter.horizontal_pieces.should == 2
26
+ cutter.vertical_pieces.should == 5
27
+ end
28
+
29
+ it "test determine connector size" do
30
+ #connector should be 20% or the size of the piece
31
+ cutter = Puzzlize::Cutter.new
32
+ cutter.piece_size = 20
33
+ cutter.connector_size.should == 4
34
+ end
35
+
36
+ it "test determine connector radius" do
37
+ #connector should be 20% or the size of the piece
38
+ cutter = Puzzlize::Cutter.new
39
+ cutter.piece_size = 20
40
+ cutter.connector_radius.should == 2
41
+ end
42
+
43
+ it "test should return the 4 positions of the first square" do
44
+ # the piece need to overlap so that we can correctly match the connectors.
45
+ cutter = Puzzlize::Cutter.new
46
+ cutter.piece_size = 20
47
+ cutter.image_width = 40
48
+ cutter.image_height = 40
49
+
50
+ cutter.get_piece_matrix.should == [[0,0, 22, 22], [18, 0, 22, 22], [0, 18, 22,22], [18, 18, 22 , 22]]
51
+ end
52
+
53
+ it "test return points for a given piece" do
54
+ #note: I think this is faulty - there is not enough overlap.
55
+ cutter = Puzzlize::Cutter.new
56
+ cutter.piece_size = 20
57
+ cutter.image_width = 40
58
+ cutter.image_height = 40
59
+
60
+ cutter.piece_points(0, 0).should == [0,0, 22, 22]
61
+ cutter.piece_points(1, 0).should == [18, 0, 22, 22]
62
+ cutter.piece_points(0, 1).should == [0, 18, 22, 22]
63
+ cutter.piece_points(1, 1).should == [18, 18, 22 , 22 ]
64
+
65
+ cutter.piece_points(0, 0, false).should == [0,0, 20, 20]
66
+ cutter.piece_points(1, 0, false).should == [20, 0, 20, 20]
67
+ cutter.piece_points(0, 1, false).should == [0, 20, 20,20]
68
+ cutter.piece_points(1, 1, false).should == [20, 20, 20 , 20 ]
69
+
70
+ cutter = Puzzlize::Cutter.new
71
+ cutter.piece_size = 20
72
+ cutter.image_width = 60
73
+ cutter.image_height = 60
74
+
75
+ cutter.piece_points(0, 0).should == [0,0, 22, 22]
76
+ cutter.piece_points(1, 0).should == [18, 0, 24, 22]
77
+ cutter.piece_points(2, 0).should == [38, 0, 22, 22]
78
+
79
+ cutter.piece_points(0, 1).should == [0, 18, 22, 24]
80
+ cutter.piece_points(1, 1).should == [18, 18, 24 , 24 ]
81
+ cutter.piece_points(2, 1).should == [38, 18, 22 , 24 ]
82
+
83
+ cutter.piece_points(0, 2).should == [0, 38, 22 , 22 ]
84
+ cutter.piece_points(1, 2).should == [18, 38, 24 , 22 ]
85
+ cutter.piece_points(2, 2).should == [38, 38, 22 , 22 ]
86
+ end
87
+
88
+ it "test determine the radius of the connector" do
89
+ cutter = Puzzlize::Cutter.new
90
+ cutter.piece_size = 20
91
+ cutter.connector_radius.should == 2
92
+ end
93
+
94
+ it "test should return connector locations give a piece" do
95
+
96
+ # 2
97
+ # *********
98
+ # 3 * * 4
99
+ # * *
100
+ # *********
101
+ # 1
102
+
103
+ # because these points will be applied to individual cut up images,
104
+ # I don't need the points as they relate to the full image.
105
+ # I only need the points as they relate to the cut image, but
106
+ # you need to account for the overlap aka buffer for the connectors.
107
+ # remember connector radius and overlap is 2px not 4px.
108
+
109
+ cutter = Puzzlize::Cutter.new
110
+ cutter.piece_size = 20
111
+ cutter.image_width = 40
112
+ cutter.image_height = 40
113
+
114
+ # [10,0], [10, 20], [0, 10], [20, 10]
115
+ cutter.connector_locations(0, 0).should == [nil, [10, 20], nil, [20, 10]]
116
+ cutter.connector_locations(1, 0).should == [nil, [12, 20], [2, 10], nil]
117
+ cutter.connector_locations(0, 1).should == [[10, 2], nil, nil, [20, 12]]
118
+ cutter.connector_locations(1, 1).should == [[12, 2], nil, [2, 12], nil]
119
+
120
+ cutter = Puzzlize::Cutter.new
121
+ cutter.piece_size = 20
122
+ cutter.image_width = 60
123
+ cutter.image_height = 60
124
+
125
+ # [10,0], [10, 20], [0, 10], [20, 10]
126
+ cutter.connector_locations(0, 0).should == [nil, [10, 20], nil, [20, 10]]
127
+ cutter.connector_locations(1, 0).should == [nil, [12, 20], [2, 10], [22,10]]
128
+ cutter.connector_locations(2, 0).should == [nil, [12, 20], [2, 10], nil]
129
+
130
+ cutter.connector_locations(0, 1).should == [[10,2], [10, 22], nil, [20, 12]]
131
+ cutter.connector_locations(1, 1).should == [[12,2], [12, 22], [2, 12], [22, 12]]
132
+ cutter.connector_locations(2, 1).should == [[12,2], [12, 22], [2, 12], nil]
133
+
134
+ cutter.connector_locations(0, 2).should == [[10,2], nil, nil, [20, 12]]
135
+ cutter.connector_locations(1, 2).should == [[12,2], nil, [2, 12], [22, 12]]
136
+ cutter.connector_locations(2, 2).should == [[12,2], nil, [2, 12], nil]
137
+ end
138
+
139
+ it "test should get points for the rectangle for the transperant piece giving a place" do
140
+ # Again, I don't need the rectangles that apply to the image as a whole, only
141
+ # the rectangles that relate to each individual piece.
142
+
143
+ cutter = Puzzlize::Cutter.new
144
+ cutter.piece_size = 20
145
+ cutter.image_width = 60
146
+ cutter.image_height = 60
147
+
148
+
149
+ # 2
150
+ # *********
151
+ # 3 * * 4
152
+ # * *
153
+ # *********
154
+ # 1
155
+
156
+ cutter.rectangle_locations(0, 0).should == [nil, [0, 20, 22, 22], nil, [20, 0, 22, 22]]
157
+ cutter.rectangle_locations(1, 0).should == [nil, [0, 20, 24, 22], [0, 0, 2, 22], [22, 0, 24, 22]]
158
+ cutter.rectangle_locations(2, 0).should == [nil, [0, 20, 22, 22], [0, 0, 2, 22], nil]
159
+
160
+ # here I have to account for the buffer space at the bottom as well as the top so
161
+ # all the points are raised.
162
+ cutter.rectangle_locations(0, 1).should == [[0, 0, 22, 2], [0, 22, 22, 24], nil, [20, 0, 22, 24]]
163
+ # image is now 24 pixel wide cause, it's in the center of the image, not bound by anything. -- we need the buffer
164
+ cutter.rectangle_locations(1, 1).should == [[0,0, 24, 2], [0, 22, 24, 24], [0, 0, 2, 24], [22, 0, 24, 24]]
165
+ cutter.rectangle_locations(2, 1).should == [[0,0, 22, 2], [0, 22, 22, 24], [0, 0, 2, 24], nil]
166
+
167
+ cutter.rectangle_locations(0, 2).should == [[0,0, 22, 2], nil, nil, [20, 0, 22, 22]]
168
+ cutter.rectangle_locations(1, 2).should == [[0,0, 24, 2], nil, [0, 0, 2, 22], [22, 0, 24, 22]]
169
+ cutter.rectangle_locations(2, 2).should == [[0,0, 22, 2], nil, [0, 0, 2, 22], nil]
170
+ end
171
+
172
+ it "test should determine connector type" do
173
+ # CONNECTOR_TOP_PATTERN = [-1, -1 ,1]
174
+ # CONNECTOR_RIGHT_PATTERN = [1, 1, -1]
175
+
176
+ cutter = Puzzlize::Cutter.new
177
+ cutter.piece_size = 20
178
+ cutter.image_width = 40
179
+ cutter.image_height = 40
180
+
181
+ cutter.connector_types(0,0).should == [nil, -1, nil, 1]
182
+ cutter.connector_types(1,0).should == [nil, -1, -1, nil]
183
+ cutter.connector_types(0,1).should == [1, nil, nil, 1]
184
+ cutter.connector_types(1,1).should == [1, nil, -1, nil]
185
+ end
186
+
187
+ it "test should determine new size of image based on piece size" do
188
+ cutter = Puzzlize::Cutter.new
189
+ cutter.piece_size = 20
190
+ cutter.image_width = 40
191
+ cutter.image_height = 65
192
+
193
+ cutter.piece_fitting_diamentions.should == [0,0, 40, 60]
194
+ cutter.image_width.should == 40
195
+ cutter.image_height.should == 60
196
+ end
197
+
198
+ end
@@ -0,0 +1,30 @@
1
+ require "spec_helper"
2
+
3
+ describe "puzzlize" do
4
+ describe "ModelAdditions" do
5
+ it "should add in get the right attribute" do
6
+ Puzzle.get_puzzlize_attribute_name.should == :puzzle
7
+ end
8
+ end
9
+
10
+ describe "ModelInstanceMethods" do
11
+ it "should include the following methods" do
12
+ p = Puzzle.new
13
+ methods = [:default_puzzle_image_path, :default_puzzle_image_url, :puzzle_images_urls, :puzzle_pieces_names, :make_puzzle, :make_single_piece, :piece_file_name, :make_all_piece]
14
+ (p.methods & methods).should == methods
15
+ end
16
+
17
+ it "should get the right puzzle_pieces_names" do
18
+ expected = ["puzzle_0-0", "puzzle_1-0", "puzzle_2-0", "puzzle_3-0", "puzzle_4-0",
19
+ "puzzle_0-1", "puzzle_1-1", "puzzle_2-1", "puzzle_3-1", "puzzle_4-1",
20
+ "puzzle_0-2", "puzzle_1-2", "puzzle_2-2", "puzzle_3-2", "puzzle_4-2",
21
+ "puzzle_0-3", "puzzle_1-3", "puzzle_2-3", "puzzle_3-3", "puzzle_4-3",
22
+ "puzzle_0-4", "puzzle_1-4", "puzzle_2-4", "puzzle_3-4", "puzzle_4-4"]
23
+ p = Puzzle.new
24
+ p.vertical_pieces = 5
25
+ p.horizontal_pieces = 5
26
+ p.puzzle_pieces_names.should == expected
27
+ end
28
+
29
+ end
30
+ end