203 lines
7.4 KiB
Python
203 lines
7.4 KiB
Python
import os
|
|
|
|
import FreeCAD as App
|
|
from . import settings
|
|
import Part
|
|
|
|
from kipy.board_types import Footprint3DModel, BoardArc, 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
|
|
from . import parts as Parts
|
|
from . import board_sketch as BoardSketch
|
|
|
|
class BoardObject(BaseObject):
|
|
TYPE = 'KiConnect::Board'
|
|
|
|
save_keys = [ 'polygon_id' ]
|
|
|
|
def __init__(self, feature, kicad_board, board_polygon):
|
|
self.feature = feature
|
|
self.substrate_body = None
|
|
self.board_sketch = None
|
|
|
|
self.kicad_board = kicad_board
|
|
|
|
# TODO needs to be resotred in onDocumentRestored
|
|
#self.board_polygon = board_polygon
|
|
self.polygon_id = board_polygon.id.value
|
|
|
|
self.via_sketch = None
|
|
|
|
super(BoardObject, self).__init__(feature)
|
|
|
|
self.feature.PolygonId = board_polygon.id.value
|
|
|
|
self.substrate_body = self.create_substrate_body()
|
|
self.board_sketch = BoardSketch.makeBoardSketch(self.substrate_body, kicad_board, board_polygon)
|
|
self.create_substrate_pad()
|
|
|
|
@property
|
|
def board_polygon(self):
|
|
return [ poly for poly in extract_polygons(self.kicad_board) if poly.id.value == self.polygon_id ][0]
|
|
|
|
def onDocumentRestored(self, feature):
|
|
super(BoardObject, self).onDocumentRestored(feature)
|
|
|
|
self.kicad_board = self.get_api().kicad.get_board()
|
|
self.board_sketch = self.feature.getObject('Substrate').getObject('BoardSketch')
|
|
|
|
def create_substrate_body(self):
|
|
substrate_body = App.ActiveDocument.addObject('PartDesign::Body', 'Substrate')
|
|
|
|
substrate_body.addProperty('App::PropertyString', 'Type', 'KiConnect', 'KiConnect specific Type', read_only=True, hidden=True)
|
|
substrate_body.Type = 'KiConnect::BoardBody'
|
|
|
|
self.feature.addObject(substrate_body)
|
|
|
|
return substrate_body
|
|
|
|
def create_substrate_pad(self):
|
|
pad = self.substrate_body.newObject('PartDesign::Pad', 'Outline')
|
|
|
|
pad.Profile = self.board_sketch
|
|
pad.Length = 1.6
|
|
pad.Midplane = True
|
|
|
|
return self.board_sketch
|
|
|
|
def get_polygon(self, kiid):
|
|
return [ s for s in self.kicad_board.get_shapes() if s.id.value == kiid ][0]
|
|
|
|
def get_boardpoly(self):
|
|
# TODO remove in favor of extract_polygons class method
|
|
board = self.kicad_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[0]
|
|
|
|
def pocket_vias(self):
|
|
board = self.kicad_board
|
|
|
|
self.via_sketch.Geometry = []
|
|
|
|
for via in board.get_vias():
|
|
stack = via.padstack
|
|
f_cu = stack.copper_layer(BoardLayer.BL_F_Cu)
|
|
|
|
self.via_sketch.addGeometry(Part.Circle((App.Vector(via.position.x, -via.position.y) / 1000000.0) - self.feature.BoardOffset.Base, App.Vector(0, 0, 1), via.drill_diameter / 1000000.0 / 2))
|
|
|
|
for pad in board.get_pads():
|
|
copper_layer = pad.padstack.copper_layer(BoardLayer.BL_F_Cu)
|
|
drill = pad.padstack.drill
|
|
|
|
if copper_layer.shape is not PadStackShape.PSS_CIRCLE: continue
|
|
|
|
center = (App.Vector(pad.position.x, -pad.position.y) / 1000000.0) - self.feature.BoardOffset.Base
|
|
|
|
if drill.diameter.x == drill.diameter.y:
|
|
rad = drill.diameter.x / 1000000.0 / 2
|
|
|
|
self.via_sketch.addGeometry(Part.Circle(center, App.Vector(0, 0, 1), rad))
|
|
|
|
def setup_vias(self):
|
|
self.via_sketch = App.ActiveDocument.addObject('Sketcher::SketchObject')
|
|
self.via_sketch.Visibility = False
|
|
self.substrate_body.addObject(self.via_sketch)
|
|
via_pocket = self.substrate_body.newObject('PartDesign::Pocket', 'Vias')
|
|
via_pocket.Profile = self.via_sketch
|
|
via_pocket.Midplane = True
|
|
via_pocket.Type = 1
|
|
|
|
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 sync_from(self):
|
|
'''
|
|
Pulls outline from KiCAD PolygonBoard and saves points as Vectors
|
|
'''
|
|
|
|
if self.board_sketch:
|
|
self.board_sketch.Proxy.sync_from()
|
|
|
|
def sync_to(self):
|
|
board = self.kicad_board
|
|
|
|
commit = board.begin_commit()
|
|
|
|
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
|
|
boardpoly = polygons[0]
|
|
poly = boardpoly.polygons[0]
|
|
poly.outline.clear()
|
|
|
|
geom = self.feature.getObject('Substrate').getObject('BoardSketch').Geometry
|
|
segments = [l for l in geom if isinstance(l, Part.LineSegment)]
|
|
|
|
for line in segments:
|
|
start = (line.StartPoint + self.feature.BoardOffset.Base) * 1000000
|
|
start.y = -start.y
|
|
|
|
poly.outline.append(PolyLineNode.from_point(Vector2.from_xy(int(start.x), int(start.y))))
|
|
|
|
board.update_items(boardpoly)
|
|
board.push_commit(commit, "Updated board outline")
|
|
|
|
# if this is done, probably need to clear selection at the start
|
|
# maybe save the selection and restore if not empty, else select the poly as below
|
|
#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
|
|
feature.Label2 = kicad_board.name
|
|
|
|
if not feature.Thickness:
|
|
feature.Thickness = 1.6
|
|
|
|
def __getstate__(self):
|
|
return None
|
|
|
|
class BoardViewProvider(BaseViewProvider):
|
|
TYPE = 'KiConnect::Board'
|
|
ICON = 'kicad/board.svg'
|
|
|
|
def makeBoard(parent, kicad_board, polygon):
|
|
feature = App.ActiveDocument.addObject('App::DocumentObjectGroupPython', 'Board')
|
|
parent.addObject(feature)
|
|
|
|
BoardObject(feature, kicad_board, polygon)
|
|
BoardViewProvider(feature.ViewObject)
|
|
|
|
Parts.makeParts(feature)
|
|
|
|
return feature, feature.Proxy.polygon_id
|
|
|
|
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) or isinstance(edge, BoardArc)) ]
|
|
|
|
return polygons
|
|
|