@tscircuit/schematic-viewer 1.2.14 → 1.4.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.
Files changed (54) hide show
  1. package/.github/workflows/chromatic.yml +30 -0
  2. package/.github/workflows/npm-build.yml +26 -0
  3. package/.github/workflows/npm-typecheck.yml +26 -0
  4. package/README.md +1 -1
  5. package/dist/index.d.ts +6 -7
  6. package/dist/index.js +690 -664
  7. package/dist/index.js.map +1 -1
  8. package/package.json +21 -12
  9. package/renovate.json +12 -1
  10. package/src/Schematic.tsx +148 -77
  11. package/src/lib/types/core.ts +14 -49
  12. package/src/lib/types/source-component.ts +6 -0
  13. package/src/lib/utils/collect-element-refs.ts +4 -3
  14. package/src/lib/utils/colors.ts +236 -0
  15. package/src/schematic-components/SVGPathComponent.tsx +84 -144
  16. package/src/schematic-components/SchematicChip.tsx +183 -0
  17. package/src/schematic-components/SchematicComponent.tsx +18 -24
  18. package/src/schematic-components/SchematicComponentFromSymbol.tsx +44 -0
  19. package/src/schematic-components/SchematicElement.tsx +4 -38
  20. package/src/schematic-components/SchematicTrace.tsx +4 -3
  21. package/src/schematic-components/index.tsx +7 -14
  22. package/src/stories/basics/schematic-net-label.stories.tsx +112 -166
  23. package/src/stories/basics/schematic-net-labels-2.stories.tsx +22 -20
  24. package/src/stories/bug-connections.stories.tsx +9 -6
  25. package/src/stories/bug-high-port-numbers.stories.tsx +99 -82
  26. package/src/stories/bug-pin-spacing.stories.tsx +1 -0
  27. package/src/stories/bugs/bug1-y-flip.stories.tsx +3 -2
  28. package/src/stories/bugs/bug3-scaling-trace.stories.tsx +11 -5
  29. package/src/stories/bugs/bug4-schematic-line.stories.tsx +0 -1
  30. package/src/stories/bugs/bug5-diode.stories.tsx +0 -1
  31. package/src/stories/bugs/bug8-autolayout.stories.tsx +22 -31
  32. package/src/stories/circuit-components/diode.stories.tsx +3 -1
  33. package/src/stories/circuit-components/resistor.stories.tsx +3 -1
  34. package/src/stories/component-drawing-example.stories.tsx +2 -2
  35. package/src/stories/led-circuit-react.stories.tsx +40 -45
  36. package/src/stories/net-alias.stories.tsx +1 -1
  37. package/src/stories/off-center-render.stories.tsx +6 -6
  38. package/src/stories/rotated-resistor.stories.tsx +1 -1
  39. package/src/stories/schematic-path.stories.tsx +1 -1
  40. package/src/stories/three-sided-bug.stories.tsx +8 -8
  41. package/src/pages/led-circuit.tsx +0 -96
  42. package/src/schematic-components/ProjectComponent.tsx +0 -70
  43. package/src/schematic-components/SchematicBox.tsx +0 -29
  44. package/src/schematic-components/SchematicBug.tsx +0 -90
  45. package/src/schematic-components/SchematicLine.tsx +0 -48
  46. package/src/schematic-components/SchematicPath.tsx +0 -51
  47. package/src/schematic-components/SchematicPort.tsx +0 -63
  48. package/src/schematic-components/SimpleCapacitor.tsx +0 -29
  49. package/src/schematic-components/SimpleDiode.tsx +0 -42
  50. package/src/schematic-components/SimpleGround.tsx +0 -30
  51. package/src/schematic-components/SimpleInductor.tsx +0 -29
  52. package/src/schematic-components/SimplePowerSource.tsx +0 -43
  53. package/src/schematic-components/SimpleResistor.tsx +0 -28
  54. package/src/stories/led-circuit-builder.stories.tsx +0 -104
