@tscircuit/schematic-viewer 1.2.7 → 1.2.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/schematic-viewer",
3
- "version": "1.2.7",
3
+ "version": "1.2.9",
4
4
  "main": "dist/index.js",
5
5
  "license": "MIT",
6
6
  "repository": "tscircuit/schematic-viewer",
@@ -33,7 +33,7 @@
33
33
  "@storybook/react": "^7.4.0",
34
34
  "@storybook/testing-library": "^0.0.14-next.2",
35
35
  "@tscircuit/builder": "^1.5.89",
36
- "@tscircuit/react-fiber": "^1.1.6",
36
+ "@tscircuit/react-fiber": "^1.1.8",
37
37
  "@tscircuit/routing": "^1.3.0",
38
38
  "@tscircuit/table-viewer": "^0.0.6",
39
39
  "@types/node": "^18.6.0",
@@ -58,7 +58,7 @@
58
58
  "zustand": "^4.0.0"
59
59
  },
60
60
  "dependencies": {
61
- "@tscircuit/soup": "^0.0.7",
61
+ "@tscircuit/soup": "^0.0.16",
62
62
  "convert-units": "^2.3.4",
63
63
  "react-error-boundary": "^4.0.4",
64
64
  "react-supergrid": "^1.0.10",
package/src/Schematic.tsx CHANGED
@@ -43,7 +43,7 @@ export const Schematic = ({
43
43
  }: {
44
44
  children?: any
45
45
 
46
- /** @deprecated */
46
+ /** @deprecated use soup */
47
47
  elements?: any
48
48
 
49
49
  soup?: any
@@ -82,6 +82,7 @@ export const Schematic = ({
82
82
  (elmBounds.height ?? 0) / height,
83
83
  100
84
84
  )
85
+ // console.log(elements)
85
86
  setElements(elements)
86
87
  setProject(createProjectFromElements(elements))
87
88
  setTransform(
@@ -1,6 +1,9 @@
1
- import { AnyElement, SourcePort } from "@tscircuit/builder"
1
+ import { AnySoupElement, SourcePort } from "@tscircuit/soup"
2
2
 
3
- export const collectElementRefs = (elm: AnyElement, allElms: AnyElement[]) => {
3
+ export const collectElementRefs = (
4
+ elm: AnySoupElement,
5
+ allElms: AnySoupElement[]
6
+ ) => {
4
7
  const source_port = allElms.find(
5
8
  (e) =>
6
9
  e.type === "source_port" &&
@@ -3,7 +3,7 @@ export const directionToVec = (direction: "up" | "down" | "left" | "right") => {
3
3
  else if (direction === "down") return { x: 0, y: -1 }
4
4
  else if (direction === "left") return { x: -1, y: 0 }
5
5
  else if (direction === "right") return { x: 1, y: 0 }
6
- else throw new Error("Invalid direction")
6
+ else throw new Error(`Invalid direction "${direction}"`)
7
7
  }
8
8
 
9
9
  export const vecToDirection = ({ x, y }: { x: number; y: number }) => {
@@ -50,31 +50,63 @@ export const SVGPathComponent = ({
50
50
  }
51
51
  // pathBounds.height = Math.max(pathBounds.height, 0.01)
52
52
  // pathBounds.width = Math.max(pathBounds.width, 0.01)
53
+
54
+ // Three sizes:
55
+ // pathBound size (the size of the path in "d")
56
+ // innerSize (the screen-space size of the path)
57
+ // fullSize (the screen-space size of the svg element, innerSize plus padding)
58
+
59
+ const padding = {
60
+ x: 0,
61
+ y: 0,
62
+ }
63
+
53
64
  const absoluteCenter = applyToPoint(ct, center)
54
- const actualAbsWidth = size.width * ct.a
55
- const actualAbsHeight = size.height * Math.abs(ct.d)
56
- const absoluteSize = {
57
- width: Math.max(1, actualAbsWidth),
58
- height: Math.max(1, actualAbsHeight),
65
+
66
+ const innerSize = {
67
+ width: size.width * ct.a,
68
+ height: size.height * Math.abs(ct.d),
69
+ }
70
+
71
+ const fullSize = {
72
+ width: innerSize.width + padding.x * 2,
73
+ height: innerSize.height + padding.y * 2,
59
74
  }
60
75
 
61
76
  const [hovering, setHovering] = useState(false)
62
77
 
63
- const svgLeft = absoluteCenter.x - absoluteSize.width / 2
64
- const svgTop = absoluteCenter.y - absoluteSize.height / 2
78
+ const svgLeft = absoluteCenter.x - fullSize.width / 2
79
+ const svgTop = absoluteCenter.y - fullSize.height / 2
65
80
 
66
- const viewBox = `${pathBounds.minX} ${pathBounds.minY} ${pathBounds.width} ${pathBounds.height}`
81
+ // const viewBox = `${pathBounds.minX} ${pathBounds.minY} ${pathBounds.width} ${pathBounds.height}`
67
82
  // const viewBox2 = `${svgLeft} ${svgTctualAbsWidth} ${actualAbsHeight}`
68
83
 
84
+ // console.log(
85
+ // pathBounds,
86
+ // fullSize,
87
+ // fullSize.width / pathBounds.width,
88
+ // fullSize.height / pathBounds.height
89
+ // )
90
+ const preferredRatio =
91
+ pathBounds.width === 0
92
+ ? innerSize.height / pathBounds.height
93
+ : innerSize.width / pathBounds.width
69
94
  const svgToScreen = compose(
70
95
  // translate(0, 0)
71
96
  scale(
72
- actualAbsWidth / pathBounds.width,
73
- actualAbsHeight / pathBounds.height
97
+ pathBounds.width === 0
98
+ ? preferredRatio
99
+ : fullSize.width / pathBounds.width,
100
+ pathBounds.height === 0
101
+ ? preferredRatio
102
+ : fullSize.height / pathBounds.height
74
103
  ),
75
104
  translate(-pathBounds.minX, -pathBounds.minY)
76
105
  // translate(center.x, center.y)
77
106
  )
107
+ // console.log(svgToScreen)
108
+ // console.log(toSVG(svgToScreen))
109
+ // console.log(paths[0].d)
78
110
  // translate(..., ...),
79
111
 
80
112
  return (
@@ -87,8 +119,8 @@ export const SVGPathComponent = ({
87
119
  left: svgLeft - 6,
88
120
  top: svgTop - 6,
89
121
  pointerEvents: "none",
90
- width: actualAbsWidth + 12,
91
- height: actualAbsHeight + 12,
122
+ width: fullSize.width + 12,
123
+ height: fullSize.height + 12,
92
124
  border: "1px red solid",
93
125
  mixBlendMode: "difference",
94
126
  zIndex: 1000,
@@ -97,7 +129,7 @@ export const SVGPathComponent = ({
97
129
  <div
98
130
  style={{
99
131
  position: "absolute",
100
- left: svgLeft + actualAbsWidth + 10,
132
+ left: svgLeft + fullSize.width + 10,
101
133
  pointerEvents: "none",
102
134
  zIndex: 1000,
103
135
  color: "red",
@@ -131,18 +163,15 @@ export const SVGPathComponent = ({
131
163
  // backgroundColor: "rgba(255, 0, 0, 0.1)",
132
164
  }}
133
165
  overflow="visible"
134
- width={absoluteSize.width}
135
- height={absoluteSize.height}
166
+ width={fullSize.width}
167
+ height={fullSize.height}
136
168
  >
137
169
  {paths.map((p, i) => (
138
170
  <path
139
171
  key={i}
140
- // transform={toSVG(inverse(ct))}
141
- // transform={`scale(${ct.a}, ${ct.a})`}
142
172
  transform={toSVG(svgToScreen)}
143
173
  fill={p.fill ?? "none"}
144
174
  strokeLinecap="round"
145
- // strokeWidth={2 * (p.strokeWidth || 1)}
146
175
  strokeWidth={1.5 * (p.strokeWidth || 1)}
147
176
  stroke={p.stroke || "red"}
148
177
  d={p.d}
@@ -0,0 +1,76 @@
1
+ import { useCameraTransform } from "lib/render-context"
2
+ import getSVGPathBounds from "lib/utils/get-svg-path-bounds"
3
+ import { useCallback, useState } from "react"
4
+
5
+ import {
6
+ applyToPoint,
7
+ toSVG,
8
+ inverse,
9
+ compose,
10
+ translate,
11
+ scale,
12
+ } from "transformation-matrix"
13
+
14
+ interface Props {
15
+ rotation: number
16
+ center: { x: number; y: number }
17
+ size: { width: number; height: number }
18
+ invertY?: boolean
19
+ shiftToBottom?: boolean
20
+ paths: Array<{
21
+ strokeWidth: number
22
+ stroke: string
23
+ fill?: string
24
+ d: string
25
+ }>
26
+ zIndex?: number
27
+ hoverContent?: any
28
+ }
29
+
30
+ export const SVGPathComponent2 = ({
31
+ size,
32
+ center,
33
+ rotation,
34
+ paths,
35
+ zIndex,
36
+ invertY,
37
+ shiftToBottom,
38
+ hoverContent,
39
+ }: Props) => {
40
+ const ct = useCameraTransform()
41
+ const pathBounds = getSVGPathBounds(paths.map((p) => p.d))
42
+ // Margin in SVG Space
43
+
44
+ return (
45
+ <svg
46
+ style={{
47
+ position: "absolute",
48
+ left: 0,
49
+ top: 0,
50
+ right: 0,
51
+ bottom: 0,
52
+ // backgroundColor: hovering ? "rgba(0, 0, 255, 0.5)" : "transparent",
53
+ pointerEvents: "none",
54
+ zIndex,
55
+ // overflow: "hidden",
56
+ // backgroundColor: badRatio ? "rgba(255, 0, 0, 0.1)" : "transparent",
57
+ // backgroundColor: "rgba(255, 0, 0, 0.1)",
58
+ }}
59
+ overflow="visible"
60
+ >
61
+ {paths.map((p, i) => (
62
+ <path
63
+ key={i}
64
+ transform={toSVG(ct)}
65
+ fill={p.fill ?? "none"}
66
+ strokeLinecap="round"
67
+ strokeWidth={1.5 * (p.strokeWidth || 1)}
68
+ stroke={p.stroke || "red"}
69
+ d={p.d}
70
+ />
71
+ ))}
72
+ </svg>
73
+ )
74
+ }
75
+
76
+ export default SVGPathComponent2
@@ -1,4 +1,4 @@
1
- import { AnyElement } from "@tscircuit/builder"
1
+ import { AnySoupElement } from "@tscircuit/soup"
2
2
  import { collectElementRefs } from "lib/utils/collect-element-refs"
3
3
  import SchematicComponent from "./SchematicComponent"
4
4
  import SchematicPort from "./SchematicPort"
@@ -8,6 +8,7 @@ import SchematicTrace from "./SchematicTrace"
8
8
  import SchematicLine from "./SchematicLine"
9
9
  import RenderError from "./RenderError"
10
10
  import SchematicPath from "./SchematicPath"
11
+ import { SchematicNetLabel } from "./SchematicNetLabel"
11
12
 
12
13
  /**
13
14
  * Render any @tsbuilder/builder AnyElement that can be put on a schematic.
@@ -16,8 +17,8 @@ export const SchematicElement = ({
16
17
  element,
17
18
  allElements,
18
19
  }: {
19
- element: AnyElement
20
- allElements: AnyElement[]
20
+ element: AnySoupElement
21
+ allElements: AnySoupElement[]
21
22
  }) => {
22
23
  // A lot of the split logic for element types into a project is here:
23
24
  // https://github.com/tscircuit/builder/blob/7e7bef9c0aadd11999795003b8986f0d244c111f/src/lib/project/create-project-from-elements.ts#L13
@@ -63,6 +64,10 @@ export const SchematicElement = ({
63
64
  return <SchematicText schematic_text={element} />
64
65
  }
65
66
 
67
+ if (element.type === "schematic_net_label") {
68
+ return <SchematicNetLabel net_label={element} />
69
+ }
70
+
66
71
  if (element.type === "source_error") {
67
72
  // TODO use the ids on the source error to put this in the right place
68
73
  return <RenderError text={element.message} />
@@ -22,8 +22,8 @@ export const SchematicLine = ({ line: { schematic } }: Props) => {
22
22
  path.lineTo(x2, y2)
23
23
  const d = path.toString()
24
24
  const pathBounds = getSVGPathBounds(d)
25
- pathBounds.height = Math.max(pathBounds.height, 1)
26
- pathBounds.width = Math.max(pathBounds.width, 1)
25
+ // pathBounds.height = Math.max(pathBounds.height, 1)
26
+ // pathBounds.width = Math.max(pathBounds.width, 1)
27
27
  const center = {
28
28
  x: pathBounds.minX + pathBounds.width / 2,
29
29
  y: pathBounds.minY + pathBounds.height / 2,
@@ -0,0 +1,47 @@
1
+ import type { SchematicNetLabel as SchematicNetLabelObject } from "@tscircuit/soup"
2
+ import SVGPathComponent from "./SVGPathComponent"
3
+ import SchematicText from "./SchematicText"
4
+
5
+ export const SchematicNetLabel = ({
6
+ net_label,
7
+ }: {
8
+ net_label: SchematicNetLabelObject
9
+ }) => {
10
+ console.log({ net_label })
11
+ const text_width = net_label.text.length * 0.15
12
+ // TODO add text
13
+ const path_width = 31 + net_label.text.length * 5
14
+ return (
15
+ <>
16
+ <SVGPathComponent
17
+ rotation={0}
18
+ center={net_label.center}
19
+ // fixed size?
20
+ size={{
21
+ width: 0.05 + text_width,
22
+ height: 0.175,
23
+ }}
24
+ paths={[
25
+ {
26
+ stroke: "gray",
27
+ strokeWidth: 0.75,
28
+ d: `M 0 15 L 5 15 L 11 9 L ${path_width} 9 L ${path_width} 21 L 11 21 L 5 15`,
29
+ },
30
+ ]}
31
+ />
32
+ <SchematicText
33
+ schematic_text={{
34
+ anchor: "left",
35
+ position: {
36
+ x: net_label.center.x - text_width / 4 + 0.025,
37
+ y: net_label.center.y,
38
+ },
39
+ schematic_component_id: "SYNTHETIC",
40
+ schematic_text_id: "SYNTHETIC",
41
+ text: net_label.text,
42
+ type: "schematic_text",
43
+ }}
44
+ />
45
+ </>
46
+ )
47
+ }
@@ -3,6 +3,7 @@ import SVGPathComponent from "./SVGPathComponent"
3
3
  import Path from "svg-path-generator"
4
4
  import getSVGPathBounds from "lib/utils/get-svg-path-bounds"
5
5
  import RenderError from "./RenderError"
6
+ import SVGPathComponent2 from "./SVGPathComponent2"
6
7
 
7
8
  interface Props {
8
9
  trace: {
@@ -32,7 +33,7 @@ export const SchematicTrace = ({ trace: { source, schematic } }: Props) => {
32
33
  y: pathBounds.minY + pathBounds.height / 2,
33
34
  }
34
35
  return (
35
- <SVGPathComponent
36
+ <SVGPathComponent2
36
37
  rotation={0}
37
38
  center={center}
38
39
  size={pathBounds}
@@ -23,6 +23,18 @@ export const SimplePowerSource = ({
23
23
  strokeWidth: 1,
24
24
  d: "M 0 -17 L 0 -3 M -8 3 L 8 3 M 0 17 L 0 3 M -12 -3 L 12 -3",
25
25
  },
26
+ // positive symbol
27
+ {
28
+ stroke: "red",
29
+ strokeWidth: 0.5,
30
+ d: "M 8 -9 L 8 -6 M 9.5 -7.5 L 6.5 -7.5",
31
+ },
32
+ // negative symbol
33
+ {
34
+ stroke: "red",
35
+ strokeWidth: 0.5,
36
+ d: "M 9.5 7.5 L 6.5 7.5",
37
+ },
26
38
  ]}
27
39
  />
28
40
  )
@@ -0,0 +1,171 @@
1
+ import { Schematic } from "../../Schematic"
2
+
3
+ export const SchematicNetLabel = () => {
4
+ return (
5
+ <Schematic
6
+ style={{ height: 500 }}
7
+ soup={
8
+ {
9
+ elements: [
10
+ {
11
+ type: "source_component",
12
+ source_component_id: "simple_resistor_0",
13
+ name: "R1",
14
+ supplier_part_numbers: {},
15
+ ftype: "simple_resistor",
16
+ resistance: 100,
17
+ source: {
18
+ type: "source_component",
19
+ source_component_id: "simple_resistor_0",
20
+ name: "R1",
21
+ supplier_part_numbers: {},
22
+ ftype: "simple_resistor",
23
+ resistance: 100,
24
+ },
25
+ },
26
+ {
27
+ type: "schematic_component",
28
+ source_component_id: "simple_resistor_0",
29
+ schematic_component_id: "schematic_component_simple_resistor_0",
30
+ rotation: 0,
31
+ size: {
32
+ width: 1,
33
+ height: 0.3,
34
+ },
35
+ center: {
36
+ x: 0,
37
+ y: 0,
38
+ },
39
+ source: {
40
+ type: "source_component",
41
+ source_component_id: "simple_resistor_0",
42
+ name: "R1",
43
+ supplier_part_numbers: {},
44
+ ftype: "simple_resistor",
45
+ resistance: 100,
46
+ },
47
+ },
48
+ {
49
+ type: "source_port",
50
+ name: "left",
51
+ source_port_id: "source_port_0",
52
+ source_component_id: "simple_resistor_0",
53
+ source: {
54
+ type: "source_component",
55
+ source_component_id: "simple_resistor_0",
56
+ name: "R1",
57
+ supplier_part_numbers: {},
58
+ ftype: "simple_resistor",
59
+ resistance: 100,
60
+ },
61
+ },
62
+ {
63
+ type: "schematic_port",
64
+ schematic_port_id: "schematic_port_0",
65
+ source_port_id: "source_port_0",
66
+ center: {
67
+ x: -0.5,
68
+ y: 0,
69
+ },
70
+ facing_direction: "left",
71
+ schematic_component_id: "schematic_component_simple_resistor_0",
72
+ source: {
73
+ type: "source_port",
74
+ name: "left",
75
+ source_port_id: "source_port_0",
76
+ source_component_id: "simple_resistor_0",
77
+ },
78
+ },
79
+ {
80
+ type: "source_port",
81
+ name: "right",
82
+ source_port_id: "source_port_1",
83
+ source_component_id: "simple_resistor_0",
84
+ source: {
85
+ type: "source_component",
86
+ source_component_id: "simple_resistor_0",
87
+ name: "R1",
88
+ supplier_part_numbers: {},
89
+ ftype: "simple_resistor",
90
+ resistance: 100,
91
+ },
92
+ },
93
+ {
94
+ type: "schematic_port",
95
+ schematic_port_id: "schematic_port_1",
96
+ source_port_id: "source_port_1",
97
+ center: {
98
+ x: 0.5,
99
+ y: 0,
100
+ },
101
+ facing_direction: "right",
102
+ schematic_component_id: "schematic_component_simple_resistor_0",
103
+ source: {
104
+ type: "source_port",
105
+ name: "right",
106
+ source_port_id: "source_port_1",
107
+ source_component_id: "simple_resistor_0",
108
+ },
109
+ },
110
+ {
111
+ type: "schematic_text",
112
+ text: "R1",
113
+ schematic_text_id: "schematic_text_0",
114
+ schematic_component_id: "schematic_component_simple_resistor_0",
115
+ anchor: "left",
116
+ position: {
117
+ x: -0.2,
118
+ y: -0.5,
119
+ },
120
+ rotation: 0,
121
+ source: null,
122
+ },
123
+ {
124
+ type: "schematic_text",
125
+ text: 100,
126
+ schematic_text_id: "schematic_text_1",
127
+ schematic_component_id: "schematic_component_simple_resistor_0",
128
+ anchor: "left",
129
+ position: {
130
+ x: -0.2,
131
+ y: -0.3,
132
+ },
133
+ rotation: 0,
134
+ source: null,
135
+ },
136
+ {
137
+ type: "source_net",
138
+ member_source_group_ids: [],
139
+ source_net_id: "net_0",
140
+ name: "GND",
141
+ source: null,
142
+ },
143
+ {
144
+ type: "source_trace",
145
+ connected_source_port_ids: ["source_port_1"],
146
+ connected_source_net_ids: ["net_0"],
147
+ source_trace_id: "source_trace_0",
148
+ source: null,
149
+ },
150
+ {
151
+ type: "schematic_net_label",
152
+ source_net_id: "net_0",
153
+ text: "GND",
154
+ anchor_side: "left",
155
+ center: {
156
+ x: 1.5,
157
+ y: 0,
158
+ },
159
+ source: null,
160
+ },
161
+ ],
162
+ }.elements
163
+ }
164
+ />
165
+ )
166
+ }
167
+
168
+ export default {
169
+ title: "Basics/SchematicNetLabel",
170
+ component: SchematicNetLabel,
171
+ }
@@ -0,0 +1,18 @@
1
+ import { useResistor } from "@tscircuit/react-fiber"
2
+ import { Schematic } from "../../Schematic"
3
+
4
+ export const Bug4SchematicLine = () => {
5
+ return (
6
+ <Schematic style={{ height: 500 }}>
7
+ {/* <resistor name="R1" resistance="10" schX={2} schY={1} /> */}
8
+ <component name="K1" schX={0} schY={0}>
9
+ <schematicline x1={0} y1={0} x2={0} y2={2} />
10
+ </component>
11
+ </Schematic>
12
+ )
13
+ }
14
+
15
+ export default {
16
+ title: "Bugs/Bug4SchematicLine",
17
+ component: Bug4SchematicLine,
18
+ }
@@ -0,0 +1,15 @@
1
+ import { useResistor } from "@tscircuit/react-fiber"
2
+ import { Schematic } from "../../Schematic"
3
+
4
+ export const Bug5SchematicLine = () => {
5
+ return (
6
+ <Schematic style={{ height: 500 }}>
7
+ <diode name="D1" />
8
+ </Schematic>
9
+ )
10
+ }
11
+
12
+ export default {
13
+ title: "Bugs/Bug5SchematicLine",
14
+ component: Bug5SchematicLine,
15
+ }
@@ -0,0 +1,54 @@
1
+ import { Schematic } from "../../Schematic"
2
+
3
+ export const Bug6TraceScaling = () => {
4
+ return (
5
+ <Schematic style={{ height: 500 }}>
6
+ <resistor name="R1" resistance="10 ohm" schX={2} schY={1} />
7
+ {/* <powersource voltage="5V" schX={1} schY={2} name="main_power" /> */}
8
+ <resistor resistance="1k" schX={1} schY={2} name="main_power" />
9
+ {/* <trace path={[".main_power > port.negative", ".R1 > port.left"]} /> */}
10
+ <trace path={[".main_power > port.right", ".R1 > port.left"]} />
11
+ </Schematic>
12
+ // <Schematic
13
+ // style={{ height: 500 }}
14
+ // soup={[
15
+ // {
16
+ // type: "source_trace",
17
+ // source_trace_id: "source_trace_0",
18
+ // },
19
+ // {
20
+ // type: "schematic_trace",
21
+ // source_trace_id: "source_trace_0",
22
+ // schematic_trace_id: "schematic_trace_0",
23
+ // edges: [
24
+ // {
25
+ // from: {
26
+ // x: 1,
27
+ // y: 1.4000000000000001,
28
+ // },
29
+ // to: {
30
+ // x: 1,
31
+ // y: 1,
32
+ // },
33
+ // },
34
+ // {
35
+ // from: {
36
+ // x: 1,
37
+ // y: 1,
38
+ // },
39
+ // to: {
40
+ // x: 1.4000000000000001,
41
+ // y: 1,
42
+ // },
43
+ // },
44
+ // ],
45
+ // },
46
+ // ]}
47
+ // />
48
+ )
49
+ }
50
+
51
+ export default {
52
+ title: "Bugs/Bug6TraceScaling",
53
+ component: Bug6TraceScaling,
54
+ }