schematic-symbols 0.0.1
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/.github/CODEOWNERS +1 -0
- package/.github/workflows/bun-pver-release.yml +25 -0
- package/README.md +88 -0
- package/assets/symbols-svg-json/boxresistor.json +90 -0
- package/assets/symbols-svg-json/capacitor.json +76 -0
- package/assets/symbols-svg-json/capacitor_polarized.json +83 -0
- package/assets/symbols-svg-json/diode.json +91 -0
- package/assets/symbols-svg-json/diode_bipolar_zener.json +166 -0
- package/assets/symbols-svg-json/diode_schottky.json +76 -0
- package/assets/symbols-svg-json/diode_zener.json +31 -0
- package/assets/symbols-svg-json/fuse.json +84 -0
- package/assets/symbols-svg-json/led.json +205 -0
- package/assets/symbols-svg-json/mosfet_depletion_normally_on.json +267 -0
- package/assets/symbols-svg-json/potentiometer.json +137 -0
- package/assets/symbols-svg-json/potentiometer2.json +160 -0
- package/assets/symbols-svg-json/testshape.json +35 -0
- package/assets/symbols-svg-json/varistor.json +129 -0
- package/assets/symbols.svg +965 -0
- package/biome.json +44 -0
- package/bun.lockb +0 -0
- package/dev-server.ts +22 -0
- package/drawing/arrow.ts +41 -0
- package/drawing/box.ts +5 -0
- package/drawing/circle.ts +7 -0
- package/drawing/defineSymbol.ts +5 -0
- package/drawing/getBoundsOfSvgJson.ts +44 -0
- package/drawing/getSvg.ts +133 -0
- package/drawing/index.ts +11 -0
- package/drawing/mapColor.ts +10 -0
- package/drawing/ninePointAnchorToSvgAnchor.ts +11 -0
- package/drawing/path.ts +5 -0
- package/drawing/pathToSvgD.ts +9 -0
- package/drawing/resizeSymbol.ts +66 -0
- package/drawing/rotateSymbol.ts +94 -0
- package/drawing/svgPathToPoints.ts +50 -0
- package/drawing/text.ts +8 -0
- package/drawing/types.ts +90 -0
- package/index.ts +1 -0
- package/package.json +25 -0
- package/scripts/build.ts +18 -0
- package/scripts/convertToObjectWithOrderedPositionIds.ts +52 -0
- package/scripts/generate-symbols-from-asset-svgs.ts +145 -0
- package/scripts/lib/applyGroupTransformsToChildren.ts +118 -0
- package/scripts/lib/findInnerText.ts +11 -0
- package/scripts/lib/generate-web-page.ts +60 -0
- package/scripts/lib/getTsFileContentForSvgGroup.ts +33 -0
- package/scripts/lib/serializeSvgPathCommands.ts +47 -0
- package/symbols/boxresistor_horz.ts +35 -0
- package/symbols/boxresistor_vert.ts +4 -0
- package/symbols/fuse_horz.ts +24 -0
- package/symbols/fuse_vert.ts +4 -0
- package/symbols/index.ts +31 -0
- package/symbols/led_horz.ts +18 -0
- package/symbols/led_vert.ts +4 -0
- package/symbols/mosfet_depletion_normally_on_horz.ts +19 -0
- package/symbols/mosfet_depletion_normally_on_vert.ts +21 -0
- package/symbols/potentiometer2_horz.ts +14 -0
- package/symbols/potentiometer2_vert.ts +4 -0
- package/symbols/potentiometer_horz.ts +18 -0
- package/symbols/potentiometer_vert.ts +6 -0
- package/symbols/varistor_horz.ts +24 -0
- package/symbols/varistor_vert.ts +4 -0
- package/tests/assets/boxresistor-untransformed.json +187 -0
- package/tests/assets/testshape-untransformed.json +25 -0
- package/tests/normalize-svg.test.ts +29 -0
- package/tsconfig.json +28 -0
package/biome.json
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "https://biomejs.dev/schemas/1.7.3/schema.json",
|
3
|
+
"organizeImports": {
|
4
|
+
"enabled": true
|
5
|
+
},
|
6
|
+
"formatter": {
|
7
|
+
"enabled": true,
|
8
|
+
"indentStyle": "space"
|
9
|
+
},
|
10
|
+
"files": {
|
11
|
+
"ignore": ["cosmos-export", "dist", "package.json"]
|
12
|
+
},
|
13
|
+
"javascript": {
|
14
|
+
"formatter": {
|
15
|
+
"jsxQuoteStyle": "double",
|
16
|
+
"quoteProperties": "asNeeded",
|
17
|
+
"trailingCommas": "all",
|
18
|
+
"semicolons": "asNeeded",
|
19
|
+
"arrowParentheses": "always",
|
20
|
+
"bracketSpacing": true,
|
21
|
+
"bracketSameLine": false
|
22
|
+
}
|
23
|
+
},
|
24
|
+
"linter": {
|
25
|
+
"enabled": false,
|
26
|
+
"rules": {
|
27
|
+
"recommended": true,
|
28
|
+
"suspicious": {
|
29
|
+
"noExplicitAny": "off"
|
30
|
+
},
|
31
|
+
"style": {
|
32
|
+
"noNonNullAssertion": "off",
|
33
|
+
"useFilenamingConvention": {
|
34
|
+
"level": "error",
|
35
|
+
"options": {
|
36
|
+
"strictCase": true,
|
37
|
+
"requireAscii": true,
|
38
|
+
"filenameCases": ["kebab-case", "export"]
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
package/bun.lockb
ADDED
Binary file
|
package/dev-server.ts
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
import { generateWebPage } from "./scripts/lib/generate-web-page"
|
2
|
+
|
3
|
+
console.log(`Serving on http://localhost:3077`)
|
4
|
+
Bun.serve({
|
5
|
+
port: 3077,
|
6
|
+
async fetch(req) {
|
7
|
+
const url = new URL(req.url)
|
8
|
+
|
9
|
+
if (url.pathname === "/") {
|
10
|
+
const html = generateWebPage()
|
11
|
+
return new Response(html, {
|
12
|
+
headers: { "Content-Type": "text/html" },
|
13
|
+
})
|
14
|
+
}
|
15
|
+
|
16
|
+
// Serve static files
|
17
|
+
return new Response("", {
|
18
|
+
status: 404,
|
19
|
+
})
|
20
|
+
// return new Response(await Bun.file(url.pathname))
|
21
|
+
},
|
22
|
+
})
|
package/drawing/arrow.ts
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
import { Point, Primitive } from "drawing"
|
2
|
+
import { path } from "./path"
|
3
|
+
|
4
|
+
export const arrow = (
|
5
|
+
start: Point,
|
6
|
+
end: Point,
|
7
|
+
color: string = "black",
|
8
|
+
headSize: number = 10,
|
9
|
+
): Primitive[] => {
|
10
|
+
// Calculate the angle and length of the arrow
|
11
|
+
const dx = end.x - start.x
|
12
|
+
const dy = end.y - start.y
|
13
|
+
const angle = Math.atan2(dy, dx)
|
14
|
+
const length = Math.sqrt(dx * dx + dy * dy)
|
15
|
+
|
16
|
+
// Calculate the points for the arrow head
|
17
|
+
const headAngle = Math.PI / 6 // 30 degrees
|
18
|
+
const headPoint1 = {
|
19
|
+
x: end.x - headSize * Math.cos(angle - headAngle),
|
20
|
+
y: end.y - headSize * Math.sin(angle - headAngle),
|
21
|
+
}
|
22
|
+
const headPoint2 = {
|
23
|
+
x: end.x - headSize * Math.cos(angle + headAngle),
|
24
|
+
y: end.y - headSize * Math.sin(angle + headAngle),
|
25
|
+
}
|
26
|
+
|
27
|
+
return [
|
28
|
+
// Arrow body
|
29
|
+
path({
|
30
|
+
points: [start, end],
|
31
|
+
color: color,
|
32
|
+
fill: false,
|
33
|
+
}),
|
34
|
+
// Arrow head
|
35
|
+
path({
|
36
|
+
points: [headPoint1, end, headPoint2],
|
37
|
+
color: color,
|
38
|
+
fill: true,
|
39
|
+
}),
|
40
|
+
]
|
41
|
+
}
|
package/drawing/box.ts
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
import type { INode } from "svgson"
|
2
|
+
import { svgPathToPoints } from "./svgPathToPoints"
|
3
|
+
|
4
|
+
export function getBoundsOfSvgJson(svgJson: INode): {
|
5
|
+
minX: number
|
6
|
+
maxX: number
|
7
|
+
minY: number
|
8
|
+
maxY: number
|
9
|
+
width: number
|
10
|
+
height: number
|
11
|
+
centerX: number
|
12
|
+
centerY: number
|
13
|
+
} {
|
14
|
+
let minX = Infinity
|
15
|
+
let maxX = -Infinity
|
16
|
+
let minY = Infinity
|
17
|
+
let maxY = -Infinity
|
18
|
+
|
19
|
+
function processNode(node: INode) {
|
20
|
+
if (node.name === "path" && node.attributes.d) {
|
21
|
+
const points = svgPathToPoints(node.attributes.d)
|
22
|
+
|
23
|
+
for (const point of points) {
|
24
|
+
minX = Math.min(minX, point.x)
|
25
|
+
maxX = Math.max(maxX, point.x)
|
26
|
+
minY = Math.min(minY, point.y)
|
27
|
+
maxY = Math.max(maxY, point.y)
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
if (node.children) {
|
32
|
+
node.children.forEach(processNode)
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
processNode(svgJson)
|
37
|
+
|
38
|
+
const width = maxX - minX
|
39
|
+
const height = maxY - minY
|
40
|
+
const centerX = (minX + maxX) / 2
|
41
|
+
const centerY = (minY + maxY) / 2
|
42
|
+
|
43
|
+
return { minX, maxX, minY, maxY, width, height, centerX, centerY }
|
44
|
+
}
|
@@ -0,0 +1,133 @@
|
|
1
|
+
import { pathToSvgD } from "./pathToSvgD"
|
2
|
+
import { mapColor } from "./mapColor"
|
3
|
+
import type {
|
4
|
+
NinePointAnchor,
|
5
|
+
TextPrimitive,
|
6
|
+
Port,
|
7
|
+
SchSymbol,
|
8
|
+
Point,
|
9
|
+
} from "./types"
|
10
|
+
|
11
|
+
function createDiamondElement(center: Point, size: number = 0.05): string {
|
12
|
+
const { x, y } = center
|
13
|
+
const halfSize = size / 2
|
14
|
+
return `<path d="M ${x} ${y - halfSize} L ${x + halfSize} ${y} L ${x} ${y + halfSize} L ${x - halfSize} ${y} Z" fill="green" />`
|
15
|
+
}
|
16
|
+
|
17
|
+
function createTextElement(primitive: TextPrimitive): {
|
18
|
+
text: string
|
19
|
+
anchor: string
|
20
|
+
} {
|
21
|
+
const { x, y, text, fontSize = 0.1, anchor } = primitive
|
22
|
+
let textAnchor: string
|
23
|
+
let dx: number = 0
|
24
|
+
let dy: number = 0
|
25
|
+
|
26
|
+
const capHeight = fontSize * 0.75
|
27
|
+
|
28
|
+
switch (anchor) {
|
29
|
+
case "top_left":
|
30
|
+
textAnchor = "start"
|
31
|
+
dy = fontSize
|
32
|
+
break
|
33
|
+
case "top_right":
|
34
|
+
textAnchor = "end"
|
35
|
+
dy = fontSize
|
36
|
+
break
|
37
|
+
case "bottom_left":
|
38
|
+
textAnchor = "start"
|
39
|
+
break
|
40
|
+
case "bottom_right":
|
41
|
+
textAnchor = "end"
|
42
|
+
break
|
43
|
+
case "center":
|
44
|
+
textAnchor = "middle"
|
45
|
+
dy = fontSize / 2
|
46
|
+
break
|
47
|
+
case "middle_top":
|
48
|
+
textAnchor = "middle"
|
49
|
+
dy = capHeight
|
50
|
+
break
|
51
|
+
case "middle_bottom":
|
52
|
+
textAnchor = "middle"
|
53
|
+
break
|
54
|
+
case "middle_left":
|
55
|
+
textAnchor = "start"
|
56
|
+
dy = capHeight / 2
|
57
|
+
break
|
58
|
+
case "middle_right":
|
59
|
+
textAnchor = "end"
|
60
|
+
dy = capHeight / 2
|
61
|
+
break
|
62
|
+
}
|
63
|
+
|
64
|
+
return {
|
65
|
+
text: `<text x="${x}" y="${y}" dx="${dx}" dy="${dy}" text-anchor="${textAnchor}" style="font: ${fontSize ?? 0.1}px monospace; fill: ${mapColor("primary")}">${text}</text>`,
|
66
|
+
anchor: `<rect x="${x - 0.025 / 2}" y="${y - 0.025 / 2}" width="0.025" height="0.025" fill="blue" />`,
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
function createPortElement(port: Port): string {
|
71
|
+
const { x, y, labels } = port
|
72
|
+
const rectSize = 0.05
|
73
|
+
const labelFontSize = 0.08
|
74
|
+
const label = labels[0] || ""
|
75
|
+
|
76
|
+
return `
|
77
|
+
<rect x="${x - rectSize / 2}" y="${y - rectSize / 2}" width="${rectSize}" height="${rectSize}" fill="red" />
|
78
|
+
<text x="${x - labelFontSize / 2}" y="${y + rectSize + labelFontSize / 2}" text-anchor="middle" style="font: ${labelFontSize}px monospace; fill: #833;">${label}</text>
|
79
|
+
`
|
80
|
+
}
|
81
|
+
|
82
|
+
export function getSvg(
|
83
|
+
symbol: SchSymbol,
|
84
|
+
options: { width?: number; height?: number; debug?: boolean } = {},
|
85
|
+
): string {
|
86
|
+
const { debug = false } = options
|
87
|
+
const { primitives, size, ports } = symbol
|
88
|
+
const svgElements = primitives.map((primitive) => {
|
89
|
+
switch (primitive.type) {
|
90
|
+
case "path":
|
91
|
+
return `<path d="${pathToSvgD(primitive.points, primitive.closed)}" fill="${primitive.fill ? mapColor(primitive.color) : "none"}" stroke="${mapColor(primitive.color)}" stroke-width="0.02" stroke-linecap="round" stroke-linejoin="round" />`
|
92
|
+
case "text":
|
93
|
+
const textElements = createTextElement(primitive)
|
94
|
+
return textElements.text + (debug ? textElements.anchor : "")
|
95
|
+
case "circle":
|
96
|
+
return `<circle cx="${primitive.x}" cy="${primitive.y}" r="${primitive.radius}" fill="${mapColor("primary")}" />`
|
97
|
+
case "box":
|
98
|
+
return `<rect x="${primitive.x}" y="${primitive.y}" width="${primitive.width}" height="${primitive.height}" fill="${mapColor("primary")}" />`
|
99
|
+
default:
|
100
|
+
return ""
|
101
|
+
}
|
102
|
+
})
|
103
|
+
|
104
|
+
// Use the center and the size to calculate the viewBox
|
105
|
+
const bufferMultiple = 1.1
|
106
|
+
const w = size.width * bufferMultiple
|
107
|
+
const h = size.height * bufferMultiple
|
108
|
+
const viewBox = {
|
109
|
+
x: symbol.center.x - w / 2,
|
110
|
+
y: symbol.center.y - h / 2,
|
111
|
+
width: w,
|
112
|
+
height: h,
|
113
|
+
}
|
114
|
+
|
115
|
+
if (options.width && !options.height) {
|
116
|
+
options.height = options.width! * (viewBox.height / viewBox.width)
|
117
|
+
} else if (!options.width && options.height) {
|
118
|
+
options.width = options.height! * (viewBox.width / viewBox.height)
|
119
|
+
} else if (!options.width && !options.height) {
|
120
|
+
options.width = viewBox.width
|
121
|
+
options.height = viewBox.height
|
122
|
+
}
|
123
|
+
|
124
|
+
const portElements = ports.map(createPortElement).join("\n ")
|
125
|
+
|
126
|
+
const centerDiamond = createDiamondElement(symbol.center)
|
127
|
+
|
128
|
+
return `<svg width="${options.width}" height="${options.height}" viewBox="${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}" xmlns="http://www.w3.org/2000/svg">
|
129
|
+
${svgElements.join("\n ")}
|
130
|
+
${portElements}
|
131
|
+
${centerDiamond}
|
132
|
+
</svg>`
|
133
|
+
}
|
package/drawing/index.ts
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
export * from "./types"
|
2
|
+
export * from "./box"
|
3
|
+
export * from "./arrow"
|
4
|
+
export * from "./getBoundsOfSvgJson"
|
5
|
+
export * from "./circle"
|
6
|
+
export * from "./getSvg"
|
7
|
+
export * from "./mapColor"
|
8
|
+
export * from "./resizeSymbol"
|
9
|
+
export * from "./svgPathToPoints"
|
10
|
+
export * from "./pathToSvgD"
|
11
|
+
export * from "./text"
|
package/drawing/path.ts
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
import type { Point } from "drawing"
|
2
|
+
|
3
|
+
export function pathToSvgD(points: Point[], closed: boolean = false): string {
|
4
|
+
const pathCommands = points
|
5
|
+
.map((point, index) => `${index === 0 ? "M" : "L"}${point.x},${point.y}`)
|
6
|
+
.join(" ")
|
7
|
+
|
8
|
+
return closed ? `${pathCommands} Z` : pathCommands
|
9
|
+
}
|
@@ -0,0 +1,66 @@
|
|
1
|
+
import { SchSymbol } from "./types"
|
2
|
+
export function resizeSymbol(
|
3
|
+
symbol: SchSymbol,
|
4
|
+
newSize: { width?: number; height?: number },
|
5
|
+
): SchSymbol {
|
6
|
+
const { width: oldWidth, height: oldHeight } = symbol.size
|
7
|
+
let scaleX = 1,
|
8
|
+
scaleY = 1
|
9
|
+
|
10
|
+
if (newSize.width && newSize.height) {
|
11
|
+
scaleX = newSize.width / oldWidth
|
12
|
+
scaleY = newSize.height / oldHeight
|
13
|
+
} else if (newSize.width) {
|
14
|
+
scaleX = scaleY = newSize.width / oldWidth
|
15
|
+
} else if (newSize.height) {
|
16
|
+
scaleX = scaleY = newSize.height / oldHeight
|
17
|
+
}
|
18
|
+
|
19
|
+
const resizedPrimitives = symbol.primitives.map((primitive) => {
|
20
|
+
switch (primitive.type) {
|
21
|
+
case "path":
|
22
|
+
return {
|
23
|
+
...primitive,
|
24
|
+
points: primitive.points.map((p) => ({
|
25
|
+
x: p.x * scaleX,
|
26
|
+
y: p.y * scaleY,
|
27
|
+
})),
|
28
|
+
}
|
29
|
+
case "text":
|
30
|
+
case "circle":
|
31
|
+
return {
|
32
|
+
...primitive,
|
33
|
+
x: primitive.x * scaleX,
|
34
|
+
y: primitive.y * scaleY,
|
35
|
+
}
|
36
|
+
case "box":
|
37
|
+
return {
|
38
|
+
...primitive,
|
39
|
+
x: primitive.x * scaleX,
|
40
|
+
y: primitive.y * scaleY,
|
41
|
+
width: primitive.width * scaleX,
|
42
|
+
height: primitive.height * scaleY,
|
43
|
+
}
|
44
|
+
default:
|
45
|
+
return primitive
|
46
|
+
}
|
47
|
+
})
|
48
|
+
|
49
|
+
return {
|
50
|
+
...symbol,
|
51
|
+
primitives: resizedPrimitives,
|
52
|
+
center: {
|
53
|
+
x: symbol.center.x * scaleX,
|
54
|
+
y: symbol.center.y * scaleY,
|
55
|
+
},
|
56
|
+
ports: symbol.ports.map((port) => ({
|
57
|
+
...port,
|
58
|
+
x: port.x * scaleX,
|
59
|
+
y: port.y * scaleY,
|
60
|
+
})),
|
61
|
+
size: {
|
62
|
+
width: oldWidth * scaleX,
|
63
|
+
height: oldHeight * scaleY,
|
64
|
+
},
|
65
|
+
}
|
66
|
+
}
|
@@ -0,0 +1,94 @@
|
|
1
|
+
import { rotate, applyToPoint } from "transformation-matrix"
|
2
|
+
import type {
|
3
|
+
SchSymbol,
|
4
|
+
Primitive,
|
5
|
+
Point,
|
6
|
+
Port,
|
7
|
+
NinePointAnchor,
|
8
|
+
} from "./types"
|
9
|
+
|
10
|
+
const rotateAnchor = (anchor: NinePointAnchor): NinePointAnchor => {
|
11
|
+
switch (anchor) {
|
12
|
+
case "middle_top":
|
13
|
+
return "middle_right"
|
14
|
+
case "middle_bottom":
|
15
|
+
return "middle_left"
|
16
|
+
}
|
17
|
+
return anchor
|
18
|
+
}
|
19
|
+
|
20
|
+
export const rotateSymbol = (
|
21
|
+
symbol: SchSymbol,
|
22
|
+
overrides?: Partial<SchSymbol>,
|
23
|
+
): SchSymbol => {
|
24
|
+
const transform = rotate(Math.PI / 2, symbol.center.x, symbol.center.y)
|
25
|
+
|
26
|
+
const { primitives, center, size, ports } = symbol
|
27
|
+
|
28
|
+
const rotatedPrimitives = primitives.map((primitive): Primitive => {
|
29
|
+
primitive = { ...primitive }
|
30
|
+
switch (primitive.type) {
|
31
|
+
case "path":
|
32
|
+
return {
|
33
|
+
...primitive,
|
34
|
+
points: primitive.points.map(
|
35
|
+
(point) => applyToPoint(transform, point) as Point,
|
36
|
+
),
|
37
|
+
}
|
38
|
+
case "text":
|
39
|
+
const rotatedPoint = applyToPoint(transform, {
|
40
|
+
x: primitive.x,
|
41
|
+
y: primitive.y,
|
42
|
+
}) as Point
|
43
|
+
|
44
|
+
primitive.anchor = rotateAnchor(primitive.anchor)
|
45
|
+
|
46
|
+
return {
|
47
|
+
...primitive,
|
48
|
+
x: rotatedPoint.x,
|
49
|
+
y: rotatedPoint.y,
|
50
|
+
}
|
51
|
+
case "circle":
|
52
|
+
const rotatedCenter = applyToPoint(transform, {
|
53
|
+
x: primitive.x,
|
54
|
+
y: primitive.y,
|
55
|
+
}) as Point
|
56
|
+
return {
|
57
|
+
...primitive,
|
58
|
+
x: rotatedCenter.x,
|
59
|
+
y: rotatedCenter.y,
|
60
|
+
}
|
61
|
+
case "box":
|
62
|
+
const rotatedCorner = applyToPoint(transform, {
|
63
|
+
x: primitive.x,
|
64
|
+
y: primitive.y,
|
65
|
+
}) as Point
|
66
|
+
return {
|
67
|
+
...primitive,
|
68
|
+
x: rotatedCorner.x,
|
69
|
+
y: rotatedCorner.y,
|
70
|
+
width: primitive.height,
|
71
|
+
height: primitive.width,
|
72
|
+
}
|
73
|
+
}
|
74
|
+
})
|
75
|
+
|
76
|
+
const rotatedPorts = ports.map(
|
77
|
+
(port): Port => ({
|
78
|
+
...port,
|
79
|
+
...(applyToPoint(transform, port) as Point),
|
80
|
+
}),
|
81
|
+
)
|
82
|
+
|
83
|
+
return {
|
84
|
+
primitives: rotatedPrimitives,
|
85
|
+
center,
|
86
|
+
ports: rotatedPorts,
|
87
|
+
// TODO recompute size using overrides
|
88
|
+
size: {
|
89
|
+
width: size.height,
|
90
|
+
height: size.width,
|
91
|
+
},
|
92
|
+
...overrides,
|
93
|
+
}
|
94
|
+
}
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import { parseSVG, makeAbsolute } from "svg-path-parser"
|
2
|
+
|
3
|
+
export function svgPathToPoints(d: string): Array<{ x: number; y: number }> {
|
4
|
+
const parsedPath = parseSVG(d)
|
5
|
+
const absolutePath = makeAbsolute(parsedPath)
|
6
|
+
|
7
|
+
const points: Array<{ x: number; y: number }> = []
|
8
|
+
let firstPoint: { x: number; y: number } | null = null
|
9
|
+
|
10
|
+
for (const command of absolutePath) {
|
11
|
+
switch (command.code) {
|
12
|
+
case "M":
|
13
|
+
if (!firstPoint) {
|
14
|
+
firstPoint = { x: command.x, y: command.y }
|
15
|
+
}
|
16
|
+
points.push({ x: command.x, y: command.y })
|
17
|
+
break
|
18
|
+
case "L":
|
19
|
+
case "H":
|
20
|
+
case "V":
|
21
|
+
points.push({ x: command.x, y: command.y })
|
22
|
+
break
|
23
|
+
case "C":
|
24
|
+
points.push(
|
25
|
+
{ x: command.x1, y: command.y1 },
|
26
|
+
{ x: command.x2, y: command.y2 },
|
27
|
+
{ x: command.x, y: command.y },
|
28
|
+
)
|
29
|
+
break
|
30
|
+
case "S":
|
31
|
+
case "Q":
|
32
|
+
points.push(
|
33
|
+
{ x: command.x0, y: command.y0 },
|
34
|
+
{ x: command.x, y: command.y },
|
35
|
+
)
|
36
|
+
break
|
37
|
+
case "T":
|
38
|
+
case "A":
|
39
|
+
points.push({ x: command.x, y: command.y })
|
40
|
+
break
|
41
|
+
case "Z":
|
42
|
+
if (firstPoint) {
|
43
|
+
points.push(firstPoint)
|
44
|
+
}
|
45
|
+
break
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
return points
|
50
|
+
}
|
package/drawing/text.ts
ADDED
package/drawing/types.ts
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
export interface Point {
|
2
|
+
x: number
|
3
|
+
y: number
|
4
|
+
}
|
5
|
+
|
6
|
+
export interface Port {
|
7
|
+
x: number
|
8
|
+
y: number
|
9
|
+
labels: string[]
|
10
|
+
}
|
11
|
+
|
12
|
+
export interface PathPrimitive {
|
13
|
+
type: "path"
|
14
|
+
fill: boolean
|
15
|
+
points: Point[]
|
16
|
+
color: string
|
17
|
+
closed?: boolean
|
18
|
+
}
|
19
|
+
|
20
|
+
export interface TextPrimitive {
|
21
|
+
type: "text"
|
22
|
+
text: string
|
23
|
+
fontSize?: number
|
24
|
+
x: number
|
25
|
+
y: number
|
26
|
+
anchor: NinePointAnchor
|
27
|
+
}
|
28
|
+
|
29
|
+
export interface CirclePrimitive {
|
30
|
+
type: "circle"
|
31
|
+
x: number
|
32
|
+
y: number
|
33
|
+
radius: number
|
34
|
+
}
|
35
|
+
|
36
|
+
export interface BoxPrimitive {
|
37
|
+
type: "box"
|
38
|
+
x: number
|
39
|
+
y: number
|
40
|
+
width: number
|
41
|
+
height: number
|
42
|
+
anchor: string
|
43
|
+
}
|
44
|
+
|
45
|
+
export type Primitive =
|
46
|
+
| PathPrimitive
|
47
|
+
| TextPrimitive
|
48
|
+
| CirclePrimitive
|
49
|
+
| BoxPrimitive
|
50
|
+
|
51
|
+
export interface SchSymbol {
|
52
|
+
primitives: Primitive[]
|
53
|
+
center: Point
|
54
|
+
ports: Port[]
|
55
|
+
size: {
|
56
|
+
width: number
|
57
|
+
height: number
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
export type Bounds = {
|
62
|
+
minX: number
|
63
|
+
maxX: number
|
64
|
+
minY: number
|
65
|
+
maxY: number
|
66
|
+
width: number
|
67
|
+
height: number
|
68
|
+
centerX: number
|
69
|
+
centerY: number
|
70
|
+
}
|
71
|
+
|
72
|
+
export interface SvgData {
|
73
|
+
paths: Record<string, PathPrimitive>
|
74
|
+
texts: Record<string, TextPrimitive>
|
75
|
+
refblocks: Record<string, Point>
|
76
|
+
bounds: Bounds
|
77
|
+
}
|
78
|
+
|
79
|
+
export type SvgAnchor = "start" | "middle" | "end"
|
80
|
+
|
81
|
+
export type NinePointAnchor =
|
82
|
+
| "top_left"
|
83
|
+
| "top_right"
|
84
|
+
| "bottom_left"
|
85
|
+
| "bottom_right"
|
86
|
+
| "center"
|
87
|
+
| "middle_top"
|
88
|
+
| "middle_bottom"
|
89
|
+
| "middle_left"
|
90
|
+
| "middle_right"
|
package/index.ts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export { default as symbols } from "./symbols"
|