squib 0.14.1 → 0.14.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +41 -41
  3. data/.travis.yml +19 -19
  4. data/CHANGELOG.md +357 -340
  5. data/CONTRIBUTING.md +40 -40
  6. data/Gemfile +2 -2
  7. data/LICENSE.txt +22 -22
  8. data/README.md +121 -121
  9. data/RELEASE TODO.md +21 -21
  10. data/Rakefile +48 -48
  11. data/appveyor.yml +24 -24
  12. data/bin/squib +4 -4
  13. data/lib/squib.rb +32 -32
  14. data/lib/squib/api/background.rb +15 -15
  15. data/lib/squib/api/data.rb +137 -137
  16. data/lib/squib/api/groups.rb +54 -54
  17. data/lib/squib/api/image.rb +49 -49
  18. data/lib/squib/api/save.rb +83 -83
  19. data/lib/squib/api/settings.rb +21 -21
  20. data/lib/squib/api/shapes.rb +124 -124
  21. data/lib/squib/api/text.rb +25 -25
  22. data/lib/squib/api/text_embed.rb +71 -71
  23. data/lib/squib/api/units.rb +27 -27
  24. data/lib/squib/args/arg_loader.rb +126 -126
  25. data/lib/squib/args/box.rb +55 -55
  26. data/lib/squib/args/card_range.rb +32 -32
  27. data/lib/squib/args/color_validator.rb +12 -12
  28. data/lib/squib/args/coords.rb +35 -35
  29. data/lib/squib/args/csv_opts.rb +25 -25
  30. data/lib/squib/args/dir_validator.rb +16 -16
  31. data/lib/squib/args/draw.rb +92 -92
  32. data/lib/squib/args/embed_adjust.rb +25 -25
  33. data/lib/squib/args/embed_key.rb +17 -17
  34. data/lib/squib/args/hand_special.rb +37 -37
  35. data/lib/squib/args/import.rb +40 -40
  36. data/lib/squib/args/input_file.rb +37 -37
  37. data/lib/squib/args/paint.rb +44 -44
  38. data/lib/squib/args/paragraph.rb +116 -116
  39. data/lib/squib/args/save_batch.rb +63 -63
  40. data/lib/squib/args/scale_box.rb +53 -53
  41. data/lib/squib/args/sheet.rb +172 -172
  42. data/lib/squib/args/showcase_special.rb +38 -38
  43. data/lib/squib/args/sprue_file.rb +44 -44
  44. data/lib/squib/args/svg_special.rb +37 -37
  45. data/lib/squib/args/transform.rb +61 -61
  46. data/lib/squib/args/typographer.rb +119 -119
  47. data/lib/squib/args/unit_conversion.rb +29 -29
  48. data/lib/squib/builtin/layouts/economy.yml +85 -85
  49. data/lib/squib/builtin/layouts/fantasy.yml +101 -101
  50. data/lib/squib/builtin/layouts/hand.yml +62 -62
  51. data/lib/squib/builtin/layouts/party.yml +94 -94
  52. data/lib/squib/builtin/layouts/playing-card.yml +35 -35
  53. data/lib/squib/builtin/layouts/tuck_box.yml +46 -46
  54. data/lib/squib/builtin/projects/advanced/.gitignore +4 -4
  55. data/lib/squib/builtin/projects/advanced/ABOUT.md +19 -19
  56. data/lib/squib/builtin/projects/advanced/Gemfile +11 -11
  57. data/lib/squib/builtin/projects/advanced/Guardfile +21 -21
  58. data/lib/squib/builtin/projects/advanced/IDEAS.md +22 -22
  59. data/lib/squib/builtin/projects/advanced/PLAYTESTING.md +26 -26
  60. data/lib/squib/builtin/projects/advanced/Rakefile +27 -27
  61. data/lib/squib/builtin/projects/advanced/config.yml +49 -49
  62. data/lib/squib/builtin/projects/advanced/docs/PNP NOTES.md +3 -3
  63. data/lib/squib/builtin/projects/advanced/docs/RULES.md +21 -21
  64. data/lib/squib/builtin/projects/advanced/img/example.svg +60 -60
  65. data/lib/squib/builtin/projects/advanced/layouts/deck.yml +27 -27
  66. data/lib/squib/builtin/projects/advanced/src/deck.rb +34 -34
  67. data/lib/squib/builtin/projects/advanced/src/version.rb +3 -3
  68. data/lib/squib/builtin/projects/basic/.gitignore +4 -4
  69. data/lib/squib/builtin/projects/basic/ABOUT.md +19 -19
  70. data/lib/squib/builtin/projects/basic/Gemfile +3 -3
  71. data/lib/squib/builtin/projects/basic/IDEAS.md +22 -22
  72. data/lib/squib/builtin/projects/basic/PLAYTESTING.md +26 -26
  73. data/lib/squib/builtin/projects/basic/PNP NOTES.md +3 -3
  74. data/lib/squib/builtin/projects/basic/RULES.md +21 -21
  75. data/lib/squib/builtin/projects/basic/Rakefile +7 -7
  76. data/lib/squib/builtin/projects/basic/config.yml +49 -49
  77. data/lib/squib/builtin/projects/basic/deck.rb +6 -6
  78. data/lib/squib/builtin/sprues/a4_euro_card.yml +42 -42
  79. data/lib/squib/builtin/sprues/a4_poker_card_8up.yml +40 -40
  80. data/lib/squib/builtin/sprues/a4_poker_card_9up.yml +42 -42
  81. data/lib/squib/builtin/sprues/a4_usa_card.yml +42 -42
  82. data/lib/squib/builtin/sprues/drivethrucards_1up.yml +10 -0
  83. data/lib/squib/builtin/sprues/letter_poker_card_9up.yml +25 -25
  84. data/lib/squib/builtin/sprues/letter_poker_foldable_8up.yml +52 -52
  85. data/lib/squib/builtin/sprues/printplaygames_18up.yml +68 -0
  86. data/lib/squib/card.rb +75 -74
  87. data/lib/squib/commands/cli.rb +39 -39
  88. data/lib/squib/commands/data/template_option.rb +109 -109
  89. data/lib/squib/commands/make_sprue.rb +275 -275
  90. data/lib/squib/commands/new.rb +77 -77
  91. data/lib/squib/conf.rb +139 -139
  92. data/lib/squib/constants.rb +17 -17
  93. data/lib/squib/deck.rb +116 -116
  94. data/lib/squib/graphics/background.rb +14 -14
  95. data/lib/squib/graphics/cairo_context_wrapper.rb +113 -113
  96. data/lib/squib/graphics/embedding_utils.rb +28 -28
  97. data/lib/squib/graphics/gradient_regex.rb +47 -47
  98. data/lib/squib/graphics/hand.rb +42 -42
  99. data/lib/squib/graphics/image.rb +108 -108
  100. data/lib/squib/graphics/save_doc.rb +61 -61
  101. data/lib/squib/graphics/save_images.rb +52 -52
  102. data/lib/squib/graphics/save_pdf.rb +90 -90
  103. data/lib/squib/graphics/save_sprue.rb +204 -205
  104. data/lib/squib/graphics/shapes.rb +143 -143
  105. data/lib/squib/graphics/showcase.rb +85 -85
  106. data/lib/squib/graphics/text.rb +174 -174
  107. data/lib/squib/import/data_frame.rb +108 -108
  108. data/lib/squib/layout_parser.rb +138 -126
  109. data/lib/squib/progress.rb +38 -38
  110. data/lib/squib/sample_helpers.rb +34 -34
  111. data/lib/squib/sprues/crop_line.rb +28 -28
  112. data/lib/squib/sprues/crop_line_dash.rb +35 -35
  113. data/lib/squib/sprues/invalid_sprue_definition.rb +9 -9
  114. data/lib/squib/sprues/sprue.rb +203 -203
  115. data/lib/squib/sprues/sprue_schema.rb +48 -48
  116. data/lib/squib/version.rb +10 -10
  117. data/samples/autoscale_font/_autoscale_font.rb +29 -29
  118. data/samples/backend/_backend.rb +26 -26
  119. data/samples/basic.rb +19 -19
  120. data/samples/build_groups/build_groups.rb +36 -36
  121. data/samples/colors/_colors.rb +38 -38
  122. data/samples/colors/_gradients.rb +34 -34
  123. data/samples/config/config_text_markup.rb +20 -20
  124. data/samples/config/custom_config.rb +18 -18
  125. data/samples/data/_csv.rb +33 -33
  126. data/samples/data/_excel.rb +55 -55
  127. data/samples/data/_yaml.rb +12 -12
  128. data/samples/hello_world.rb +6 -6
  129. data/samples/images/_cairo_access.rb +39 -39
  130. data/samples/images/_images.rb +104 -104
  131. data/samples/images/_more_load_images.rb +102 -102
  132. data/samples/intro/01_hello.rb +8 -8
  133. data/samples/intro/02_options.rb +14 -14
  134. data/samples/intro/03_layout.rb +11 -11
  135. data/samples/intro/04_arrays.rb +15 -15
  136. data/samples/intro/05_excel.rb +14 -14
  137. data/samples/layouts/builtin_layouts.rb +97 -97
  138. data/samples/layouts/layouts.rb +71 -71
  139. data/samples/project/src/characters.rb +8 -8
  140. data/samples/project/src/skills.rb +7 -7
  141. data/samples/proofs/_tgc_proofs.rb +16 -16
  142. data/samples/ranges/_ranges.rb +64 -64
  143. data/samples/saves/_hand.rb +23 -23
  144. data/samples/saves/_portrait_landscape.rb +23 -23
  145. data/samples/saves/_save_pdf.rb +29 -29
  146. data/samples/saves/_saves.rb +51 -51
  147. data/samples/saves/_showcase.rb +25 -25
  148. data/samples/shapes/_draw_shapes.rb +60 -58
  149. data/samples/shapes/_proofs.rb +22 -22
  150. data/samples/sprues/_advanced_sprues.rb +24 -24
  151. data/samples/sprues/_builtin_sprues.rb +21 -19
  152. data/samples/sprues/_fold_sheet.rb +27 -27
  153. data/samples/sprues/_hex_tiles.rb +15 -15
  154. data/samples/sprues/_mints.rb +11 -11
  155. data/samples/sprues/_sprue_example.rb +11 -11
  156. data/samples/text/_embed_text.rb +128 -128
  157. data/samples/text/_text.rb +47 -47
  158. data/samples/text/_text_options.rb +102 -102
  159. data/samples/text/bug134.rb +14 -14
  160. data/samples/units/_units.rb +32 -32
  161. data/squib.gemspec +52 -51
  162. metadata +66 -50
