circuit-to-canvas 0.0.30 → 0.0.31
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 +27 -1
- package/dist/index.js +401 -24
- package/lib/drawer/CircuitToCanvasDrawer.ts +105 -2
- package/lib/drawer/elements/index.ts +5 -0
- package/lib/drawer/elements/pcb-smtpad.ts +174 -0
- package/lib/drawer/elements/soldermask-margin.ts +266 -0
- package/package.json +1 -1
- package/tests/elements/__snapshots__/pcb-smtpad-soldermask-margin.snap.png +0 -0
- package/tests/elements/pcb-smtpad-soldermask-margin.test.ts +163 -0
package/dist/index.d.ts
CHANGED
|
@@ -99,6 +99,7 @@ declare class CircuitToCanvasDrawer {
|
|
|
99
99
|
configure(config: DrawerConfig): void;
|
|
100
100
|
setCameraBounds(bounds: CameraBounds): void;
|
|
101
101
|
drawElements(elements: AnyCircuitElement[], options?: DrawElementsOptions): void;
|
|
102
|
+
private drawBoardWithSoldermask;
|
|
102
103
|
private drawElement;
|
|
103
104
|
}
|
|
104
105
|
|
|
@@ -287,6 +288,31 @@ interface DrawPcbSmtPadParams {
|
|
|
287
288
|
}
|
|
288
289
|
declare function drawPcbSmtPad(params: DrawPcbSmtPadParams): void;
|
|
289
290
|
|
|
291
|
+
/**
|
|
292
|
+
* Draws a soldermask ring for rectangular shapes with negative margin
|
|
293
|
+
* (soldermask appears inside the pad boundary)
|
|
294
|
+
*/
|
|
295
|
+
declare function drawSoldermaskRingForRect(ctx: CanvasContext, center: {
|
|
296
|
+
x: number;
|
|
297
|
+
y: number;
|
|
298
|
+
}, width: number, height: number, margin: number, borderRadius: number, rotation: number, realToCanvasMat: Matrix, soldermaskColor: string, padColor: string): void;
|
|
299
|
+
/**
|
|
300
|
+
* Draws a soldermask ring for circular shapes with negative margin
|
|
301
|
+
* (soldermask appears inside the pad boundary)
|
|
302
|
+
*/
|
|
303
|
+
declare function drawSoldermaskRingForCircle(ctx: CanvasContext, center: {
|
|
304
|
+
x: number;
|
|
305
|
+
y: number;
|
|
306
|
+
}, radius: number, margin: number, realToCanvasMat: Matrix, soldermaskColor: string, padColor: string): void;
|
|
307
|
+
/**
|
|
308
|
+
* Draws a soldermask ring for pill shapes with negative margin
|
|
309
|
+
* (soldermask appears inside the pad boundary)
|
|
310
|
+
*/
|
|
311
|
+
declare function drawSoldermaskRingForPill(ctx: CanvasContext, center: {
|
|
312
|
+
x: number;
|
|
313
|
+
y: number;
|
|
314
|
+
}, width: number, height: number, margin: number, rotation: number, realToCanvasMat: Matrix, soldermaskColor: string, padColor: string): void;
|
|
315
|
+
|
|
290
316
|
interface DrawPcbTraceParams {
|
|
291
317
|
ctx: CanvasContext;
|
|
292
318
|
trace: PCBTrace;
|
|
@@ -431,4 +457,4 @@ interface DrawPcbNoteDimensionParams {
|
|
|
431
457
|
}
|
|
432
458
|
declare function drawPcbNoteDimension(params: DrawPcbNoteDimensionParams): void;
|
|
433
459
|
|
|
434
|
-
export { type AlphabetLayout, type AnchorAlignment, type CameraBounds, type CanvasContext, CircuitToCanvasDrawer, type CopperColorMap, type CopperLayerName, DEFAULT_PCB_COLOR_MAP, type DrawArrowParams, type DrawCircleParams, type DrawContext, type DrawElementsOptions, type DrawLineParams, type DrawOvalParams, type DrawPathParams, type DrawPcbBoardParams, type DrawPcbCopperPourParams, type DrawPcbCopperTextParams, type DrawPcbCutoutParams, type DrawPcbFabricationNotePathParams, type DrawPcbFabricationNoteRectParams, type DrawPcbFabricationNoteTextParams, type DrawPcbHoleParams, type DrawPcbNoteDimensionParams, type DrawPcbNotePathParams, type DrawPcbNoteRectParams, type DrawPcbNoteTextParams, type DrawPcbPlatedHoleParams, type DrawPcbSilkscreenCircleParams, type DrawPcbSilkscreenLineParams, type DrawPcbSilkscreenPathParams, type DrawPcbSilkscreenPillParams, type DrawPcbSilkscreenRectParams, type DrawPcbSilkscreenTextParams, type DrawPcbSmtPadParams, type DrawPcbTraceParams, type DrawPcbViaParams, type DrawPillParams, type DrawPolygonParams, type DrawRectParams, type DrawTextParams, type DrawerConfig, type PcbColorMap, drawArrow, drawCircle, drawLine, drawOval, drawPath, drawPcbBoard, drawPcbCopperPour, drawPcbCopperText, drawPcbCutout, drawPcbFabricationNotePath, drawPcbFabricationNoteRect, drawPcbFabricationNoteText, drawPcbHole, drawPcbNoteDimension, drawPcbNotePath, drawPcbNoteRect, drawPcbNoteText, drawPcbPlatedHole, drawPcbSilkscreenCircle, drawPcbSilkscreenLine, drawPcbSilkscreenPath, drawPcbSilkscreenPill, drawPcbSilkscreenRect, drawPcbSilkscreenText, drawPcbSmtPad, drawPcbTrace, drawPcbVia, drawPill, drawPolygon, drawRect, drawText, getAlphabetLayout, getTextStartPosition, strokeAlphabetText };
|
|
460
|
+
export { type AlphabetLayout, type AnchorAlignment, type CameraBounds, type CanvasContext, CircuitToCanvasDrawer, type CopperColorMap, type CopperLayerName, DEFAULT_PCB_COLOR_MAP, type DrawArrowParams, type DrawCircleParams, type DrawContext, type DrawElementsOptions, type DrawLineParams, type DrawOvalParams, type DrawPathParams, type DrawPcbBoardParams, type DrawPcbCopperPourParams, type DrawPcbCopperTextParams, type DrawPcbCutoutParams, type DrawPcbFabricationNotePathParams, type DrawPcbFabricationNoteRectParams, type DrawPcbFabricationNoteTextParams, type DrawPcbHoleParams, type DrawPcbNoteDimensionParams, type DrawPcbNotePathParams, type DrawPcbNoteRectParams, type DrawPcbNoteTextParams, type DrawPcbPlatedHoleParams, type DrawPcbSilkscreenCircleParams, type DrawPcbSilkscreenLineParams, type DrawPcbSilkscreenPathParams, type DrawPcbSilkscreenPillParams, type DrawPcbSilkscreenRectParams, type DrawPcbSilkscreenTextParams, type DrawPcbSmtPadParams, type DrawPcbTraceParams, type DrawPcbViaParams, type DrawPillParams, type DrawPolygonParams, type DrawRectParams, type DrawTextParams, type DrawerConfig, type PcbColorMap, drawArrow, drawCircle, drawLine, drawOval, drawPath, drawPcbBoard, drawPcbCopperPour, drawPcbCopperText, drawPcbCutout, drawPcbFabricationNotePath, drawPcbFabricationNoteRect, drawPcbFabricationNoteText, drawPcbHole, drawPcbNoteDimension, drawPcbNotePath, drawPcbNoteRect, drawPcbNoteText, drawPcbPlatedHole, drawPcbSilkscreenCircle, drawPcbSilkscreenLine, drawPcbSilkscreenPath, drawPcbSilkscreenPill, drawPcbSilkscreenRect, drawPcbSilkscreenText, drawPcbSmtPad, drawPcbTrace, drawPcbVia, drawPill, drawPolygon, drawRect, drawSoldermaskRingForCircle, drawSoldermaskRingForPill, drawSoldermaskRingForRect, drawText, getAlphabetLayout, getTextStartPosition, strokeAlphabetText };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
// lib/drawer/CircuitToCanvasDrawer.ts
|
|
2
|
+
import {
|
|
3
|
+
identity,
|
|
4
|
+
compose,
|
|
5
|
+
translate,
|
|
6
|
+
scale,
|
|
7
|
+
applyToPoint as applyToPoint15
|
|
8
|
+
} from "transformation-matrix";
|
|
9
|
+
|
|
1
10
|
// lib/drawer/pcb-render-layer-filter.ts
|
|
2
11
|
import { getElementRenderLayers } from "@tscircuit/circuit-json-util";
|
|
3
12
|
function shouldDrawElement(element, options) {
|
|
@@ -11,9 +20,6 @@ function shouldDrawElement(element, options) {
|
|
|
11
20
|
return elementLayers.some((layer) => options.layers.includes(layer));
|
|
12
21
|
}
|
|
13
22
|
|
|
14
|
-
// lib/drawer/CircuitToCanvasDrawer.ts
|
|
15
|
-
import { identity, compose, translate, scale } from "transformation-matrix";
|
|
16
|
-
|
|
17
23
|
// lib/drawer/types.ts
|
|
18
24
|
var DEFAULT_PCB_COLOR_MAP = {
|
|
19
25
|
copper: {
|
|
@@ -509,14 +515,207 @@ function drawPcbHole(params) {
|
|
|
509
515
|
}
|
|
510
516
|
}
|
|
511
517
|
|
|
518
|
+
// lib/drawer/elements/soldermask-margin.ts
|
|
519
|
+
import { applyToPoint as applyToPoint6 } from "transformation-matrix";
|
|
520
|
+
function drawSoldermaskRingForRect(ctx, center, width, height, margin, borderRadius, rotation, realToCanvasMat, soldermaskColor, padColor) {
|
|
521
|
+
const [cx, cy] = applyToPoint6(realToCanvasMat, [center.x, center.y]);
|
|
522
|
+
const scaledWidth = width * Math.abs(realToCanvasMat.a);
|
|
523
|
+
const scaledHeight = height * Math.abs(realToCanvasMat.a);
|
|
524
|
+
const scaledMargin = Math.abs(margin) * Math.abs(realToCanvasMat.a);
|
|
525
|
+
const scaledRadius = borderRadius * Math.abs(realToCanvasMat.a);
|
|
526
|
+
ctx.save();
|
|
527
|
+
ctx.translate(cx, cy);
|
|
528
|
+
if (rotation !== 0) {
|
|
529
|
+
ctx.rotate(-rotation * (Math.PI / 180));
|
|
530
|
+
}
|
|
531
|
+
const prevCompositeOp = ctx.globalCompositeOperation;
|
|
532
|
+
if (ctx.globalCompositeOperation !== void 0) {
|
|
533
|
+
ctx.globalCompositeOperation = "source-atop";
|
|
534
|
+
}
|
|
535
|
+
const outerWidth = scaledWidth;
|
|
536
|
+
const outerHeight = scaledHeight;
|
|
537
|
+
const outerRadius = scaledRadius;
|
|
538
|
+
ctx.beginPath();
|
|
539
|
+
if (outerRadius > 0) {
|
|
540
|
+
const x = -outerWidth / 2;
|
|
541
|
+
const y = -outerHeight / 2;
|
|
542
|
+
const r = Math.min(outerRadius, outerWidth / 2, outerHeight / 2);
|
|
543
|
+
ctx.moveTo(x + r, y);
|
|
544
|
+
ctx.lineTo(x + outerWidth - r, y);
|
|
545
|
+
ctx.arcTo(x + outerWidth, y, x + outerWidth, y + r, r);
|
|
546
|
+
ctx.lineTo(x + outerWidth, y + outerHeight - r);
|
|
547
|
+
ctx.arcTo(
|
|
548
|
+
x + outerWidth,
|
|
549
|
+
y + outerHeight,
|
|
550
|
+
x + outerWidth - r,
|
|
551
|
+
y + outerHeight,
|
|
552
|
+
r
|
|
553
|
+
);
|
|
554
|
+
ctx.lineTo(x + r, y + outerHeight);
|
|
555
|
+
ctx.arcTo(x, y + outerHeight, x, y + outerHeight - r, r);
|
|
556
|
+
ctx.lineTo(x, y + r);
|
|
557
|
+
ctx.arcTo(x, y, x + r, y, r);
|
|
558
|
+
} else {
|
|
559
|
+
ctx.rect(-outerWidth / 2, -outerHeight / 2, outerWidth, outerHeight);
|
|
560
|
+
}
|
|
561
|
+
ctx.fillStyle = soldermaskColor;
|
|
562
|
+
ctx.fill();
|
|
563
|
+
if (ctx.globalCompositeOperation !== void 0) {
|
|
564
|
+
ctx.globalCompositeOperation = prevCompositeOp || "source-over";
|
|
565
|
+
}
|
|
566
|
+
const innerWidth = scaledWidth - scaledMargin * 2;
|
|
567
|
+
const innerHeight = scaledHeight - scaledMargin * 2;
|
|
568
|
+
const innerRadius = Math.max(0, scaledRadius - scaledMargin);
|
|
569
|
+
if (innerWidth > 0 && innerHeight > 0) {
|
|
570
|
+
ctx.beginPath();
|
|
571
|
+
if (innerRadius > 0) {
|
|
572
|
+
const x = -innerWidth / 2;
|
|
573
|
+
const y = -innerHeight / 2;
|
|
574
|
+
const r = Math.min(innerRadius, innerWidth / 2, innerHeight / 2);
|
|
575
|
+
ctx.moveTo(x + r, y);
|
|
576
|
+
ctx.lineTo(x + innerWidth - r, y);
|
|
577
|
+
ctx.arcTo(x + innerWidth, y, x + innerWidth, y + r, r);
|
|
578
|
+
ctx.lineTo(x + innerWidth, y + innerHeight - r);
|
|
579
|
+
ctx.arcTo(
|
|
580
|
+
x + innerWidth,
|
|
581
|
+
y + innerHeight,
|
|
582
|
+
x + innerWidth - r,
|
|
583
|
+
y + innerHeight,
|
|
584
|
+
r
|
|
585
|
+
);
|
|
586
|
+
ctx.lineTo(x + r, y + innerHeight);
|
|
587
|
+
ctx.arcTo(x, y + innerHeight, x, y + innerHeight - r, r);
|
|
588
|
+
ctx.lineTo(x, y + r);
|
|
589
|
+
ctx.arcTo(x, y, x + r, y, r);
|
|
590
|
+
} else {
|
|
591
|
+
ctx.rect(-innerWidth / 2, -innerHeight / 2, innerWidth, innerHeight);
|
|
592
|
+
}
|
|
593
|
+
ctx.fillStyle = padColor;
|
|
594
|
+
ctx.fill();
|
|
595
|
+
}
|
|
596
|
+
ctx.restore();
|
|
597
|
+
}
|
|
598
|
+
function drawSoldermaskRingForCircle(ctx, center, radius, margin, realToCanvasMat, soldermaskColor, padColor) {
|
|
599
|
+
const [cx, cy] = applyToPoint6(realToCanvasMat, [center.x, center.y]);
|
|
600
|
+
const scaledRadius = radius * Math.abs(realToCanvasMat.a);
|
|
601
|
+
const scaledMargin = Math.abs(margin) * Math.abs(realToCanvasMat.a);
|
|
602
|
+
ctx.save();
|
|
603
|
+
const prevCompositeOp = ctx.globalCompositeOperation;
|
|
604
|
+
if (ctx.globalCompositeOperation !== void 0) {
|
|
605
|
+
ctx.globalCompositeOperation = "source-atop";
|
|
606
|
+
}
|
|
607
|
+
ctx.beginPath();
|
|
608
|
+
ctx.arc(cx, cy, scaledRadius, 0, Math.PI * 2);
|
|
609
|
+
ctx.fillStyle = soldermaskColor;
|
|
610
|
+
ctx.fill();
|
|
611
|
+
if (ctx.globalCompositeOperation !== void 0) {
|
|
612
|
+
ctx.globalCompositeOperation = prevCompositeOp || "source-over";
|
|
613
|
+
}
|
|
614
|
+
const innerRadius = Math.max(0, scaledRadius - scaledMargin);
|
|
615
|
+
if (innerRadius > 0) {
|
|
616
|
+
ctx.beginPath();
|
|
617
|
+
ctx.arc(cx, cy, innerRadius, 0, Math.PI * 2);
|
|
618
|
+
ctx.fillStyle = padColor;
|
|
619
|
+
ctx.fill();
|
|
620
|
+
}
|
|
621
|
+
ctx.restore();
|
|
622
|
+
}
|
|
623
|
+
function drawSoldermaskRingForPill(ctx, center, width, height, margin, rotation, realToCanvasMat, soldermaskColor, padColor) {
|
|
624
|
+
const [cx, cy] = applyToPoint6(realToCanvasMat, [center.x, center.y]);
|
|
625
|
+
const scaledWidth = width * Math.abs(realToCanvasMat.a);
|
|
626
|
+
const scaledHeight = height * Math.abs(realToCanvasMat.a);
|
|
627
|
+
const scaledMargin = Math.abs(margin) * Math.abs(realToCanvasMat.a);
|
|
628
|
+
ctx.save();
|
|
629
|
+
ctx.translate(cx, cy);
|
|
630
|
+
if (rotation !== 0) {
|
|
631
|
+
ctx.rotate(-rotation * (Math.PI / 180));
|
|
632
|
+
}
|
|
633
|
+
const prevCompositeOp = ctx.globalCompositeOperation;
|
|
634
|
+
if (ctx.globalCompositeOperation !== void 0) {
|
|
635
|
+
ctx.globalCompositeOperation = "source-atop";
|
|
636
|
+
}
|
|
637
|
+
const outerWidth = scaledWidth;
|
|
638
|
+
const outerHeight = scaledHeight;
|
|
639
|
+
ctx.beginPath();
|
|
640
|
+
if (outerWidth > outerHeight) {
|
|
641
|
+
const radius = outerHeight / 2;
|
|
642
|
+
const straightLength = outerWidth - outerHeight;
|
|
643
|
+
ctx.moveTo(-straightLength / 2, -radius);
|
|
644
|
+
ctx.lineTo(straightLength / 2, -radius);
|
|
645
|
+
ctx.arc(straightLength / 2, 0, radius, -Math.PI / 2, Math.PI / 2);
|
|
646
|
+
ctx.lineTo(-straightLength / 2, radius);
|
|
647
|
+
ctx.arc(-straightLength / 2, 0, radius, Math.PI / 2, -Math.PI / 2);
|
|
648
|
+
} else if (outerHeight > outerWidth) {
|
|
649
|
+
const radius = outerWidth / 2;
|
|
650
|
+
const straightLength = outerHeight - outerWidth;
|
|
651
|
+
ctx.moveTo(radius, -straightLength / 2);
|
|
652
|
+
ctx.lineTo(radius, straightLength / 2);
|
|
653
|
+
ctx.arc(0, straightLength / 2, radius, 0, Math.PI);
|
|
654
|
+
ctx.lineTo(-radius, -straightLength / 2);
|
|
655
|
+
ctx.arc(0, -straightLength / 2, radius, Math.PI, 0);
|
|
656
|
+
} else {
|
|
657
|
+
ctx.arc(0, 0, outerWidth / 2, 0, Math.PI * 2);
|
|
658
|
+
}
|
|
659
|
+
ctx.fillStyle = soldermaskColor;
|
|
660
|
+
ctx.fill();
|
|
661
|
+
if (ctx.globalCompositeOperation !== void 0) {
|
|
662
|
+
ctx.globalCompositeOperation = prevCompositeOp || "source-over";
|
|
663
|
+
}
|
|
664
|
+
const innerWidth = scaledWidth - scaledMargin * 2;
|
|
665
|
+
const innerHeight = scaledHeight - scaledMargin * 2;
|
|
666
|
+
if (innerWidth > 0 && innerHeight > 0) {
|
|
667
|
+
ctx.beginPath();
|
|
668
|
+
if (innerWidth > innerHeight) {
|
|
669
|
+
const radius = innerHeight / 2;
|
|
670
|
+
const straightLength = innerWidth - innerHeight;
|
|
671
|
+
ctx.moveTo(-straightLength / 2, -radius);
|
|
672
|
+
ctx.lineTo(straightLength / 2, -radius);
|
|
673
|
+
ctx.arc(straightLength / 2, 0, radius, -Math.PI / 2, Math.PI / 2);
|
|
674
|
+
ctx.lineTo(-straightLength / 2, radius);
|
|
675
|
+
ctx.arc(-straightLength / 2, 0, radius, Math.PI / 2, -Math.PI / 2);
|
|
676
|
+
} else if (innerHeight > innerWidth) {
|
|
677
|
+
const radius = innerWidth / 2;
|
|
678
|
+
const straightLength = innerHeight - innerWidth;
|
|
679
|
+
ctx.moveTo(radius, -straightLength / 2);
|
|
680
|
+
ctx.lineTo(radius, straightLength / 2);
|
|
681
|
+
ctx.arc(0, straightLength / 2, radius, 0, Math.PI);
|
|
682
|
+
ctx.lineTo(-radius, -straightLength / 2);
|
|
683
|
+
ctx.arc(0, -straightLength / 2, radius, Math.PI, 0);
|
|
684
|
+
} else {
|
|
685
|
+
ctx.arc(0, 0, innerWidth / 2, 0, Math.PI * 2);
|
|
686
|
+
}
|
|
687
|
+
ctx.fillStyle = padColor;
|
|
688
|
+
ctx.fill();
|
|
689
|
+
}
|
|
690
|
+
ctx.restore();
|
|
691
|
+
}
|
|
692
|
+
|
|
512
693
|
// lib/drawer/elements/pcb-smtpad.ts
|
|
513
694
|
function layerToColor(layer, colorMap) {
|
|
514
695
|
return colorMap.copper[layer] ?? colorMap.copper.top;
|
|
515
696
|
}
|
|
697
|
+
function getSoldermaskColor(layer, colorMap) {
|
|
698
|
+
return colorMap.soldermaskOverCopper[layer] ?? colorMap.soldermaskOverCopper.top;
|
|
699
|
+
}
|
|
516
700
|
function drawPcbSmtPad(params) {
|
|
517
701
|
const { ctx, pad, realToCanvasMat, colorMap } = params;
|
|
518
702
|
const color = layerToColor(pad.layer, colorMap);
|
|
703
|
+
const hasSoldermask = pad.is_covered_with_solder_mask === true && pad.soldermask_margin !== void 0 && pad.soldermask_margin !== 0;
|
|
704
|
+
const margin = hasSoldermask ? pad.soldermask_margin : 0;
|
|
705
|
+
const soldermaskRingColor = getSoldermaskColor(pad.layer, colorMap);
|
|
706
|
+
const positiveMarginColor = colorMap.substrate;
|
|
519
707
|
if (pad.shape === "rect") {
|
|
708
|
+
if (hasSoldermask && margin > 0) {
|
|
709
|
+
drawRect({
|
|
710
|
+
ctx,
|
|
711
|
+
center: { x: pad.x, y: pad.y },
|
|
712
|
+
width: pad.width + margin * 2,
|
|
713
|
+
height: pad.height + margin * 2,
|
|
714
|
+
fill: positiveMarginColor,
|
|
715
|
+
realToCanvasMat,
|
|
716
|
+
borderRadius: (pad.corner_radius ?? pad.rect_border_radius ?? 0) + margin
|
|
717
|
+
});
|
|
718
|
+
}
|
|
520
719
|
drawRect({
|
|
521
720
|
ctx,
|
|
522
721
|
center: { x: pad.x, y: pad.y },
|
|
@@ -526,9 +725,35 @@ function drawPcbSmtPad(params) {
|
|
|
526
725
|
realToCanvasMat,
|
|
527
726
|
borderRadius: pad.corner_radius ?? pad.rect_border_radius ?? 0
|
|
528
727
|
});
|
|
728
|
+
if (hasSoldermask && margin < 0) {
|
|
729
|
+
drawSoldermaskRingForRect(
|
|
730
|
+
ctx,
|
|
731
|
+
{ x: pad.x, y: pad.y },
|
|
732
|
+
pad.width,
|
|
733
|
+
pad.height,
|
|
734
|
+
margin,
|
|
735
|
+
pad.corner_radius ?? pad.rect_border_radius ?? 0,
|
|
736
|
+
0,
|
|
737
|
+
realToCanvasMat,
|
|
738
|
+
soldermaskRingColor,
|
|
739
|
+
color
|
|
740
|
+
);
|
|
741
|
+
}
|
|
529
742
|
return;
|
|
530
743
|
}
|
|
531
744
|
if (pad.shape === "rotated_rect") {
|
|
745
|
+
if (hasSoldermask && margin > 0) {
|
|
746
|
+
drawRect({
|
|
747
|
+
ctx,
|
|
748
|
+
center: { x: pad.x, y: pad.y },
|
|
749
|
+
width: pad.width + margin * 2,
|
|
750
|
+
height: pad.height + margin * 2,
|
|
751
|
+
fill: positiveMarginColor,
|
|
752
|
+
realToCanvasMat,
|
|
753
|
+
borderRadius: (pad.corner_radius ?? pad.rect_border_radius ?? 0) + margin,
|
|
754
|
+
rotation: pad.ccw_rotation ?? 0
|
|
755
|
+
});
|
|
756
|
+
}
|
|
532
757
|
drawRect({
|
|
533
758
|
ctx,
|
|
534
759
|
center: { x: pad.x, y: pad.y },
|
|
@@ -539,9 +764,32 @@ function drawPcbSmtPad(params) {
|
|
|
539
764
|
borderRadius: pad.corner_radius ?? pad.rect_border_radius ?? 0,
|
|
540
765
|
rotation: pad.ccw_rotation ?? 0
|
|
541
766
|
});
|
|
767
|
+
if (hasSoldermask && margin < 0) {
|
|
768
|
+
drawSoldermaskRingForRect(
|
|
769
|
+
ctx,
|
|
770
|
+
{ x: pad.x, y: pad.y },
|
|
771
|
+
pad.width,
|
|
772
|
+
pad.height,
|
|
773
|
+
margin,
|
|
774
|
+
pad.corner_radius ?? pad.rect_border_radius ?? 0,
|
|
775
|
+
pad.ccw_rotation ?? 0,
|
|
776
|
+
realToCanvasMat,
|
|
777
|
+
soldermaskRingColor,
|
|
778
|
+
color
|
|
779
|
+
);
|
|
780
|
+
}
|
|
542
781
|
return;
|
|
543
782
|
}
|
|
544
783
|
if (pad.shape === "circle") {
|
|
784
|
+
if (hasSoldermask && margin > 0) {
|
|
785
|
+
drawCircle({
|
|
786
|
+
ctx,
|
|
787
|
+
center: { x: pad.x, y: pad.y },
|
|
788
|
+
radius: pad.radius + margin,
|
|
789
|
+
fill: positiveMarginColor,
|
|
790
|
+
realToCanvasMat
|
|
791
|
+
});
|
|
792
|
+
}
|
|
545
793
|
drawCircle({
|
|
546
794
|
ctx,
|
|
547
795
|
center: { x: pad.x, y: pad.y },
|
|
@@ -549,9 +797,30 @@ function drawPcbSmtPad(params) {
|
|
|
549
797
|
fill: color,
|
|
550
798
|
realToCanvasMat
|
|
551
799
|
});
|
|
800
|
+
if (hasSoldermask && margin < 0) {
|
|
801
|
+
drawSoldermaskRingForCircle(
|
|
802
|
+
ctx,
|
|
803
|
+
{ x: pad.x, y: pad.y },
|
|
804
|
+
pad.radius,
|
|
805
|
+
margin,
|
|
806
|
+
realToCanvasMat,
|
|
807
|
+
soldermaskRingColor,
|
|
808
|
+
color
|
|
809
|
+
);
|
|
810
|
+
}
|
|
552
811
|
return;
|
|
553
812
|
}
|
|
554
813
|
if (pad.shape === "pill") {
|
|
814
|
+
if (hasSoldermask && margin > 0) {
|
|
815
|
+
drawPill({
|
|
816
|
+
ctx,
|
|
817
|
+
center: { x: pad.x, y: pad.y },
|
|
818
|
+
width: pad.width + margin * 2,
|
|
819
|
+
height: pad.height + margin * 2,
|
|
820
|
+
fill: positiveMarginColor,
|
|
821
|
+
realToCanvasMat
|
|
822
|
+
});
|
|
823
|
+
}
|
|
555
824
|
drawPill({
|
|
556
825
|
ctx,
|
|
557
826
|
center: { x: pad.x, y: pad.y },
|
|
@@ -560,9 +829,33 @@ function drawPcbSmtPad(params) {
|
|
|
560
829
|
fill: color,
|
|
561
830
|
realToCanvasMat
|
|
562
831
|
});
|
|
832
|
+
if (hasSoldermask && margin < 0) {
|
|
833
|
+
drawSoldermaskRingForPill(
|
|
834
|
+
ctx,
|
|
835
|
+
{ x: pad.x, y: pad.y },
|
|
836
|
+
pad.width,
|
|
837
|
+
pad.height,
|
|
838
|
+
margin,
|
|
839
|
+
0,
|
|
840
|
+
realToCanvasMat,
|
|
841
|
+
soldermaskRingColor,
|
|
842
|
+
color
|
|
843
|
+
);
|
|
844
|
+
}
|
|
563
845
|
return;
|
|
564
846
|
}
|
|
565
847
|
if (pad.shape === "rotated_pill") {
|
|
848
|
+
if (hasSoldermask && margin > 0) {
|
|
849
|
+
drawPill({
|
|
850
|
+
ctx,
|
|
851
|
+
center: { x: pad.x, y: pad.y },
|
|
852
|
+
width: pad.width + margin * 2,
|
|
853
|
+
height: pad.height + margin * 2,
|
|
854
|
+
fill: positiveMarginColor,
|
|
855
|
+
realToCanvasMat,
|
|
856
|
+
rotation: pad.ccw_rotation ?? 0
|
|
857
|
+
});
|
|
858
|
+
}
|
|
566
859
|
drawPill({
|
|
567
860
|
ctx,
|
|
568
861
|
center: { x: pad.x, y: pad.y },
|
|
@@ -572,6 +865,19 @@ function drawPcbSmtPad(params) {
|
|
|
572
865
|
realToCanvasMat,
|
|
573
866
|
rotation: pad.ccw_rotation ?? 0
|
|
574
867
|
});
|
|
868
|
+
if (hasSoldermask && margin < 0) {
|
|
869
|
+
drawSoldermaskRingForPill(
|
|
870
|
+
ctx,
|
|
871
|
+
{ x: pad.x, y: pad.y },
|
|
872
|
+
pad.width,
|
|
873
|
+
pad.height,
|
|
874
|
+
margin,
|
|
875
|
+
pad.ccw_rotation ?? 0,
|
|
876
|
+
realToCanvasMat,
|
|
877
|
+
soldermaskRingColor,
|
|
878
|
+
color
|
|
879
|
+
);
|
|
880
|
+
}
|
|
575
881
|
return;
|
|
576
882
|
}
|
|
577
883
|
if (pad.shape === "polygon") {
|
|
@@ -588,7 +894,7 @@ function drawPcbSmtPad(params) {
|
|
|
588
894
|
}
|
|
589
895
|
|
|
590
896
|
// lib/drawer/shapes/line.ts
|
|
591
|
-
import { applyToPoint as
|
|
897
|
+
import { applyToPoint as applyToPoint7 } from "transformation-matrix";
|
|
592
898
|
function drawLine(params) {
|
|
593
899
|
const {
|
|
594
900
|
ctx,
|
|
@@ -599,8 +905,8 @@ function drawLine(params) {
|
|
|
599
905
|
realToCanvasMat,
|
|
600
906
|
lineCap = "round"
|
|
601
907
|
} = params;
|
|
602
|
-
const [x1, y1] =
|
|
603
|
-
const [x2, y2] =
|
|
908
|
+
const [x1, y1] = applyToPoint7(realToCanvasMat, [start.x, start.y]);
|
|
909
|
+
const [x2, y2] = applyToPoint7(realToCanvasMat, [end.x, end.y]);
|
|
604
910
|
const scaledStrokeWidth = strokeWidth * Math.abs(realToCanvasMat.a);
|
|
605
911
|
ctx.beginPath();
|
|
606
912
|
ctx.moveTo(x1, y1);
|
|
@@ -641,7 +947,7 @@ function drawPcbTrace(params) {
|
|
|
641
947
|
}
|
|
642
948
|
|
|
643
949
|
// lib/drawer/shapes/path.ts
|
|
644
|
-
import { applyToPoint as
|
|
950
|
+
import { applyToPoint as applyToPoint8 } from "transformation-matrix";
|
|
645
951
|
function drawPath(params) {
|
|
646
952
|
const {
|
|
647
953
|
ctx,
|
|
@@ -655,7 +961,7 @@ function drawPath(params) {
|
|
|
655
961
|
if (points.length < 2) return;
|
|
656
962
|
ctx.beginPath();
|
|
657
963
|
const canvasPoints = points.map(
|
|
658
|
-
(p) =>
|
|
964
|
+
(p) => applyToPoint8(realToCanvasMat, [p.x, p.y])
|
|
659
965
|
);
|
|
660
966
|
const firstPoint = canvasPoints[0];
|
|
661
967
|
if (!firstPoint) return;
|
|
@@ -726,11 +1032,11 @@ function drawPcbBoard(params) {
|
|
|
726
1032
|
}
|
|
727
1033
|
|
|
728
1034
|
// lib/drawer/elements/pcb-silkscreen-text.ts
|
|
729
|
-
import { applyToPoint as
|
|
1035
|
+
import { applyToPoint as applyToPoint10 } from "transformation-matrix";
|
|
730
1036
|
|
|
731
1037
|
// lib/drawer/shapes/text/text.ts
|
|
732
1038
|
import { lineAlphabet } from "@tscircuit/alphabet";
|
|
733
|
-
import { applyToPoint as
|
|
1039
|
+
import { applyToPoint as applyToPoint9 } from "transformation-matrix";
|
|
734
1040
|
|
|
735
1041
|
// lib/drawer/shapes/text/getAlphabetLayout.ts
|
|
736
1042
|
var GLYPH_WIDTH_RATIO = 0.62;
|
|
@@ -827,7 +1133,7 @@ function drawText(params) {
|
|
|
827
1133
|
rotation = 0
|
|
828
1134
|
} = params;
|
|
829
1135
|
if (!text) return;
|
|
830
|
-
const [canvasX, canvasY] =
|
|
1136
|
+
const [canvasX, canvasY] = applyToPoint9(realToCanvasMat, [x, y]);
|
|
831
1137
|
const scale2 = Math.abs(realToCanvasMat.a);
|
|
832
1138
|
const scaledFontSize = fontSize * scale2;
|
|
833
1139
|
const layout = getAlphabetLayout(text, scaledFontSize);
|
|
@@ -864,7 +1170,7 @@ function drawPcbSilkscreenText(params) {
|
|
|
864
1170
|
const content = text.text ?? "";
|
|
865
1171
|
if (!content) return;
|
|
866
1172
|
const color = layerToSilkscreenColor(text.layer, colorMap);
|
|
867
|
-
const [x, y] =
|
|
1173
|
+
const [x, y] = applyToPoint10(realToCanvasMat, [
|
|
868
1174
|
text.anchor_position.x,
|
|
869
1175
|
text.anchor_position.y
|
|
870
1176
|
]);
|
|
@@ -1027,7 +1333,7 @@ function drawPcbCutout(params) {
|
|
|
1027
1333
|
}
|
|
1028
1334
|
|
|
1029
1335
|
// lib/drawer/elements/pcb-copper-pour.ts
|
|
1030
|
-
import { applyToPoint as
|
|
1336
|
+
import { applyToPoint as applyToPoint11 } from "transformation-matrix";
|
|
1031
1337
|
function layerToColor3(layer, colorMap) {
|
|
1032
1338
|
return colorMap.copper[layer] ?? colorMap.copper.top;
|
|
1033
1339
|
}
|
|
@@ -1036,7 +1342,7 @@ function drawPcbCopperPour(params) {
|
|
|
1036
1342
|
const color = layerToColor3(pour.layer, colorMap);
|
|
1037
1343
|
ctx.save();
|
|
1038
1344
|
if (pour.shape === "rect") {
|
|
1039
|
-
const [cx, cy] =
|
|
1345
|
+
const [cx, cy] = applyToPoint11(realToCanvasMat, [
|
|
1040
1346
|
pour.center.x,
|
|
1041
1347
|
pour.center.y
|
|
1042
1348
|
]);
|
|
@@ -1057,7 +1363,7 @@ function drawPcbCopperPour(params) {
|
|
|
1057
1363
|
if (pour.shape === "polygon") {
|
|
1058
1364
|
if (pour.points && pour.points.length >= 3) {
|
|
1059
1365
|
const canvasPoints = pour.points.map(
|
|
1060
|
-
(p) =>
|
|
1366
|
+
(p) => applyToPoint11(realToCanvasMat, [p.x, p.y])
|
|
1061
1367
|
);
|
|
1062
1368
|
const firstPoint = canvasPoints[0];
|
|
1063
1369
|
if (!firstPoint) {
|
|
@@ -1085,7 +1391,7 @@ function drawPcbCopperPour(params) {
|
|
|
1085
1391
|
}
|
|
1086
1392
|
|
|
1087
1393
|
// lib/drawer/elements/pcb-copper-text.ts
|
|
1088
|
-
import { applyToPoint as
|
|
1394
|
+
import { applyToPoint as applyToPoint12 } from "transformation-matrix";
|
|
1089
1395
|
var DEFAULT_PADDING = { left: 0.2, right: 0.2, top: 0.2, bottom: 0.2 };
|
|
1090
1396
|
function layerToCopperColor(layer, colorMap) {
|
|
1091
1397
|
return colorMap.copper[layer] ?? colorMap.copper.top;
|
|
@@ -1100,7 +1406,7 @@ function drawPcbCopperText(params) {
|
|
|
1100
1406
|
const { ctx, text, realToCanvasMat, colorMap } = params;
|
|
1101
1407
|
const content = text.text ?? "";
|
|
1102
1408
|
if (!content) return;
|
|
1103
|
-
const [x, y] =
|
|
1409
|
+
const [x, y] = applyToPoint12(realToCanvasMat, [
|
|
1104
1410
|
text.anchor_position.x,
|
|
1105
1411
|
text.anchor_position.y
|
|
1106
1412
|
]);
|
|
@@ -1284,7 +1590,7 @@ function drawPcbNoteText(params) {
|
|
|
1284
1590
|
}
|
|
1285
1591
|
|
|
1286
1592
|
// lib/drawer/elements/pcb-note-dimension.ts
|
|
1287
|
-
import { applyToPoint as
|
|
1593
|
+
import { applyToPoint as applyToPoint13 } from "transformation-matrix";
|
|
1288
1594
|
|
|
1289
1595
|
// lib/drawer/shapes/arrow.ts
|
|
1290
1596
|
function drawArrow(params) {
|
|
@@ -1369,11 +1675,11 @@ function drawPcbNoteDimension(params) {
|
|
|
1369
1675
|
stroke: color,
|
|
1370
1676
|
realToCanvasMat
|
|
1371
1677
|
});
|
|
1372
|
-
const [canvasFromX, canvasFromY] =
|
|
1678
|
+
const [canvasFromX, canvasFromY] = applyToPoint13(realToCanvasMat, [
|
|
1373
1679
|
fromX,
|
|
1374
1680
|
fromY
|
|
1375
1681
|
]);
|
|
1376
|
-
const [canvasToX, canvasToY] =
|
|
1682
|
+
const [canvasToX, canvasToY] = applyToPoint13(realToCanvasMat, [toX, toY]);
|
|
1377
1683
|
const canvasDx = canvasToX - canvasFromX;
|
|
1378
1684
|
const canvasDy = canvasToY - canvasFromY;
|
|
1379
1685
|
const lineAngle = Math.atan2(canvasDy, canvasDx);
|
|
@@ -1434,15 +1740,15 @@ function drawPcbNoteDimension(params) {
|
|
|
1434
1740
|
}
|
|
1435
1741
|
|
|
1436
1742
|
// lib/drawer/elements/pcb-note-line.ts
|
|
1437
|
-
import { applyToPoint as
|
|
1743
|
+
import { applyToPoint as applyToPoint14 } from "transformation-matrix";
|
|
1438
1744
|
function drawPcbNoteLine(params) {
|
|
1439
1745
|
const { ctx, line, realToCanvasMat, colorMap } = params;
|
|
1440
1746
|
const defaultColor = "rgb(89, 148, 220)";
|
|
1441
1747
|
const color = line.color ?? defaultColor;
|
|
1442
1748
|
const strokeWidth = line.stroke_width ?? 0.1;
|
|
1443
1749
|
const isDashed = line.is_dashed ?? false;
|
|
1444
|
-
const [x1, y1] =
|
|
1445
|
-
const [x2, y2] =
|
|
1750
|
+
const [x1, y1] = applyToPoint14(realToCanvasMat, [line.x1, line.y1]);
|
|
1751
|
+
const [x2, y2] = applyToPoint14(realToCanvasMat, [line.x2, line.y2]);
|
|
1446
1752
|
const scaledStrokeWidth = strokeWidth * Math.abs(realToCanvasMat.a);
|
|
1447
1753
|
ctx.save();
|
|
1448
1754
|
if (isDashed) {
|
|
@@ -1524,8 +1830,76 @@ var CircuitToCanvasDrawer = class {
|
|
|
1524
1830
|
);
|
|
1525
1831
|
}
|
|
1526
1832
|
drawElements(elements, options = {}) {
|
|
1833
|
+
const hasSoldermaskPads = elements.some(
|
|
1834
|
+
(el) => el.type === "pcb_smtpad" && el.is_covered_with_solder_mask === true
|
|
1835
|
+
);
|
|
1527
1836
|
for (const element of elements) {
|
|
1528
|
-
|
|
1837
|
+
if (element.type === "pcb_board" && hasSoldermaskPads) {
|
|
1838
|
+
this.drawBoardWithSoldermask(element);
|
|
1839
|
+
} else {
|
|
1840
|
+
this.drawElement(element, options);
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
drawBoardWithSoldermask(board) {
|
|
1845
|
+
const { width, height, center, outline } = board;
|
|
1846
|
+
const layer = "top";
|
|
1847
|
+
if (outline && Array.isArray(outline) && outline.length >= 3) {
|
|
1848
|
+
const soldermaskColor = this.colorMap.soldermask[layer] ?? this.colorMap.soldermask.top;
|
|
1849
|
+
const canvasPoints = outline.map((p) => {
|
|
1850
|
+
const [x, y] = applyToPoint15(this.realToCanvasMat, [p.x, p.y]);
|
|
1851
|
+
return { x, y };
|
|
1852
|
+
});
|
|
1853
|
+
this.ctx.beginPath();
|
|
1854
|
+
const firstPoint = canvasPoints[0];
|
|
1855
|
+
if (firstPoint) {
|
|
1856
|
+
this.ctx.moveTo(firstPoint.x, firstPoint.y);
|
|
1857
|
+
for (let i = 1; i < canvasPoints.length; i++) {
|
|
1858
|
+
const point = canvasPoints[i];
|
|
1859
|
+
if (point) {
|
|
1860
|
+
this.ctx.lineTo(point.x, point.y);
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
this.ctx.closePath();
|
|
1864
|
+
}
|
|
1865
|
+
this.ctx.fillStyle = soldermaskColor;
|
|
1866
|
+
this.ctx.fill();
|
|
1867
|
+
drawPath({
|
|
1868
|
+
ctx: this.ctx,
|
|
1869
|
+
points: outline.map((p) => ({ x: p.x, y: p.y })),
|
|
1870
|
+
stroke: this.colorMap.boardOutline,
|
|
1871
|
+
strokeWidth: 0.1,
|
|
1872
|
+
realToCanvasMat: this.realToCanvasMat,
|
|
1873
|
+
closePath: true
|
|
1874
|
+
});
|
|
1875
|
+
return;
|
|
1876
|
+
}
|
|
1877
|
+
if (width !== void 0 && height !== void 0 && center) {
|
|
1878
|
+
const soldermaskColor = this.colorMap.soldermask[layer] ?? this.colorMap.soldermask.top;
|
|
1879
|
+
drawRect({
|
|
1880
|
+
ctx: this.ctx,
|
|
1881
|
+
center,
|
|
1882
|
+
width,
|
|
1883
|
+
height,
|
|
1884
|
+
fill: soldermaskColor,
|
|
1885
|
+
realToCanvasMat: this.realToCanvasMat
|
|
1886
|
+
});
|
|
1887
|
+
const halfWidth = width / 2;
|
|
1888
|
+
const halfHeight = height / 2;
|
|
1889
|
+
const corners = [
|
|
1890
|
+
{ x: center.x - halfWidth, y: center.y - halfHeight },
|
|
1891
|
+
{ x: center.x + halfWidth, y: center.y - halfHeight },
|
|
1892
|
+
{ x: center.x + halfWidth, y: center.y + halfHeight },
|
|
1893
|
+
{ x: center.x - halfWidth, y: center.y + halfHeight }
|
|
1894
|
+
];
|
|
1895
|
+
drawPath({
|
|
1896
|
+
ctx: this.ctx,
|
|
1897
|
+
points: corners,
|
|
1898
|
+
stroke: this.colorMap.boardOutline,
|
|
1899
|
+
strokeWidth: 0.1,
|
|
1900
|
+
realToCanvasMat: this.realToCanvasMat,
|
|
1901
|
+
closePath: true
|
|
1902
|
+
});
|
|
1529
1903
|
}
|
|
1530
1904
|
}
|
|
1531
1905
|
drawElement(element, options) {
|
|
@@ -1751,6 +2125,9 @@ export {
|
|
|
1751
2125
|
drawPill,
|
|
1752
2126
|
drawPolygon,
|
|
1753
2127
|
drawRect,
|
|
2128
|
+
drawSoldermaskRingForCircle,
|
|
2129
|
+
drawSoldermaskRingForPill,
|
|
2130
|
+
drawSoldermaskRingForRect,
|
|
1754
2131
|
drawText,
|
|
1755
2132
|
getAlphabetLayout,
|
|
1756
2133
|
getTextStartPosition,
|