@@ -0,0 +1,236 @@
1
+ // Kicad-2020 color scheme
2
+ export const colorMap = {
3
+ "3d_viewer": {
4
+ background_bottom: "rgb(102, 102, 128)",
5
+ background_top: "rgb(204, 204, 230)",
6
+ board: "rgb(51, 43, 23)",
7
+ copper: "rgb(179, 156, 0)",
8
+ silkscreen_bottom: "rgb(230, 230, 230)",
9
+ silkscreen_top: "rgb(230, 230, 230)",
10
+ soldermask: "rgb(20, 51, 36)",
11
+ solderpaste: "rgb(128, 128, 128)",
12
+ },
13
+ board: {
14
+ anchor: "rgb(255, 38, 226)",
15
+ aux_items: "rgb(255, 255, 255)",
16
+ b_adhes: "rgb(0, 0, 132)",
17
+ b_crtyd: "rgb(255, 38, 226)",
18
+ b_fab: "rgb(88, 93, 132)",
19
+ b_mask: "rgba(2, 255, 238, 0.400)",
20
+ b_paste: "rgb(0, 194, 194)",
21
+ b_silks: "rgb(232, 178, 167)",
22
+ background: "rgb(0, 16, 35)",
23
+ cmts_user: "rgb(89, 148, 220)",
24
+ copper: {
25
+ b: "rgb(77, 127, 196)",
26
+ f: "rgb(200, 52, 52)",
27
+ in1: "rgb(127, 200, 127)",
28
+ in10: "rgb(237, 124, 51)",
29
+ in11: "rgb(91, 195, 235)",
30
+ in12: "rgb(247, 111, 142)",
31
+ in13: "rgb(167, 165, 198)",
32
+ in14: "rgb(40, 204, 217)",
33
+ in15: "rgb(232, 178, 167)",
34
+ in16: "rgb(242, 237, 161)",
35
+ in17: "rgb(237, 124, 51)",
36
+ in18: "rgb(91, 195, 235)",
37
+ in19: "rgb(247, 111, 142)",
38
+ in2: "rgb(206, 125, 44)",
39
+ in20: "rgb(167, 165, 198)",
40
+ in21: "rgb(40, 204, 217)",
41
+ in22: "rgb(232, 178, 167)",
42
+ in23: "rgb(242, 237, 161)",
43
+ in24: "rgb(237, 124, 51)",
44
+ in25: "rgb(91, 195, 235)",
45
+ in26: "rgb(247, 111, 142)",
46
+ in27: "rgb(167, 165, 198)",
47
+ in28: "rgb(40, 204, 217)",
48
+ in29: "rgb(232, 178, 167)",
49
+ in3: "rgb(79, 203, 203)",
50
+ in30: "rgb(242, 237, 161)",
51
+ in4: "rgb(219, 98, 139)",
52
+ in5: "rgb(167, 165, 198)",
53
+ in6: "rgb(40, 204, 217)",
54
+ in7: "rgb(232, 178, 167)",
55
+ in8: "rgb(242, 237, 161)",
56
+ in9: "rgb(141, 203, 129)",
57
+ },
58
+ cursor: "rgb(255, 255, 255)",
59
+ drc: "rgb(194, 194, 194)",
60
+ drc_error: "rgba(215, 91, 107, 0.800)",
61
+ drc_exclusion: "rgb(255, 255, 255)",
62
+ drc_warning: "rgba(255, 208, 66, 0.902)",
63
+ dwgs_user: "rgb(194, 194, 194)",
64
+ eco1_user: "rgb(180, 219, 210)",
65
+ eco2_user: "rgb(216, 200, 82)",
66
+ edge_cuts: "rgb(208, 210, 205)",
67
+ f_adhes: "rgb(132, 0, 132)",
68
+ f_crtyd: "rgb(255, 0, 245)",
69
+ f_fab: "rgb(175, 175, 175)",
70
+ f_mask: "rgba(216, 100, 255, 0.400)",
71
+ f_paste: "rgba(180, 160, 154, 0.902)",
72
+ f_silks: "rgb(242, 237, 161)",
73
+ footprint_text_back: "rgb(0, 0, 132)",
74
+ footprint_text_front: "rgb(194, 194, 194)",
75
+ footprint_text_invisible: "rgb(132, 132, 132)",
76
+ grid: "rgb(132, 132, 132)",
77
+ grid_axes: "rgb(194, 194, 194)",
78
+ margin: "rgb(255, 38, 226)",
79
+ microvia: "rgb(0, 132, 132)",
80
+ no_connect: "rgb(0, 0, 132)",
81
+ pad_back: "rgb(77, 127, 196)",
82
+ pad_front: "rgb(200, 52, 52)",
83
+ pad_plated_hole: "rgb(194, 194, 0)",
84
+ pad_through_hole: "rgb(227, 183, 46)",
85
+ plated_hole: "rgb(26, 196, 210)",
86
+ ratsnest: "rgba(245, 255, 213, 0.702)",
87
+ select_overlay: "rgb(4, 255, 67)",
88
+ through_via: "rgb(236, 236, 236)",
89
+ user_1: "rgb(194, 194, 194)",
90
+ user_2: "rgb(89, 148, 220)",
91
+ user_3: "rgb(180, 219, 210)",
92
+ user_4: "rgb(216, 200, 82)",
93
+ user_5: "rgb(194, 194, 194)",
94
+ user_6: "rgb(89, 148, 220)",
95
+ user_7: "rgb(180, 219, 210)",
96
+ user_8: "rgb(216, 200, 82)",
97
+ user_9: "rgb(232, 178, 167)",
98
+ via_blind_buried: "rgb(187, 151, 38)",
99
+ via_hole: "rgb(227, 183, 46)",
100
+ via_micro: "rgb(0, 132, 132)",
101
+ via_through: "rgb(236, 236, 236)",
102
+ worksheet: "rgb(200, 114, 171)",
103
+ },
104
+ gerbview: {
105
+ axes: "rgb(0, 0, 132)",
106
+ background: "rgb(0, 0, 0)",
107
+ dcodes: "rgb(255, 255, 255)",
108
+ grid: "rgb(132, 132, 132)",
109
+ layers: [
110
+ "rgb(132, 0, 0)",
111
+ "rgb(194, 194, 0)",
112
+ "rgb(194, 0, 194)",
113
+ "rgb(194, 0, 0)",
114
+ "rgb(0, 132, 132)",
115
+ "rgb(0, 132, 0)",
116
+ "rgb(0, 0, 132)",
117
+ "rgb(132, 132, 132)",
118
+ "rgb(132, 0, 132)",
119
+ "rgb(194, 194, 194)",
120
+ "rgb(132, 0, 132)",
121
+ "rgb(132, 0, 0)",
122
+ "rgb(132, 132, 0)",
123
+ "rgb(194, 194, 194)",
124
+ "rgb(0, 0, 132)",
125
+ "rgb(0, 132, 0)",
126
+ "rgb(132, 0, 0)",
127
+ "rgb(194, 194, 0)",
128
+ "rgb(194, 0, 194)",
129
+ "rgb(194, 0, 0)",
130
+ "rgb(0, 132, 132)",
131
+ "rgb(0, 132, 0)",
132
+ "rgb(0, 0, 132)",
133
+ "rgb(132, 132, 132)",
134
+ "rgb(132, 0, 132)",
135
+ "rgb(194, 194, 194)",
136
+ "rgb(132, 0, 132)",
137
+ "rgb(132, 0, 0)",
138
+ "rgb(132, 132, 0)",
139
+ "rgb(194, 194, 194)",
140
+ "rgb(0, 0, 132)",
141
+ "rgb(0, 132, 0)",
142
+ "rgb(132, 0, 0)",
143
+ "rgb(194, 194, 0)",
144
+ "rgb(194, 0, 194)",
145
+ "rgb(194, 0, 0)",
146
+ "rgb(0, 132, 132)",
147
+ "rgb(0, 132, 0)",
148
+ "rgb(0, 0, 132)",
149
+ "rgb(132, 132, 132)",
150
+ "rgb(132, 0, 132)",
151
+ "rgb(194, 194, 194)",
152
+ "rgb(132, 0, 132)",
153
+ "rgb(132, 0, 0)",
154
+ "rgb(132, 132, 0)",
155
+ "rgb(194, 194, 194)",
156
+ "rgb(0, 0, 132)",
157
+ "rgb(0, 132, 0)",
158
+ "rgb(132, 0, 0)",
159
+ "rgb(194, 194, 0)",
160
+ "rgb(194, 0, 194)",
161
+ "rgb(194, 0, 0)",
162
+ "rgb(0, 132, 132)",
163
+ "rgb(0, 132, 0)",
164
+ "rgb(0, 0, 132)",
165
+ "rgb(132, 132, 132)",
166
+ "rgb(132, 0, 132)",
167
+ "rgb(194, 194, 194)",
168
+ "rgb(132, 0, 132)",
169
+ "rgb(132, 0, 0)",
170
+ ],
171
+ negative_objects: "rgb(132, 132, 132)",
172
+ worksheet: "rgb(0, 0, 132)",
173
+ },
174
+ meta: {
175
+ filename: "kicad_2020",
176
+ name: "KiCad 2020",
177
+ version: 2,
178
+ },
179
+ palette: [
180
+ "rgb(132, 0, 0)",
181
+ "rgb(194, 194, 0)",
182
+ "rgb(194, 0, 194)",
183
+ "rgb(194, 0, 0)",
184
+ "rgb(0, 132, 132)",
185
+ "rgb(0, 132, 0)",
186
+ "rgb(0, 0, 132)",
187
+ "rgb(132, 132, 132)",
188
+ "rgb(132, 0, 132)",
189
+ "rgb(194, 194, 194)",
190
+ "rgb(132, 0, 132)",
191
+ "rgb(132, 0, 0)",
192
+ "rgb(132, 132, 0)",
193
+ "rgb(194, 194, 194)",
194
+ "rgb(0, 0, 132)",
195
+ "rgb(0, 132, 0)",
196
+ ],
197
+ schematic: {
198
+ aux_items: "rgb(46, 46, 46)",
199
+ background: "rgb(245, 241, 237)",
200
+ brightened: "rgb(255, 0, 255)",
201
+ bus: "rgb(0, 0, 132)",
202
+ bus_junction: "rgb(0, 0, 132)",
203
+ component_body: "rgb(255, 255, 194)",
204
+ component_outline: "rgb(132, 0, 0)",
205
+ cursor: "rgb(15, 15, 15)",
206
+ erc_error: "rgba(230, 9, 13, 0.800)",
207
+ erc_warning: "rgba(209, 146, 0, 0.800)",
208
+ fields: "rgb(132, 0, 132)",
209
+ grid: "rgb(181, 181, 181)",
210
+ grid_axes: "rgb(0, 0, 132)",
211
+ hidden: "rgb(194, 194, 194)",
212
+ junction: "rgb(0, 150, 0)",
213
+ label_global: "rgb(132, 0, 0)",
214
+ label_hier: "rgb(114, 86, 0)",
215
+ label_local: "rgb(15, 15, 15)",
216
+ net_name: "rgb(132, 132, 132)",
217
+ no_connect: "rgb(0, 0, 132)",
218
+ note: "rgb(0, 0, 194)",
219
+ override_item_colors: false,
220
+ pin: "rgb(132, 0, 0)",
221
+ pin_name: "rgb(0, 100, 100)",
222
+ pin_number: "rgb(169, 0, 0)",
223
+ reference: "rgb(0, 100, 100)",
224
+ shadow: "rgba(102, 179, 255, 0.800)",
225
+ sheet: "rgb(132, 0, 0)",
226
+ sheet_background: "rgba(253, 255, 231, 0.000)",
227
+ sheet_fields: "rgb(132, 0, 132)",
228
+ sheet_filename: "rgb(114, 86, 0)",
229
+ sheet_label: "rgb(0, 100, 100)",
230
+ sheet_name: "rgb(0, 100, 100)",
231
+ value: "rgb(0, 100, 100)",
232
+ wire: "rgb(0, 150, 0)",
233
+ worksheet: "rgb(132, 0, 0)",
234
+ },
235
+ }
236
+
@@ -1,30 +1,37 @@
1
- import { useGlobalStore } from "lib/render-context"
2
- import getSVGPathBounds from "lib/utils/get-svg-path-bounds"
3
- import { useCallback, useState } from "react"
1
+ import { useGlobalStore } from "lib/render-context";
2
+ import getSVGPathBounds from "lib/utils/get-svg-path-bounds";
3
+ import { useState } from "react";
4
+ import { applyToPoint, compose, scale, toSVG, translate } from "transformation-matrix";
4
5
 
