@tscircuit/core 0.0.18 → 0.0.20

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/index.d.ts CHANGED
@@ -6,10 +6,10 @@ import { ReactElement } from 'react';
6
6
  import { Matrix } from 'transformation-matrix';
7
7
  import { SchSymbol, BaseSymbolName } from 'schematic-symbols';
8
8
  import * as Props from '@tscircuit/props';
9
- import { boardProps, footprintProps, smtPadProps, resistorProps, ledProps, traceProps, traceHintProps, groupProps, chipProps, jumperProps, silkscreenPathProps, platedHoleProps } from '@tscircuit/props';
9
+ import { boardProps, footprintProps, smtPadProps, resistorProps, ledProps, capacitorProps, traceProps, traceHintProps, groupProps, chipProps, jumperProps, silkscreenPathProps, platedHoleProps } from '@tscircuit/props';
10
10
  import * as _tscircuit_layout from '@tscircuit/layout';
11
11
 
12
- declare class Project {
12
+ declare class Circuit {
13
13
  rootComponent: PrimitiveComponent | null;
14
14
  children: PrimitiveComponent[];
15
15
  db: SoupUtilObjects;
@@ -31,10 +31,13 @@ declare class Project {
31
31
  computeGlobalSchematicTransform(): Matrix;
32
32
  computeGlobalPcbTransform(): Matrix;
33
33
  selectAll(selector: string): PrimitiveComponent[];
34
- selectOne(selector: string): PrimitiveComponent | null;
34
+ selectOne(selector: string, opts: {
35
+ type?: "component" | "port";
36
+ }): PrimitiveComponent | null;
35
37
  }
38
+ declare const Project: typeof Circuit;
36
39
 
37
- declare const orderedRenderPhases: readonly ["ReactSubtreesRender", "SourceRender", "SourceParentAttachment", "PortDiscovery", "PortMatching", "SourceTraceRender", "SchematicComponentRender", "SchematicLayout", "SchematicPortRender", "SchematicTraceRender", "PcbComponentRender", "PcbPortRender", "PcbPrimitiveRender", "PcbParentAttachment", "PcbLayout", "PcbTraceRender", "CadModelRender", "PcbAnalysis"];
40
+ declare const orderedRenderPhases: readonly ["ReactSubtreesRender", "CreateTracesFromProps", "SourceRender", "SourceParentAttachment", "PortDiscovery", "PortMatching", "SourceTraceRender", "SchematicComponentRender", "SchematicLayout", "SchematicPortRender", "SchematicTraceRender", "PcbComponentRender", "PcbPortRender", "PcbPrimitiveRender", "PcbParentAttachment", "PcbLayout", "PcbTraceRender", "CadModelRender", "PcbAnalysis"];
38
41
  type RenderPhase = (typeof orderedRenderPhases)[number];
39
42
  type RenderPhaseFn<K extends RenderPhase = RenderPhase> = `doInitial${K}` | `update${K}` | `remove${K}`;
40
43
  type RenderPhaseStates = Record<RenderPhase, {
@@ -89,19 +92,26 @@ declare abstract class PrimitiveComponent<ZodProps extends ZodType = any> extend
89
92
  children: PrimitiveComponent[];
90
93
  childrenPendingRemoval: PrimitiveComponent[];
91
94
  get config(): BaseComponentConfig;
92
- project: Project | null;
95
+ project: Circuit | null;
93
96
  props: z.input<ZodProps>;
94
97
  _parsedProps: z.infer<ZodProps>;
95
98
  componentName: string;
96
99
  lowercaseComponentName: string;
97
100
  externallyAddedAliases: string[];
101
+ /**
102
+ * An opaque group is a self-contained subcircuit. All the selectors inside
103
+ * an opaque group are relative to the group. You can have multiple opaque
104
+ * groups and their selectors will not interact with each other (even if the
105
+ * components share the same names) unless you explicitly break out some ports
106
+ */
107
+ get isOpaqueGroup(): boolean;
98
108
  source_group_id: string | null;
99
109
  source_component_id: string | null;
100
110
  schematic_component_id: string | null;
101
111
  pcb_component_id: string | null;
102
112
  cad_component_id: string | null;
103
113
  constructor(props: z.input<ZodProps>);
104
- setProject(project: Project): void;
114
+ setProject(project: Circuit): void;
105
115
  setProps(props: Partial<z.input<ZodProps>>): void;
106
116
  /**
107
117
  * Computes a transformation matrix from the props of this component for PCB
@@ -145,10 +155,13 @@ declare abstract class PrimitiveComponent<ZodProps extends ZodType = any> extend
145
155
  add(component: PrimitiveComponent): void;
146
156
  addAll(components: PrimitiveComponent[]): void;
147
157
  remove(component: PrimitiveComponent): void;
158
+ getOpaqueGroupSelector(): string;
159
+ getFullPathSelector(): string;
148
160
  getNameAndAliases(): string[];
149
161
  isMatchingNameOrAlias(name: string): boolean;
150
162
  isMatchingAnyOf(aliases: Array<string | number>): boolean;
151
163
  doesSelectorMatch(selector: string): boolean;
164
+ getOpaqueGroup(): PrimitiveComponent;
152
165
  selectAll(selector: string): PrimitiveComponent[];
153
166
  selectOne(selector: string, options?: {
154
167
  type?: string;
@@ -287,6 +300,7 @@ declare class NormalComponent<ZodProps extends ZodType = any, PortNames extends
287
300
 
288
301
  declare class Board extends NormalComponent<typeof boardProps> {
289
302
  pcb_board_id: string | null;
303
+ get isOpaqueGroup(): boolean;
290
304
  get config(): {
291
305
  zodProps: z.ZodObject<{
292
306
  width: z.ZodOptional<z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>>;
@@ -428,33 +442,33 @@ type PolarizedPassivePorts = PassivePorts | "anode" | "cathode" | "pos" | "neg";
428
442
  declare class Resistor extends NormalComponent<typeof resistorProps, PassivePorts> {
429
443
  get config(): {
430
444
  schematicSymbolName: BaseSymbolName;
431
- zodProps: zod.ZodObject<zod.objectUtil.extendShape<zod.objectUtil.extendShape<zod.objectUtil.extendShape<{
432
- pcbX: zod.ZodOptional<zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>>;
433
- pcbY: zod.ZodOptional<zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>>;
434
- pcbRotation: zod.ZodOptional<zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>>;
435
- schX: zod.ZodOptional<zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>>;
436
- schY: zod.ZodOptional<zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>>;
437
- schRotation: zod.ZodOptional<zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>>;
438
- layer: zod.ZodOptional<zod.ZodEffects<zod.ZodUnion<[zod.ZodEnum<["top", "bottom", "inner1", "inner2", "inner3", "inner4", "inner5", "inner6"]>, zod.ZodObject<{
439
- name: zod.ZodEnum<["top", "bottom", "inner1", "inner2", "inner3", "inner4", "inner5", "inner6"]>;
440
- }, "strip", zod.ZodTypeAny, {
445
+ zodProps: z.ZodObject<z.objectUtil.extendShape<z.objectUtil.extendShape<z.objectUtil.extendShape<{
446
+ pcbX: z.ZodOptional<z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>>;
447
+ pcbY: z.ZodOptional<z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>>;
448
+ pcbRotation: z.ZodOptional<z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>>;
449
+ schX: z.ZodOptional<z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>>;
450
+ schY: z.ZodOptional<z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>>;
451
+ schRotation: z.ZodOptional<z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>>;
452
+ layer: z.ZodOptional<z.ZodEffects<z.ZodUnion<[z.ZodEnum<["top", "bottom", "inner1", "inner2", "inner3", "inner4", "inner5", "inner6"]>, z.ZodObject<{
453
+ name: z.ZodEnum<["top", "bottom", "inner1", "inner2", "inner3", "inner4", "inner5", "inner6"]>;
454
+ }, "strip", z.ZodTypeAny, {
441
455
  name: "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6";
442
456
  }, {
443
457
  name: "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6";
444
458
  }>]>, "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6", "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6" | {
445
459
  name: "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6";
446
460
  }>>;
447
- footprint: zod.ZodOptional<zod.ZodType<Props.Footprint, zod.ZodTypeDef, Props.Footprint>>;
461
+ footprint: z.ZodOptional<z.ZodType<Props.Footprint, z.ZodTypeDef, Props.Footprint>>;
448
462
  }, {
449
- supplierPartNumbers: zod.ZodOptional<zod.ZodRecord<zod.ZodEnum<["jlcpcb", "macrofab", "pcbway", "digikey", "mouser", "lcsc"]>, zod.ZodArray<zod.ZodString, "many">>>;
463
+ supplierPartNumbers: z.ZodOptional<z.ZodRecord<z.ZodEnum<["jlcpcb", "macrofab", "pcbway", "digikey", "mouser", "lcsc"]>, z.ZodArray<z.ZodString, "many">>>;
450
464
  }>, {
451
- name: zod.ZodString;
452
- cadModel: zod.ZodOptional<zod.ZodUnion<[zod.ZodString, zod.ZodObject<zod.objectUtil.extendShape<{
453
- rotationOffset: zod.ZodOptional<zod.ZodUnion<[zod.ZodNumber, zod.ZodObject<{
454
- x: zod.ZodUnion<[zod.ZodNumber, zod.ZodString]>;
455
- y: zod.ZodUnion<[zod.ZodNumber, zod.ZodString]>;
456
- z: zod.ZodUnion<[zod.ZodNumber, zod.ZodString]>;
457
- }, "strip", zod.ZodTypeAny, {
465
+ name: z.ZodString;
466
+ cadModel: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodObject<z.objectUtil.extendShape<{
467
+ rotationOffset: z.ZodOptional<z.ZodUnion<[z.ZodNumber, z.ZodObject<{
468
+ x: z.ZodUnion<[z.ZodNumber, z.ZodString]>;
469
+ y: z.ZodUnion<[z.ZodNumber, z.ZodString]>;
470
+ z: z.ZodUnion<[z.ZodNumber, z.ZodString]>;
471
+ }, "strip", z.ZodTypeAny, {
458
472
  x: string | number;
459
473
  y: string | number;
460
474
  z: string | number;
@@ -463,11 +477,11 @@ declare class Resistor extends NormalComponent<typeof resistorProps, PassivePort
463
477
  y: string | number;
464
478
  z: string | number;
465
479
  }>]>>;
466
- positionOffset: zod.ZodOptional<zod.ZodObject<{
467
- x: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
468
- y: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
469
- z: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
470
- }, "strip", zod.ZodTypeAny, {
480
+ positionOffset: z.ZodOptional<z.ZodObject<{
481
+ x: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
482
+ y: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
483
+ z: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
484
+ }, "strip", z.ZodTypeAny, {
471
485
  x: number;
472
486
  y: number;
473
487
  z: number;
@@ -476,11 +490,11 @@ declare class Resistor extends NormalComponent<typeof resistorProps, PassivePort
476
490
  y: string | number;
477
491
  z: string | number;
478
492
  }>>;
479
- size: zod.ZodOptional<zod.ZodObject<{
480
- x: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
481
- y: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
482
- z: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
483
- }, "strip", zod.ZodTypeAny, {
493
+ size: z.ZodOptional<z.ZodObject<{
494
+ x: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
495
+ y: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
496
+ z: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
497
+ }, "strip", z.ZodTypeAny, {
484
498
  x: number;
485
499
  y: number;
486
500
  z: number;
@@ -490,8 +504,8 @@ declare class Resistor extends NormalComponent<typeof resistorProps, PassivePort
490
504
  z: string | number;
491
505
  }>>;
492
506
  }, {
493
- stlUrl: zod.ZodString;
494
- }>, "strip", zod.ZodTypeAny, {
507
+ stlUrl: z.ZodString;
508
+ }>, "strip", z.ZodTypeAny, {
495
509
  stlUrl: string;
496
510
  rotationOffset?: number | {
497
511
  x: string | number;
@@ -525,12 +539,12 @@ declare class Resistor extends NormalComponent<typeof resistorProps, PassivePort
525
539
  y: string | number;
526
540
  z: string | number;
527
541
  } | undefined;
528
- }>, zod.ZodObject<zod.objectUtil.extendShape<{
529
- rotationOffset: zod.ZodOptional<zod.ZodUnion<[zod.ZodNumber, zod.ZodObject<{
530
- x: zod.ZodUnion<[zod.ZodNumber, zod.ZodString]>;
531
- y: zod.ZodUnion<[zod.ZodNumber, zod.ZodString]>;
532
- z: zod.ZodUnion<[zod.ZodNumber, zod.ZodString]>;
533
- }, "strip", zod.ZodTypeAny, {
542
+ }>, z.ZodObject<z.objectUtil.extendShape<{
543
+ rotationOffset: z.ZodOptional<z.ZodUnion<[z.ZodNumber, z.ZodObject<{
544
+ x: z.ZodUnion<[z.ZodNumber, z.ZodString]>;
545
+ y: z.ZodUnion<[z.ZodNumber, z.ZodString]>;
546
+ z: z.ZodUnion<[z.ZodNumber, z.ZodString]>;
547
+ }, "strip", z.ZodTypeAny, {
534
548
  x: string | number;
535
549
  y: string | number;
536
550
  z: string | number;
@@ -539,11 +553,11 @@ declare class Resistor extends NormalComponent<typeof resistorProps, PassivePort
539
553
  y: string | number;
540
554
  z: string | number;
541
555
  }>]>>;
542
- positionOffset: zod.ZodOptional<zod.ZodObject<{
543
- x: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
544
- y: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
545
- z: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
546
- }, "strip", zod.ZodTypeAny, {
556
+ positionOffset: z.ZodOptional<z.ZodObject<{
557
+ x: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
558
+ y: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
559
+ z: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
560
+ }, "strip", z.ZodTypeAny, {
547
561
  x: number;
548
562
  y: number;
549
563
  z: number;
@@ -552,11 +566,11 @@ declare class Resistor extends NormalComponent<typeof resistorProps, PassivePort
552
566
  y: string | number;
553
567
  z: string | number;
554
568
  }>>;
555
- size: zod.ZodOptional<zod.ZodObject<{
556
- x: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
557
- y: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
558
- z: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
559
- }, "strip", zod.ZodTypeAny, {
569
+ size: z.ZodOptional<z.ZodObject<{
570
+ x: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
571
+ y: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
572
+ z: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
573
+ }, "strip", z.ZodTypeAny, {
560
574
  x: number;
561
575
  y: number;
562
576
  z: number;
@@ -566,9 +580,9 @@ declare class Resistor extends NormalComponent<typeof resistorProps, PassivePort
566
580
  z: string | number;
567
581
  }>>;
568
582
  }, {
569
- objUrl: zod.ZodString;
570
- mtlUrl: zod.ZodOptional<zod.ZodString>;
571
- }>, "strip", zod.ZodTypeAny, {
583
+ objUrl: z.ZodString;
584
+ mtlUrl: z.ZodOptional<z.ZodString>;
585
+ }>, "strip", z.ZodTypeAny, {
572
586
  objUrl: string;
573
587
  rotationOffset?: number | {
574
588
  x: string | number;
@@ -604,12 +618,12 @@ declare class Resistor extends NormalComponent<typeof resistorProps, PassivePort
604
618
  z: string | number;
605
619
  } | undefined;
606
620
  mtlUrl?: string | undefined;
607
- }>, zod.ZodObject<zod.objectUtil.extendShape<{
608
- rotationOffset: zod.ZodOptional<zod.ZodUnion<[zod.ZodNumber, zod.ZodObject<{
609
- x: zod.ZodUnion<[zod.ZodNumber, zod.ZodString]>;
610
- y: zod.ZodUnion<[zod.ZodNumber, zod.ZodString]>;
611
- z: zod.ZodUnion<[zod.ZodNumber, zod.ZodString]>;
612
- }, "strip", zod.ZodTypeAny, {
621
+ }>, z.ZodObject<z.objectUtil.extendShape<{
622
+ rotationOffset: z.ZodOptional<z.ZodUnion<[z.ZodNumber, z.ZodObject<{
623
+ x: z.ZodUnion<[z.ZodNumber, z.ZodString]>;
624
+ y: z.ZodUnion<[z.ZodNumber, z.ZodString]>;
625
+ z: z.ZodUnion<[z.ZodNumber, z.ZodString]>;
626
+ }, "strip", z.ZodTypeAny, {
613
627
  x: string | number;
614
628
  y: string | number;
615
629
  z: string | number;
@@ -618,11 +632,11 @@ declare class Resistor extends NormalComponent<typeof resistorProps, PassivePort
618
632
  y: string | number;
619
633
  z: string | number;
620
634
  }>]>>;
621
- positionOffset: zod.ZodOptional<zod.ZodObject<{
622
- x: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
623
- y: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
624
- z: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
625
- }, "strip", zod.ZodTypeAny, {
635
+ positionOffset: z.ZodOptional<z.ZodObject<{
636
+ x: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
637
+ y: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
638
+ z: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
639
+ }, "strip", z.ZodTypeAny, {
626
640
  x: number;
627
641
  y: number;
628
642
  z: number;
@@ -631,11 +645,11 @@ declare class Resistor extends NormalComponent<typeof resistorProps, PassivePort
631
645
  y: string | number;
632
646
  z: string | number;
633
647
  }>>;
634
- size: zod.ZodOptional<zod.ZodObject<{
635
- x: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
636
- y: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
637
- z: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
638
- }, "strip", zod.ZodTypeAny, {
648
+ size: z.ZodOptional<z.ZodObject<{
649
+ x: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
650
+ y: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
651
+ z: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
652
+ }, "strip", z.ZodTypeAny, {
639
653
  x: number;
640
654
  y: number;
641
655
  z: number;
@@ -645,8 +659,8 @@ declare class Resistor extends NormalComponent<typeof resistorProps, PassivePort
645
659
  z: string | number;
646
660
  }>>;
647
661
  }, {
648
- jscad: zod.ZodRecord<zod.ZodString, zod.ZodAny>;
649
- }>, "strip", zod.ZodTypeAny, {
662
+ jscad: z.ZodRecord<z.ZodString, z.ZodAny>;
663
+ }>, "strip", z.ZodTypeAny, {
650
664
  jscad: Record<string, any>;
651
665
  rotationOffset?: number | {
652
666
  x: string | number;
@@ -681,15 +695,15 @@ declare class Resistor extends NormalComponent<typeof resistorProps, PassivePort
681
695
  z: string | number;
682
696
  } | undefined;
683
697
  }>]>>;
684
- children: zod.ZodOptional<zod.ZodAny>;
685
- symbolName: zod.ZodOptional<zod.ZodString>;
698
+ children: z.ZodOptional<z.ZodAny>;
699
+ symbolName: z.ZodOptional<z.ZodString>;
686
700
  }>, {
687
- resistance: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
688
- pullupFor: zod.ZodOptional<zod.ZodString>;
689
- pullupTo: zod.ZodOptional<zod.ZodString>;
690
- pulldownFor: zod.ZodOptional<zod.ZodString>;
691
- pulldownTo: zod.ZodOptional<zod.ZodString>;
692
- }>, "strip", zod.ZodTypeAny, {
701
+ resistance: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
702
+ pullupFor: z.ZodOptional<z.ZodString>;
703
+ pullupTo: z.ZodOptional<z.ZodString>;
704
+ pulldownFor: z.ZodOptional<z.ZodString>;
705
+ pulldownTo: z.ZodOptional<z.ZodString>;
706
+ }>, "strip", z.ZodTypeAny, {
693
707
  name: string;
694
708
  resistance: number;
695
709
  pcbX?: number | undefined;
@@ -838,6 +852,7 @@ declare class Resistor extends NormalComponent<typeof resistorProps, PassivePort
838
852
  };
839
853
  pin1: Port;
840
854
  pin2: Port;
855
+ doInitialCreateTracesFromProps(): void;
841
856
  doInitialSourceRender(): void;
842
857
  }
843
858
 
@@ -1249,7 +1264,7 @@ declare class Led extends NormalComponent<typeof ledProps, PolarizedPassivePorts
1249
1264
  }
1250
1265
 
1251
1266
  type PortNames = "1" | "2" | "pin1" | "pin2" | "left" | "right" | "anode" | "cathode";
1252
- declare class Capacitor extends NormalComponent<typeof ledProps, PortNames> {
1267
+ declare class Capacitor extends NormalComponent<typeof capacitorProps, PortNames> {
1253
1268
  get config(): {
1254
1269
  zodProps: zod.ZodObject<zod.objectUtil.extendShape<zod.objectUtil.extendShape<zod.objectUtil.extendShape<{
1255
1270
  pcbX: zod.ZodOptional<zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>>;
@@ -1647,6 +1662,10 @@ declare class Capacitor extends NormalComponent<typeof ledProps, PortNames> {
1647
1662
  }>;
1648
1663
  sourceFtype: "simple_capacitor";
1649
1664
  };
1665
+ pin1: Port;
1666
+ pin2: Port;
1667
+ doInitialCreateTracesFromProps(): void;
1668
+ doInitialSourceRender(): void;
1650
1669
  }
1651
1670
 
1652
1671
  declare const netProps: z.ZodObject<{
@@ -3340,4 +3359,4 @@ declare global {
3340
3359
  }
3341
3360
  }
3342
3361
 
3343
- export { Board, Capacitor, Chip, Footprint, Group, type IRenderable, Jumper, Led, Net, NormalComponent, PlatedHole, Port, PrimitiveComponent, Project, Renderable, Resistor, SilkscreenPath, SmtPad, Trace, TraceHint };
3362
+ export { Board, Capacitor, Chip, Circuit, Footprint, Group, type IRenderable, Jumper, Led, Net, NormalComponent, PlatedHole, Port, PrimitiveComponent, Project, Renderable, Resistor, SilkscreenPath, SmtPad, Trace, TraceHint };
package/dist/index.js CHANGED
@@ -37,6 +37,7 @@ import "react";
37
37
  var orderedRenderPhases = [
38
38
  "ReactSubtreesRender",
39
39
  // probably going to be removed b/c subtrees should render instantly
40
+ "CreateTracesFromProps",
40
41
  "SourceRender",
41
42
  "SourceParentAttachment",
42
43
  "PortDiscovery",
@@ -123,6 +124,7 @@ import {
123
124
  applyToPoint,
124
125
  compose,
125
126
  identity,
127
+ rotate,
126
128
  translate
127
129
  } from "transformation-matrix";
128
130
 
@@ -171,6 +173,16 @@ var PrimitiveComponent = class extends Renderable {
171
173
  componentName = "";
172
174
  lowercaseComponentName = "";
173
175
  externallyAddedAliases;
176
+ /**
177
+ * An opaque group is a self-contained subcircuit. All the selectors inside
178
+ * an opaque group are relative to the group. You can have multiple opaque
179
+ * groups and their selectors will not interact with each other (even if the
180
+ * components share the same names) unless you explicitly break out some ports
181
+ */
182
+ get isOpaqueGroup() {
183
+ return Boolean(this.props.opaque) || // Implied opaque group for top-level group
184
+ this.lowercaseComponentName === "group" && this?.parent?.props?.name === "$root";
185
+ }
174
186
  source_group_id = null;
175
187
  source_component_id = null;
176
188
  schematic_component_id = null;
@@ -216,7 +228,12 @@ var PrimitiveComponent = class extends Renderable {
216
228
  * components
217
229
  */
218
230
  computePcbPropsTransform() {
219
- return translate(this.props.pcbX ?? 0, this.props.pcbY ?? 0);
231
+ const { _parsedProps: props } = this;
232
+ const matrix = compose(
233
+ translate(props.pcbX ?? 0, props.pcbY ?? 0),
234
+ rotate((props.pcbRotation ?? 0) * Math.PI / 180)
235
+ );
236
+ return matrix;
220
237
  }
221
238
  /**
222
239
  * Compute a transformation matrix combining all parent transforms for PCB
@@ -288,6 +305,20 @@ var PrimitiveComponent = class extends Renderable {
288
305
  this.childrenPendingRemoval.push(component);
289
306
  component.shouldBeRemoved = true;
290
307
  }
308
+ getOpaqueGroupSelector() {
309
+ const name = this._parsedProps.name;
310
+ const endPart = name ? `${this.lowercaseComponentName}.${name}` : this.lowercaseComponentName;
311
+ if (!this.parent) return endPart;
312
+ if (this.parent.isOpaqueGroup) return endPart;
313
+ return `${this.parent.getOpaqueGroupSelector()} > ${endPart}`;
314
+ }
315
+ getFullPathSelector() {
316
+ const name = this._parsedProps.name;
317
+ const endPart = name ? `${this.lowercaseComponentName}.${name}` : this.lowercaseComponentName;
318
+ const parentSelector = this.parent?.getFullPathSelector?.();
319
+ if (!parentSelector) return endPart;
320
+ return `${parentSelector} > ${endPart}`;
321
+ }
291
322
  getNameAndAliases() {
292
323
  return [
293
324
  this._parsedProps.name,
@@ -316,6 +347,13 @@ var PrimitiveComponent = class extends Renderable {
316
347
  return true;
317
348
  return false;
318
349
  }
350
+ getOpaqueGroup() {
351
+ if (this.isOpaqueGroup) return this;
352
+ const group = this.parent?.getOpaqueGroup();
353
+ if (!group)
354
+ throw new Error("Component is not inside an opaque group (no board?)");
355
+ return group;
356
+ }
319
357
  selectAll(selector) {
320
358
  const parts = selector.trim().split(/\s+/);
321
359
  let results = [this];
@@ -740,6 +778,7 @@ function getPortFromHints(hints) {
740
778
 
741
779
  // lib/components/primitive-components/SmtPad.ts
742
780
  import { smtPadProps } from "@tscircuit/props";
781
+ import { decomposeTSR } from "transformation-matrix";
743
782
  var SmtPad = class extends PrimitiveComponent {
744
783
  pcb_smtpad_id = null;
745
784
  matchedPort = null;
@@ -769,6 +808,8 @@ var SmtPad = class extends PrimitiveComponent {
769
808
  const { _parsedProps: props } = this;
770
809
  if (!props.portHints) return;
771
810
  const position = this.getGlobalPcbPosition();
811
+ const decomposedMat = decomposeTSR(this.computePcbGlobalTransform());
812
+ const isRotated90 = Math.abs(decomposedMat.rotation.angle * (180 / Math.PI) - 90) < 0.01;
772
813
  let pcb_smtpad = null;
773
814
  if (props.shape === "circle") {
774
815
  pcb_smtpad = db.pcb_smtpad.insert({
@@ -788,9 +829,7 @@ var SmtPad = class extends PrimitiveComponent {
788
829
  pcb_port_id: this.matchedPort?.pcb_port_id,
789
830
  layer: props.layer ?? "top",
790
831
  shape: "rect",
791
- // @ts-ignore: no idea why this is triggering
792
- width: props.width,
793
- height: props.height,
832
+ ...isRotated90 ? { width: props.height, height: props.width } : { width: props.width, height: props.height },
794
833
  port_hints: props.portHints.map((ph) => ph.toString()),
795
834
  x: position.x,
796
835
  y: position.y
@@ -804,6 +843,7 @@ var SmtPad = class extends PrimitiveComponent {
804
843
 
805
844
  // lib/components/primitive-components/SilkscreenPath.ts
806
845
  import { silkscreenPathProps } from "@tscircuit/props";
846
+ import { applyToPoint as applyToPoint3 } from "transformation-matrix";
807
847
  var SilkscreenPath = class extends PrimitiveComponent {
808
848
  pcb_silkscreen_path_id = null;
809
849
  get config() {
@@ -820,10 +860,21 @@ var SilkscreenPath = class extends PrimitiveComponent {
820
860
  `Invalid layer "${layer}" for SilkscreenPath. Must be "top" or "bottom".`
821
861
  );
822
862
  }
863
+ const transform = this.computePcbGlobalTransform();
823
864
  const pcb_silkscreen_path = db.pcb_silkscreen_path.insert({
824
865
  pcb_component_id: this.parent?.pcb_component_id,
825
866
  layer,
826
- route: props.route,
867
+ route: props.route.map((p) => {
868
+ const transformedPosition = applyToPoint3(transform, {
869
+ x: p.x,
870
+ y: p.y
871
+ });
872
+ return {
873
+ ...p,
874
+ x: transformedPosition.x,
875
+ y: transformedPosition.y
876
+ };
877
+ }),
827
878
  stroke_width: props.strokeWidth ?? 0.1
828
879
  });
829
880
  this.pcb_silkscreen_path_id = pcb_silkscreen_path.pcb_silkscreen_path_id;
@@ -1065,7 +1116,7 @@ var NormalComponent = class extends PrimitiveComponent {
1065
1116
  width: 0,
1066
1117
  height: 0,
1067
1118
  layer: props.layer ?? "top",
1068
- rotation: props.rotation ?? 0,
1119
+ rotation: props.pcbRotation ?? 0,
1069
1120
  source_component_id: this.source_component_id
1070
1121
  });
1071
1122
  this.pcb_component_id = pcb_component.pcb_component_id;
@@ -1191,6 +1242,9 @@ import { boardProps } from "@tscircuit/props";
1191
1242
  import { identity as identity3 } from "transformation-matrix";
1192
1243
  var Board = class extends NormalComponent {
1193
1244
  pcb_board_id = null;
1245
+ get isOpaqueGroup() {
1246
+ return true;
1247
+ }
1194
1248
  get config() {
1195
1249
  return {
1196
1250
  zodProps: boardProps
@@ -1222,82 +1276,7 @@ var Board = class extends NormalComponent {
1222
1276
 
1223
1277
  // lib/components/normal-components/Resistor.ts
1224
1278
  import { resistorProps } from "@tscircuit/props";
1225
- var Resistor = class extends NormalComponent {
1226
- get config() {
1227
- return {
1228
- schematicSymbolName: "boxresistor",
1229
- zodProps: resistorProps,
1230
- sourceFtype: "simple_resistor"
1231
- };
1232
- }
1233
- pin1 = this.portMap.pin1;
1234
- pin2 = this.portMap.pin2;
1235
- doInitialSourceRender() {
1236
- const { db } = this.project;
1237
- const { _parsedProps: props } = this;
1238
- const source_component = db.source_component.insert({
1239
- ftype: "simple_resistor",
1240
- name: props.name,
1241
- // @ts-ignore
1242
- manufacturer_part_number: props.manufacturerPartNumber ?? props.mfn,
1243
- supplier_part_numbers: props.supplierPartNumbers,
1244
- resistance: props.resistance
1245
- });
1246
- this.source_component_id = source_component.source_component_id;
1247
- }
1248
- };
1249
-
1250
- // lib/components/normal-components/Led.ts
1251
- import { ledProps } from "@tscircuit/props";
1252
- var Led = class extends NormalComponent {
1253
- get config() {
1254
- return {
1255
- schematicSymbolName: "led",
1256
- zodProps: ledProps,
1257
- sourceFtype: "simple_diode"
1258
- };
1259
- }
1260
- pos = this.portMap.pin1;
1261
- pin1 = this.portMap.pin1;
1262
- anode = this.portMap.pin1;
1263
- neg = this.portMap.pin2;
1264
- pin2 = this.portMap.pin2;
1265
- cathode = this.portMap.pin2;
1266
- };
1267
-
1268
- // lib/components/normal-components/Capacitor.ts
1269
- import { ledProps as ledProps2 } from "@tscircuit/props";
1270
-
1271
- // lib/utils/constants.ts
1272
- var stringProxy = new Proxy(
1273
- {},
1274
- {
1275
- get: (target, prop) => prop
1276
- }
1277
- );
1278
- var FTYPE = stringProxy;
1279
-
1280
- // lib/components/normal-components/Capacitor.ts
1281
- var Capacitor = class extends NormalComponent {
1282
- get config() {
1283
- return {
1284
- // schematicSymbolName: BASE_SYMBOLS.capacitor,
1285
- zodProps: ledProps2,
1286
- sourceFtype: FTYPE.simple_capacitor
1287
- };
1288
- }
1289
- };
1290
-
1291
- // lib/components/primitive-components/Net.ts
1292
- import { z as z4 } from "zod";
1293
- var netProps = z4.object({
1294
- name: z4.string()
1295
- });
1296
- var Net = class extends PrimitiveComponent {
1297
- getPortSelector() {
1298
- return `net.${this.props.name}`;
1299
- }
1300
- };
1279
+ import "zod";
1301
1280
 
1302
1281
  // lib/components/primitive-components/Trace.ts
1303
1282
  import { traceProps } from "@tscircuit/props";
@@ -1476,18 +1455,20 @@ var Trace = class extends PrimitiveComponent {
1476
1455
  const portSelectors = this.getTracePortPathSelectors();
1477
1456
  const ports = portSelectors.map((selector) => ({
1478
1457
  selector,
1479
- port: parent.selectOne(selector, { type: "port" })
1458
+ port: this.getOpaqueGroup().selectOne(selector, { type: "port" }) ?? null
1480
1459
  }));
1481
1460
  for (const { selector, port } of ports) {
1482
1461
  if (!port) {
1483
1462
  const parentSelector = selector.replace(/\>.*$/, "");
1484
- const targetComponent = parent.selectOne(parentSelector);
1463
+ const targetComponent = this.getOpaqueGroup().selectOne(parentSelector);
1485
1464
  if (!targetComponent) {
1486
1465
  this.renderError(`Could not find port for selector "${selector}"`);
1487
1466
  } else {
1488
1467
  this.renderError(
1489
1468
  `Could not find port for selector "${selector}"
1490
- searched component ${targetComponent.getString()}, which has ports:${targetComponent.children.filter((c) => c.componentName === "Port").map((c) => ` ${c.getString()}`).join("\n")}`
1469
+ searched component ${targetComponent.getString()}, which has ports: ${targetComponent.children.filter((c) => c.componentName === "Port").map(
1470
+ (c) => `${c.getString()}(${c.getNameAndAliases().join(",")})`
1471
+ ).join(" & ")}`
1491
1472
  );
1492
1473
  }
1493
1474
  }
@@ -1539,6 +1520,12 @@ searched component ${targetComponent.getString()}, which has ports:${targetCompo
1539
1520
  if (pcbRouteHints.length === 0) {
1540
1521
  const { solution } = autoroute(pcbElements.concat([source_trace]));
1541
1522
  const inputPcbTrace = solution[0];
1523
+ if (!inputPcbTrace) {
1524
+ console.log(
1525
+ `Failed to find route ffrom ${ports[0].port} to ${ports[1].port} render error!`
1526
+ );
1527
+ return;
1528
+ }
1542
1529
  const pcb_trace2 = db.pcb_trace.insert(inputPcbTrace);
1543
1530
  this.pcb_trace_id = pcb_trace2.pcb_trace_id;
1544
1531
  return;
@@ -1673,9 +1660,134 @@ searched component ${targetComponent.getString()}, which has ports:${targetCompo
1673
1660
  }
1674
1661
  };
1675
1662
 
1663
+ // lib/components/normal-components/Resistor.ts
1664
+ var Resistor = class extends NormalComponent {
1665
+ get config() {
1666
+ return {
1667
+ schematicSymbolName: "boxresistor",
1668
+ zodProps: resistorProps,
1669
+ sourceFtype: "simple_resistor"
1670
+ };
1671
+ }
1672
+ pin1 = this.portMap.pin1;
1673
+ pin2 = this.portMap.pin2;
1674
+ doInitialCreateTracesFromProps() {
1675
+ if (this.props.pullupFor && this.props.pullupTo) {
1676
+ this.add(
1677
+ new Trace({
1678
+ from: `${this.getOpaqueGroupSelector()} > port.1`,
1679
+ to: this.props.pullupFor
1680
+ })
1681
+ );
1682
+ this.add(
1683
+ new Trace({
1684
+ from: `${this.getOpaqueGroupSelector()} > port.2`,
1685
+ to: this.props.pullupTo
1686
+ })
1687
+ );
1688
+ }
1689
+ }
1690
+ doInitialSourceRender() {
1691
+ const { db } = this.project;
1692
+ const { _parsedProps: props } = this;
1693
+ const source_component = db.source_component.insert({
1694
+ ftype: "simple_resistor",
1695
+ name: props.name,
1696
+ // @ts-ignore
1697
+ manufacturer_part_number: props.manufacturerPartNumber ?? props.mfn,
1698
+ supplier_part_numbers: props.supplierPartNumbers,
1699
+ resistance: props.resistance
1700
+ });
1701
+ this.source_component_id = source_component.source_component_id;
1702
+ }
1703
+ };
1704
+
1705
+ // lib/components/normal-components/Led.ts
1706
+ import { ledProps } from "@tscircuit/props";
1707
+ var Led = class extends NormalComponent {
1708
+ get config() {
1709
+ return {
1710
+ schematicSymbolName: "led",
1711
+ zodProps: ledProps,
1712
+ sourceFtype: "simple_diode"
1713
+ };
1714
+ }
1715
+ pos = this.portMap.pin1;
1716
+ pin1 = this.portMap.pin1;
1717
+ anode = this.portMap.pin1;
1718
+ neg = this.portMap.pin2;
1719
+ pin2 = this.portMap.pin2;
1720
+ cathode = this.portMap.pin2;
1721
+ };
1722
+
1723
+ // lib/components/normal-components/Capacitor.ts
1724
+ import { ledProps as ledProps2 } from "@tscircuit/props";
1725
+
1726
+ // lib/utils/constants.ts
1727
+ var stringProxy = new Proxy(
1728
+ {},
1729
+ {
1730
+ get: (target, prop) => prop
1731
+ }
1732
+ );
1733
+ var FTYPE = stringProxy;
1734
+
1735
+ // lib/components/normal-components/Capacitor.ts
1736
+ var Capacitor = class extends NormalComponent {
1737
+ get config() {
1738
+ return {
1739
+ // schematicSymbolName: BASE_SYMBOLS.capacitor,
1740
+ zodProps: ledProps2,
1741
+ sourceFtype: FTYPE.simple_capacitor
1742
+ };
1743
+ }
1744
+ pin1 = this.portMap.pin1;
1745
+ pin2 = this.portMap.pin2;
1746
+ doInitialCreateTracesFromProps() {
1747
+ if (this.props.decouplingFor && this.props.decouplingTo) {
1748
+ this.add(
1749
+ new Trace({
1750
+ from: `${this.getOpaqueGroupSelector()} > port.1`,
1751
+ to: this.props.decouplingFor
1752
+ })
1753
+ );
1754
+ this.add(
1755
+ new Trace({
1756
+ from: `${this.getOpaqueGroupSelector()} > port.2`,
1757
+ to: this.props.decouplingTo
1758
+ })
1759
+ );
1760
+ }
1761
+ }
1762
+ doInitialSourceRender() {
1763
+ const { db } = this.project;
1764
+ const { _parsedProps: props } = this;
1765
+ const source_component = db.source_component.insert({
1766
+ ftype: "simple_capacitor",
1767
+ name: props.name,
1768
+ // @ts-ignore
1769
+ manufacturer_part_number: props.manufacturerPartNumber ?? props.mfn,
1770
+ supplier_part_numbers: props.supplierPartNumbers,
1771
+ capacitance: props.capacitance
1772
+ });
1773
+ this.source_component_id = source_component.source_component_id;
1774
+ }
1775
+ };
1776
+
1777
+ // lib/components/primitive-components/Net.ts
1778
+ import { z as z5 } from "zod";
1779
+ var netProps = z5.object({
1780
+ name: z5.string()
1781
+ });
1782
+ var Net = class extends PrimitiveComponent {
1783
+ getPortSelector() {
1784
+ return `net.${this.props.name}`;
1785
+ }
1786
+ };
1787
+
1676
1788
  // lib/components/primitive-components/TraceHint.ts
1677
1789
  import "@tscircuit/props";
1678
- import { applyToPoint as applyToPoint3 } from "transformation-matrix";
1790
+ import { applyToPoint as applyToPoint4 } from "transformation-matrix";
1679
1791
  var TraceHint = class extends PrimitiveComponent {
1680
1792
  matchedPort = null;
1681
1793
  doInitialPortMatching() {
@@ -1709,7 +1821,7 @@ var TraceHint = class extends PrimitiveComponent {
1709
1821
  const globalTransform = this.computePcbGlobalTransform();
1710
1822
  return offsets.map(
1711
1823
  (offset) => ({
1712
- ...applyToPoint3(globalTransform, offset),
1824
+ ...applyToPoint4(globalTransform, offset),
1713
1825
  via: offset.via,
1714
1826
  to_layer: offset.to_layer,
1715
1827
  trace_width: offset.trace_width
@@ -2157,7 +2269,7 @@ var Jumper = class extends NormalComponent {
2157
2269
  import { su } from "@tscircuit/soup-util";
2158
2270
  import { isValidElement as isValidElement2 } from "react";
2159
2271
  import { identity as identity5 } from "transformation-matrix";
2160
- var Project = class {
2272
+ var Circuit = class {
2161
2273
  rootComponent = null;
2162
2274
  children;
2163
2275
  db;
@@ -2237,10 +2349,11 @@ var Project = class {
2237
2349
  selectAll(selector) {
2238
2350
  return this.rootComponent?.selectAll(selector) ?? [];
2239
2351
  }
2240
- selectOne(selector) {
2241
- return this.rootComponent?.selectOne(selector) ?? null;
2352
+ selectOne(selector, opts) {
2353
+ return this.rootComponent?.selectOne(selector, opts) ?? null;
2242
2354
  }
2243
2355
  };
2356
+ var Project = Circuit;
2244
2357
 
2245
2358
  // lib/register-catalogue.ts
2246
2359
  extendCatalogue(components_exports);
@@ -2251,6 +2364,7 @@ export {
2251
2364
  Board,
2252
2365
  Capacitor,
2253
2366
  Chip,
2367
+ Circuit,
2254
2368
  Footprint,
2255
2369
  Group,
2256
2370
  Jumper,
package/lib/Project.ts CHANGED
@@ -6,7 +6,7 @@ import { isValidElement, type ReactElement } from "react"
6
6
  import { createInstanceFromReactElement } from "./fiber/create-instance-from-react-element"
7
7
  import { identity, type Matrix } from "transformation-matrix"
8
8
 
9
- export class Project {
9
+ export class Circuit {
10
10
  rootComponent: PrimitiveComponent | null = null
11
11
  children: PrimitiveComponent[]
12
12
  db: SoupUtilObjects
@@ -115,7 +115,12 @@ export class Project {
115
115
  return this.rootComponent?.selectAll(selector) ?? []
116
116
  }
117
117
 
118
- selectOne(selector: string): PrimitiveComponent | null {
119
- return this.rootComponent?.selectOne(selector) ?? null
118
+ selectOne(
119
+ selector: string,
120
+ opts: { type?: "component" | "port" },
121
+ ): PrimitiveComponent | null {
122
+ return this.rootComponent?.selectOne(selector, opts) ?? null
120
123
  }
121
124
  }
125
+
126
+ export const Project = Circuit
@@ -197,7 +197,7 @@ export class NormalComponent<
197
197
  width: 0,
198
198
  height: 0,
199
199
  layer: props.layer ?? "top",
200
- rotation: props.rotation ?? 0,
200
+ rotation: props.pcbRotation ?? 0,
201
201
  source_component_id: this.source_component_id!,
202
202
  })
203
203
  this.pcb_component_id = pcb_component.pcb_component_id
@@ -1,5 +1,5 @@
1
1
  import type { AnySoupElement, AnySourceComponent } from "@tscircuit/soup"
2
- import type { Project } from "../../Project"
2
+ import type { Circuit } from "../../Project"
3
3
  import type { ZodType } from "zod"
4
4
  import { z } from "zod"
5
5
  import { symbols, type SchSymbol, type BaseSymbolName } from "schematic-symbols"
@@ -10,6 +10,7 @@ import {
10
10
  applyToPoint,
11
11
  compose,
12
12
  identity,
13
+ rotate,
13
14
  translate,
14
15
  type Matrix,
15
16
  } from "transformation-matrix"
@@ -39,7 +40,7 @@ export abstract class PrimitiveComponent<
39
40
  }
40
41
  }
41
42
 
42
- project: Project | null = null
43
+ project: Circuit | null = null
43
44
  props: z.input<ZodProps>
44
45
  _parsedProps: z.infer<ZodProps>
45
46
 
@@ -48,6 +49,21 @@ export abstract class PrimitiveComponent<
48
49
 
49
50
  externallyAddedAliases: string[]
50
51
 
52
+ /**
53
+ * An opaque group is a self-contained subcircuit. All the selectors inside
54
+ * an opaque group are relative to the group. You can have multiple opaque
55
+ * groups and their selectors will not interact with each other (even if the
56
+ * components share the same names) unless you explicitly break out some ports
57
+ */
58
+ get isOpaqueGroup() {
59
+ return (
60
+ Boolean(this.props.opaque) ||
61
+ // Implied opaque group for top-level group
62
+ (this.lowercaseComponentName === "group" &&
63
+ this?.parent?.props?.name === "$root")
64
+ )
65
+ }
66
+
51
67
  source_group_id: string | null = null
52
68
  source_component_id: string | null = null
53
69
  schematic_component_id: string | null = null
@@ -69,7 +85,7 @@ export abstract class PrimitiveComponent<
69
85
  }
70
86
  }
71
87
 
72
- setProject(project: Project) {
88
+ setProject(project: Circuit) {
73
89
  this.project = project
74
90
  for (const c of this.children) {
75
91
  c.setProject(project)
@@ -97,8 +113,14 @@ export abstract class PrimitiveComponent<
97
113
  * components
98
114
  */
99
115
  computePcbPropsTransform(): Matrix {
100
- // TODO rotations
101
- return translate(this.props.pcbX ?? 0, this.props.pcbY ?? 0)
116
+ const { _parsedProps: props } = this
117
+
118
+ const matrix = compose(
119
+ translate(props.pcbX ?? 0, props.pcbY ?? 0),
120
+ rotate(((props.pcbRotation ?? 0) * Math.PI) / 180),
121
+ )
122
+
123
+ return matrix
102
124
  }
103
125
 
104
126
  /**
@@ -190,6 +212,27 @@ export abstract class PrimitiveComponent<
190
212
  component.shouldBeRemoved = true
191
213
  }
192
214
 
215
+ getOpaqueGroupSelector(): string {
216
+ const name = this._parsedProps.name
217
+ const endPart = name
218
+ ? `${this.lowercaseComponentName}.${name}`
219
+ : this.lowercaseComponentName
220
+
221
+ if (!this.parent) return endPart
222
+ if (this.parent.isOpaqueGroup) return endPart
223
+ return `${this.parent.getOpaqueGroupSelector()} > ${endPart}`
224
+ }
225
+
226
+ getFullPathSelector(): string {
227
+ const name = this._parsedProps.name
228
+ const endPart = name
229
+ ? `${this.lowercaseComponentName}.${name}`
230
+ : this.lowercaseComponentName
231
+ const parentSelector = this.parent?.getFullPathSelector?.()
232
+ if (!parentSelector) return endPart
233
+ return `${parentSelector} > ${endPart}`
234
+ }
235
+
193
236
  getNameAndAliases(): string[] {
194
237
  return [
195
238
  this._parsedProps.name,
@@ -223,6 +266,14 @@ export abstract class PrimitiveComponent<
223
266
  return false
224
267
  }
225
268
 
269
+ getOpaqueGroup(): PrimitiveComponent {
270
+ if (this.isOpaqueGroup) return this
271
+ const group = this.parent?.getOpaqueGroup()
272
+ if (!group)
273
+ throw new Error("Component is not inside an opaque group (no board?)")
274
+ return group
275
+ }
276
+
226
277
  selectAll(selector: string): PrimitiveComponent[] {
227
278
  const parts = selector.trim().split(/\s+/)
228
279
  let results: PrimitiveComponent[] = [this]
@@ -3,6 +3,7 @@ import { Component, createElement, type ReactElement } from "react"
3
3
 
4
4
  export const orderedRenderPhases = [
5
5
  "ReactSubtreesRender", // probably going to be removed b/c subtrees should render instantly
6
+ "CreateTracesFromProps",
6
7
  "SourceRender",
7
8
  "SourceParentAttachment",
8
9
  "PortDiscovery", // probably going to be removed b/c port discovery can always be done on prop change
@@ -6,6 +6,10 @@ import { identity, type Matrix } from "transformation-matrix"
6
6
  export class Board extends NormalComponent<typeof boardProps> {
7
7
  pcb_board_id: string | null = null
8
8
 
9
+ get isOpaqueGroup() {
10
+ return true
11
+ }
12
+
9
13
  get config() {
10
14
  return {
11
15
  zodProps: boardProps,
@@ -1,7 +1,8 @@
1
- import { ledProps } from "@tscircuit/props"
2
- import { PrimitiveComponent } from "../base-components/PrimitiveComponent"
1
+ import { capacitorProps, ledProps } from "@tscircuit/props"
3
2
  import { FTYPE, SYMBOL } from "lib/utils/constants"
4
3
  import { NormalComponent } from "../base-components/NormalComponent"
4
+ import type { capacitance, SourceSimpleCapacitorInput } from "@tscircuit/soup"
5
+ import { Trace } from "../primitive-components/Trace"
5
6
 
6
7
  type PortNames =
7
8
  | "1"
@@ -13,7 +14,10 @@ type PortNames =
13
14
  | "anode"
14
15
  | "cathode"
15
16
 
16
- export class Capacitor extends NormalComponent<typeof ledProps, PortNames> {
17
+ export class Capacitor extends NormalComponent<
18
+ typeof capacitorProps,
19
+ PortNames
20
+ > {
17
21
  get config() {
18
22
  return {
19
23
  // schematicSymbolName: BASE_SYMBOLS.capacitor,
@@ -21,4 +25,39 @@ export class Capacitor extends NormalComponent<typeof ledProps, PortNames> {
21
25
  sourceFtype: FTYPE.simple_capacitor,
22
26
  }
23
27
  }
28
+
29
+ pin1 = this.portMap.pin1
30
+ pin2 = this.portMap.pin2
31
+
32
+ doInitialCreateTracesFromProps() {
33
+ if (this.props.decouplingFor && this.props.decouplingTo) {
34
+ this.add(
35
+ new Trace({
36
+ from: `${this.getOpaqueGroupSelector()} > port.1`,
37
+ to: this.props.decouplingFor,
38
+ }),
39
+ )
40
+ this.add(
41
+ new Trace({
42
+ from: `${this.getOpaqueGroupSelector()} > port.2`,
43
+ to: this.props.decouplingTo,
44
+ }),
45
+ )
46
+ }
47
+ }
48
+
49
+ doInitialSourceRender() {
50
+ const { db } = this.project!
51
+ const { _parsedProps: props } = this
52
+ const source_component = db.source_component.insert({
53
+ ftype: "simple_capacitor",
54
+ name: props.name,
55
+ // @ts-ignore
56
+ manufacturer_part_number: props.manufacturerPartNumber ?? props.mfn,
57
+ supplier_part_numbers: props.supplierPartNumbers,
58
+
59
+ capacitance: props.capacitance,
60
+ } as SourceSimpleCapacitorInput)
61
+ this.source_component_id = source_component.source_component_id
62
+ }
24
63
  }
@@ -2,6 +2,8 @@ import { resistorProps } from "@tscircuit/props"
2
2
  import type { PassivePorts, Ftype, BaseSymbolName } from "lib/utils/constants"
3
3
  import { NormalComponent } from "../base-components/NormalComponent"
4
4
  import type { SourceSimpleResistorInput } from "@tscircuit/soup"
5
+ import { z } from "zod"
6
+ import { Trace } from "../primitive-components/Trace"
5
7
 
6
8
  export class Resistor extends NormalComponent<
7
9
  typeof resistorProps,
@@ -18,6 +20,22 @@ export class Resistor extends NormalComponent<
18
20
  pin1 = this.portMap.pin1
19
21
  pin2 = this.portMap.pin2
20
22
 
23
+ doInitialCreateTracesFromProps() {
24
+ if (this.props.pullupFor && this.props.pullupTo) {
25
+ this.add(
26
+ new Trace({
27
+ from: `${this.getOpaqueGroupSelector()} > port.1`,
28
+ to: this.props.pullupFor,
29
+ }),
30
+ )
31
+ this.add(
32
+ new Trace({
33
+ from: `${this.getOpaqueGroupSelector()} > port.2`,
34
+ to: this.props.pullupTo,
35
+ }),
36
+ )
37
+ }
38
+ }
21
39
  doInitialSourceRender() {
22
40
  const { db } = this.project!
23
41
  const { _parsedProps: props } = this
@@ -1,5 +1,6 @@
1
1
  import { silkscreenPathProps } from "@tscircuit/props"
2
2
  import { PrimitiveComponent } from "../base-components/PrimitiveComponent"
3
+ import { applyToPoint } from "transformation-matrix"
3
4
 
4
5
  export class SilkscreenPath extends PrimitiveComponent<
5
6
  typeof silkscreenPathProps
@@ -23,10 +24,22 @@ export class SilkscreenPath extends PrimitiveComponent<
23
24
  )
24
25
  }
25
26
 
27
+ const transform = this.computePcbGlobalTransform()
28
+
26
29
  const pcb_silkscreen_path = db.pcb_silkscreen_path.insert({
27
30
  pcb_component_id: this.parent?.pcb_component_id!,
28
31
  layer,
29
- route: props.route,
32
+ route: props.route.map((p) => {
33
+ const transformedPosition = applyToPoint(transform, {
34
+ x: p.x,
35
+ y: p.y,
36
+ })
37
+ return {
38
+ ...p,
39
+ x: transformedPosition.x,
40
+ y: transformedPosition.y,
41
+ }
42
+ }),
30
43
  stroke_width: props.strokeWidth ?? 0.1,
31
44
  })
32
45
 
@@ -3,6 +3,7 @@ import { smtPadProps } from "@tscircuit/props"
3
3
  import type { Port } from "./Port"
4
4
  import type { RenderPhaseFn } from "../base-components/Renderable"
5
5
  import type { PCBSMTPad } from "@tscircuit/soup"
6
+ import { decomposeTSR } from "transformation-matrix"
6
7
 
7
8
  export class SmtPad extends PrimitiveComponent<typeof smtPadProps> {
8
9
  pcb_smtpad_id: string | null = null
@@ -40,6 +41,9 @@ export class SmtPad extends PrimitiveComponent<typeof smtPadProps> {
40
41
  const { _parsedProps: props } = this
41
42
  if (!props.portHints) return
42
43
  const position = this.getGlobalPcbPosition()
44
+ const decomposedMat = decomposeTSR(this.computePcbGlobalTransform())
45
+ const isRotated90 =
46
+ Math.abs(decomposedMat.rotation.angle * (180 / Math.PI) - 90) < 0.01
43
47
  let pcb_smtpad: PCBSMTPad | null = null
44
48
  if (props.shape === "circle") {
45
49
  pcb_smtpad = db.pcb_smtpad.insert({
@@ -63,9 +67,9 @@ export class SmtPad extends PrimitiveComponent<typeof smtPadProps> {
63
67
  layer: props.layer ?? "top",
64
68
  shape: "rect",
65
69
 
66
- // @ts-ignore: no idea why this is triggering
67
- width: props.width,
68
- height: props.height,
70
+ ...(isRotated90
71
+ ? { width: props.height, height: props.width }
72
+ : { width: props.width, height: props.height }),
69
73
 
70
74
  port_hints: props.portHints.map((ph) => ph.toString()),
71
75
 
@@ -79,21 +79,25 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
79
79
 
80
80
  const ports = portSelectors.map((selector) => ({
81
81
  selector,
82
- port: parent.selectOne(selector, { type: "port" }) as Port,
82
+ port:
83
+ (this.getOpaqueGroup().selectOne(selector, { type: "port" }) as Port) ??
84
+ null,
83
85
  }))
84
86
 
85
87
  for (const { selector, port } of ports) {
86
88
  if (!port) {
87
89
  const parentSelector = selector.replace(/\>.*$/, "")
88
- const targetComponent = parent.selectOne(parentSelector)
90
+ const targetComponent = this.getOpaqueGroup().selectOne(parentSelector)
89
91
  if (!targetComponent) {
90
92
  this.renderError(`Could not find port for selector "${selector}"`)
91
93
  } else {
92
94
  this.renderError(
93
- `Could not find port for selector "${selector}"\nsearched component ${targetComponent.getString()}, which has ports:${targetComponent.children
95
+ `Could not find port for selector "${selector}"\nsearched component ${targetComponent.getString()}, which has ports: ${targetComponent.children
94
96
  .filter((c) => c.componentName === "Port")
95
- .map((c) => ` ${c.getString()}`)
96
- .join("\n")}`,
97
+ .map(
98
+ (c) => `${c.getString()}(${c.getNameAndAliases().join(",")})`,
99
+ )
100
+ .join(" & ")}`,
97
101
  )
98
102
  }
99
103
  }
@@ -173,6 +177,14 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
173
177
  const { solution } = autoroute(pcbElements.concat([source_trace]))
174
178
  // TODO for some reason, the solution gets duplicated inside ijump-astar
175
179
  const inputPcbTrace = solution[0]
180
+
181
+ if (!inputPcbTrace) {
182
+ // TODO render error indicating we could not find a route
183
+ console.log(
184
+ `Failed to find route ffrom ${ports[0].port} to ${ports[1].port} render error!`,
185
+ )
186
+ return
187
+ }
176
188
  const pcb_trace = db.pcb_trace.insert(inputPcbTrace as any)
177
189
 
178
190
  this.pcb_trace_id = pcb_trace.pcb_trace_id
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@tscircuit/core",
3
3
  "module": "index.ts",
4
4
  "type": "module",
5
- "version": "0.0.18",
5
+ "version": "0.0.20",
6
6
  "types": "dist/index.d.ts",
7
7
  "main": "dist/index.js",
8
8
  "files": [
@@ -31,7 +31,7 @@
31
31
  "typescript": "^5.0.0"
32
32
  },
33
33
  "dependencies": {
34
- "@tscircuit/infgrid-ijump-astar": "0.0.5",
34
+ "@tscircuit/infgrid-ijump-astar": "^0.0.6",
35
35
  "@tscircuit/props": "^0.0.49",
36
36
  "@tscircuit/soup": "^0.0.58",
37
37
  "@tscircuit/soup-util": "0.0.18",