@tscircuit/core 0.0.32 → 0.0.34

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
@@ -3348,6 +3348,7 @@ declare class PlatedHole extends PrimitiveComponent<typeof platedHoleProps> {
3348
3348
  name: "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6";
3349
3349
  }>>;
3350
3350
  }, "pcbRotation" | "layer">, {
3351
+ name: zod.ZodOptional<zod.ZodString>;
3351
3352
  shape: zod.ZodLiteral<"circle">;
3352
3353
  holeDiameter: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
3353
3354
  outerDiameter: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
@@ -3358,6 +3359,7 @@ declare class PlatedHole extends PrimitiveComponent<typeof platedHoleProps> {
3358
3359
  outerDiameter: number;
3359
3360
  pcbX?: number | undefined;
3360
3361
  pcbY?: number | undefined;
3362
+ name?: string | undefined;
3361
3363
  portHints?: (string | number)[] | undefined;
3362
3364
  }, {
3363
3365
  shape: "circle";
@@ -3365,6 +3367,7 @@ declare class PlatedHole extends PrimitiveComponent<typeof platedHoleProps> {
3365
3367
  outerDiameter: string | number;
3366
3368
  pcbX?: string | number | undefined;
3367
3369
  pcbY?: string | number | undefined;
3370
+ name?: string | undefined;
3368
3371
  portHints?: (string | number)[] | undefined;
3369
3372
  }>, zod.ZodObject<zod.objectUtil.extendShape<Omit<{
3370
3373
  pcbX: zod.ZodOptional<zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>>;
@@ -3380,6 +3383,7 @@ declare class PlatedHole extends PrimitiveComponent<typeof platedHoleProps> {
3380
3383
  name: "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6";
3381
3384
  }>>;
3382
3385
  }, "pcbRotation" | "layer">, {
3386
+ name: zod.ZodOptional<zod.ZodString>;
3383
3387
  shape: zod.ZodLiteral<"oval">;
3384
3388
  outerWidth: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
3385
3389
  outerHeight: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
@@ -3394,6 +3398,7 @@ declare class PlatedHole extends PrimitiveComponent<typeof platedHoleProps> {
3394
3398
  innerHeight: number;
3395
3399
  pcbX?: number | undefined;
3396
3400
  pcbY?: number | undefined;
3401
+ name?: string | undefined;
3397
3402
  portHints?: (string | number)[] | undefined;
3398
3403
  }, {
3399
3404
  shape: "oval";
@@ -3403,6 +3408,7 @@ declare class PlatedHole extends PrimitiveComponent<typeof platedHoleProps> {
3403
3408
  innerHeight: string | number;
3404
3409
  pcbX?: string | number | undefined;
3405
3410
  pcbY?: string | number | undefined;
3411
+ name?: string | undefined;
3406
3412
  portHints?: (string | number)[] | undefined;
3407
3413
  }>]>;
3408
3414
  };
@@ -3410,84 +3416,98 @@ declare class PlatedHole extends PrimitiveComponent<typeof platedHoleProps> {
3410
3416
  width: number;
3411
3417
  height: number;
3412
3418
  };
3419
+ _getCircuitJsonBounds(): {
3420
+ center: {
3421
+ x: number;
3422
+ y: number;
3423
+ };
3424
+ bounds: {
3425
+ left: number;
3426
+ top: number;
3427
+ right: number;
3428
+ bottom: number;
3429
+ };
3430
+ width: number;
3431
+ height: number;
3432
+ };
3433
+ _setPositionFromLayout(newCenter: {
3434
+ x: number;
3435
+ y: number;
3436
+ }): void;
3413
3437
  doInitialPortMatching(): void;
3414
3438
  doInitialPcbPrimitiveRender(): void;
3415
3439
  }
3416
3440
 
3441
+ declare const edgeSpecifiers: readonly ["leftedge", "rightedge", "topedge", "bottomedge", "center"];
3442
+ type EdgeSpecifier = (typeof edgeSpecifiers)[number];
3417
3443
  declare class Constraint extends PrimitiveComponent<typeof constraintProps> {
3418
3444
  get config(): {
3419
3445
  zodProps: z.ZodUnion<[z.ZodObject<{
3420
3446
  pcb: z.ZodOptional<z.ZodLiteral<true>>;
3421
- type: z.ZodOptional<z.ZodLiteral<"xdist">>;
3422
- xdist: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
3447
+ xDist: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
3423
3448
  left: z.ZodString;
3424
3449
  right: z.ZodString;
3425
3450
  edgeToEdge: z.ZodOptional<z.ZodLiteral<true>>;
3426
3451
  centerToCenter: z.ZodOptional<z.ZodLiteral<true>>;
3427
- fromLeftEdge: z.ZodOptional<z.ZodLiteral<true>>;
3428
- fromLeftCenter: z.ZodOptional<z.ZodLiteral<true>>;
3429
- toRightEdge: z.ZodOptional<z.ZodLiteral<true>>;
3430
- toRightCenter: z.ZodOptional<z.ZodLiteral<true>>;
3431
3452
  }, "strip", z.ZodTypeAny, {
3432
3453
  left: string;
3433
3454
  right: string;
3434
- xdist: number;
3435
- type?: "xdist" | undefined;
3455
+ xDist: number;
3436
3456
  pcb?: true | undefined;
3437
3457
  edgeToEdge?: true | undefined;
3438
3458
  centerToCenter?: true | undefined;
3439
- fromLeftEdge?: true | undefined;
3440
- fromLeftCenter?: true | undefined;
3441
- toRightEdge?: true | undefined;
3442
- toRightCenter?: true | undefined;
3443
3459
  }, {
3444
3460
  left: string;
3445
3461
  right: string;
3446
- xdist: string | number;
3447
- type?: "xdist" | undefined;
3462
+ xDist: string | number;
3448
3463
  pcb?: true | undefined;
3449
3464
  edgeToEdge?: true | undefined;
3450
3465
  centerToCenter?: true | undefined;
3451
- fromLeftEdge?: true | undefined;
3452
- fromLeftCenter?: true | undefined;
3453
- toRightEdge?: true | undefined;
3454
- toRightCenter?: true | undefined;
3455
3466
  }>, z.ZodObject<{
3456
3467
  pcb: z.ZodOptional<z.ZodLiteral<true>>;
3457
- type: z.ZodOptional<z.ZodLiteral<"ydist">>;
3458
- ydist: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
3468
+ yDist: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodNumber]>, number, string | number>;
3459
3469
  top: z.ZodString;
3460
3470
  bottom: z.ZodString;
3461
3471
  edgeToEdge: z.ZodOptional<z.ZodLiteral<true>>;
3462
3472
  centerToCenter: z.ZodOptional<z.ZodLiteral<true>>;
3463
- fromTopEdge: z.ZodOptional<z.ZodLiteral<true>>;
3464
- fromTopCenter: z.ZodOptional<z.ZodLiteral<true>>;
3465
- toBottomEdge: z.ZodOptional<z.ZodLiteral<true>>;
3466
- toBottomCenter: z.ZodOptional<z.ZodLiteral<true>>;
3467
3473
  }, "strip", z.ZodTypeAny, {
3468
3474
  top: string;
3469
3475
  bottom: string;
3470
- ydist: number;
3471
- type?: "ydist" | undefined;
3476
+ yDist: number;
3472
3477
  pcb?: true | undefined;
3473
3478
  edgeToEdge?: true | undefined;
3474
3479
  centerToCenter?: true | undefined;
3475
- fromTopEdge?: true | undefined;
3476
- fromTopCenter?: true | undefined;
3477
- toBottomEdge?: true | undefined;
3478
- toBottomCenter?: true | undefined;
3479
3480
  }, {
3480
3481
  top: string;
3481
3482
  bottom: string;
3482
- ydist: string | number;
3483
- type?: "ydist" | undefined;
3483
+ yDist: string | number;
3484
3484
  pcb?: true | undefined;
3485
3485
  edgeToEdge?: true | undefined;
3486
3486
  centerToCenter?: true | undefined;
3487
- fromTopEdge?: true | undefined;
3488
- fromTopCenter?: true | undefined;
3489
- toBottomEdge?: true | undefined;
3490
- toBottomCenter?: true | undefined;
3487
+ }>, z.ZodObject<{
3488
+ pcb: z.ZodOptional<z.ZodLiteral<true>>;
3489
+ sameY: z.ZodOptional<z.ZodLiteral<true>>;
3490
+ for: z.ZodArray<z.ZodString, "many">;
3491
+ }, "strip", z.ZodTypeAny, {
3492
+ for: string[];
3493
+ pcb?: true | undefined;
3494
+ sameY?: true | undefined;
3495
+ }, {
3496
+ for: string[];
3497
+ pcb?: true | undefined;
3498
+ sameY?: true | undefined;
3499
+ }>, z.ZodObject<{
3500
+ pcb: z.ZodOptional<z.ZodLiteral<true>>;
3501
+ sameX: z.ZodOptional<z.ZodLiteral<true>>;
3502
+ for: z.ZodArray<z.ZodString, "many">;
3503
+ }, "strip", z.ZodTypeAny, {
3504
+ for: string[];
3505
+ pcb?: true | undefined;
3506
+ sameX?: true | undefined;
3507
+ }, {
3508
+ for: string[];
3509
+ pcb?: true | undefined;
3510
+ sameX?: true | undefined;
3491
3511
  }>]>;
3492
3512
  };
3493
3513
  constructor(props: z.input<typeof constraintProps>);
@@ -3495,6 +3515,8 @@ declare class Constraint extends PrimitiveComponent<typeof constraintProps> {
3495
3515
  componentsWithSelectors: Array<{
3496
3516
  component: PrimitiveComponent<any>;
3497
3517
  selector: string;
3518
+ componentSelector: string;
3519
+ edge: EdgeSpecifier | undefined;
3498
3520
  }>;
3499
3521
  };
3500
3522
  }