@@ -1,143 +1,143 @@
1
- module Squib
2
- # @api private
3
- class Card
4
-
5
- # :nodoc:
6
- # @api private
7
- def rect(box, draw, trans)
8
- use_cairo do |cc|
9
- cc.rotate_about(box.x, box.y, trans.angle)
10
- cc.rounded_rectangle(box.x, box.y, box.width, box.height,
11
- box.x_radius, box.y_radius)
12
- cc.fill_n_stroke(draw)
13
- end
14
- end
15
-
16
- # :nodoc:
17
- # @api private
18
- def circle(box, draw)
19
- x, y, r = box.x, box.y, box.radius
20
- use_cairo do |cc|
21
- if box.arc_direction == :clockwise
22
- cc.arc(x, y, r, box.arc_start, box.arc_end)
23
- else
24
- cc.arc_negative(x, y, r, box.arc_start, box.arc_end)
25
- end
26
- if box.arc_close
27
- cc.close_path();
28
- end
29
- cc.fill_n_stroke(draw)
30
- end
31
- end
32
-
33
- # Ellipse drawing taken from looking at the control points in Inkscape
34
- # Think of it like a rectangle. Curves go from mid-points of the sides
35
- # of the rectangle. Control points are at 1/4 and 3/4 of the side.
36
- # :nodoc:
37
- # @api private
38
- def ellipse(box, draw, trans)
39
- x, y, w, h = box.x, box.y, box.width, box.height
40
- use_cairo do |cc|
41
- cc.rotate_about(box.x, box.y, trans.angle)
42
- cc.move_to(x, y + 0.5 * h) # start west
43
- cc.curve_to(x, y + 0.25 * h, # west to north
44
- x + 0.25 * w, y,
45
- x + 0.5 * w, y)
46
- cc.curve_to(x + 0.75 * w, y, # north to east
47
- x + w, y + 0.25 * h,
48
- x + w, y + 0.5 * h)
49
- cc.curve_to(x + w, y + 0.75 * h, # east to south
50
- x + 0.75 * w, y + h,
51
- x + 0.5 * w, y + h)
52
- cc.curve_to(x + 0.25 * w, y + h, # south to west
53
- x, y + 0.75 * h,
54
- x, y + 0.5 * h)
55
- cc.fill_n_stroke(draw)
56
- end
57
- end
58
-
59
- # :nodoc:
60
- # @api private
61
- def grid(box, draw)
62
- x, y, w, h = box.x, box.y, box.width, box.height
63
- use_cairo do |cc|
64
- (x..@width + w).step(w) { |ix| line_xy(ix, y - @height, ix, @height + y, draw) }
65
- (y..@height + h).step(h) { |iy| line_xy(x - @width, iy, @width + x, iy, draw) }
66
- end
67
- end
68
-
69
- # :nodoc:
70
- # @api private
71
- def triangle(tri, draw)
72
- use_cairo do |cc|
73
- cc.triangle(tri.x1, tri.y1, tri.x2, tri.y2, tri.x3, tri.y3)
74
- cc.fill_n_stroke(draw)
75
- end
76
- end
77
-
78
- # :nodoc:
79
- # @api private
80
- def line(coord, draw)
81
- line_xy(coord.x1, coord.y1, coord.x2, coord.y2, draw)
82
- end
83
-
84
- # :nodoc:
85
- # @api private
86
- def line_xy(x1, y1, x2, y2, draw)
87
- use_cairo do |cc|
88
- cc.move_to(x1, y1)
89
- cc.line_to(x2, y2)
90
- cc.fancy_stroke(draw)
91
- end
92
- end
93
-
94
- # :nodoc:
95
- # @api private
96
- def curve(bez, draw)
97
- x1, y1, cx1, cy1 = bez.x1, bez.y1, bez.cx1, bez.cy1
98
- cx2, cy2, x2, y2 = bez.cx2, bez.cy2, bez.x2, bez.y2
99
- use_cairo do |cc|
100
- cc.move_to(x1, y1)
101
- cc.curve_to(cx1, cy1, cx2, cy2, x2, y2)
102
- cc.fill_n_stroke(draw)
103
- end
104
- end
105
-
106
- # :nodoc:
107
- # @api private
108
- def star(poly, trans, draw)
109
- x, y, n = poly.x, poly.y, poly.n
110
- inner_radius, outer_radius = poly.inner_radius, poly.outer_radius
111
- use_cairo do |cc|
112
- cc.rotate_about(x, y, trans.angle)
113
- cc.move_to(x + outer_radius, y) # i = 0, so cos(0)=1 and sin(0)=0
114
- theta = Math::PI / n.to_f # i.e. (2*pi) / (2*n)
115
- 0.upto(2 * n) do |i|
116
- radius = i.even? ? outer_radius : inner_radius
117
- cc.line_to(x + radius * Math::cos(i * theta),
118
- y + radius * Math::sin(i * theta))
119
- end
120
- cc.close_path
121
- cc.fill_n_stroke(draw)
122
- end
123
- end
124
-
125
- # :nodoc:
126
- # @api private
127
- def polygon(poly, trans, draw)
128
- x, y, n, radius = poly.x, poly.y, poly.n, poly.radius
129
- use_cairo do |cc|
130
- cc.rotate_about(x, y, trans.angle)
131
- cc.move_to(x + radius, y) # i = 0, so cos(0)=1 and sin(0)=0
132
- theta = (2 * Math::PI) / n.to_f
133
- 0.upto(n) do |i|
134
- cc.line_to(x + radius * Math::cos(i * theta),
135
- y + radius * Math::sin(i * theta))
136
- end
137
- cc.close_path
138
- cc.fill_n_stroke(draw)
139
- end
140
- end
141
-
142
- end
143
- end
1
+ module Squib
2
+ # @api private
3
+ class Card
4
+
5
+ # :nodoc:
6
+ # @api private
7
+ def rect(box, draw, trans)
8
+ use_cairo do |cc|
9
+ cc.rotate_about(box.x, box.y, trans.angle)
10
+ cc.rounded_rectangle(box.x, box.y, box.width, box.height,
11
+ box.x_radius, box.y_radius)
12
+ cc.fill_n_stroke(draw)
13
+ end
14
+ end
15
+
16
+ # :nodoc:
17
+ # @api private
18
+ def circle(box, draw)
19
+ x, y, r = box.x, box.y, box.radius
20
+ use_cairo do |cc|
21
+ if box.arc_direction == :clockwise
22
+ cc.arc(x, y, r, box.arc_start, box.arc_end)
23
+ else
24
+ cc.arc_negative(x, y, r, box.arc_start, box.arc_end)
25
+ end
26
+ if box.arc_close
27
+ cc.close_path();
28
+ end
29
+ cc.fill_n_stroke(draw)
30
+ end
31
+ end
32
+
33
+ # Ellipse drawing taken from looking at the control points in Inkscape
34
+ # Think of it like a rectangle. Curves go from mid-points of the sides
35
+ # of the rectangle. Control points are at 1/4 and 3/4 of the side.
36
+ # :nodoc:
37
+ # @api private
38
+ def ellipse(box, draw, trans)
39
+ x, y, w, h = box.x, box.y, box.width, box.height
40
+ use_cairo do |cc|
41
+ cc.rotate_about(box.x, box.y, trans.angle)
42
+ cc.move_to(x, y + 0.5 * h) # start west
43
+ cc.curve_to(x, y + 0.25 * h, # west to north
44
+ x + 0.25 * w, y,
45
+ x + 0.5 * w, y)
46
+ cc.curve_to(x + 0.75 * w, y, # north to east
47
+ x + w, y + 0.25 * h,
48
+ x + w, y + 0.5 * h)
49
+ cc.curve_to(x + w, y + 0.75 * h, # east to south
50
+ x + 0.75 * w, y + h,
51
+ x + 0.5 * w, y + h)
52
+ cc.curve_to(x + 0.25 * w, y + h, # south to west
53
+ x, y + 0.75 * h,
54
+ x, y + 0.5 * h)
55
+ cc.fill_n_stroke(draw)
56
+ end
57
+ end
58
+
59
+ # :nodoc:
60
+ # @api private
61
+ def grid(box, draw)
62
+ x, y, w, h = box.x, box.y, box.width, box.height
63
+ use_cairo do |cc|
64
+ (x..@width + w).step(w) { |ix| line_xy(ix, y - @height, ix, @height + y, draw) }
65
+ (y..@height + h).step(h) { |iy| line_xy(x - @width, iy, @width + x, iy, draw) }
66
+ end
67
+ end
68
+
69
+ # :nodoc:
70
+ # @api private
71
+ def triangle(tri, draw)
72
+ use_cairo do |cc|
73
+ cc.triangle(tri.x1, tri.y1, tri.x2, tri.y2, tri.x3, tri.y3)
74
+ cc.fill_n_stroke(draw)
75
+ end
76
+ end
77
+
78
+ # :nodoc:
79
+ # @api private
80
+ def line(coord, draw)
81
+ line_xy(coord.x1, coord.y1, coord.x2, coord.y2, draw)
82
+ end
83
+
84
+ # :nodoc:
85
+ # @api private
86
+ def line_xy(x1, y1, x2, y2, draw)
87
+ use_cairo do |cc|
88
+ cc.move_to(x1, y1)
89
+ cc.line_to(x2, y2)
90
+ cc.fancy_stroke(draw)
91
+ end
92
+ end
93
+
94
+ # :nodoc:
95
+ # @api private
96
+ def curve(bez, draw)
97
+ x1, y1, cx1, cy1 = bez.x1, bez.y1, bez.cx1, bez.cy1
98
+ cx2, cy2, x2, y2 = bez.cx2, bez.cy2, bez.x2, bez.y2
99
+ use_cairo do |cc|
100
+ cc.move_to(x1, y1)
101
+ cc.curve_to(cx1, cy1, cx2, cy2, x2, y2)
102
+ cc.fill_n_stroke(draw)
103
+ end
104
+ end
105
+
106
+ # :nodoc:
107
+ # @api private
108
+ def star(poly, trans, draw)
109
+ x, y, n = poly.x, poly.y, poly.n
110
+ inner_radius, outer_radius = poly.inner_radius, poly.outer_radius
111
+ use_cairo do |cc|
112
+ cc.rotate_about(x, y, trans.angle)
113
+ cc.move_to(x + outer_radius, y) # i = 0, so cos(0)=1 and sin(0)=0
114
+ theta = Math::PI / n.to_f # i.e. (2*pi) / (2*n)
115
+ 0.upto(2 * n) do |i|
116
+ radius = i.even? ? outer_radius : inner_radius
117
+ cc.line_to(x + radius * Math::cos(i * theta),
118
+ y + radius * Math::sin(i * theta))
119
+ end
120
+ cc.close_path
121
+ cc.fill_n_stroke(draw)
122
+ end
123
+ end
124
+
125
+ # :nodoc:
126
+ # @api private
127
+ def polygon(poly, trans, draw)
128
+ x, y, n, radius = poly.x, poly.y, poly.n, poly.radius
129
+ use_cairo do |cc|
130
+ cc.rotate_about(x, y, trans.angle)
131
+ cc.move_to(x + radius, y) # i = 0, so cos(0)=1 and sin(0)=0
132
+ theta = (2 * Math::PI) / n.to_f
133
+ 0.upto(n) do |i|
134
+ cc.line_to(x + radius * Math::cos(i * theta),
135
+ y + radius * Math::sin(i * theta))
136
+ end
137
+ cc.close_path
138
+ cc.fill_n_stroke(draw)
139
+ end
140
+ end
141
+
142
+ end
143
+ end
@@ -1,85 +1,85 @@
1
- require_relative 'cairo_context_wrapper'
2
-
3
- module Squib
4
- class Deck
5
-
6
- # So the Cairo people have said over and over again that they won't support the 3x3 matrices that would handle perspective transforms.
7
- # Since our perspective transform needs are a bit simpler, we can use a "striping method" that does the job for us.
8
- # It's a little bit involved, but it works well enough for limited ranges of our parameters.
9
- # These were also helpful:
10
- # http://kapo-cpp.blogspot.com/2008/01/perspective-effect-using-cairo.html
11
- # http://zetcode.com/gui/pygtk/drawingII/
12
- # :nodoc:
13
- # @api private
14
- def render_showcase(range, sheet, showcase)
15
- out_width = range.size * ((@width - 2 * sheet.trim) * showcase.scale * showcase.offset) + 2 * sheet.margin
16
- out_height = showcase.reflect_offset + (1.0 + showcase.reflect_percent) * (@height - 2 * sheet.trim) + 2 * sheet.margin
17
- out_cc = Cairo::Context.new(Cairo::ImageSurface.new(out_width, out_height))
18
- wrapper = Squib::Graphics::CairoContextWrapper.new(out_cc)
19
- wrapper.set_source_squibcolor(sheet.fill_color)
20
- wrapper.paint
21
-
22
- cards = range.collect { |i| @cards[i] }
23
- cards.each_with_index do |card, i|
24
- trimmed = trim_rounded(card.cairo_surface, sheet.trim, sheet.trim_radius)
25
- reflected = reflect(trimmed, showcase.reflect_offset, showcase.reflect_percent, showcase.reflect_strength)
26
- perspectived = perspective(reflected, showcase.scale, showcase.face_right?)
27
- out_cc.set_source(perspectived, sheet.margin + i * perspectived.width * showcase.offset, sheet.margin)
28
- out_cc.paint
29
- end
30
- out_cc.target.write_to_png("#{sheet.dir}/#{sheet.file}")
31
- end
32
-
33
- # :nodoc:
34
- # @api private
35
- def trim_rounded(src, trim, radius)
36
- trim_cc = Cairo::Context.new(Cairo::ImageSurface.new(@width - 2.0 * trim, @height - 2.0 * trim))
37
- trim_cc.rounded_rectangle(0, 0, trim_cc.target.width, trim_cc.target.height, radius, radius)
38
- trim_cc.set_source(src, -1 * trim, -1 * trim)
39
- trim_cc.clip
40
- trim_cc.paint
41
- return trim_cc.target
42
- end
43
-
44
- # :nodoc:
45
- # @api private
46
- def reflect(src, roffset, rpercent, rstrength)
47
- tmp_cc = Cairo::Context.new(Cairo::ImageSurface.new(src.width, src.height * (1.0 + rpercent) + roffset))
48
- tmp_cc.set_source(src, 0, 0)
49
- tmp_cc.paint
50
- # Flip affine magic from: http://cairographics.org/matrix_transform/
51
- matrix = Cairo::Matrix.new(1, 0, 0, -1, 0, 2 * src.height + roffset)
52
- tmp_cc.transform(matrix) # flips the coordinate system
53
- top_y = src.height # top of the reflection
54
- bottom_y = src.height * (1.0 - rpercent) + roffset # bottom of the reflection
55
- gradient = Cairo::LinearPattern.new(0, top_y, 0, bottom_y)
56
- gradient.add_color_stop_rgba(0.0, 0, 0, 0, rstrength) # start a little reflected
57
- gradient.add_color_stop_rgba(1.0, 0, 0, 0, 0.0) # fade to nothing
58
- tmp_cc.set_source(src, 0, 0)
59
- tmp_cc.mask(gradient)
60
- return tmp_cc.target
61
- end
62
-
63
- # :nodoc:
64
- # @api private
65
- def perspective(src, scale, face_right)
66
- dest_cxt = Cairo::Context.new(Cairo::ImageSurface.new(src.width * scale, src.height))
67
- in_thickness = 1 # Take strip 1 pixel-width at a time
68
- out_thickness = 3 # Scale it to 3 pixels wider to cover any gaps
69
- (0..src.width).step(in_thickness) do |i|
70
- percentage = i / src.width.to_f
71
- i = src.width - i if face_right
72
- factor = scale + (percentage * (1.0 - scale)) # linear interpolation
73
- dest_cxt.save
74
- dest_cxt.translate 0, src.height / 2.0 * (1.0 - factor)
75
- dest_cxt.scale factor * scale, factor
76
- dest_cxt.set_source src, 0, 0
77
- dest_cxt.rounded_rectangle i, 0, out_thickness, src.height, 0, 0
78
- dest_cxt.fill
79
- dest_cxt.restore
80
- end
81
- return dest_cxt.target
82
- end
83
-
84
- end
85
- end
1
+ require_relative 'cairo_context_wrapper'
2
+
3
+ module Squib
4
+ class Deck
5
+
6
+ # So the Cairo people have said over and over again that they won't support the 3x3 matrices that would handle perspective transforms.
7
+ # Since our perspective transform needs are a bit simpler, we can use a "striping method" that does the job for us.
8
+ # It's a little bit involved, but it works well enough for limited ranges of our parameters.
9
+ # These were also helpful:
10
+ # http://kapo-cpp.blogspot.com/2008/01/perspective-effect-using-cairo.html
11
+ # http://zetcode.com/gui/pygtk/drawingII/
12
+ # :nodoc:
13
+ # @api private
14
+ def render_showcase(range, sheet, showcase)
15
+ out_width = range.size * ((@width - 2 * sheet.trim) * showcase.scale * showcase.offset) + 2 * sheet.margin
16
+ out_height = showcase.reflect_offset + (1.0 + showcase.reflect_percent) * (@height - 2 * sheet.trim) + 2 * sheet.margin
17
+ out_cc = Cairo::Context.new(Cairo::ImageSurface.new(out_width, out_height))
18
+ wrapper = Squib::Graphics::CairoContextWrapper.new(out_cc)
19
+ wrapper.set_source_squibcolor(sheet.fill_color)
20
+ wrapper.paint
21
+
22
+ cards = range.collect { |i| @cards[i] }
23
+ cards.each_with_index do |card, i|
24
+ trimmed = trim_rounded(card.cairo_surface, sheet.trim, sheet.trim_radius)
25
+ reflected = reflect(trimmed, showcase.reflect_offset, showcase.reflect_percent, showcase.reflect_strength)
26
+ perspectived = perspective(reflected, showcase.scale, showcase.face_right?)
27
+ out_cc.set_source(perspectived, sheet.margin + i * perspectived.width * showcase.offset, sheet.margin)
28
+ out_cc.paint
29
+ end
30
+ out_cc.target.write_to_png("#{sheet.dir}/#{sheet.file}")
31
+ end
32
+
33
+ # :nodoc:
34
+ # @api private
35
+ def trim_rounded(src, trim, radius)
36
+ trim_cc = Cairo::Context.new(Cairo::ImageSurface.new(@width - 2.0 * trim, @height - 2.0 * trim))
37
+ trim_cc.rounded_rectangle(0, 0, trim_cc.target.width, trim_cc.target.height, radius, radius)
38
+ trim_cc.set_source(src, -1 * trim, -1 * trim)
39
+ trim_cc.clip
40
+ trim_cc.paint
41
+ return trim_cc.target
42
+ end
43
+
44
+ # :nodoc:
45
+ # @api private
46
+ def reflect(src, roffset, rpercent, rstrength)
47
+ tmp_cc = Cairo::Context.new(Cairo::ImageSurface.new(src.width, src.height * (1.0 + rpercent) + roffset))
48
+ tmp_cc.set_source(src, 0, 0)
49
+ tmp_cc.paint
50
+ # Flip affine magic from: http://cairographics.org/matrix_transform/
51
+ matrix = Cairo::Matrix.new(1, 0, 0, -1, 0, 2 * src.height + roffset)
52
+ tmp_cc.transform(matrix) # flips the coordinate system
53
+ top_y = src.height # top of the reflection
54
+ bottom_y = src.height * (1.0 - rpercent) + roffset # bottom of the reflection
55
+ gradient = Cairo::LinearPattern.new(0, top_y, 0, bottom_y)
56
+ gradient.add_color_stop_rgba(0.0, 0, 0, 0, rstrength) # start a little reflected
57
+ gradient.add_color_stop_rgba(1.0, 0, 0, 0, 0.0) # fade to nothing
58
+ tmp_cc.set_source(src, 0, 0)
59
+ tmp_cc.mask(gradient)
60
+ return tmp_cc.target
61
+ end
62
+
63
+ # :nodoc:
64
+ # @api private
65
+ def perspective(src, scale, face_right)
66
+ dest_cxt = Cairo::Context.new(Cairo::ImageSurface.new(src.width * scale, src.height))
67
+ in_thickness = 1 # Take strip 1 pixel-width at a time
68
+ out_thickness = 3 # Scale it to 3 pixels wider to cover any gaps
69
+ (0..src.width).step(in_thickness) do |i|
70
+ percentage = i / src.width.to_f
71
+ i = src.width - i if face_right
72
+ factor = scale + (percentage * (1.0 - scale)) # linear interpolation
73
+ dest_cxt.save
74
+ dest_cxt.translate 0, src.height / 2.0 * (1.0 - factor)
75
+ dest_cxt.scale factor * scale, factor
76
+ dest_cxt.set_source src, 0, 0
77
+ dest_cxt.rounded_rectangle i, 0, out_thickness, src.height, 0, 0
78
+ dest_cxt.fill
79
+ dest_cxt.restore
80
+ end
81
+ return dest_cxt.target
82
+ end
83
+
84
+ end
85
+ end