5
- import {
6
- applyToPoint,
7
- toSVG,
8
- inverse,
9
- compose,
10
- translate,
11
- scale,
12
- } from "transformation-matrix"
6
+ interface PathProps {
7
+ type?: 'path';
8
+ strokeWidth: number;
9
+ stroke: string;
10
+ fill?: string;
11
+ d: string;
12
+ }
13
+
14
+ interface CircleProps {
15
+ type: 'circle';
16
+ cx: number;
17
+ cy: number;
18
+ r: number;
19
+ strokeWidth: number;
20
+ stroke: string;
21
+ fill?: string;
22
+ }
23
+
24
+ export type SVGElement = PathProps | CircleProps;
13
25
 
14
26
  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
27
+ rotation: number;
28
+ center: { x: number; y: number };
29
+ size: { width: number; height: number };
30
+ invertY?: boolean;
31
+ shiftToBottom?: boolean;
32
+ paths: SVGElement[];
33
+ zIndex?: number;
34
+ hoverContent?: any;
28
35
  }
29
36
 
30
37
  export const SVGPathComponent = ({
@@ -37,136 +44,69 @@ export const SVGPathComponent = ({
37
44
  shiftToBottom,
38
45
  hoverContent,
39
46
  }: Props) => {
40
- const ct = useGlobalStore((s) => s.camera_transform)
41
- const pathBounds = getSVGPathBounds(paths.map((p) => p.d))
42
- // Margin in SVG Space
43
- const badRatio =
44
- Math.abs(pathBounds.width / pathBounds.height - size.width / size.height) >
45
- 0.01
46
- if (badRatio) {
47
- // console.warn(
48
- // `Ratio doesn't match for component. ${pathBounds.width}:${pathBounds.height} is not close to ${size.width}:${size.height}`
49
- // )
50
- }
51
- // pathBounds.height = Math.max(pathBounds.height, 0.01)
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
-
64
- const absoluteCenter = applyToPoint(ct, center)
47
+ const ct = useGlobalStore((s) => s.camera_transform);
48
+ const pathBounds = getSVGPathBounds(paths.filter((p): p is PathProps => p.type !== 'circle').map(p => p.d));
65
49
 
50
+ const padding = { x: 0, y: 0 };
51
+ const absoluteCenter = applyToPoint(ct, center);
66
52
  const innerSize = {
67
53
  width: size.width * ct.a,
68
54
  height: size.height * Math.abs(ct.d),
69
- }
70
-
55
+ };
71
56
  const fullSize = {
72
57
  width: innerSize.width + padding.x * 2,
73
58
  height: innerSize.height + padding.y * 2,
74
- }
59
+ };
75
60
 
76
- const [hovering, setHovering] = useState(false)
61
+ const [hovering, setHovering] = useState(false);
77
62
 
78
- const svgLeft = absoluteCenter.x - fullSize.width / 2
79
- const svgTop = absoluteCenter.y - fullSize.height / 2
63
+ const svgLeft = absoluteCenter.x - fullSize.width / 2;
64
+ const svgTop = absoluteCenter.y - fullSize.height / 2;
80
65
 
81
- // const viewBox = `${pathBounds.minX} ${pathBounds.minY} ${pathBounds.width} ${pathBounds.height}`
82
- // const viewBox2 = `${svgLeft} ${svgTctualAbsWidth} ${actualAbsHeight}`
66
+ const preferredRatio = pathBounds.width === 0
67
+ ? innerSize.height / pathBounds.height
68
+ : innerSize.width / pathBounds.width;
83
69
 
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
94
70
  const svgToScreen = compose(
95
- // translate(0, 0)
96
71
  scale(
97
- pathBounds.width === 0
98
- ? preferredRatio
99
- : fullSize.width / pathBounds.width,
100
- pathBounds.height === 0
101
- ? preferredRatio
102
- : fullSize.height / pathBounds.height
72
+ pathBounds.width === 0 ? preferredRatio : fullSize.width / pathBounds.width,
73
+ pathBounds.height === 0 ? preferredRatio : fullSize.height / pathBounds.height,
103
74
  ),
104
- translate(-pathBounds.minX, -pathBounds.minY)
105
- // translate(center.x, center.y)
106
- )
107
- // console.log(svgToScreen)
108
- // console.log(toSVG(svgToScreen))
109
- // console.log(paths[0].d)
110
- // translate(..., ...),
75
+ translate(-pathBounds.minX, -pathBounds.minY),
76
+ );
111
77
 
