create-atom.io 0.0.2 → 0.0.4
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/dist/{create-atom-DOkiQlT8.js → create-atom-C0gh5ath.js} +3 -3
- package/dist/create-atom-C0gh5ath.js.map +1 -0
- package/dist/create-atom.d.ts +1 -4
- package/dist/create-atom.d.ts.map +1 -1
- package/dist/create-atom.js +2 -2
- package/dist/create-atom.x.js +1 -1
- package/package.json +10 -9
- package/src/create-atom.ts +6 -2
- package/dist/create-atom-DOkiQlT8.js.map +0 -1
- package/templates/preact-svg-editor/_gitignore +0 -24
- package/templates/preact-svg-editor/eslint.config.ts +0 -179
- package/templates/preact-svg-editor/eslint.d.ts +0 -31
- package/templates/preact-svg-editor/index.html +0 -14
- package/templates/preact-svg-editor/node_modules/.bin/vite +0 -21
- package/templates/preact-svg-editor/package.json +0 -24
- package/templates/preact-svg-editor/public/preact.svg +0 -6
- package/templates/preact-svg-editor/src/BezierPlayground.tsx +0 -458
- package/templates/preact-svg-editor/src/index.tsx +0 -50
- package/templates/preact-svg-editor/src/msg.md +0 -105
- package/templates/preact-svg-editor/src/style.css +0 -126
- package/templates/preact-svg-editor/tsconfig.json +0 -21
- package/templates/preact-svg-editor/vite.config.ts +0 -10
- package/templates/react-node-backend/.turbo/turbo-build.log +0 -12
- package/templates/react-node-backend/README.md +0 -75
- package/templates/react-node-backend/_gitignore +0 -24
- package/templates/react-node-backend/dist/assets/index-6PkP9syN.js +0 -9
- package/templates/react-node-backend/dist/assets/index-By2j7w9s.css +0 -1
- package/templates/react-node-backend/dist/index.html +0 -14
- package/templates/react-node-backend/dist/react.svg +0 -1
- package/templates/react-node-backend/dist/vite.svg +0 -1
- package/templates/react-node-backend/eslint.config.ts +0 -176
- package/templates/react-node-backend/eslint.d.ts +0 -31
- package/templates/react-node-backend/index.html +0 -13
- package/templates/react-node-backend/node/authenticator.ts +0 -47
- package/templates/react-node-backend/node/server.ts +0 -103
- package/templates/react-node-backend/node_modules/.bin/conc +0 -21
- package/templates/react-node-backend/node_modules/.bin/concurrently +0 -21
- package/templates/react-node-backend/node_modules/.bin/eslint +0 -21
- package/templates/react-node-backend/node_modules/.bin/tsc +0 -21
- package/templates/react-node-backend/node_modules/.bin/tsserver +0 -21
- package/templates/react-node-backend/node_modules/.bin/vite +0 -21
- package/templates/react-node-backend/package.json +0 -33
- package/templates/react-node-backend/public/react.svg +0 -1
- package/templates/react-node-backend/public/vite.svg +0 -1
- package/templates/react-node-backend/src/App.tsx +0 -69
- package/templates/react-node-backend/src/index.css +0 -145
- package/templates/react-node-backend/src/main.tsx +0 -12
- package/templates/react-node-backend/tsconfig.app.json +0 -28
- package/templates/react-node-backend/tsconfig.json +0 -7
- package/templates/react-node-backend/tsconfig.node.json +0 -26
- package/templates/react-node-backend/vite.config.ts +0 -13
|
@@ -1,458 +0,0 @@
|
|
|
1
|
-
import type { Loadable, RegularAtomToken } from "atom.io"
|
|
2
|
-
import {
|
|
3
|
-
atom,
|
|
4
|
-
atomFamily,
|
|
5
|
-
getState,
|
|
6
|
-
resetState,
|
|
7
|
-
runTransaction,
|
|
8
|
-
selectorFamily,
|
|
9
|
-
setState,
|
|
10
|
-
transaction,
|
|
11
|
-
} from "atom.io"
|
|
12
|
-
import { useO } from "atom.io/react"
|
|
13
|
-
import type { PointerEventHandler, TargetedPointerEvent, VNode } from "preact"
|
|
14
|
-
import type { MutableRef } from "preact/hooks"
|
|
15
|
-
import { useCallback, useEffect, useRef } from "preact/hooks"
|
|
16
|
-
|
|
17
|
-
type PointXY = { x: number; y: number }
|
|
18
|
-
type EdgeXY = { c?: PointXY; s: PointXY }
|
|
19
|
-
|
|
20
|
-
const pathKeysAtom = atom<string[]>({
|
|
21
|
-
key: `pathKeys`,
|
|
22
|
-
default: [],
|
|
23
|
-
})
|
|
24
|
-
const subpathKeysAtoms = atomFamily<string[], string>({
|
|
25
|
-
key: `subpathKeys`,
|
|
26
|
-
default: [],
|
|
27
|
-
})
|
|
28
|
-
const nodeAtoms = atomFamily<PointXY | null, string>({
|
|
29
|
-
key: `nodeAtoms`,
|
|
30
|
-
default: null,
|
|
31
|
-
})
|
|
32
|
-
const edgeAtoms = atomFamily<EdgeXY | boolean, string>({
|
|
33
|
-
key: `edgeAtoms`,
|
|
34
|
-
default: true,
|
|
35
|
-
})
|
|
36
|
-
const pathDrawSelectors = selectorFamily<string, string>({
|
|
37
|
-
key: `pathDrawSelectors`,
|
|
38
|
-
get:
|
|
39
|
-
(pathKey) =>
|
|
40
|
-
({ get }) => {
|
|
41
|
-
const subpathKeys = get(subpathKeysAtoms, pathKey)
|
|
42
|
-
return subpathKeys
|
|
43
|
-
.map((subpathKey, idx) => {
|
|
44
|
-
const node = get(nodeAtoms, subpathKey)
|
|
45
|
-
const edge = get(edgeAtoms, subpathKey)
|
|
46
|
-
|
|
47
|
-
if (node === null) {
|
|
48
|
-
return `Z`
|
|
49
|
-
}
|
|
50
|
-
if (idx === 0) {
|
|
51
|
-
return `M ${node.x} ${node.y}`
|
|
52
|
-
}
|
|
53
|
-
if (edge === false) {
|
|
54
|
-
return `M ${node.x} ${node.y}`
|
|
55
|
-
}
|
|
56
|
-
if (edge === true) {
|
|
57
|
-
return `L ${node.x} ${node.y}`
|
|
58
|
-
}
|
|
59
|
-
if (`c` in edge) {
|
|
60
|
-
return `C ${edge.c.x} ${edge.c.y} ${edge.s.x} ${edge.s.y} ${node.x} ${node.y}`
|
|
61
|
-
}
|
|
62
|
-
return `S ${edge.s.x} ${edge.s.y} ${node.x} ${node.y}`
|
|
63
|
-
})
|
|
64
|
-
.join(` `)
|
|
65
|
-
},
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
export function useAtomicRef<T>(
|
|
69
|
-
token: RegularAtomToken<T | null>,
|
|
70
|
-
): MutableRef<T | null> {
|
|
71
|
-
const ref = useRef<T | null>(null)
|
|
72
|
-
useEffect(() => {
|
|
73
|
-
setState(token, ref.current)
|
|
74
|
-
}, [token])
|
|
75
|
-
return ref
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function clamp(n: number, min: number, max: number) {
|
|
79
|
-
return Math.max(min, Math.min(max, n))
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function Bezier({
|
|
83
|
-
at,
|
|
84
|
-
subpathKey,
|
|
85
|
-
prevSubpathKey,
|
|
86
|
-
node: maybeNode,
|
|
87
|
-
}: {
|
|
88
|
-
at: PointXY
|
|
89
|
-
subpathKey: string
|
|
90
|
-
prevSubpathKey: string
|
|
91
|
-
node?: PointXY
|
|
92
|
-
}) {
|
|
93
|
-
let node: PointXY | null
|
|
94
|
-
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
|
|
95
|
-
switch (typeof maybeNode) {
|
|
96
|
-
case `undefined`:
|
|
97
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
98
|
-
node = useO(nodeAtoms, prevSubpathKey)
|
|
99
|
-
break
|
|
100
|
-
default:
|
|
101
|
-
node = maybeNode
|
|
102
|
-
}
|
|
103
|
-
return node === null ? null : (
|
|
104
|
-
<>
|
|
105
|
-
<line
|
|
106
|
-
class="bezier"
|
|
107
|
-
x1={node.x}
|
|
108
|
-
y1={node.y}
|
|
109
|
-
x2={at.x}
|
|
110
|
-
y2={at.y}
|
|
111
|
-
stroke="#777"
|
|
112
|
-
stroke-width={1}
|
|
113
|
-
/>
|
|
114
|
-
<circle
|
|
115
|
-
class="bezier"
|
|
116
|
-
fill="#777"
|
|
117
|
-
stroke="#aaa"
|
|
118
|
-
stroke-width={1}
|
|
119
|
-
cx={at.x}
|
|
120
|
-
cy={at.y}
|
|
121
|
-
r={2}
|
|
122
|
-
/>
|
|
123
|
-
<circle
|
|
124
|
-
class="bezier-draggable"
|
|
125
|
-
fill="transparent"
|
|
126
|
-
cx={at.x}
|
|
127
|
-
cy={at.y}
|
|
128
|
-
r={6}
|
|
129
|
-
onPointerDown={(evt) => {
|
|
130
|
-
evt.currentTarget.setPointerCapture(evt.pointerId)
|
|
131
|
-
setState(currentlyDraggingAtom, {
|
|
132
|
-
key: subpathKey,
|
|
133
|
-
by: !maybeNode ? `c` : `s`,
|
|
134
|
-
})
|
|
135
|
-
}}
|
|
136
|
-
/>
|
|
137
|
-
</>
|
|
138
|
-
)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function Node({
|
|
142
|
-
subpathKey,
|
|
143
|
-
nextSubpathKey,
|
|
144
|
-
}: {
|
|
145
|
-
subpathKey: string
|
|
146
|
-
nextSubpathKey: string
|
|
147
|
-
}) {
|
|
148
|
-
const node = useO(nodeAtoms, subpathKey)
|
|
149
|
-
const edge = useO(edgeAtoms, subpathKey)
|
|
150
|
-
return node === null ? null : (
|
|
151
|
-
<>
|
|
152
|
-
{typeof edge === `boolean` ? (
|
|
153
|
-
<rect class="node" x={node.x - 3} y={node.y - 3} width={6} height={6} />
|
|
154
|
-
) : (
|
|
155
|
-
<>
|
|
156
|
-
<Bezier
|
|
157
|
-
at={edge.s}
|
|
158
|
-
node={node}
|
|
159
|
-
subpathKey={subpathKey}
|
|
160
|
-
prevSubpathKey={nextSubpathKey}
|
|
161
|
-
/>
|
|
162
|
-
{edge.c ? (
|
|
163
|
-
<Bezier
|
|
164
|
-
at={edge.c}
|
|
165
|
-
subpathKey={subpathKey}
|
|
166
|
-
prevSubpathKey={nextSubpathKey}
|
|
167
|
-
/>
|
|
168
|
-
) : null}
|
|
169
|
-
<circle class="node" cx={node.x} cy={node.y} r={3} />
|
|
170
|
-
</>
|
|
171
|
-
)}
|
|
172
|
-
<circle
|
|
173
|
-
class="node-draggable"
|
|
174
|
-
fill="transparent"
|
|
175
|
-
cx={node.x}
|
|
176
|
-
cy={node.y}
|
|
177
|
-
r={10}
|
|
178
|
-
onPointerDown={(evt) => {
|
|
179
|
-
evt.currentTarget.setPointerCapture(evt.pointerId)
|
|
180
|
-
setState(currentlyDraggingAtom, { key: subpathKey })
|
|
181
|
-
}}
|
|
182
|
-
/>
|
|
183
|
-
</>
|
|
184
|
-
)
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function RenderedPath({ pathKey }: { pathKey: string }) {
|
|
188
|
-
const draw = useO(pathDrawSelectors, pathKey)
|
|
189
|
-
return <path d={`${draw} Z`} class="path" style={{ pointerEvents: `none` }} />
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function Path({ pathKey }: { pathKey: string }) {
|
|
193
|
-
const subpathKeys = useO(subpathKeysAtoms, pathKey)
|
|
194
|
-
return (
|
|
195
|
-
<>
|
|
196
|
-
<RenderedPath pathKey={pathKey} />
|
|
197
|
-
{subpathKeys.toReversed().map((spk, idx, arr) => (
|
|
198
|
-
<Node
|
|
199
|
-
subpathKey={spk}
|
|
200
|
-
nextSubpathKey={arr[idx + 1] ?? arr[0]}
|
|
201
|
-
key={spk}
|
|
202
|
-
/>
|
|
203
|
-
))}
|
|
204
|
-
</>
|
|
205
|
-
)
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function PathsDemo() {
|
|
209
|
-
const pathKeys = useO(pathKeysAtom)
|
|
210
|
-
return (
|
|
211
|
-
<>
|
|
212
|
-
{pathKeys.map((pathKey) => {
|
|
213
|
-
return <Path pathKey={pathKey} key={pathKey} />
|
|
214
|
-
})}
|
|
215
|
-
</>
|
|
216
|
-
)
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const svgRefAtom = atom<SVGSVGElement | null>({
|
|
220
|
-
key: `svgRef`,
|
|
221
|
-
default: null,
|
|
222
|
-
})
|
|
223
|
-
const currentlyDraggingAtom = atom<{ key: string; by?: `c` | `s` } | null>({
|
|
224
|
-
key: `currentlyDragging`,
|
|
225
|
-
default: null,
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
function onPointerMove(evt: TargetedPointerEvent<SVGSVGElement>): void {
|
|
229
|
-
evt.preventDefault()
|
|
230
|
-
const { key: currentlyDragging, by: draggingBy } =
|
|
231
|
-
getState(currentlyDraggingAtom) ?? {}
|
|
232
|
-
const svg = getState(svgRefAtom)
|
|
233
|
-
if (!svg || !currentlyDragging) {
|
|
234
|
-
return
|
|
235
|
-
}
|
|
236
|
-
const pt = svg.createSVGPoint()
|
|
237
|
-
pt.x = evt.clientX
|
|
238
|
-
pt.y = evt.clientY
|
|
239
|
-
const ctm = svg.getScreenCTM()?.inverse()
|
|
240
|
-
const { x, y } = pt.matrixTransform(ctm)
|
|
241
|
-
|
|
242
|
-
switch (draggingBy) {
|
|
243
|
-
case undefined:
|
|
244
|
-
setState(nodeAtoms, currentlyDragging, {
|
|
245
|
-
x: clamp(x, -185, WIDTH + 185),
|
|
246
|
-
y: clamp(y, -10, HEIGHT + 10),
|
|
247
|
-
})
|
|
248
|
-
break
|
|
249
|
-
case `s`:
|
|
250
|
-
setState(edgeAtoms, currentlyDragging, (prev) => ({
|
|
251
|
-
...(prev as EdgeXY),
|
|
252
|
-
s: { x: clamp(x, -185, WIDTH + 185), y: clamp(y, -10, HEIGHT + 10) },
|
|
253
|
-
}))
|
|
254
|
-
break
|
|
255
|
-
case `c`:
|
|
256
|
-
setState(edgeAtoms, currentlyDragging, (prev) => ({
|
|
257
|
-
...(prev as EdgeXY),
|
|
258
|
-
c: { x: clamp(x, -185, WIDTH + 185), y: clamp(y, -10, HEIGHT + 10) },
|
|
259
|
-
}))
|
|
260
|
-
break
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const CODES = [`m`, `M`, `l`, `L`, `c`, `C`, `v`, `V`, `z`, `Z`] as const
|
|
265
|
-
|
|
266
|
-
const preactLogoAtom = atom<Loadable<string>>({
|
|
267
|
-
key: `preactLogo`,
|
|
268
|
-
default: () => fetch(`preact.svg`).then((res) => res.text()),
|
|
269
|
-
})
|
|
270
|
-
|
|
271
|
-
const resetTX = transaction<() => Promise<void>>({
|
|
272
|
-
key: `reset`,
|
|
273
|
-
do: async () => {
|
|
274
|
-
const logo = await getState(preactLogoAtom)
|
|
275
|
-
for (const pathKey of getState(pathKeysAtom)) {
|
|
276
|
-
resetState(subpathKeysAtoms, pathKey)
|
|
277
|
-
}
|
|
278
|
-
resetState(pathKeysAtom)
|
|
279
|
-
|
|
280
|
-
const shapes = logo
|
|
281
|
-
.split(`\n`)
|
|
282
|
-
.filter((line) => line.startsWith(`\t<path`))
|
|
283
|
-
.map((line) => {
|
|
284
|
-
const raw = line.split(`d="`)[1].slice(0, -9)
|
|
285
|
-
|
|
286
|
-
type Letter = (typeof CODES)[number]
|
|
287
|
-
let letter: Letter | undefined
|
|
288
|
-
let number = ``
|
|
289
|
-
let numbers: number[] = []
|
|
290
|
-
|
|
291
|
-
const instructions: { letter: Letter; numbers: number[] }[] = []
|
|
292
|
-
for (const c of raw) {
|
|
293
|
-
if (CODES.includes(c as Letter)) {
|
|
294
|
-
if (number) {
|
|
295
|
-
numbers.push(Number.parseFloat(number))
|
|
296
|
-
number = ``
|
|
297
|
-
}
|
|
298
|
-
if (letter) {
|
|
299
|
-
instructions.push({ letter, numbers })
|
|
300
|
-
}
|
|
301
|
-
letter = c as Letter
|
|
302
|
-
numbers = []
|
|
303
|
-
continue
|
|
304
|
-
}
|
|
305
|
-
if (c === ` `) {
|
|
306
|
-
numbers.push(Number.parseFloat(number))
|
|
307
|
-
number = ``
|
|
308
|
-
continue
|
|
309
|
-
}
|
|
310
|
-
if (c === `-` && number) {
|
|
311
|
-
numbers.push(Number.parseFloat(number))
|
|
312
|
-
number = `-`
|
|
313
|
-
continue
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
number += c
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
let prev: PointXY = { x: 0, y: 0 }
|
|
320
|
-
const edgeNodes = instructions.map<{
|
|
321
|
-
node: PointXY | null
|
|
322
|
-
edge: boolean | { c?: PointXY; s: PointXY }
|
|
323
|
-
}>(({ letter: l, numbers: ns }) => {
|
|
324
|
-
let node: PointXY | null
|
|
325
|
-
let edge: boolean | { c?: PointXY; s: PointXY }
|
|
326
|
-
switch (l) {
|
|
327
|
-
case `m`:
|
|
328
|
-
node = { x: prev.x + ns[0], y: prev.y + ns[1] }
|
|
329
|
-
edge = false
|
|
330
|
-
break
|
|
331
|
-
case `M`:
|
|
332
|
-
node = { x: ns[0], y: ns[1] }
|
|
333
|
-
edge = false
|
|
334
|
-
break
|
|
335
|
-
case `l`:
|
|
336
|
-
node = { x: prev.x + ns[0], y: prev.y + ns[1] }
|
|
337
|
-
edge = true
|
|
338
|
-
break
|
|
339
|
-
case `L`:
|
|
340
|
-
node = { x: ns[0], y: ns[1] }
|
|
341
|
-
edge = true
|
|
342
|
-
break
|
|
343
|
-
case `c`:
|
|
344
|
-
node = { x: prev.x + ns[4], y: prev.y + ns[5] }
|
|
345
|
-
edge = {
|
|
346
|
-
c: { x: prev.x + ns[0], y: prev.y + ns[1] },
|
|
347
|
-
s: { x: prev.x + ns[2], y: prev.y + ns[3] },
|
|
348
|
-
}
|
|
349
|
-
break
|
|
350
|
-
case `C`:
|
|
351
|
-
node = { x: ns[4], y: ns[5] }
|
|
352
|
-
edge = {
|
|
353
|
-
c: { x: ns[0], y: ns[1] },
|
|
354
|
-
s: { x: ns[2], y: ns[3] },
|
|
355
|
-
}
|
|
356
|
-
break
|
|
357
|
-
case `v`:
|
|
358
|
-
node = { x: prev.x, y: prev.y + ns[0] }
|
|
359
|
-
edge = true
|
|
360
|
-
break
|
|
361
|
-
case `V`:
|
|
362
|
-
node = { x: prev.x, y: ns[0] }
|
|
363
|
-
edge = true
|
|
364
|
-
break
|
|
365
|
-
case `z`:
|
|
366
|
-
case `Z`:
|
|
367
|
-
node = null
|
|
368
|
-
edge = true
|
|
369
|
-
}
|
|
370
|
-
if (node) {
|
|
371
|
-
prev = node
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
return { node, edge }
|
|
375
|
-
})
|
|
376
|
-
|
|
377
|
-
return edgeNodes
|
|
378
|
-
})
|
|
379
|
-
|
|
380
|
-
let i = 0
|
|
381
|
-
let j = 0
|
|
382
|
-
for (const shape of shapes) {
|
|
383
|
-
const jj = j
|
|
384
|
-
for (const { node, edge } of shape) {
|
|
385
|
-
setState(edgeAtoms, `subpath${j}`, edge)
|
|
386
|
-
setState(nodeAtoms, `subpath${j}`, node)
|
|
387
|
-
j++
|
|
388
|
-
}
|
|
389
|
-
const numberOfNodes = j - jj
|
|
390
|
-
setState(
|
|
391
|
-
subpathKeysAtoms,
|
|
392
|
-
`path${i}`,
|
|
393
|
-
Array.from(
|
|
394
|
-
{ length: numberOfNodes },
|
|
395
|
-
(_, nodeNum) => `subpath${jj + nodeNum}`,
|
|
396
|
-
),
|
|
397
|
-
)
|
|
398
|
-
setState(pathKeysAtom, (prev) => [...prev, `path${i}`])
|
|
399
|
-
i++
|
|
400
|
-
}
|
|
401
|
-
},
|
|
402
|
-
})
|
|
403
|
-
const reset = runTransaction(resetTX)
|
|
404
|
-
|
|
405
|
-
const WIDTH = 256
|
|
406
|
-
const HEIGHT = 296
|
|
407
|
-
export default function BezierPlayground(): VNode {
|
|
408
|
-
const svgRef = useAtomicRef(svgRefAtom)
|
|
409
|
-
const onPointerUp: PointerEventHandler<SVGSVGElement> = useCallback((evt) => {
|
|
410
|
-
evt.currentTarget.releasePointerCapture(evt.pointerId)
|
|
411
|
-
setState(currentlyDraggingAtom, null)
|
|
412
|
-
}, [])
|
|
413
|
-
|
|
414
|
-
useEffect(() => void reset(), [])
|
|
415
|
-
|
|
416
|
-
return (
|
|
417
|
-
<div
|
|
418
|
-
style={{
|
|
419
|
-
display: `flex`,
|
|
420
|
-
flexFlow: `column`,
|
|
421
|
-
position: `relative`,
|
|
422
|
-
overflow: `hidden`,
|
|
423
|
-
maxWidth: `1280px`,
|
|
424
|
-
width: `100vw`,
|
|
425
|
-
alignItems: `center`,
|
|
426
|
-
}}
|
|
427
|
-
>
|
|
428
|
-
<svg
|
|
429
|
-
ref={svgRef}
|
|
430
|
-
viewBox={`-185 -15 ${WIDTH + 370} ${HEIGHT + 30}`}
|
|
431
|
-
width={1000}
|
|
432
|
-
height={500}
|
|
433
|
-
onPointerMove={onPointerMove}
|
|
434
|
-
onPointerUp={onPointerUp}
|
|
435
|
-
onPointerCancel={onPointerUp}
|
|
436
|
-
>
|
|
437
|
-
<title>Bezier Playground</title>
|
|
438
|
-
<defs>
|
|
439
|
-
<pattern id="grid" width="5" height="5" patternUnits="userSpaceOnUse">
|
|
440
|
-
<rect x="0" y="0" width=".5" height=".5" fill="none" stroke="#aaa" />
|
|
441
|
-
</pattern>
|
|
442
|
-
</defs>
|
|
443
|
-
<rect x={0} y={0} width={WIDTH} height={HEIGHT} fill="#aaa3" />
|
|
444
|
-
<rect
|
|
445
|
-
x={-185}
|
|
446
|
-
y={-10}
|
|
447
|
-
width={WIDTH + 370}
|
|
448
|
-
height={HEIGHT + 20}
|
|
449
|
-
fill="url(#grid)"
|
|
450
|
-
/>
|
|
451
|
-
<PathsDemo />
|
|
452
|
-
</svg>
|
|
453
|
-
<button type="button" class="flat" onClick={reset}>
|
|
454
|
-
Reset
|
|
455
|
-
</button>
|
|
456
|
-
</div>
|
|
457
|
-
)
|
|
458
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import "./style.css"
|
|
2
|
-
|
|
3
|
-
import type { VNode } from "preact"
|
|
4
|
-
import { render } from "preact"
|
|
5
|
-
|
|
6
|
-
import BezierPlayground from "./BezierPlayground.tsx"
|
|
7
|
-
|
|
8
|
-
export function App(): VNode {
|
|
9
|
-
return (
|
|
10
|
-
<>
|
|
11
|
-
<h1>Atom.io in Preact on Vite</h1>
|
|
12
|
-
<main>
|
|
13
|
-
<BezierPlayground />
|
|
14
|
-
<article>
|
|
15
|
-
<Resource
|
|
16
|
-
title="Learn Atom.io"
|
|
17
|
-
description="Atom.io is where data lives."
|
|
18
|
-
href="https://atom.io.fyi/docs/getting-started"
|
|
19
|
-
/>
|
|
20
|
-
<Resource
|
|
21
|
-
title="Learn Preact"
|
|
22
|
-
description="If you're new to Preact, try the interactive tutorial to learn important concepts"
|
|
23
|
-
href="https://preactjs.com/tutorial"
|
|
24
|
-
/>
|
|
25
|
-
<Resource
|
|
26
|
-
title="Learn Vite"
|
|
27
|
-
description="To learn more about Vite and how you can customize it to fit your needs, take a look at their excellent documentation"
|
|
28
|
-
href="https://vitejs.dev"
|
|
29
|
-
/>
|
|
30
|
-
</article>
|
|
31
|
-
</main>
|
|
32
|
-
</>
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
type ResourceProps = {
|
|
37
|
-
title: string
|
|
38
|
-
description: string
|
|
39
|
-
href: string
|
|
40
|
-
}
|
|
41
|
-
function Resource(props: ResourceProps) {
|
|
42
|
-
return (
|
|
43
|
-
<a href={props.href} target="_blank" class="resource" rel="noreferrer">
|
|
44
|
-
<h2>{props.title}</h2>
|
|
45
|
-
<p>{props.description}</p>
|
|
46
|
-
</a>
|
|
47
|
-
)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
render(<App />, document.getElementById(`app`)!)
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
Great question — the `d` attribute in an SVG `<path>` element is one of the most powerful but also most cryptic parts of SVG. It defines the shape of the path by listing a series of **commands** and **parameters**. Let’s break it down step by step so you can confidently read (and write) it.
|
|
2
|
-
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
## 🧭 1. What the `d` Attribute Is
|
|
6
|
-
|
|
7
|
-
The `d` attribute contains **a mini language** consisting of:
|
|
8
|
-
|
|
9
|
-
- Single-letter commands (e.g. `M`, `L`, `C`, `Z`)
|
|
10
|
-
- A series of numbers following those commands (coordinates or parameters)
|
|
11
|
-
- Optional spaces and commas
|
|
12
|
-
|
|
13
|
-
For example:
|
|
14
|
-
|
|
15
|
-
```svg
|
|
16
|
-
<path d="M10 10 L50 50 L90 10 Z" />
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
This draws a triangle:
|
|
20
|
-
|
|
21
|
-
1. Move to `(10,10)`
|
|
22
|
-
2. Draw a line to `(50,50)`
|
|
23
|
-
3. Draw a line to `(90,10)`
|
|
24
|
-
4. Close the shape
|
|
25
|
-
|
|
26
|
-
---
|
|
27
|
-
|
|
28
|
-
## ✍️ 2. Absolute vs Relative Commands
|
|
29
|
-
|
|
30
|
-
Each command can be:
|
|
31
|
-
|
|
32
|
-
- **Uppercase** → Absolute coordinates
|
|
33
|
-
- **Lowercase** → Relative coordinates (relative to the current point)
|
|
34
|
-
|
|
35
|
-
Example:
|
|
36
|
-
|
|
37
|
-
```svg
|
|
38
|
-
M 100 100 // Move to (100,100)
|
|
39
|
-
l 50 0 // Draw line relative +50 in x, +0 in y → ends at (150,100)
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
## 🧱 3. Common Path Commands
|
|
45
|
-
|
|
46
|
-
| Command | Name | Parameters | What it does |
|
|
47
|
-
| --------- | ------------------- | --------------------------------------------------- | -------------------------------------------------------- |
|
|
48
|
-
| `M` / `m` | moveto | x y | Moves the “pen” to a new position without drawing |
|
|
49
|
-
| `L` / `l` | lineto | x y | Draws a straight line from current point to new point |
|
|
50
|
-
| `H` / `h` | horizontal lineto | x | Line horizontally (only x changes) |
|
|
51
|
-
| `V` / `v` | vertical lineto | y | Line vertically (only y changes) |
|
|
52
|
-
| `C` / `c` | cubic Bézier curve | x1 y1 x2 y2 x y | Curve with 2 control points and an endpoint |
|
|
53
|
-
| `S` / `s` | smooth cubic Bézier | x2 y2 x y | Like `C` but assumes first control point is a reflection |
|
|
54
|
-
| `Q` / `q` | quadratic Bézier | x1 y1 x y | Curve with 1 control point and an endpoint |
|
|
55
|
-
| `T` / `t` | smooth quadratic | x y | Like `Q` but auto-control point |
|
|
56
|
-
| `A` / `a` | elliptical arc | rx ry x-axis-rotation large-arc-flag sweep-flag x y | Draws an arc |
|
|
57
|
-
| `Z` / `z` | closepath | _(none)_ | Closes the path (line back to start) |
|
|
58
|
-
|
|
59
|
-
---
|
|
60
|
-
|
|
61
|
-
## 🧮 4. How to Read a Complex Example
|
|
62
|
-
|
|
63
|
-
Here’s a more complex example:
|
|
64
|
-
|
|
65
|
-
```svg
|
|
66
|
-
<path d="M 100 100 C 100 50, 200 50, 200 100 S 300 150, 300 100 Z" />
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
Let’s break it down:
|
|
70
|
-
|
|
71
|
-
1. `M 100 100` — move to `(100,100)`
|
|
72
|
-
2. `C 100 50, 200 50, 200 100` — draw cubic Bézier curve with control points `(100,50)` and `(200,50)`, endpoint `(200,100)`
|
|
73
|
-
3. `S 300 150, 300 100` — smooth curve, auto control point from previous, second control `(300,150)`, endpoint `(300,100)`
|
|
74
|
-
4. `Z` — close the path (draw a line back to `(100,100)`)
|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## 🧠 5. Tips for Reading
|
|
79
|
-
|
|
80
|
-
- Think of the “pen” moving around.
|
|
81
|
-
- Break the path at each command letter.
|
|
82
|
-
- Translate relative commands (`l`, `c`, etc.) into absolute positions step by step if needed.
|
|
83
|
-
- Visualization tools like [SVG Path Editor](https://yqnn.github.io/svg-path-editor/) can help you see what’s happening interactively.
|
|
84
|
-
|
|
85
|
-
---
|
|
86
|
-
|
|
87
|
-
## 🧰 6. Handy Shortcuts
|
|
88
|
-
|
|
89
|
-
- Repeating commands can omit the command letter.
|
|
90
|
-
|
|
91
|
-
```svg
|
|
92
|
-
M10 10 L20 20 30 10
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
is equivalent to:
|
|
96
|
-
|
|
97
|
-
```svg
|
|
98
|
-
M10 10 L20 20 L30 10
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
- For curves (`C`, `S`, `Q`, `T`), the number of coordinates tells you how many segments there are.
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
Would you like me to **deconstruct a specific `d` string** you have? (e.g. paste one and I’ll walk through it step by step.)
|