@tscircuit/3d-viewer 0.0.457 → 0.0.459

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.
Files changed (2) hide show
  1. package/dist/index.js +226 -28
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -28509,7 +28509,7 @@ import * as THREE15 from "three";
28509
28509
  // package.json
28510
28510
  var package_default = {
28511
28511
  name: "@tscircuit/3d-viewer",
28512
- version: "0.0.456",
28512
+ version: "0.0.458",
28513
28513
  main: "./dist/index.js",
28514
28514
  module: "./dist/index.js",
28515
28515
  type: "module",
@@ -34072,25 +34072,67 @@ function createSoldermaskTextureForLayer({
34072
34072
  if (pad2.shape === "rect") {
34073
34073
  const width10 = pad2.width * traceTextureResolution;
34074
34074
  const height10 = pad2.height * traceTextureResolution;
34075
- ctx.fillRect(canvasX - width10 / 2, canvasY - height10 / 2, width10, height10);
34075
+ const rawRadius = extractRectBorderRadius(pad2);
34076
+ const borderRadius = clampRectBorderRadius(
34077
+ pad2.width,
34078
+ pad2.height,
34079
+ rawRadius
34080
+ ) * traceTextureResolution;
34081
+ if (borderRadius > 0) {
34082
+ ctx.beginPath();
34083
+ ctx.roundRect(
34084
+ canvasX - width10 / 2,
34085
+ canvasY - height10 / 2,
34086
+ width10,
34087
+ height10,
34088
+ borderRadius
34089
+ );
34090
+ ctx.fill();
34091
+ } else {
34092
+ ctx.fillRect(canvasX - width10 / 2, canvasY - height10 / 2, width10, height10);
34093
+ }
34076
34094
  } else if (pad2.shape === "circle") {
34077
34095
  const radius = (pad2.radius ?? pad2.width / 2) * traceTextureResolution;
34078
34096
  ctx.beginPath();
34079
34097
  ctx.arc(canvasX, canvasY, radius, 0, 2 * Math.PI);
34080
34098
  ctx.fill();
34081
- } else if (pad2.shape === "pill" || pad2.shape === "rotated_rect") {
34099
+ } else if (pad2.shape === "pill") {
34082
34100
  const width10 = pad2.width * traceTextureResolution;
34083
34101
  const height10 = pad2.height * traceTextureResolution;
34084
- const radius = Math.min(width10, height10) / 2;
34102
+ const rawRadius = extractRectBorderRadius(pad2);
34103
+ const borderRadius = clampRectBorderRadius(
34104
+ pad2.width,
34105
+ pad2.height,
34106
+ rawRadius
34107
+ ) * traceTextureResolution;
34085
34108
  ctx.beginPath();
34086
34109
  ctx.roundRect(
34087
34110
  canvasX - width10 / 2,
34088
34111
  canvasY - height10 / 2,
34089
34112
  width10,
34090
34113
  height10,
34091
- radius
34114
+ borderRadius
34092
34115
  );
34093
34116
  ctx.fill();
34117
+ } else if (pad2.shape === "rotated_rect") {
34118
+ const width10 = pad2.width * traceTextureResolution;
34119
+ const height10 = pad2.height * traceTextureResolution;
34120
+ const rawRadius = extractRectBorderRadius(pad2);
34121
+ const borderRadius = clampRectBorderRadius(
34122
+ pad2.width,
34123
+ pad2.height,
34124
+ rawRadius
34125
+ ) * traceTextureResolution;
34126
+ const ccwRotation = pad2.ccw_rotation || 0;
34127
+ const rotationRadians = ccwRotation * (Math.PI / 180);
34128
+ const rotation2 = layer === "bottom" ? -rotationRadians : rotationRadians;
34129
+ ctx.save();
34130
+ ctx.translate(canvasX, canvasY);
34131
+ ctx.rotate(rotation2);
34132
+ ctx.beginPath();
34133
+ ctx.roundRect(-width10 / 2, -height10 / 2, width10, height10, borderRadius);
34134
+ ctx.fill();
34135
+ ctx.restore();
34094
34136
  }
34095
34137
  });
34096
34138
  const pcbVias = su13(circuitJson).pcb_via.list();
@@ -34234,6 +34276,71 @@ function createSoldermaskTextureForLayer({
34234
34276
  ctx.arc(adjustedCanvasX, adjustedCanvasY, canvasRadius, 0, 2 * Math.PI);
34235
34277
  ctx.fill();
34236
34278
  }
34279
+ } else if (hole.shape === "circular_hole_with_rect_pad") {
34280
+ const padWidth = (hole.rect_pad_width ?? hole.hole_diameter ?? 0) * traceTextureResolution;
34281
+ const padHeight = (hole.rect_pad_height ?? hole.hole_diameter ?? 0) * traceTextureResolution;
34282
+ const rawRadius = extractRectBorderRadius(hole);
34283
+ const borderRadius = clampRectBorderRadius(
34284
+ hole.rect_pad_width ?? hole.hole_diameter ?? 0,
34285
+ hole.rect_pad_height ?? hole.hole_diameter ?? 0,
34286
+ rawRadius
34287
+ ) * traceTextureResolution;
34288
+ if (borderRadius > 0) {
34289
+ ctx.beginPath();
34290
+ ctx.roundRect(
34291
+ canvasX - padWidth / 2,
34292
+ canvasY - padHeight / 2,
34293
+ padWidth,
34294
+ padHeight,
34295
+ borderRadius
34296
+ );
34297
+ ctx.fill();
34298
+ } else {
34299
+ ctx.fillRect(
34300
+ canvasX - padWidth / 2,
34301
+ canvasY - padHeight / 2,
34302
+ padWidth,
34303
+ padHeight
34304
+ );
34305
+ }
34306
+ } else if (hole.shape === "pill_hole_with_rect_pad") {
34307
+ const padWidth = (hole.rect_pad_width ?? hole.hole_width ?? 0) * traceTextureResolution;
34308
+ const padHeight = (hole.rect_pad_height ?? hole.hole_height ?? 0) * traceTextureResolution;
34309
+ const rawRadius = extractRectBorderRadius(hole);
34310
+ const borderRadius = clampRectBorderRadius(
34311
+ hole.rect_pad_width ?? hole.hole_width ?? 0,
34312
+ hole.rect_pad_height ?? hole.hole_height ?? 0,
34313
+ rawRadius
34314
+ ) * traceTextureResolution;
34315
+ let rotation2 = hole.ccw_rotation || 0;
34316
+ if (layer === "bottom") {
34317
+ rotation2 = -rotation2;
34318
+ }
34319
+ if (rotation2) {
34320
+ ctx.save();
34321
+ ctx.translate(canvasX, canvasY);
34322
+ ctx.rotate(rotation2 * Math.PI / 180);
34323
+ ctx.beginPath();
34324
+ ctx.roundRect(
34325
+ -padWidth / 2,
34326
+ -padHeight / 2,
34327
+ padWidth,
34328
+ padHeight,
34329
+ borderRadius
34330
+ );
34331
+ ctx.fill();
34332
+ ctx.restore();
34333
+ } else {
34334
+ ctx.beginPath();
34335
+ ctx.roundRect(
34336
+ canvasX - padWidth / 2,
34337
+ canvasY - padHeight / 2,
34338
+ padWidth,
34339
+ padHeight,
34340
+ borderRadius
34341
+ );
34342
+ ctx.fill();
34343
+ }
34237
34344
  }
34238
34345
  });