@@ -3503,7 +3525,7 @@ declare class Hole extends PrimitiveComponent<typeof holeProps> {
3503
3525
  pcb_hole_id: string | null;
3504
3526
  isPcbPrimitive: boolean;
3505
3527
  get config(): {
3506
- zodProps: zod.ZodObject<zod.objectUtil.extendShape<Omit<{
3528
+ zodProps: zod.ZodEffects<zod.ZodObject<zod.objectUtil.extendShape<Omit<{
3507
3529
  pcbX: zod.ZodOptional<zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>>;
3508
3530
  pcbY: zod.ZodOptional<zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>>;
3509
3531
  pcbRotation: zod.ZodOptional<zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>>;
@@ -3517,19 +3539,41 @@ declare class Hole extends PrimitiveComponent<typeof holeProps> {
3517
3539
  name: "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6";
3518
3540
  }>>;
3519
3541
  }, "pcbRotation">, {
3520
- holeDiameter: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
3542
+ name: zod.ZodOptional<zod.ZodString>;
3543
+ diameter: zod.ZodOptional<zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>>;
3544
+ radius: zod.ZodOptional<zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>>;
3521
3545
  }>, "strip", zod.ZodTypeAny, {
3522
- holeDiameter: number;
3523
3546
  pcbX?: number | undefined;
3524
3547
  pcbY?: number | undefined;
3525
3548
  layer?: "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6" | undefined;
3549
+ name?: string | undefined;
3550
+ radius?: number | undefined;
3551
+ diameter?: number | undefined;
3552
+ }, {
3553
+ pcbX?: string | number | undefined;
3554
+ pcbY?: string | number | undefined;
3555
+ layer?: "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6" | {
3556
+ name: "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6";
3557
+ } | undefined;
3558
+ name?: string | undefined;
3559
+ radius?: string | number | undefined;
3560
+ diameter?: string | number | undefined;
3561
+ }>, {
3562
+ diameter: number;
3563
+ radius: number;
3564
+ pcbX?: number | undefined;
3565
+ pcbY?: number | undefined;
3566
+ layer?: "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6" | undefined;
3567
+ name?: string | undefined;
3526
3568
  }, {
3527
- holeDiameter: string | number;
3528
3569
  pcbX?: string | number | undefined;
3529
3570
  pcbY?: string | number | undefined;
3530
3571
  layer?: "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6" | {
3531
3572
  name: "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6";
3532
3573
  } | undefined;
3574
+ name?: string | undefined;
3575
+ radius?: string | number | undefined;
3576
+ diameter?: string | number | undefined;
3533
3577
  }>;
3534
3578
  };
3535
3579
  getPcbSize(): {
@@ -3537,6 +3581,24 @@ declare class Hole extends PrimitiveComponent<typeof holeProps> {
3537
3581
  height: number;
3538
3582
  };
3539
3583
  doInitialPcbPrimitiveRender(): void;
3584
+ _getCircuitJsonBounds(): {
3585
+ center: {
3586
+ x: number;
3587
+ y: number;
3588
+ };
3589
+ bounds: {
3590
+ left: number;
3591
+ top: number;
3592
+ right: number;
3593
+ bottom: number;
3594
+ };
3595
+ width: number;
3596
+ height: number;
3597
+ };
3598
+ _setPositionFromLayout(newCenter: {
3599
+ x: number;
3600
+ y: number;
3601
+ }): void;
3540
3602
  }
3541
3603
 
3542
3604
  declare global {
package/dist/index.js CHANGED
@@ -522,11 +522,18 @@ var Footprint = class extends PrimitiveComponent {
522
522
  if (constraints.length === 0) return;
523
523
  const involvedComponents = constraints.flatMap(
524
524
  (constraint) => constraint._getAllReferencedComponents().componentsWithSelectors
525
- ).map(({ component, selector }) => ({
525
+ ).map(({ component, selector, componentSelector, edge }) => ({
526
526
  component,
527
527
  selector,
528
+ componentSelector,
529
+ edge,
528
530
  bounds: component._getCircuitJsonBounds()
529
531
  }));
532
+ if (involvedComponents.some((c) => c.edge)) {
533
+ throw new Error(
534
+ "edge constraints not implemented yet for footprint layout, contributions welcome!"
535
+ );
536
+ }
530
537
  function getComponentDetails(selector) {
531
538
  return involvedComponents.find(({ selector: s }) => s === selector);
532
539
  }
@@ -535,21 +542,20 @@ var Footprint = class extends PrimitiveComponent {
535
542
  function getKVar(name) {
536
543
  if (!(name in kVars)) {
537
544
  kVars[name] = new kiwi.Variable(name);
545
+ solver.addEditVariable(kVars[name], kiwi.Strength.weak);
538
546
  }
539
547
  return kVars[name];
540
548
  }
541
- for (const { selector, bounds } of involvedComponents) {
549
+ for (const { selector, bounds: bounds2 } of involvedComponents) {
542
550
  const kvx = getKVar(`${selector}_x`);
543
551
  const kvy = getKVar(`${selector}_y`);
544
- solver.addEditVariable(kvx, kiwi.Strength.weak);
545
- solver.addEditVariable(kvy, kiwi.Strength.weak);
546
- solver.suggestValue(kvx, bounds.center.x);
547
- solver.suggestValue(kvy, bounds.center.y);
552
+ solver.suggestValue(kvx, bounds2.center.x);
553
+ solver.suggestValue(kvy, bounds2.center.y);
548
554
  }
549
555
  for (const constraint of constraints) {
550
556
  const props = constraint._parsedProps;
551
- if ("xdist" in props) {
552
- const { xdist, left, right, edgeToEdge, centerToCenter } = props;
557
+ if ("xDist" in props) {
558
+ const { xDist, left, right, edgeToEdge, centerToCenter } = props;
553
559
  const leftVar = getKVar(`${left}_x`);
554
560
  const rightVar = getKVar(`${right}_x`);
555
561
  const leftBounds = getComponentDetails(left)?.bounds;
@@ -560,14 +566,14 @@ var Footprint = class extends PrimitiveComponent {
560
566
  new kiwi.Constraint(
561
567
  expr,
562
568
  kiwi.Operator.Eq,
563
- props.xdist,
569
+ props.xDist,
564
570
  kiwi.Strength.required
565
571
  )
566
572
  );
567
573
  } else if (edgeToEdge) {
568
574
  const expr = new kiwi.Expression(
569
575
  rightVar,
570
- rightBounds.width / 2,
576
+ -rightBounds.width / 2,
571
577
  [-1, leftVar],
572
578
  -leftBounds.width / 2
573
579
  );
@@ -575,55 +581,113 @@ var Footprint = class extends PrimitiveComponent {
575
581
  new kiwi.Constraint(
576
582
  expr,
577
583
  kiwi.Operator.Eq,
578
- props.xdist,
584
+ props.xDist,
579
585
  kiwi.Strength.required
580
586
  )
581
587
  );
582
588
  }
583
- }
584
- solver.updateVariables();
585
- if (debug.enabled) {
586
- console.log("Solution to layout constraints:");
587
- console.table(
588
- Object.entries(kVars).map(([key, kvar]) => ({
589
- var: key,
590
- val: kvar.value()
591
- }))
589
+ } else if ("yDist" in props) {
590
+ const { yDist, top, bottom, edgeToEdge, centerToCenter } = props;
591
+ const topVar = getKVar(`${top}_y`);
592
+ const bottomVar = getKVar(`${bottom}_y`);
593
+ const topBounds = getComponentDetails(top)?.bounds;
594
+ const bottomBounds = getComponentDetails(bottom)?.bounds;
595
+ if (centerToCenter) {
596
+ const expr = new kiwi.Expression(topVar, [-1, bottomVar]);
597
+ solver.addConstraint(
598
+ new kiwi.Constraint(
599
+ expr,
600
+ kiwi.Operator.Eq,
601
+ props.yDist,
602
+ kiwi.Strength.required
603
+ )
604
+ );
605
+ } else if (edgeToEdge) {
606
+ const expr = new kiwi.Expression(
607
+ topVar,
608
+ topBounds.height / 2,
609
+ [-1, bottomVar],
610
+ -bottomBounds.height / 2
611
+ );
612
+ solver.addConstraint(
613
+ new kiwi.Constraint(
614
+ expr,
615
+ kiwi.Operator.Eq,
616
+ props.yDist,
617
+ kiwi.Strength.required
618
+ )
619
+ );
620
+ }
621
+ } else if ("sameY" in props) {
622
+ const { for: selectors } = props;
623
+ if (selectors.length < 2) continue;
624
+ const vars = selectors.map((selector) => getKVar(`${selector}_y`));
625
+ const expr = new kiwi.Expression(...vars.slice(1));
626
+ solver.addConstraint(
627
+ new kiwi.Constraint(
628
+ expr,
629
+ kiwi.Operator.Eq,
630
+ vars[0],
631
+ kiwi.Strength.required
632
+ )
633
+ );
634
+ } else if ("sameX" in props) {
635
+ const { for: selectors } = props;
636
+ if (selectors.length < 2) continue;
637
+ const vars = selectors.map((selector) => getKVar(`${selector}_x`));
638
+ const expr = new kiwi.Expression(...vars.slice(1));
639
+ solver.addConstraint(
640
+ new kiwi.Constraint(
641
+ expr,
642
+ kiwi.Operator.Eq,
643
+ vars[0],
644
+ kiwi.Strength.required
645
+ )
592
646
  );
593
647
  }
594
- const bounds = {
595
- left: Infinity,
596
- right: -Infinity,
597
- top: -Infinity,
598
- bottom: Infinity
599
- };
600
- for (const {
601
- selector,
602
- bounds: { width, height }
603
- } of involvedComponents) {
604
- const kvx = getKVar(`${selector}_x`);
605
- const kvy = getKVar(`${selector}_y`);
606
- const newLeft = kvx.value() - width / 2;
607
- const newRight = kvx.value() + width / 2;
608
- const newTop = kvy.value() + height / 2;
609
- const newBottom = kvy.value() - height / 2;
610
- bounds.left = Math.min(bounds.left, newLeft);
611
- bounds.right = Math.max(bounds.right, newRight);
612
- bounds.top = Math.max(bounds.top, newTop);
613
- bounds.bottom = Math.min(bounds.bottom, newBottom);
614
- }
615
- const globalOffset = {
616
- x: -(bounds.right + bounds.left) / 2,
617
- y: -(bounds.top + bounds.bottom) / 2
618
- };
619
- for (const { component, selector } of involvedComponents) {
620
- const kvx = getKVar(`${selector}_x`);
621
- const kvy = getKVar(`${selector}_y`);
622
- component._setPositionFromLayout({
623
- x: kvx.value() + globalOffset.x,
624
- y: kvy.value() + globalOffset.y
625
- });
626
- }
648
+ }
649
+ solver.updateVariables();
650
+ if (debug.enabled) {
651
+ console.log("Solution to layout constraints:");
652
+ console.table(
653
+ Object.entries(kVars).map(([key, kvar]) => ({
654
+ var: key,
655
+ val: kvar.value()
656
+ }))
657
+ );
658
+ }
659
+ const bounds = {
660
+ left: Infinity,
661
+ right: -Infinity,
662
+ top: -Infinity,
663
+ bottom: Infinity
664
+ };
665
+ for (const {
666
+ selector,
667
+ bounds: { width, height }
668
+ } of involvedComponents) {
669
+ const kvx = getKVar(`${selector}_x`);
670
+ const kvy = getKVar(`${selector}_y`);
671
+ const newLeft = kvx.value() - width / 2;
672
+ const newRight = kvx.value() + width / 2;
673
+ const newTop = kvy.value() + height / 2;
674
+ const newBottom = kvy.value() - height / 2;
675
+ bounds.left = Math.min(bounds.left, newLeft);
676
+ bounds.right = Math.max(bounds.right, newRight);
677
+ bounds.top = Math.max(bounds.top, newTop);
678
+ bounds.bottom = Math.min(bounds.bottom, newBottom);
679
+ }
680
+ const globalOffset = {
681
+ x: -(bounds.right + bounds.left) / 2,
682
+ y: -(bounds.top + bounds.bottom) / 2
683
+ };
684
+ for (const { component, selector } of involvedComponents) {
685
+ const kvx = getKVar(`${selector}_x`);
686
+ const kvy = getKVar(`${selector}_y`);
687
+ component._setPositionFromLayout({
688
+ x: kvx.value() + globalOffset.x,
689
+ y: kvy.value() + globalOffset.y
690
+ });
627
691
  }
628
692
  }
629
693
  };
@@ -1164,6 +1228,29 @@ var PlatedHole = class extends PrimitiveComponent {
1164
1228
  `getPcbSize for shape "${props.shape}" not implemented for ${this.componentName}`
1165
1229
  );
1166
1230
  }
1231
+ _getCircuitJsonBounds() {
1232
+ const { db } = this.root;
1233
+ const platedHole = db.pcb_plated_hole.get(this.pcb_plated_hole_id);
1234
+ const size = this.getPcbSize();
1235
+ return {
1236
+ center: { x: platedHole.x, y: platedHole.y },
1237
+ bounds: {
1238
+ left: platedHole.x - size.width / 2,
1239
+ top: platedHole.y - size.height / 2,
1240
+ right: platedHole.x + size.width / 2,
1241
+ bottom: platedHole.y + size.height / 2
1242
+ },
1243
+ width: size.width,
1244
+ height: size.height
1245
+ };
1246
+ }
1247
+ _setPositionFromLayout(newCenter) {
1248
+ const { db } = this.root;
1249
+ db.pcb_plated_hole.update(this.pcb_plated_hole_id, {
1250
+ x: newCenter.x,
1251
+ y: newCenter.y
1252
+ });
1253
+ }
1167
1254
  doInitialPortMatching() {
1168
1255
  const parentPorts = (this.parent?.children ?? []).filter(
1169
1256
  (c) => c.componentName === "Port"
@@ -1182,22 +1269,21 @@ var PlatedHole = class extends PrimitiveComponent {
1182
1269
  doInitialPcbPrimitiveRender() {
1183
1270
  const { db } = this.root;
1184
1271
  const { _parsedProps: props } = this;
1185
- if (!props.portHints) return;
1186
1272
  const position = this._getGlobalPcbPositionBeforeLayout();
1187
1273
  if (props.shape === "circle") {
1188
- const plated_hole_input = {
1274
+ const pcb_plated_hole = db.pcb_plated_hole.insert({
1189
1275
  pcb_component_id: this.parent?.pcb_component_id,
1190
1276
  pcb_port_id: this.matchedPort?.pcb_port_id,
1191
- layers: ["top", "bottom"],
1277
+ // @ts-ignore - some issue with @tscircuit/soup union type
1192
1278
  outer_diameter: props.outerDiameter,
1193
1279
  hole_diameter: props.holeDiameter,
1194
1280
  shape: "circle",
1195
1281
  port_hints: this.getNameAndAliases(),
1196
1282
  x: position.x,
1197
1283
  y: position.y,
1198
- type: "pcb_plated_hole"
1199
- };
1200
- const pcb_plated_hole = db.pcb_plated_hole.insert(plated_hole_input);
1284
+ layers: ["top", "bottom"]
1285
+ });
1286
+ this.pcb_plated_hole_id = pcb_plated_hole.pcb_plated_hole_id;
1201
1287
  }
1202
1288
  }
1203
1289
  };
@@ -2888,6 +2974,13 @@ var Jumper = class extends NormalComponent {
2888
2974
  // lib/components/primitive-components/Constraint.ts
2889
2975
  import { constraintProps } from "@tscircuit/props";
2890
2976
  import "zod";
2977
+ var edgeSpecifiers = [
2978
+ "leftedge",
2979
+ "rightedge",
2980
+ "topedge",
2981
+ "bottomedge",
2982
+ "center"
2983
+ ];
2891
2984
  var Constraint2 = class extends PrimitiveComponent {
2892
2985
  get config() {
2893
2986
  return {
@@ -2899,23 +2992,38 @@ var Constraint2 = class extends PrimitiveComponent {
2899
2992
  if ("xdist" in props || "ydist" in props) {
2900
2993
  if (!("edgeToEdge" in props) && !("centerToCenter" in props)) {
2901
2994
  throw new Error(
2902
- "edgeToEdge, centerToCenter (or from*/to* props) must be set for xdist or ydist for <constraint />"
2995
+ `edgeToEdge, centerToCenter must be set for xDist or yDist for ${this}`
2903
2996
  );
2904
2997
  }
2905
2998
  }
2999
+ if ("for" in props && props.for.length < 2) {
3000
+ throw new Error(`"for" must have at least two selectors for ${this}`);
3001
+ }
2906
3002
  }
2907
3003
  _getAllReferencedComponents() {
2908
3004
  const componentsWithSelectors = [];
3005
+ const subcircuit = this.getSubcircuit();
3006
+ function addComponentFromSelector(selector) {
3007
+ const component = subcircuit.selectOne(selector);
3008
+ if (component) {
3009
+ const maybeEdge = selector.split(" ").pop();
3010
+ const edge = edgeSpecifiers.includes(maybeEdge) ? maybeEdge : void 0;
3011
+ componentsWithSelectors.push({
3012
+ selector,
3013
+ component,
3014
+ componentSelector: edge ? selector.replace(` ${edge}`, "") : selector,
3015
+ edge
3016
+ });
3017
+ }
3018
+ }
2909
3019
  for (const key of ["left", "right", "top", "bottom"]) {
2910
3020
  if (key in this._parsedProps) {
2911
- const selector = this._parsedProps[key];
2912
- const component = this.getSubcircuit().selectOne(selector);
2913
- if (component) {
2914
- componentsWithSelectors.push({
2915
- selector,
2916
- component
2917
- });
2918
- }
3021
+ addComponentFromSelector(this._parsedProps[key]);
3022
+ }
3023
+ }
3024
+ if ("for" in this._parsedProps) {
3025
+ for (const selector of this._parsedProps.for) {
3026
+ addComponentFromSelector(selector);
2919
3027
  }
2920
3028
  }
2921
3029
  return { componentsWithSelectors };
@@ -2934,20 +3042,43 @@ var Hole = class extends PrimitiveComponent {
2934
3042
  }
2935
3043
  getPcbSize() {
2936
3044
  const { _parsedProps: props } = this;
2937
- return { width: props.holeDiameter, height: props.holeDiameter };
3045
+ return { width: props.diameter, height: props.diameter };
2938
3046
  }
2939
3047
  doInitialPcbPrimitiveRender() {
2940
3048
  const { db } = this.root;
2941
3049
  const { _parsedProps: props } = this;
2942
3050
  const position = this._getGlobalPcbPositionBeforeLayout();
2943
- const pcb_hole = {
2944
- type: "pcb_hole",
3051
+ const inserted_hole = db.pcb_hole.insert({
2945
3052
  hole_shape: "round",
2946
- hole_diameter: props.holeDiameter,
3053
+ // @ts-ignore
3054
+ hole_diameter: props.diameter,
2947
3055
  x: position.x,
2948
3056
  y: position.y
3057
+ });
3058
+ this.pcb_hole_id = inserted_hole.pcb_hole_id;
3059
+ }
3060
+ _getCircuitJsonBounds() {
3061
+ const { db } = this.root;
3062
+ const hole = db.pcb_hole.get(this.pcb_hole_id);
3063
+ const size = this.getPcbSize();
3064
+ return {
3065
+ center: { x: hole.x, y: hole.y },
3066
+ bounds: {
3067
+ left: hole.x - size.width / 2,
3068
+ top: hole.y - size.height / 2,
3069
+ right: hole.x + size.width / 2,
3070
+ bottom: hole.y + size.height / 2
3071
+ },
3072
+ width: size.width,
3073
+ height: size.height
2949
3074
  };
2950
- const inserted_hole = db.pcb_hole.insert(pcb_hole);
3075
+ }
3076
+ _setPositionFromLayout(newCenter) {
3077
+ const { db } = this.root;
3078
+ db.pcb_hole.update(this.pcb_hole_id, {
3079
+ x: newCenter.x,
3080
+ y: newCenter.y
3081
+ });
2951
3082
  }
2952
3083
  };
2953
3084
 
@@ -2,6 +2,16 @@ import { constraintProps } from "@tscircuit/props"
2
2
  import { z } from "zod"
3
3
  import { PrimitiveComponent } from "../base-components/PrimitiveComponent"
4
4
 
5
+ const edgeSpecifiers = [
6
+ "leftedge",
7
+ "rightedge",
8
+ "topedge",
9
+ "bottomedge",
10
+ "center",
11
+ ] as const
12
+
13
+ export type EdgeSpecifier = (typeof edgeSpecifiers)[number]
14
+
5
15
  export class Constraint extends PrimitiveComponent<typeof constraintProps> {
6
16
  get config() {
7
17
  return {
@@ -13,34 +23,60 @@ export class Constraint extends PrimitiveComponent<typeof constraintProps> {
13
23
  super(props)
14
24
  if ("xdist" in props || "ydist" in props) {
15
25
  if (!("edgeToEdge" in props) && !("centerToCenter" in props)) {
26
+ // TODO don't throw an error if the selectors specify an edge
16
27
  throw new Error(
17
- "edgeToEdge, centerToCenter (or from*/to* props) must be set for xdist or ydist for <constraint />",
28
+ `edgeToEdge, centerToCenter must be set for xDist or yDist for ${this}`,
18
29
  )
19
30
  }
20
31
  }
32
+ if ("for" in props && props.for.length < 2) {
33
+ throw new Error(`"for" must have at least two selectors for ${this}`)
34
+ }
21
35
  }
22
36
 
23
37
  _getAllReferencedComponents(): {
24
38
  componentsWithSelectors: Array<{
25
39
  component: PrimitiveComponent<any>
26
40
  selector: string
41
+ componentSelector: string
42
+ edge: EdgeSpecifier | undefined
27
43
  }>
28
44
  } {
29
45
  const componentsWithSelectors: Array<{
30
46
  component: PrimitiveComponent<any>
31
47
  selector: string
48
+ componentSelector: string
49
+ edge: EdgeSpecifier | undefined
32
50
  }> = []
33
51
 
52
+ const subcircuit = this.getSubcircuit()
53
+
54
+ function addComponentFromSelector(selector: string) {
55
+ // TODO this selector has to be modified in case it contains a leftedge,
56
+ // center/topedge/rightedge indicator
57
+ const component = subcircuit.selectOne(selector)
58
+ if (component) {
59
+ const maybeEdge = selector.split(" ").pop() as EdgeSpecifier
60
+ const edge = edgeSpecifiers.includes(maybeEdge) ? maybeEdge : undefined
61
+
62
+ componentsWithSelectors.push({
63
+ selector,
64
+ component,
65
+ componentSelector: edge ? selector.replace(` ${edge}`, "") : selector,
66
+ edge,
67
+ })
68
+ }
69
+ }
70
+
34
71
  for (const key of ["left", "right", "top", "bottom"]) {
35
72
  if (key in this._parsedProps) {
36
- const selector = (this._parsedProps as any)[key] as string
37
- const component = this.getSubcircuit().selectOne(selector)
38
- if (component) {
39
- componentsWithSelectors.push({
40
- selector,
41
- component,
42
- })
43
- }
73
+ addComponentFromSelector((this._parsedProps as any)[key] as string)
74
+ }
75
+ }
76
+
77
+ if ("for" in this._parsedProps) {
78
+ for (const selector of this._parsedProps.for) {
79
+ addComponentFromSelector(selector)
44
80
  }
45
81
  }
46
82
 
@@ -23,12 +23,20 @@ export class Footprint extends PrimitiveComponent<typeof footprintProps> {
23
23
  (constraint) =>
24
24
  constraint._getAllReferencedComponents().componentsWithSelectors,
25
25
  )
26
- .map(({ component, selector }) => ({
26
+ .map(({ component, selector, componentSelector, edge }) => ({
27
27
  component,
28
28
  selector,
29
+ componentSelector,
30
+ edge,
29
31
  bounds: component._getCircuitJsonBounds(),
30
32
  }))
31
33
 
34
+ if (involvedComponents.some((c) => c.edge)) {
35
+ throw new Error(
36
+ "edge constraints not implemented yet for footprint layout, contributions welcome!",
37
+ )
38
+ }
39
+
32
40
  function getComponentDetails(selector: string) {
33
41
  return involvedComponents.find(({ selector: s }) => s === selector)
34
42
  }
@@ -39,6 +47,7 @@ export class Footprint extends PrimitiveComponent<typeof footprintProps> {
39
47
  function getKVar(name: string) {
40
48
  if (!(name in kVars)) {
41
49
  kVars[name] = new kiwi.Variable(name)
50
+ solver.addEditVariable(kVars[name], kiwi.Strength.weak)
42
51
  }
43
52
  return kVars[name]
44
53
  }
@@ -47,8 +56,6 @@ export class Footprint extends PrimitiveComponent<typeof footprintProps> {
47
56
  for (const { selector, bounds } of involvedComponents) {
48
57
  const kvx = getKVar(`${selector}_x`)
49
58
  const kvy = getKVar(`${selector}_y`)
50
- solver.addEditVariable(kvx, kiwi.Strength.weak)
51
- solver.addEditVariable(kvy, kiwi.Strength.weak)
52
59
  solver.suggestValue(kvx, bounds.center.x)
53
60
  solver.suggestValue(kvy, bounds.center.y)
54
61
  }
@@ -57,8 +64,8 @@ export class Footprint extends PrimitiveComponent<typeof footprintProps> {
57
64
  for (const constraint of constraints) {
58
65
  const props = constraint._parsedProps
59
66
 
60
- if ("xdist" in props) {
61
- const { xdist, left, right, edgeToEdge, centerToCenter } = props
67
+ if ("xDist" in props) {
68
+ const { xDist, left, right, edgeToEdge, centerToCenter } = props
62
69
  const leftVar = getKVar(`${left}_x`)
63
70
  const rightVar = getKVar(`${right}_x`)
64
71
  const leftBounds = getComponentDetails(left)?.bounds!
@@ -71,16 +78,16 @@ export class Footprint extends PrimitiveComponent<typeof footprintProps> {
71
78
  new kiwi.Constraint(
72
79
  expr,
73
80
  kiwi.Operator.Eq,
74
- props.xdist,
81
+ props.xDist,
75
82
  kiwi.Strength.required,
76
83
  ),
77
84
  )
78
85
  } else if (edgeToEdge) {
79
86
  // rightEdge - leftEdge = xdist
80
- // right + rightBounds.width/2 - left - leftBounds.width/2 = xdist
87
+ // right - rightBounds.width/2 - left - leftBounds.width/2 = xdist
81
88
  const expr = new kiwi.Expression(
82
89
  rightVar,
83
- rightBounds.width / 2,
90
+ -rightBounds.width / 2,
84
91
  [-1, leftVar],
85
92
  -leftBounds.width / 2,
86
93
  )
@@ -88,74 +95,139 @@ export class Footprint extends PrimitiveComponent<typeof footprintProps> {
88
95
  new kiwi.Constraint(
89
96
  expr,
90
97
  kiwi.Operator.Eq,
91
- props.xdist,
98
+ props.xDist,
92
99
  kiwi.Strength.required,
93
100
  ),
94
101
  )
95
102
  }
96
- }
103
+ } else if ("yDist" in props) {
104
+ const { yDist, top, bottom, edgeToEdge, centerToCenter } = props
105
+ const topVar = getKVar(`${top}_y`)
106
+ const bottomVar = getKVar(`${bottom}_y`)
107
+ const topBounds = getComponentDetails(top)?.bounds!
108
+ const bottomBounds = getComponentDetails(bottom)?.bounds!
109
+
110
+ // Top - Bottom = ydist
97
111
 
98
- // 3. Solve the system of equations
99
- solver.updateVariables()
100
- if (debug.enabled) {
101
- console.log("Solution to layout constraints:")
102
- console.table(
103
- Object.entries(kVars).map(([key, kvar]) => ({
104
- var: key,
105
- val: kvar.value(),
106
- })),
112
+ if (centerToCenter) {
113
+ // top - bottom = ydist
114
+ const expr = new kiwi.Expression(topVar, [-1, bottomVar])
115
+ solver.addConstraint(
116
+ new kiwi.Constraint(
117
+ expr,
118
+ kiwi.Operator.Eq,
119
+ props.yDist,
120
+ kiwi.Strength.required,
121
+ ),
122
+ )
123
+ } else if (edgeToEdge) {
124
+ // topElmBottomEdge - bottomElmTopEdge = ydist
125
+ // topElmCenterY - topElmHeight/2 - bottomElmCenterY - bottomElmHeight/2 = ydist
126
+ const expr = new kiwi.Expression(
127
+ topVar,
128
+ topBounds.height / 2,
129
+ [-1, bottomVar],
130
+ -bottomBounds.height / 2,
131
+ )
132
+ solver.addConstraint(
133
+ new kiwi.Constraint(
134
+ expr,
135
+ kiwi.Operator.Eq,
136
+ props.yDist,
137
+ kiwi.Strength.required,
138
+ ),
139
+ )
140
+ }
141
+ } else if ("sameY" in props) {
142
+ const { for: selectors } = props
143
+ if (selectors.length < 2) continue
144
+ const vars = selectors.map((selector) => getKVar(`${selector}_y`))
145
+ const expr = new kiwi.Expression(...vars.slice(1))
146
+
147
+ solver.addConstraint(
148
+ new kiwi.Constraint(
149
+ expr,
150
+ kiwi.Operator.Eq,
151
+ vars[0],
152
+ kiwi.Strength.required,
153
+ ),
154
+ )
155
+ } else if ("sameX" in props) {
156
+ const { for: selectors } = props
157
+ if (selectors.length < 2) continue
158
+ const vars = selectors.map((selector) => getKVar(`${selector}_x`))
159
+ const expr = new kiwi.Expression(...vars.slice(1))
160
+ solver.addConstraint(
161
+ new kiwi.Constraint(
162
+ expr,
163
+ kiwi.Operator.Eq,
164
+ vars[0],
165
+ kiwi.Strength.required,
166
+ ),
107
167
  )
108
168
  }
169
+ }
109
170
 
110
- // 3.1 Compute the global offset. There are different ways to do this:
111
- // - If any component has a fixed position, then that can be used as the
112
- // origin to determine the offset of all other components
113
- // - If no component has a fixed position, then we recenter everything
114
- // using the new bounds of all the involved components
115
-
116
- // TODO determine if there's a fixed component
117
-
118
- // Determine the new bounds all the involved components and compute the
119
- // bounds of this footprint
120
- const bounds = {
121
- left: Infinity,
122
- right: -Infinity,
123
- top: -Infinity,
124
- bottom: Infinity,
125
- }
126
- for (const {
127
- selector,
128
- bounds: { width, height },
129
- } of involvedComponents) {
130
- const kvx = getKVar(`${selector}_x`)
131
- const kvy = getKVar(`${selector}_y`)
132
-
133
- const newLeft = kvx.value() - width / 2
134
- const newRight = kvx.value() + width / 2
135
- const newTop = kvy.value() + height / 2
136
- const newBottom = kvy.value() - height / 2
137
-
138
- bounds.left = Math.min(bounds.left, newLeft)
139
- bounds.right = Math.max(bounds.right, newRight)
140
- bounds.top = Math.max(bounds.top, newTop)
141
- bounds.bottom = Math.min(bounds.bottom, newBottom)
142
- }
171
+ // 3. Solve the system of equations
172
+ solver.updateVariables()
173
+ if (debug.enabled) {
174
+ console.log("Solution to layout constraints:")
175
+ console.table(
176
+ Object.entries(kVars).map(([key, kvar]) => ({
177
+ var: key,
178
+ val: kvar.value(),
179
+ })),
180
+ )
181
+ }
143
182
 
144
- // Compute the global offset, we can use this to recenter each component
145
- const globalOffset = {
146
- x: -(bounds.right + bounds.left) / 2,
147
- y: -(bounds.top + bounds.bottom) / 2,
148
- }
183
+ // 3.1 Compute the global offset. There are different ways to do this:
184
+ // - If any component has a fixed position, then that can be used as the
185
+ // origin to determine the offset of all other components
186
+ // - If no component has a fixed position, then we recenter everything
187
+ // using the new bounds of all the involved components
188
+
189
+ // TODO determine if there's a fixed component
190
+
191
+ // Determine the new bounds all the involved components and compute the
192
+ // bounds of this footprint
193
+ const bounds = {
194
+ left: Infinity,
195
+ right: -Infinity,
196
+ top: -Infinity,
197
+ bottom: Infinity,
198
+ }
199
+ for (const {
200
+ selector,
201
+ bounds: { width, height },
202
+ } of involvedComponents) {
203
+ const kvx = getKVar(`${selector}_x`)
204
+ const kvy = getKVar(`${selector}_y`)
149
205
 
150
- // 4. Update the component positions
151
- for (const { component, selector } of involvedComponents) {
152
- const kvx = getKVar(`${selector}_x`)
153
- const kvy = getKVar(`${selector}_y`)
154
- component._setPositionFromLayout({
155
- x: kvx.value() + globalOffset.x,
156
- y: kvy.value() + globalOffset.y,
157
- })
158
- }
206
+ const newLeft = kvx.value() - width / 2
207
+ const newRight = kvx.value() + width / 2
208
+ const newTop = kvy.value() + height / 2
209
+ const newBottom = kvy.value() - height / 2
210
+
211
+ bounds.left = Math.min(bounds.left, newLeft)
212
+ bounds.right = Math.max(bounds.right, newRight)
213
+ bounds.top = Math.max(bounds.top, newTop)
214
+ bounds.bottom = Math.min(bounds.bottom, newBottom)
215
+ }
216
+
217
+ // Compute the global offset, we can use this to recenter each component
218
+ const globalOffset = {
219
+ x: -(bounds.right + bounds.left) / 2,
220
+ y: -(bounds.top + bounds.bottom) / 2,
221
+ }
222
+
223
+ // 4. Update the component positions
224
+ for (const { component, selector } of involvedComponents) {
225
+ const kvx = getKVar(`${selector}_x`)
226
+ const kvy = getKVar(`${selector}_y`)
227
+ component._setPositionFromLayout({
228
+ x: kvx.value() + globalOffset.x,
229
+ y: kvy.value() + globalOffset.y,
230
+ })
159
231
  }
160
232
  }
161
233
  }
@@ -14,7 +14,7 @@ export class Hole extends PrimitiveComponent<typeof holeProps> {
14
14
 
15
15
  getPcbSize(): { width: number; height: number } {
16
16
  const { _parsedProps: props } = this
17
- return { width: props.holeDiameter, height: props.holeDiameter }
17
+ return { width: props.diameter, height: props.diameter }
18
18
  }
19
19
 
20
20
  doInitialPcbPrimitiveRender(): void {
@@ -22,15 +22,44 @@ export class Hole extends PrimitiveComponent<typeof holeProps> {
22
22
  const { _parsedProps: props } = this
23
23
  const position = this._getGlobalPcbPositionBeforeLayout()
24
24
 
25
- const pcb_hole: PCBHole = {
26
- type: "pcb_hole",
25
+ const inserted_hole = db.pcb_hole.insert({
27
26
  hole_shape: "round",
28
- hole_diameter: props.holeDiameter,
27
+ // @ts-ignore
28
+ hole_diameter: props.diameter,
29
29
  x: position.x,
30
30
  y: position.y,
31
+ })
32
+ this.pcb_hole_id = inserted_hole.pcb_hole_id!
33
+ }
34
+
35
+ _getCircuitJsonBounds(): {
36
+ center: { x: number; y: number }
37
+ bounds: { left: number; top: number; right: number; bottom: number }
38
+ width: number
39
+ height: number
40
+ } {
41
+ const { db } = this.root!
42
+ const hole = db.pcb_hole.get(this.pcb_hole_id!)!
43
+ const size = this.getPcbSize()
44
+
45
+ return {
46
+ center: { x: hole.x, y: hole.y },
47
+ bounds: {
48
+ left: hole.x - size.width / 2,
49
+ top: hole.y - size.height / 2,
50
+ right: hole.x + size.width / 2,
51
+ bottom: hole.y + size.height / 2,
52
+ },
53
+ width: size.width,
54
+ height: size.height,
31
55
  }
56
+ }
32
57
 
33
- const inserted_hole = db.pcb_hole.insert(pcb_hole)
34
- // this.pcb_hole_id = inserted_hole.pcb_hole_id!
58
+ _setPositionFromLayout(newCenter: { x: number; y: number }) {
59
+ const { db } = this.root!
60
+ db.pcb_hole.update(this.pcb_hole_id!, {
61
+ x: newCenter.x,
62
+ y: newCenter.y,
63
+ })
35
64
  }
36
65
  }
@@ -27,6 +27,37 @@ export class PlatedHole extends PrimitiveComponent<typeof platedHoleProps> {
27
27
  )
28
28
  }
29
29
 
30
+ _getCircuitJsonBounds(): {
31
+ center: { x: number; y: number }
32
+ bounds: { left: number; top: number; right: number; bottom: number }
33
+ width: number
34
+ height: number
35
+ } {
36
+ const { db } = this.root!
37
+ const platedHole = db.pcb_plated_hole.get(this.pcb_plated_hole_id!)!
38
+ const size = this.getPcbSize()
39
+
40
+ return {
41
+ center: { x: platedHole.x, y: platedHole.y },
42
+ bounds: {
43
+ left: platedHole.x - size.width / 2,
44
+ top: platedHole.y - size.height / 2,
45
+ right: platedHole.x + size.width / 2,
46
+ bottom: platedHole.y + size.height / 2,
47
+ },
48
+ width: size.width,
49
+ height: size.height,
50
+ }
51
+ }
52
+
53
+ _setPositionFromLayout(newCenter: { x: number; y: number }) {
54
+ const { db } = this.root!
55
+ db.pcb_plated_hole.update(this.pcb_plated_hole_id!, {
56
+ x: newCenter.x,
57
+ y: newCenter.y,
58
+ })
59
+ }
60
+
30
61
  doInitialPortMatching(): void {
31
62
  const parentPorts = (this.parent?.children ?? []).filter(
32
63
  (c) => c.componentName === "Port",
@@ -48,26 +79,22 @@ export class PlatedHole extends PrimitiveComponent<typeof platedHoleProps> {
48
79
  doInitialPcbPrimitiveRender(): void {
49
80
  const { db } = this.root!
50
81
  const { _parsedProps: props } = this
51
- if (!props.portHints) return
52
82
  const position = this._getGlobalPcbPositionBeforeLayout()
53
83
  if (props.shape === "circle") {
54
- const plated_hole_input: PCBPlatedHoleInput = {
84
+ const pcb_plated_hole = db.pcb_plated_hole.insert({
55
85
  pcb_component_id: this.parent?.pcb_component_id!,
56
86
  pcb_port_id: this.matchedPort?.pcb_port_id!,
57
- layers: ["top", "bottom"],
87
+ // @ts-ignore - some issue with @tscircuit/soup union type
58
88
  outer_diameter: props.outerDiameter,
59
89
  hole_diameter: props.holeDiameter,
60
- shape: "circle",
90
+ shape: "circle" as const,
61
91
  port_hints: this.getNameAndAliases(),
62
92
  x: position.x,
63
93
  y: position.y,
64
- type: "pcb_plated_hole",
65
- }
66
-
67
- // @ts-ignore - some issue with soup-util types it seems
68
- const pcb_plated_hole = db.pcb_plated_hole.insert(plated_hole_input)
94
+ layers: ["top", "bottom"],
95
+ })
69
96
 
70
- // this.pcb_plated_hole_id = pcb_plated_hole.pcb_plated_hole_id
97
+ this.pcb_plated_hole_id = pcb_plated_hole.pcb_plated_hole_id
71
98
  }
72
99
  }
73
100
  }
@@ -1,21 +1,23 @@
1
- import type { SchematicPinStyles } from "@tscircuit/props"
1
+ import type { SchematicPinStyle } from "@tscircuit/props"
2
2
  import { schematic_component } from "@tscircuit/soup"
3
3
  import { z } from "zod"
4
4
 
5
5
  type UnderscorePinStyles = z.input<typeof schematic_component>["pin_styles"]
6
6
 
7
7
  export const underscorifyPinStyles = (
8
- pinStyles: SchematicPinStyles | undefined,
8
+ pinStyles: Record<string, SchematicPinStyle> | undefined,
9
9
  ): UnderscorePinStyles | undefined => {
10
10
  if (!pinStyles) return undefined
11
11
  const underscorePinStyles: UnderscorePinStyles = {}
12
12
 
13
- for (const [pinName, pinStyle] of Object.entries(pinStyles)) {
13
+ for (const [pinName, pinStyle] of Object.entries(pinStyles) as Array<
14
+ [string, SchematicPinStyle]
15
+ >) {
14
16
  underscorePinStyles[pinName] = {
15
- bottom_margin: pinStyle.bottomMargin,
16
- left_margin: pinStyle.leftMargin,
17
- right_margin: pinStyle.rightMargin,
18
- top_margin: pinStyle.topMargin,
17
+ bottom_margin: pinStyle.bottomMargin as number,
18
+ left_margin: pinStyle.leftMargin as number,
19
+ right_margin: pinStyle.rightMargin as number,
20
+ top_margin: pinStyle.topMargin as number,
19
21
  }
20
22
  }
21
23
 
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.32",
5
+ "version": "0.0.34",
6
6
  "types": "dist/index.d.ts",
7
7
  "main": "dist/index.js",
8
8
  "files": [
@@ -35,8 +35,8 @@
35
35
  "dependencies": {
36
36
  "@lume/kiwi": "^0.4.3",
37
37
  "@tscircuit/infgrid-ijump-astar": "^0.0.6",
38
- "@tscircuit/props": "^0.0.58",
39
- "@tscircuit/soup": "^0.0.58",
38
+ "@tscircuit/props": "^0.0.62",
39
+ "@tscircuit/soup": "^0.0.66",
40
40
  "@tscircuit/soup-util": "0.0.18",
41
41
  "footprinter": "^0.0.44",
42
42
  "react": "^18.3.1",