puzzlize 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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