Compare commits

...

6 commits

Author SHA1 Message Date
Morgan 'ARR\!' Allen
6a639f2c7f major rework of Sketch creation, now properly handles Arcs in addition to Points on a Polygon 2025-10-15 11:33:17 -07:00
Morgan 'ARR\!' Allen
35363fac75 fix description for Vectors associated with a sketch
XXX Verify this isn't actually being used and remove. After supporting Arcs this does not make sense, plus all geometry is available right in the sketch
2025-10-15 11:32:05 -07:00
Morgan 'ARR\!' Allen
a2d800849a typo in comment 2025-10-15 11:25:18 -07:00
Morgan 'ARR\!' Allen
22ccb608dc extract BoardArcs along with BoardPolygon 2025-10-15 11:17:31 -07:00
Morgan 'ARR\!' Allen
3984abc2f3 remove debug prints and raise actual Exception
this should be replaced with an actual typed exception
2025-10-15 11:11:58 -07:00
Morgan 'ARR\!' Allen
261b3e91e7 fixed more typos 2025-09-29 17:12:35 -07:00
4 changed files with 82 additions and 29 deletions

View file

@ -37,10 +37,10 @@ Hope to support this one of several ways, depending on the future of KiCAD/API
* KiCAD supports multi-document, API include `get_boards()` * KiCAD supports multi-document, API include `get_boards()`
* `kicad-cli` supports full API, spin up instances as needed * `kicad-cli` supports full API, spin up instances as needed
* Manually switch between KiCAD projects and import new boards * Manually switch between KiCAD projects and import new boards
* socker-per-project but this seems messy * socket-per-project but this seems messy
### Panelization ### Panelization
In it's most basic form, this will probably utilize `kikit` to assist in basic panelization but in multiboard projects it will assist in panelizing differnt boards into one panel. In it's most basic form, this will probably utilize `kikit` to assist in basic panelization but in multiboard projects it will assist in panelizing different boards into one panel.
### Syncronize all the things! ### Synchronize all the things!
Syncing selections is fairly straight forward, but long term it would be awesome to have everything sync as their changed. Syncing selections is fairly straight forward, but long term it would be awesome to have everything sync as their changed.

View file

@ -61,12 +61,11 @@ class APIObject(BaseObject):
if self.is_connected: if self.is_connected:
self.kicad_board = self.kicad.get_board() self.kicad_board = self.kicad.get_board()
polygons = Board.extract_polygons(self.kicad_board) polygons = Board.extract_polygons(self.kicad_board)
print('>', polygons)
for poly in polygons: for poly in polygons:
self.polygons[poly.id.value] = poly self.polygons[poly.id.value] = poly
else: else:
print('*****NOT CONNECTED') raise Exception('KiCAD API not connected.')
@property @property
def is_connected(self): def is_connected(self):
@ -77,7 +76,6 @@ class APIObject(BaseObject):
return self.feature.Connected return self.feature.Connected
def get_polygon(self, polygon_id): def get_polygon(self, polygon_id):
print(polygon_id, self.polygons)
self.refresh_polygons() self.refresh_polygons()
return self.polygons.get(polygon_id) return self.polygons.get(polygon_id)

View file

@ -4,7 +4,7 @@ import FreeCAD as App
from . import settings from . import settings
import Part import Part
from kipy.board_types import Footprint3DModel, BoardPolygon, BoardSegment, PadStackShape from kipy.board_types import Footprint3DModel, BoardArc, BoardPolygon, BoardSegment, PadStackShape
from kipy.geometry import PolygonWithHoles, PolyLine, PolyLineNode, Vector2 from kipy.geometry import PolygonWithHoles, PolyLine, PolyLineNode, Vector2
from kipy.proto.common.types import KiCadObjectType from kipy.proto.common.types import KiCadObjectType
from kipy.util.board_layer import BoardLayer from kipy.util.board_layer import BoardLayer
@ -25,7 +25,7 @@ class BoardObject(BaseObject):
self.kicad_board = kicad_board self.kicad_board = kicad_board
# TODO needs to be resotred in onDocumentRestored # TODO needs to be restored in onDocumentRestored
#self.board_polygon = board_polygon #self.board_polygon = board_polygon
self.polygon_id = board_polygon.id.value self.polygon_id = board_polygon.id.value
@ -197,8 +197,7 @@ def makeBoard(parent, kicad_board, polygon):
def extract_polygons(board): def extract_polygons(board):
# find polygons of Edge Cuts # find polygons of Edge Cuts
edge_cuts = [ edge for edge in board.get_shapes() if edge.layer == BoardLayer.BL_Edge_Cuts ] edge_cuts = [ edge for edge in board.get_shapes() if edge.layer == BoardLayer.BL_Edge_Cuts ]
polygons = [ edge for edge in edge_cuts if isinstance(edge, BoardPolygon) ] polygons = [ edge for edge in edge_cuts if (isinstance(edge, BoardPolygon) or isinstance(edge, BoardArc)) ]
# XXX only single board supported at the moment
return polygons return polygons

View file

