ranicoma 0.1.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.
@@ -0,0 +1,14 @@
1
+ require "ranicoma/version"
2
+ require "ranicoma/rect"
3
+ require "ranicoma/util"
4
+ require "ranicoma/creator"
5
+
6
+ require "ranicoma/design/base"
7
+ require "ranicoma/design/spbox"
8
+ require "ranicoma/design/polypoly"
9
+ require "ranicoma/design/rotobj"
10
+ require "ranicoma/design/geji"
11
+
12
+ module Ranicoma
13
+ class Error < StandardError; end
14
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+ require 'rexml/formatters/pretty'
5
+
6
+ module Ranicoma
7
+ class App
8
+ def get_option
9
+ OptionParser.new do |opt|
10
+ opt.on("--size VALUE", 'size in pix (required)', Numeric)
11
+ opt.on("--seed VALUE", 'random number seed', Integer)
12
+ o={}
13
+ opt.permute(ARGV, into:o)
14
+ return o
15
+ end
16
+ end
17
+
18
+ def run
19
+ option=get_option
20
+ seed = option[:seed] || Random.new_seed
21
+ size = option[:size] || 100
22
+ formatter = REXML::Formatters::Pretty.new
23
+ c=Ranicoma::Creator.new(seed, size)
24
+ formatter.write(c.create, $stdout)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rexml/document'
4
+ require "ranicoma/util"
5
+
6
+ module Ranicoma
7
+ class Creator
8
+ include Util
9
+ def initialize( seed, size )
10
+ @rng=Random.new(seed)
11
+ @size=size
12
+ @doc = REXML::Document.new
13
+ @doc << REXML::XMLDecl.new('1.0', 'UTF-8')
14
+ end
15
+ attr_reader(:rng)
16
+ attr_reader(:size)
17
+ attr_reader(:doc)
18
+
19
+ def create
20
+ design = Design::Base.subclasses.sample( random:rng ).new(rng)
21
+ doc << (
22
+ element("svg", xmlns:"http://www.w3.org/2000/svg", height:"#{size}px", width:"#{size}px", viewBox:"0 0 1 1" ){
23
+ design.create
24
+ }
25
+ )
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ranicoma/util"
4
+ require "ranicoma/rect"
5
+
6
+ module Ranicoma
7
+ module Design
8
+ class Base
9
+ include Util
10
+ def self.add_subclass(cls)
11
+ @subclasses ||= []
12
+ @subclasses.push(cls)
13
+ end
14
+
15
+ def rainbow(t,mx=->(v){v})
16
+ f = lambda{ |t0|
17
+ v = lambda{ |x|
18
+ case x
19
+ when 0..1 then x
20
+ when 1..2 then 2-x
21
+ else 0
22
+ end
23
+ }[t0 % 3]
24
+ (mx[v]*255).round
25
+ }
26
+ [f[t],f[t+1],f[t+2]]
27
+ end
28
+
29
+ def Base.subclasses
30
+ @subclasses
31
+ end
32
+
33
+ def initialize(rng)
34
+ @rng=rng
35
+ end
36
+
37
+ def points_str(pts)
38
+ pts.map{ |e| e.join(",") }.join(" ")
39
+ end
40
+
41
+ def rect_element(rc, col)
42
+ element("rect", **rectpos(rc), **fill(col))
43
+ end
44
+
45
+ attr_reader(:rng)
46
+
47
+ def rand(*args)
48
+ rng.rand(*args)
49
+ end
50
+
51
+ def rectpos(rc)
52
+ { x:rc.x, y:rc.y, width:rc.w, height:rc.h }
53
+ end
54
+
55
+ def rand_rotate(ary)
56
+ ix=rng.rand(ary.size)
57
+ ary[ix,ary.size-ix] + ary[0,ix]
58
+ end
59
+
60
+ def fill(col)
61
+ case col
62
+ when Array
63
+ { style:"fill:rgb(#{col.join(",")})" }
64
+ when /^\d+\,\d+\,\d+$/
65
+ { style:"fill:rgb(#{col})" }
66
+ else
67
+ { style:"fill:#{col}" }
68
+ end
69
+ end
70
+
71
+ def stroke(col, w)
72
+ c=case col
73
+ when Array
74
+ "rgb(#{col.join(",")})"
75
+ when /^\d+\,\d+\,\d+$/
76
+ "rgb(#{col})"
77
+ else
78
+ col
79
+ end
80
+ { stroke:c, "stroke-width":w }
81
+ end
82
+
83
+ def Base.inherited(subclass)
84
+ Base.add_subclass(subclass)
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,249 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rexml/document'
4
+
5
+ require "ranicoma/design/base"
6
+ require "ranicoma/rect"
7
+
8
+ module Ranicoma
9
+ module Design
10
+ class Geji < Base
11
+
12
+ PROJ_METHODS=[]
13
+
14
+ def initialize(rng)
15
+ super
16
+ @line_width = 1.0/80
17
+ @projsize = rand(0.1..0.3)
18
+ @center_rc = Rect.new( projsize, projsize, 1-projsize*2, 1-projsize*2 )
19
+ end
20
+
21
+ def proj_attr
22
+ fill(rainbow(rand(3.0))).merge(stroke(:black, line_width))
23
+ end
24
+
25
+ attr_reader(:line_width)
26
+ attr_reader(:projsize)
27
+ attr_reader(:center_rc)
28
+
29
+ def eqcolors(n)
30
+ base=rand(3.0)
31
+ Array.new(n){ |ix|
32
+ rainbow(base+3.0*ix/n)
33
+ }
34
+ end
35
+ def center_rect
36
+ gx = center_rc.w * rand(0.05..0.3)
37
+ gy = center_rc.h * rand(0.05..0.3)
38
+ w0 = (center_rc.w-gx*2)*rand(0.6..0.9)
39
+ h0 = (center_rc.h-gy*2)*rand(0.6..0.9)
40
+ w1 = (center_rc.w-gx*2)*rand(0.6..0.9)
41
+ h1 = (center_rc.h-gy*2)*rand(0.6..0.9)
42
+ subrects = if rand(2)==0
43
+ [
44
+ Rect.new( center_rc.x+gx, center_rc.y+gy, w0, h0 ),
45
+ Rect.new( center_rc.right-gx-w1, center_rc.bottom-gy-h1, w1, h1 ),
46
+ ]
47
+ else
48
+ [
49
+ Rect.new( center_rc.right-gx-w0, center_rc.y+gy, w0, h0 ),
50
+ Rect.new( center_rc.x+gx, center_rc.bottom-gy-h1, w1, h1 ),
51
+ ]
52
+ end.shuffle( random:rng )
53
+ if rand(2)==0
54
+ h = [h0, h1].min * rand(0.3..1.1)
55
+ w = [w0, w1].min * rand(0.3..1.1)
56
+ subrects.push( Rect.new( center_rc.cx-w/2, center_rc.cy-h/2, w, h ) )
57
+ end
58
+ cols = eqcolors(subrects.size+1)
59
+ makeattr=->(){
60
+ {
61
+ rx:line_width*3,
62
+ **fill(cols.pop),
63
+ **stroke(:black, line_width)
64
+ }
65
+ }
66
+ ([ center_rc ]+subrects).map{ |rc|
67
+ element( "rect", **rectpos(rc), **makeattr[] )
68
+ }
69
+ end
70
+
71
+ def shorten_rc(rc)
72
+ d = rand(rc.h/2)
73
+ Rect.new( rc.x, rc.y+d, rc.w, rc.h-d )
74
+ end
75
+
76
+ def bezier(rc, corners)
77
+ q = corners.each_cons(2).map{ |(x0,y0),(x1,y1)|
78
+ mx = (x0+x1)/2.0
79
+ my = (y0+y1)/2.0
80
+ "Q #{x0} #{y0} #{mx} #{my} "
81
+ }
82
+ path = <<~PATH
83
+ M #{rc.x} #{rc.bottom}
84
+ #{q.join}
85
+ L #{corners.last.join(" ")}
86
+ L #{rc.x} #{rc.bottom}
87
+ PATH
88
+ element( "path", d:path, **proj_attr )
89
+ end
90
+
91
+ PROJ_METHODS<<
92
+ def bezier0(rc0)
93
+ rc = shorten_rc(rc0)
94
+ y0 = rc.y + rc.h*(2.0/3)
95
+ y1 = rc.y + rc.h*(1.0/3)
96
+ corners = [
97
+ [ rc.x, rc.bottom ],
98
+ [ rc.x, y0 ],
99
+ [ rc.cx, y0 ],
100
+ [ rc.cx, y1 ],
101
+ [ rc.x, y1 ],
102
+ [ rc.x, rc.y ],
103
+ [ rc.right, rc.y ],
104
+ [ rc.right, rc.bottom ],
105
+ ]
106
+ if rand(2)==0
107
+ bezier(rc, corners.map{ |x,y| [rc.cx*2-x, y] } )
108
+ else
109
+ bezier(rc, corners)
110
+ end
111
+ end
112
+
113
+ PROJ_METHODS<<
114
+ def bezier1(rc0)
115
+ rc = shorten_rc(rc0)
116
+ y0 = rc.y + rc.h*(2.0/3)
117
+ y1 = rc.y + rc.h*(1.0/3)
118
+ corners = [
119
+ [ rc.x, rc.bottom ],
120
+ [ rc.x, y0 ],
121
+ [ rc.cx, y0 ],
122
+ [ rc.x, y0 ],
123
+ [ rc.x, y1 ],
124
+ [ rc.cx, y1 ],
125
+ [ rc.x, y1 ],
126
+ [ rc.x, rc.y ],
127
+ [ rc.right, rc.y ],
128
+ [ rc.right, rc.bottom ],
129
+ ]
130
+ if rand(2)==0
131
+ bezier(rc, corners.map{ |x,y| [rc.cx*2-x, y] } )
132
+ else
133
+ bezier(rc, corners)
134
+ end
135
+ end
136
+
137
+ PROJ_METHODS<<
138
+ def bezier2(rc0)
139
+ rc = shorten_rc(rc0)
140
+ y0 = rc.y + rc.h*(2.0/3)
141
+ y1 = rc.y + rc.h*(1.0/3)
142
+ corners = [
143
+ [ rc.x, rc.bottom ],
144
+ [ rc.x, y0 ],
145
+ [ rc.cx, y0 ],
146
+ [ rc.x, y0 ],
147
+ [ rc.x, y1 ],
148
+ [ rc.cx, y1 ],
149
+ [ rc.x, y1 ],
150
+ [ rc.x, rc.y ],
151
+ [ rc.right, rc.y ],
152
+ [ rc.right, y1 ],
153
+ [ rc.cx, y1 ],
154
+ [ rc.right, y1 ],
155
+ [ rc.right, y0 ],
156
+ [ rc.cx, y0 ],
157
+ [ rc.right, y0 ],
158
+ [ rc.right, rc.bottom ],
159
+ ]
160
+ bezier(rc, corners)
161
+ end
162
+
163
+ PROJ_METHODS<<
164
+ def bezier3(rc0)
165
+ rc = shorten_rc(rc0)
166
+ y0 = rc.y + rc.h*0.9
167
+ y1 = rc.y + rc.h*(1.0/3)
168
+ x0 = rc.x + rc.w*0.4
169
+ x1 = rc.x + rc.w*0.6
170
+ corners = [
171
+ [ rc.x, rc.bottom ],
172
+ [ rc.x, y0 ],
173
+ [ x0, y0 ],
174
+ [ x0, y1 ],
175
+ [ rc.x, y1 ],
176
+ [ rc.x, rc.y ],
177
+ [ rc.right, rc.y ],
178
+ [ rc.right, y1 ],
179
+ [ x1, y1 ],
180
+ [ x1, y0 ],
181
+ [ rc.right, y0 ],
182
+ [ rc.right, rc.bottom ],
183
+ ]
184
+ bezier(rc, corners)
185
+ end
186
+
187
+ PROJ_METHODS<<
188
+ def box_proj(rc0)
189
+ rc = shorten_rc(rc0)
190
+ element( "rect",
191
+ **rectpos(rc),
192
+ **proj_attr )
193
+ end
194
+
195
+ PROJ_METHODS<<
196
+ def roundbox_proj(rc0)
197
+ d = rand(rc0.h/2)
198
+ rx = [rc0.w, rc0.h].min/3
199
+ rc = Rect.new( rc0.x, rc0.y+d, rc0.w, rc0.h+rx-d )
200
+ element( "rect",
201
+ **rectpos(rc),
202
+ rx:rx,
203
+ **proj_attr )
204
+ end
205
+
206
+ PROJ_METHODS<<
207
+ def triangle_proj(rc0)
208
+ rc = shorten_rc(rc0)
209
+ points = [ [rc.cx,rc.y], [rc.x, rc.bottom], [rc.right, rc.bottom] ]
210
+ element("polygon",
211
+ points:points_str(points),
212
+ **proj_attr,
213
+ "stroke-linejoin": :round )
214
+ end
215
+
216
+ def projects(pos)
217
+ count = rand(2..5)
218
+ rot = "rotate(#{90*pos},0.5,0.5)"
219
+ proj_rects = center_rc.hsplit(*([1]*count))
220
+ proj_rects.each{ |e|
221
+ e.x += e.w*0.1
222
+ e.w *= 0.8
223
+ e.y=line_width
224
+ e.h = projsize
225
+ }
226
+ pattern = rand(1..2**count-1) | rand(1..2**count-1)
227
+ element("g", transform:rot ){
228
+ Array.new(count){ |ix|
229
+ if pattern[ix]==0
230
+ []
231
+ else
232
+ send PROJ_METHODS.sample(random:rng), proj_rects[ix]
233
+ end
234
+ }.flatten
235
+ }
236
+ end
237
+
238
+ def create
239
+ [
240
+ Array.new(4){ |pos|
241
+ projects(pos)
242
+ },
243
+ center_rect
244
+ ].flatten
245
+ end
246
+ end
247
+ end
248
+ end
249
+
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rexml/document'
4
+
5
+ require "ranicoma/design/base"
6
+ require "ranicoma/rect"
7
+
8
+ module Ranicoma
9
+ module Design
10
+ class PolyPoly < Base
11
+
12
+ def initialize(rng)
13
+ super
14
+ @psize = rng.rand(6..20)
15
+ @line_width = 1.0/(@psize*5)
16
+ end
17
+
18
+ attr_reader(:psize)
19
+ attr_reader(:line_width)
20
+
21
+ def colors(n)
22
+ f = lambda{ |t0|
23
+ v = lambda{ |t|
24
+ case t
25
+ when 0..1 then t
26
+ when 1..2 then 2-t
27
+ else 0
28
+ end
29
+ }[t0 % 3]
30
+ (v**0.5*255).round
31
+ }
32
+ Array.new(n){ |i|
33
+ t = i*3.0/n+2
34
+ [f[t],f[t+1],f[t+2]]
35
+ }
36
+ end
37
+
38
+ def poly(center, r)
39
+ delta = Math::PI*2/psize*0.7
40
+ dirs = Array.new(psize){ |ix| Math::PI*2*ix / psize + rng.rand(delta) }
41
+ outer = dirs.map{ |e|
42
+ [Math.cos(e)*r+center[0], Math.sin(e)*r+center[1]]
43
+ }
44
+ edelta = rng.rand(-0.2..0.2) * Math::PI*2
45
+ ri = r * rng.rand(0.2..0.5)
46
+ cd = rng.rand(Math::PI)
47
+ rdelta = rng.rand((r-ri-line_width)*0.8)
48
+ inner_cx = center[0] + Math.cos(cd)*rdelta
49
+ inner_cy = center[1] + Math.sin(cd)*rdelta
50
+ inner = dirs.map{ |e|
51
+ t = e + edelta
52
+ [Math.cos(t)*ri+inner_cx, Math.sin(t)*ri+inner_cy]
53
+ }
54
+ use = Array.new(inner.size){ rng.rand<0.8 }
55
+ cols = rand_rotate(colors(use.count(true)))
56
+ center_poly = element( "polygon",
57
+ points:points_str(inner),
58
+ **fill(%i(black white gray).sample( random:rng)),
59
+ **stroke(:black, line_width),
60
+ "stroke-linejoin": :round )
61
+ Array.new( outer.size ){ |ix|
62
+ if ! use[ix]
63
+ []
64
+ else
65
+ pts = [
66
+ outer[ix % psize],
67
+ outer[(ix+1) % psize],
68
+ inner[(ix+1) % psize],
69
+ inner[ix % psize]
70
+ ]
71
+ element(
72
+ "polygon",
73
+ points:points_str(pts),
74
+ **fill(cols.pop),
75
+ **stroke(:black, line_width),
76
+ "stroke-linejoin": :round )
77
+ end
78
+ } + [center_poly]
79
+ end
80
+
81
+ def create
82
+ [
83
+ poly([0.5,0.5],0.5-line_width)
84
+ ].flatten
85
+ end
86
+ end
87
+ end
88
+ end
89
+