Compare commits
11 commits
c1194ecee0
...
984cb42b1c
Author | SHA1 | Date | |
---|---|---|---|
|
984cb42b1c | ||
|
e1a0301e35 | ||
|
0e5f275b97 | ||
|
3a40276260 | ||
|
da98f7ba01 | ||
|
961b0b44f3 | ||
|
6d6ccadccf | ||
|
ac84562e1a | ||
|
a5dbdee3c8 | ||
|
5b73f2837a | ||
|
c4e8e79395 |
6 changed files with 168 additions and 105 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
__pycache__
|
|
@ -11,6 +11,8 @@ class APIObject(BaseObject):
|
||||||
TYPE = 'KiConnect::API'
|
TYPE = 'KiConnect::API'
|
||||||
|
|
||||||
def __init__(self, feature):
|
def __init__(self, feature):
|
||||||
|
self.kicad = KiCad()
|
||||||
|
|
||||||
super(APIObject, self).__init__(feature)
|
super(APIObject, self).__init__(feature)
|
||||||
|
|
||||||
feature.addProperty('App::PropertyFile', 'Socket', 'KiConnect', 'Path to the KiCAD Socket File').Socket = '/tmp/kicad/api.lock'
|
feature.addProperty('App::PropertyFile', 'Socket', 'KiConnect', 'Path to the KiCAD Socket File').Socket = '/tmp/kicad/api.lock'
|
||||||
|
@ -19,6 +21,8 @@ class APIObject(BaseObject):
|
||||||
|
|
||||||
self.onDocumentRestored(feature)
|
self.onDocumentRestored(feature)
|
||||||
|
|
||||||
|
self.ping_connection(feature)
|
||||||
|
|
||||||
def onDocumentRestored(self, feature):
|
def onDocumentRestored(self, feature):
|
||||||
super(APIObject, self).onDocumentRestored(feature)
|
super(APIObject, self).onDocumentRestored(feature)
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ class BaseObject:
|
||||||
|
|
||||||
feature.Type = self.TYPE
|
feature.Type = self.TYPE
|
||||||
|
|
||||||
|
self.sync_from()
|
||||||
|
|
||||||
def execute(self, feature):
|
def execute(self, feature):
|
||||||
# TODO this might not be the right move
|
# TODO this might not be the right move
|
||||||
print(self, 'BaseObject.execute')
|
print(self, 'BaseObject.execute')
|
||||||
|
@ -52,7 +54,7 @@ class BaseObject:
|
||||||
self.feature.addExtension(ext)
|
self.feature.addExtension(ext)
|
||||||
|
|
||||||
def setup_properties(self, feature):
|
def setup_properties(self, feature):
|
||||||
feature.addProperty('App::PropertyString', 'Type', 'KiConnect', 'Internatl KiConnect Type', read_only=True, hidden=True)
|
feature.addProperty('App::PropertyString', 'Type', 'KiConnect', 'Internal KiConnect Type', read_only=True, hidden=True)
|
||||||
|
|
||||||
def sync_from(self):
|
def sync_from(self):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -10,45 +10,48 @@ from kipy.proto.common.types import KiCadObjectType
|
||||||
from kipy.util.board_layer import BoardLayer
|
from kipy.util.board_layer import BoardLayer
|
||||||
|
|
||||||
from .bases import BaseObject, BaseViewProvider
|
from .bases import BaseObject, BaseViewProvider
|
||||||
from . import parts
|
from . import parts as Parts
|
||||||
|
from . import board_sketch as BoardSketch
|
||||||
|
|
||||||
class BoardObject(BaseObject):
|
class BoardObject(BaseObject):
|
||||||
TYPE = 'KiConnect::Board'
|
TYPE = 'KiConnect::Board'
|
||||||
|
|
||||||
def __init__(self, feature):
|
def __init__(self, feature, kicad_board, board_polygon):
|
||||||
super(BoardObject, self).__init__(feature)
|
self.feature = feature
|
||||||
|
self.kicad_board = kicad_board
|
||||||
|
# TODO add this to FreeCAD Preferences and Property?
|
||||||
|
self.do_offset = True
|
||||||
|
# TODO needs to be resotred in onDocumentRestored
|
||||||
|
#self.board_polygon = board_polygon
|
||||||
|
self.polygon_id = board_polygon.id.value
|
||||||
|
|
||||||
self.kicad_board = None
|
|
||||||
self.via_sketch = None
|
self.via_sketch = None
|
||||||
|
|
||||||
feature.addProperty('App::PropertyPlacement', 'BoardOffset', 'KiConnect', 'Internal offset for zeroing out Footprint offset', hidden=True, read_only=True)
|
super(BoardObject, self).__init__(feature)
|
||||||
|
|
||||||
|
self.feature.PolygonId = board_polygon.id.value
|
||||||
|
|
||||||
def execute(self, feature):
|
self.board_sketch = BoardSketch.makeBoardSketch(self.substrate_body, kicad_board, board_polygon)
|
||||||
super(BoardObject, self).execute(feature)
|
self.create_substrate_pad()
|
||||||
|
|
||||||
if self.kicad_board is None:
|
@property
|
||||||
self.kicad_board = self.get_api().kicad.get_board()
|
def board_polygon(self):
|
||||||
|
return [ poly for poly in extract_polygons(self.kicad_board) if poly.id.value == self.polygon_id ][0]
|
||||||
if not self.substrate_body:
|
|
||||||
self.create_substrate_body()
|
|
||||||
|
|
||||||
if not self.substrate_sketch:
|
|
||||||
self.create_substrate_sketch()
|
|
||||||
self.sketch_outline()
|
|
||||||
|
|
||||||
def onDocumentRestored(self, feature):
|
def onDocumentRestored(self, feature):
|
||||||
super(BoardObject, self).onDocumentRestored(feature)
|
super(BoardObject, self).onDocumentRestored(feature)
|
||||||
|
|
||||||
|
self.board_sketch = self.feature.get('BoardSketch')
|
||||||
self.kicad_board = self.get_api().kicad.get_board()
|
self.kicad_board = self.get_api().kicad.get_board()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def substrate_body(self):
|
def substrate_body(self):
|
||||||
return self.feature.getObject('Substrate')
|
body = self.feature.getObject('Substrate')
|
||||||
|
|
||||||
@property
|
if not body:
|
||||||
def substrate_sketch(self):
|
body = self.create_substrate_body()
|
||||||
return self.substrate_body.getObject('Sketch')
|
|
||||||
|
return body
|
||||||
|
|
||||||
def create_substrate_body(self):
|
def create_substrate_body(self):
|
||||||
substrate_body = App.ActiveDocument.addObject('PartDesign::Body', 'Substrate')
|
substrate_body = App.ActiveDocument.addObject('PartDesign::Body', 'Substrate')
|
||||||
|
@ -58,35 +61,19 @@ class BoardObject(BaseObject):
|
||||||
|
|
||||||
self.feature.addObject(substrate_body)
|
self.feature.addObject(substrate_body)
|
||||||
|
|
||||||
def create_substrate_sketch(self):
|
return substrate_body
|
||||||
substrate_sketch = App.ActiveDocument.addObject('Sketcher::SketchObject', 'Sketch')
|
|
||||||
substrate_sketch.Visibility = False
|
|
||||||
|
|
||||||
self.substrate_body.addObject(substrate_sketch)
|
|
||||||
|
|
||||||
|
def create_substrate_pad(self):
|
||||||
pad = self.substrate_body.newObject('PartDesign::Pad', 'Outline')
|
pad = self.substrate_body.newObject('PartDesign::Pad', 'Outline')
|
||||||
|
|
||||||
pad.Profile = substrate_sketch
|
pad.Profile = self.board_sketch
|
||||||
pad.Length = 1.6
|
pad.Length = 1.6
|
||||||
pad.Midplane = True
|
pad.Midplane = True
|
||||||
|
|
||||||
|
return self.board_sketch
|
||||||
def extrude_substrate(self, feature):
|
|
||||||
self.substrate_sketch = App.ActiveDocument.addObject('Sketcher::SketchObject', 'Sketch')
|
|
||||||
self.substrate_sketch.Visibility = False
|
|
||||||
|
|
||||||
self.substrate_body = App.ActiveDocument.addObject('PartDesign::Body', 'Substrate')
|
|
||||||
self.feature.addObject(self.substrate_body)
|
|
||||||
|
|
||||||
pad = self.substrate_body.newObject('PartDesign::Pad', 'Outline')
|
|
||||||
|
|
||||||
pad.Profile = self.substrate_sketch
|
|
||||||
pad.Length = 1.6
|
|
||||||
pad.Midplane = True
|
|
||||||
|
|
||||||
self.substrate_body.addObject(self.substrate_sketch)
|
|
||||||
|
|
||||||
def get_boardpoly(self):
|
def get_boardpoly(self):
|
||||||
|
# TODO remove in favor of extract_polygons class method
|
||||||
board = self.kicad_board
|
board = self.kicad_board
|
||||||
|
|
||||||
# find polygons of Edge Cuts
|
# find polygons of Edge Cuts
|
||||||
|
@ -129,59 +116,29 @@ class BoardObject(BaseObject):
|
||||||
via_pocket.Midplane = True
|
via_pocket.Midplane = True
|
||||||
via_pocket.Type = 1
|
via_pocket.Type = 1
|
||||||
|
|
||||||
def sketch_outline(self, do_offset=True):
|
def point_to_vector(self, point, offset=True):
|
||||||
'''
|
return (
|
||||||
Draws the Board outline fetched from the API
|
App.Vector(point.x,
|
||||||
|
-point.y
|
||||||
|
)) / 1000000.0 - (self.feature.BoardOffset.Base if offset else 0)
|
||||||
|
|
||||||
Parameters:
|
|
||||||
do_offset (bool): If offset should be recalcualted, typically this is undesired after calculated the first time. (Default: True)
|
|
||||||
'''
|
|
||||||
|
|
||||||
boardpoly = self.get_boardpoly()
|
|
||||||
poly = boardpoly.polygons[0]
|
|
||||||
|
|
||||||
# track KIID
|
|
||||||
# TODO FeaturePython objects interacting with kipy objects should inherit common parent class
|
|
||||||
self.feature.PolygonId = boardpoly.id.value
|
|
||||||
|
|
||||||
# this offset centers the board to 0,0
|
|
||||||
if do_offset:
|
|
||||||
bb = boardpoly.bounding_box()
|
|
||||||
self.feature.BoardOffset.Base = (App.Vector(bb.pos.x, -bb.pos.y) + App.Vector(bb.size.x, -bb.size.y) / 2) / 1000000.0
|
|
||||||
|
|
||||||
begin = None
|
|
||||||
start = None
|
|
||||||
|
|
||||||
# reset Sketch Constraints and Geometry
|
|
||||||
self.substrate_sketch.Constraints = []
|
|
||||||
self.substrate_sketch.Geometry = []
|
|
||||||
|
|
||||||
# sketch outline
|
|
||||||
for segment in poly.outline:
|
|
||||||
if not start:
|
|
||||||
start = (App.Vector(segment.point.x, -segment.point.y)) / 1000000.0 - self.feature.BoardOffset.Base
|
|
||||||
|
|
||||||
# needs to remain to connect the last segment
|
|
||||||
begin = start
|
|
||||||
|
|
||||||
continue
|
|
||||||
|
|
||||||
end = (App.Vector(segment.point.x, -segment.point.y)) / 1000000.0 - self.feature.BoardOffset.Base
|
|
||||||
|
|
||||||
self.substrate_sketch.addGeometry(
|
|
||||||
Part.LineSegment(start, end)
|
|
||||||
)
|
|
||||||
|
|
||||||
start = end
|
|
||||||
|
|
||||||
# make final connection
|
|
||||||
self.substrate_sketch.addGeometry(
|
|
||||||
Part.LineSegment(start, begin)
|
|
||||||
)
|
|
||||||
|
|
||||||
def sync_from(self):
|
def sync_from(self):
|
||||||
self.sketch_outline()
|
'''
|
||||||
self.substrate_body.recompute(True)
|
Pulls outline from KiCAD PolygonBoard and saves points as Vectors
|
||||||
|
'''
|
||||||
|
|
||||||
|
# bit of a quick hack to keep the board in one place, needs more testing
|
||||||
|
if self.do_offset and not self.feature.BoardOffset:
|
||||||
|
bb = self.board_polygon.bounding_box()
|
||||||
|
self.feature.BoardOffset.Base = (App.Vector(bb.pos.x, -bb.pos.y) + App.Vector(bb.size.x, -bb.size.y) / 2) / 1000000.0
|
||||||
|
|
||||||
|
vectors = []
|
||||||
|
|
||||||
|
for node in self.board_polygon.polygons[0].outline:
|
||||||
|
vectors.append(self.point_to_vector(node.point))
|
||||||
|
|
||||||
|
self.feature.Vectors = vectors
|
||||||
|
|
||||||
def sync_to(self):
|
def sync_to(self):
|
||||||
board = self.kicad_board
|
board = self.kicad_board
|
||||||
|
@ -212,6 +169,22 @@ class BoardObject(BaseObject):
|
||||||
# maybe save the selection and restore if not empty, else select the poly as below
|
# maybe save the selection and restore if not empty, else select the poly as below
|
||||||
#board.add_to_selection(boardpoly)
|
#board.add_to_selection(boardpoly)
|
||||||
|
|
||||||
|
def setup_properties(self, feature):
|
||||||
|
super(BoardObject, self).setup_properties(feature)
|
||||||
|
|
||||||
|
kicad_board = self.kicad_board
|
||||||
|
|
||||||
|
feature.addProperty('App::PropertyPlacement', 'BoardOffset', 'KiConnect', 'Internal offset for zeroing out Footprint offset', hidden=True, read_only=True)
|
||||||
|
feature.addProperty('App::PropertyString', 'Doc', 'KiConnect', 'Doc in project to sync with', read_only=True)
|
||||||
|
feature.addProperty('App::PropertyString', 'PolygonId', 'KiConnect', 'Polygon ID for the original outline', hidden=True, read_only=True)
|
||||||
|
feature.addProperty('App::PropertyLength', 'Thickness', 'KiConnect', 'Thickness of PCB', hidden=True, read_only=True)
|
||||||
|
feature.addProperty('App::PropertyVectorList', 'Vectors', 'KiConnect', 'Internal offset for zeroing out Footprint offset', hidden=True)
|
||||||
|
|
||||||
|
feature.Doc = kicad_board.name
|
||||||
|
|
||||||
|
if not feature.Thickness:
|
||||||
|
feature.Thickness = 1.6
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -219,18 +192,22 @@ class BoardViewProvider(BaseViewProvider):
|
||||||
TYPE = 'KiConnect::Board'
|
TYPE = 'KiConnect::Board'
|
||||||
ICON = 'board.svg'
|
ICON = 'board.svg'
|
||||||
|
|
||||||
class Board:
|
def makeBoard(parent, kicad_board, polygon):
|
||||||
def __init__(self, kicad_board, parent):
|
feature = App.ActiveDocument.addObject('App::DocumentObjectGroupPython', 'Board')
|
||||||
feature = App.ActiveDocument.addObject('App::DocumentObjectGroupPython', 'Board')
|
parent.addObject(feature)
|
||||||
parent.addObject(feature)
|
|
||||||
self.feature = feature
|
|
||||||
|
|
||||||
BoardObject(feature)
|
BoardObject(feature, kicad_board, polygon)
|
||||||
BoardViewProvider(feature.ViewObject)
|
BoardViewProvider(feature.ViewObject)
|
||||||
|
|
||||||
# TODO move into BoardObject
|
Parts.makeParts(feature)
|
||||||
feature.addProperty('App::PropertyString', 'Doc', 'KiConnect', 'Doc in project to sync with', read_only=True).Doc = kicad_board.name
|
|
||||||
feature.addProperty('App::PropertyString', 'PolygonId', 'KiConnect', 'Polygon ID for the original outline', hidden=True, read_only=True)
|
|
||||||
|
|
||||||
parts.makeParts(feature)
|
return feature
|
||||||
|
|
||||||
|
def extract_polygons(board):
|
||||||
|
# find polygons of 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) ]
|
||||||
|
|
||||||
|
# XXX only single board supported at the moment
|
||||||
|
return polygons
|
||||||
|
|
||||||
|
|
77
freecad/kiconnect/board_sketch.py
Normal file
77
freecad/kiconnect/board_sketch.py
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
import FreeCAD as App
|
||||||
|
from . import settings
|
||||||
|
import Part
|
||||||
|
|
||||||
|
from kipy.board_types import Footprint3DModel, BoardPolygon, BoardSegment, PadStackShape
|
||||||
|
from kipy.geometry import PolygonWithHoles, PolyLine, PolyLineNode, Vector2
|
||||||
|
from kipy.proto.common.types import KiCadObjectType
|
||||||
|
from kipy.util.board_layer import BoardLayer
|
||||||
|
|
||||||
|
from .bases import BaseObject, BaseViewProvider
|
||||||
|
|
||||||
|
class BoardSketchObject(BaseObject):
|
||||||
|
TYPE = 'KiConnect::BoardSketch'
|
||||||
|
|
||||||
|
def __init__(self, feature, kicad_board, board_polygon):
|
||||||
|
super(BoardSketchObject, self).__init__(feature)
|
||||||
|
|
||||||
|
#feature.Visibility = False
|
||||||
|
|
||||||
|
vectors = []
|
||||||
|
|
||||||
|
for node in board_polygon.polygons[0].outline:
|
||||||
|
vectors.append(self.point_to_vector(node.point))
|
||||||
|
|
||||||
|
self.feature.Vectors = vectors
|
||||||
|
|
||||||
|
def execute(self, feature):
|
||||||
|
begin = None
|
||||||
|
start = None
|
||||||
|
|
||||||
|
# this probably needs a bit more control..
|
||||||
|
feature.Constraints = []
|
||||||
|
feature.Geometry = []
|
||||||
|
|
||||||
|
for vector in self.feature.Vectors:
|
||||||
|
if not start:
|
||||||
|
start = vector
|
||||||
|
begin = vector
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
feature.addGeometry(Part.LineSegment(start, vector))
|
||||||
|
start = vector
|
||||||
|
|
||||||
|
feature.addGeometry(Part.LineSegment(start, begin))
|
||||||
|
|
||||||
|
feature.recompute()
|
||||||
|
|
||||||
|
def point_to_vector(self, point, offset=True):
|
||||||
|
return (
|
||||||
|
App.Vector(point.x,
|
||||||
|
-point.y
|
||||||
|
)) / 1000000.0 - (self.feature.BoardOffset.Base if offset else 0)
|
||||||
|
|
||||||
|
def setup_properties(self, feature):
|
||||||
|
super(BoardSketchObject, self).setup_properties(feature)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
def __getstate__(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
class BoardSketchViewProvider(BaseViewProvider):
|
||||||
|
TYPE = 'KiConnect::BoardSketch'
|
||||||
|
#ICON = 'board.svg'
|
||||||
|
|
||||||
|
def makeBoardSketch(parent, kicad_board, polygon):
|
||||||
|
feature = App.ActiveDocument.addObject('Sketcher::SketchObjectPython', 'BoardSketch')
|
||||||
|
parent.addObject(feature)
|
||||||
|
|
||||||
|
BoardSketchObject(feature, kicad_board, polygon)
|
||||||
|
BoardSketchViewProvider(feature.ViewObject)
|
||||||
|
|
||||||
|
return feature
|
|
@ -9,7 +9,7 @@ from . import settings
|
||||||
|
|
||||||
from . import api
|
from . import api
|
||||||
from .copper import Copper
|
from .copper import Copper
|
||||||
from .board import Board
|
from . import board as Board
|
||||||
#from .parts import Parts
|
#from .parts import Parts
|
||||||
|
|
||||||
class Project:
|
class Project:
|
||||||
|
@ -31,7 +31,9 @@ class Project:
|
||||||
if self.API.Proxy.is_connected and self.API.DocumentCount > 0:
|
if self.API.Proxy.is_connected and self.API.DocumentCount > 0:
|
||||||
kicad_board = self.API.Proxy.kicad.get_board()
|
kicad_board = self.API.Proxy.kicad.get_board()
|
||||||
|
|
||||||
self.board = Board(kicad_board, self.feature)
|
polygons = Board.extract_polygons(kicad_board)
|
||||||
|
for polygon in polygons:
|
||||||
|
self.board = Board.makeBoard(self.feature, kicad_board, polygon)
|
||||||
|
|
||||||
#self.copper = Copper(kicad_board, self.board)
|
#self.copper = Copper(kicad_board, self.board)
|
||||||
#self.board.feature.addObject(self.copper.feature)
|
#self.board.feature.addObject(self.copper.feature)
|
||||||
|
|
Loading…
Add table
Reference in a new issue