@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 +102 -40
- package/dist/index.js +204 -73
- package/lib/components/primitive-components/Constraint.ts +45 -9
- package/lib/components/primitive-components/Footprint.ts +138 -66
- package/lib/components/primitive-components/Hole.ts +35 -6
- package/lib/components/primitive-components/PlatedHole.ts +37 -10
- package/lib/soup/underscorifyPinStyles.ts +9 -7
- package/package.json +3 -3
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3483
|
-
type?: "ydist" | undefined;
|
|
3483
|
+
yDist: string | number;
|
|
3484
3484
|
pcb?: true | undefined;
|
|
3485
3485
|
edgeToEdge?: true | undefined;
|
|
3486
3486
|
centerToCenter?: true | undefined;
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
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
|
-
|
|
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.
|
|
545
|
-
solver.
|
|
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 ("
|
|
552
|
-
const {
|
|
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.
|
|
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.
|
|
584
|
+
props.xDist,
|
|
579
585
|
kiwi.Strength.required
|
|
580
586
|
)
|
|
581
587
|
);
|
|
582
588
|
}
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
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
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
const
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1199
|
-
};
|
|
1200
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
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.
|
|
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
|
|
2944
|
-
type: "pcb_hole",
|
|
3051
|
+
const inserted_hole = db.pcb_hole.insert({
|
|
2945
3052
|
hole_shape: "round",
|
|
2946
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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 ("
|
|
61
|
-
const {
|
|
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.
|
|
81
|
+
props.xDist,
|
|
75
82
|
kiwi.Strength.required,
|
|
76
83
|
),
|
|
77
84
|
)
|
|
78
85
|
} else if (edgeToEdge) {
|
|
79
86
|
// rightEdge - leftEdge = xdist
|
|
80
|
-
// right
|
|
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.
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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.
|
|
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
|
|
26
|
-
type: "pcb_hole",
|
|
25
|
+
const inserted_hole = db.pcb_hole.insert({
|
|
27
26
|
hole_shape: "round",
|
|
28
|
-
|
|
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
|
-
|
|
34
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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:
|
|
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.
|
|
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.
|
|
39
|
-
"@tscircuit/soup": "^0.0.
|
|
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",
|