circuit-json-to-lbrn 0.0.51 → 0.0.53

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.
@@ -0,0 +1,29 @@
1
+ name: 'Close stale issues and PRs'
2
+ on:
3
+ schedule:
4
+ - cron: '30 1 * * *' # Runs at 1:30 AM UTC every day
5
+
6
+ jobs:
7
+ stale:
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - uses: actions/stale@v9
11
+ with:
12
+ # Issues config
13
+ stale-issue-message: 'This issue has been automatically marked as stale because it has had no recent activity. It will be closed if no further activity occurs.'
14
+ close-issue-message: 'This issue was closed because it has been inactive for 14 days since being marked as stale.'
15
+ days-before-issue-stale: 60
16
+ days-before-issue-close: 14
17
+
18
+ # Pull request config
19
+ stale-pr-message: 'This PR has been automatically marked as stale because it has had no recent activity. It will be closed if no further activity occurs.'
20
+ close-pr-message: 'This PR was closed because it has been inactive for 1 day since being marked as stale.'
21
+ days-before-pr-stale: 2
22
+ days-before-pr-close: 1
23
+
24
+ # General config
25
+ exempt-issue-labels: 'pinned,security,help-wanted'
26
+ exempt-pr-labels: 'pinned,security,help-wanted'
27
+ stale-issue-label: 'no-issue-activity'
28
+ stale-pr-label: 'no-pr-activity'
29
+ operations-per-run: 100
package/CLAUDE.md ADDED
@@ -0,0 +1 @@
1
+ - Use `bun`/`bunx` (DO NOT USE `npm` or `npx`)
package/dist/index.d.ts CHANGED
@@ -26,6 +26,12 @@ interface ConvertCircuitJsonToLbrnOptions {
26
26
  * This determines how wide the band of copper removal will be around traces/pads.
27
27
  */
28
28
  copperCutFillMargin?: number;
29
+ /**
30
+ * Whether to generate an oxidation cleaning layer.
31
+ * Creates a filled area covering the entire "inside" of the board outline
32
+ * for laser ablation to clean oxidation from the copper surface.
33
+ */
34
+ includeOxidationCleaningLayer?: boolean;
29
35
  laserProfile?: {
30
36
  copper?: {
31
37
  speed?: number;
package/dist/index.js CHANGED
@@ -2663,6 +2663,95 @@ var createCopperCutFillForLayer = async ({
2663
2663
  }
2664
2664
  };
2665
2665
 
2666
+ // lib/createOxidationCleaningLayerForLayer.ts
2667
+ import { Polygon as Polygon6, Point as Point3 } from "@flatten-js/core";
2668
+ import { ShapePath as ShapePath29, ShapeGroup as ShapeGroup2 } from "lbrnts";
2669
+ var contourToPolygon2 = (contour) => {
2670
+ if (contour.length < 3) return null;
2671
+ const points = contour.map(([x, y]) => new Point3(x, y));
2672
+ try {
2673
+ const polygon = new Polygon6(points);
2674
+ if (polygon.faces.size > 0) {
2675
+ return polygon;
2676
+ }
2677
+ } catch {
2678
+ }
2679
+ return null;
2680
+ };
2681
+ var createOxidationCleaningLayerForLayer = async ({
2682
+ layer,
2683
+ ctx
2684
+ }) => {
2685
+ const { project, boardOutlineContour } = ctx;
2686
+ const cutSetting = layer === "top" ? ctx.topOxidationCleaningCutSetting : ctx.bottomOxidationCleaningCutSetting;
2687
+ if (!cutSetting) {
2688
+ return;
2689
+ }
2690
+ if (!boardOutlineContour || boardOutlineContour.length < 3) {
2691
+ console.warn(
2692
+ `Cannot create oxidation cleaning layer for ${layer}: no board outline available`
2693
+ );
2694
+ return;
2695
+ }
2696
+ try {
2697
+ const manifold = await getManifold();
2698
+ const { CrossSection } = manifold;
2699
+ const fillArea = new CrossSection([boardOutlineContour], "Positive");
2700
+ const simplifiedArea = fillArea.simplify(1e-3);
2701
+ const resultContours = simplifiedArea.toPolygons();
2702
+ if (resultContours.length === 0) {
2703
+ fillArea.delete();
2704
+ simplifiedArea.delete();
2705
+ return;
2706
+ }
2707
+ const shapeGroup = new ShapeGroup2();
2708
+ for (const contour of resultContours) {
2709
+ const polygon = contourToPolygon2(contour);
2710
+ if (!polygon) continue;
2711
+ for (const island of polygon.splitToIslands()) {
2712
+ const { verts, prims } = polygonToShapePathData(island);
2713
+ if (verts.length > 0) {
2714
+ shapeGroup.children.push(
2715
+ new ShapePath29({
2716
+ cutIndex: cutSetting.index,
2717
+ verts,
2718
+ prims,
2719
+ isClosed: true
2720
+ // Filled shapes should be closed
2721
+ })
2722
+ );
2723
+ }
2724
+ }
2725
+ }
2726
+ if (shapeGroup.children.length > 0) {
2727
+ project.children.push(shapeGroup);
2728
+ }
2729
+ fillArea.delete();
2730
+ simplifiedArea.delete();
2731
+ } catch (error) {
2732
+ console.warn(
2733
+ `Failed to create oxidation cleaning layer for ${layer} layer:`,
2734
+ error
2735
+ );
2736
+ }
2737
+ };
2738
+
2739
+ // lib/layer-indexes.ts
2740
+ var LAYER_INDEXES = {
2741
+ // Cut layers (vector/outline)
2742
+ topCopper: 0,
2743
+ bottomCopper: 1,
2744
+ throughBoard: 2,
2745
+ // Scan layers (raster/fill)
2746
+ soldermask: 3,
2747
+ topTraceClearance: 4,
2748
+ bottomTraceClearance: 5,
2749
+ topCopperCutFill: 6,
2750
+ bottomCopperCutFill: 7,
2751
+ topOxidationCleaning: 8,
2752
+ bottomOxidationCleaning: 9
2753
+ };
2754
+
2666
2755
  // lib/index.ts
2667
2756
  var convertCircuitJsonToLbrn = async (circuitJson, options = {}) => {
2668
2757
  const db = cju2(circuitJson);
@@ -2680,6 +2769,7 @@ var convertCircuitJsonToLbrn = async (circuitJson, options = {}) => {
2680
2769
  const laserProfile = options.laserProfile;
2681
2770
  const includeCopperCutFill = options.includeCopperCutFill ?? false;
2682
2771
  const copperCutFillMargin = options.copperCutFillMargin ?? 0.5;
2772
+ const includeOxidationCleaningLayer = options.includeOxidationCleaningLayer ?? false;
2683
2773
  const defaultCopperSettings = {
2684
2774
  speed: 300,
2685
2775
  numPasses: 100,
@@ -2699,7 +2789,7 @@ var convertCircuitJsonToLbrn = async (circuitJson, options = {}) => {
2699
2789
  throw new Error("traceMargin requires includeCopper to be true");
2700
2790
  }
2701
2791
  const topCopperCutSetting = new CutSetting({
2702
- index: 0,
2792
+ index: LAYER_INDEXES.topCopper,
2703
2793
  name: "Cut Top Copper",
2704
2794
  numPasses: copperSettings.numPasses,
2705
2795
  speed: copperSettings.speed,
@@ -2708,7 +2798,7 @@ var convertCircuitJsonToLbrn = async (circuitJson, options = {}) => {
2708
2798
  });
2709
2799
  project.children.push(topCopperCutSetting);
2710
2800
  const bottomCopperCutSetting = new CutSetting({
2711
- index: 1,
2801
+ index: LAYER_INDEXES.bottomCopper,
2712
2802
  name: "Cut Bottom Copper",
2713
2803
  numPasses: copperSettings.numPasses,
2714
2804
  speed: copperSettings.speed,
@@ -2717,7 +2807,7 @@ var convertCircuitJsonToLbrn = async (circuitJson, options = {}) => {
2717
2807
  });
2718
2808
  project.children.push(bottomCopperCutSetting);
2719
2809
  const throughBoardCutSetting = new CutSetting({
2720
- index: 2,
2810
+ index: LAYER_INDEXES.throughBoard,
2721
2811
  name: "Cut Through Board",
2722
2812
  numPasses: boardSettings.numPasses,
2723
2813
  speed: boardSettings.speed,
@@ -2727,7 +2817,7 @@ var convertCircuitJsonToLbrn = async (circuitJson, options = {}) => {
2727
2817
  project.children.push(throughBoardCutSetting);
2728
2818
  const soldermaskCutSetting = new CutSetting({
2729
2819
  type: "Scan",
2730
- index: 3,
2820
+ index: LAYER_INDEXES.soldermask,
2731
2821
  name: "Cut Soldermask",
2732
2822
  numPasses: 1,
2733
2823
  speed: 150,
@@ -2737,14 +2827,13 @@ var convertCircuitJsonToLbrn = async (circuitJson, options = {}) => {
2737
2827
  crossHatch: true
2738
2828
  });
2739
2829
  project.children.push(soldermaskCutSetting);
2740
- let nextCutIndex = 4;
2741
2830
  let topTraceClearanceAreaCutSetting;
2742
2831
  let bottomTraceClearanceAreaCutSetting;
2743
2832
  if (shouldGenerateTraceClearanceZones) {
2744
2833
  if (includeLayers.includes("top")) {
2745
2834
  topTraceClearanceAreaCutSetting = new CutSetting({
2746
2835
  type: "Scan",
2747
- index: nextCutIndex++,
2836
+ index: LAYER_INDEXES.topTraceClearance,
2748
2837
  name: "Clear Top Trace Clearance Areas",
2749
2838
  numPasses: 12,
2750
2839
  speed: 100,
@@ -2758,7 +2847,7 @@ var convertCircuitJsonToLbrn = async (circuitJson, options = {}) => {
2758
2847
  if (includeLayers.includes("bottom")) {
2759
2848
  bottomTraceClearanceAreaCutSetting = new CutSetting({
2760
2849
  type: "Scan",
2761
- index: nextCutIndex++,
2850
+ index: LAYER_INDEXES.bottomTraceClearance,
2762
2851
  name: "Clear Bottom Trace Clearance Areas",
2763
2852
  numPasses: 12,
2764
2853
  speed: 100,
@@ -2776,7 +2865,7 @@ var convertCircuitJsonToLbrn = async (circuitJson, options = {}) => {
2776
2865
  if (includeLayers.includes("top")) {
2777
2866
  topCopperCutFillCutSetting = new CutSetting({
2778
2867
  type: "Scan",
2779
- index: nextCutIndex++,
2868
+ index: LAYER_INDEXES.topCopperCutFill,
2780
2869
  name: "Top Copper Cut Fill",
2781
2870
  numPasses: copperSettings.numPasses,
2782
2871
  speed: copperSettings.speed,
@@ -2790,7 +2879,7 @@ var convertCircuitJsonToLbrn = async (circuitJson, options = {}) => {
2790
2879
  if (includeLayers.includes("bottom")) {
2791
2880
  bottomCopperCutFillCutSetting = new CutSetting({
2792
2881
  type: "Scan",
2793
- index: nextCutIndex++,
2882
+ index: LAYER_INDEXES.bottomCopperCutFill,
2794
2883
  name: "Bottom Copper Cut Fill",
2795
2884
  numPasses: copperSettings.numPasses,
2796
2885
  speed: copperSettings.speed,
@@ -2802,6 +2891,38 @@ var convertCircuitJsonToLbrn = async (circuitJson, options = {}) => {
2802
2891
  project.children.push(bottomCopperCutFillCutSetting);
2803
2892
  }
2804
2893
  }
2894
+ let topOxidationCleaningCutSetting;
2895
+ let bottomOxidationCleaningCutSetting;
2896
+ if (includeOxidationCleaningLayer) {
2897
+ if (includeLayers.includes("top")) {
2898
+ topOxidationCleaningCutSetting = new CutSetting({
2899
+ type: "Scan",
2900
+ index: LAYER_INDEXES.topOxidationCleaning,
2901
+ name: "Top Oxidation Cleaning",
2902
+ numPasses: copperSettings.numPasses,
2903
+ speed: 500,
2904
+ scanOpt: "individual",
2905
+ interval: laserSpotSize,
2906
+ angle: 45,
2907
+ crossHatch: true
2908
+ });
2909
+ project.children.push(topOxidationCleaningCutSetting);
2910
+ }
2911
+ if (includeLayers.includes("bottom")) {
2912
+ bottomOxidationCleaningCutSetting = new CutSetting({
2913
+ type: "Scan",
2914
+ index: LAYER_INDEXES.bottomOxidationCleaning,
2915
+ name: "Bottom Oxidation Cleaning",
2916
+ numPasses: copperSettings.numPasses,
2917
+ speed: 500,
2918
+ scanOpt: "individual",
2919
+ interval: laserSpotSize,
2920
+ angle: 45,
2921
+ crossHatch: true
2922
+ });
2923
+ project.children.push(bottomOxidationCleaningCutSetting);
2924
+ }
2925
+ }
2805
2926
  const connMap = getFullConnectivityMapFromCircuitJson(circuitJson);
2806
2927
  let origin = options.origin;
2807
2928
  if (!origin) {
@@ -2833,6 +2954,8 @@ var convertCircuitJsonToLbrn = async (circuitJson, options = {}) => {
2833
2954
  topCopperCutFillCutSetting,
2834
2955
  bottomCopperCutFillCutSetting,
2835
2956
  copperCutFillMargin,
2957
+ topOxidationCleaningCutSetting,
2958
+ bottomOxidationCleaningCutSetting,
2836
2959
  topTraceEndpoints: /* @__PURE__ */ new Set(),
2837
2960
  bottomTraceEndpoints: /* @__PURE__ */ new Set()
2838
2961
  };
@@ -2909,6 +3032,14 @@ var convertCircuitJsonToLbrn = async (circuitJson, options = {}) => {
2909
3032
  await createCopperCutFillForLayer({ layer: "bottom", ctx });
2910
3033
  }
2911
3034
  }
3035
+ if (includeOxidationCleaningLayer) {
3036
+ if (includeLayers.includes("top")) {
3037
+ await createOxidationCleaningLayerForLayer({ layer: "top", ctx });
3038
+ }
3039
+ if (includeLayers.includes("bottom")) {
3040
+ await createOxidationCleaningLayerForLayer({ layer: "bottom", ctx });
3041
+ }
3042
+ }
2912
3043
  return project;
2913
3044
  };
2914
3045
  export {
@@ -50,6 +50,10 @@ export interface ConvertContext {
50
50
  topCopperCutFillCutSetting?: CutSetting
51
51
  bottomCopperCutFillCutSetting?: CutSetting
52
52
 
53
+ // Cut settings for oxidation cleaning layer
54
+ topOxidationCleaningCutSetting?: CutSetting
55
+ bottomOxidationCleaningCutSetting?: CutSetting
56
+
53
57
  // Percent-based solder mask margin (scales with element size)
54
58
  solderMaskMarginPercent: number
55
59
 
@@ -0,0 +1,120 @@
1
+ import { Polygon, Point } from "@flatten-js/core"
2
+ import type { ConvertContext } from "./ConvertContext"
3
+ import { polygonToShapePathData } from "./polygon-to-shape-path"
4
+ import { ShapePath, ShapeGroup } from "lbrnts"
5
+ import { getManifold } from "./getManifold"
6
+
7
+ type Contour = Array<[number, number]>
8
+
9
+ /**
10
+ * Converts a single contour to a flatten-js Polygon
11
+ */
12
+ const contourToPolygon = (contour: Contour): Polygon | null => {
13
+ if (contour.length < 3) return null
14
+
15
+ const points = contour.map(([x, y]) => new Point(x, y))
16
+ try {
17
+ const polygon = new Polygon(points)
18
+ if (polygon.faces.size > 0) {
19
+ return polygon
20
+ }
21
+ } catch {
22
+ // Skip invalid polygons
23
+ }
24
+ return null
25
+ }
26
+
27
+ /**
28
+ * Creates an oxidation cleaning layer for a given layer.
29
+ *
30
+ * This generates a filled area covering the entire "inside" of the board outline
31
+ * that can be used to laser clean oxidation from the copper surface.
32
+ *
33
+ * The algorithm:
34
+ * 1. Get the board outline contour
35
+ * 2. Optionally inset (shrink) the outline by the margin to leave a border
36
+ * 3. Create SCAN mode shapes that will fill the entire board area
37
+ */
38
+ export const createOxidationCleaningLayerForLayer = async ({
39
+ layer,
40
+ ctx,
41
+ }: {
42
+ layer: "top" | "bottom"
43
+ ctx: ConvertContext
44
+ }): Promise<void> => {
45
+ const { project, boardOutlineContour } = ctx
46
+
47
+ // Get the appropriate cut setting for this layer
48
+ const cutSetting =
49
+ layer === "top"
50
+ ? ctx.topOxidationCleaningCutSetting
51
+ : ctx.bottomOxidationCleaningCutSetting
52
+
53
+ if (!cutSetting) {
54
+ return
55
+ }
56
+
57
+ if (!boardOutlineContour || boardOutlineContour.length < 3) {
58
+ console.warn(
59
+ `Cannot create oxidation cleaning layer for ${layer}: no board outline available`,
60
+ )
61
+ return
62
+ }
63
+
64
+ try {
65
+ const manifold = await getManifold()
66
+ const { CrossSection } = manifold
67
+
68
+ // Create board outline as CrossSection (the fill area)
69
+ const fillArea = new CrossSection([boardOutlineContour], "Positive")
70
+
71
+ // Simplify to clean up any spurious tiny segments
72
+ const simplifiedArea = fillArea.simplify(0.001)
73
+
74
+ // Get the resulting contours
75
+ const resultContours: Contour[] = simplifiedArea.toPolygons()
76
+
77
+ if (resultContours.length === 0) {
78
+ fillArea.delete()
79
+ simplifiedArea.delete()
80
+ return
81
+ }
82
+
83
+ // Create a ShapeGroup to hold all contours
84
+ const shapeGroup = new ShapeGroup()
85
+
86
+ for (const contour of resultContours) {
87
+ const polygon = contourToPolygon(contour)
88
+ if (!polygon) continue
89
+
90
+ for (const island of polygon.splitToIslands()) {
91
+ const { verts, prims } = polygonToShapePathData(island)
92
+
93
+ if (verts.length > 0) {
94
+ shapeGroup.children.push(
95
+ new ShapePath({
96
+ cutIndex: cutSetting.index,
97
+ verts,
98
+ prims,
99
+ isClosed: true, // Filled shapes should be closed
100
+ }),
101
+ )
102
+ }
103
+ }
104
+ }
105
+
106
+ // Add the group to the project if it has shapes
107
+ if (shapeGroup.children.length > 0) {
108
+ project.children.push(shapeGroup)
109
+ }
110
+
111
+ // Clean up WASM memory
112
+ fillArea.delete()
113
+ simplifiedArea.delete()
114
+ } catch (error) {
115
+ console.warn(
116
+ `Failed to create oxidation cleaning layer for ${layer} layer:`,
117
+ error,
118
+ )
119
+ }
120
+ }
package/lib/index.ts CHANGED
@@ -17,6 +17,8 @@ import { addPcbCutout } from "./element-handlers/addPcbCutout"
17
17
  import { createCopperShapesForLayer } from "./createCopperShapesForLayer"
18
18
  import { createTraceClearanceAreasForLayer } from "./createTraceClearanceAreasForLayer"
19
19
  import { createCopperCutFillForLayer } from "./createCopperCutFillForLayer"
20
+ import { createOxidationCleaningLayerForLayer } from "./createOxidationCleaningLayerForLayer"
21
+ import { LAYER_INDEXES } from "./layer-indexes"
20
22
 
21
23
  export interface ConvertCircuitJsonToLbrnOptions {
22
24
  includeSilkscreen?: boolean
@@ -40,6 +42,12 @@ export interface ConvertCircuitJsonToLbrnOptions {
40
42
  * This determines how wide the band of copper removal will be around traces/pads.
41
43
  */
42
44
  copperCutFillMargin?: number
45
+ /**
46
+ * Whether to generate an oxidation cleaning layer.
47
+ * Creates a filled area covering the entire "inside" of the board outline
48
+ * for laser ablation to clean oxidation from the copper surface.
49
+ */
50
+ includeOxidationCleaningLayer?: boolean
43
51
  laserProfile?: {
44
52
  copper?: {
45
53
  speed?: number
@@ -77,6 +85,8 @@ export const convertCircuitJsonToLbrn = async (
77
85
  const laserProfile = options.laserProfile
78
86
  const includeCopperCutFill = options.includeCopperCutFill ?? false
79
87
  const copperCutFillMargin = options.copperCutFillMargin ?? 0.5
88
+ const includeOxidationCleaningLayer =
89
+ options.includeOxidationCleaningLayer ?? false
80
90
 
81
91
  // Default laser settings
82
92
  const defaultCopperSettings = {
@@ -106,7 +116,7 @@ export const convertCircuitJsonToLbrn = async (
106
116
 
107
117
  // Create cut settings
108
118
  const topCopperCutSetting = new CutSetting({
109
- index: 0,
119
+ index: LAYER_INDEXES.topCopper,
110
120
  name: "Cut Top Copper",
111
121
  numPasses: copperSettings.numPasses,
112
122
  speed: copperSettings.speed,
@@ -116,7 +126,7 @@ export const convertCircuitJsonToLbrn = async (
116
126
  project.children.push(topCopperCutSetting)
117
127
 
118
128
  const bottomCopperCutSetting = new CutSetting({
119
- index: 1,
129
+ index: LAYER_INDEXES.bottomCopper,
120
130
  name: "Cut Bottom Copper",
121
131
  numPasses: copperSettings.numPasses,
122
132
  speed: copperSettings.speed,
@@ -126,7 +136,7 @@ export const convertCircuitJsonToLbrn = async (
126
136
  project.children.push(bottomCopperCutSetting)
127
137
 
128
138
  const throughBoardCutSetting = new CutSetting({
129
- index: 2,
139
+ index: LAYER_INDEXES.throughBoard,
130
140
  name: "Cut Through Board",
131
141
  numPasses: boardSettings.numPasses,
132
142
  speed: boardSettings.speed,
@@ -137,7 +147,7 @@ export const convertCircuitJsonToLbrn = async (
137
147
 
138
148
  const soldermaskCutSetting = new CutSetting({
139
149
  type: "Scan",
140
- index: 3,
150
+ index: LAYER_INDEXES.soldermask,
141
151
  name: "Cut Soldermask",
142
152
  numPasses: 1,
143
153
  speed: 150,
@@ -148,9 +158,6 @@ export const convertCircuitJsonToLbrn = async (
148
158
  })
149
159
  project.children.push(soldermaskCutSetting)
150
160
 
151
- // Track the next available cut setting index
152
- let nextCutIndex = 4
153
-
154
161
  // Create trace clearance cut settings if needed
155
162
  let topTraceClearanceAreaCutSetting: CutSetting | undefined
156
163
  let bottomTraceClearanceAreaCutSetting: CutSetting | undefined
@@ -159,7 +166,7 @@ export const convertCircuitJsonToLbrn = async (
159
166
  if (includeLayers.includes("top")) {
160
167
  topTraceClearanceAreaCutSetting = new CutSetting({
161
168
  type: "Scan",
162
- index: nextCutIndex++,
169
+ index: LAYER_INDEXES.topTraceClearance,
163
170
  name: "Clear Top Trace Clearance Areas",
164
171
  numPasses: 12,
165
172
  speed: 100,
@@ -174,7 +181,7 @@ export const convertCircuitJsonToLbrn = async (
174
181
  if (includeLayers.includes("bottom")) {
175
182
  bottomTraceClearanceAreaCutSetting = new CutSetting({
176
183
  type: "Scan",
177
- index: nextCutIndex++,
184
+ index: LAYER_INDEXES.bottomTraceClearance,
178
185
  name: "Clear Bottom Trace Clearance Areas",
179
186
  numPasses: 12,
180
187
  speed: 100,
@@ -195,7 +202,7 @@ export const convertCircuitJsonToLbrn = async (
195
202
  if (includeLayers.includes("top")) {
196
203
  topCopperCutFillCutSetting = new CutSetting({
197
204
  type: "Scan",
198
- index: nextCutIndex++,
205
+ index: LAYER_INDEXES.topCopperCutFill,
199
206
  name: "Top Copper Cut Fill",
200
207
  numPasses: copperSettings.numPasses,
201
208
  speed: copperSettings.speed,
@@ -210,7 +217,7 @@ export const convertCircuitJsonToLbrn = async (
210
217
  if (includeLayers.includes("bottom")) {
211
218
  bottomCopperCutFillCutSetting = new CutSetting({
212
219
  type: "Scan",
213
- index: nextCutIndex++,
220
+ index: LAYER_INDEXES.bottomCopperCutFill,
214
221
  name: "Bottom Copper Cut Fill",
215
222
  numPasses: copperSettings.numPasses,
216
223
  speed: copperSettings.speed,
@@ -223,6 +230,42 @@ export const convertCircuitJsonToLbrn = async (
223
230
  }
224
231
  }
225
232
 
233
+ // Create oxidation cleaning layer cut settings if needed
234
+ let topOxidationCleaningCutSetting: CutSetting | undefined
235
+ let bottomOxidationCleaningCutSetting: CutSetting | undefined
236
+
237
+ if (includeOxidationCleaningLayer) {
238
+ if (includeLayers.includes("top")) {
239
+ topOxidationCleaningCutSetting = new CutSetting({
240
+ type: "Scan",
241
+ index: LAYER_INDEXES.topOxidationCleaning,
242
+ name: "Top Oxidation Cleaning",
243
+ numPasses: copperSettings.numPasses,
244
+ speed: 500,
245
+ scanOpt: "individual",
246
+ interval: laserSpotSize,
247
+ angle: 45,
248
+ crossHatch: true,
249
+ })
250
+ project.children.push(topOxidationCleaningCutSetting)
251
+ }
252
+
253
+ if (includeLayers.includes("bottom")) {
254
+ bottomOxidationCleaningCutSetting = new CutSetting({
255
+ type: "Scan",
256
+ index: LAYER_INDEXES.bottomOxidationCleaning,
257
+ name: "Bottom Oxidation Cleaning",
258
+ numPasses: copperSettings.numPasses,
259
+ speed: 500,
260
+ scanOpt: "individual",
261
+ interval: laserSpotSize,
262
+ angle: 45,
263
+ crossHatch: true,
264
+ })
265
+ project.children.push(bottomOxidationCleaningCutSetting)
266
+ }
267
+ }
268
+
226
269
  // Build connectivity map and origin
227
270
  const connMap = getFullConnectivityMapFromCircuitJson(circuitJson)
228
271
  let origin = options.origin
@@ -257,6 +300,8 @@ export const convertCircuitJsonToLbrn = async (
257
300
  topCopperCutFillCutSetting,
258
301
  bottomCopperCutFillCutSetting,
259
302
  copperCutFillMargin,
303
+ topOxidationCleaningCutSetting,
304
+ bottomOxidationCleaningCutSetting,
260
305
  topTraceEndpoints: new Set(),
261
306
  bottomTraceEndpoints: new Set(),
262
307
  }
@@ -356,5 +401,15 @@ export const convertCircuitJsonToLbrn = async (
356
401
  }
357
402
  }
358
403
 
404
+ // Create oxidation cleaning layer for each layer
405
+ if (includeOxidationCleaningLayer) {
406
+ if (includeLayers.includes("top")) {
407
+ await createOxidationCleaningLayerForLayer({ layer: "top", ctx })
408
+ }
409
+ if (includeLayers.includes("bottom")) {
410
+ await createOxidationCleaningLayerForLayer({ layer: "bottom", ctx })
411
+ }
412
+ }
413
+
359
414
  return project
360
415
  }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Consistent layer index assignments for LightBurn cut settings.
3
+ * Each layer type always gets the same index (and thus the same color)
4
+ * regardless of what other layers are enabled.
5
+ */
6
+ export const LAYER_INDEXES = {
7
+ // Cut layers (vector/outline)
8
+ topCopper: 0,
9
+ bottomCopper: 1,
10
+ throughBoard: 2,
11
+
12
+ // Scan layers (raster/fill)
13
+ soldermask: 3,
14
+ topTraceClearance: 4,
15
+ bottomTraceClearance: 5,
16
+ topCopperCutFill: 6,
17
+ bottomCopperCutFill: 7,
18
+ topOxidationCleaning: 8,
19
+ bottomOxidationCleaning: 9,
20
+ } as const
21
+
22
+ export type LayerIndex = (typeof LAYER_INDEXES)[keyof typeof LAYER_INDEXES]
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "circuit-json-to-lbrn",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.51",
4
+ "version": "0.0.53",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "start": "bun run site/index.html",
@@ -3,6 +3,6 @@
3
3
  <style></style><rect class="boundary" x="0" y="0" fill="#000" width="800" height="600" data-type="pcb_background" data-pcb-layer="global"/><rect class="pcb-boundary" fill="none" stroke="#fff" stroke-width="0.3" x="127.27272727272725" y="27.272727272727252" width="545.4545454545455" height="545.4545454545455" data-type="pcb_boundary" data-pcb-layer="global"/><path class="pcb-board" d="M 127.27272727272725 572.7272727272727 L 672.7272727272727 572.7272727272727 L 672.7272727272727 27.272727272727252 L 127.27272727272725 27.272727272727252 Z" fill="none" stroke="rgba(255, 255, 255, 0.5)" stroke-width="2.7272727272727275" data-type="pcb_board" data-pcb-layer="board"/><path class="pcb-trace" stroke="rgb(200, 52, 52)" fill="none" d="M 263.6363636363636 300 L 536.3636363636364 300" stroke-width="13.636363636363637" stroke-linecap="round" stroke-linejoin="round" shape-rendering="crispEdges" data-type="pcb_trace" data-pcb-layer="top"/>
4
4
  </g>
5
5
  <g transform="translate(0, 600) scale(20, 20) translate(14.65, 19.65)">
6
- <defs><pattern id="hatch-0.0050-45-true-_23FFFF00-0.1000-0.80__stack1" patternUnits="userSpaceOnUse" width="0.005" height="0.005" patternTransform="rotate(45)"><path d="M -0.005 0 L 0.005 0 M 0 -0.005 L 0 0.005" stroke="#FFFF00" stroke-width="0.1" stroke-opacity="0.8"/></pattern></defs><rect x="-14.65" y="-19.65" width="40" height="40" fill="white"/><g transform="matrix(1 0 0 -1 0 0.7000000000000028)"><g transform="matrix(1,0,0,1,0,0)"><path d="M -4.65 -9.65 L 15.35 -9.65 L 15.35 10.35 L -4.65 10.35 L -4.65 -9.65 L -4.65 -9.65 Z" fill="none" stroke="#FF0000" stroke-width="0.1" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="matrix(1,0,0,1,0,0)"><path d="M 10.23867555286557 0.09999999999999998 L 10.2447620560996 0.09593312855939617 L 10.35 0.07499999999999996 L 10.4552379439004 0.09593312855939612 L 10.5444543648263 0.15554563517369935 L 10.604066871440603 0.2447620560996001 L 10.625 0.35 L 10.604066871440603 0.4552379439003997 L 10.5444543648263 0.5444543648263005 L 10.4552379439004 0.6040668714406039 L 10.35 0.625 L 10.2447620560996 0.6040668714406039 L 10.238675552865573 0.6 L 0.46132444713442683 0.6 L 0.45523794390039934 0.6040668714406039 L 0.34999999999999964 0.625 L 0.24476205609959995 0.6040668714406039 L 0.15554563517369907 0.5444543648263006 L 0.09593312855939579 0.45523794390039973 L 0.07499999999999962 0.35000000000000003 L 0.09593312855939573 0.2447620560996003 L 0.15554563517369902 0.1555456351736994 L 0.24476205609959978 0.09593312855939617 L 0.3499999999999996 0.07499999999999996 L 0.4552379439003994 0.09593312855939612 L 0.46132444713442683 0.09999999999999998 L 10.23867555286557 0.09999999999999998 L 10.23867555286557 0.09999999999999998" fill="none" stroke="#000000" stroke-width="0.1" stroke-linecap="round" stroke-linejoin="round"/></g><g><path d="M 10.244762055575848 0.09593313187360764 L 10.238675557076931 0.10000000149011612 L 0.46132444590330124 0.10000000149011612 L 0.4552379474043846 0.09593313187360764 L 0.3500000014901161 0.07500000298023224 L 0.24476205557584763 0.09593313187360764 L 0.15554563701152802 0.15554563701152802 L 0.09593313187360764 0.24476205557584763 L 0.07500000298023224 0.3500000014901161 L 0.09593313187360764 0.4552379474043846 L 0.15554563701152802 0.5444543659687042 L 0.24476205557584763 0.6040668711066246 L 0.3500000014901161 0.625 L 0.4552379474043846 0.6040668711066246 L 0.46132444590330124 0.6000000014901161 L 10.238675557076931 0.6000000014901161 L 10.244762055575848 0.6040668711066246 L 10.350000001490116 0.625 L 10.455237947404385 0.6040668711066246 L 10.544454365968704 0.5444543659687042 L 10.604066871106625 0.4552379474043846 L 10.625 0.3500000014901161 L 10.604066871106625 0.24476205557584763 L 10.544454365968704 0.15554563701152802 L 10.455237947404385 0.09593313187360764 L 10.350000001490116 0.07500000298023224 L 10.244762055575848 0.09593313187360764 L 10.244762055575848 0.09593313187360764 Z M 10.447545163333416 -0.4153926372528076 L 10.552783109247684 -0.3944595083594322 L 10.646579667925835 -0.36600664258003235 L 10.733023069798946 -0.3198016807436943 L 10.822239503264427 -0.26018916070461273 L 10.898007772862911 -0.1980077400803566 L 10.960189193487167 -0.12223946303129196 L 11.019801691174507 -0.033023037016391754 L 11.066006653010845 0.05342037230730057 L 11.094459511339664 0.14721688628196716 L 11.11539264023304 0.25245483219623566 L 11.125 0.3499999865889549 L 11.11539264023304 0.447545163333416 L 11.094459511339664 0.5527831092476845 L 11.066006638109684 0.6465796679258347 L 11.019801691174507 0.7330230474472046 L 10.960189193487167 0.8222394734621048 L 10.898007780313492 0.8980077430605888 L 10.822239473462105 0.9601891934871674 L 10.733023047447205 1.0198016911745071 L 10.646579638123512 1.0660066530108452 L 10.552783109247684 1.0944595113396645 L 10.447545163333416 1.1153926402330399 L 10.350000001490116 1.125 L 10.252454832196236 1.1153926402330399 L 10.175070807337761 1.1000000014901161 L 0.5249291881918907 1.1000000014901161 L 0.447545163333416 1.1153926402330399 L 0.3500000014901161 1.125 L 0.25245483219623566 1.1153926402330399 L 0.14721688628196716 1.0944595113396645 L 0.053420327603816986 1.0660066306591034 L -0.033023037016391754 1.0198016911745071 L -0.12223946303129196 0.9601891934871674 L -0.198007732629776 0.8980077803134918 L -0.26018916070461273 0.8222395032644272 L -0.3198016732931137 0.733023077249527 L -0.36600662767887115 0.6465796828269958 L -0.3944595083594322 0.5527831315994263 L -0.4153926372528076 0.447545163333416 L -0.42499999701976776 0.3500000014901161 L -0.4153926372528076 0.25245483219623566 L -0.3944595083594322 0.14721688628196716 L -0.36600662767887115 0.053420327603816986 L -0.3198016732931137 -0.03302306681871414 L -0.26018916070461273 -0.12223949283361435 L -0.1980077400803566 -0.1980077624320984 L -0.12223949283361435 -0.26018916070461273 L -0.03302306681871414 -0.3198016732931137 L 0.053420327603816986 -0.36600662767887115 L 0.14721687883138657 -0.3944595083594322 L 0.25245483219623566 -0.4153926372528076 L 0.3499999865889549 -0.42499999701976776 L 0.447545163333416 -0.4153926372528076 L 0.5249291881918907 -0.3999999985098839 L 10.175070807337761 -0.3999999985098839 L 10.252454832196236 -0.4153926372528076 L 10.349999986588955 -0.42499999701976776 L 10.447545163333416 -0.4153926372528076 L 10.447545163333416 -0.4153926372528076 Z" fill="url(#hatch-0.0050-45-true-_23FFFF00-0.1000-0.80__stack1)" fill-rule="nonzero" stroke="none"/><path d="M 10.244762055575848 0.09593313187360764 L 10.238675557076931 0.10000000149011612 L 0.46132444590330124 0.10000000149011612 L 0.4552379474043846 0.09593313187360764 L 0.3500000014901161 0.07500000298023224 L 0.24476205557584763 0.09593313187360764 L 0.15554563701152802 0.15554563701152802 L 0.09593313187360764 0.24476205557584763 L 0.07500000298023224 0.3500000014901161 L 0.09593313187360764 0.4552379474043846 L 0.15554563701152802 0.5444543659687042 L 0.24476205557584763 0.6040668711066246 L 0.3500000014901161 0.625 L 0.4552379474043846 0.6040668711066246 L 0.46132444590330124 0.6000000014901161 L 10.238675557076931 0.6000000014901161 L 10.244762055575848 0.6040668711066246 L 10.350000001490116 0.625 L 10.455237947404385 0.6040668711066246 L 10.544454365968704 0.5444543659687042 L 10.604066871106625 0.4552379474043846 L 10.625 0.3500000014901161 L 10.604066871106625 0.24476205557584763 L 10.544454365968704 0.15554563701152802 L 10.455237947404385 0.09593313187360764 L 10.350000001490116 0.07500000298023224 L 10.244762055575848 0.09593313187360764 L 10.244762055575848 0.09593313187360764 Z M 10.447545163333416 -0.4153926372528076 L 10.552783109247684 -0.3944595083594322 L 10.646579667925835 -0.36600664258003235 L 10.733023069798946 -0.3198016807436943 L 10.822239503264427 -0.26018916070461273 L 10.898007772862911 -0.1980077400803566 L 10.960189193487167 -0.12223946303129196 L 11.019801691174507 -0.033023037016391754 L 11.066006653010845 0.05342037230730057 L 11.094459511339664 0.14721688628196716 L 11.11539264023304 0.25245483219623566 L 11.125 0.3499999865889549 L 11.11539264023304 0.447545163333416 L 11.094459511339664 0.5527831092476845 L 11.066006638109684 0.6465796679258347 L 11.019801691174507 0.7330230474472046 L 10.960189193487167 0.8222394734621048 L 10.898007780313492 0.8980077430605888 L 10.822239473462105 0.9601891934871674 L 10.733023047447205 1.0198016911745071 L 10.646579638123512 1.0660066530108452 L 10.552783109247684 1.0944595113396645 L 10.447545163333416 1.1153926402330399 L 10.350000001490116 1.125 L 10.252454832196236 1.1153926402330399 L 10.175070807337761 1.1000000014901161 L 0.5249291881918907 1.1000000014901161 L 0.447545163333416 1.1153926402330399 L 0.3500000014901161 1.125 L 0.25245483219623566 1.1153926402330399 L 0.14721688628196716 1.0944595113396645 L 0.053420327603816986 1.0660066306591034 L -0.033023037016391754 1.0198016911745071 L -0.12223946303129196 0.9601891934871674 L -0.198007732629776 0.8980077803134918 L -0.26018916070461273 0.8222395032644272 L -0.3198016732931137 0.733023077249527 L -0.36600662767887115 0.6465796828269958 L -0.3944595083594322 0.5527831315994263 L -0.4153926372528076 0.447545163333416 L -0.42499999701976776 0.3500000014901161 L -0.4153926372528076 0.25245483219623566 L -0.3944595083594322 0.14721688628196716 L -0.36600662767887115 0.053420327603816986 L -0.3198016732931137 -0.03302306681871414 L -0.26018916070461273 -0.12223949283361435 L -0.1980077400803566 -0.1980077624320984 L -0.12223949283361435 -0.26018916070461273 L -0.03302306681871414 -0.3198016732931137 L 0.053420327603816986 -0.36600662767887115 L 0.14721687883138657 -0.3944595083594322 L 0.25245483219623566 -0.4153926372528076 L 0.3499999865889549 -0.42499999701976776 L 0.447545163333416 -0.4153926372528076 L 0.5249291881918907 -0.3999999985098839 L 10.175070807337761 -0.3999999985098839 L 10.252454832196236 -0.4153926372528076 L 10.349999986588955 -0.42499999701976776 L 10.447545163333416 -0.4153926372528076 L 10.447545163333416 -0.4153926372528076 Z" fill="none" fill-rule="nonzero" stroke="#FFFF00" stroke-width="0.1"/></g></g>
6
+ <defs><pattern id="hatch-0.0050-45-true-_2300FFFF-0.1000-0.80__stack1" patternUnits="userSpaceOnUse" width="0.005" height="0.005" patternTransform="rotate(45)"><path d="M -0.005 0 L 0.005 0 M 0 -0.005 L 0 0.005" stroke="#00FFFF" stroke-width="0.1" stroke-opacity="0.8"/></pattern></defs><rect x="-14.65" y="-19.65" width="40" height="40" fill="white"/><g transform="matrix(1 0 0 -1 0 0.7000000000000028)"><g transform="matrix(1,0,0,1,0,0)"><path d="M -4.65 -9.65 L 15.35 -9.65 L 15.35 10.35 L -4.65 10.35 L -4.65 -9.65 L -4.65 -9.65 Z" fill="none" stroke="#FF0000" stroke-width="0.1" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="matrix(1,0,0,1,0,0)"><path d="M 10.23867555286557 0.09999999999999998 L 10.2447620560996 0.09593312855939617 L 10.35 0.07499999999999996 L 10.4552379439004 0.09593312855939612 L 10.5444543648263 0.15554563517369935 L 10.604066871440603 0.2447620560996001 L 10.625 0.35 L 10.604066871440603 0.4552379439003997 L 10.5444543648263 0.5444543648263005 L 10.4552379439004 0.6040668714406039 L 10.35 0.625 L 10.2447620560996 0.6040668714406039 L 10.238675552865573 0.6 L 0.46132444713442683 0.6 L 0.45523794390039934 0.6040668714406039 L 0.34999999999999964 0.625 L 0.24476205609959995 0.6040668714406039 L 0.15554563517369907 0.5444543648263006 L 0.09593312855939579 0.45523794390039973 L 0.07499999999999962 0.35000000000000003 L 0.09593312855939573 0.2447620560996003 L 0.15554563517369902 0.1555456351736994 L 0.24476205609959978 0.09593312855939617 L 0.3499999999999996 0.07499999999999996 L 0.4552379439003994 0.09593312855939612 L 0.46132444713442683 0.09999999999999998 L 10.23867555286557 0.09999999999999998 L 10.23867555286557 0.09999999999999998" fill="none" stroke="#000000" stroke-width="0.1" stroke-linecap="round" stroke-linejoin="round"/></g><g><path d="M 10.244762055575848 0.09593313187360764 L 10.238675557076931 0.10000000149011612 L 0.46132444590330124 0.10000000149011612 L 0.4552379474043846 0.09593313187360764 L 0.3500000014901161 0.07500000298023224 L 0.24476205557584763 0.09593313187360764 L 0.15554563701152802 0.15554563701152802 L 0.09593313187360764 0.24476205557584763 L 0.07500000298023224 0.3500000014901161 L 0.09593313187360764 0.4552379474043846 L 0.15554563701152802 0.5444543659687042 L 0.24476205557584763 0.6040668711066246 L 0.3500000014901161 0.625 L 0.4552379474043846 0.6040668711066246 L 0.46132444590330124 0.6000000014901161 L 10.238675557076931 0.6000000014901161 L 10.244762055575848 0.6040668711066246 L 10.350000001490116 0.625 L 10.455237947404385 0.6040668711066246 L 10.544454365968704 0.5444543659687042 L 10.604066871106625 0.4552379474043846 L 10.625 0.3500000014901161 L 10.604066871106625 0.24476205557584763 L 10.544454365968704 0.15554563701152802 L 10.455237947404385 0.09593313187360764 L 10.350000001490116 0.07500000298023224 L 10.244762055575848 0.09593313187360764 L 10.244762055575848 0.09593313187360764 Z M 10.447545163333416 -0.4153926372528076 L 10.552783109247684 -0.3944595083594322 L 10.646579667925835 -0.36600664258003235 L 10.733023069798946 -0.3198016807436943 L 10.822239503264427 -0.26018916070461273 L 10.898007772862911 -0.1980077400803566 L 10.960189193487167 -0.12223946303129196 L 11.019801691174507 -0.033023037016391754 L 11.066006653010845 0.05342037230730057 L 11.094459511339664 0.14721688628196716 L 11.11539264023304 0.25245483219623566 L 11.125 0.3499999865889549 L 11.11539264023304 0.447545163333416 L 11.094459511339664 0.5527831092476845 L 11.066006638109684 0.6465796679258347 L 11.019801691174507 0.7330230474472046 L 10.960189193487167 0.8222394734621048 L 10.898007780313492 0.8980077430605888 L 10.822239473462105 0.9601891934871674 L 10.733023047447205 1.0198016911745071 L 10.646579638123512 1.0660066530108452 L 10.552783109247684 1.0944595113396645 L 10.447545163333416 1.1153926402330399 L 10.350000001490116 1.125 L 10.252454832196236 1.1153926402330399 L 10.175070807337761 1.1000000014901161 L 0.5249291881918907 1.1000000014901161 L 0.447545163333416 1.1153926402330399 L 0.3500000014901161 1.125 L 0.25245483219623566 1.1153926402330399 L 0.14721688628196716 1.0944595113396645 L 0.053420327603816986 1.0660066306591034 L -0.033023037016391754 1.0198016911745071 L -0.12223946303129196 0.9601891934871674 L -0.198007732629776 0.8980077803134918 L -0.26018916070461273 0.8222395032644272 L -0.3198016732931137 0.733023077249527 L -0.36600662767887115 0.6465796828269958 L -0.3944595083594322 0.5527831315994263 L -0.4153926372528076 0.447545163333416 L -0.42499999701976776 0.3500000014901161 L -0.4153926372528076 0.25245483219623566 L -0.3944595083594322 0.14721688628196716 L -0.36600662767887115 0.053420327603816986 L -0.3198016732931137 -0.03302306681871414 L -0.26018916070461273 -0.12223949283361435 L -0.1980077400803566 -0.1980077624320984 L -0.12223949283361435 -0.26018916070461273 L -0.03302306681871414 -0.3198016732931137 L 0.053420327603816986 -0.36600662767887115 L 0.14721687883138657 -0.3944595083594322 L 0.25245483219623566 -0.4153926372528076 L 0.3499999865889549 -0.42499999701976776 L 0.447545163333416 -0.4153926372528076 L 0.5249291881918907 -0.3999999985098839 L 10.175070807337761 -0.3999999985098839 L 10.252454832196236 -0.4153926372528076 L 10.349999986588955 -0.42499999701976776 L 10.447545163333416 -0.4153926372528076 L 10.447545163333416 -0.4153926372528076 Z" fill="url(#hatch-0.0050-45-true-_2300FFFF-0.1000-0.80__stack1)" fill-rule="nonzero" stroke="none"/><path d="M 10.244762055575848 0.09593313187360764 L 10.238675557076931 0.10000000149011612 L 0.46132444590330124 0.10000000149011612 L 0.4552379474043846 0.09593313187360764 L 0.3500000014901161 0.07500000298023224 L 0.24476205557584763 0.09593313187360764 L 0.15554563701152802 0.15554563701152802 L 0.09593313187360764 0.24476205557584763 L 0.07500000298023224 0.3500000014901161 L 0.09593313187360764 0.4552379474043846 L 0.15554563701152802 0.5444543659687042 L 0.24476205557584763 0.6040668711066246 L 0.3500000014901161 0.625 L 0.4552379474043846 0.6040668711066246 L 0.46132444590330124 0.6000000014901161 L 10.238675557076931 0.6000000014901161 L 10.244762055575848 0.6040668711066246 L 10.350000001490116 0.625 L 10.455237947404385 0.6040668711066246 L 10.544454365968704 0.5444543659687042 L 10.604066871106625 0.4552379474043846 L 10.625 0.3500000014901161 L 10.604066871106625 0.24476205557584763 L 10.544454365968704 0.15554563701152802 L 10.455237947404385 0.09593313187360764 L 10.350000001490116 0.07500000298023224 L 10.244762055575848 0.09593313187360764 L 10.244762055575848 0.09593313187360764 Z M 10.447545163333416 -0.4153926372528076 L 10.552783109247684 -0.3944595083594322 L 10.646579667925835 -0.36600664258003235 L 10.733023069798946 -0.3198016807436943 L 10.822239503264427 -0.26018916070461273 L 10.898007772862911 -0.1980077400803566 L 10.960189193487167 -0.12223946303129196 L 11.019801691174507 -0.033023037016391754 L 11.066006653010845 0.05342037230730057 L 11.094459511339664 0.14721688628196716 L 11.11539264023304 0.25245483219623566 L 11.125 0.3499999865889549 L 11.11539264023304 0.447545163333416 L 11.094459511339664 0.5527831092476845 L 11.066006638109684 0.6465796679258347 L 11.019801691174507 0.7330230474472046 L 10.960189193487167 0.8222394734621048 L 10.898007780313492 0.8980077430605888 L 10.822239473462105 0.9601891934871674 L 10.733023047447205 1.0198016911745071 L 10.646579638123512 1.0660066530108452 L 10.552783109247684 1.0944595113396645 L 10.447545163333416 1.1153926402330399 L 10.350000001490116 1.125 L 10.252454832196236 1.1153926402330399 L 10.175070807337761 1.1000000014901161 L 0.5249291881918907 1.1000000014901161 L 0.447545163333416 1.1153926402330399 L 0.3500000014901161 1.125 L 0.25245483219623566 1.1153926402330399 L 0.14721688628196716 1.0944595113396645 L 0.053420327603816986 1.0660066306591034 L -0.033023037016391754 1.0198016911745071 L -0.12223946303129196 0.9601891934871674 L -0.198007732629776 0.8980077803134918 L -0.26018916070461273 0.8222395032644272 L -0.3198016732931137 0.733023077249527 L -0.36600662767887115 0.6465796828269958 L -0.3944595083594322 0.5527831315994263 L -0.4153926372528076 0.447545163333416 L -0.42499999701976776 0.3500000014901161 L -0.4153926372528076 0.25245483219623566 L -0.3944595083594322 0.14721688628196716 L -0.36600662767887115 0.053420327603816986 L -0.3198016732931137 -0.03302306681871414 L -0.26018916070461273 -0.12223949283361435 L -0.1980077400803566 -0.1980077624320984 L -0.12223949283361435 -0.26018916070461273 L -0.03302306681871414 -0.3198016732931137 L 0.053420327603816986 -0.36600662767887115 L 0.14721687883138657 -0.3944595083594322 L 0.25245483219623566 -0.4153926372528076 L 0.3499999865889549 -0.42499999701976776 L 0.447545163333416 -0.4153926372528076 L 0.5249291881918907 -0.3999999985098839 L 10.175070807337761 -0.3999999985098839 L 10.252454832196236 -0.4153926372528076 L 10.349999986588955 -0.42499999701976776 L 10.447545163333416 -0.4153926372528076 L 10.447545163333416 -0.4153926372528076 Z" fill="none" fill-rule="nonzero" stroke="#00FFFF" stroke-width="0.1"/></g></g>
7
7
  </g>
8
8
  </svg>