circuit-json-to-tscircuit 0.0.86 → 0.0.87
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/{chunk-UZVNOAO4.js → chunk-DZP2ZWBQ.js} +191 -69
- package/dist/cli/main.js +2 -2
- package/dist/lib/index.js +1 -1
- package/lib/generate-board-schematic-sheet-tsx.ts +11 -6
- package/lib/generate-board-schematic-tsx/get-renderer-compatible-symbol-transform.ts +43 -0
- package/lib/generate-board-schematic-tsx/get-rigid-pin-identity-transform.ts +95 -0
- package/lib/generate-board-schematic-tsx/get-symbol-to-schematic-transform.ts +23 -90
- package/lib/generate-board-schematic-tsx/match-schematic-ports-to-symbol-ports.ts +79 -0
- package/lib/generate-board-schematic-tsx/match-symbol-ports-by-pin-identity.ts +40 -0
- package/package.json +1 -1
- package/tests/__snapshots__/test29-standalone-ti-schematic-roundtrip-schematic-comparison.snap.svg +1 -1
- package/tests/fixtures/create-ti-evm-roundtrip.ts +16 -7
- package/tests/fixtures/ti-evms/README.md +3 -2
- package/tests/fixtures/ti-evms/tmds62levm-sheet-13.circuit.json.gz +0 -0
- package/tests/ti-evms/__snapshots__/ti-drv8307evm-roundtrip-schematic-comparison.snap.svg +3 -3
- package/tests/ti-evms/__snapshots__/ti-drv8307evm-roundtrip.test.tsx.snap +1 -1
- package/tests/ti-evms/__snapshots__/ti-lm251772evm-pd-roundtrip-schematic-comparison.snap.svg +40 -40
- package/tests/ti-evms/__snapshots__/ti-lm251772evm-pd-roundtrip.test.tsx.snap +1 -1
- package/tests/ti-evms/__snapshots__/ti-lm5155evm-fly-roundtrip-schematic-comparison.snap.svg +4 -4
- package/tests/ti-evms/__snapshots__/ti-lmg342x-bb-evm-roundtrip-schematic-comparison.snap.svg +4 -4
- package/tests/ti-evms/__snapshots__/ti-lmg342x-bb-evm-roundtrip.test.tsx.snap +1 -1
- package/tests/ti-evms/__snapshots__/tmds62levm-sheet-13-mosfet-geometry.snap.svg +30 -0
- package/tests/ti-evms/ti-lm5155evm-fly-roundtrip.test.tsx +1 -1
- package/tests/ti-evms/tmds62levm-sheet-13-mosfet-geometry.test.tsx +107 -0
|
@@ -570,85 +570,134 @@ import { su as su3 } from "@tscircuit/soup-util";
|
|
|
570
570
|
import { symbols } from "schematic-symbols";
|
|
571
571
|
|
|
572
572
|
// lib/generate-board-schematic-tsx/get-symbol-to-schematic-transform.ts
|
|
573
|
+
import { fromTriangles as fromTriangles2 } from "transformation-matrix";
|
|
574
|
+
|
|
575
|
+
// lib/generate-board-schematic-tsx/get-renderer-compatible-symbol-transform.ts
|
|
576
|
+
import { compose as compose2, scale, translate as translate2 } from "transformation-matrix";
|
|
577
|
+
|
|
578
|
+
// lib/generate-board-schematic-tsx/match-schematic-ports-to-symbol-ports.ts
|
|
579
|
+
var getAngle = ({
|
|
580
|
+
center,
|
|
581
|
+
point
|
|
582
|
+
}) => Math.atan2(point.y - center.y, point.x - center.x);
|
|
583
|
+
var getAngularDifference = (first, second) => {
|
|
584
|
+
const directDifference = Math.abs(first - second);
|
|
585
|
+
return Math.min(directDifference, 2 * Math.PI - directDifference);
|
|
586
|
+
};
|
|
587
|
+
var matchSchematicPortsToSymbolPorts = ({
|
|
588
|
+
schematicComponent,
|
|
589
|
+
schematicPorts,
|
|
590
|
+
symbol
|
|
591
|
+
}) => {
|
|
592
|
+
const schematicPortsByAngle = schematicPorts.map((schematicPort) => ({
|
|
593
|
+
angle: getAngle({
|
|
594
|
+
center: schematicComponent.center,
|
|
595
|
+
point: schematicPort.center
|
|
596
|
+
}),
|
|
597
|
+
schematicPort
|
|
598
|
+
})).sort((first, second) => first.angle - second.angle);
|
|
599
|
+
const symbolPortsByAngle = symbol.ports.map((symbolPort) => ({
|
|
600
|
+
angle: getAngle({ center: symbol.center, point: symbolPort }),
|
|
601
|
+
symbolPort
|
|
602
|
+
})).sort((first, second) => first.angle - second.angle);
|
|
603
|
+
const usedSymbolPorts = /* @__PURE__ */ new Set();
|
|
604
|
+
const matches = [];
|
|
605
|
+
for (const schematicPortWithAngle of schematicPortsByAngle) {
|
|
606
|
+
const closestSymbolPort = symbolPortsByAngle.filter(({ symbolPort }) => !usedSymbolPorts.has(symbolPort)).map(({ angle, symbolPort }) => ({
|
|
607
|
+
angularDifference: getAngularDifference(
|
|
608
|
+
schematicPortWithAngle.angle,
|
|
609
|
+
angle
|
|
610
|
+
),
|
|
611
|
+
symbolPort
|
|
612
|
+
})).sort(
|
|
613
|
+
(first, second) => first.angularDifference - second.angularDifference
|
|
614
|
+
)[0];
|
|
615
|
+
if (!closestSymbolPort || closestSymbolPort.angularDifference >= Math.PI / 4)
|
|
616
|
+
continue;
|
|
617
|
+
usedSymbolPorts.add(closestSymbolPort.symbolPort);
|
|
618
|
+
matches.push({
|
|
619
|
+
schematicPort: schematicPortWithAngle.schematicPort,
|
|
620
|
+
symbolPort: closestSymbolPort.symbolPort
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
return matches;
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
// lib/generate-board-schematic-tsx/get-renderer-compatible-symbol-transform.ts
|
|
627
|
+
var getRendererCompatibleSymbolTransform = ({
|
|
628
|
+
schematicComponent,
|
|
629
|
+
schematicPorts,
|
|
630
|
+
symbol
|
|
631
|
+
}) => {
|
|
632
|
+
const matches = matchSchematicPortsToSymbolPorts({
|
|
633
|
+
schematicComponent,
|
|
634
|
+
schematicPorts,
|
|
635
|
+
symbol
|
|
636
|
+
});
|
|
637
|
+
const first = matches[0];
|
|
638
|
+
if (!first) return void 0;
|
|
639
|
+
const second = matches[1];
|
|
640
|
+
const firstSymbolPoint = second?.symbolPort ?? symbol.center;
|
|
641
|
+
const firstSchematicPoint = second?.schematicPort.center ?? schematicComponent.center;
|
|
642
|
+
const originalDistance = Math.hypot(
|
|
643
|
+
first.symbolPort.x - firstSymbolPoint.x,
|
|
644
|
+
first.symbolPort.y - firstSymbolPoint.y
|
|
645
|
+
);
|
|
646
|
+
if (originalDistance === 0) return void 0;
|
|
647
|
+
const renderedDistance = Math.hypot(
|
|
648
|
+
first.schematicPort.center.x - firstSchematicPoint.x,
|
|
649
|
+
first.schematicPort.center.y - firstSchematicPoint.y
|
|
650
|
+
);
|
|
651
|
+
const uniformScale = renderedDistance / originalDistance;
|
|
652
|
+
return compose2(
|
|
653
|
+
translate2(firstSchematicPoint.x, firstSchematicPoint.y),
|
|
654
|
+
scale(uniformScale),
|
|
655
|
+
translate2(-firstSymbolPoint.x, -firstSymbolPoint.y)
|
|
656
|
+
);
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
// lib/generate-board-schematic-tsx/get-rigid-pin-identity-transform.ts
|
|
573
660
|
import { applyToPoint as applyToPoint3, fromTriangles } from "transformation-matrix";
|
|
574
661
|
|
|
575
662
|
// lib/generate-board-schematic-tsx/get-port-pair-triangle.ts
|
|
576
663
|
import {
|
|
577
664
|
applyToPoint as applyToPoint2,
|
|
578
|
-
compose as
|
|
665
|
+
compose as compose3,
|
|
579
666
|
rotateDEG as rotateDEG2,
|
|
580
|
-
translate as
|
|
667
|
+
translate as translate3
|
|
581
668
|
} from "transformation-matrix";
|
|
582
669
|
var getPortPairTriangle = ({
|
|
583
670
|
first,
|
|
584
671
|
second
|
|
585
672
|
}) => {
|
|
586
|
-
const portToPerpendicularTransform =
|
|
587
|
-
|
|
673
|
+
const portToPerpendicularTransform = compose3(
|
|
674
|
+
translate3(first.x, first.y),
|
|
588
675
|
rotateDEG2(90),
|
|
589
|
-
|
|
676
|
+
translate3(-first.x, -first.y)
|
|
590
677
|
);
|
|
591
678
|
return [first, second, applyToPoint2(portToPerpendicularTransform, second)];
|
|
592
679
|
};
|
|
593
680
|
|
|
594
|
-
// lib/generate-board-schematic-tsx/get-
|
|
595
|
-
var
|
|
596
|
-
|
|
597
|
-
schematicComponent,
|
|
598
|
-
schematicPorts,
|
|
599
|
-
sourcePorts
|
|
600
|
-
}) => {
|
|
601
|
-
const matches = symbol.ports.flatMap((symbolPort) => {
|
|
602
|
-
const candidates = schematicPorts.filter((schematicPort) => {
|
|
603
|
-
const sourcePort = sourcePorts.find(
|
|
604
|
-
(port) => port.source_port_id === schematicPort.source_port_id
|
|
605
|
-
);
|
|
606
|
-
const pinNumber = schematicPort.pin_number ?? sourcePort?.pin_number;
|
|
607
|
-
const labels = [
|
|
608
|
-
sourcePort?.name,
|
|
609
|
-
...sourcePort?.port_hints ?? [],
|
|
610
|
-
...pinNumber === void 0 ? [] : [String(pinNumber), `pin${pinNumber}`]
|
|
611
|
-
];
|
|
612
|
-
return labels.some(
|
|
613
|
-
(label) => label !== void 0 && symbolPort.labels.includes(label)
|
|
614
|
-
);
|
|
615
|
-
});
|
|
616
|
-
return candidates.length === 1 ? [{ symbolPort, schematicPort: candidates[0] }] : [];
|
|
617
|
-
});
|
|
681
|
+
// lib/generate-board-schematic-tsx/get-rigid-pin-identity-transform.ts
|
|
682
|
+
var TRANSFORM_TOLERANCE = 1e-6;
|
|
683
|
+
var getRigidPinIdentityTransform = (matches) => {
|
|
618
684
|
const first = matches[0];
|
|
619
685
|
const second = matches.find(
|
|
620
686
|
(match) => match !== first && match.schematicPort !== first?.schematicPort
|
|
621
687
|
);
|
|
622
|
-
if (first
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
const transform = fromTriangles(
|
|
636
|
-
[first.symbolPort, second.symbolPort, third.symbolPort],
|
|
637
|
-
[
|
|
638
|
-
first.schematicPort.center,
|
|
639
|
-
second.schematicPort.center,
|
|
640
|
-
third.schematicPort.center
|
|
641
|
-
]
|
|
642
|
-
);
|
|
643
|
-
const fitsAllMatchedPorts = matches.every((match) => {
|
|
644
|
-
const transformedPort = applyToPoint3(transform, match.symbolPort);
|
|
645
|
-
return Math.hypot(
|
|
646
|
-
transformedPort.x - match.schematicPort.center.x,
|
|
647
|
-
transformedPort.y - match.schematicPort.center.y
|
|
648
|
-
) < 1e-6;
|
|
649
|
-
});
|
|
650
|
-
if (fitsAllMatchedPorts) return transform;
|
|
651
|
-
}
|
|
688
|
+
if (!first || !second) return void 0;
|
|
689
|
+
const third = matches.find(
|
|
690
|
+
(match) => match !== first && match !== second && pointsAreNonCollinear({
|
|
691
|
+
first: first.symbolPort,
|
|
692
|
+
second: second.symbolPort,
|
|
693
|
+
third: match.symbolPort
|
|
694
|
+
}) && pointsAreNonCollinear({
|
|
695
|
+
first: first.schematicPort.center,
|
|
696
|
+
second: second.schematicPort.center,
|
|
697
|
+
third: match.schematicPort.center
|
|
698
|
+
})
|
|
699
|
+
);
|
|
700
|
+
if (!third) {
|
|
652
701
|
return fromTriangles(
|
|
653
702
|
getPortPairTriangle({
|
|
654
703
|
first: first.symbolPort,
|
|
@@ -660,7 +709,87 @@ var getSymbolToSchematicTransform = ({
|
|
|
660
709
|
})
|
|
661
710
|
);
|
|
662
711
|
}
|
|
663
|
-
|
|
712
|
+
const transform = fromTriangles(
|
|
713
|
+
[first.symbolPort, second.symbolPort, third.symbolPort],
|
|
714
|
+
[
|
|
715
|
+
first.schematicPort.center,
|
|
716
|
+
second.schematicPort.center,
|
|
717
|
+
third.schematicPort.center
|
|
718
|
+
]
|
|
719
|
+
);
|
|
720
|
+
if (!isRigidUniformTransform(transform)) return void 0;
|
|
721
|
+
const fitsAllMatchedPorts = matches.every((match) => {
|
|
722
|
+
const transformedPort = applyToPoint3(transform, match.symbolPort);
|
|
723
|
+
return Math.hypot(
|
|
724
|
+
transformedPort.x - match.schematicPort.center.x,
|
|
725
|
+
transformedPort.y - match.schematicPort.center.y
|
|
726
|
+
) < TRANSFORM_TOLERANCE;
|
|
727
|
+
});
|
|
728
|
+
return fitsAllMatchedPorts ? transform : void 0;
|
|
729
|
+
};
|
|
730
|
+
var pointsAreNonCollinear = ({
|
|
731
|
+
first,
|
|
732
|
+
second,
|
|
733
|
+
third
|
|
734
|
+
}) => Math.abs(
|
|
735
|
+
(second.x - first.x) * (third.y - first.y) - (second.y - first.y) * (third.x - first.x)
|
|
736
|
+
) > Number.EPSILON;
|
|
737
|
+
var isRigidUniformTransform = (transform) => {
|
|
738
|
+
const firstAxisLength = Math.hypot(transform.a, transform.b);
|
|
739
|
+
const secondAxisLength = Math.hypot(transform.c, transform.d);
|
|
740
|
+
if (firstAxisLength === 0 || secondAxisLength === 0) return false;
|
|
741
|
+
const axisDotProduct = transform.a * transform.c + transform.b * transform.d;
|
|
742
|
+
const relativeScaleDifference = Math.abs(firstAxisLength - secondAxisLength) / Math.max(firstAxisLength, secondAxisLength);
|
|
743
|
+
const normalizedAxisDotProduct = Math.abs(axisDotProduct) / (firstAxisLength * secondAxisLength);
|
|
744
|
+
return relativeScaleDifference < TRANSFORM_TOLERANCE && normalizedAxisDotProduct < TRANSFORM_TOLERANCE;
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
// lib/generate-board-schematic-tsx/match-symbol-ports-by-pin-identity.ts
|
|
748
|
+
var matchSymbolPortsByPinIdentity = ({
|
|
749
|
+
schematicPorts,
|
|
750
|
+
sourcePorts,
|
|
751
|
+
symbol
|
|
752
|
+
}) => symbol.ports.flatMap((symbolPort) => {
|
|
753
|
+
const candidates = schematicPorts.filter((schematicPort) => {
|
|
754
|
+
const sourcePort = sourcePorts.find(
|
|
755
|
+
(port) => port.source_port_id === schematicPort.source_port_id
|
|
756
|
+
);
|
|
757
|
+
const pinNumber = schematicPort.pin_number ?? sourcePort?.pin_number;
|
|
758
|
+
const portIdentities = [
|
|
759
|
+
sourcePort?.name,
|
|
760
|
+
...sourcePort?.port_hints ?? [],
|
|
761
|
+
...pinNumber === void 0 ? [] : [String(pinNumber), `pin${pinNumber}`]
|
|
762
|
+
];
|
|
763
|
+
return portIdentities.some(
|
|
764
|
+
(portIdentity) => portIdentity !== void 0 && symbolPort.labels.includes(portIdentity)
|
|
765
|
+
);
|
|
766
|
+
});
|
|
767
|
+
return candidates.length === 1 ? [{ symbolPort, schematicPort: candidates[0] }] : [];
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
// lib/generate-board-schematic-tsx/get-symbol-to-schematic-transform.ts
|
|
771
|
+
var getSymbolToSchematicTransform = ({
|
|
772
|
+
symbol,
|
|
773
|
+
schematicComponent,
|
|
774
|
+
schematicPorts,
|
|
775
|
+
sourcePorts
|
|
776
|
+
}) => {
|
|
777
|
+
const pinIdentityMatches = matchSymbolPortsByPinIdentity({
|
|
778
|
+
schematicPorts,
|
|
779
|
+
sourcePorts,
|
|
780
|
+
symbol
|
|
781
|
+
});
|
|
782
|
+
const rigidPinIdentityTransform = getRigidPinIdentityTransform(pinIdentityMatches);
|
|
783
|
+
if (rigidPinIdentityTransform) return rigidPinIdentityTransform;
|
|
784
|
+
if (pinIdentityMatches.length >= 3) {
|
|
785
|
+
const rendererCompatibleTransform = getRendererCompatibleSymbolTransform({
|
|
786
|
+
schematicComponent,
|
|
787
|
+
schematicPorts,
|
|
788
|
+
symbol
|
|
789
|
+
});
|
|
790
|
+
if (rendererCompatibleTransform) return rendererCompatibleTransform;
|
|
791
|
+
}
|
|
792
|
+
return fromTriangles2(
|
|
664
793
|
[
|
|
665
794
|
symbol.center,
|
|
666
795
|
{ x: symbol.center.x + symbol.size.width, y: symbol.center.y },
|
|
@@ -679,13 +808,6 @@ var getSymbolToSchematicTransform = ({
|
|
|
679
808
|
]
|
|
680
809
|
);
|
|
681
810
|
};
|
|
682
|
-
var isNonCollinear = ({
|
|
683
|
-
first,
|
|
684
|
-
second,
|
|
685
|
-
third
|
|
686
|
-
}) => Math.abs(
|
|
687
|
-
(second.x - first.x) * (third.y - first.y) - (second.y - first.y) * (third.x - first.x)
|
|
688
|
-
) > Number.EPSILON;
|
|
689
811
|
|
|
690
812
|
// lib/generate-board-schematic-tsx/convert-symbol-primitives.ts
|
|
691
813
|
import { schematic_text as schematic_text2 } from "circuit-json";
|
|
@@ -1041,7 +1163,7 @@ import { symbols as symbols2 } from "schematic-symbols";
|
|
|
1041
1163
|
// lib/generate-board-schematic-tsx/get-net-label-symbol-to-schematic-transform.ts
|
|
1042
1164
|
import {
|
|
1043
1165
|
fromOneMovingPoint,
|
|
1044
|
-
translate as
|
|
1166
|
+
translate as translate4
|
|
1045
1167
|
} from "transformation-matrix";
|
|
1046
1168
|
var getNetLabelSymbolToSchematicTransform = ({
|
|
1047
1169
|
symbol,
|
|
@@ -1049,7 +1171,7 @@ var getNetLabelSymbolToSchematicTransform = ({
|
|
|
1049
1171
|
}) => {
|
|
1050
1172
|
const symbolPort = symbol.ports[0];
|
|
1051
1173
|
if (!symbolPort) {
|
|
1052
|
-
return
|
|
1174
|
+
return translate4(schematicAnchorPosition.x, schematicAnchorPosition.y);
|
|
1053
1175
|
}
|
|
1054
1176
|
return fromOneMovingPoint(symbolPort, schematicAnchorPosition);
|
|
1055
1177
|
};
|
package/dist/cli/main.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
convertCircuitJsonToTscircuit
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-DZP2ZWBQ.js";
|
|
5
5
|
|
|
6
6
|
// cli/main.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -16,7 +16,7 @@ var package_default = {
|
|
|
16
16
|
"./cli": "./dist/cli/main.js"
|
|
17
17
|
},
|
|
18
18
|
type: "module",
|
|
19
|
-
version: "0.0.
|
|
19
|
+
version: "0.0.86",
|
|
20
20
|
repository: {
|
|
21
21
|
type: "git",
|
|
22
22
|
url: "https://github.com/tscircuit/circuit-json-to-tscircuit.git"
|
package/dist/lib/index.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { mmStr } from "@tscircuit/mm"
|
|
2
2
|
import type { AnyCircuitElement } from "circuit-json"
|
|
3
3
|
|
|
4
|
+
const DEFAULT_SHEET_DIMENSIONS_MM = {
|
|
5
|
+
a4: { width: 297, height: 210 },
|
|
6
|
+
ansi_b: { width: 431.8, height: 279.4 },
|
|
7
|
+
} as const
|
|
8
|
+
|
|
4
9
|
export const generateBoardSchematicSheetTsx = ({
|
|
5
10
|
circuitJson,
|
|
6
11
|
schematicElements,
|
|
@@ -21,11 +26,11 @@ export const generateBoardSchematicSheetTsx = ({
|
|
|
21
26
|
if (sheet.sheet_size) {
|
|
22
27
|
attrs.push(`sheetSize="${sheet.sheet_size === "a4" ? "A4" : "ANSI_B"}"`)
|
|
23
28
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
29
|
+
const defaultDimensions =
|
|
30
|
+
DEFAULT_SHEET_DIMENSIONS_MM[sheet.sheet_size ?? "a4"]
|
|
31
|
+
const sheetWidth = sheet.sheet_width ?? defaultDimensions.width
|
|
32
|
+
const sheetHeight = sheet.sheet_height ?? defaultDimensions.height
|
|
33
|
+
attrs.push(`sheetWidth="${mmStr(sheetWidth)}"`)
|
|
34
|
+
attrs.push(`sheetHeight="${mmStr(sheetHeight)}"`)
|
|
30
35
|
return `<schematicsheet ${attrs.join(" ")}>\n${schematicElements.join("\n")}\n</schematicsheet>`
|
|
31
36
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { SchematicComponent, SchematicPort } from "circuit-json"
|
|
2
|
+
import type { SchSymbol } from "schematic-symbols"
|
|
3
|
+
import { compose, scale, translate, type Matrix } from "transformation-matrix"
|
|
4
|
+
import { matchSchematicPortsToSymbolPorts } from "./match-schematic-ports-to-symbol-ports"
|
|
5
|
+
|
|
6
|
+
export const getRendererCompatibleSymbolTransform = ({
|
|
7
|
+
schematicComponent,
|
|
8
|
+
schematicPorts,
|
|
9
|
+
symbol,
|
|
10
|
+
}: {
|
|
11
|
+
schematicComponent: SchematicComponent
|
|
12
|
+
schematicPorts: SchematicPort[]
|
|
13
|
+
symbol: SchSymbol
|
|
14
|
+
}): Matrix | undefined => {
|
|
15
|
+
const matches = matchSchematicPortsToSymbolPorts({
|
|
16
|
+
schematicComponent,
|
|
17
|
+
schematicPorts,
|
|
18
|
+
symbol,
|
|
19
|
+
})
|
|
20
|
+
const first = matches[0]
|
|
21
|
+
if (!first) return undefined
|
|
22
|
+
|
|
23
|
+
const second = matches[1]
|
|
24
|
+
const firstSymbolPoint = second?.symbolPort ?? symbol.center
|
|
25
|
+
const firstSchematicPoint =
|
|
26
|
+
second?.schematicPort.center ?? schematicComponent.center
|
|
27
|
+
const originalDistance = Math.hypot(
|
|
28
|
+
first.symbolPort.x - firstSymbolPoint.x,
|
|
29
|
+
first.symbolPort.y - firstSymbolPoint.y,
|
|
30
|
+
)
|
|
31
|
+
if (originalDistance === 0) return undefined
|
|
32
|
+
|
|
33
|
+
const renderedDistance = Math.hypot(
|
|
34
|
+
first.schematicPort.center.x - firstSchematicPoint.x,
|
|
35
|
+
first.schematicPort.center.y - firstSchematicPoint.y,
|
|
36
|
+
)
|
|
37
|
+
const uniformScale = renderedDistance / originalDistance
|
|
38
|
+
return compose(
|
|
39
|
+
translate(firstSchematicPoint.x, firstSchematicPoint.y),
|
|
40
|
+
scale(uniformScale),
|
|
41
|
+
translate(-firstSymbolPoint.x, -firstSymbolPoint.y),
|
|
42
|
+
)
|
|
43
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { applyToPoint, fromTriangles, type Matrix } from "transformation-matrix"
|
|
2
|
+
import { getPortPairTriangle } from "./get-port-pair-triangle"
|
|
3
|
+
import type { SchematicSymbolPortMatch } from "./match-symbol-ports-by-pin-identity"
|
|
4
|
+
|
|
5
|
+
const TRANSFORM_TOLERANCE = 0.000001
|
|
6
|
+
|
|
7
|
+
export const getRigidPinIdentityTransform = (
|
|
8
|
+
matches: SchematicSymbolPortMatch[],
|
|
9
|
+
): Matrix | undefined => {
|
|
10
|
+
const first = matches[0]
|
|
11
|
+
const second = matches.find(
|
|
12
|
+
(match) => match !== first && match.schematicPort !== first?.schematicPort,
|
|
13
|
+
)
|
|
14
|
+
if (!first || !second) return undefined
|
|
15
|
+
|
|
16
|
+
const third = matches.find(
|
|
17
|
+
(match) =>
|
|
18
|
+
match !== first &&
|
|
19
|
+
match !== second &&
|
|
20
|
+
pointsAreNonCollinear({
|
|
21
|
+
first: first.symbolPort,
|
|
22
|
+
second: second.symbolPort,
|
|
23
|
+
third: match.symbolPort,
|
|
24
|
+
}) &&
|
|
25
|
+
pointsAreNonCollinear({
|
|
26
|
+
first: first.schematicPort.center,
|
|
27
|
+
second: second.schematicPort.center,
|
|
28
|
+
third: match.schematicPort.center,
|
|
29
|
+
}),
|
|
30
|
+
)
|
|
31
|
+
if (!third) {
|
|
32
|
+
return fromTriangles(
|
|
33
|
+
getPortPairTriangle({
|
|
34
|
+
first: first.symbolPort,
|
|
35
|
+
second: second.symbolPort,
|
|
36
|
+
}),
|
|
37
|
+
getPortPairTriangle({
|
|
38
|
+
first: first.schematicPort.center,
|
|
39
|
+
second: second.schematicPort.center,
|
|
40
|
+
}),
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const transform = fromTriangles(
|
|
45
|
+
[first.symbolPort, second.symbolPort, third.symbolPort],
|
|
46
|
+
[
|
|
47
|
+
first.schematicPort.center,
|
|
48
|
+
second.schematicPort.center,
|
|
49
|
+
third.schematicPort.center,
|
|
50
|
+
],
|
|
51
|
+
)
|
|
52
|
+
if (!isRigidUniformTransform(transform)) return undefined
|
|
53
|
+
|
|
54
|
+
const fitsAllMatchedPorts = matches.every((match) => {
|
|
55
|
+
const transformedPort = applyToPoint(transform, match.symbolPort)
|
|
56
|
+
return (
|
|
57
|
+
Math.hypot(
|
|
58
|
+
transformedPort.x - match.schematicPort.center.x,
|
|
59
|
+
transformedPort.y - match.schematicPort.center.y,
|
|
60
|
+
) < TRANSFORM_TOLERANCE
|
|
61
|
+
)
|
|
62
|
+
})
|
|
63
|
+
return fitsAllMatchedPorts ? transform : undefined
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const pointsAreNonCollinear = ({
|
|
67
|
+
first,
|
|
68
|
+
second,
|
|
69
|
+
third,
|
|
70
|
+
}: {
|
|
71
|
+
first: { x: number; y: number }
|
|
72
|
+
second: { x: number; y: number }
|
|
73
|
+
third: { x: number; y: number }
|
|
74
|
+
}) =>
|
|
75
|
+
Math.abs(
|
|
76
|
+
(second.x - first.x) * (third.y - first.y) -
|
|
77
|
+
(second.y - first.y) * (third.x - first.x),
|
|
78
|
+
) > Number.EPSILON
|
|
79
|
+
|
|
80
|
+
const isRigidUniformTransform = (transform: Matrix) => {
|
|
81
|
+
const firstAxisLength = Math.hypot(transform.a, transform.b)
|
|
82
|
+
const secondAxisLength = Math.hypot(transform.c, transform.d)
|
|
83
|
+
if (firstAxisLength === 0 || secondAxisLength === 0) return false
|
|
84
|
+
|
|
85
|
+
const axisDotProduct = transform.a * transform.c + transform.b * transform.d
|
|
86
|
+
const relativeScaleDifference =
|
|
87
|
+
Math.abs(firstAxisLength - secondAxisLength) /
|
|
88
|
+
Math.max(firstAxisLength, secondAxisLength)
|
|
89
|
+
const normalizedAxisDotProduct =
|
|
90
|
+
Math.abs(axisDotProduct) / (firstAxisLength * secondAxisLength)
|
|
91
|
+
return (
|
|
92
|
+
relativeScaleDifference < TRANSFORM_TOLERANCE &&
|
|
93
|
+
normalizedAxisDotProduct < TRANSFORM_TOLERANCE
|
|
94
|
+
)
|
|
95
|
+
}
|
|
@@ -4,9 +4,10 @@ import type {
|
|
|
4
4
|
SourcePort,
|
|
5
5
|
} from "circuit-json"
|
|
6
6
|
import type { SchSymbol } from "schematic-symbols"
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
import {
|
|
7
|
+
import { fromTriangles } from "transformation-matrix"
|
|
8
|
+
import { getRendererCompatibleSymbolTransform } from "./get-renderer-compatible-symbol-transform"
|
|
9
|
+
import { getRigidPinIdentityTransform } from "./get-rigid-pin-identity-transform"
|
|
10
|
+
import { matchSymbolPortsByPinIdentity } from "./match-symbol-ports-by-pin-identity"
|
|
10
11
|
|
|
11
12
|
export const getSymbolToSchematicTransform = ({
|
|
12
13
|
symbol,
|
|
@@ -19,81 +20,27 @@ export const getSymbolToSchematicTransform = ({
|
|
|
19
20
|
schematicPorts: SchematicPort[]
|
|
20
21
|
sourcePorts: SourcePort[]
|
|
21
22
|
}) => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
(port) => port.source_port_id === schematicPort.source_port_id,
|
|
27
|
-
)
|
|
28
|
-
const pinNumber = schematicPort.pin_number ?? sourcePort?.pin_number
|
|
29
|
-
const labels = [
|
|
30
|
-
sourcePort?.name,
|
|
31
|
-
...(sourcePort?.port_hints ?? []),
|
|
32
|
-
...(pinNumber === undefined
|
|
33
|
-
? []
|
|
34
|
-
: [String(pinNumber), `pin${pinNumber}`]),
|
|
35
|
-
]
|
|
36
|
-
return labels.some(
|
|
37
|
-
(label) => label !== undefined && symbolPort.labels.includes(label),
|
|
38
|
-
)
|
|
39
|
-
})
|
|
40
|
-
return candidates.length === 1
|
|
41
|
-
? [{ symbolPort, schematicPort: candidates[0]! }]
|
|
42
|
-
: []
|
|
23
|
+
const pinIdentityMatches = matchSymbolPortsByPinIdentity({
|
|
24
|
+
schematicPorts,
|
|
25
|
+
sourcePorts,
|
|
26
|
+
symbol,
|
|
43
27
|
})
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}) &&
|
|
58
|
-
isNonCollinear({
|
|
59
|
-
first: first.schematicPort.center,
|
|
60
|
-
second: second.schematicPort.center,
|
|
61
|
-
third: match.schematicPort.center,
|
|
62
|
-
}),
|
|
63
|
-
)
|
|
64
|
-
if (third) {
|
|
65
|
-
const transform = fromTriangles(
|
|
66
|
-
[first.symbolPort, second.symbolPort, third.symbolPort],
|
|
67
|
-
[
|
|
68
|
-
first.schematicPort.center,
|
|
69
|
-
second.schematicPort.center,
|
|
70
|
-
third.schematicPort.center,
|
|
71
|
-
],
|
|
72
|
-
)
|
|
73
|
-
const fitsAllMatchedPorts = matches.every((match) => {
|
|
74
|
-
const transformedPort = applyToPoint(transform, match.symbolPort)
|
|
75
|
-
return (
|
|
76
|
-
Math.hypot(
|
|
77
|
-
transformedPort.x - match.schematicPort.center.x,
|
|
78
|
-
transformedPort.y - match.schematicPort.center.y,
|
|
79
|
-
) < 0.000001
|
|
80
|
-
)
|
|
81
|
-
})
|
|
82
|
-
if (fitsAllMatchedPorts) return transform
|
|
83
|
-
}
|
|
84
|
-
return fromTriangles(
|
|
85
|
-
getPortPairTriangle({
|
|
86
|
-
first: first.symbolPort,
|
|
87
|
-
second: second.symbolPort,
|
|
88
|
-
}),
|
|
89
|
-
getPortPairTriangle({
|
|
90
|
-
first: first.schematicPort.center,
|
|
91
|
-
second: second.schematicPort.center,
|
|
92
|
-
}),
|
|
93
|
-
)
|
|
28
|
+
const rigidPinIdentityTransform =
|
|
29
|
+
getRigidPinIdentityTransform(pinIdentityMatches)
|
|
30
|
+
if (rigidPinIdentityTransform) return rigidPinIdentityTransform
|
|
31
|
+
|
|
32
|
+
if (pinIdentityMatches.length >= 3) {
|
|
33
|
+
// Inconsistent pin identities must not shear standard symbol artwork.
|
|
34
|
+
// Match circuit-to-svg's established transform for the visual fallback.
|
|
35
|
+
const rendererCompatibleTransform = getRendererCompatibleSymbolTransform({
|
|
36
|
+
schematicComponent,
|
|
37
|
+
schematicPorts,
|
|
38
|
+
symbol,
|
|
39
|
+
})
|
|
40
|
+
if (rendererCompatibleTransform) return rendererCompatibleTransform
|
|
94
41
|
}
|
|
95
|
-
|
|
96
|
-
//
|
|
42
|
+
|
|
43
|
+
// Without matching ports, use the explicitly imported component bounds.
|
|
97
44
|
return fromTriangles(
|
|
98
45
|
[
|
|
99
46
|
symbol.center,
|
|
@@ -113,17 +60,3 @@ export const getSymbolToSchematicTransform = ({
|
|
|
113
60
|
],
|
|
114
61
|
)
|
|
115
62
|
}
|
|
116
|
-
|
|
117
|
-
const isNonCollinear = ({
|
|
118
|
-
first,
|
|
119
|
-
second,
|
|
120
|
-
third,
|
|
121
|
-
}: {
|
|
122
|
-
first: { x: number; y: number }
|
|
123
|
-
second: { x: number; y: number }
|
|
124
|
-
third: { x: number; y: number }
|
|
125
|
-
}) =>
|
|
126
|
-
Math.abs(
|
|
127
|
-
(second.x - first.x) * (third.y - first.y) -
|
|
128
|
-
(second.y - first.y) * (third.x - first.x),
|
|
129
|
-
) > Number.EPSILON
|