geo_pattern 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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