34239
34346
  const pcbHoles = su13(circuitJson).pcb_hole.list();
@@ -34248,35 +34355,33 @@ function createSoldermaskTextureForLayer({
34248
34355
  ctx.beginPath();
34249
34356
  ctx.arc(canvasX, canvasY, canvasRadius, 0, 2 * Math.PI);
34250
34357
  ctx.fill();
34251
- } else if (holeShape === "pill" && typeof hole.hole_width === "number" && typeof hole.hole_height === "number") {
34252
- const width10 = hole.hole_width * traceTextureResolution;
34253
- const height10 = hole.hole_height * traceTextureResolution;
34254
- const radius = Math.min(width10, height10) / 2;
34255
- ctx.beginPath();
34256
- ctx.roundRect(
34257
- canvasX - width10 / 2,
34258
- canvasY - height10 / 2,
34259
- width10,
34260
- height10,
34261
- radius
34262
- );
34263
- ctx.fill();
34264
- } else if (holeShape === "rotated_pill" && typeof hole.hole_width === "number" && typeof hole.hole_height === "number") {
34358
+ } else if ((holeShape === "pill" || holeShape === "rotated_pill") && typeof hole.hole_width === "number" && typeof hole.hole_height === "number") {
34265
34359
  const width10 = hole.hole_width * traceTextureResolution;
34266
34360
  const height10 = hole.hole_height * traceTextureResolution;
34267
34361
  const radius = Math.min(width10, height10) / 2;
34268
- const rotation2 = (hole.ccw_rotation || 0) * (Math.PI / 180);
34269
- ctx.save();
34270
- ctx.translate(canvasX, canvasY);
34362
+ let rotation2 = (hole.ccw_rotation || 0) * (Math.PI / 180);
34271
34363
  if (layer === "bottom") {
34272
- ctx.rotate(-rotation2);
34273
- } else {
34364
+ rotation2 = -rotation2;
34365
+ }
34366
+ if (rotation2) {
34367
+ ctx.save();
34368
+ ctx.translate(canvasX, canvasY);
34274
34369
  ctx.rotate(rotation2);
34370
+ ctx.beginPath();
34371
+ ctx.roundRect(-width10 / 2, -height10 / 2, width10, height10, radius);
34372
+ ctx.fill();
34373
+ ctx.restore();
34374
+ } else {
34375
+ ctx.beginPath();
34376
+ ctx.roundRect(
34377
+ canvasX - width10 / 2,
34378
+ canvasY - height10 / 2,
34379
+ width10,
34380
+ height10,
34381
+ radius
34382
+ );
34383
+ ctx.fill();
34275
34384
  }
34276
- ctx.beginPath();
34277
- ctx.roundRect(-width10 / 2, -height10 / 2, width10, height10, radius);
34278
- ctx.fill();
34279
- ctx.restore();
34280
34385
  }
34281
34386
  });