112
78
  return (
113
- <>
114
- {hovering && (
115
- <>
116
- <div
117
- style={{
118
- position: "absolute",
119
- left: svgLeft - 6,
120
- top: svgTop - 6,
121
- pointerEvents: "none",
122
- width: fullSize.width + 12,
123
- height: fullSize.height + 12,
124
- border: "1px red solid",
125
- mixBlendMode: "difference",
126
- zIndex: 1000,
127
- }}
79
+ <svg
80
+ onMouseOver={() => setHovering(Boolean(hoverContent))}
81
+ onMouseOut={() => setHovering(false)}
82
+ style={{
83
+ position: "absolute",
84
+ cursor: hovering ? "pointer" : undefined,
85
+ zIndex,
86
+ transform: [
87
+ invertY ? "scale(1, 1)" : "scale(1, -1)",
88
+ shiftToBottom ? "translate(0, 100%)" : "",
89
+ rotation === 0 ? "" : `rotate(${rotation}deg)`,
90
+ ].join(" "),
91
+ left: svgLeft,
92
+ top: svgTop,
93
+ }}
94
+ overflow="visible"
95
+ width={fullSize.width}
96
+ height={fullSize.height}
97
+ >
98
+ {paths.map((p, i) =>
99
+ p.type === 'circle' ? (
100
+ <circle
101
+ key={i}
102
+ cx={p.cx}
103
+ cy={p.cy}
104
+ r={p.r}
105
+ fill={p.fill ?? "none"}
106
+ strokeWidth={1.5 * (p.strokeWidth || 1)}
107
+ stroke={p.stroke || "red"}
128
108
  />
129
- <div
130
- style={{
131
- position: "absolute",
132
- left: svgLeft + fullSize.width + 10,
133
- pointerEvents: "none",
134
- zIndex: 1000,
135
- color: "red",
136
- mixBlendMode: "difference",
137
- top: svgTop,
138
- fontFamily: "monospace",
139
- fontSize: 14,
140
- }}
141
- >
142
- {hoverContent}
143
- </div>
144
- </>
145
- )}
146
- <svg
147
- onMouseOver={() => setHovering(Boolean(hoverContent))}
148
- onMouseOut={() => setHovering(false)}
149
- style={{
150
- position: "absolute",
151
- // backgroundColor: hovering ? "rgba(0, 0, 255, 0.5)" : "transparent",
152
- cursor: hovering ? "pointer" : undefined,
153
- zIndex,
154
- transform: [
155
- invertY ? "scale(1, 1)" : "scale(1, -1)", // TODO based on ct.d
156
- shiftToBottom ? "translate(0, 100%)" : "",
157
- rotation === 0 ? "" : `rotate(${rotation}rad)`,
158
- ].join(" "),
159
- left: svgLeft,
160
- top: svgTop,
161
- // overflow: "hidden",
162
- // backgroundColor: badRatio ? "rgba(255, 0, 0, 0.1)" : "transparent",
163
- // backgroundColor: "rgba(255, 0, 0, 0.1)",
164
- }}
165
- overflow="visible"
166
- width={fullSize.width}
167
- height={fullSize.height}
168
- >
169
- {paths.map((p, i) => (
109
+ ) : (
170
110
  <path
171
111
  key={i}
172
112
  transform={toSVG(svgToScreen)}
@@ -174,12 +114,12 @@ export const SVGPathComponent = ({
174
114
  strokeLinecap="round"
175
115
  strokeWidth={1.5 * (p.strokeWidth || 1)}
176
116
  stroke={p.stroke || "red"}
177
- d={p.d}
117
+ d={p.d || ""}
178
118
  />
179
- ))}
180
- </svg>
181
- </>
182
- )
183
- }
119
+ )
120
+ )}
121
+ </svg>
122
+ );
123
+ };
184
124
 
