morphico 0.4.0 → 0.5.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.
- package/README.md +62 -45
- package/dist/esm/all.js +50 -0
- package/dist/esm/clip.js +283 -0
- package/dist/esm/engine.js +25 -0
- package/dist/esm/face.js +153 -0
- package/dist/esm/geometry.js +459 -0
- package/dist/esm/icon.js +41 -0
- package/dist/esm/index.js +50 -0
- package/dist/esm/morph.js +107 -0
- package/dist/esm/roundness.js +37 -0
- package/dist/esm/sets/battery.js +87 -0
- package/dist/esm/sets/bell.js +101 -0
- package/dist/esm/sets/characters.js +43 -0
- package/dist/esm/sets/check-mark.js +74 -0
- package/dist/esm/sets/checkbox.js +91 -0
- package/dist/esm/sets/chevron.js +70 -0
- package/dist/esm/sets/condensed.js +137 -0
- package/dist/esm/sets/copy.js +87 -0
- package/dist/esm/sets/eye.js +105 -0
- package/dist/esm/sets/faces.js +482 -0
- package/dist/esm/sets/folder.js +112 -0
- package/dist/esm/sets/handwritten.js +136 -0
- package/dist/esm/sets/lock.js +75 -0
- package/dist/esm/sets/lucide.js +66 -0
- package/dist/esm/sets/media.js +109 -0
- package/dist/esm/sets/menu.js +84 -0
- package/dist/esm/sets/mic.js +102 -0
- package/dist/esm/sets/radio.js +84 -0
- package/dist/esm/sets/round.js +244 -0
- package/dist/esm/sets/shapes.js +455 -0
- package/dist/esm/sets/slim.js +148 -0
- package/dist/esm/sets/square.js +149 -0
- package/dist/esm/sets/theme.js +107 -0
- package/dist/esm/sets/toggle.js +73 -0
- package/dist/esm/sets/volume.js +89 -0
- package/dist/esm/sets/weather.js +691 -0
- package/dist/esm/sets/wifi.js +68 -0
- package/dist/esm/svg.js +31 -0
- package/dist/esm/text.js +88 -0
- package/dist/esm/types.js +3 -0
- package/dist/morphico.js +3764 -3750
- package/dist/types/{iconSets.d.ts → all.d.ts} +0 -2
- package/dist/types/clip.d.ts +4 -11
- package/dist/types/face.d.ts +2 -6
- package/dist/types/icon.d.ts +4 -9
- package/dist/types/index.d.ts +1 -3
- package/dist/types/roundness.d.ts +8 -0
- package/dist/types/sets/battery.d.ts +1 -1
- package/dist/types/sets/bell.d.ts +1 -1
- package/dist/types/sets/checkbox.d.ts +1 -1
- package/dist/types/sets/chevron.d.ts +1 -1
- package/dist/types/sets/condensed.d.ts +1 -1
- package/dist/types/sets/copy.d.ts +1 -1
- package/dist/types/sets/eye.d.ts +1 -1
- package/dist/types/sets/folder.d.ts +1 -1
- package/dist/types/sets/handwritten.d.ts +1 -1
- package/dist/types/sets/lock.d.ts +1 -1
- package/dist/types/sets/media.d.ts +1 -1
- package/dist/types/sets/menu.d.ts +1 -1
- package/dist/types/sets/mic.d.ts +1 -1
- package/dist/types/sets/radio.d.ts +1 -1
- package/dist/types/sets/round.d.ts +1 -1
- package/dist/types/sets/slim.d.ts +1 -1
- package/dist/types/sets/square.d.ts +1 -1
- package/dist/types/sets/theme.d.ts +1 -1
- package/dist/types/sets/toggle.d.ts +1 -1
- package/dist/types/sets/volume.d.ts +1 -1
- package/dist/types/sets/weather.d.ts +1 -1
- package/dist/types/sets/wifi.d.ts +1 -1
- package/dist/types/text.d.ts +8 -13
- package/package.json +95 -3
- package/dist/morphico.mjs +0 -4642
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/*! morphico v0.5.0 | MIT License, Copyright (c) 2026 Raycho Ivanov (see LICENSE) */
|
|
2
|
+
// Digit set "square": rounded-square "techno" digits, drafted from the reference images in icons/square/
|
|
3
|
+
// (the set format is described in ../types.ts).
|
|
4
|
+
//
|
|
5
|
+
// The images are tiny (every digit 28 px tall, 4 px stroke), so the shapes are written on the images' own pixel
|
|
6
|
+
// grid and converted: x/y are the stroke's middle line in image pixels, measured from the digit's ink box
|
|
7
|
+
// (y 2…26 = top to bottom middle line; x 2 = middle of a stroke on the left edge). 1.5 image px = 1 grid unit.
|
|
8
|
+
// - straight strokes with rounded corners (roundedPolyline), round ends
|
|
9
|
+
// - 3 and 5 share the same right-side bowls (corner radius 5 px); 8 = two stacked rounded rectangles sharing
|
|
10
|
+
// the middle bar; 9 = 6 turned 180° around 12,12
|
|
11
|
+
// - T-junctions (1's foot, 3's middle bar, 4's crossbar) are drawn out and back over themselves
|
|
12
|
+
import { roundedPolyline } from '../geometry.js';
|
|
13
|
+
import { withTextCharacters } from './characters.js';
|
|
14
|
+
const PIXELS_PER_UNIT = 1.5;
|
|
15
|
+
/** The images' stroke is 4 px ≈ 2.67 units; thinner by the user's choice. */
|
|
16
|
+
const STROKE_WIDTH = 1.6;
|
|
17
|
+
/** Image pixels (middle line, measured from the ink box of a digit `inkWidth` px wide) → grid units. */
|
|
18
|
+
const fromImage = (inkWidth) => ([x, y]) => [12 + (x - inkWidth / 2) / PIXELS_PER_UNIT, 4 + (y - 2) / PIXELS_PER_UNIT];
|
|
19
|
+
/** A rounded polyline given in image pixels; radii in image pixels too. */
|
|
20
|
+
const drawn = (inkWidth, points, radii) => roundedPolyline(points.map(fromImage(inkWidth)), Array.isArray(radii) ? radii.map((radius) => radius / PIXELS_PER_UNIT) : radii / PIXELS_PER_UNIT);
|
|
21
|
+
const turnedAround = ([x, y]) => [24 - x, 24 - y];
|
|
22
|
+
// corner radii (image px), each the best pixel match with the images; bowls are exact half-circles
|
|
23
|
+
const ZERO_RADIUS = 8; // half the width: the 0 is a stadium
|
|
24
|
+
const THREE_BOWL_RADIUS = 6; // half of each bowl's height (12 px)
|
|
25
|
+
const FIVE_BOWL_RADIUS = 6.5; // half of the bowl's height (13 px)
|
|
26
|
+
const SIX_RADIUS = 7;
|
|
27
|
+
const EIGHT_RADIUS = 6;
|
|
28
|
+
// the 6: hook from the top right, round the left side, then the closed bottom loop back to the left side
|
|
29
|
+
const SIX_POINTS = [[15, 2], [2, 2], [2, 26], [19, 26], [19, 12], [2, 12]];
|
|
30
|
+
const SIX_RADII = [0, SIX_RADIUS, SIX_RADIUS, SIX_RADIUS, SIX_RADIUS, 0];
|
|
31
|
+
// '-' '/' '.' ',' ':' are added by withTextCharacters (characters.ts)
|
|
32
|
+
export const digitsSquare = withTextCharacters({
|
|
33
|
+
segmentCount: 20,
|
|
34
|
+
strokeWidth: STROKE_WIDTH,
|
|
35
|
+
lineCap: 'round',
|
|
36
|
+
lineJoin: 'round',
|
|
37
|
+
strokes: {
|
|
38
|
+
// closed rounded rectangle, from the top middle counter-clockwise
|
|
39
|
+
0: drawn(20, [[10, 2], [2, 2], [2, 26], [18, 26], [18, 2], [10, 2]], ZERO_RADIUS),
|
|
40
|
+
// flag → stem down → foot to the left and back across to the right
|
|
41
|
+
1: drawn(16, [[2, 2], [8, 2], [8, 26], [2, 26], [14, 26]], 0),
|
|
42
|
+
// top bar → right side → diagonal down to the left → left side → base
|
|
43
|
+
2: drawn(20, [[3, 2], [18, 2], [18, 9.25], [2, 17.25], [2, 26], [18, 26]], [0, 2, 4.5, 4.5, 0, 0]),
|
|
44
|
+
// top bar → upper bowl → middle bar out to the left and back → lower bowl → bottom bar
|
|
45
|
+
3: drawn(20, [[2, 2], [18, 2], [18, 14], [6, 14], [18, 14], [18, 26], [2, 26]], [0, THREE_BOWL_RADIUS, THREE_BOWL_RADIUS, 0, THREE_BOWL_RADIUS, THREE_BOWL_RADIUS, 0]),
|
|
46
|
+
// left stem down → crossbar to its right end and back to the right stem → down to the bottom and back up to the top
|
|
47
|
+
4: drawn(24, [[2, 2], [2, 18], [22, 18], [18, 18], [18, 26], [18, 2]], [0, 1, 0, 0, 0, 0]),
|
|
48
|
+
// top bar right → left, down the left side, middle bar → bowl → bottom bar
|
|
49
|
+
5: drawn(20, [[16, 2], [2, 2], [2, 13], [18, 13], [18, 26], [2, 26]], [0, 0, 0, FIVE_BOWL_RADIUS, FIVE_BOWL_RADIUS, 0]),
|
|
50
|
+
6: drawn(21, SIX_POINTS, SIX_RADII),
|
|
51
|
+
// top bar → diagonal down to the left
|
|
52
|
+
7: drawn(20, [[2, 2], [17.5, 2], [3.7, 26]], 0),
|
|
53
|
+
// from the waist: top rectangle (left side up, clockwise), along the middle bar, bottom rectangle (counter-clockwise)
|
|
54
|
+
8: drawn(20, [[7, 14], [3, 14], [3, 2], [17, 2], [17, 14], [2, 14], [2, 26], [18, 26], [18, 14], [7, 14]], [0, EIGHT_RADIUS, EIGHT_RADIUS, EIGHT_RADIUS, EIGHT_RADIUS, EIGHT_RADIUS, EIGHT_RADIUS, EIGHT_RADIUS, EIGHT_RADIUS, 0]),
|
|
55
|
+
// the 6 turned 180°, drawn the other way round (loop first, then the hook)
|
|
56
|
+
9: roundedPolyline(SIX_POINTS.map(fromImage(21)).map(turnedAround).reverse(), [...SIX_RADII].reverse().map((radius) => radius / PIXELS_PER_UNIT)),
|
|
57
|
+
},
|
|
58
|
+
alternativeStrokes: {
|
|
59
|
+
// the 8 traced like the 0 (from the top, counter-clockwise): left side of the top loop down to the middle of
|
|
60
|
+
// the middle bar, once around the bottom loop back to the middle, right side of the top loop back up
|
|
61
|
+
'8-like-0': drawn(20, [[10, 2], [3, 2], [3, 14], [10, 14], [2, 14], [2, 26], [18, 26], [18, 14], [10, 14], [17, 14], [17, 2], [10, 2]], [0, EIGHT_RADIUS, EIGHT_RADIUS, 0, EIGHT_RADIUS, EIGHT_RADIUS, EIGHT_RADIUS, EIGHT_RADIUS, 0, EIGHT_RADIUS, EIGHT_RADIUS, 0]),
|
|
62
|
+
// the 8 traced like the 3, starting and ending at the left part of the middle bar: around the top loop, along
|
|
63
|
+
// the middle bar out to the left and back, around the bottom loop
|
|
64
|
+
'8-like-3': drawn(20, [[9, 14], [3, 14], [3, 2], [17, 2], [17, 14], [8, 14], [18, 14], [18, 26], [2, 26], [2, 14], [8, 14]], [0, EIGHT_RADIUS, EIGHT_RADIUS, EIGHT_RADIUS, EIGHT_RADIUS, 0, EIGHT_RADIUS, EIGHT_RADIUS, EIGHT_RADIUS, EIGHT_RADIUS, 0]),
|
|
65
|
+
},
|
|
66
|
+
// optimizer fits (tools/check.ts) for the pairs the user rated bad / midd
|
|
67
|
+
pairLayouts: {
|
|
68
|
+
0: {
|
|
69
|
+
1: { startSegment: 5, reversed: true },
|
|
70
|
+
2: { startSegment: 15 },
|
|
71
|
+
3: { startSegment: 5, reversed: true },
|
|
72
|
+
5: { startSegment: 5, reversed: true },
|
|
73
|
+
7: { startSegment: 5, reversed: true },
|
|
74
|
+
9: { startSegment: 7, reversed: true },
|
|
75
|
+
},
|
|
76
|
+
1: {
|
|
77
|
+
9: { breakpoints: [0, 0.2083, 0.2083, 0.4792, 1, 1, 1.25, 1.4583, 1.5417, 1.75, 2, 2, 2.0208, 2.25, 2.25, 2.25, 2.25, 2.25, 2.25, 3, 4] },
|
|
78
|
+
0: { breakpoints: [0, 0, 0, 0, 0.5, 1, 1.0208, 1.0833, 1.2083, 1.3333, 1.5, 1.6667, 1.7917, 1.9167, 1.9792, 2, 2.5, 3, 3, 3, 4] },
|
|
79
|
+
},
|
|
80
|
+
2: {
|
|
81
|
+
4: { breakpoints: [0, 1, 1, 1, 1, 2, 3, 4, 4.2292, 4.2292, 4.2292, 5, 5.2292, 5.5208, 5.5208, 5.5208, 5.5208, 5.5208, 6, 7, 8] },
|
|
82
|
+
6: { breakpoints: [0, 1, 2, 3, 4, 5, 5.0833, 5.2917, 6, 6.375, 7, 7.4375, 7.6042, 7.7708, 7.7708, 7.7708, 7.7708, 7.7708, 7.7708, 7.7708, 8] },
|
|
83
|
+
9: { breakpoints: [0, 0.2708, 0.2708, 0.2708, 0.2708, 0.2708, 0.2708, 0.2708, 0.2708, 0.3958, 0.6458, 1, 2, 3, 4, 4.0833, 4.1875, 5, 6, 7, 8] },
|
|
84
|
+
0: { breakpoints: [0, 1, 2, 2, 2, 3, 4, 4.6667, 4.7917, 5, 5.1042, 5.6667, 6, 7, 7.3333, 7.5, 7.6875, 7.8542, 7.9792, 8, 8] },
|
|
85
|
+
},
|
|
86
|
+
3: {
|
|
87
|
+
4: { breakpoints: [0, 1, 1.3958, 1.3958, 1.3958, 1.5833, 2, 2.4167, 2.6042, 2.6042, 2.6042, 3, 4, 5, 5.5625, 6, 6, 7, 7.9792, 8, 8] },
|
|
88
|
+
6: { breakpoints: [0, 1, 1.1458, 1.1458, 1.1458, 1.1458, 1.1458, 1.1458, 2, 3, 4, 4, 5, 6, 6.2292, 6.2292, 6.2292, 6.2292, 6.2292, 7, 8] },
|
|
89
|
+
9: { breakpoints: [0, 0.3333, 0.3333, 0.4792, 1, 1.7292, 2, 2.3125, 2.3125, 3, 4, 5, 5.5833, 5.5833, 5.5833, 6, 6.5, 7, 7.3542, 7.5625, 8] },
|
|
90
|
+
0: { breakpoints: [0, 0, 0.0417, 0.2292, 0.4792, 0.8333, 1, 1.5, 2, 3, 4, 5, 6, 6.5, 7, 7.1667, 7.5208, 7.7708, 7.9583, 8, 8] },
|
|
91
|
+
},
|
|
92
|
+
4: {
|
|
93
|
+
7: { breakpoints: [0, 1, 2, 2.3125, 2.3125, 3, 4, 5, 5.7083, 5.7083, 5.7708, 5.8542, 5.9167, 6, 6, 6, 6, 6, 6, 6, 6], reversed: true },
|
|
94
|
+
9: { breakpoints: [0, 1, 2, 3, 4, 5, 5.5625, 5.7083, 5.7083, 5.7083, 5.7083, 5.7083, 5.7083, 5.7083, 5.7083, 5.7083, 5.7083, 5.7083, 5.7083, 5.7083, 6], reversed: true },
|
|
95
|
+
3: { breakpoints: [0, 1, 2, 3, 4, 5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5208, 5.625, 5.75, 5.875, 5.9792, 6, 6, 6, 6], reversed: true },
|
|
96
|
+
2: { breakpoints: [0, 1, 2, 2.1042, 2.1042, 2.1042, 2.1042, 2.1042, 2.2083, 2.2083, 3, 4, 5, 5.8125, 5.8125, 5.9375, 6, 6, 6, 6, 6], reversed: true },
|
|
97
|
+
},
|
|
98
|
+
5: {
|
|
99
|
+
6: { breakpoints: [0, 0.3542, 0.4792, 1, 1.3333, 1.625, 2, 2, 2, 2.0208, 2.2292, 3, 4, 4, 4, 4, 4, 4, 4, 5, 6] },
|
|
100
|
+
8: { breakpoints: [0, 0.1042, 0.1042, 0.1042, 0.1042, 0.3542, 0.4583, 1, 1.5625, 2, 2.6667, 2.6667, 3, 4, 4.5, 5, 5.3542, 5.875, 5.8958, 5.8958, 6] },
|
|
101
|
+
9: { breakpoints: [0, 1, 1.4792, 1.4792, 1.4792, 1.4792, 1.4792, 1.4792, 1.4792, 1.4792, 2, 3, 3.3958, 3.7708, 4, 4.3333, 4.6667, 5, 5.3542, 5.5417, 6] },
|
|
102
|
+
0: { breakpoints: [0, 1, 1.2083, 1.2083, 1.2083, 2, 3, 3.1875, 3.2708, 3.3958, 3.5208, 4, 4.125, 4.5, 4.875, 5, 5.5208, 5.75, 5.9583, 6, 6] },
|
|
103
|
+
},
|
|
104
|
+
6: {
|
|
105
|
+
7: { breakpoints: [0, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 0.375, 1, 2, 2.625, 3, 4, 5, 6, 7, 8] },
|
|
106
|
+
8: { breakpoints: [0, 0.2292, 0.2292, 0.2292, 0.2292, 1, 2, 2.5833, 2.6458, 3, 3.5417, 4, 5, 5.4583, 6, 7, 7.3542, 7.3542, 7.6875, 7.8333, 8] },
|
|
107
|
+
5: { breakpoints: [0, 0.5, 1, 1.4792, 1.6875, 2, 2.375, 2.375, 2.375, 2.375, 3, 4, 5, 5.9792, 5.9792, 5.9792, 5.9792, 5.9792, 6, 7, 8] },
|
|
108
|
+
3: { breakpoints: [0, 0.1458, 0.1458, 0.1458, 0.1458, 0.1458, 0.1458, 0.1458, 0.1458, 1, 2, 3, 4, 5, 5.625, 5.625, 5.625, 5.625, 6, 7, 8] },
|
|
109
|
+
2: { breakpoints: [0, 0, 0, 0, 1, 2, 2.8125, 2.8125, 3, 3.3125, 3.5208, 4, 5, 5.2292, 5.2292, 5.2292, 5.2292, 5.2292, 6, 7, 8] },
|
|
110
|
+
},
|
|
111
|
+
7: {
|
|
112
|
+
6: { breakpoints: [0, 0.6667, 0.6667, 0.6667, 0.6667, 0.6667, 0.6667, 0.6667, 0.6667, 0.6667, 0.6667, 0.6667, 1, 1.5208, 1.7083, 1.7083, 1.7083, 1.7083, 1.7083, 1.7083, 2] },
|
|
113
|
+
4: { breakpoints: [0, 0.9167, 0.9167, 0.9167, 0.9167, 0.9167, 0.9167, 1, 1.1042, 1.125, 1.2083, 1.2292, 1.2917, 1.5833, 1.5833, 1.5833, 1.6667, 1.7083, 1.7292, 1.7292, 2] },
|
|
114
|
+
0: { breakpoints: [0, 0, 0.0208, 0.1458, 0.3125, 0.5208, 0.6875, 1, 1.1667, 1.2292, 1.375, 1.4792, 1.6042, 1.7083, 1.8125, 1.875, 1.875, 1.875, 1.875, 1.875, 2] },
|
|
115
|
+
},
|
|
116
|
+
8: {
|
|
117
|
+
5: { startSegment: 11, reversed: true },
|
|
118
|
+
6: { startSegment: 4 },
|
|
119
|
+
7: { tracing: '8-like-3', breakpoints: [0, 1, 1.8542, 2, 3, 3.2083, 3.3958, 3.5208, 3.5833, 3.6875, 4, 4.3542, 4.7708, 5, 6, 7, 8, 9, 10, 11, 12] },
|
|
120
|
+
9: { tracing: '8-like-3', breakpoints: [0, 0, 0, 0, 0.3542, 0.5833, 1, 1.2083, 1.5625, 2, 3, 3.375, 4, 5, 6, 7, 8, 9, 10, 11, 12] },
|
|
121
|
+
4: { tracing: '8-like-0', breakpoints: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9, 9, 10, 11, 12, 13, 14, 15] },
|
|
122
|
+
// the user's idea: both ends of the 3 move to the left tip of its middle bar, where the 8's loops close
|
|
123
|
+
3: {
|
|
124
|
+
tracing: '8-like-3',
|
|
125
|
+
breakpoints: [0, 1, 2, 3, 3.4375, 3.7083, 4, 4.2917, 4.5625, 5, 5.7292, 6, 6.1458, 7, 7.5, 8, 8.5, 9, 10, 11, 12],
|
|
126
|
+
},
|
|
127
|
+
// the user's idea: the 0's left and right sides move in to the middle of the 8's middle bar
|
|
128
|
+
0: {
|
|
129
|
+
tracing: '8-like-0',
|
|
130
|
+
breakpoints: [0, 1, 1.6042, 2, 3, 4, 5, 6, 6.5, 7, 7.5, 8, 8.5, 9, 10, 11, 12, 13, 13.3958, 14, 15],
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
9: {
|
|
134
|
+
5: { breakpoints: [0, 1, 2, 2.1875, 2.1875, 2.1875, 2.1875, 2.1875, 2.1875, 2.1875, 3, 4, 5, 5.8333, 6, 6.375, 6.6875, 7, 7.5417, 8, 8] },
|
|
135
|
+
4: { breakpoints: [0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5.5, 6, 6.0208, 6.0208, 7, 8, 8] },
|
|
136
|
+
3: { breakpoints: [0, 1, 2, 3, 4, 4.6667, 5, 5.1875, 5.4375, 5.5, 5.5, 5.5, 5.5, 5.5, 5.6667, 6, 6.5417, 7, 7.4583, 8, 8] },
|
|
137
|
+
2: { breakpoints: [0, 1, 2, 2.8333, 2.8333, 2.8333, 2.8333, 2.8333, 2.8333, 3, 4, 4.3542, 4.5833, 4.8125, 5, 5.0625, 6, 7, 8, 8, 8] },
|
|
138
|
+
1: { breakpoints: [0, 1, 2, 3, 3.5, 4, 5, 5.3958, 5.6042, 6, 7, 7.2708, 7.2708, 7.5208, 7.5208, 7.5208, 7.5208, 7.5208, 7.5208, 7.5208, 8] },
|
|
139
|
+
0: { breakpoints: [0, 1, 1.6667, 2, 2.2083, 2.5, 3, 3.5, 4, 4.5, 4.7917, 5, 5.5, 6, 6.1875, 6.5417, 6.875, 7, 7.875, 8, 8] },
|
|
140
|
+
// turned back by 180° this is exactly the 6 (same segment split, same direction) → a pure spin
|
|
141
|
+
6: {
|
|
142
|
+
breakpoints: [0, 0.5, 1, 4 / 3, 5 / 3, 2, 7 / 3, 8 / 3, 3, 4, 13 / 3, 14 / 3, 5, 16 / 3, 17 / 3, 6, 19 / 3, 20 / 3, 7, 7.5, 8],
|
|
143
|
+
reversed: true,
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
spins: { 6: { 9: 180 }, 9: { 6: -180 } },
|
|
148
|
+
textLayout: { cellWidth: 13.5, spacing: 4, baselineY: 20, colonDotsY: [8, 16] },
|
|
149
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/*! morphico v0.5.0 | code: MIT License, Copyright (c) 2026 Raycho Ivanov (see LICENSE)
|
|
2
|
+
* Icons based on Lucide (https://lucide.dev), some of them from Feather (https://feathericons.com):
|
|
3
|
+
*
|
|
4
|
+
* ISC License
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) 2026 Lucide Icons and Contributors
|
|
7
|
+
*
|
|
8
|
+
* Permission to use, copy, modify, and/or distribute this software for any
|
|
9
|
+
* purpose with or without fee is hereby granted, provided that the above
|
|
10
|
+
* copyright notice and this permission notice appear in all copies.
|
|
11
|
+
*
|
|
12
|
+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
13
|
+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
14
|
+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
15
|
+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
16
|
+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
17
|
+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
18
|
+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
19
|
+
*
|
|
20
|
+
* The MIT License (MIT) (for the Lucide icons derived from Feather, listed in LICENSE-lucide)
|
|
21
|
+
*
|
|
22
|
+
* Copyright (c) 2013-present Cole Bemis
|
|
23
|
+
*
|
|
24
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
25
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
26
|
+
* in the Software without restriction, including without limitation the rights
|
|
27
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
28
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
29
|
+
* furnished to do so, subject to the following conditions:
|
|
30
|
+
*
|
|
31
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
32
|
+
* copies or substantial portions of the Software.
|
|
33
|
+
*
|
|
34
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
35
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
36
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
37
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
38
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
39
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
40
|
+
* SOFTWARE.
|
|
41
|
+
*/
|
|
42
|
+
// Icon set "theme": light (sun), dark (moon) — Lucide 1.61.0 icons "sun", "moon" (ISC, see LICENSE-lucide).
|
|
43
|
+
// Coordinates below are Lucide's; lucideLine / lucideArc / lucideCubic scale them into our icon box (see lucide.ts).
|
|
44
|
+
// Nine pieces:
|
|
45
|
+
// piece 1: the sun's circle | the moon's crescent — the circle grows into the moon and its top right quarter
|
|
46
|
+
// flattens and caves in evenly into the inner curve (the circle starts where the moon's outer arc starts).
|
|
47
|
+
// pieces 2–9: one ray each | nothing — rays run from their outer end inwards; light → dark: they fly outwards and
|
|
48
|
+
// vanish (erased towards their outer tip, PairLayout.draw — the user's sketch) within the first 60 % of the
|
|
49
|
+
// morph; dark → light: they fly back in during the last 60 %. At rest the moon's ray pieces are tiny lines hidden
|
|
50
|
+
// on its outline.
|
|
51
|
+
import { arc, cubic, line } from '../geometry.js';
|
|
52
|
+
import { fromLucide, LUCIDE_STROKE_WIDTH, lucideLength } from './lucide.js';
|
|
53
|
+
const lucideLine = (fromX, fromY, toX, toY) => line(...fromLucide([fromX, fromY]), ...fromLucide([toX, toY]));
|
|
54
|
+
const lucideArc = (centerX, centerY, radius, startAngle, endAngle) => arc(...fromLucide([centerX, centerY]), lucideLength(radius), startAngle, endAngle);
|
|
55
|
+
const lucideCubic = (...coordinates) => {
|
|
56
|
+
const [startX, startY, control1X, control1Y, control2X, control2Y, endX, endY] = coordinates;
|
|
57
|
+
return cubic(...fromLucide([startX, startY]), ...fromLucide([control1X, control1Y]), ...fromLucide([control2X, control2Y]), ...fromLucide([endX, endY]));
|
|
58
|
+
};
|
|
59
|
+
/** Lucide "moon": the outer arc clockwise from its right end, the top tip, the inner arc back, the right tip. */
|
|
60
|
+
const MOON = [
|
|
61
|
+
lucideArc(11.9981, 12.0009, 9, 3.09, 266.904),
|
|
62
|
+
lucideCubic(11.512, 3.014, 11.917, 2.992, 12.129, 3.474, 11.914, 3.817),
|
|
63
|
+
lucideArc(17.002, 6.997, 6, -147.9941, -302.0059),
|
|
64
|
+
lucideCubic(20.182, 12.085, 20.526, 11.87, 21.007, 12.081, 20.985, 12.486),
|
|
65
|
+
];
|
|
66
|
+
/** Lucide "sun"'s circle, started at the angle where the moon's outer arc starts. */
|
|
67
|
+
const SUN_CIRCLE = [lucideArc(12, 12, 4, 3.09, 363.09)];
|
|
68
|
+
/** The share of the circle under the moon's outer arc (3.09° … 266.904°). */
|
|
69
|
+
const OUTER_SHARE = (266.904 - 3.09) / 360;
|
|
70
|
+
const CIRCLE_FOR_MOON = [
|
|
71
|
+
...Array.from({ length: 9 }, (_, index) => (index / 8) * OUTER_SHARE),
|
|
72
|
+
...Array.from({ length: 4 }, (_, index) => OUTER_SHARE + ((index + 1) / 4) * (1 - OUTER_SHARE)),
|
|
73
|
+
].map((breakpoint) => +breakpoint.toFixed(4));
|
|
74
|
+
/** Lucide "sun"'s rays, each from its outer end inwards (erasing keeps the outer end: the ray flies out). */
|
|
75
|
+
const RAYS = [
|
|
76
|
+
lucideLine(12, 2, 12, 4),
|
|
77
|
+
lucideLine(19.07, 4.93, 17.66, 6.34),
|
|
78
|
+
lucideLine(22, 12, 20, 12),
|
|
79
|
+
lucideLine(19.07, 19.07, 17.66, 17.66),
|
|
80
|
+
lucideLine(12, 22, 12, 20),
|
|
81
|
+
lucideLine(4.93, 19.07, 6.34, 17.66),
|
|
82
|
+
lucideLine(2, 12, 4, 12),
|
|
83
|
+
lucideLine(4.93, 4.93, 6.34, 6.34),
|
|
84
|
+
];
|
|
85
|
+
/** The moon's ray pieces at rest: a tiny line on the bottom of its outline (it doesn't show). */
|
|
86
|
+
const HIDDEN_RAY = [lucideLine(11.95, 21, 12.05, 21)];
|
|
87
|
+
const rayPiece = (ray) => ({
|
|
88
|
+
strokes: { light: [ray], dark: HIDDEN_RAY },
|
|
89
|
+
alternativeStrokes: {},
|
|
90
|
+
pairLayouts: { dark: { light: { draw: 0.6 } } },
|
|
91
|
+
});
|
|
92
|
+
export const iconsTheme = {
|
|
93
|
+
segmentCount: 12,
|
|
94
|
+
strokeWidth: LUCIDE_STROKE_WIDTH,
|
|
95
|
+
lineCap: 'round',
|
|
96
|
+
lineJoin: 'round',
|
|
97
|
+
strokes: { light: SUN_CIRCLE, dark: MOON },
|
|
98
|
+
alternativeStrokes: {},
|
|
99
|
+
// even, by hand: the circle's part under the outer arc (263.8°) → 8 equal segments of the outer arc; its top right
|
|
100
|
+
// quarter (96.2°) → 4 equal segments: tip, inner curve (2), tip. (The optimizer's fit made a lumpy notch.)
|
|
101
|
+
pairLayouts: {
|
|
102
|
+
light: { dark: { breakpoints: CIRCLE_FOR_MOON } },
|
|
103
|
+
dark: { light: { breakpoints: [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1, 2, 2.5, 3, 4] } },
|
|
104
|
+
},
|
|
105
|
+
morePieces: RAYS.map(rayPiece),
|
|
106
|
+
spins: {},
|
|
107
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/*! morphico v0.5.0 | code: MIT License, Copyright (c) 2026 Raycho Ivanov (see LICENSE)
|
|
2
|
+
* Icons based on Lucide (https://lucide.dev), some of them from Feather (https://feathericons.com):
|
|
3
|
+
*
|
|
4
|
+
* ISC License
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) 2026 Lucide Icons and Contributors
|
|
7
|
+
*
|
|
8
|
+
* Permission to use, copy, modify, and/or distribute this software for any
|
|
9
|
+
* purpose with or without fee is hereby granted, provided that the above
|
|
10
|
+
* copyright notice and this permission notice appear in all copies.
|
|
11
|
+
*
|
|
12
|
+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
13
|
+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
14
|
+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
15
|
+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
16
|
+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
17
|
+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
18
|
+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
19
|
+
*
|
|
20
|
+
* The MIT License (MIT) (for the Lucide icons derived from Feather, listed in LICENSE-lucide)
|
|
21
|
+
*
|
|
22
|
+
* Copyright (c) 2013-present Cole Bemis
|
|
23
|
+
*
|
|
24
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
25
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
26
|
+
* in the Software without restriction, including without limitation the rights
|
|
27
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
28
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
29
|
+
* furnished to do so, subject to the following conditions:
|
|
30
|
+
*
|
|
31
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
32
|
+
* copies or substantial portions of the Software.
|
|
33
|
+
*
|
|
34
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
35
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
36
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
37
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
38
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
39
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
40
|
+
* SOFTWARE.
|
|
41
|
+
*/
|
|
42
|
+
import { LUCIDE_STROKE_WIDTH, lucideLine } from './lucide.js';
|
|
43
|
+
const HORIZONTAL = [lucideLine(5, 12, 19, 12)];
|
|
44
|
+
const first = {
|
|
45
|
+
strokes: {
|
|
46
|
+
plus: HORIZONTAL,
|
|
47
|
+
minus: HORIZONTAL,
|
|
48
|
+
close: [lucideLine(6, 6, 18, 18)],
|
|
49
|
+
check: [lucideLine(4, 12, 9, 17)],
|
|
50
|
+
},
|
|
51
|
+
alternativeStrokes: {},
|
|
52
|
+
pairLayouts: {},
|
|
53
|
+
};
|
|
54
|
+
const second = {
|
|
55
|
+
strokes: {
|
|
56
|
+
plus: [lucideLine(12, 5, 12, 19)],
|
|
57
|
+
minus: HORIZONTAL,
|
|
58
|
+
close: [lucideLine(18, 6, 6, 18)],
|
|
59
|
+
check: [lucideLine(20, 6, 9, 17)],
|
|
60
|
+
},
|
|
61
|
+
alternativeStrokes: {},
|
|
62
|
+
pairLayouts: {},
|
|
63
|
+
};
|
|
64
|
+
export const iconsToggle = {
|
|
65
|
+
segmentCount: 1,
|
|
66
|
+
strokeWidth: LUCIDE_STROKE_WIDTH,
|
|
67
|
+
lineCap: 'round',
|
|
68
|
+
lineJoin: 'round',
|
|
69
|
+
...first,
|
|
70
|
+
morePieces: [second],
|
|
71
|
+
// plus turned by 45° clockwise is the ✕ (a little smaller): turn while blending
|
|
72
|
+
spins: { plus: { close: 45 }, close: { plus: -45 } },
|
|
73
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/*! morphico v0.5.0 | code: MIT License, Copyright (c) 2026 Raycho Ivanov (see LICENSE)
|
|
2
|
+
* Icons based on Lucide (https://lucide.dev), some of them from Feather (https://feathericons.com):
|
|
3
|
+
*
|
|
4
|
+
* ISC License
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) 2026 Lucide Icons and Contributors
|
|
7
|
+
*
|
|
8
|
+
* Permission to use, copy, modify, and/or distribute this software for any
|
|
9
|
+
* purpose with or without fee is hereby granted, provided that the above
|
|
10
|
+
* copyright notice and this permission notice appear in all copies.
|
|
11
|
+
*
|
|
12
|
+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
13
|
+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
14
|
+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
15
|
+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
16
|
+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
17
|
+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
18
|
+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
19
|
+
*
|
|
20
|
+
* The MIT License (MIT) (for the Lucide icons derived from Feather, listed in LICENSE-lucide)
|
|
21
|
+
*
|
|
22
|
+
* Copyright (c) 2013-present Cole Bemis
|
|
23
|
+
*
|
|
24
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
25
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
26
|
+
* in the Software without restriction, including without limitation the rights
|
|
27
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
28
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
29
|
+
* furnished to do so, subject to the following conditions:
|
|
30
|
+
*
|
|
31
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
32
|
+
* copies or substantial portions of the Software.
|
|
33
|
+
*
|
|
34
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
35
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
36
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
37
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
38
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
39
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
40
|
+
* SOFTWARE.
|
|
41
|
+
*/
|
|
42
|
+
import { LUCIDE_STROKE_WIDTH, lucideArc, lucideLine, lucidePoint } from './lucide.js';
|
|
43
|
+
/** Lucide's speaker, from the top of its front edge counterclockwise around. */
|
|
44
|
+
const SPEAKER = [
|
|
45
|
+
lucideArc(10.295, 4.703, 0.705, -0.0828, -134.9415),
|
|
46
|
+
lucideLine(9.797, 4.204, 6.413, 7.587),
|
|
47
|
+
lucideArc(5.4201, 6.6, 1.4, 44.8292, 90.168),
|
|
48
|
+
lucideLine(5.416, 8, 3, 8),
|
|
49
|
+
lucideArc(3, 9, 1, -90, -180),
|
|
50
|
+
lucideLine(2, 9, 2, 15),
|
|
51
|
+
lucideArc(3, 15, 1, 180, 90),
|
|
52
|
+
lucideLine(3, 16, 5.416, 16),
|
|
53
|
+
lucideArc(5.4201, 17.4, 1.4, -90.168, -44.8292),
|
|
54
|
+
lucideLine(6.413, 16.413, 9.796, 19.797),
|
|
55
|
+
lucideArc(10.295, 19.299, 0.705, 135.0564, -0.0797),
|
|
56
|
+
lucideLine(11, 19.298, 11, 4.702),
|
|
57
|
+
];
|
|
58
|
+
/** The waves, each from its top end down. */
|
|
59
|
+
const SMALL_WAVE = [lucideArc(12, 12, 5, -36.8699, 36.8699)];
|
|
60
|
+
const BIG_WAVE = [lucideArc(13.0001, 12, 9, -45.0004, 45.0004)];
|
|
61
|
+
/** The ✕, each line from its top end down (so a wave unfolds into it without turning over). */
|
|
62
|
+
const X_SLASH = [lucideLine(21.5, 9.5, 16.5, 14.5)];
|
|
63
|
+
const X_BACKSLASH = [lucideLine(16.5, 9.5, 21.5, 14.5)];
|
|
64
|
+
/** No wave: a point on the middle of the speaker's front edge. */
|
|
65
|
+
const NO_WAVE = lucidePoint(11, 12);
|
|
66
|
+
const speaker = {
|
|
67
|
+
strokes: { muted: SPEAKER, low: SPEAKER, medium: SPEAKER, high: SPEAKER },
|
|
68
|
+
alternativeStrokes: {},
|
|
69
|
+
pairLayouts: {},
|
|
70
|
+
};
|
|
71
|
+
const smallWave = {
|
|
72
|
+
strokes: { muted: X_SLASH, low: NO_WAVE, medium: SMALL_WAVE, high: SMALL_WAVE },
|
|
73
|
+
alternativeStrokes: {},
|
|
74
|
+
pairLayouts: {},
|
|
75
|
+
};
|
|
76
|
+
const bigWave = {
|
|
77
|
+
strokes: { muted: X_BACKSLASH, low: NO_WAVE, medium: SMALL_WAVE, high: BIG_WAVE },
|
|
78
|
+
alternativeStrokes: {},
|
|
79
|
+
pairLayouts: {},
|
|
80
|
+
};
|
|
81
|
+
export const iconsVolume = {
|
|
82
|
+
segmentCount: 16,
|
|
83
|
+
strokeWidth: LUCIDE_STROKE_WIDTH,
|
|
84
|
+
lineCap: 'round',
|
|
85
|
+
lineJoin: 'round',
|
|
86
|
+
...speaker,
|
|
87
|
+
morePieces: [smallWave, bigWave],
|
|
88
|
+
spins: {},
|
|
89
|
+
};
|