Files: 3bbf848a52801dc212408be047c6d41ea59e312e / index.js
4575 bytesRaw
1 | module.exports = solarpunkIcon |
2 | |
3 | function solarpunkIcon () { |
4 | return ` |
5 | <svg |
6 | xmlns="http://www.w3.org/2000/svg" |
7 | xmlns:xlink="http://www.w3.org/1999/xlink" |
8 | viewBox="-1 -1 2 2" |
9 | > |
10 | ${plants({ size: 0.25, offsetRadius: 0.2 })} |
11 | ${gear({ offsetRadius: 0.15, toothLength: 0.3, toothWidth: 0.2 })} |
12 | ${moons({ moonRadius: 0.1, offsetRadius: 0.7 })} |
13 | </svg> |
14 | ` |
15 | } |
16 | |
17 | function gear ({ offsetRadius, toothLength, toothWidth }) { |
18 | return ` |
19 | <style type="text/css"> |
20 | .gear { |
21 | stroke: black; |
22 | stroke-width: ${toothLength / 20}; |
23 | } |
24 | .gear-tooth { |
25 | fill: purple; |
26 | } |
27 | </style> |
28 | <g class="gear"> |
29 | ${range({ start: 0, stop: 1, step: 1/8 }) |
30 | .map(index => gearTooth({ |
31 | angle: (1/16 + index) * 2 * Math.PI, |
32 | offset: offsetRadius, |
33 | length: toothLength, |
34 | width: toothWidth |
35 | })) |
36 | .join('\n') |
37 | } |
38 | </g> |
39 | ` |
40 | } |
41 | |
42 | function gearTooth ({ angle, offset, length, width }) { |
43 | return ` |
44 | <g |
45 | class="gear-tooth" |
46 | transform="rotate(${angle * 180 / Math.PI})" |
47 | > |
48 | <rect x="${-width / 2}" y="${offset}" height="${length}" width="${width}" /> |
49 | </g> |
50 | ` |
51 | } |
52 | |
53 | function plants ({ size, offsetRadius }) { |
54 | return ` |
55 | <style type="text/css"> |
56 | .plant { |
57 | stroke: black; |
58 | stroke-width: ${size / 20}; |
59 | } |
60 | .plant-stem { |
61 | fill: none; |
62 | } |
63 | </style> |
64 | ${range({ start: 0, stop: 1, step: 1/8 }) |
65 | .map(index => plant({ |
66 | size, |
67 | angle: index * 2 * Math.PI, |
68 | offsetRadius |
69 | })) |
70 | .join('\n') |
71 | } |
72 | ` |
73 | } |
74 | |
75 | function plant ({ size, angle, offsetRadius }) { |
76 | return ` |
77 | <g |
78 | class="plant" |
79 | transform=" |
80 | rotate(${angle * 180 / Math.PI}) |
81 | translate(0, ${offsetRadius}) |
82 | " |
83 | > |
84 | <path class="plant-stem" |
85 | d=" |
86 | M 0,0 |
87 | L 0,${size} |
88 | " |
89 | /> |
90 | <path class="plant-stem" |
91 | d=" |
92 | M 0,${size * 0.9} |
93 | C 0,${size * 0.8} 0,${size * 1.2} ${size * 0.3},${size * 1.4} |
94 | " |
95 | /> |
96 | <path class="plant-stem" |
97 | d=" |
98 | M 0,${size * 0.9} |
99 | C 0,${size * 0.8} 0,${size * 1.2} ${size * -0.3},${size * 1.4} |
100 | " |
101 | /> |
102 | </g> |
103 | ` |
104 | } |
105 | |
106 | function moons ({ moonRadius, offsetRadius }) { |
107 | return ` |
108 | <style type="text/css"> |
109 | .moon { |
110 | stroke: black; |
111 | stroke-width: ${moonRadius / 20}; |
112 | } |
113 | .moon-front { |
114 | fill: white; |
115 | } |
116 | .moon-back { |
117 | fill: purple; |
118 | } |
119 | </style> |
120 | ${range({ start: 0, stop: 1, step: 1/8 }) |
121 | .map(index => moon({ |
122 | radius: moonRadius, |
123 | center: rotate({ angle: index * 2 * Math.PI, point: { x: 0, y: offsetRadius } }), |
124 | phase: index |
125 | })) |
126 | .join('\n') |
127 | } |
128 | ` |
129 | } |
130 | |
131 | // inspired by https://github.com/tingletech/moon-phase |
132 | function moon ({ radius, center, phase }) { |
133 | var sweep = [] |
134 | var mag |
135 | |
136 | // the "sweep-flag" and the direction of movement change every quarter moon |
137 | // zero and one are both new moon; 0.50 is full moon |
138 | if (phase <= 0.25) { |
139 | sweep = [ 1, 0 ] |
140 | mag = 1 - phase * 4 |
141 | } else if (phase <= 0.50) { |
142 | sweep = [ 0, 0 ] |
143 | mag = (phase - 0.25) * 4 |
144 | } else if (phase <= 0.75) { |
145 | sweep = [ 1, 1 ] |
146 | mag = 1 - (phase - 0.50) * 4 |
147 | } else if (phase <= 1) { |
148 | sweep = [ 0, 1 ] |
149 | mag = (phase - 0.75) * 4 |
150 | } else { |
151 | throw new Error(`unexpected moon phase: ${phase}`) |
152 | } |
153 | |
154 | // http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands |
155 | // http://www.i-programmer.info/programming/graphics-and-imaging/3254-svg-javascript-and-the-dom.html |
156 | |
157 | var r = radius / 5 |
158 | |
159 | return ` |
160 | <g class="moon"> |
161 | <path |
162 | class="moon-back" |
163 | d="M ${center.x},${center.y - radius / 2} |
164 | a ${r},${r} 0 1,1 0,${radius} |
165 | a ${r},${r} 0 1,1 0,-${radius}" |
166 | /> |
167 | <path |
168 | class="moon-front" |
169 | d="M ${center.x},${center.y - radius / 2} |
170 | a ${mag * r},${r} 0 1,${sweep[0]} 0,${radius} |
171 | a ${r},${r} 0 1,${sweep[1]} 0,-${radius}" |
172 | /> |
173 | </g> |
174 | ` |
175 | } |
176 | |
177 | function rotate ({ center = { x: 0, y: 0}, angle, point }) { |
178 | const a = Math.atan2(point.y - center.y, point.x - center.x) |
179 | const r = Math.sqrt(Math.pow(point.x - center.x, 2) + Math.pow(point.y - center.y, 2)) |
180 | return { |
181 | x: r * Math.cos(a + angle), |
182 | y: r * Math.sin(a + angle) |
183 | } |
184 | } |
185 | |
186 | |
187 | // https://stackoverflow.com/a/44957114 |
188 | function range ({ start = 0, stop, step = 1 }) { |
189 | return Array(Math.ceil((stop - start) / step)) |
190 | .fill(start).map((x, y) => x + y * step) |
191 | } |
192 |
Built with git-ssb-web