1+ function random ( min , max ) {
2+ return min + Math . random ( ) * ( max - min )
3+ }
4+
5+ function randomChoice ( list ) {
6+ return list [ Math . floor ( Math . random ( ) * list . length ) ]
7+ }
8+
9+ function clamp ( x , min , max ) {
10+ [ min , max ] = [ Math . min ( min , max ) , Math . max ( min , max ) ]
11+ return Math . min ( max , Math . max ( min , x ) )
12+ }
13+
14+ function rgb2hex ( red , green , blue ) {
15+ const rgb = blue | ( green << 8 ) | ( red << 16 )
16+ return '#' + ( 0x1000000 + rgb ) . toString ( 16 ) . slice ( 1 )
17+ }
18+
19+ export class PolygonFactory {
20+ constructor ( width , height , options ) {
21+ this . width = width
22+ this . height = height
23+ this . options = options
24+ }
25+
26+ generatePoint ( ) {
27+ if ( this . options . restriction . enable ) {
28+ const sides = this . options . restriction . sides
29+ const angle = this . options . restriction . angle
30+ const radius = Math . min ( this . width , this . height ) / 2
31+
32+ const i1 = Math . floor ( Math . random ( ) * sides )
33+ const i2 = ( i1 + 1 ) % sides
34+
35+ const x1 = Math . cos ( i1 / sides * 2 * Math . PI + angle ) * radius + this . width / 2
36+ const y1 = Math . sin ( i1 / sides * 2 * Math . PI + angle ) * radius + this . height / 2
37+
38+ const x2 = Math . cos ( i2 / sides * 2 * Math . PI + angle ) * radius + this . width / 2
39+ const y2 = Math . sin ( i2 / sides * 2 * Math . PI + angle ) * radius + this . height / 2
40+
41+ const t = Math . random ( )
42+ const x = ( 1 - t ) * x1 + t * x2
43+ const y = ( 1 - t ) * y1 + t * y2
44+
45+ return [ x , y ]
46+ } else {
47+ return [ random ( 0 , this . width ) , random ( 0 , this . height ) ]
48+ }
49+ }
50+
51+ tweakPoint ( point ) {
52+ if ( this . options . restriction . enable ) {
53+ return this . generatePoint ( )
54+ } else {
55+ return [ clamp ( point [ 0 ] + random ( - this . width / 10 , this . width / 10 ) , 0 , this . width ) ,
56+ clamp ( point [ 1 ] + random ( - this . height / 10 , this . height / 10 ) , 0 , this . height ) ]
57+ }
58+ }
59+
60+ generate ( ) {
61+ const points = [ ]
62+ const sides = Math . floor ( random ( this . options . minSides , this . options . maxSides ) )
63+
64+ for ( let i = 0 ; i < sides ; i ++ ) {
65+ points . push ( this . generatePoint ( ) )
66+ }
67+
68+ return {
69+ r : Math . floor ( Math . random ( ) * 256 ) ,
70+ g : Math . floor ( Math . random ( ) * 256 ) ,
71+ b : Math . floor ( Math . random ( ) * 256 ) ,
72+ a : this . options . transparent ? Math . random ( ) : 1 ,
73+ points
74+ }
75+ }
76+
77+ tweak ( polygon ) {
78+ const newPolygon = { ...polygon , points : polygon . points . slice ( ) }
79+ const r = random ( 0 , polygon . points . length + 4 )
80+ if ( r < 3 ) {
81+ const color = randomChoice ( [ 'r' , 'g' , 'b' ] )
82+ newPolygon [ color ] = clamp ( polygon [ color ] + random ( - 25 , 25 ) , 0 , 255 )
83+ } else if ( r < 4 && this . options . transparent ) {
84+ newPolygon . a = clamp ( polygon . a + random ( - 0.1 , 0.1 ) , 0 , 1 )
85+ } else {
86+ const i = Math . floor ( Math . random ( ) * polygon . points . length )
87+ if ( polygon . points . length > this . options . minSides && Math . random ( ) < 0.1 ) {
88+ newPolygon . points . splice ( i , 1 )
89+ } else if ( polygon . points . length < this . options . maxSides && Math . random ( ) < 0.1 ) {
90+ newPolygon . points . splice ( i , 0 , this . generatePoint ( ) )
91+ } else if ( Math . random ( ) < 0.5 ) {
92+ const j = Math . floor ( Math . random ( ) * polygon . points . length )
93+ newPolygon . points [ i ] = polygon . points [ j ]
94+ newPolygon . points [ j ] = polygon . points [ i ]
95+ } else {
96+ newPolygon . points [ i ] = this . tweakPoint ( polygon . points [ i ] )
97+ }
98+ }
99+
100+ return newPolygon
101+ }
102+
103+ draw ( ctx , polygon ) {
104+ const style = `rgba(${ polygon . r } , ${ polygon . g } , ${ polygon . b } , ${ polygon . a } )`
105+ ctx . strokeStyle = ctx . fillStyle = style
106+
107+ ctx . beginPath ( )
108+ ctx . moveTo ( polygon . points [ 0 ] [ 0 ] , polygon . points [ 0 ] [ 1 ] )
109+ for ( let i = 0 ; i < polygon . points . length ; i ++ ) {
110+ ctx . lineTo ( polygon . points [ i ] [ 0 ] , polygon . points [ i ] [ 1 ] )
111+ }
112+
113+ if ( polygon . points . length === 2 ) {
114+ ctx . stroke ( )
115+ } else {
116+ ctx . fill ( )
117+ }
118+ }
119+
120+ svg ( polygon ) {
121+ const style = rgb2hex ( polygon . r , polygon . g , polygon . b )
122+ const points = polygon . points . map ( ( [ x , y ] ) => `${ x } ,${ y } ` )
123+ return `<polygon points="${ points . join ( ' ' ) } " fill="${ style } " opacity="${ polygon . a } "/>`
124+ }
125+ }
126+
127+ export class RegularPolygonFactory extends PolygonFactory {
128+ generate ( ) {
129+ return {
130+ r : Math . floor ( Math . random ( ) * 256 ) ,
131+ g : Math . floor ( Math . random ( ) * 256 ) ,
132+ b : Math . floor ( Math . random ( ) * 256 ) ,
133+ a : this . options . transparent ? Math . random ( ) : 1 ,
134+ sides : Math . floor ( random ( this . options . minSides , this . options . maxSides ) ) ,
135+ x : random ( 0 , this . width ) , y : random ( 0 , this . height ) ,
136+ radius : random ( this . options . minRadius , this . options . maxRadius ) ,
137+ angle : random ( 0 , 2 * Math . PI )
138+ }
139+ }
140+
141+ tweak ( polygon ) {
142+ const newPolygon = { ...polygon }
143+ const r = random ( 0 , 10 )
144+ if ( r < 4 && ( polygon . sides > this . options . minSides || polygon . sides < this . options . maxSides ) ) {
145+ if ( polygon . sides > this . options . minSides ) {
146+ newPolygon . sides --
147+ } else if ( polygon . sides < this . options . maxSides ) {
148+ newPolygon . sides ++
149+ }
150+ } else if ( r < 6 ) {
151+ const color = randomChoice ( [ 'r' , 'g' , 'b' ] )
152+ newPolygon [ color ] = clamp ( polygon [ color ] + random ( - 25 , 25 ) , 0 , 255 )
153+ } else if ( r < 7 && this . options . transparent ) {
154+ newPolygon . a = clamp ( polygon . a + random ( - 0.1 , 0.1 ) , 0 , 1 )
155+ } else {
156+ newPolygon . angle = ( polygon . angle + random ( - 0.1 , 0.1 ) ) % ( 2 * Math . PI )
157+ }
158+
159+ return newPolygon
160+ }
161+
162+ getPoint ( polygon , i ) {
163+ const rot = i / polygon . sides * 2 * Math . PI + polygon . angle
164+ return [
165+ Math . cos ( rot ) * polygon . radius + polygon . x ,
166+ Math . sin ( rot ) * polygon . radius + polygon . y
167+ ]
168+ }
169+
170+ draw ( ctx , polygon ) {
171+ const style = `rgba(${ polygon . r } , ${ polygon . g } , ${ polygon . b } , ${ polygon . a } )`
172+ ctx . strokeStyle = ctx . fillStyle = style
173+
174+ ctx . beginPath ( )
175+ if ( polygon . sides === 50 ) {
176+ ctx . arc ( polygon . x , polygon . y , polygon . radius , 0 , 2 * Math . PI )
177+ } else {
178+ for ( let i = 0 ; i < polygon . sides ; i ++ ) {
179+ const [ x , y ] = this . getPoint ( polygon , i )
180+ if ( i === 0 ) {
181+ ctx . moveTo ( x , y )
182+ } else {
183+ ctx . lineTo ( x , y )
184+ }
185+ }
186+ }
187+
188+ if ( polygon . sides === 2 ) {
189+ ctx . stroke ( )
190+ } else {
191+ ctx . fill ( )
192+ }
193+ }
194+
195+ svg ( polygon ) {
196+ const style = rgb2hex ( polygon . r , polygon . g , polygon . b )
197+
198+ if ( polygon . sides === 50 ) {
199+ return `<circle cx="${ polygon . x } " cy="${ polygon . y } " r="${ polygon . radius } " fill="${ style } " />`
200+ } else {
201+ const points = [ ]
202+ for ( let i = 0 ; i < polygon . sides ; i ++ ) {
203+ const [ x , y ] = this . getPoint ( polygon , i )
204+ points . push ( `${ x } ,${ y } ` )
205+ }
206+ return `<polygon points="${ points . join ( ' ' ) } " fill="${ style } " opacity="${ polygon . a } "/>`
207+ }
208+ }
209+ }
210+
211+ export class TextureFactory {
212+ constructor ( width , height , options , textureCanvas ) {
213+ this . width = width
214+ this . height = height
215+ this . options = options
216+ this . textureCanvas = textureCanvas
217+ }
218+
219+ generate ( ) {
220+ return {
221+ a : this . options . transparent ? Math . random ( ) : 1 ,
222+ x : random ( 0 , this . width ) ,
223+ y : random ( 0 , this . height ) ,
224+ radius : random ( this . options . minRadius , this . options . maxRadius ) ,
225+ angle : random ( 0 , 2 * Math . PI )
226+ }
227+ }
228+
229+ tweak ( texture ) {
230+ const newTexture = { ...texture }
231+
232+ const r = random ( 0 , 5 )
233+ if ( r < 1 && this . options . transparent ) {
234+ newTexture . a = clamp ( texture . a + random ( - 0.1 , 0.1 ) , 0 , 1 )
235+ } else if ( r < 2 ) {
236+ newTexture . x = clamp ( texture . x + random ( - 0.1 , 0.1 ) * this . width , - texture . radius , this . width + texture . radius )
237+ } else if ( r < 3 ) {
238+ newTexture . y = clamp ( texture . y + random ( - 0.1 , 0.1 ) * this . height , - texture . radius , this . height + texture . radius )
239+ } else if ( r < 4 ) {
240+ newTexture . radius = clamp ( texture . radius + random ( this . options . minRadius , this . options . maxRadius ) / 10 , this . options . minRadius , this . options . maxRadius )
241+ } else {
242+ newTexture . angle = ( texture . angle + random ( - 0.1 , 0.1 ) ) % ( 2 * Math . PI )
243+ }
244+
245+ return newTexture
246+ }
247+
248+ draw ( ctx , texture ) {
249+ ctx . save ( )
250+ ctx . globalAlpha = texture . a
251+ ctx . translate ( texture . x , texture . y )
252+ ctx . rotate ( texture . angle )
253+
254+ let width = texture . radius
255+ let height = texture . radius
256+
257+ if ( texture . width > texture . height ) {
258+ height *= this . textureCanvas . height / this . textureCanvas . width
259+ } else {
260+ width *= this . textureCanvas . width / this . textureCanvas . height
261+ }
262+
263+ ctx . drawImage ( this . textureCanvas , - width / 2 , - height / 2 , width , height )
264+ ctx . restore ( )
265+ }
266+ }
0 commit comments