missing board.py! (and icon)
This commit is contained in:
parent
f51bbad9b1
commit
cd21370c90
2 changed files with 418 additions and 0 deletions
189
freecad/kiconnect/board.py
Normal file
189
freecad/kiconnect/board.py
Normal file
|
@ -0,0 +1,189 @@
|
|||
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
|
||||
from . import parts
|
||||
|
||||
class BoardObject(BaseObject):
|
||||
TYPE = 'KiConnect::Board'
|
||||
|
||||
def __init__(self, feature):
|
||||
super(BoardObject, self).__init__(feature)
|
||||
|
||||
self.kicad_board = None
|
||||
self.substrate_body = None
|
||||
self.substrate_sketch = None
|
||||
self.via_sketch = None
|
||||
|
||||
feature.addProperty('App::PropertyPlacement', 'BoardOffset', 'KiConnect', 'Internal offset for zeroing out Footprint offset', hidden=True, read_only=True)
|
||||
|
||||
def onDocumentRestored(self, feature):
|
||||
if self.kicad_board is None:
|
||||
self.kicad_board = self.get_api().kicad.get_board()
|
||||
|
||||
if self.substrate_body is None:
|
||||
self.extrude_substrate(feature)
|
||||
self.sketch_outline(feature)
|
||||
|
||||
if len(self.kicad_board.get_vias()) > 0 and self.via_sketch is None:
|
||||
self.setup_vias()
|
||||
self.pocket_vias()
|
||||
|
||||
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):
|
||||
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 sketch_outline(self, feature):
|
||||
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
|
||||
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 Geometry
|
||||
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(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('Sketch').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 __getstate__(self):
|
||||
return None
|
||||
|
||||
class BoardViewProvider(BaseViewProvider):
|
||||
TYPE = 'KiConnect::Board'
|
||||
ICON = 'board.svg'
|
||||
|
||||
class Board:
|
||||
def __init__(self, kicad_board, parent):
|
||||
feature = App.ActiveDocument.addObject('App::DocumentObjectGroupPython', 'Board')
|
||||
parent.addObject(feature)
|
||||
self.feature = feature
|
||||
|
||||
BoardObject(feature)
|
||||
BoardViewProvider(feature.ViewObject)
|
||||
|
||||
# TODO move into BoardObject
|
||||
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)
|
||||
|
229
freecad/kiconnect/resources/icons/board.svg
Normal file
229
freecad/kiconnect/resources/icons/board.svg
Normal file
|
@ -0,0 +1,229 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
id="Слой_1"
|
||||
data-name="Слой 1"
|
||||
viewBox="0 0 24 24"
|
||||
version="1.1"
|
||||
sodipodi:docname="board.svg"
|
||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
||||
inkscape:export-filename="/Users/jeff/kicad_dev/kicad/bitmaps_png/png_26/import_brd_file.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1368"
|
||||
id="namedview30"
|
||||
showgrid="true"
|
||||
inkscape:zoom="21.709691"
|
||||
inkscape:cx="6.0802339"
|
||||
inkscape:cy="15.177554"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:document-rotation="0"
|
||||
inkscape:current-layer="Слой_1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid_kicad"
|
||||
spacingx="0.5"
|
||||
spacingy="0.5"
|
||||
color="#9999ff"
|
||||
opacity="0.13"
|
||||
empspacing="2"
|
||||
originx="0"
|
||||
originy="0"
|
||||
units="px" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata43">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>import_brd_file</dc:title>
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs25341">
|
||||
<style
|
||||
id="style25339">.cls-1{fill:#8f8f8f;}.cls-2{fill:#DED3DD;}.cls-3,.cls-6{fill:none;}.cls-3{stroke:#8f8f8f;stroke-miterlimit:10;stroke-width:0.9551px;}.cls-4{fill:#42B8EB;}.cls-5{fill:#f2647e;}.cls-6{stroke:#545454;stroke-linecap:round;stroke-linejoin:round;stroke-width:2px;}</style>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath884">
|
||||
<rect
|
||||
style="display:inline;fill:#f29100;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect886"
|
||||
width="21"
|
||||
height="21"
|
||||
x="-33"
|
||||
y="-2"
|
||||
ry="3.4854646"
|
||||
rx="3.4854646" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<title
|
||||
id="title25343">import_brd_file</title>
|
||||
<g
|
||||
id="g952"
|
||||
clip-path="url(#clipPath884)"
|
||||
transform="translate(33.00181,1.9988756)"
|
||||
inkscape:label="g952">
|
||||
<rect
|
||||
class="cls-1"
|
||||
x="1.4020385e-07"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
rx="0"
|
||||
id="rect9"
|
||||
style="fill:#489648;fill-opacity:1;stroke:none;stroke-width:0.571427;stroke-opacity:1"
|
||||
clip-path="none"
|
||||
transform="matrix(0.875,0,0,0.875,-33,-2)" />
|
||||
<path
|
||||
class="cls-2"
|
||||
d="m 9.7142857,24 v -1.714286 l 4.0000003,-4 V 12.571429 L 14.285714,12 h 4 l 2.285715,1.714286 H 24 V 4.5714286 H 20.571429 L 18.285714,6.2857143 H 16.571429 L 14.5,4 V 0 C 9.9949374,-0.001605 2.029505,0.001245 -0.0020687,0.001285 0,3.25 0,24 0,24 c 1.5,-0.02777 4.0236148,0 9.7142857,0 z"
|
||||
id="path11"
|
||||
sodipodi:nodetypes="ccccccccccccccccc"
|
||||
style="fill:#006400;fill-opacity:1;stroke-width:0.571427"
|
||||
clip-path="none"
|
||||
transform="matrix(0.875,0,0,0.875,-33,-2)" />
|
||||
<path
|
||||
style="fill:none;stroke:#489648;stroke-width:2.28571;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 20.571429,9.1428571 H 14.857143 L 11.428571,5.7142857 v -6.8571428"
|
||||
id="path2253"
|
||||
sodipodi:nodetypes="cccc"
|
||||
clip-path="none"
|
||||
transform="matrix(0.875,0,0,0.875,-33,-2)" />
|
||||
<path
|
||||
style="fill:none;stroke:#489648;stroke-width:3.23249;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 0,14.5 H 2"
|
||||
id="path892"
|
||||
sodipodi:nodetypes="cc"
|
||||
clip-path="none"
|
||||
transform="matrix(2.1875,0,0,0.875,-33,-2.0750619)" />
|
||||
<path
|
||||
style="fill:none;stroke:#489648;stroke-width:2.85714;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 5.2135977,25.142857 V 21.714286 L 9.7850263,17.142857 V 11.428571 L 5.2135977,6.8571429 v -7.42857147"
|
||||
id="path2265"
|
||||
sodipodi:nodetypes="cccccc"
|
||||
clip-path="none"
|
||||
transform="matrix(0.875,0,0,0.875,-32.561898,-2)" />
|
||||
<rect
|
||||
style="fill:#006400;fill-opacity:1;stroke:none;stroke-width:2.65576;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="rect878"
|
||||
width="6"
|
||||
height="5"
|
||||
x="17"
|
||||
y="14"
|
||||
rx="0"
|
||||
ry="0.80000001"
|
||||
clip-path="none"
|
||||
transform="matrix(0.875,0,0,0.875,-33,-1.25)" />
|
||||
<rect
|
||||
style="fill:#f29100;fill-opacity:1;stroke:none;stroke-width:1.5333;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="rect880"
|
||||
width="4"
|
||||
height="3"
|
||||
x="18"
|
||||
y="15"
|
||||
rx="0"
|
||||
ry="1"
|
||||
clip-path="none"
|
||||
transform="matrix(0.875,0,0,0.875,-33,-1.25)" />
|
||||
<rect
|
||||
style="fill:#006400;fill-opacity:1;stroke:none;stroke-width:2.65576;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="rect882"
|
||||
width="6"
|
||||
height="5"
|
||||
x="17"
|
||||
y="20"
|
||||
rx="0"
|
||||
ry="0.80000001"
|
||||
clip-path="none"
|
||||
transform="matrix(0.875,0,0,0.875,-33,-1.25)" />
|
||||
<rect
|
||||
style="fill:#f29100;fill-opacity:1;stroke:none;stroke-width:1.5333;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="rect884"
|
||||
width="4"
|
||||
height="3"
|
||||
x="18"
|
||||
y="21"
|
||||
rx="0"
|
||||
ry="0.96360058"
|
||||
clip-path="none"
|
||||
transform="matrix(0.875,0,0,0.875,-33,-1.25)" />
|
||||
<path
|
||||
style="fill:none;stroke:#2cb22c;stroke-width:1.14285;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 18,16.5 H 16"
|
||||
id="path886"
|
||||
sodipodi:nodetypes="cc"
|
||||
clip-path="none"
|
||||
transform="matrix(0.875,0,0,0.875,-33,-1.25)" />
|
||||
<path
|
||||
style="fill:none;stroke:#2cb22c;stroke-width:1.14285;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 24,16.5 H 22"
|
||||
id="path888"
|
||||
sodipodi:nodetypes="cc"
|
||||
clip-path="none"
|
||||
transform="matrix(0.875,0,0,0.875,-33,-1.25)" />
|
||||
<path
|
||||
style="fill:none;stroke:#2cb22c;stroke-width:2.28571;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 20,14 v 1"
|
||||
id="path890"
|
||||
sodipodi:nodetypes="cc"
|
||||
clip-path="none"
|
||||
transform="matrix(0.875,0,0,0.875,-33,-1.25)" />
|
||||
<circle
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#dd8d15;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path936"
|
||||
cx="-29.11735"
|
||||
cy="10.58021"
|
||||
r="1.6027977"
|
||||
clip-path="none" />
|
||||
<circle
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#dd8d15;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle938"
|
||||
cx="-14.137123"
|
||||
cy="5.9967451"
|
||||
r="1.6027977"
|
||||
clip-path="none" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8.4 KiB |
Loading…
Add table
Reference in a new issue