circuit-to-canvas 0.0.3 → 0.0.5
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/README.md +1 -1
- package/dist/index.d.ts +82 -3
- package/dist/index.js +357 -4
- package/lib/drawer/CircuitToCanvasDrawer.ts +55 -0
- package/lib/drawer/elements/index.ts +25 -0
- package/lib/drawer/elements/pcb-copper-text.ts +99 -0
- package/lib/drawer/elements/pcb-fabrication-note-path.ts +41 -0
- package/lib/drawer/elements/pcb-fabrication-note-rect.ts +39 -0
- package/lib/drawer/elements/pcb-fabrication-note-text.ts +42 -0
- package/lib/drawer/elements/pcb-note-rect.ts +37 -0
- package/lib/drawer/shapes/index.ts +9 -0
- package/lib/drawer/shapes/rect.ts +28 -3
- package/lib/drawer/shapes/text.ts +218 -0
- package/lib/drawer/types.ts +8 -0
- package/package.json +3 -1
- package/tests/elements/__snapshots__/pcb-copper-text-knockout.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-copper-text.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-fabrication-note-path-custom-color.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-fabrication-note-path-thick-stroke.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-fabrication-note-path.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-fabrication-note-rect-all-features.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-fabrication-note-rect-corner-radius.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-fabrication-note-rect-custom-color.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-fabrication-note-rect-dashed.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-fabrication-note-rect-default-color.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-fabrication-note-text-rgba-color.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-fabrication-note-text-small.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-note-rect-all-features.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-note-rect-dashed-stroke.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-note-rect-filled-no-stroke.snap.png +0 -0
- package/tests/elements/pcb-copper-text.test.ts +102 -0
- package/tests/elements/pcb-fabrication-note-path-custom-color.test.ts +37 -0
- package/tests/elements/pcb-fabrication-note-path-thick-stroke.test.ts +37 -0
- package/tests/elements/pcb-fabrication-note-path.test.ts +36 -0
- package/tests/elements/pcb-fabrication-note-rect-all-features.test.ts +35 -0
- package/tests/elements/pcb-fabrication-note-rect-corner-radius.test.ts +33 -0
- package/tests/elements/pcb-fabrication-note-rect-custom-color.test.ts +32 -0
- package/tests/elements/pcb-fabrication-note-rect-dashed.test.ts +33 -0
- package/tests/elements/pcb-fabrication-note-rect-default-color.test.ts +32 -0
- package/tests/elements/pcb-fabrication-note-text-rgba-color.test.ts +34 -0
- package/tests/elements/pcb-fabrication-note-text-small.test.ts +33 -0
- package/tests/elements/pcb-note-rect-all-features.test.ts +38 -0
- package/tests/elements/pcb-note-rect-dashed-stroke.test.ts +32 -0
- package/tests/elements/pcb-note-rect-filled-no-stroke.test.ts +32 -0
package/README.md
CHANGED
|
@@ -71,7 +71,7 @@ This checklist tracks PCB drawing features from [circuit-to-svg](https://github.
|
|
|
71
71
|
|
|
72
72
|
### Fabrication Notes
|
|
73
73
|
|
|
74
|
-
- [
|
|
74
|
+
- [x] `pcb_fabrication_note_text` - Fabrication note text
|
|
75
75
|
- [ ] `pcb_fabrication_note_rect` - Fabrication note rectangles
|
|
76
76
|
- [ ] `pcb_fabrication_note_path` - Fabrication note paths
|
|
77
77
|
- [ ] `pcb_fabrication_note_dimension` - Fabrication dimension annotations
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnyCircuitElement, PcbPlatedHole, PCBVia, PCBHole, PcbSmtPad, PCBTrace, PcbBoard, PcbSilkscreenText, PcbSilkscreenRect, PcbSilkscreenCircle, PcbSilkscreenLine, PcbSilkscreenPath, PcbCutout, PcbCopperPour } from 'circuit-json';
|
|
1
|
+
import { AnyCircuitElement, PcbPlatedHole, PCBVia, PCBHole, PcbSmtPad, PCBTrace, PcbBoard, PcbSilkscreenText, PcbSilkscreenRect, PcbSilkscreenCircle, PcbSilkscreenLine, PcbSilkscreenPath, PcbCutout, PcbCopperPour, PcbCopperText, PcbFabricationNoteText, PcbFabricationNoteRect, PcbNoteRect, PcbFabricationNotePath } from 'circuit-json';
|
|
2
2
|
import { Matrix } from 'transformation-matrix';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -21,16 +21,24 @@ interface CanvasContext {
|
|
|
21
21
|
translate(x: number, y: number): void;
|
|
22
22
|
rotate(angle: number): void;
|
|
23
23
|
scale(x: number, y: number): void;
|
|
24
|
+
globalCompositeOperation?: string;
|
|
24
25
|
fillStyle: string | CanvasGradient | CanvasPattern;
|
|
25
26
|
strokeStyle: string | CanvasGradient | CanvasPattern;
|
|
26
27
|
lineWidth: number;
|
|
27
28
|
lineCap: "butt" | "round" | "square";
|
|
28
29
|
lineJoin: "bevel" | "round" | "miter";
|
|
30
|
+
setLineDash(segments: number[]): void;
|
|
29
31
|
canvas: {
|
|
30
32
|
width: number;
|
|
31
33
|
height: number;
|
|
32
34
|
};
|
|
33
35
|
fillText(text: string, x: number, y: number): void;
|
|
36
|
+
fillRect(x: number, y: number, width: number, height: number): void;
|
|
37
|
+
measureText?: (text: string) => {
|
|
38
|
+
width: number;
|
|
39
|
+
actualBoundingBoxAscent?: number;
|
|
40
|
+
actualBoundingBoxDescent?: number;
|
|
41
|
+
};
|
|
34
42
|
font: string;
|
|
35
43
|
textAlign: "start" | "end" | "left" | "right" | "center";
|
|
36
44
|
textBaseline: "top" | "hanging" | "middle" | "alphabetic" | "ideographic" | "bottom";
|
|
@@ -115,10 +123,13 @@ interface DrawRectParams {
|
|
|
115
123
|
};
|
|
116
124
|
width: number;
|
|
117
125
|
height: number;
|
|
118
|
-
fill
|
|
126
|
+
fill?: string;
|
|
119
127
|
transform: Matrix;
|
|
120
128
|
borderRadius?: number;
|
|
121
129
|
rotation?: number;
|
|
130
|
+
stroke?: string;
|
|
131
|
+
strokeWidth?: number;
|
|
132
|
+
isStrokeDashed?: boolean;
|
|
122
133
|
}
|
|
123
134
|
declare function drawRect(params: DrawRectParams): void;
|
|
124
135
|
|
|
@@ -192,6 +203,34 @@ interface DrawPathParams {
|
|
|
192
203
|
}
|
|
193
204
|
declare function drawPath(params: DrawPathParams): void;
|
|
194
205
|
|
|
206
|
+
type AlphabetLayout = {
|
|
207
|
+
width: number;
|
|
208
|
+
height: number;
|
|
209
|
+
glyphWidth: number;
|
|
210
|
+
letterSpacing: number;
|
|
211
|
+
spaceWidth: number;
|
|
212
|
+
strokeWidth: number;
|
|
213
|
+
};
|
|
214
|
+
declare function getAlphabetLayout(text: string, fontSize: number): AlphabetLayout;
|
|
215
|
+
type AnchorAlignment = "center" | "top_left" | "top_right" | "bottom_left" | "bottom_right" | "left" | "right" | "top" | "bottom";
|
|
216
|
+
declare function getTextStartPosition(alignment: AnchorAlignment, layout: AlphabetLayout): {
|
|
217
|
+
x: number;
|
|
218
|
+
y: number;
|
|
219
|
+
};
|
|
220
|
+
declare function strokeAlphabetText(ctx: CanvasContext, text: string, layout: AlphabetLayout, startX: number, startY: number): void;
|
|
221
|
+
interface DrawTextParams {
|
|
222
|
+
ctx: CanvasContext;
|
|
223
|
+
text: string;
|
|
224
|
+
x: number;
|
|
225
|
+
y: number;
|
|
226
|
+
fontSize: number;
|
|
227
|
+
color: string;
|
|
228
|
+
transform: Matrix;
|
|
229
|
+
anchorAlignment: AnchorAlignment;
|
|
230
|
+
rotation?: number;
|
|
231
|
+
}
|
|
232
|
+
declare function drawText(params: DrawTextParams): void;
|
|
233
|
+
|
|
195
234
|
interface DrawPcbPlatedHoleParams {
|
|
196
235
|
ctx: CanvasContext;
|
|
197
236
|
hole: PcbPlatedHole;
|
|
@@ -292,4 +331,44 @@ interface DrawPcbCopperPourParams {
|
|
|
292
331
|
}
|
|
293
332
|
declare function drawPcbCopperPour(params: DrawPcbCopperPourParams): void;
|
|
294
333
|
|
|
295
|
-
|
|
334
|
+
interface DrawPcbCopperTextParams {
|
|
335
|
+
ctx: CanvasContext;
|
|
336
|
+
text: PcbCopperText;
|
|
337
|
+
transform: Matrix;
|
|
338
|
+
colorMap: PcbColorMap;
|
|
339
|
+
}
|
|
340
|
+
declare function drawPcbCopperText(params: DrawPcbCopperTextParams): void;
|
|
341
|
+
|
|
342
|
+
interface DrawPcbFabricationNoteTextParams {
|
|
343
|
+
ctx: CanvasContext;
|
|
344
|
+
text: PcbFabricationNoteText;
|
|
345
|
+
transform: Matrix;
|
|
346
|
+
colorMap: PcbColorMap;
|
|
347
|
+
}
|
|
348
|
+
declare function drawPcbFabricationNoteText(params: DrawPcbFabricationNoteTextParams): void;
|
|
349
|
+
|
|
350
|
+
interface DrawPcbFabricationNoteRectParams {
|
|
351
|
+
ctx: CanvasContext;
|
|
352
|
+
rect: PcbFabricationNoteRect;
|
|
353
|
+
transform: Matrix;
|
|
354
|
+
colorMap: PcbColorMap;
|
|
355
|
+
}
|
|
356
|
+
declare function drawPcbFabricationNoteRect(params: DrawPcbFabricationNoteRectParams): void;
|
|
357
|
+
|
|
358
|
+
interface DrawPcbNoteRectParams {
|
|
359
|
+
ctx: CanvasContext;
|
|
360
|
+
rect: PcbNoteRect;
|
|
361
|
+
transform: Matrix;
|
|
362
|
+
colorMap: PcbColorMap;
|
|
363
|
+
}
|
|
364
|
+
declare function drawPcbNoteRect(params: DrawPcbNoteRectParams): void;
|
|
365
|
+
|
|
366
|
+
interface DrawPcbFabricationNotePathParams {
|
|
367
|
+
ctx: CanvasContext;
|
|
368
|
+
path: PcbFabricationNotePath;
|
|
369
|
+
transform: Matrix;
|
|
370
|
+
colorMap: PcbColorMap;
|
|
371
|
+
}
|
|
372
|
+
declare function drawPcbFabricationNotePath(params: DrawPcbFabricationNotePathParams): void;
|
|
373
|
+
|
|
374
|
+
export { type AlphabetLayout, type AnchorAlignment, type CameraBounds, type CanvasContext, CircuitToCanvasDrawer, type CopperColorMap, type CopperLayerName, DEFAULT_PCB_COLOR_MAP, 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 DrawPcbNoteRectParams, type DrawPcbPlatedHoleParams, type DrawPcbSilkscreenCircleParams, type DrawPcbSilkscreenLineParams, type DrawPcbSilkscreenPathParams, type DrawPcbSilkscreenRectParams, type DrawPcbSilkscreenTextParams, type DrawPcbSmtPadParams, type DrawPcbTraceParams, type DrawPcbViaParams, type DrawPillParams, type DrawPolygonParams, type DrawRectParams, type DrawTextParams, type DrawerConfig, type PcbColorMap, drawCircle, drawLine, drawOval, drawPath, drawPcbBoard, drawPcbCopperPour, drawPcbCopperText, drawPcbCutout, drawPcbFabricationNotePath, drawPcbFabricationNoteRect, drawPcbFabricationNoteText, drawPcbHole, drawPcbNoteRect, drawPcbPlatedHole, drawPcbSilkscreenCircle, drawPcbSilkscreenLine, drawPcbSilkscreenPath, drawPcbSilkscreenRect, drawPcbSilkscreenText, drawPcbSmtPad, drawPcbTrace, drawPcbVia, drawPill, drawPolygon, drawRect, drawText, getAlphabetLayout, getTextStartPosition, strokeAlphabetText };
|
package/dist/index.js
CHANGED
|
@@ -58,17 +58,26 @@ function drawRect(params) {
|
|
|
58
58
|
fill,
|
|
59
59
|
transform,
|
|
60
60
|
borderRadius = 0,
|
|
61
|
-
rotation = 0
|
|
61
|
+
rotation = 0,
|
|
62
|
+
stroke,
|
|
63
|
+
strokeWidth,
|
|
64
|
+
isStrokeDashed = false
|
|
62
65
|
} = params;
|
|
63
66
|
const [cx, cy] = applyToPoint2(transform, [center.x, center.y]);
|
|
64
67
|
const scaledWidth = width * Math.abs(transform.a);
|
|
65
68
|
const scaledHeight = height * Math.abs(transform.a);
|
|
66
69
|
const scaledRadius = borderRadius * Math.abs(transform.a);
|
|
70
|
+
const scaledStrokeWidth = strokeWidth ? strokeWidth * Math.abs(transform.a) : void 0;
|
|
67
71
|
ctx.save();
|
|
68
72
|
ctx.translate(cx, cy);
|
|
69
73
|
if (rotation !== 0) {
|
|
70
74
|
ctx.rotate(-rotation * (Math.PI / 180));
|
|
71
75
|
}
|
|
76
|
+
if (isStrokeDashed && scaledStrokeWidth) {
|
|
77
|
+
ctx.setLineDash([scaledStrokeWidth * 2, scaledStrokeWidth * 2]);
|
|
78
|
+
} else {
|
|
79
|
+
ctx.setLineDash([]);
|
|
80
|
+
}
|
|
72
81
|
ctx.beginPath();
|
|
73
82
|
if (scaledRadius > 0) {
|
|
74
83
|
const x = -scaledWidth / 2;
|
|
@@ -92,8 +101,15 @@ function drawRect(params) {
|
|
|
92
101
|
} else {
|
|
93
102
|
ctx.rect(-scaledWidth / 2, -scaledHeight / 2, scaledWidth, scaledHeight);
|
|
94
103
|
}
|
|
95
|
-
|
|
96
|
-
|
|
104
|
+
if (fill) {
|
|
105
|
+
ctx.fillStyle = fill;
|
|
106
|
+
ctx.fill();
|
|
107
|
+
}
|
|
108
|
+
if (stroke && scaledStrokeWidth) {
|
|
109
|
+
ctx.strokeStyle = stroke;
|
|
110
|
+
ctx.lineWidth = scaledStrokeWidth;
|
|
111
|
+
ctx.stroke();
|
|
112
|
+
}
|
|
97
113
|
ctx.restore();
|
|
98
114
|
}
|
|
99
115
|
|
|
@@ -794,6 +810,294 @@ function drawPcbCopperPour(params) {
|
|
|
794
810
|
ctx.restore();
|
|
795
811
|
}
|
|
796
812
|
|
|
813
|
+
// lib/drawer/elements/pcb-copper-text.ts
|
|
814
|
+
import { applyToPoint as applyToPoint11 } from "transformation-matrix";
|
|
815
|
+
|
|
816
|
+
// lib/drawer/shapes/text.ts
|
|
817
|
+
import { lineAlphabet } from "@tscircuit/alphabet";
|
|
818
|
+
import { applyToPoint as applyToPoint10 } from "transformation-matrix";
|
|
819
|
+
var GLYPH_WIDTH_RATIO = 0.62;
|
|
820
|
+
var LETTER_SPACING_RATIO = 0.3;
|
|
821
|
+
var SPACE_WIDTH_RATIO = 1;
|
|
822
|
+
var STROKE_WIDTH_RATIO = 0.13;
|
|
823
|
+
var CURVED_GLYPHS = /* @__PURE__ */ new Set(["O", "o", "0"]);
|
|
824
|
+
function getAlphabetLayout(text, fontSize) {
|
|
825
|
+
const glyphWidth = fontSize * GLYPH_WIDTH_RATIO;
|
|
826
|
+
const letterSpacing = glyphWidth * LETTER_SPACING_RATIO;
|
|
827
|
+
const spaceWidth = glyphWidth * SPACE_WIDTH_RATIO;
|
|
828
|
+
const characters = Array.from(text);
|
|
829
|
+
let width = 0;
|
|
830
|
+
characters.forEach((char, index) => {
|
|
831
|
+
const advance = char === " " ? spaceWidth : glyphWidth;
|
|
832
|
+
width += advance;
|
|
833
|
+
if (index < characters.length - 1) width += letterSpacing;
|
|
834
|
+
});
|
|
835
|
+
const strokeWidth = Math.max(fontSize * STROKE_WIDTH_RATIO, 0.35);
|
|
836
|
+
return {
|
|
837
|
+
width,
|
|
838
|
+
height: fontSize,
|
|
839
|
+
glyphWidth,
|
|
840
|
+
letterSpacing,
|
|
841
|
+
spaceWidth,
|
|
842
|
+
strokeWidth
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
var getGlyphLines = (char) => lineAlphabet[char] ?? lineAlphabet[char.toUpperCase()];
|
|
846
|
+
function getTextStartPosition(alignment, layout) {
|
|
847
|
+
const totalWidth = layout.width + layout.strokeWidth;
|
|
848
|
+
const totalHeight = layout.height + layout.strokeWidth;
|
|
849
|
+
let x = 0;
|
|
850
|
+
let y = 0;
|
|
851
|
+
if (alignment === "center") {
|
|
852
|
+
x = -totalWidth / 2;
|
|
853
|
+
} else if (alignment === "top_left" || alignment === "bottom_left" || alignment === "left") {
|
|
854
|
+
x = 0;
|
|
855
|
+
} else if (alignment === "top_right" || alignment === "bottom_right" || alignment === "right") {
|
|
856
|
+
x = -totalWidth;
|
|
857
|
+
}
|
|
858
|
+
if (alignment === "center") {
|
|
859
|
+
y = -totalHeight / 2;
|
|
860
|
+
} else if (alignment === "top_left" || alignment === "top_right" || alignment === "top") {
|
|
861
|
+
y = 0;
|
|
862
|
+
} else if (alignment === "bottom_left" || alignment === "bottom_right" || alignment === "bottom") {
|
|
863
|
+
y = -totalHeight;
|
|
864
|
+
}
|
|
865
|
+
return { x, y };
|
|
866
|
+
}
|
|
867
|
+
function strokeAlphabetText(ctx, text, layout, startX, startY) {
|
|
868
|
+
const { glyphWidth, letterSpacing, spaceWidth, height, strokeWidth } = layout;
|
|
869
|
+
const yOffset = startY + height / 2;
|
|
870
|
+
const characters = Array.from(text);
|
|
871
|
+
let cursor = startX + strokeWidth / 2;
|
|
872
|
+
characters.forEach((char, index) => {
|
|
873
|
+
const lines = getGlyphLines(char);
|
|
874
|
+
const advance = char === " " ? spaceWidth : glyphWidth;
|
|
875
|
+
if (CURVED_GLYPHS.has(char)) {
|
|
876
|
+
const radiusX = Math.max(glyphWidth / 2 - strokeWidth / 2, strokeWidth);
|
|
877
|
+
const radiusY = Math.max(height / 2 - strokeWidth / 2, strokeWidth);
|
|
878
|
+
const centerY = yOffset - height / 2;
|
|
879
|
+
ctx.beginPath();
|
|
880
|
+
ctx.ellipse(
|
|
881
|
+
cursor + glyphWidth / 2,
|
|
882
|
+
centerY,
|
|
883
|
+
radiusX,
|
|
884
|
+
radiusY,
|
|
885
|
+
0,
|
|
886
|
+
0,
|
|
887
|
+
Math.PI * 2
|
|
888
|
+
);
|
|
889
|
+
ctx.stroke();
|
|
890
|
+
} else if (lines?.length) {
|
|
891
|
+
ctx.beginPath();
|
|
892
|
+
for (const line of lines) {
|
|
893
|
+
const x1 = cursor + line.x1 * glyphWidth;
|
|
894
|
+
const y1 = yOffset - line.y1 * height;
|
|
895
|
+
const x2 = cursor + line.x2 * glyphWidth;
|
|
896
|
+
const y2 = yOffset - line.y2 * height;
|
|
897
|
+
ctx.moveTo(x1, y1);
|
|
898
|
+
ctx.lineTo(x2, y2);
|
|
899
|
+
}
|
|
900
|
+
ctx.stroke();
|
|
901
|
+
}
|
|
902
|
+
cursor += advance;
|
|
903
|
+
if (index < characters.length - 1) {
|
|
904
|
+
cursor += letterSpacing;
|
|
905
|
+
}
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
function drawText(params) {
|
|
909
|
+
const {
|
|
910
|
+
ctx,
|
|
911
|
+
text,
|
|
912
|
+
x,
|
|
913
|
+
y,
|
|
914
|
+
fontSize,
|
|
915
|
+
color,
|
|
916
|
+
transform,
|
|
917
|
+
anchorAlignment,
|
|
918
|
+
rotation = 0
|
|
919
|
+
} = params;
|
|
920
|
+
if (!text) return;
|
|
921
|
+
const [transformedX, transformedY] = applyToPoint10(transform, [x, y]);
|
|
922
|
+
const scale2 = Math.abs(transform.a);
|
|
923
|
+
const scaledFontSize = fontSize * scale2;
|
|
924
|
+
const layout = getAlphabetLayout(text, scaledFontSize);
|
|
925
|
+
const startPos = getTextStartPosition(anchorAlignment, layout);
|
|
926
|
+
ctx.save();
|
|
927
|
+
ctx.translate(transformedX, transformedY);
|
|
928
|
+
if (rotation !== 0) {
|
|
929
|
+
ctx.rotate(-rotation * (Math.PI / 180));
|
|
930
|
+
}
|
|
931
|
+
ctx.lineWidth = layout.strokeWidth;
|
|
932
|
+
ctx.lineCap = "round";
|
|
933
|
+
ctx.lineJoin = "round";
|
|
934
|
+
ctx.strokeStyle = color;
|
|
935
|
+
strokeAlphabetText(
|
|
936
|
+
ctx,
|
|
937
|
+
text,
|
|
938
|
+
layout,
|
|
939
|
+
startPos.x,
|
|
940
|
+
startPos.y + layout.strokeWidth / 2
|
|
941
|
+
);
|
|
942
|
+
ctx.restore();
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// lib/drawer/elements/pcb-copper-text.ts
|
|
946
|
+
var DEFAULT_PADDING = { left: 0.2, right: 0.2, top: 0.2, bottom: 0.2 };
|
|
947
|
+
function layerToCopperColor(layer, colorMap) {
|
|
948
|
+
return colorMap.copper[layer] ?? colorMap.copper.top;
|
|
949
|
+
}
|
|
950
|
+
function mapAnchorAlignment2(alignment) {
|
|
951
|
+
if (!alignment) return "center";
|
|
952
|
+
if (alignment.includes("left")) return "left";
|
|
953
|
+
if (alignment.includes("right")) return "right";
|
|
954
|
+
return "center";
|
|
955
|
+
}
|
|
956
|
+
function drawPcbCopperText(params) {
|
|
957
|
+
const { ctx, text, transform, colorMap } = params;
|
|
958
|
+
const content = text.text ?? "";
|
|
959
|
+
if (!content) return;
|
|
960
|
+
const [x, y] = applyToPoint11(transform, [
|
|
961
|
+
text.anchor_position.x,
|
|
962
|
+
text.anchor_position.y
|
|
963
|
+
]);
|
|
964
|
+
const scale2 = Math.abs(transform.a);
|
|
965
|
+
const fontSize = (text.font_size ?? 1) * scale2;
|
|
966
|
+
const rotation = text.ccw_rotation ?? 0;
|
|
967
|
+
const padding = {
|
|
968
|
+
...DEFAULT_PADDING,
|
|
969
|
+
...text.knockout_padding
|
|
970
|
+
};
|
|
971
|
+
const textColor = layerToCopperColor(text.layer, colorMap);
|
|
972
|
+
const layout = getAlphabetLayout(content, fontSize);
|
|
973
|
+
const totalWidth = layout.width + layout.strokeWidth;
|
|
974
|
+
const totalHeight = layout.height + layout.strokeWidth;
|
|
975
|
+
const alignment = mapAnchorAlignment2(text.anchor_alignment);
|
|
976
|
+
const startPos = getTextStartPosition(alignment, layout);
|
|
977
|
+
const startX = startPos.x;
|
|
978
|
+
const startY = 0;
|
|
979
|
+
ctx.save();
|
|
980
|
+
ctx.translate(x, y);
|
|
981
|
+
if (text.is_mirrored) ctx.scale(-1, 1);
|
|
982
|
+
if (rotation !== 0) ctx.rotate(-rotation * (Math.PI / 180));
|
|
983
|
+
ctx.lineWidth = layout.strokeWidth;
|
|
984
|
+
ctx.lineCap = "round";
|
|
985
|
+
ctx.lineJoin = "round";
|
|
986
|
+
if (text.is_knockout) {
|
|
987
|
+
const paddingLeft = padding.left * scale2;
|
|
988
|
+
const paddingRight = padding.right * scale2;
|
|
989
|
+
const paddingTop = padding.top * scale2;
|
|
990
|
+
const paddingBottom = padding.bottom * scale2;
|
|
991
|
+
const xOffset = startX - paddingLeft;
|
|
992
|
+
const yOffset = -(layout.height / 2) - layout.strokeWidth / 2 - paddingTop;
|
|
993
|
+
const knockoutWidth = totalWidth + paddingLeft + paddingRight;
|
|
994
|
+
const knockoutHeight = totalHeight + paddingTop + paddingBottom;
|
|
995
|
+
ctx.fillStyle = textColor;
|
|
996
|
+
ctx.fillRect(xOffset, yOffset, knockoutWidth, knockoutHeight);
|
|
997
|
+
const previousCompositeOperation = ctx.globalCompositeOperation;
|
|
998
|
+
ctx.globalCompositeOperation = "destination-out";
|
|
999
|
+
ctx.fillStyle = "rgba(0,0,0,1)";
|
|
1000
|
+
ctx.strokeStyle = "rgba(0,0,0,1)";
|
|
1001
|
+
strokeAlphabetText(ctx, content, layout, startX, startY);
|
|
1002
|
+
if (previousCompositeOperation) {
|
|
1003
|
+
ctx.globalCompositeOperation = previousCompositeOperation;
|
|
1004
|
+
} else {
|
|
1005
|
+
ctx.globalCompositeOperation = "source-over";
|
|
1006
|
+
}
|
|
1007
|
+
} else {
|
|
1008
|
+
ctx.strokeStyle = textColor;
|
|
1009
|
+
strokeAlphabetText(ctx, content, layout, startX, startY);
|
|
1010
|
+
}
|
|
1011
|
+
ctx.restore();
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
// lib/drawer/elements/pcb-fabrication-note-text.ts
|
|
1015
|
+
var DEFAULT_FABRICATION_NOTE_COLOR = "rgba(255,255,255,0.5)";
|
|
1016
|
+
function layerToColor4(layer, colorMap) {
|
|
1017
|
+
return DEFAULT_FABRICATION_NOTE_COLOR;
|
|
1018
|
+
}
|
|
1019
|
+
function drawPcbFabricationNoteText(params) {
|
|
1020
|
+
const { ctx, text, transform, colorMap } = params;
|
|
1021
|
+
const defaultColor = layerToColor4(text.layer, colorMap);
|
|
1022
|
+
const color = text.color ?? defaultColor;
|
|
1023
|
+
const fontSize = text.font_size;
|
|
1024
|
+
drawText({
|
|
1025
|
+
ctx,
|
|
1026
|
+
text: text.text,
|
|
1027
|
+
x: text.anchor_position.x,
|
|
1028
|
+
y: text.anchor_position.y,
|
|
1029
|
+
fontSize,
|
|
1030
|
+
color,
|
|
1031
|
+
transform,
|
|
1032
|
+
anchorAlignment: text.anchor_alignment
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
// lib/drawer/elements/pcb-fabrication-note-rect.ts
|
|
1037
|
+
function drawPcbFabricationNoteRect(params) {
|
|
1038
|
+
const { ctx, rect, transform, colorMap } = params;
|
|
1039
|
+
const defaultColor = "rgba(255,255,255,0.5)";
|
|
1040
|
+
const color = rect.color ?? defaultColor;
|
|
1041
|
+
const isFilled = rect.is_filled ?? false;
|
|
1042
|
+
const hasStroke = rect.has_stroke ?? true;
|
|
1043
|
+
const isStrokeDashed = rect.is_stroke_dashed ?? false;
|
|
1044
|
+
drawRect({
|
|
1045
|
+
ctx,
|
|
1046
|
+
center: rect.center,
|
|
1047
|
+
width: rect.width,
|
|
1048
|
+
height: rect.height,
|
|
1049
|
+
fill: isFilled ? color : void 0,
|
|
1050
|
+
stroke: hasStroke ? color : void 0,
|
|
1051
|
+
strokeWidth: hasStroke ? rect.stroke_width : void 0,
|
|
1052
|
+
borderRadius: rect.corner_radius,
|
|
1053
|
+
transform,
|
|
1054
|
+
isStrokeDashed
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
// lib/drawer/elements/pcb-note-rect.ts
|
|
1059
|
+
function drawPcbNoteRect(params) {
|
|
1060
|
+
const { ctx, rect, transform, colorMap } = params;
|
|
1061
|
+
const defaultColor = "rgb(89, 148, 220)";
|
|
1062
|
+
const color = rect.color ?? defaultColor;
|
|
1063
|
+
const isFilled = rect.is_filled ?? false;
|
|
1064
|
+
const hasStroke = rect.has_stroke ?? true;
|
|
1065
|
+
const isStrokeDashed = rect.is_stroke_dashed ?? false;
|
|
1066
|
+
drawRect({
|
|
1067
|
+
ctx,
|
|
1068
|
+
center: rect.center,
|
|
1069
|
+
width: rect.width,
|
|
1070
|
+
height: rect.height,
|
|
1071
|
+
fill: isFilled ? color : void 0,
|
|
1072
|
+
stroke: hasStroke ? color : void 0,
|
|
1073
|
+
strokeWidth: hasStroke ? rect.stroke_width : void 0,
|
|
1074
|
+
borderRadius: rect.corner_radius,
|
|
1075
|
+
transform,
|
|
1076
|
+
isStrokeDashed
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
// lib/drawer/elements/pcb-fabrication-note-path.ts
|
|
1081
|
+
function drawPcbFabricationNotePath(params) {
|
|
1082
|
+
const { ctx, path, transform, colorMap } = params;
|
|
1083
|
+
const defaultColor = "rgba(255,255,255,0.5)";
|
|
1084
|
+
const color = path.color ?? defaultColor;
|
|
1085
|
+
if (!path.route || path.route.length < 2) return;
|
|
1086
|
+
for (let i = 0; i < path.route.length - 1; i++) {
|
|
1087
|
+
const start = path.route[i];
|
|
1088
|
+
const end = path.route[i + 1];
|
|
1089
|
+
if (!start || !end) continue;
|
|
1090
|
+
drawLine({
|
|
1091
|
+
ctx,
|
|
1092
|
+
start: { x: start.x, y: start.y },
|
|
1093
|
+
end: { x: end.x, y: end.y },
|
|
1094
|
+
strokeWidth: path.stroke_width ?? 0.1,
|
|
1095
|
+
stroke: color,
|
|
1096
|
+
transform
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
797
1101
|
// lib/drawer/CircuitToCanvasDrawer.ts
|
|
798
1102
|
var CircuitToCanvasDrawer = class {
|
|
799
1103
|
ctx;
|
|
@@ -967,6 +1271,46 @@ var CircuitToCanvasDrawer = class {
|
|
|
967
1271
|
colorMap: this.colorMap
|
|
968
1272
|
});
|
|
969
1273
|
}
|
|
1274
|
+
if (element.type === "pcb_copper_text") {
|
|
1275
|
+
drawPcbCopperText({
|
|
1276
|
+
ctx: this.ctx,
|
|
1277
|
+
text: element,
|
|
1278
|
+
transform: this.realToCanvasMat,
|
|
1279
|
+
colorMap: this.colorMap
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
if (element.type === "pcb_fabrication_note_text") {
|
|
1283
|
+
drawPcbFabricationNoteText({
|
|
1284
|
+
ctx: this.ctx,
|
|
1285
|
+
text: element,
|
|
1286
|
+
transform: this.realToCanvasMat,
|
|
1287
|
+
colorMap: this.colorMap
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
if (element.type === "pcb_fabrication_note_rect") {
|
|
1291
|
+
drawPcbFabricationNoteRect({
|
|
1292
|
+
ctx: this.ctx,
|
|
1293
|
+
rect: element,
|
|
1294
|
+
transform: this.realToCanvasMat,
|
|
1295
|
+
colorMap: this.colorMap
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
if (element.type === "pcb_note_rect") {
|
|
1299
|
+
drawPcbNoteRect({
|
|
1300
|
+
transform: this.realToCanvasMat,
|
|
1301
|
+
colorMap: this.colorMap,
|
|
1302
|
+
ctx: this.ctx,
|
|
1303
|
+
rect: element
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
if (element.type === "pcb_fabrication_note_path") {
|
|
1307
|
+
drawPcbFabricationNotePath({
|
|
1308
|
+
ctx: this.ctx,
|
|
1309
|
+
path: element,
|
|
1310
|
+
transform: this.realToCanvasMat,
|
|
1311
|
+
colorMap: this.colorMap
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
970
1314
|
}
|
|
971
1315
|
};
|
|
972
1316
|
export {
|
|
@@ -978,8 +1322,13 @@ export {
|
|
|
978
1322
|
drawPath,
|
|
979
1323
|
drawPcbBoard,
|
|
980
1324
|
drawPcbCopperPour,
|
|
1325
|
+
drawPcbCopperText,
|
|
981
1326
|
drawPcbCutout,
|
|
1327
|
+
drawPcbFabricationNotePath,
|
|
1328
|
+
drawPcbFabricationNoteRect,
|
|
1329
|
+
drawPcbFabricationNoteText,
|
|
982
1330
|
drawPcbHole,
|
|
1331
|
+
drawPcbNoteRect,
|
|
983
1332
|
drawPcbPlatedHole,
|
|
984
1333
|
drawPcbSilkscreenCircle,
|
|
985
1334
|
drawPcbSilkscreenLine,
|
|
@@ -991,5 +1340,9 @@ export {
|
|
|
991
1340
|
drawPcbVia,
|
|
992
1341
|
drawPill,
|
|
993
1342
|
drawPolygon,
|
|
994
|
-
drawRect
|
|
1343
|
+
drawRect,
|
|
1344
|
+
drawText,
|
|
1345
|
+
getAlphabetLayout,
|
|
1346
|
+
getTextStartPosition,
|
|
1347
|
+
strokeAlphabetText
|
|
995
1348
|
};
|
|
@@ -13,6 +13,11 @@ import type {
|
|
|
13
13
|
PcbSilkscreenPath,
|
|
14
14
|
PcbCutout,
|
|
15
15
|
PcbCopperPour,
|
|
16
|
+
PcbCopperText,
|
|
17
|
+
PcbFabricationNoteText,
|
|
18
|
+
PcbFabricationNoteRect,
|
|
19
|
+
PcbNoteRect,
|
|
20
|
+
PcbFabricationNotePath,
|
|
16
21
|
} from "circuit-json"
|
|
17
22
|
import { identity, compose, translate, scale } from "transformation-matrix"
|
|
18
23
|
import type { Matrix } from "transformation-matrix"
|
|
@@ -38,6 +43,11 @@ import {
|
|
|
38
43
|
} from "./elements/pcb-silkscreen"
|
|
39
44
|
import { drawPcbCutout } from "./elements/pcb-cutout"
|
|
40
45
|
import { drawPcbCopperPour } from "./elements/pcb-copper-pour"
|
|
46
|
+
import { drawPcbCopperText } from "./elements/pcb-copper-text"
|
|
47
|
+
import { drawPcbFabricationNoteText } from "./elements/pcb-fabrication-note-text"
|
|
48
|
+
import { drawPcbFabricationNoteRect } from "./elements/pcb-fabrication-note-rect"
|
|
49
|
+
import { drawPcbNoteRect } from "./elements/pcb-note-rect"
|
|
50
|
+
import { drawPcbFabricationNotePath } from "./elements/pcb-fabrication-note-path"
|
|
41
51
|
|
|
42
52
|
export interface DrawElementsOptions {
|
|
43
53
|
layers?: string[]
|
|
@@ -252,5 +262,50 @@ export class CircuitToCanvasDrawer {
|
|
|
252
262
|
colorMap: this.colorMap,
|
|
253
263
|
})
|
|
254
264
|
}
|
|
265
|
+
|
|
266
|
+
if (element.type === "pcb_copper_text") {
|
|
267
|
+
drawPcbCopperText({
|
|
268
|
+
ctx: this.ctx,
|
|
269
|
+
text: element as PcbCopperText,
|
|
270
|
+
transform: this.realToCanvasMat,
|
|
271
|
+
colorMap: this.colorMap,
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (element.type === "pcb_fabrication_note_text") {
|
|
276
|
+
drawPcbFabricationNoteText({
|
|
277
|
+
ctx: this.ctx,
|
|
278
|
+
text: element as PcbFabricationNoteText,
|
|
279
|
+
transform: this.realToCanvasMat,
|
|
280
|
+
colorMap: this.colorMap,
|
|
281
|
+
})
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (element.type === "pcb_fabrication_note_rect") {
|
|
285
|
+
drawPcbFabricationNoteRect({
|
|
286
|
+
ctx: this.ctx,
|
|
287
|
+
rect: element as PcbFabricationNoteRect,
|
|
288
|
+
transform: this.realToCanvasMat,
|
|
289
|
+
colorMap: this.colorMap,
|
|
290
|
+
})
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (element.type === "pcb_note_rect") {
|
|
294
|
+
drawPcbNoteRect({
|
|
295
|
+
transform: this.realToCanvasMat,
|
|
296
|
+
colorMap: this.colorMap,
|
|
297
|
+
ctx: this.ctx,
|
|
298
|
+
rect: element as PcbNoteRect,
|
|
299
|
+
})
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (element.type === "pcb_fabrication_note_path") {
|
|
303
|
+
drawPcbFabricationNotePath({
|
|
304
|
+
ctx: this.ctx,
|
|
305
|
+
path: element as PcbFabricationNotePath,
|
|
306
|
+
transform: this.realToCanvasMat,
|
|
307
|
+
colorMap: this.colorMap,
|
|
308
|
+
})
|
|
309
|
+
}
|
|
255
310
|
}
|
|
256
311
|
}
|
|
@@ -32,3 +32,28 @@ export {
|
|
|
32
32
|
drawPcbCopperPour,
|
|
33
33
|
type DrawPcbCopperPourParams,
|
|
34
34
|
} from "./pcb-copper-pour"
|
|
35
|
+
|
|
36
|
+
export {
|
|
37
|
+
drawPcbCopperText,
|
|
38
|
+
type DrawPcbCopperTextParams,
|
|
39
|
+
} from "./pcb-copper-text"
|
|
40
|
+
|
|
41
|
+
export {
|
|
42
|
+
drawPcbFabricationNoteText,
|
|
43
|
+
type DrawPcbFabricationNoteTextParams,
|
|
44
|
+
} from "./pcb-fabrication-note-text"
|
|
45
|
+
|
|
46
|
+
export {
|
|
47
|
+
drawPcbFabricationNoteRect,
|
|
48
|
+
type DrawPcbFabricationNoteRectParams,
|
|
49
|
+
} from "./pcb-fabrication-note-rect"
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
drawPcbNoteRect,
|
|
53
|
+
type DrawPcbNoteRectParams,
|
|
54
|
+
} from "./pcb-note-rect"
|
|
55
|
+
|
|
56
|
+
export {
|
|
57
|
+
drawPcbFabricationNotePath,
|
|
58
|
+
type DrawPcbFabricationNotePathParams,
|
|
59
|
+
} from "./pcb-fabrication-note-path"
|