altium-to-circuit-json 0.0.82 → 0.0.83

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 CHANGED
@@ -5181,7 +5181,10 @@ function contourToPoints(contour) {
5181
5181
  import {
5182
5182
  AltiumFillRecord,
5183
5183
  AltiumPolygonRecord,
5184
- AltiumRegionRecord as AltiumRegionRecord3
5184
+ AltiumRegionRecord as AltiumRegionRecord3,
5185
+ getPcbContour as getPcbContour2,
5186
+ getPcbRecordPolygonIndex,
5187
+ getPcbRegionGeometry as getPcbRegionGeometry5
5185
5188
  } from "altiumts";
5186
5189
 
5187
5190
  // lib/pcb/copperAreas/convertCopperFill.ts
@@ -5216,8 +5219,13 @@ function convertCopperFill({
5216
5219
  }
5217
5220
 
5218
5221
  // lib/pcb/copperAreas/convertCopperPolygon.ts
5219
- import { getPcbContour } from "altiumts";
5222
+ import {
5223
+ getPcbContour,
5224
+ getPcbRegionGeometry as getPcbRegionGeometry3
5225
+ } from "altiumts";
5226
+ import polygonClipping from "polygon-clipping";
5220
5227
  function convertCopperPolygon({
5228
+ cutouts,
5221
5229
  polygonIndex,
5222
5230
  layerMap,
5223
5231
  record,
@@ -5227,21 +5235,45 @@ function convertCopperPolygon({
5227
5235
  if (!layer) return [];
5228
5236
  const points = contourToPoints(getPcbContour(record));
5229
5237
  if (points.length < 3) return [];
5230
- return [
5231
- {
5232
- type: "pcb_copper_pour",
5233
- pcb_copper_pour_id: `pcb_copper_pour_altium_polygon_${polygonIndex}`,
5234
- ...sourceNetId ? { source_net_id: sourceNetId } : {},
5235
- covered_with_solder_mask: true,
5236
- layer,
5237
- shape: "polygon",
5238
- points
5238
+ const cutoutPolygons = cutouts.map((cutout) => {
5239
+ const geometry = getPcbRegionGeometry3(cutout);
5240
+ return [geometry.outline, ...geometry.holes].map(
5241
+ (contour) => contourToPoints(contour).map((point) => [point.x, point.y])
5242
+ );
5243
+ });
5244
+ const copperPolygons = cutouts.length === 0 ? [[points]] : polygonClipping.difference(
5245
+ [points.map((point) => [point.x, point.y])],
5246
+ cutoutPolygons
5247
+ ).map(
5248
+ (polygon) => polygon.map(
5249
+ (ring) => ring.slice(0, -1).map(([x, y]) => ({ x, y }))
5250
+ )
5251
+ );
5252
+ return copperPolygons.flatMap(
5253
+ ([outerRing, ...holes], partIndex) => {
5254
+ if (!outerRing || outerRing.length < 3) return [];
5255
+ return [
5256
+ {
5257
+ type: "pcb_copper_pour",
5258
+ pcb_copper_pour_id: `pcb_copper_pour_altium_polygon_${polygonIndex}${partIndex === 0 ? "" : `_island_${partIndex}`}`,
5259
+ ...sourceNetId ? { source_net_id: sourceNetId } : {},
5260
+ covered_with_solder_mask: true,
5261
+ layer,
5262
+ ...holes.length === 0 ? { shape: "polygon", points: outerRing } : {
5263
+ shape: "brep",
5264
+ brep_shape: {
5265
+ outer_ring: { vertices: outerRing },
5266
+ inner_rings: holes.map((vertices) => ({ vertices }))
5267
+ }
5268
+ }
5269
+ }
5270
+ ];
5239
5271
  }
5240
- ];
5272
+ );
5241
5273
  }
5242
5274
 
5243
5275
  // lib/pcb/copperAreas/convertCopperRegion.ts
5244
- import { getPcbRegionGeometry as getPcbRegionGeometry3 } from "altiumts";
5276
+ import { getPcbRegionGeometry as getPcbRegionGeometry4 } from "altiumts";
5245
5277
  function convertCopperRegion({
5246
5278
  layerMap,
5247
5279
  record,
@@ -5253,7 +5285,7 @@ function convertCopperRegion({
5253
5285
  }
5254
5286
  const layer = layerMap.getLayer(record.layer);
5255
5287
  if (!layer) return [];
5256
- const geometry = getPcbRegionGeometry3(record);
5288
+ const geometry = getPcbRegionGeometry4(record);
5257
5289
  const outerVertices = contourToPoints(geometry.outline);
5258
5290
  if (outerVertices.length < 3) return [];
5259
5291
  const innerRings = geometry.holes.map((hole) => ({ vertices: contourToPoints(hole) })).filter((ring) => ring.vertices.length >= 3);
@@ -5286,20 +5318,97 @@ function convertCopperRegion({
5286
5318
  ];
5287
5319
  }
5288
5320
 
5321
+ // lib/pcb/copperAreas/isContourInsideContour.ts
5322
+ import { doesLineIntersectLine } from "@tscircuit/math-utils";
5323
+ function isContourInsideContour({
5324
+ innerContour,
5325
+ outerContour
5326
+ }) {
5327
+ const innerBounds = innerContour.bounds;
5328
+ const outerBounds = outerContour.bounds;
5329
+ if (!innerBounds || !outerBounds || innerBounds.minX <= outerBounds.minX || innerBounds.maxX >= outerBounds.maxX || innerBounds.minY <= outerBounds.minY || innerBounds.maxY >= outerBounds.maxY) {
5330
+ return false;
5331
+ }
5332
+ const innerPoints = innerContour.isExplicitlyClosed ? innerContour.points.slice(0, -1) : innerContour.points;
5333
+ const outerPoints = outerContour.isExplicitlyClosed ? outerContour.points.slice(0, -1) : outerContour.points;
5334
+ if (innerPoints.length < 3 || outerPoints.length < 3) return false;
5335
+ const toleranceMils = 8 * Number.EPSILON * Math.max(
5336
+ 1,
5337
+ Math.abs(outerBounds.minX),
5338
+ Math.abs(outerBounds.maxX),
5339
+ Math.abs(outerBounds.minY),
5340
+ Math.abs(outerBounds.maxY)
5341
+ );
5342
+ for (const point of innerPoints) {
5343
+ let isInside = false;
5344
+ for (let index = 0; index < outerPoints.length; index++) {
5345
+ const start = outerPoints[index];
5346
+ const end = outerPoints[(index + 1) % outerPoints.length];
5347
+ if (!start || !end) continue;
5348
+ if (start.y > point.y !== end.y > point.y && point.x < start.x + (point.y - start.y) * (end.x - start.x) / (end.y - start.y)) {
5349
+ isInside = !isInside;
5350
+ }
5351
+ }
5352
+ if (!isInside) return false;
5353
+ }
5354
+ for (let innerIndex = 0; innerIndex < innerPoints.length; innerIndex++) {
5355
+ const innerStart = innerPoints[innerIndex];
5356
+ const innerEnd = innerPoints[(innerIndex + 1) % innerPoints.length];
5357
+ if (!innerStart || !innerEnd) continue;
5358
+ for (let outerIndex = 0; outerIndex < outerPoints.length; outerIndex++) {
5359
+ const outerStart = outerPoints[outerIndex];
5360
+ const outerEnd = outerPoints[(outerIndex + 1) % outerPoints.length];
5361
+ if (!outerStart || !outerEnd) continue;
5362
+ if (doesLineIntersectLine([innerStart, innerEnd], [outerStart, outerEnd], {
5363
+ lineThickness: toleranceMils
5364
+ })) {
5365
+ return false;
5366
+ }
5367
+ }
5368
+ }
5369
+ return true;
5370
+ }
5371
+
5289
5372
  // lib/pcb/copperAreas/convertAltiumCopperAreas.ts
5290
5373
  function convertAltiumCopperAreas(document, {
5291
5374
  layerMap = new PcbCopperLayerMap(document),
5292
5375
  getSourceNetId = () => void 0
5293
5376
  } = {}) {
5294
- const polygonsWithRegions = new Set(
5295
- document.records.flatMap((record) => {
5296
- if (!(record instanceof AltiumRegionRecord3) || record.recordKind !== "Region") {
5297
- return [];
5377
+ const polygonsWithCopperRegions = /* @__PURE__ */ new Set();
5378
+ const cutoutsByPolygon = /* @__PURE__ */ new Map();
5379
+ const polygonContours = /* @__PURE__ */ new Map();
5380
+ for (const record of document.records) {
5381
+ if (!(record instanceof AltiumRegionRecord3) || record.recordKind !== "Region") {
5382
+ continue;
5383
+ }
5384
+ const polygonIndex = getPcbRecordPolygonIndex(document, record);
5385
+ const linkedPolygon = document.getPolygonForRecord(record);
5386
+ if (record.regionKind === "COPPER") {
5387
+ if (linkedPolygon) polygonsWithCopperRegions.add(linkedPolygon);
5388
+ } else if (record.regionKind === "POLYGON_CUTOUT") {
5389
+ const cutoutGeometry = getPcbRegionGeometry5(record);
5390
+ const candidatePolygons = polygonIndex === void 0 ? document.polygons : linkedPolygon ? [linkedPolygon] : [];
5391
+ const polygons = candidatePolygons.filter((polygon) => {
5392
+ if (record.layer !== polygon.layer && record.layer !== "MULTILAYER") {
5393
+ return false;
5394
+ }
5395
+ let polygonContour = polygonContours.get(polygon);
5396
+ if (!polygonContour) {
5397
+ polygonContour = getPcbContour2(polygon);
5398
+ polygonContours.set(polygon, polygonContour);
5399
+ }
5400
+ return isContourInsideContour({
5401
+ innerContour: cutoutGeometry.outline,
5402
+ outerContour: polygonContour
5403
+ });
5404
+ });
5405
+ for (const polygon of polygons) {
5406
+ const cutouts = cutoutsByPolygon.get(polygon) ?? [];
5407
+ cutouts.push(record);
5408
+ cutoutsByPolygon.set(polygon, cutouts);
5298
5409
  }
5299
- const polygon = document.getPolygonForRecord(record);
5300
- return polygon ? [polygon] : [];
5301
- })
5302
- );
5410
+ }
5411
+ }
5303
5412
  const polygonIndexes = new Map(
5304
5413
  document.polygons.map((polygon, index) => [polygon, index])
5305
5414
  );
@@ -5314,10 +5423,11 @@ function convertAltiumCopperAreas(document, {
5314
5423
  }
5315
5424
  if (record instanceof AltiumPolygonRecord) {
5316
5425
  const polygonIndex = polygonIndexes.get(record);
5317
- if (record.shelved === true || polygonIndex === void 0 || polygonsWithRegions.has(record)) {
5426
+ if (record.shelved === true || polygonIndex === void 0 || polygonsWithCopperRegions.has(record)) {
5318
5427
  return [];
5319
5428
  }
5320
5429
  return convertCopperPolygon({
5430
+ cutouts: cutoutsByPolygon.get(record) ?? [],
5321
5431
  layerMap,
5322
5432
  polygonIndex,
5323
5433
  record,
@@ -5763,12 +5873,12 @@ function convertPcbSilkscreenLine({
5763
5873
  }
5764
5874
 
5765
5875
  // lib/pcb/silkscreen/convertPcbSilkscreenRegion.ts
5766
- import { getPcbRegionGeometry as getPcbRegionGeometry4 } from "altiumts";
5876
+ import { getPcbRegionGeometry as getPcbRegionGeometry6 } from "altiumts";
5767
5877
  function convertPcbSilkscreenRegion({
5768
5878
  record,
5769
5879
  recordIndex
5770
5880
  }) {
5771
- const geometry = getPcbRegionGeometry4(record);
5881
+ const geometry = getPcbRegionGeometry6(record);
5772
5882
  const outerVertices = geometry.outline.points.map(toMillimeterPoint);
5773
5883
  if (outerVertices.length < 3) return void 0;
5774
5884
  const innerRings = geometry.holes.map((hole) => ({ vertices: hole.points.map(toMillimeterPoint) })).filter((ring) => ring.vertices.length >= 3);