| 1796 | } |
| 1797 | |
| 1798 | function paperBall(cx, cy, r, extra = false) { |
| 1799 | // Deterministic "crumple" wobble so the paper balls don't look perfectly round. |
| 1800 | const seed = (((Math.round(cx * 10) * 73856093) ^ (Math.round(cy * 10) * 19349663)) >>> 0); |
| 1801 | const rot = seed % 8; |
| 1802 | const base = [0.00, -0.12, 0.10, -0.16, 0.12, -0.08, 0.10, -0.14]; |
| 1803 | const wobble = extra ? 0.85 : 0.55; |
| 1804 | const pts = []; |
| 1805 | for (let i = 0; i < 8; i++) { |
| 1806 | const idx = (i + rot) % 8; |
| 1807 | const a = (Math.PI * 2 * i) / 8; |
| 1808 | const m = 1 + (base[idx] * wobble); |
| 1809 | pts.push({ |
| 1810 | x: cx + Math.cos(a) * r * m, |
| 1811 | y: cy + Math.sin(a) * r * m |
| 1812 | }); |
| 1813 | } |
| 1814 | |
| 1815 | const mid = (a, b) => ({ x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 }); |
| 1816 | const start = mid(pts[pts.length - 1], pts[0]); |
| 1817 | let d = `M ${start.x} ${start.y}`; |
| 1818 | for (let i = 0; i < pts.length; i++) { |
| 1819 | const p = pts[i]; |
| 1820 | const n = pts[(i + 1) % pts.length]; |
| 1821 | const m = mid(p, n); |
| 1822 | d += ` Q ${p.x} ${p.y} ${m.x} ${m.y}`; |
| 1823 | } |
| 1824 | d += ' Z'; |
| 1825 | |
| 1826 | const crease1 = ` |
| 1827 | M ${cx - r*0.62} ${cy - r*0.08} |
| 1828 | C ${cx - r*0.12} ${cy - r*0.78}, ${cx + r*0.52} ${cy - r*0.46}, ${cx + r*0.35} ${cy + r*0.05} |
| 1829 | `; |
| 1830 | const crease2 = ` |
| 1831 | M ${cx - r*0.45} ${cy + r*0.25} |
| 1832 | C ${cx - r*0.05} ${cy + r*0.10}, ${cx + r*0.20} ${cy + r*0.55}, ${cx + r*0.55} ${cy + r*0.30} |
| 1833 | `; |
| 1834 | return ` |
| 1835 | <path d="${d}" fill="#f7f9ff" stroke="#c7d2e4" stroke-width="0.48"/> |
| 1836 | <path d="${crease1}" fill="none" stroke="#ffffff" stroke-opacity="0.55" stroke-width="0.55" stroke-linecap="round"/> |
| 1837 | <path d="${crease2}" fill="none" stroke="#8fa7cf" stroke-opacity="0.48" stroke-width="0.50" stroke-linecap="round"/> |
| 1838 | ${extra ? ` |
| 1839 | <path d="M ${cx - r*0.18} ${cy + r*0.62} l ${r*0.62} ${-r*0.42}" |
| 1840 | fill="none" stroke="#8fa7cf" stroke-opacity="0.40" stroke-width="0.45" stroke-linecap="round"/> |
| 1841 | ` : ''} |
| 1842 | `; |
| 1843 | } |
| 1844 | } |
| 1845 | |
| 1846 | function renderRecycleBinNode(hasItems = false) { |