185
- export default SVGPathComponent
125
+ export default SVGPathComponent;
@@ -0,0 +1,183 @@
1
+ import { AnyCircuitElement, SchematicPort as OriginalSchematicPort, SchematicComponent } from "circuit-json";
2
+ import * as Type from "lib/types";
3
+ import { colorMap } from "lib/utils/colors";
4
+ import React from 'react';
5
+ import SVGPathComponent from "./SVGPathComponent";
6
+ import SchematicText from "./SchematicText";
7
+
8
+ interface Props {
9
+ component: {
10
+ source: Type.SimpleBug;
11
+ schematic: SchematicComponent;
12
+ allElements: AnyCircuitElement[];
13
+ }
14
+ }
15
+
16
+ type ExtendedCenter = OriginalSchematicPort['center'] & {
17
+ side: "left" | "right" | "top" | "bottom";
18
+ pinNumber: number;
19
+ distanceFromEdge: number;
20
+ trueIndex: number;
21
+ };
22
+
23
+ type SchematicPort = Omit<OriginalSchematicPort, 'center'> & {
24
+ center: ExtendedCenter;
25
+ };
26
+
27
+ export const SchematicChip: React.FC<Props> = ({ component: { source, schematic, allElements } }) => {
28
+ const { center, size, rotation, schematic_component_id } = schematic;
29
+ const { manufacturerPartNumber, name } = source;
30
+ const chipWidth = size.width;
31
+ const chipHeight = size.height;
32
+
33
+ const paths: Array<{type?: 'path' | 'circle', strokeWidth: number, stroke: string, fill?: string, d?: string, cx?: number, cy?: number, r?: number}> = [];
34
+
35
+ // Main chip rectangle
36
+ paths.push({
37
+ type: 'path',
38
+ strokeWidth: 0.02,
39
+ stroke: colorMap.schematic.component_outline,
40
+ fill: colorMap.schematic.component_body,
41
+ d: `M ${-chipWidth / 2},${-chipHeight / 2} h ${chipWidth} v ${chipHeight} h ${-chipWidth} Z`,
42
+ });
43
+
44
+ const schematicPorts = allElements.filter(
45
+ (item): item is SchematicPort =>
46
+ item.type === "schematic_port" &&
47
+ item.schematic_component_id === schematic_component_id
48
+ );
49
+
50
+ const portLength = 0.2;
51
+ const circleRadius = 0.05;
52
+ const labelOffset = 0.1;
53
+
54
+ const pinLabels: Array<{x: number, y: number, text: string, anchor: string}> = [];
55
+
56
+ schematicPorts.forEach((port) => {
57
+ const { side, pinNumber, distanceFromEdge } = port.center;
58
+ let x = 0, y = 0, endX = 0, endY = 0;
59
+ let labelX = 0, labelY = 0;
60
+ let textAnchor = "middle";
61
+
62
+ switch (side) {
63
+ case "left":
64
+ x = -chipWidth / 2;
65
+ y = -chipHeight / 2 + distanceFromEdge;
66
+ endX = x - portLength;
67
+ endY = y;
68
+ labelX = endX;
69
+ labelY = y + labelOffset;
70
+ textAnchor = "end";
71
+ break;
72
+ case "right":
73
+ x = chipWidth / 2;
74
+ y = chipHeight / 2 - distanceFromEdge;
75
+ endX = x + portLength;
76
+ endY = y;
77
+ labelX = endX - labelOffset;
78
+ labelY = y + labelOffset;
79
+ textAnchor = "start";
80
+ break;
81
+ case "bottom":
82
+ x = -chipWidth / 2 + distanceFromEdge;
83
+ y = -chipHeight / 2;
84
+ endX = x;
85
+ endY = y - portLength;
86
+ labelX = x;
87
+ labelY = endY + labelOffset;
88
+ break;
89
+ case "top":
90
+ x = chipWidth / 2 - distanceFromEdge;
91
+ y = chipHeight / 2;
92
+ endX = x;
93
+ endY = y + portLength;
94
+ labelX = x;
95
+ labelY = endY + labelOffset;
96
+ break;
97
+ }
98
+
99
+ // Port line
100
+ paths.push({
101
+ type: 'path',
102
+ strokeWidth: 0.02,
103
+ stroke: colorMap.schematic.component_outline,
104
+ d: `M ${x},${y} L ${endX},${endY}`,
105
+ });
106
+
107
+ // Port circle at the end of the line
108
+ paths.push({
109
+ type: 'circle',
110
+ cx: endX,
111
+ cy: endY,
112
+ r: circleRadius,
113
+ strokeWidth: 0.01,
114
+ stroke: colorMap.schematic.component_outline,
115
+ fill: colorMap.schematic.component_outline,
116
+ });
117
+
118
+ // Add pin label
119
+ if(pinNumber !== undefined) {
120
+ pinLabels.push({
121
+ x: labelX,
122
+ y: labelY,
123
+ text: `${pinNumber}`,
124
+ anchor: textAnchor
125
+ });
126
+ }
127
+ });
128
+
129
+ return (
130
+ <>
131
+ <SVGPathComponent
132
+ rotation={rotation}
133
+ center={center}
134
+ size={size}
135
+ paths={paths as any}
136
+ />
137
+ {pinLabels.map((label, index) => (
138
+ <SchematicText
139
+ key={index}
140
+ schematic_text={{
141
+ anchor: label.anchor as any,
142
+ position: {
143
+ x: center.x + label.x,
144
+ y: center.y + label.y,
145
+ },
146
+ schematic_component_id: "SYNTHETIC",
147
+ schematic_text_id: `PIN_LABEL_${index}`,
148
+ text: label.text,
149
+ type: "schematic_text",
150
+ }}
151
+ />
152
+ ))}
153
+ <SchematicText
154
+ schematic_text={{
155
+ anchor: "center",
156
+ position: {
157
+ x: center.x,
158
+ y: center.y - chipHeight / 2 - 0.2,
159
+ },
160
+ schematic_component_id: "SYNTHETIC",
161
+ schematic_text_id: "SYNTHETIC_MPN",
162
+ text: manufacturerPartNumber,
163
+ type: "schematic_text",
164
+ }}
165
+ />
166
+ <SchematicText
167
+ schematic_text={{
168
+ anchor: "center",
169
+ position: {
170
+ x: center.x,
171
+ y: center.y + chipHeight / 2 + 0.2,
172
+ },
173
+ schematic_component_id: "SYNTHETIC",
174
+ schematic_text_id: "SYNTHETIC_NAME",
175
+ text: name,
176
+ type: "schematic_text",
177
+ }}
178
+ />
179
+ </>
180
+ );
181
+ };
182
+
183
+ export default SchematicChip;