Skip to content
Merged
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
104 changes: 72 additions & 32 deletions cadquery/occ_impl/assembly.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Iterable, Tuple, Dict, overload, Optional, Any, List
from typing import Union, Iterable, Tuple, Dict, overload, Optional, Any, List
from typing_extensions import Protocol
from math import degrees

Expand All @@ -20,6 +20,10 @@
from .geom import Location
from .shapes import Shape, Compound
from .exporters.vtk import toString
from ..cq import Workplane

# type definitions
AssemblyObjects = Union[Shape, Workplane, None]


class Color(object):
Expand Down Expand Up @@ -50,9 +54,18 @@ def __init__(self, r: float, g: float, b: float, a: float = 0):
"""
...

@overload
def __init__(self):
"""
Construct a Color with default value.
"""
...

def __init__(self, *args, **kwargs):

if len(args) == 1:
if len(args) == 0:
self.wrapped = Quantity_ColorRGBA()
elif len(args) == 1:
self.wrapped = Quantity_ColorRGBA()
exists = Quantity_ColorRGBA.ColorFromName_s(args[0], self.wrapped)
if not exists:
Expand Down Expand Up @@ -122,7 +135,11 @@ def setColor(l: TDF_Label, color: Color, tool):


def toCAF(
assy: AssemblyProtocol, coloredSTEP: bool = False
assy: AssemblyProtocol,
coloredSTEP: bool = False,
mesh: bool = False,
tolerance: float = 1e-3,
angularTolerance: float = 0.1,
) -> Tuple[TDF_Label, TDocStd_Document]:

# prepare a doc
Expand All @@ -139,40 +156,63 @@ def toCAF(
top = tool.NewShape()
TDataStd_Name.Set_s(top, TCollection_ExtendedString("CQ assembly"))

# add leafs and subassemblies
subassys: Dict[str, Tuple[TDF_Label, Location]] = {}
for k, v in assy.traverse():
# leaf part
lab = tool.NewShape()
tool.SetShape(lab, Compound.makeCompound(v.shapes).wrapped)
setName(lab, f"{k}_part", tool)
# used to store labels with unique part-color combinations
unique_objs: Dict[Tuple[Color, AssemblyObjects], TDF_Label] = {}
# used to cache unique, possibly meshed, compounds; allows to avoid redundant meshing operations if same object is referenced multiple times in an assy
compounds: Dict[AssemblyObjects, Compound] = {}

def _toCAF(el, ancestor, color):

# assy part
# create a subassy
subassy = tool.NewShape()
tool.AddComponent(subassy, lab, TopLoc_Location())
setName(subassy, k, tool)

# handle colors - this logic is needed for proper STEP export
color = v.color
tmp = v
if coloredSTEP:
while not color and tmp.parent:
tmp = tmp.parent
color = tmp.color
if color:
setColor(lab, color, ctool)
else:
if color:
setColor(subassy, color, ctool)
setName(subassy, el.name, tool)

# define the current color
current_color = el.color if el.color else color

# add a leaf with the actual part if needed
if el.obj:
# get/register unique parts referenced in the assy
key0 = (current_color, el.obj) # (color, shape)
key1 = el.obj # shape

if key0 in unique_objs:
lab = unique_objs[key0]
else:
lab = tool.NewShape()
if key1 in compounds:
compound = compounds[key1].copy(mesh)
else:
compound = Compound.makeCompound(el.shapes)
if mesh:
compound.mesh(tolerance, angularTolerance)

compounds[key1] = compound

tool.SetShape(lab, compound.wrapped)
setName(lab, f"{el.name}_part", tool)
unique_objs[key0] = lab

# handle colors when exporting to STEP
if coloredSTEP and current_color:
setColor(lab, current_color, ctool)

tool.AddComponent(subassy, lab, TopLoc_Location())

# handle colors when *not* exporting to STEP
if not coloredSTEP and current_color:
setColor(subassy, current_color, ctool)

# add children recursively
for child in el.children:
_toCAF(child, subassy, current_color)

subassys[k] = (subassy, v.loc)
# add the current subassy to the higher level assy
tool.AddComponent(ancestor, subassy, el.loc.wrapped)

for ch in v.children:
tool.AddComponent(
subassy, subassys[ch.name][0], subassys[ch.name][1].wrapped
)
# process the whole assy recursively
_toCAF(assy, top, None)

tool.AddComponent(top, subassys[assy.name][0], assy.loc.wrapped)
tool.UpdateAssemblies()

return top, doc
Expand Down
9 changes: 2 additions & 7 deletions cadquery/occ_impl/exporters/assembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def exportGLTF(
assy: AssemblyProtocol,
path: str,
binary: bool = True,
tolerance: float = 0.1,
tolerance: float = 1e-3,
angularTolerance: float = 0.1,
):
"""
Expand All @@ -167,12 +167,7 @@ def exportGLTF(
orig_loc = assy.loc
assy.loc *= Location((0, 0, 0), (1, 0, 0), -90)

# mesh all the shapes
for _, el in assy.traverse():
for s in el.shapes:
s.mesh(tolerance, angularTolerance)

_, doc = toCAF(assy, True)
_, doc = toCAF(assy, True, True, tolerance, angularTolerance)

writer = RWGltf_CafWriter(TCollection_AsciiString(path), binary)
result = writer.Perform(
Expand Down
7 changes: 5 additions & 2 deletions cadquery/occ_impl/shapes.py
Original file line number Diff line number Diff line change
Expand Up @@ -912,12 +912,15 @@ def scale(self, factor: float) -> "Shape":

return self._apply_transform(T)

def copy(self: T) -> T:
def copy(self: T, mesh: bool = False) -> T:
"""
Creates a new object that is a copy of this object.

:param mesh: should I copy the triangulation too (default: False)
:returns: a copy of the object
"""

return self.__class__(BRepBuilderAPI_Copy(self.wrapped).Shape())
return self.__class__(BRepBuilderAPI_Copy(self.wrapped, True, mesh).Shape())

def transformShape(self, tMatrix: Matrix) -> "Shape":
"""
Expand Down
Loading