|
| 1 | +const rand = (a, b) => Math.min(a, b) + (Math.abs(a - b) * Math.random()); |
| 2 | + |
1 | 3 | const size = 600; |
2 | 4 | const count = 8; |
3 | 5 | const color = "grey"; |
4 | 6 |
|
5 | | -const angle = 2 * Math.PI / count; |
6 | | -const distance = size / 3; |
| 7 | +const angle = 2 * Math.PI/count; |
| 8 | +const distance = size/3; |
| 9 | +const ctrlDistance = distance * 4/3 * Math.tan(angle/4); |
7 | 10 |
|
8 | 11 | const points: {x: number, y: number}[] = []; |
9 | | -for (let i = 0; i < count; i += 1) { |
| 12 | +for (let i = 0; i < count; i++) { |
10 | 13 | points.push({ |
11 | | - x: Math.sin(i * angle) * distance, |
12 | | - y: Math.cos(i * angle) * distance, |
| 14 | + x: Math.sin(i*angle) * distance, |
| 15 | + y: Math.cos(i*angle) * distance, |
13 | 16 | }); |
14 | 17 | } |
15 | 18 |
|
16 | | -const paths = points.map<string>((point, i) => { |
| 19 | +const controls: {x1: number, y1: number, x2: number, y2: number}[] = []; |
| 20 | +for (let i = 0; i < count; i++) { |
| 21 | + const j = (i+count-1)%count; |
| 22 | + controls.push({ |
| 23 | + x1: points[i].x - Math.cos(i*angle) * ctrlDistance, |
| 24 | + y1: points[i].y + Math.sin(i*angle) * ctrlDistance, |
| 25 | + x2: points[j].x + Math.cos(j*angle) * ctrlDistance, |
| 26 | + y2: points[j].y - Math.sin(j*angle) * ctrlDistance, |
| 27 | + }); |
| 28 | +} |
| 29 | + |
| 30 | +const paths: string[] = []; |
| 31 | +for (let i = 0; i <= count; i++) { |
| 32 | + const point = points[i%count]; |
| 33 | + const control = controls[i%count]; |
| 34 | + |
| 35 | + // Start at the first point's coordinates. |
17 | 36 | if (i === 0) { |
18 | | - return `M${point.x},${point.y}`; |
| 37 | + paths.push(`M${point.x},${point.y}`); |
| 38 | + continue; |
19 | 39 | } |
20 | | - return `L${point.x},${point.y}`; |
21 | | -}); |
| 40 | + |
| 41 | + // |
| 42 | + paths.push(`C${control.x2},${control.y2},${control.x1},${control.y1},${point.x},${point.y}`); |
| 43 | + |
| 44 | +} |
| 45 | +// Close the path. |
22 | 46 | paths.push("Z"); |
23 | 47 |
|
24 | 48 | console.log(` |
25 | 49 | <svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg"> |
26 | | - <g transform="translate(${size / 2}, ${size / 2})"> |
| 50 | + <g transform=" |
| 51 | + translate(${size / 2}, ${size / 2}) |
| 52 | + rotate(${rand(0, 360 / count)}) |
| 53 | + "> |
| 54 | + ${points.map(({x, y}, i) => { |
| 55 | + return `<line x1="${x}" y1="${y}" x2="${controls[i].x1}" y2="${controls[i].y1}" stroke-width="1" stroke="green" />`; |
| 56 | + }).join("")} |
| 57 | + ${points.map(({x, y}) => { |
| 58 | + return `<circle cx="${x}" cy="${y}" r="4" fill="red" />`; |
| 59 | + }).join("")} |
| 60 | + ${controls.map(({x1: x, y1: y}, i) => { |
| 61 | + return `<circle cx="${x}" cy="${y}" r="2" fill="${i === 0 ? "black" : "blue"}" />`; |
| 62 | + }).join("")} |
| 63 | + ${controls.map(({x2: x, y2: y}, i) => { |
| 64 | + return `<circle cx="${x}" cy="${y}" r="2" fill="${i === 0 ? "black" : "blue"}" />`; |
| 65 | + }).join("")} |
27 | 66 | <path |
28 | 67 | stroke="none" |
29 | 68 | stroke-width="0" |
30 | 69 | fill="${color}" |
31 | | - d="${paths.join("\n")}" |
| 70 | + d="${paths.join("")}" |
32 | 71 | /> |
33 | 72 | </g> |
34 | 73 | </svg> |
35 | 74 | `); |
36 | | - |
37 | | -// const r = (a, b) => Math.min(a, b) + (Math.abs(a - b) * Math.random()); |
|
0 commit comments