circuit-to-canvas 0.0.36 → 0.0.38
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.js +429 -275
- package/lib/drawer/CircuitToCanvasDrawer.ts +7 -1
- package/lib/drawer/elements/pcb-plated-hole.ts +203 -7
- package/lib/drawer/shapes/dimension-line.ts +74 -79
- package/package.json +1 -1
- package/tests/elements/__snapshots__/pcb-fabrication-note-dimension.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-plated-hole-soldermask-margin.snap.png +0 -0
- package/tests/elements/pcb-plated-hole-soldermask-margin.test.ts +225 -0
|
@@ -172,11 +172,17 @@ export class CircuitToCanvasDrawer {
|
|
|
172
172
|
(el as PcbHole & { is_covered_with_solder_mask?: boolean })
|
|
173
173
|
.is_covered_with_solder_mask === true,
|
|
174
174
|
)
|
|
175
|
+
const hasSoldermaskPlatedHoles = elements.some(
|
|
176
|
+
(el) =>
|
|
177
|
+
el.type === "pcb_plated_hole" &&
|
|
178
|
+
(el as PcbPlatedHole & { is_covered_with_solder_mask?: boolean })
|
|
179
|
+
.is_covered_with_solder_mask === true,
|
|
180
|
+
)
|
|
175
181
|
|
|
176
182
|
for (const element of elements) {
|
|
177
183
|
if (
|
|
178
184
|
element.type === "pcb_board" &&
|
|
179
|
-
(hasSoldermaskPads || hasSoldermaskHoles)
|
|
185
|
+
(hasSoldermaskPads || hasSoldermaskHoles || hasSoldermaskPlatedHoles)
|
|
180
186
|
) {
|
|
181
187
|
// Draw board with soldermask fill when pads or holes have soldermask
|
|
182
188
|
this.drawBoardWithSoldermask(element as PcbBoard)
|
|
@@ -6,6 +6,12 @@ import { drawRect } from "../shapes/rect"
|
|
|
6
6
|
import { drawOval } from "../shapes/oval"
|
|
7
7
|
import { drawPill } from "../shapes/pill"
|
|
8
8
|
import { drawPolygon } from "../shapes/polygon"
|
|
9
|
+
import {
|
|
10
|
+
drawSoldermaskRingForCircle,
|
|
11
|
+
drawSoldermaskRingForOval,
|
|
12
|
+
drawSoldermaskRingForPill,
|
|
13
|
+
drawSoldermaskRingForRect,
|
|
14
|
+
} from "./soldermask-margin"
|
|
9
15
|
|
|
10
16
|
export interface DrawPcbPlatedHoleParams {
|
|
11
17
|
ctx: CanvasContext
|
|
@@ -14,19 +20,64 @@ export interface DrawPcbPlatedHoleParams {
|
|
|
14
20
|
colorMap: PcbColorMap
|
|
15
21
|
}
|
|
16
22
|
|
|
23
|
+
function getSoldermaskColor(
|
|
24
|
+
layers: string[] | undefined,
|
|
25
|
+
colorMap: PcbColorMap,
|
|
26
|
+
): string {
|
|
27
|
+
const layer = layers?.includes("top") ? "top" : "bottom"
|
|
28
|
+
return (
|
|
29
|
+
colorMap.soldermaskOverCopper[
|
|
30
|
+
layer as keyof typeof colorMap.soldermaskOverCopper
|
|
31
|
+
] ?? colorMap.soldermaskOverCopper.top
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
17
35
|
export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
|
|
18
36
|
const { ctx, hole, realToCanvasMat, colorMap } = params
|
|
19
37
|
|
|
38
|
+
const hasSoldermask =
|
|
39
|
+
hole.is_covered_with_solder_mask === true &&
|
|
40
|
+
hole.soldermask_margin !== undefined &&
|
|
41
|
+
hole.soldermask_margin !== 0
|
|
42
|
+
const margin = hasSoldermask ? hole.soldermask_margin! : 0
|
|
43
|
+
const soldermaskRingColor = getSoldermaskColor(hole.layers, colorMap)
|
|
44
|
+
const positiveMarginColor = colorMap.substrate
|
|
45
|
+
const copperColor = colorMap.copper.top
|
|
46
|
+
|
|
20
47
|
if (hole.shape === "circle") {
|
|
48
|
+
// For positive margins, draw extended mask area first
|
|
49
|
+
if (hasSoldermask && margin > 0) {
|
|
50
|
+
drawCircle({
|
|
51
|
+
ctx,
|
|
52
|
+
center: { x: hole.x, y: hole.y },
|
|
53
|
+
radius: hole.outer_diameter / 2 + margin,
|
|
54
|
+
fill: positiveMarginColor,
|
|
55
|
+
realToCanvasMat,
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
21
59
|
// Draw outer copper ring
|
|
22
60
|
drawCircle({
|
|
23
61
|
ctx,
|
|
24
62
|
center: { x: hole.x, y: hole.y },
|
|
25
63
|
radius: hole.outer_diameter / 2,
|
|
26
|
-
fill:
|
|
64
|
+
fill: copperColor,
|
|
27
65
|
realToCanvasMat,
|
|
28
66
|
})
|
|
29
67
|
|
|
68
|
+
// For negative margins, draw soldermask ring on top of the copper ring
|
|
69
|
+
if (hasSoldermask && margin < 0) {
|
|
70
|
+
drawSoldermaskRingForCircle(
|
|
71
|
+
ctx,
|
|
72
|
+
{ x: hole.x, y: hole.y },
|
|
73
|
+
hole.outer_diameter / 2,
|
|
74
|
+
margin,
|
|
75
|
+
realToCanvasMat,
|
|
76
|
+
soldermaskRingColor,
|
|
77
|
+
copperColor,
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
30
81
|
// Draw inner drill hole
|
|
31
82
|
drawCircle({
|
|
32
83
|
ctx,
|
|
@@ -39,17 +90,45 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
|
|
|
39
90
|
}
|
|
40
91
|
|
|
41
92
|
if (hole.shape === "oval") {
|
|
93
|
+
// For positive margins, draw extended mask area first
|
|
94
|
+
if (hasSoldermask && margin > 0) {
|
|
95
|
+
drawOval({
|
|
96
|
+
ctx,
|
|
97
|
+
center: { x: hole.x, y: hole.y },
|
|
98
|
+
radius_x: hole.outer_width / 2 + margin,
|
|
99
|
+
radius_y: hole.outer_height / 2 + margin,
|
|
100
|
+
fill: positiveMarginColor,
|
|
101
|
+
realToCanvasMat,
|
|
102
|
+
rotation: hole.ccw_rotation,
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
|
|
42
106
|
// Draw outer copper oval
|
|
43
107
|
drawOval({
|
|
44
108
|
ctx,
|
|
45
109
|
center: { x: hole.x, y: hole.y },
|
|
46
110
|
radius_x: hole.outer_width / 2,
|
|
47
111
|
radius_y: hole.outer_height / 2,
|
|
48
|
-
fill:
|
|
112
|
+
fill: copperColor,
|
|
49
113
|
realToCanvasMat,
|
|
50
114
|
rotation: hole.ccw_rotation,
|
|
51
115
|
})
|
|
52
116
|
|
|
117
|
+
// For negative margins, draw soldermask ring on top of the copper oval
|
|
118
|
+
if (hasSoldermask && margin < 0) {
|
|
119
|
+
drawSoldermaskRingForOval(
|
|
120
|
+
ctx,
|
|
121
|
+
{ x: hole.x, y: hole.y },
|
|
122
|
+
hole.outer_width / 2,
|
|
123
|
+
hole.outer_height / 2,
|
|
124
|
+
margin,
|
|
125
|
+
hole.ccw_rotation ?? 0,
|
|
126
|
+
realToCanvasMat,
|
|
127
|
+
soldermaskRingColor,
|
|
128
|
+
copperColor,
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
53
132
|
// Draw inner drill hole
|
|
54
133
|
drawOval({
|
|
55
134
|
ctx,
|
|
@@ -64,17 +143,45 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
|
|
|
64
143
|
}
|
|
65
144
|
|
|
66
145
|
if (hole.shape === "pill") {
|
|
146
|
+
// For positive margins, draw extended mask area first
|
|
147
|
+
if (hasSoldermask && margin > 0) {
|
|
148
|
+
drawPill({
|
|
149
|
+
ctx,
|
|
150
|
+
center: { x: hole.x, y: hole.y },
|
|
151
|
+
width: hole.outer_width + margin * 2,
|
|
152
|
+
height: hole.outer_height + margin * 2,
|
|
153
|
+
fill: positiveMarginColor,
|
|
154
|
+
realToCanvasMat,
|
|
155
|
+
rotation: hole.ccw_rotation,
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
|
|
67
159
|
// Draw outer copper pill
|
|
68
160
|
drawPill({
|
|
69
161
|
ctx,
|
|
70
162
|
center: { x: hole.x, y: hole.y },
|
|
71
163
|
width: hole.outer_width,
|
|
72
164
|
height: hole.outer_height,
|
|
73
|
-
fill:
|
|
165
|
+
fill: copperColor,
|
|
74
166
|
realToCanvasMat,
|
|
75
167
|
rotation: hole.ccw_rotation,
|
|
76
168
|
})
|
|
77
169
|
|
|
170
|
+
// For negative margins, draw soldermask ring on top of the copper pill
|
|
171
|
+
if (hasSoldermask && margin < 0) {
|
|
172
|
+
drawSoldermaskRingForPill(
|
|
173
|
+
ctx,
|
|
174
|
+
{ x: hole.x, y: hole.y },
|
|
175
|
+
hole.outer_width,
|
|
176
|
+
hole.outer_height,
|
|
177
|
+
margin,
|
|
178
|
+
hole.ccw_rotation ?? 0,
|
|
179
|
+
realToCanvasMat,
|
|
180
|
+
soldermaskRingColor,
|
|
181
|
+
copperColor,
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
|
|
78
185
|
// Draw inner drill hole
|
|
79
186
|
drawPill({
|
|
80
187
|
ctx,
|
|
@@ -89,17 +196,46 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
|
|
|
89
196
|
}
|
|
90
197
|
|
|
91
198
|
if (hole.shape === "circular_hole_with_rect_pad") {
|
|
199
|
+
// For positive margins, draw extended mask area first
|
|
200
|
+
if (hasSoldermask && margin > 0) {
|
|
201
|
+
drawRect({
|
|
202
|
+
ctx,
|
|
203
|
+
center: { x: hole.x, y: hole.y },
|
|
204
|
+
width: hole.rect_pad_width + margin * 2,
|
|
205
|
+
height: hole.rect_pad_height + margin * 2,
|
|
206
|
+
fill: positiveMarginColor,
|
|
207
|
+
realToCanvasMat,
|
|
208
|
+
borderRadius: (hole.rect_border_radius ?? 0) + margin,
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
|
|
92
212
|
// Draw rectangular pad
|
|
93
213
|
drawRect({
|
|
94
214
|
ctx,
|
|
95
215
|
center: { x: hole.x, y: hole.y },
|
|
96
216
|
width: hole.rect_pad_width,
|
|
97
217
|
height: hole.rect_pad_height,
|
|
98
|
-
fill:
|
|
218
|
+
fill: copperColor,
|
|
99
219
|
realToCanvasMat,
|
|
100
220
|
borderRadius: hole.rect_border_radius ?? 0,
|
|
101
221
|
})
|
|
102
222
|
|
|
223
|
+
// For negative margins, draw soldermask ring on top of the pad
|
|
224
|
+
if (hasSoldermask && margin < 0) {
|
|
225
|
+
drawSoldermaskRingForRect(
|
|
226
|
+
ctx,
|
|
227
|
+
{ x: hole.x, y: hole.y },
|
|
228
|
+
hole.rect_pad_width,
|
|
229
|
+
hole.rect_pad_height,
|
|
230
|
+
margin,
|
|
231
|
+
hole.rect_border_radius ?? 0,
|
|
232
|
+
0,
|
|
233
|
+
realToCanvasMat,
|
|
234
|
+
soldermaskRingColor,
|
|
235
|
+
copperColor,
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
|
|
103
239
|
// Draw circular drill hole (with offset)
|
|
104
240
|
const holeX = hole.x + (hole.hole_offset_x ?? 0)
|
|
105
241
|
const holeY = hole.y + (hole.hole_offset_y ?? 0)
|
|
@@ -114,17 +250,46 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
|
|
|
114
250
|
}
|
|
115
251
|
|
|
116
252
|
if (hole.shape === "pill_hole_with_rect_pad") {
|
|
253
|
+
// For positive margins, draw extended mask area first
|
|
254
|
+
if (hasSoldermask && margin > 0) {
|
|
255
|
+
drawRect({
|
|
256
|
+
ctx,
|
|
257
|
+
center: { x: hole.x, y: hole.y },
|
|
258
|
+
width: hole.rect_pad_width + margin * 2,
|
|
259
|
+
height: hole.rect_pad_height + margin * 2,
|
|
260
|
+
fill: positiveMarginColor,
|
|
261
|
+
realToCanvasMat,
|
|
262
|
+
borderRadius: (hole.rect_border_radius ?? 0) + margin,
|
|
263
|
+
})
|
|
264
|
+
}
|
|
265
|
+
|
|
117
266
|
// Draw rectangular pad
|
|
118
267
|
drawRect({
|
|
119
268
|
ctx,
|
|
120
269
|
center: { x: hole.x, y: hole.y },
|
|
121
270
|
width: hole.rect_pad_width,
|
|
122
271
|
height: hole.rect_pad_height,
|
|
123
|
-
fill:
|
|
272
|
+
fill: copperColor,
|
|
124
273
|
realToCanvasMat,
|
|
125
274
|
borderRadius: hole.rect_border_radius ?? 0,
|
|
126
275
|
})
|
|
127
276
|
|
|
277
|
+
// For negative margins, draw soldermask ring on top of the pad
|
|
278
|
+
if (hasSoldermask && margin < 0) {
|
|
279
|
+
drawSoldermaskRingForRect(
|
|
280
|
+
ctx,
|
|
281
|
+
{ x: hole.x, y: hole.y },
|
|
282
|
+
hole.rect_pad_width,
|
|
283
|
+
hole.rect_pad_height,
|
|
284
|
+
margin,
|
|
285
|
+
hole.rect_border_radius ?? 0,
|
|
286
|
+
0,
|
|
287
|
+
realToCanvasMat,
|
|
288
|
+
soldermaskRingColor,
|
|
289
|
+
copperColor,
|
|
290
|
+
)
|
|
291
|
+
}
|
|
292
|
+
|
|
128
293
|
// Draw pill drill hole (with offset)
|
|
129
294
|
const holeX = hole.x + (hole.hole_offset_x ?? 0)
|
|
130
295
|
const holeY = hole.y + (hole.hole_offset_y ?? 0)
|
|
@@ -140,18 +305,48 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
|
|
|
140
305
|
}
|
|
141
306
|
|
|
142
307
|
if (hole.shape === "rotated_pill_hole_with_rect_pad") {
|
|
308
|
+
// For positive margins, draw extended mask area first
|
|
309
|
+
if (hasSoldermask && margin > 0) {
|
|
310
|
+
drawRect({
|
|
311
|
+
ctx,
|
|
312
|
+
center: { x: hole.x, y: hole.y },
|
|
313
|
+
width: hole.rect_pad_width + margin * 2,
|
|
314
|
+
height: hole.rect_pad_height + margin * 2,
|
|
315
|
+
fill: positiveMarginColor,
|
|
316
|
+
realToCanvasMat,
|
|
317
|
+
borderRadius: (hole.rect_border_radius ?? 0) + margin,
|
|
318
|
+
rotation: hole.rect_ccw_rotation,
|
|
319
|
+
})
|
|
320
|
+
}
|
|
321
|
+
|
|
143
322
|
// Draw rotated rectangular pad
|
|
144
323
|
drawRect({
|
|
145
324
|
ctx,
|
|
146
325
|
center: { x: hole.x, y: hole.y },
|
|
147
326
|
width: hole.rect_pad_width,
|
|
148
327
|
height: hole.rect_pad_height,
|
|
149
|
-
fill:
|
|
328
|
+
fill: copperColor,
|
|
150
329
|
realToCanvasMat,
|
|
151
330
|
borderRadius: hole.rect_border_radius ?? 0,
|
|
152
331
|
rotation: hole.rect_ccw_rotation,
|
|
153
332
|
})
|
|
154
333
|
|
|
334
|
+
// For negative margins, draw soldermask ring on top of the pad
|
|
335
|
+
if (hasSoldermask && margin < 0) {
|
|
336
|
+
drawSoldermaskRingForRect(
|
|
337
|
+
ctx,
|
|
338
|
+
{ x: hole.x, y: hole.y },
|
|
339
|
+
hole.rect_pad_width,
|
|
340
|
+
hole.rect_pad_height,
|
|
341
|
+
margin,
|
|
342
|
+
hole.rect_border_radius ?? 0,
|
|
343
|
+
hole.rect_ccw_rotation ?? 0,
|
|
344
|
+
realToCanvasMat,
|
|
345
|
+
soldermaskRingColor,
|
|
346
|
+
copperColor,
|
|
347
|
+
)
|
|
348
|
+
}
|
|
349
|
+
|
|
155
350
|
// Draw rotated pill drill hole (with offset)
|
|
156
351
|
const holeX = hole.x + (hole.hole_offset_x ?? 0)
|
|
157
352
|
const holeY = hole.y + (hole.hole_offset_y ?? 0)
|
|
@@ -168,6 +363,7 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
|
|
|
168
363
|
}
|
|
169
364
|
|
|
170
365
|
if (hole.shape === "hole_with_polygon_pad") {
|
|
366
|
+
// Note: Polygon pads don't support soldermask margins (similar to SMT polygon pads)
|
|
171
367
|
// Draw polygon pad
|
|
172
368
|
const padOutline = hole.pad_outline
|
|
173
369
|
if (padOutline && padOutline.length >= 3) {
|
|
@@ -179,7 +375,7 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
|
|
|
179
375
|
drawPolygon({
|
|
180
376
|
ctx,
|
|
181
377
|
points: padPoints,
|
|
182
|
-
fill:
|
|
378
|
+
fill: copperColor,
|
|
183
379
|
realToCanvasMat,
|
|
184
380
|
})
|
|
185
381
|
}
|
|
@@ -121,85 +121,80 @@ export function drawDimensionLine(params: DrawDimensionLineParams): void {
|
|
|
121
121
|
]
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
allPoints.push(
|
|
130
|
-
|
|
131
|
-
//
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
allPoints.push(
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
direction.y * arrowSize -
|
|
199
|
-
perpendicular.y * (arrowSize / 2),
|
|
200
|
-
},
|
|
201
|
-
]
|
|
202
|
-
allPoints.push(...arrow2, arrow2[0]!)
|
|
124
|
+
// Unified Perimeter Approach:
|
|
125
|
+
// Draw the main line and both arrows as a single continuous perimeter path.
|
|
126
|
+
// This eliminates self-intersections and winding issues.
|
|
127
|
+
|
|
128
|
+
// 1. Tip 1
|
|
129
|
+
allPoints.push(fromOffset)
|
|
130
|
+
|
|
131
|
+
// 2. Arrow 1 base corner 1
|
|
132
|
+
allPoints.push({
|
|
133
|
+
x: fromBase.x + perpendicular.x * (arrowSize / 2),
|
|
134
|
+
y: fromBase.y + perpendicular.y * (arrowSize / 2),
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
// 3. Main Line corner 1
|
|
138
|
+
allPoints.push({
|
|
139
|
+
x: fromBase.x + perpendicular.x * (strokeWidth / 2),
|
|
140
|
+
y: fromBase.y + perpendicular.y * (strokeWidth / 2),
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
// 4. Main Line corner 2 (at toBase)
|
|
144
|
+
allPoints.push({
|
|
145
|
+
x: toBase.x + perpendicular.x * (strokeWidth / 2),
|
|
146
|
+
y: toBase.y + perpendicular.y * (strokeWidth / 2),
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
// 5. Arrow 2 base corner 1
|
|
150
|
+
allPoints.push({
|
|
151
|
+
x: toBase.x + perpendicular.x * (arrowSize / 2),
|
|
152
|
+
y: toBase.y + perpendicular.y * (arrowSize / 2),
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
// 6. Tip 2
|
|
156
|
+
allPoints.push(toOffset)
|
|
157
|
+
|
|
158
|
+
// 7. Arrow 2 base corner 2
|
|
159
|
+
allPoints.push({
|
|
160
|
+
x: toBase.x - perpendicular.x * (arrowSize / 2),
|
|
161
|
+
y: toBase.y - perpendicular.y * (arrowSize / 2),
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
// 8. Main Line corner 3 (at toBase)
|
|
165
|
+
allPoints.push({
|
|
166
|
+
x: toBase.x - perpendicular.x * (strokeWidth / 2),
|
|
167
|
+
y: toBase.y - perpendicular.y * (strokeWidth / 2),
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
// 9. Main Line corner 4 (at fromBase)
|
|
171
|
+
allPoints.push({
|
|
172
|
+
x: fromBase.x - perpendicular.x * (strokeWidth / 2),
|
|
173
|
+
y: fromBase.y - perpendicular.y * (strokeWidth / 2),
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
// 10. Arrow 1 base corner 2
|
|
177
|
+
allPoints.push({
|
|
178
|
+
x: fromBase.x - perpendicular.x * (arrowSize / 2),
|
|
179
|
+
y: fromBase.y - perpendicular.y * (arrowSize / 2),
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
// 11. Back to Tip 1
|
|
183
|
+
allPoints.push(fromOffset)
|
|
184
|
+
|
|
185
|
+
// Bridge Ticks (Extension lines)
|
|
186
|
+
const startPoint = allPoints[0]!
|
|
187
|
+
const addTick = (anchor: { x: number; y: number }) => {
|
|
188
|
+
const pts = getExtensionPoints(anchor)
|
|
189
|
+
allPoints.push(startPoint)
|
|
190
|
+
allPoints.push(pts[0]!)
|
|
191
|
+
allPoints.push(...pts)
|
|
192
|
+
allPoints.push(pts[0]!)
|
|
193
|
+
allPoints.push(startPoint)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
addTick(from)
|
|
197
|
+
addTick(to)
|
|
203
198
|
|
|
204
199
|
drawPolygon({
|
|
205
200
|
ctx,
|
package/package.json
CHANGED
|
Binary file
|