geo_pattern 1.2.1 → 1.3.0

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.
@@ -1,906 +0,0 @@
1
- require 'base64'
2
- require 'digest/sha1'
3
- require 'color'
4
-
5
- module GeoPattern
6
- class Pattern
7
- DEFAULTS = {
8
- :base_color => '#933c3c'
9
- }
10
-
11
- PATTERNS = %w[
12
- octogons
13
- overlapping_circles
14
- plus_signs
15
- xes
16
- sine_waves
17
- hexagons
18
- overlapping_rings
19
- plaid
20
- triangles
21
- squares
22
- concentric_circles
23
- diamonds
24
- tessellation
25
- nested_squares
26
- mosaic_squares
27
- chevrons
28
- ].freeze
29
-
30
- FILL_COLOR_DARK = "#222"
31
- FILL_COLOR_LIGHT = "#ddd"
32
- STROKE_COLOR = "#000"
33
- STROKE_OPACITY = 0.02
34
- OPACITY_MIN = 0.02
35
- OPACITY_MAX = 0.15
36
-
37
- attr_reader :opts, :hash, :svg
38
-
39
- def initialize(string, opts={})
40
- @opts = DEFAULTS.merge(opts)
41
- @hash = Digest::SHA1.hexdigest string
42
- @svg = SVG.new
43
- generate_background
44
- generate_pattern
45
- end
46
-
47
- def svg_string
48
- svg.to_s
49
- end
50
-
51
- def to_s
52
- svg_string
53
- end
54
-
55
- def base64_string
56
- Base64.strict_encode64(svg.to_s)
57
- end
58
-
59
- def uri_image
60
- "url(data:image/svg+xml;base64,#{base64_string});"
61
- end
62
-
63
- def generate_background
64
- if opts[:color]
65
- rgb = Color::RGB.from_html(opts[:color])
66
- else
67
- hue_offset = map(hex_val(14, 3), 0, 4095, 0, 359)
68
- sat_offset = hex_val(17, 1)
69
- base_color = Color::RGB.from_html(opts[:base_color]).to_hsl
70
- base_color.hue = base_color.hue - hue_offset;
71
-
72
- if (sat_offset % 2 == 0)
73
- base_color.saturation = base_color.saturation + sat_offset
74
- else
75
- base_color.saturation = base_color.saturation - sat_offset
76
- end
77
- rgb = base_color.to_rgb
78
- end
79
- r = (rgb.r * 255).round
80
- g = (rgb.g * 255).round
81
- b = (rgb.b * 255).round
82
- svg.rect(0, 0, "100%", "100%", {"fill" => "rgb(#{r}, #{g}, #{b})"})
83
- end
84
-
85
- def generate_pattern
86
- if opts[:generator]
87
- if PATTERNS.include?(opts[:generator])
88
- send("geo_#{opts[:generator]}")
89
- else
90
- abort("Error: the requested generator is invalid.")
91
- end
92
- else
93
- pattern = hex_val(20, 1)
94
- send("geo_#{PATTERNS[pattern]}")
95
- end
96
- end
97
-
98
- def geo_hexagons
99
- scale = hex_val(0, 1)
100
- side_length = map(scale, 0, 15, 8, 60)
101
- hex_height = side_length * Math.sqrt(3)
102
- hex_width = side_length * 2
103
- hex = build_hexagon_shape(side_length)
104
-
105
- svg.set_width((hex_width * 3) + (side_length * 3))
106
- svg.set_height(hex_height * 6)
107
-
108
- i = 0
109
- for y in 0..5
110
- for x in 0..5
111
- val = hex_val(i, 1)
112
- dy = x % 2 == 0 ? y*hex_height : y*hex_height + hex_height/2
113
- opacity = opacity(val)
114
- fill = fill_color(val)
115
-
116
- styles = {
117
- "fill" => fill,
118
- "fill-opacity" => opacity,
119
- "stroke" => STROKE_COLOR,
120
- "stroke-opacity" => STROKE_OPACITY
121
- }
122
-
123
- svg.polyline(hex, styles.merge({"transform" => "translate(#{x*side_length*1.5 - hex_width/2}, #{dy - hex_height/2})"}))
124
-
125
- # Add an extra one at top-right, for tiling.
126
- if (x == 0)
127
- svg.polyline(hex, styles.merge({"transform" => "translate(#{6*side_length*1.5 - hex_width/2}, #{dy - hex_height/2})"}))
128
- end
129
-
130
- # Add an extra row at the end that matches the first row, for tiling.
131
- if (y == 0)
132
- dy = x % 2 == 0 ? 6*hex_height : 6*hex_height + hex_height/2;
133
- svg.polyline(hex, styles.merge({"transform" => "translate(#{x*side_length*1.5 - hex_width/2}, #{dy - hex_height/2})"}))
134
- end
135
-
136
- # Add an extra one at bottom-right, for tiling.
137
- if (x == 0 && y == 0)
138
- svg.polyline(hex, styles.merge({"transform" => "translate(#{6*side_length*1.5 - hex_width/2}, #{5*hex_height + hex_height/2})"}))
139
- end
140
- i += 1
141
- end
142
- end
143
- end
144
-
145
- def geo_sine_waves
146
- period = map(hex_val(0, 1), 0, 15, 100, 400).floor
147
- amplitude = map(hex_val(1, 1), 0, 15, 30, 100).floor
148
- wave_width = map(hex_val(2, 1), 0, 15, 3, 30).floor
149
-
150
- svg.set_width(period)
151
- svg.set_height(wave_width * 36)
152
-
153
- for i in 0..35
154
- val = hex_val(i, 1)
155
- opacity = opacity(val)
156
- fill = fill_color(val)
157
- x_offset = period / 4 * 0.7
158
-
159
- styles = {
160
- "fill" => "none",
161
- "stroke" => fill,
162
- "style" => {
163
- "opacity" => opacity,
164
- "stroke-width" => "#{wave_width}px"
165
- }
166
- }
167
-
168
- str = "M0 "+amplitude.to_s+
169
- " C "+x_offset.to_s+" 0, "+(period/2 - x_offset).to_s+" 0, "+(period/2).to_s+" "+amplitude.to_s+
170
- " S "+(period-x_offset).to_s+" "+(amplitude*2).to_s+", "+period.to_s+" "+amplitude.to_s+
171
- " S "+(period*1.5-x_offset).to_s+" 0, "+(period*1.5).to_s+", "+amplitude.to_s;
172
-
173
- svg.path(str, styles.merge({"transform" => "translate(-#{period/4}, #{wave_width*i-amplitude*1.5})"}))
174
- svg.path(str, styles.merge({"transform" => "translate(-#{period/4}, #{wave_width*i-amplitude*1.5 + wave_width*36})"}))
175
- end
176
- end
177
-
178
- def geo_chevrons
179
- chevron_width = map(hex_val(0, 1), 0, 15, 30, 80)
180
- chevron_height = map(hex_val(0, 1), 0, 15, 30, 80)
181
- chevron = build_chevron_shape(chevron_width, chevron_height)
182
-
183
- svg.set_width(chevron_width * 6)
184
- svg.set_height(chevron_height * 6 * 0.66)
185
-
186
- i = 0
187
- for y in 0..5
188
- for x in 0..5
189
- val = hex_val(i, 1)
190
- opacity = opacity(val)
191
- fill = fill_color(val)
192
-
193
- styles = {
194
- "stroke" => STROKE_COLOR,
195
- "stroke-opacity" => STROKE_OPACITY,
196
- "fill" => fill,
197
- "fill-opacity" => opacity,
198
- "stroke-width" => 1
199
- }
200
-
201
- svg.group(chevron, styles.merge({"transform" => "translate(#{x*chevron_width},#{y*chevron_height*0.66 - chevron_height/2})"}))
202
-
203
- # Add an extra row at the end that matches the first row, for tiling.
204
- if (y == 0)
205
- svg.group(chevron, styles.merge({"transform" => "translate(#{x*chevron_width},#{6*chevron_height*0.66 - chevron_height/2})"}))
206
- end
207
- i += 1
208
- end
209
- end
210
- end
211
-
212
- def geo_plus_signs
213
- square_size = map(hex_val(0, 1), 0, 15, 10, 25)
214
- plus_size = square_size * 3
215
- plus_shape = build_plus_shape(square_size)
216
-
217
- svg.set_width(square_size * 12)
218
- svg.set_height(square_size * 12)
219
-
220
- i = 0
221
- for y in 0..5
222
- for x in 0..5
223
- val = hex_val(i, 1)
224
- opacity = opacity(val)
225
- fill = fill_color(val)
226
- dx = (y % 2 == 0) ? 0 : 1
227
-
228
- styles = {
229
- "fill" => fill,
230
- "stroke" => STROKE_COLOR,
231
- "stroke-opacity" => STROKE_OPACITY,
232
- "style" => {
233
- "fill-opacity" => opacity
234
- }
235
- }
236
-
237
- svg.group(plus_shape, styles.merge({
238
- "transform" => "translate(#{x*plus_size - x*square_size + dx*square_size - square_size},#{y*plus_size - y*square_size - plus_size/2})"}))
239
-
240
- # Add an extra column on the right for tiling.
241
- if (x == 0)
242
- svg.group(plus_shape, styles.merge({
243
- "transform" => "translate(#{4*plus_size - x*square_size + dx*square_size - square_size},#{y*plus_size - y*square_size - plus_size/2})"}))
244
- end
245
-
246
- # Add an extra row on the bottom that matches the first row, for tiling.
247
- if (y == 0)
248
- svg.group(plus_shape, styles.merge({
249
- "transform" => "translate(#{x*plus_size - x*square_size + dx*square_size - square_size},#{4*plus_size - y*square_size - plus_size/2})"}))
250
- end
251
-
252
- # Add an extra one at top-right and bottom-right, for tiling.
253
- if (x == 0 && y == 0)
254
- svg.group(plus_shape, styles.merge({
255
- "transform" => "translate(#{4*plus_size - x*square_size + dx*square_size - square_size},#{4*plus_size - y*square_size - plus_size/2})"}))
256
- end
257
- i += 1
258
- end
259
- end
260
- end
261
-
262
- def geo_xes
263
- square_size = map(hex_val(0, 1), 0, 15, 10, 25)
264
- x_shape = build_plus_shape(square_size) # rotated later
265
- x_size = square_size * 3 * 0.943
266
-
267
- svg.set_width(x_size * 3)
268
- svg.set_height(x_size * 3)
269
-
270
- i = 0
271
- for y in 0..5
272
- for x in 0..5
273
- val = hex_val(i, 1)
274
- opacity = opacity(val)
275
- dy = x % 2 == 0 ? y*x_size - x_size*0.5 : y*x_size - x_size*0.5 + x_size/4
276
- fill = fill_color(val)
277
-
278
- styles = {
279
- "fill" => fill,
280
- "style" => {
281
- "opacity" => opacity
282
- }
283
- }
284
-
285
- svg.group(x_shape, styles.merge({
286
- "transform" => "translate(#{x*x_size/2 - x_size/2},#{dy - y*x_size/2}) rotate(45, #{x_size/2}, #{x_size/2})"}))
287
-
288
- # Add an extra column on the right for tiling.
289
- if (x == 0)
290
- svg.group(x_shape, styles.merge({
291
- "transform" => "translate(#{6*x_size/2 - x_size/2},#{dy - y*x_size/2}) rotate(45, #{x_size/2}, #{x_size/2})"}))
292
- end
293
-
294
- # Add an extra row on the bottom that matches the first row, for tiling.
295
- if (y == 0)
296
- dy = x % 2 == 0 ? 6*x_size - x_size/2 : 6*x_size - x_size/2 + x_size/4;
297
- svg.group(x_shape, styles.merge({
298
- "transform" => "translate(#{x*x_size/2 - x_size/2},#{dy - 6*x_size/2}) rotate(45, #{x_size/2}, #{x_size/2})"}))
299
- end
300
-
301
- # These can hang off the bottom, so put a row at the top for tiling.
302
- if (y == 5)
303
- svg.group(x_shape, styles.merge({
304
- "transform" => "translate(#{x*x_size/2 - x_size/2},#{dy - 11*x_size/2}) rotate(45, #{x_size/2}, #{x_size/2})"}))
305
- end
306
-
307
- # Add an extra one at top-right and bottom-right, for tiling.
308
- if (x == 0 && y == 0)
309
- svg.group(x_shape, styles.merge({
310
- "transform" => "translate(#{6*x_size/2 - x_size/2},#{dy - 6*x_size/2}) rotate(45, #{x_size/2}, #{x_size/2})"}))
311
- end
312
- i += 1
313
- end
314
- end
315
- end
316
-
317
- def geo_overlapping_circles
318
- scale = hex_val(0, 1)
319
- diameter = map(scale, 0, 15, 25, 200)
320
- radius = diameter/2;
321
-
322
- svg.set_width(radius * 6)
323
- svg.set_height(radius * 6)
324
-
325
- i = 0
326
- for y in 0..5
327
- for x in 0..5
328
- val = hex_val(i, 1)
329
- opacity = opacity(val)
330
- fill = fill_color(val)
331
-
332
- styles = {
333
- "fill" => fill,
334
- "style" => {
335
- "opacity" => opacity
336
- }
337
- }
338
-
339
- svg.circle(x*radius, y*radius, radius, styles)
340
-
341
- # Add an extra one at top-right, for tiling.
342
- if (x == 0)
343
- svg.circle(6*radius, y*radius, radius, styles)
344
- end
345
-
346
- # Add an extra row at the end that matches the first row, for tiling.
347
- if (y == 0)
348
- svg.circle(x*radius, 6*radius, radius, styles)
349
- end
350
-
351
- # Add an extra one at bottom-right, for tiling.
352
- if (x == 0 and y == 0)
353
- svg.circle(6*radius, 6*radius, radius, styles)
354
- end
355
- i += 1
356
- end
357
- end
358
- end
359
-
360
- def geo_octogons
361
- square_size = map(hex_val(0, 1), 0, 15, 10, 60)
362
- tile = build_octogon_shape(square_size)
363
-
364
- svg.set_width(square_size * 6)
365
- svg.set_height(square_size * 6)
366
-
367
- i = 0
368
- for y in 0..5
369
- for x in 0..5
370
- val = hex_val(i, 1)
371
- opacity = opacity(val)
372
- fill = fill_color(val)
373
-
374
- svg.polyline(tile, {
375
- "fill" => fill,
376
- "fill-opacity" => opacity,
377
- "stroke" => STROKE_COLOR,
378
- "stroke-opacity" => STROKE_OPACITY,
379
- "transform" => "translate(#{x*square_size}, #{y*square_size})"
380
- })
381
- i += 1
382
- end
383
- end
384
- end
385
-
386
- def geo_squares
387
- square_size = map(hex_val(0, 1), 0, 15, 10, 60)
388
-
389
- svg.set_width(square_size * 6)
390
- svg.set_height(square_size * 6)
391
-
392
- i = 0
393
- for y in 0..5
394
- for x in 0..5
395
- val = hex_val(i, 1)
396
- opacity = opacity(val)
397
- fill = fill_color(val)
398
-
399
- svg.rect(x*square_size, y*square_size, square_size, square_size, {
400
- "fill" => fill,
401
- "fill-opacity" => opacity,
402
- "stroke" => STROKE_COLOR,
403
- "stroke-opacity" => STROKE_OPACITY
404
- })
405
- i += 1
406
- end
407
- end
408
- end
409
-
410
- def geo_concentric_circles
411
- scale = hex_val(0, 1)
412
- ring_size = map(scale, 0, 15, 10, 60)
413
- stroke_width = ring_size / 5
414
-
415
- svg.set_width((ring_size + stroke_width) * 6)
416
- svg.set_height((ring_size + stroke_width) * 6)
417
-
418
- i = 0
419
- for y in 0..5
420
- for x in 0..5
421
- val = hex_val(i, 1)
422
- opacity = opacity(val)
423
- fill = fill_color(val)
424
-
425
- svg.circle(
426
- x*ring_size + x*stroke_width + (ring_size + stroke_width)/2,
427
- y*ring_size + y*stroke_width + (ring_size + stroke_width)/2,
428
- ring_size/2, {
429
- "fill" => "none",
430
- "stroke" => fill,
431
- "style" => {
432
- "opacity" => opacity,
433
- "stroke-width" => "#{stroke_width}px"
434
- }
435
- })
436
-
437
- val = hex_val(39-i, 1)
438
- opacity = opacity(val)
439
- fill = fill_color(val)
440
-
441
- svg.circle(
442
- x*ring_size + x*stroke_width + (ring_size + stroke_width)/2,
443
- y*ring_size + y*stroke_width + (ring_size + stroke_width)/2,
444
- ring_size/4, {
445
- "fill" => fill,
446
- "fill-opacity" => opacity
447
- })
448
-
449
- i += 1
450
- end
451
- end
452
- end
453
-
454
- def geo_overlapping_rings
455
- scale = hex_val(0, 1)
456
- ring_size = map(scale, 0, 15, 10, 60)
457
- stroke_width = ring_size / 4
458
-
459
- svg.set_width(ring_size * 6)
460
- svg.set_height(ring_size * 6)
461
-
462
- i = 0
463
- for y in 0..5
464
- for x in 0..5
465
- val = hex_val(i, 1)
466
- opacity = opacity(val)
467
- fill = fill_color(val)
468
-
469
- styles = {
470
- "fill" => "none",
471
- "stroke" => fill,
472
- "style" => {
473
- "opacity" => opacity,
474
- "stroke-width" => "#{stroke_width}px"
475
- }
476
- }
477
-
478
- svg.circle(x*ring_size, y*ring_size, ring_size - stroke_width/2, styles)
479
-
480
- # Add an extra one at top-right, for tiling.
481
- if (x == 0)
482
- svg.circle(6*ring_size, y*ring_size, ring_size - stroke_width/2, styles)
483
- end
484
-
485
- if (y == 0)
486
- svg.circle(x*ring_size, 6*ring_size, ring_size - stroke_width/2, styles)
487
- end
488
-
489
- if (x == 0 and y == 0)
490
- svg.circle(6*ring_size, 6*ring_size, ring_size - stroke_width/2, styles)
491
- end
492
- i += 1
493
- end
494
- end
495
- end
496
-
497
- def geo_triangles
498
- scale = hex_val(0, 1)
499
- side_length = map(scale, 0, 15, 15, 80)
500
- triangle_height = side_length/2 * Math.sqrt(3)
501
- triangle = build_triangle_shape(side_length, triangle_height)
502
-
503
- svg.set_width(side_length * 3)
504
- svg.set_height(triangle_height * 6)
505
-
506
- i = 0
507
- for y in 0..5
508
- for x in 0..5
509
- val = hex_val(i, 1)
510
- opacity = opacity(val)
511
- fill = fill_color(val)
512
-
513
- styles = {
514
- "fill" => fill,
515
- "fill-opacity" => opacity,
516
- "stroke" => STROKE_COLOR,
517
- "stroke-opacity" => STROKE_OPACITY
518
- }
519
-
520
- rotation = ""
521
- if y % 2 == 0
522
- rotation = x % 2 == 0 ? 180 : 0
523
- else
524
- rotation = x % 2 != 0 ? 180 : 0
525
- end
526
-
527
- svg.polyline(triangle, styles.merge({
528
- "transform" => "translate(#{x*side_length*0.5 - side_length/2}, #{triangle_height*y}) rotate(#{rotation}, #{side_length/2}, #{triangle_height/2})"}))
529
-
530
- # Add an extra one at top-right, for tiling.
531
- if (x == 0)
532
- svg.polyline(triangle, styles.merge({
533
- "transform" => "translate(#{6*side_length*0.5 - side_length/2}, #{triangle_height*y}) rotate(#{rotation}, #{side_length/2}, #{triangle_height/2})"}))
534
- end
535
- i += 1
536
- end
537
- end
538
- end
539
-
540
- def geo_diamonds
541
- diamond_width = map(hex_val(0, 1), 0, 15, 10, 50)
542
- diamond_height = map(hex_val(1, 1), 0, 15, 10, 50)
543
- diamond = build_diamond_shape(diamond_width, diamond_height)
544
-
545
- svg.set_width(diamond_width * 6)
546
- svg.set_height(diamond_height * 3)
547
-
548
- i = 0
549
- for y in 0..5
550
- for x in 0..5
551
- val = hex_val(i, 1)
552
- opacity = opacity(val)
553
- fill = fill_color(val)
554
-
555
- styles = {
556
- "fill" => fill,
557
- "fill-opacity" => opacity,
558
- "stroke" => STROKE_COLOR,
559
- "stroke-opacity" => STROKE_OPACITY
560
- }
561
-
562
- dx = (y % 2 == 0) ? 0 : diamond_width / 2
563
-
564
- svg.polyline(diamond, styles.merge({
565
- "transform" => "translate(#{x*diamond_width - diamond_width/2 + dx}, #{diamond_height/2*y - diamond_height/2})"}))
566
-
567
- # Add an extra one at top-right, for tiling.
568
- if (x == 0)
569
- svg.polyline(diamond, styles.merge({
570
- "transform" => "translate(#{6*diamond_width - diamond_width/2 + dx}, #{diamond_height/2*y - diamond_height/2})"}))
571
- end
572
-
573
- # Add an extra row at the end that matches the first row, for tiling.
574
- if (y == 0)
575
- svg.polyline(diamond, styles.merge({
576
- "transform" => "translate(#{x*diamond_width - diamond_width/2 + dx}, #{diamond_height/2*6 - diamond_height/2})"}))
577
- end
578
-
579
- # Add an extra one at bottom-right, for tiling.
580
- if (x == 0 and y == 0)
581
- svg.polyline(diamond, styles.merge({
582
- "transform" => "translate(#{6*diamond_width - diamond_width/2 + dx}, #{diamond_height/2*6 - diamond_height/2})"}))
583
- end
584
- i += 1
585
- end
586
- end
587
- end
588
-
589
- def geo_nested_squares
590
- block_size = map(hex_val(0, 1), 0, 15, 4, 12)
591
- square_size = block_size * 7
592
-
593
- svg.set_width((square_size + block_size)*6 + block_size*6)
594
- svg.set_height((square_size + block_size)*6 + block_size*6)
595
-
596
- i = 0
597
- for y in 0..5
598
- for x in 0..5
599
- val = hex_val(i, 1)
600
- opacity = opacity(val)
601
- fill = fill_color(val)
602
-
603
- styles = {
604
- "fill" => "none",
605
- "stroke" => fill,
606
- "style" => {
607
- "opacity" => opacity,
608
- "stroke-width" => "#{block_size}px"
609
- }
610
- }
611
-
612
- svg.rect(x*square_size + x*block_size*2 + block_size/2,
613
- y*square_size + y*block_size*2 + block_size/2,
614
- square_size, square_size, styles)
615
-
616
- val = hex_val(39-i, 1)
617
- opacity = opacity(val)
618
- fill = fill_color(val)
619
-
620
- styles = {
621
- "fill" => "none",
622
- "stroke" => fill,
623
- "style" => {
624
- "opacity" => opacity,
625
- "stroke-width" => "#{block_size}px"
626
- }
627
- }
628
-
629
- svg.rect(x*square_size + x*block_size*2 + block_size/2 + block_size*2,
630
- y*square_size + y*block_size*2 + block_size/2 + block_size*2,
631
- block_size * 3, block_size * 3, styles)
632
- i += 1
633
- end
634
- end
635
- end
636
-
637
- def geo_mosaic_squares
638
- triangle_size = map(hex_val(0, 1), 0, 15, 15, 50)
639
-
640
- svg.set_width(triangle_size * 8)
641
- svg.set_height(triangle_size * 8)
642
-
643
- i = 0
644
- for y in 0..3
645
- for x in 0..3
646
-
647
- if (x % 2 == 0)
648
- if (y % 2 == 0)
649
- draw_outer_mosaic_tile(x*triangle_size*2, y*triangle_size*2, triangle_size, hex_val(i, 1))
650
- else
651
- draw_inner_mosaic_tile(x*triangle_size*2, y*triangle_size*2, triangle_size, [hex_val(i, 1), hex_val(i+1, 1)])
652
- end
653
- else
654
- if (y % 2 == 0)
655
- draw_inner_mosaic_tile(x*triangle_size*2, y*triangle_size*2, triangle_size, [hex_val(i, 1), hex_val(i+1, 1)])
656
- else
657
- draw_outer_mosaic_tile(x*triangle_size*2, y*triangle_size*2, triangle_size, hex_val(i, 1))
658
- end
659
- end
660
- i += 1
661
- end
662
- end
663
- end
664
-
665
- def geo_plaid
666
- height = 0
667
- width = 0
668
-
669
- # horizontal stripes
670
- i = 0
671
- 18.times do
672
- space = hex_val(i, 1)
673
- height += space + 5
674
-
675
- val = hex_val(i+1, 1)
676
- opacity = opacity(val)
677
- fill = fill_color(val)
678
- stripe_height = val + 5
679
-
680
- svg.rect(0, height, "100%", stripe_height, {
681
- "opacity" => opacity,
682
- "fill" => fill
683
- })
684
- height += stripe_height
685
- i += 2
686
- end
687
-
688
- # vertical stripes
689
- i = 0
690
- 18.times do
691
- space = hex_val(i, 1)
692
- width += space + 5
693
-
694
- val = hex_val(i+1, 1)
695
- opacity = opacity(val)
696
- fill = fill_color(val)
697
- stripe_width = val + 5
698
-
699
- svg.rect(width, 0, stripe_width, "100%", {
700
- "opacity" => opacity,
701
- "fill" => fill
702
- })
703
- width += stripe_width
704
- i += 2
705
- end
706
-
707
- svg.set_width(width)
708
- svg.set_height(height)
709
- end
710
-
711
- def geo_tessellation
712
- # 3.4.6.4 semi-regular tessellation
713
- side_length = map(hex_val(0, 1), 0, 15, 5, 40)
714
- hex_height = side_length * Math.sqrt(3)
715
- hex_width = side_length * 2
716
- triangle_height = side_length/2 * Math.sqrt(3)
717
- triangle = build_rotated_triangle_shape(side_length, triangle_height)
718
- tile_width = side_length*3 + triangle_height*2
719
- tile_height = (hex_height * 2) + (side_length * 2)
720
-
721
- svg.set_width(tile_width)
722
- svg.set_height(tile_height)
723
-
724
- for i in 0..19
725
- val = hex_val(i, 1)
726
- opacity = opacity(val)
727
- fill = fill_color(val)
728
-
729
- styles = {
730
- "stroke" => STROKE_COLOR,
731
- "stroke-opacity" => STROKE_OPACITY,
732
- "fill" => fill,
733
- "fill-opacity" => opacity,
734
- "stroke-width" => 1
735
- }
736
-
737
- case i
738
- when 0 # all 4 corners
739
- svg.rect(-side_length/2, -side_length/2, side_length, side_length, styles)
740
- svg.rect(tile_width - side_length/2, -side_length/2, side_length, side_length, styles)
741
- svg.rect(-side_length/2, tile_height-side_length/2, side_length, side_length, styles)
742
- svg.rect(tile_width - side_length/2, tile_height-side_length/2, side_length, side_length, styles)
743
- when 1 # center / top square
744
- svg.rect(hex_width/2 + triangle_height, hex_height/2, side_length, side_length, styles)
745
- when 2 # side squares
746
- svg.rect(-side_length/2, tile_height/2-side_length/2, side_length, side_length, styles)
747
- svg.rect(tile_width-side_length/2, tile_height/2-side_length/2, side_length, side_length, styles)
748
- when 3 # center / bottom square
749
- svg.rect(hex_width/2 + triangle_height, hex_height * 1.5 + side_length, side_length, side_length, styles)
750
- when 4 # left top / bottom triangle
751
- svg.polyline(triangle, styles.merge({"transform" => "translate(#{side_length/2}, #{-side_length/2}) rotate(0, #{side_length/2}, #{triangle_height/2})"}))
752
- svg.polyline(triangle, styles.merge({"transform" => "translate(#{side_length/2}, #{tile_height--side_length/2}) rotate(0, #{side_length/2}, #{triangle_height/2}) scale(1, -1)"}))
753
- when 5 # right top / bottom triangle
754
- svg.polyline(triangle, styles.merge({"transform" => "translate(#{tile_width-side_length/2}, #{-side_length/2}) rotate(0, #{side_length/2}, #{triangle_height/2}) scale(-1, 1)"}))
755
- svg.polyline(triangle, styles.merge({"transform" => "translate(#{tile_width-side_length/2}, #{tile_height+side_length/2}) rotate(0, #{side_length/2}, #{triangle_height/2}) scale(-1, -1)"}))
756
- when 6 # center / top / right triangle
757
- svg.polyline(triangle, styles.merge({"transform" => "translate(#{tile_width/2+side_length/2}, #{hex_height/2})"}))
758
- when 7 # center / top / left triangle
759
- svg.polyline(triangle, styles.merge({"transform" => "translate(#{tile_width-tile_width/2-side_length/2}, #{hex_height/2}) scale(-1, 1)"}))
760
- when 8 # center / bottom / right triangle
761
- svg.polyline(triangle, styles.merge({"transform" => "translate(#{tile_width/2+side_length/2}, #{tile_height-hex_height/2}) scale(1, -1)"}))
762
- when 9 # center / bottom / left triangle
763
- svg.polyline(triangle, styles.merge({"transform" => "translate(#{tile_width-tile_width/2-side_length/2}, #{tile_height-hex_height/2}) scale(-1, -1)"}))
764
- when 10 # left / middle triangle
765
- svg.polyline(triangle, styles.merge({"transform" => "translate(#{side_length/2}, #{tile_height/2 - side_length/2})"}))
766
- when 11 # right / middle triangle
767
- svg.polyline(triangle, styles.merge({"transform" => "translate(#{tile_width-side_length/2}, #{tile_height/2 - side_length/2}) scale(-1, 1)"}))
768
- when 12 # left / top square
769
- svg.rect(0, 0, side_length, side_length,
770
- styles.merge({"transform" => "translate(#{side_length/2}, #{side_length/2}) rotate(-30, 0, 0)"}))
771
- when 13 # right / top square
772
- svg.rect(0, 0, side_length, side_length,
773
- styles.merge({"transform" => "scale(-1, 1) translate(#{-tile_width+side_length/2}, #{side_length/2}) rotate(-30, 0, 0)" }))
774
- when 14 # left / center-top square
775
- svg.rect(0, 0, side_length, side_length,
776
- styles.merge({"transform" => "translate(#{side_length/2}, #{tile_height/2-side_length/2-side_length}) rotate(30, 0, #{side_length})" }))
777
- when 15 # right / center-top square
778
- svg.rect(0, 0, side_length, side_length,
779
- styles.merge({"transform" => "scale(-1, 1) translate(#{-tile_width+side_length/2}, #{tile_height/2-side_length/2-side_length}) rotate(30, 0, #{side_length})" }))
780
- when 16 # left / center-top square
781
- svg.rect(0, 0, side_length, side_length,
782
- styles.merge({"transform" => "scale(1, -1) translate(#{side_length/2}, #{-tile_height+tile_height/2-side_length/2-side_length}) rotate(30, 0, #{side_length})" }))
783
- when 17 # right / center-bottom square
784
- svg.rect(0, 0, side_length, side_length,
785
- styles.merge({"transform" => "scale(-1, -1) translate(#{-tile_width+side_length/2}, #{-tile_height+tile_height/2-side_length/2-side_length}) rotate(30, 0, #{side_length})" }))
786
- when 18 # left / bottom square
787
- svg.rect(0, 0, side_length, side_length,
788
- styles.merge({"transform" => "scale(1, -1) translate(#{side_length/2}, #{-tile_height+side_length/2}) rotate(-30, 0, 0)"}))
789
- when 19 # right / bottom square
790
- svg.rect(0, 0, side_length, side_length,
791
- styles.merge({"transform" => "scale(-1, -1) translate(#{-tile_width+side_length/2}, #{-tile_height+side_length/2}) rotate(-30, 0, 0)"}))
792
- end
793
- end
794
- end
795
-
796
- def build_chevron_shape(width, height)
797
- e = height * 0.66
798
- [
799
- %Q{polyline("0,0,#{width/2},#{height-e},#{width/2},#{height},0,#{e},0,0")},
800
- %Q{polyline("#{width/2},#{height-e},#{width},0,#{width},#{e},#{width/2},#{height},#{width/2},#{height-e}")}
801
- ]
802
- end
803
-
804
- def build_octogon_shape(square_size)
805
- s = square_size
806
- c = s * 0.33
807
- "#{c},0,#{s-c},0,#{s},#{c},#{s},#{s-c},#{s-c},#{s},#{c},#{s},0,#{s-c},0,#{c},#{c},0"
808
- end
809
-
810
- def build_hexagon_shape(sideLength)
811
- c = sideLength
812
- a = c/2
813
- b = Math.sin(60 * Math::PI / 180)*c
814
- "0,#{b},#{a},0,#{a+c},0,#{2*c},#{b},#{a+c},#{2*b},#{a},#{2*b},0,#{b}"
815
- end
816
-
817
- def build_plus_shape(square_size)
818
- [
819
- "rect(#{square_size},0,#{square_size},#{square_size * 3})",
820
- "rect(0, #{square_size},#{square_size * 3},#{square_size})"
821
- ]
822
- end
823
-
824
- def build_triangle_shape(side_length, height)
825
- half_width = side_length / 2
826
- "#{half_width}, 0, #{side_length}, #{height}, 0, #{height}, #{half_width}, 0"
827
- end
828
-
829
- def build_rotated_triangle_shape(side_length, width)
830
- half_height = side_length / 2
831
- "0, 0, #{width}, #{half_height}, 0, #{side_length}, 0, 0"
832
- end
833
-
834
- def build_right_triangle_shape(side_length)
835
- "0, 0, #{side_length}, #{side_length}, 0, #{side_length}, 0, 0"
836
- end
837
-
838
- def build_diamond_shape(width, height)
839
- "#{width/2}, 0, #{width}, #{height/2}, #{width/2}, #{height}, 0, #{height/2}"
840
- end
841
-
842
- def draw_inner_mosaic_tile(x, y, triangle_size, vals)
843
- triangle = build_right_triangle_shape(triangle_size)
844
- opacity = opacity(vals[0])
845
- fill = fill_color(vals[0])
846
- styles = {
847
- "stroke" => STROKE_COLOR,
848
- "stroke-opacity" => STROKE_OPACITY,
849
- "fill-opacity" => opacity,
850
- "fill" => fill
851
- }
852
- svg.polyline(triangle, styles.merge({"transform" => "translate(#{x+triangle_size}, #{y}) scale(-1, 1)"}))
853
- svg.polyline(triangle, styles.merge({"transform" => "translate(#{x+triangle_size}, #{y+triangle_size*2}) scale(1, -1)"}))
854
-
855
- opacity = opacity(vals[1])
856
- fill = fill_color(vals[1])
857
- styles = {
858
- "stroke" => STROKE_COLOR,
859
- "stroke-opacity" => STROKE_OPACITY,
860
- "fill-opacity" => opacity,
861
- "fill" => fill
862
- }
863
- svg.polyline(triangle, styles.merge({"transform" => "translate(#{x+triangle_size}, #{y+triangle_size*2}) scale(-1, -1)"}))
864
- svg.polyline(triangle, styles.merge({"transform" => "translate(#{x+triangle_size}, #{y}) scale(1, 1)"}))
865
- end
866
-
867
- def draw_outer_mosaic_tile(x, y, triangle_size, val)
868
- opacity = opacity(val)
869
- fill = fill_color(val)
870
- triangle = build_right_triangle_shape(triangle_size)
871
- styles = {
872
- "stroke" => STROKE_COLOR,
873
- "stroke-opacity" => STROKE_OPACITY,
874
- "fill-opacity" => opacity,
875
- "fill" => fill
876
- }
877
-
878
- svg.polyline(triangle, styles.merge({"transform" => "translate(#{x}, #{y+triangle_size}) scale(1, -1)"}))
879
- svg.polyline(triangle, styles.merge({"transform" => "translate(#{x+triangle_size*2}, #{y+triangle_size}) scale(-1, -1)"}))
880
- svg.polyline(triangle, styles.merge({"transform" => "translate(#{x}, #{y+triangle_size}) scale(1, 1)"}))
881
- svg.polyline(triangle, styles.merge({"transform" => "translate(#{x+triangle_size*2}, #{y+triangle_size}) scale(-1, 1)"}))
882
- end
883
-
884
- def hex_val(index, len)
885
- hash[index, len || 1].to_i(16)
886
- end
887
-
888
- def fill_color(val)
889
- (val % 2 == 0) ? FILL_COLOR_LIGHT : FILL_COLOR_DARK
890
- end
891
-
892
- def opacity(val)
893
- map(val, 0, 15, OPACITY_MIN, OPACITY_MAX)
894
- end
895
-
896
- # Ruby implementation of Processing's map function
897
- # http://processing.org/reference/map_.html
898
- def map(value, v_min, v_max, d_min, d_max) # v for value, d for desired
899
- v_value = value.to_f # so it returns float
900
-
901
- v_range = v_max - v_min
902
- d_range = d_max - d_min
903
- (v_value - v_min) * d_range / v_range + d_min
904
- end
905
- end
906
- end