Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions lib/stages/pcb/CollectFootprintsStage/layer-utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Footprint } from "kicadts"
import type { LayerRef } from "circuit-json"
import type { LayerRef, PcbRenderLayer } from "circuit-json"

/**
* Determines the layer (top or bottom) of a component based on the footprint's layer information
Expand Down Expand Up @@ -29,17 +29,29 @@ export function determineLayerFromLayers(layers: any): LayerRef {
return "top"
}

export interface LayerMapping {
side: "top" | "bottom"
type: PcbRenderLayer | "unknown"
}

/**
* Maps KiCad text layer to Circuit JSON layer (top or bottom)
* Gets a detailed mapping for a KiCad layer
*/
export function mapTextLayer(kicadLayer: any): "top" | "bottom" {
// Handle both string and Layer object
export function getLayerMapping(kicadLayer: any): LayerMapping {
const layerStr =
typeof kicadLayer === "string"
? kicadLayer
: kicadLayer?.names?.join(" ") || ""
if (layerStr.includes("B.") || layerStr.includes("Back")) {
return "bottom"

const side: "top" | "bottom" =
layerStr.includes("B.") || layerStr.includes("Back") ? "bottom" : "top"

let type: PcbRenderLayer | "unknown" = "unknown"
if (layerStr.includes("Silk")) {
type = side === "top" ? "top_silkscreen" : "bottom_silkscreen"
} else if (layerStr.includes("Edge.Cuts")) {
type = "edge_cuts"
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO:
handling other layers

  if (layerStr.includes("Silk")) {
    type = side === "top" ? "top_silkscreen" : "bottom_silkscreen"
  } else if (layerStr.includes("Fab")) {
    type = side === "top" ? "top_fabrication_note" : "bottom_fabrication_note"
  } else if (layerStr.includes("CrtYd") || layerStr.includes("Courtyard")) {
    type = side === "top" ? "top_courtyard" : "bottom_courtyard"
  } else if (layerStr.includes(".Cu")) {
    type = side === "top" ? "top_copper" : "bottom_copper"
  } else if (layerStr.includes("Mask")) {
    type = side === "top" ? "top_soldermask" : "bottom_soldermask"
  } else if (layerStr.includes("Edge.Cuts")) {
    type = "edge_cuts"
  }

}
return "top"

return { side, type }
}
81 changes: 46 additions & 35 deletions lib/stages/pcb/CollectFootprintsStage/process-graphics.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Footprint, FpPoly, FpLine, FpCircle, FpArc } from "kicadts"
import { applyToPoint } from "transformation-matrix"
import type { ConverterContext } from "../../../types"
import { mapTextLayer } from "./layer-utils"
import { getLayerMapping } from "./layer-utils"

/**
* Rotates a point by a given angle (in degrees)
Expand Down Expand Up @@ -120,15 +120,17 @@ export function createFootprintLine(
const startPos = applyToPoint(ctx.k2cMatPcb, startKicadPos)
const endPos = applyToPoint(ctx.k2cMatPcb, endKicadPos)

const layer = mapTextLayer(line.layer)
const mapping = getLayerMapping(line.layer)
const strokeWidth = line.stroke?.width || line.width || 0.12

ctx.db.pcb_silkscreen_path.insert({
pcb_component_id: componentId,
layer: layer,
route: [startPos, endPos],
stroke_width: strokeWidth,
})
if (mapping.type.includes("silkscreen")) {
ctx.db.pcb_silkscreen_path.insert({
pcb_component_id: componentId,
layer: mapping.side,
route: [startPos, endPos],
stroke_width: strokeWidth,
})
}
}

/**
Expand Down Expand Up @@ -161,7 +163,7 @@ export function createFootprintCircle(
// Transform to Circuit JSON coordinates
const centerPos = applyToPoint(ctx.k2cMatPcb, centerKicadPos)

const layer = mapTextLayer(circle.layer)
const mapping = getLayerMapping(circle.layer)
const strokeWidth = circle.stroke?.width || circle.width || 0.12

// Create circle as a pcb_silkscreen_circle (if supported) or as a path with many points
Expand All @@ -175,12 +177,14 @@ export function createFootprintCircle(
circleRoute.push({ x, y })
}

ctx.db.pcb_silkscreen_path.insert({
pcb_component_id: componentId,
layer: layer,
route: circleRoute,
stroke_width: strokeWidth,
})
if (mapping.type.includes("silkscreen")) {
ctx.db.pcb_silkscreen_path.insert({
pcb_component_id: componentId,
layer: mapping.side,
route: circleRoute,
stroke_width: strokeWidth,
})
}
}

/**
Expand Down Expand Up @@ -249,7 +253,7 @@ export function createFootprintArc(
y: kicadComponentPos.y + rotatedEnd.y,
}

const layer = mapTextLayer(arc.layer)
const mapping = getLayerMapping(arc.layer)
const strokeWidth = arc.stroke?.width || arc.width || 0.12

// Calculate the arc center and radius IN KICAD SPACE (before coordinate transformation)
Expand All @@ -259,12 +263,15 @@ export function createFootprintArc(
// If points are collinear, fall back to straight line
const startPos = applyToPoint(ctx.k2cMatPcb, startKicadPos)
const endPos = applyToPoint(ctx.k2cMatPcb, endKicadPos)
ctx.db.pcb_silkscreen_path.insert({
pcb_component_id: componentId,
layer: layer,
route: [startPos, endPos],
stroke_width: strokeWidth,
})

if (mapping.type.includes("silkscreen")) {
ctx.db.pcb_silkscreen_path.insert({
pcb_component_id: componentId,
layer: mapping.side,
route: [startPos, endPos],
stroke_width: strokeWidth,
})
}
return
}

Expand Down Expand Up @@ -329,12 +336,14 @@ export function createFootprintArc(
arcRoute.push(cjPoint)
}

ctx.db.pcb_silkscreen_path.insert({
pcb_component_id: componentId,
layer: layer,
route: arcRoute,
stroke_width: strokeWidth,
})
if (mapping.type.includes("silkscreen")) {
ctx.db.pcb_silkscreen_path.insert({
pcb_component_id: componentId,
layer: mapping.side,
route: arcRoute,
stroke_width: strokeWidth,
})
}
}

export function createFootprintPoly(
Expand All @@ -351,7 +360,7 @@ export function createFootprintPoly(
if (ptArray.length === 0) return

// Extract layer
const layer = mapTextLayer(poly.layer)
const mapping = getLayerMapping(poly.layer)

// Extract stroke width
const strokeWidth = poly.stroke?.width || poly.width || 0.12
Expand All @@ -369,10 +378,12 @@ export function createFootprintPoly(
return applyToPoint(ctx.k2cMatPcb!, kicadPos)
})

ctx.db.pcb_silkscreen_path.insert({
pcb_component_id: componentId,
layer: layer,
route: transformedPts,
stroke_width: strokeWidth,
})
if (mapping.type.includes("silkscreen")) {
ctx.db.pcb_silkscreen_path.insert({
pcb_component_id: componentId,
layer: mapping.side,
route: transformedPts,
stroke_width: strokeWidth,
})
}
}
54 changes: 24 additions & 30 deletions lib/stages/pcb/CollectFootprintsStage/process-text.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Footprint } from "kicadts"
import { applyToPoint } from "transformation-matrix"
import type { ConverterContext } from "../../../types"
import { mapTextLayer } from "./layer-utils"
import { getLayerMapping } from "./layer-utils"
import { substituteKicadVariables } from "./text-utils"

/**
Expand Down Expand Up @@ -30,13 +30,9 @@ export function processFootprintText(
const textArray = Array.isArray(texts) ? texts : [texts]

for (const text of textArray) {
// Only process text on silkscreen layers (filter out F.Fab, etc.)
const layerStr =
typeof text.layer === "string"
? text.layer
: text.layer?.names?.join(" ") || ""
const isSilkscreen = layerStr.includes("SilkS") || layerStr.includes("Silk")
if (!isSilkscreen) continue
// Check if the layer is one we want to process
const mapping = getLayerMapping(text.layer)
if (!mapping.type.includes("silkscreen")) continue

// Create a properly structured text element with _sxPosition mapped to at
const textElement = {
Expand All @@ -47,7 +43,7 @@ export function processFootprintText(
_sxEffects: (text as any)._sxEffects, // Pass _sxEffects for font size access
}

createSilkscreenText(
createFootprintText(
ctx,
textElement,
componentId,
Expand Down Expand Up @@ -77,16 +73,11 @@ export function processFootprintProperties(
// Only process properties with a layer field
if (!property.layer) continue

// Check if the property is on a silkscreen layer
const layerStr =
typeof property.layer === "string"
? property.layer
: property.layer?.names?.join(" ") || ""
const isSilkscreen = layerStr.includes("SilkS") || layerStr.includes("Silk")
// Check if the property is on a supported layer
const mapping = getLayerMapping(property.layer)
if (!mapping.type.includes("silkscreen")) continue

if (!isSilkscreen) continue

// Create silkscreen text for this property
// Create footprint text for this property
// Property structure uses _sxAt for position (kicadts internal field)
const textElement = {
text: property.value,
Expand All @@ -96,7 +87,7 @@ export function processFootprintProperties(
_sxEffects: (property as any)._sxEffects, // Pass _sxEffects for font size access
}

createSilkscreenText(
createFootprintText(
ctx,
textElement,
componentId,
Expand All @@ -108,9 +99,9 @@ export function processFootprintProperties(
}

/**
* Creates a silkscreen text element in Circuit JSON
* Creates a text element in Circuit JSON (silkscreen or fabrication)
*/
export function createSilkscreenText(
export function createFootprintText(
ctx: ConverterContext,
text: any,
componentId: string,
Expand Down Expand Up @@ -138,7 +129,7 @@ export function createSilkscreenText(
}
const pos = applyToPoint(ctx.k2cMatPcb, textKicadPos)

const layer = mapTextLayer(text.layer)
const mapping = getLayerMapping(text.layer)

// Substitute KiCad variables in text
const processedText = substituteKicadVariables(text.text || "", footprint)
Expand All @@ -149,12 +140,15 @@ export function createSilkscreenText(
text.effects?.font?.size?.y ||
1

ctx.db.pcb_silkscreen_text.insert({
pcb_component_id: componentId,
font: "tscircuit2024",
font_size: kicadFontSize * 1.5,
text: processedText,
anchor_position: pos,
layer: layer,
} as any)
if (mapping.type.includes("silkscreen")) {
ctx.db.pcb_silkscreen_text.insert({
pcb_component_id: componentId,
font: "tscircuit2024",
font_size: kicadFontSize * 1.5,
text: processedText,
anchor_position: pos,
layer: mapping.side,
anchor_alignment: "center",
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO:

handling the anchor_alignment for pcb_silkscreen_text

})
}
}
Loading