34282
34387
  const pcbCopperPours = su13(circuitJson).pcb_copper_pour.list();
@@ -34304,6 +34409,99 @@ function createSoldermaskTextureForLayer({
34304
34409
  ctx.fill();
34305
34410
  }
34306
34411
  });
34412
+ const pcbCutouts = su13(circuitJson).pcb_cutout.list();
34413
+ pcbCutouts.forEach((cutout) => {
34414
+ switch (cutout.shape) {
34415
+ case "rect": {
34416
+ const canvasX = canvasXFromPcb(cutout.center.x);
34417
+ const canvasY = canvasYFromPcb(cutout.center.y);
34418
+ const width10 = cutout.width * traceTextureResolution;
34419
+ const height10 = cutout.height * traceTextureResolution;
34420
+ const rectCornerRadius = extractRectBorderRadius(cutout);
34421
+ const borderRadius = clampRectBorderRadius(
34422
+ cutout.width,
34423
+ cutout.height,
34424
+ rectCornerRadius
34425
+ );
34426
+ if (cutout.rotation) {
34427
+ ctx.save();
34428
+ ctx.translate(canvasX, canvasY);
34429
+ const rotation2 = layer === "bottom" ? -cutout.rotation * (Math.PI / 180) : cutout.rotation * (Math.PI / 180);
34430
+ ctx.rotate(rotation2);
34431
+ if (borderRadius > 0) {
34432
+ ctx.beginPath();
34433
+ ctx.roundRect(
34434
+ -width10 / 2,
34435
+ -height10 / 2,
34436
+ width10,
34437
+ height10,
34438
+ borderRadius * traceTextureResolution
34439
+ );
34440
+ ctx.fill();
34441
+ } else {
34442
+ ctx.fillRect(-width10 / 2, -height10 / 2, width10, height10);
34443
+ }
34444
+ ctx.restore();
34445
+ } else {
34446
+ if (borderRadius > 0) {
34447
+ ctx.beginPath();
34448
+ ctx.roundRect(
34449
+ canvasX - width10 / 2,
34450
+ canvasY - height10 / 2,
34451
+ width10,
34452
+ height10,
34453
+ borderRadius * traceTextureResolution
34454
+ );
34455
+ ctx.fill();
34456
+ } else {
34457
+ ctx.fillRect(
34458
+ canvasX - width10 / 2,
34459
+ canvasY - height10 / 2,
34460
+ width10,
34461
+ height10
34462
+ );
34463
+ }
34464
+ }
34465
+ break;
34466
+ }
34467
+ case "circle": {
34468
+ const canvasX = canvasXFromPcb(cutout.center.x);
34469
+ const canvasY = canvasYFromPcb(cutout.center.y);
34470
+ const canvasRadius = cutout.radius * traceTextureResolution;
34471
+ ctx.beginPath();
34472
+ ctx.arc(canvasX, canvasY, canvasRadius, 0, 2 * Math.PI);
34473
+ ctx.fill();
34474
+ break;
34475
+ }
34476
+ case "polygon": {
34477
+ if (!cutout.points || cutout.points.length < 3) {
34478
+ console.warn(
34479
+ `PCB Cutout [${cutout.pcb_cutout_id}] polygon has fewer than 3 points, skipping in soldermask texture.`
34480
+ );
34481
+ break;
34482
+ }
34483
+ ctx.beginPath();
34484
+ cutout.points.forEach(
34485
+ (point2, index2) => {
34486
+ const px = canvasXFromPcb(point2.x);
34487
+ const py = canvasYFromPcb(point2.y);
34488
+ if (index2 === 0) {
34489
+ ctx.moveTo(px, py);
34490
+ } else {
34491
+ ctx.lineTo(px, py);
34492
+ }
34493
+ }
34494
+ );
34495
+ ctx.closePath();
34496
+ ctx.fill();
34497
+ break;
34498
+ }
34499
+ default:
34500
+ console.warn(
34501
+ `Unsupported cutout shape: ${cutout.shape} for cutout ${cutout.pcb_cutout_id} in soldermask texture.`
34502
+ );
34503
+ }
34504
+ });
34307
34505
  ctx.globalCompositeOperation = "source-over";
34308
34506
  const texture = new THREE24.CanvasTexture(canvas);
34309
34507
  texture.generateMipmaps = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/3d-viewer",
3
- "version": "0.0.457",
3
+ "version": "0.0.459",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.js",
6
6
  "type": "module",