@ -1,8 +1,9 @@
import os import os
import FreeCAD as App import FreeCAD as App
from . import settings from . import settings
import Part import Part
import Sketcher
from kipy.board_types import Footprint3DModel, BoardPolygon, BoardSegment, PadStackShape from kipy.board_types import Footprint3DModel, BoardPolygon, BoardSegment, PadStackShape
from kipy.geometry import PolygonWithHoles, PolyLine, PolyLineNode, Vector2 from kipy.geometry import PolygonWithHoles, PolyLine, PolyLineNode, Vector2
@ -39,49 +40,104 @@ class BoardSketchObject(BaseObject):
def setup_properties(self, feature): def setup_properties(self, feature):
super(BoardSketchObject, self).setup_properties(feature) super(BoardSketchObject, self).setup_properties(feature)
# TODO remove in favor of getting BoardOffset from parent Board object
feature.addProperty('App::PropertyPlacement', 'BoardOffset', 'KiConnect', 'Internal offset for zeroing out Footprint offset', hidden=True, read_only=True) feature.addProperty('App::PropertyPlacement', 'BoardOffset', 'KiConnect', 'Internal offset for zeroing out Footprint offset', hidden=True, read_only=True)
feature.addProperty('App::PropertyVectorList', 'Vectors', 'KiConnect', 'Internal offset for zeroing out Footprint offset', hidden=True) feature.addProperty('App::PropertyVectorList', 'Vectors', 'KiConnect', 'Board Outline as Vectors', hidden=True)
def sync_from(self): def sync_from(self):
feature = self.feature feature = self.feature
vectors = [] vectors = []
# board.get_shapes needs to be called to ensure polygons are actually up to date # board.get_shapes needs to be called to ensure polygons are actually up to date
# XXX get_polygon should be moved to the Sketches parent Board object
# then the board should be making the api refresh board call and process
# the polygon, instead of that functionality being in the API
board_polygon = self.get_api().get_polygon(self.polygon_id) board_polygon = self.get_api().get_polygon(self.polygon_id)
if not board_polygon: if not board_polygon:
raise BoardPolyNotFoundException('Board Polygon not found: ' + self.polygon_id) raise BoardPolyNotFoundException('Board Polygon not found: ' + self.polygon_id)
for node in board_polygon.polygons[0].outline: arc_count = 0
vectors.append(self.point_to_vector(node.point))
self.feature.Vectors = vectors
begin = None
start = None start = None
# this probably needs a bit more control.. outline = board_polygon.polygons[0].outline
feature.Constraints = [] line = []
feature.Geometry = []
for vector in self.feature.Vectors: for node in outline:
if not start: if node.has_arc:
start = vector arc_count = arc_count + 1
begin = vector
continue if arc_count % 2 == 0:
continue
feature.addGeometry(Part.LineSegment(start, vector)) arc = node.arc
start = vector
feature.addGeometry(Part.LineSegment(start, begin))
arc_start = self.point_to_vector(arc.start)
arc_mid = self.point_to_vector(arc.mid)
arc_end = self.point_to_vector(arc.end)
if not start:
start = arc_start
# if there is already part of a LineSegment, connect it to the start of this Arc
if len(line) == 1:
idx = feature.addGeometry(Part.LineSegment(line[0], arc_start))
feature.addConstraint(Sketcher.Constraint("Coincident", idx - 1, 2, idx, 1))
if line[0].x == arc_start.x:
feature.addConstraint(Sketcher.Constraint("Vertical", idx))
if line[0].y == arc_start.y:
feature.addConstraint(Sketcher.Constraint("Horizontal", idx))
line = []
idx = feature.addGeometry(Part.Arc(arc_start, arc_mid, arc_end))
if idx > 1:
feature.addConstraint(Sketcher.Constraint("Coincident", idx - 1, 2, idx, 1))
line.append(arc_end)
elif node.has_point:
vector = self.point_to_vector(node.point)
if not start:
start = vector
line.append(vector)
else:
raise Exception('Hit unknown geometry type in board polygon')
if len(line) > 2: raise Exception('Too many points in line segment', line)
if len(line) == 2:
idx = feature.addGeometry(Part.LineSegment(line[0], line[1]))
feature.addConstraint(Sketcher.Constraint("Coincident", idx - 1, 2, idx, 1))
if line[0].x == line[1].x:
feature.addConstraint(Sketcher.Constraint("Vertical", idx))
if line[0].y == line[1].y:
feature.addConstraint(Sketcher.Constraint("Horizontal", idx))
line = [ line[1] ]
if len(line) == 1:
idx = feature.addGeometry(Part.LineSegment(start, line[0]))
feature.addConstraint(Sketcher.Constraint("Coincident", 0, 1, idx, 1))
feature.addConstraint(Sketcher.Constraint("Coincident", idx - 1, 2, idx, 2))
else:
FreeCAD.Console.PrintError("Bad line segment:", line)
feature.recompute() feature.recompute()
class BoardSketchViewProvider(BaseViewProvider): class BoardSketchViewProvider(BaseViewProvider):
TYPE = 'KiConnect::BoardSketch' TYPE = 'KiConnect::BoardSketch'
#ICON = 'kicad/board.svg' #ICON = 'kicad/board.svg'
def makeBoardSketch(parent, kicad_board, polygon): def makeBoardSketch(parent, kicad_board, polygon):
feature = App.ActiveDocument.addObject('Sketcher::SketchObjectPython', 'BoardSketch') feature = App.ActiveDocument.addObject('Sketcher::SketchObjectPython', 'BoardSketch')
parent.addObject(feature) parent.addObject(feature)