circuit-to-canvas 0.0.29 → 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 +29 -3
- package/dist/index.js +415 -22
- package/lib/drawer/CircuitToCanvasDrawer.ts +119 -9
- 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/lib/drawer/pcb-render-layer-filter.ts +30 -0
- package/package.json +3 -3
- package/tests/elements/__snapshots__/layer-filter.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-smtpad-soldermask-margin.snap.png +0 -0
- package/tests/elements/layer-filter.test.ts +42 -0
- package/tests/elements/pcb-smtpad-soldermask-margin.test.ts +163 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnyCircuitElement, NinePointAnchor, PcbPlatedHole, PCBVia, PCBHole, PcbSmtPad, PCBTrace, PcbBoard, PcbSilkscreenText, PcbSilkscreenRect, PcbSilkscreenCircle, PcbSilkscreenLine, PcbSilkscreenPath, PcbSilkscreenPill, PcbCutout, PcbCopperPour, PcbCopperText, PcbFabricationNoteText, PcbFabricationNoteRect, PcbNoteRect, PcbFabricationNotePath, PcbNotePath, PcbNoteText, PcbNoteDimension } from 'circuit-json';
|
|
1
|
+
import { AnyCircuitElement, PcbRenderLayer, NinePointAnchor, PcbPlatedHole, PCBVia, PCBHole, PcbSmtPad, PCBTrace, PcbBoard, PcbSilkscreenText, PcbSilkscreenRect, PcbSilkscreenCircle, PcbSilkscreenLine, PcbSilkscreenPath, PcbSilkscreenPill, PcbCutout, PcbCopperPour, PcbCopperText, PcbFabricationNoteText, PcbFabricationNoteRect, PcbNoteRect, PcbFabricationNotePath, PcbNotePath, PcbNoteText, PcbNoteDimension } from 'circuit-json';
|
|
2
2
|
import { Matrix } from 'transformation-matrix';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -86,7 +86,7 @@ interface DrawContext {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
interface DrawElementsOptions {
|
|
89
|
-
layers?:
|
|
89
|
+
layers?: PcbRenderLayer[];
|
|
90
90
|
}
|
|
91
91
|
interface CanvasLike {
|
|
92
92
|
getContext(contextId: "2d"): CanvasContext | null;
|
|
@@ -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,5 +1,24 @@
|
|
|
1
1
|
// lib/drawer/CircuitToCanvasDrawer.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
identity,
|
|
4
|
+
compose,
|
|
5
|
+
translate,
|
|
6
|
+
scale,
|
|
7
|
+
applyToPoint as applyToPoint15
|
|
8
|
+
} from "transformation-matrix";
|
|
9
|
+
|
|
10
|
+
// lib/drawer/pcb-render-layer-filter.ts
|
|
11
|
+
import { getElementRenderLayers } from "@tscircuit/circuit-json-util";
|
|
12
|
+
function shouldDrawElement(element, options) {
|
|
13
|
+
if (!options.layers || options.layers.length === 0) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
const elementLayers = getElementRenderLayers(element);
|
|
17
|
+
if (elementLayers.length === 0) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
return elementLayers.some((layer) => options.layers.includes(layer));
|
|
21
|
+
}
|
|
3
22
|
|
|
4
23
|
// lib/drawer/types.ts
|
|
5
24
|
var DEFAULT_PCB_COLOR_MAP = {
|
|
@@ -496,14 +515,207 @@ function drawPcbHole(params) {
|
|
|
496
515
|
}
|
|
497
516
|
}
|
|
498
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
|
+
|
|
499
693
|
// lib/drawer/elements/pcb-smtpad.ts
|
|
500
694
|
function layerToColor(layer, colorMap) {
|
|
501
695
|
return colorMap.copper[layer] ?? colorMap.copper.top;
|
|
502
696
|
}
|
|
697
|
+
function getSoldermaskColor(layer, colorMap) {
|
|
698
|
+
return colorMap.soldermaskOverCopper[layer] ?? colorMap.soldermaskOverCopper.top;
|
|
699
|
+
}
|
|
503
700
|
function drawPcbSmtPad(params) {
|
|
504
701
|
const { ctx, pad, realToCanvasMat, colorMap } = params;
|
|
505
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;
|
|
506
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
|
+
}
|
|
507
719
|
drawRect({
|
|
508
720
|
ctx,
|
|
509
721
|
center: { x: pad.x, y: pad.y },
|
|
@@ -513,9 +725,35 @@ function drawPcbSmtPad(params) {
|
|
|
513
725
|
realToCanvasMat,
|
|
514
726
|
borderRadius: pad.corner_radius ?? pad.rect_border_radius ?? 0
|
|
515
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
|
+
}
|
|
516
742
|
return;
|
|
517
743
|
}
|
|
518
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
|
+
}
|
|
519
757
|
drawRect({
|
|
520
758
|
ctx,
|
|
521
759
|
center: { x: pad.x, y: pad.y },
|
|
@@ -526,9 +764,32 @@ function drawPcbSmtPad(params) {
|
|
|
526
764
|
borderRadius: pad.corner_radius ?? pad.rect_border_radius ?? 0,
|
|
527
765
|
rotation: pad.ccw_rotation ?? 0
|
|
528
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
|
+
}
|
|
529
781
|
return;
|
|
530
782
|
}
|
|
531
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
|
+
}
|
|
532
793
|
drawCircle({
|
|
533
794
|
ctx,
|
|
534
795
|
center: { x: pad.x, y: pad.y },
|
|
@@ -536,9 +797,30 @@ function drawPcbSmtPad(params) {
|
|
|
536
797
|
fill: color,
|
|
537
798
|
realToCanvasMat
|
|
538
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
|
+
}
|
|
539
811
|
return;
|
|
540
812
|
}
|
|
541
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
|
+
}
|
|
542
824
|
drawPill({
|
|
543
825
|
ctx,
|
|
544
826
|
center: { x: pad.x, y: pad.y },
|
|
@@ -547,9 +829,33 @@ function drawPcbSmtPad(params) {
|
|
|
547
829
|
fill: color,
|
|
548
830
|
realToCanvasMat
|
|
549
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
|
+
}
|
|
550
845
|
return;
|
|
551
846
|
}
|
|
552
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
|
+
}
|
|
553
859
|
drawPill({
|
|
554
860
|
ctx,
|
|
555
861
|
center: { x: pad.x, y: pad.y },
|
|
@@ -559,6 +865,19 @@ function drawPcbSmtPad(params) {
|
|
|
559
865
|
realToCanvasMat,
|
|
560
866
|
rotation: pad.ccw_rotation ?? 0
|
|
561
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
|
+
}
|
|
562
881
|
return;
|
|
563
882
|
}
|
|
564
883
|
if (pad.shape === "polygon") {
|
|
@@ -575,7 +894,7 @@ function drawPcbSmtPad(params) {
|
|
|
575
894
|
}
|
|
576
895
|
|
|
577
896
|
// lib/drawer/shapes/line.ts
|
|
578
|
-
import { applyToPoint as
|
|
897
|
+
import { applyToPoint as applyToPoint7 } from "transformation-matrix";
|
|
579
898
|
function drawLine(params) {
|
|
580
899
|
const {
|
|
581
900
|
ctx,
|
|
@@ -586,8 +905,8 @@ function drawLine(params) {
|
|
|
586
905
|
realToCanvasMat,
|
|
587
906
|
lineCap = "round"
|
|
588
907
|
} = params;
|
|
589
|
-
const [x1, y1] =
|
|
590
|
-
const [x2, y2] =
|
|
908
|
+
const [x1, y1] = applyToPoint7(realToCanvasMat, [start.x, start.y]);
|
|
909
|
+
const [x2, y2] = applyToPoint7(realToCanvasMat, [end.x, end.y]);
|
|
591
910
|
const scaledStrokeWidth = strokeWidth * Math.abs(realToCanvasMat.a);
|
|
592
911
|
ctx.beginPath();
|
|
593
912
|
ctx.moveTo(x1, y1);
|
|
@@ -628,7 +947,7 @@ function drawPcbTrace(params) {
|
|
|
628
947
|
}
|
|
629
948
|
|
|
630
949
|
// lib/drawer/shapes/path.ts
|
|
631
|
-
import { applyToPoint as
|
|
950
|
+
import { applyToPoint as applyToPoint8 } from "transformation-matrix";
|
|
632
951
|
function drawPath(params) {
|
|
633
952
|
const {
|
|
634
953
|
ctx,
|
|
@@ -642,7 +961,7 @@ function drawPath(params) {
|
|
|
642
961
|
if (points.length < 2) return;
|
|
643
962
|
ctx.beginPath();
|
|
644
963
|
const canvasPoints = points.map(
|
|
645
|
-
(p) =>
|
|
964
|
+
(p) => applyToPoint8(realToCanvasMat, [p.x, p.y])
|
|
646
965
|
);
|
|
647
966
|
const firstPoint = canvasPoints[0];
|
|
648
967
|
if (!firstPoint) return;
|
|
@@ -713,11 +1032,11 @@ function drawPcbBoard(params) {
|
|
|
713
1032
|
}
|
|
714
1033
|
|
|
715
1034
|
// lib/drawer/elements/pcb-silkscreen-text.ts
|
|
716
|
-
import { applyToPoint as
|
|
1035
|
+
import { applyToPoint as applyToPoint10 } from "transformation-matrix";
|
|
717
1036
|
|
|
718
1037
|
// lib/drawer/shapes/text/text.ts
|
|
719
1038
|
import { lineAlphabet } from "@tscircuit/alphabet";
|
|
720
|
-
import { applyToPoint as
|
|
1039
|
+
import { applyToPoint as applyToPoint9 } from "transformation-matrix";
|
|
721
1040
|
|
|
722
1041
|
// lib/drawer/shapes/text/getAlphabetLayout.ts
|
|
723
1042
|
var GLYPH_WIDTH_RATIO = 0.62;
|
|
@@ -814,7 +1133,7 @@ function drawText(params) {
|
|
|
814
1133
|
rotation = 0
|
|
815
1134
|
} = params;
|
|
816
1135
|
if (!text) return;
|
|
817
|
-
const [canvasX, canvasY] =
|
|
1136
|
+
const [canvasX, canvasY] = applyToPoint9(realToCanvasMat, [x, y]);
|
|
818
1137
|
const scale2 = Math.abs(realToCanvasMat.a);
|
|
819
1138
|
const scaledFontSize = fontSize * scale2;
|
|
820
1139
|
const layout = getAlphabetLayout(text, scaledFontSize);
|
|
@@ -851,7 +1170,7 @@ function drawPcbSilkscreenText(params) {
|
|
|
851
1170
|
const content = text.text ?? "";
|
|
852
1171
|
if (!content) return;
|
|
853
1172
|
const color = layerToSilkscreenColor(text.layer, colorMap);
|
|
854
|
-
const [x, y] =
|
|
1173
|
+
const [x, y] = applyToPoint10(realToCanvasMat, [
|
|
855
1174
|
text.anchor_position.x,
|
|
856
1175
|
text.anchor_position.y
|
|
857
1176
|
]);
|
|
@@ -1014,7 +1333,7 @@ function drawPcbCutout(params) {
|
|
|
1014
1333
|
}
|
|
1015
1334
|
|
|
1016
1335
|
// lib/drawer/elements/pcb-copper-pour.ts
|
|
1017
|
-
import { applyToPoint as
|
|
1336
|
+
import { applyToPoint as applyToPoint11 } from "transformation-matrix";
|
|
1018
1337
|
function layerToColor3(layer, colorMap) {
|
|
1019
1338
|
return colorMap.copper[layer] ?? colorMap.copper.top;
|
|
1020
1339
|
}
|
|
@@ -1023,7 +1342,7 @@ function drawPcbCopperPour(params) {
|
|
|
1023
1342
|
const color = layerToColor3(pour.layer, colorMap);
|
|
1024
1343
|
ctx.save();
|
|
1025
1344
|
if (pour.shape === "rect") {
|
|
1026
|
-
const [cx, cy] =
|
|
1345
|
+
const [cx, cy] = applyToPoint11(realToCanvasMat, [
|
|
1027
1346
|
pour.center.x,
|
|
1028
1347
|
pour.center.y
|
|
1029
1348
|
]);
|
|
@@ -1044,7 +1363,7 @@ function drawPcbCopperPour(params) {
|
|
|
1044
1363
|
if (pour.shape === "polygon") {
|
|
1045
1364
|
if (pour.points && pour.points.length >= 3) {
|
|
1046
1365
|
const canvasPoints = pour.points.map(
|
|
1047
|
-
(p) =>
|
|
1366
|
+
(p) => applyToPoint11(realToCanvasMat, [p.x, p.y])
|
|
1048
1367
|
);
|
|
1049
1368
|
const firstPoint = canvasPoints[0];
|
|
1050
1369
|
if (!firstPoint) {
|
|
@@ -1072,7 +1391,7 @@ function drawPcbCopperPour(params) {
|
|
|
1072
1391
|
}
|
|
1073
1392
|
|
|
1074
1393
|
// lib/drawer/elements/pcb-copper-text.ts
|
|
1075
|
-
import { applyToPoint as
|
|
1394
|
+
import { applyToPoint as applyToPoint12 } from "transformation-matrix";
|
|
1076
1395
|
var DEFAULT_PADDING = { left: 0.2, right: 0.2, top: 0.2, bottom: 0.2 };
|
|
1077
1396
|
function layerToCopperColor(layer, colorMap) {
|
|
1078
1397
|
return colorMap.copper[layer] ?? colorMap.copper.top;
|
|
@@ -1087,7 +1406,7 @@ function drawPcbCopperText(params) {
|
|
|
1087
1406
|
const { ctx, text, realToCanvasMat, colorMap } = params;
|
|
1088
1407
|
const content = text.text ?? "";
|
|
1089
1408
|
if (!content) return;
|
|
1090
|
-
const [x, y] =
|
|
1409
|
+
const [x, y] = applyToPoint12(realToCanvasMat, [
|
|
1091
1410
|
text.anchor_position.x,
|
|
1092
1411
|
text.anchor_position.y
|
|
1093
1412
|
]);
|
|
@@ -1271,7 +1590,7 @@ function drawPcbNoteText(params) {
|
|
|
1271
1590
|
}
|
|
1272
1591
|
|
|
1273
1592
|
// lib/drawer/elements/pcb-note-dimension.ts
|
|
1274
|
-
import { applyToPoint as
|
|
1593
|
+
import { applyToPoint as applyToPoint13 } from "transformation-matrix";
|
|
1275
1594
|
|
|
1276
1595
|
// lib/drawer/shapes/arrow.ts
|
|
1277
1596
|
function drawArrow(params) {
|
|
@@ -1356,11 +1675,11 @@ function drawPcbNoteDimension(params) {
|
|
|
1356
1675
|
stroke: color,
|
|
1357
1676
|
realToCanvasMat
|
|
1358
1677
|
});
|
|
1359
|
-
const [canvasFromX, canvasFromY] =
|
|
1678
|
+
const [canvasFromX, canvasFromY] = applyToPoint13(realToCanvasMat, [
|
|
1360
1679
|
fromX,
|
|
1361
1680
|
fromY
|
|
1362
1681
|
]);
|
|
1363
|
-
const [canvasToX, canvasToY] =
|
|
1682
|
+
const [canvasToX, canvasToY] = applyToPoint13(realToCanvasMat, [toX, toY]);
|
|
1364
1683
|
const canvasDx = canvasToX - canvasFromX;
|
|
1365
1684
|
const canvasDy = canvasToY - canvasFromY;
|
|
1366
1685
|
const lineAngle = Math.atan2(canvasDy, canvasDx);
|
|
@@ -1421,15 +1740,15 @@ function drawPcbNoteDimension(params) {
|
|
|
1421
1740
|
}
|
|
1422
1741
|
|
|
1423
1742
|
// lib/drawer/elements/pcb-note-line.ts
|
|
1424
|
-
import { applyToPoint as
|
|
1743
|
+
import { applyToPoint as applyToPoint14 } from "transformation-matrix";
|
|
1425
1744
|
function drawPcbNoteLine(params) {
|
|
1426
1745
|
const { ctx, line, realToCanvasMat, colorMap } = params;
|
|
1427
1746
|
const defaultColor = "rgb(89, 148, 220)";
|
|
1428
1747
|
const color = line.color ?? defaultColor;
|
|
1429
1748
|
const strokeWidth = line.stroke_width ?? 0.1;
|
|
1430
1749
|
const isDashed = line.is_dashed ?? false;
|
|
1431
|
-
const [x1, y1] =
|
|
1432
|
-
const [x2, y2] =
|
|
1750
|
+
const [x1, y1] = applyToPoint14(realToCanvasMat, [line.x1, line.y1]);
|
|
1751
|
+
const [x2, y2] = applyToPoint14(realToCanvasMat, [line.x2, line.y2]);
|
|
1433
1752
|
const scaledStrokeWidth = strokeWidth * Math.abs(realToCanvasMat.a);
|
|
1434
1753
|
ctx.save();
|
|
1435
1754
|
if (isDashed) {
|
|
@@ -1511,11 +1830,82 @@ var CircuitToCanvasDrawer = class {
|
|
|
1511
1830
|
);
|
|
1512
1831
|
}
|
|
1513
1832
|
drawElements(elements, options = {}) {
|
|
1833
|
+
const hasSoldermaskPads = elements.some(
|
|
1834
|
+
(el) => el.type === "pcb_smtpad" && el.is_covered_with_solder_mask === true
|
|
1835
|
+
);
|
|
1514
1836
|
for (const element of elements) {
|
|
1515
|
-
|
|
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
|
+
});
|
|
1516
1903
|
}
|
|
1517
1904
|
}
|
|
1518
1905
|
drawElement(element, options) {
|
|
1906
|
+
if (!shouldDrawElement(element, options)) {
|
|
1907
|
+
return;
|
|
1908
|
+
}
|
|
1519
1909
|
if (element.type === "pcb_plated_hole") {
|
|
1520
1910
|
drawPcbPlatedHole({
|
|
1521
1911
|
ctx: this.ctx,
|
|
@@ -1735,6 +2125,9 @@ export {
|
|
|
1735
2125
|
drawPill,
|
|
1736
2126
|
drawPolygon,
|
|
1737
2127
|
drawRect,
|
|
2128
|
+
drawSoldermaskRingForCircle,
|
|
2129
|
+
drawSoldermaskRingForPill,
|
|
2130
|
+
drawSoldermaskRingForRect,
|
|
1738
2131
|
drawText,
|
|
1739
2132
|
getAlphabetLayout,
|
|
1740
2133
|
getTextStartPosition,
|