Skip to content

Commit 9e47392

Browse files
Pickling support (#1762)
* Pickle for geom and Shape * Color pickling * Plane pickling * Pickle forConstruction * Fix Vector pickling * Add tests * Add Matrix * Fix Plane --------- Co-authored-by: adam-urbanczyk <[email protected]>
1 parent c063b94 commit 9e47392

File tree

4 files changed

+127
-0
lines changed

4 files changed

+127
-0
lines changed

cadquery/occ_impl/assembly.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,14 @@ def toTuple(self) -> Tuple[float, float, float, float]:
117117

118118
return (rgb.Red(), rgb.Green(), rgb.Blue(), a)
119119

120+
def __getstate__(self) -> Tuple[float, float, float, float]:
121+
122+
return self.toTuple()
123+
124+
def __setstate__(self, data: Tuple[float, float, float, float]):
125+
126+
self.wrapped = Quantity_ColorRGBA(*data)
127+
120128

121129
class AssemblyProtocol(Protocol):
122130
@property

cadquery/occ_impl/geom.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
from typing import overload, Sequence, Union, Tuple, Type, Optional, Iterator
44

5+
from io import BytesIO
6+
57
from OCP.gp import (
68
gp_Vec,
79
gp_Ax1,
@@ -22,6 +24,7 @@
2224
from OCP.BRepMesh import BRepMesh_IncrementalMesh
2325
from OCP.TopoDS import TopoDS_Shape
2426
from OCP.TopLoc import TopLoc_Location
27+
from OCP.BinTools import BinTools_LocationSet
2528

2629
from ..types import Real
2730
from ..utils import multimethod
@@ -256,6 +259,16 @@ def transform(self, T: "Matrix") -> "Vector":
256259

257260
return Vector(gp_Vec(pnt_t.XYZ()))
258261

262+
def __getstate__(self) -> tuple[float, float, float]:
263+
264+
return (self.x, self.y, self.z)
265+
266+
def __setstate__(self, state: tuple[float, float, float]):
267+
268+
self._wrapped = gp_Vec()
269+
270+
self.x, self.y, self.z = state
271+
259272

260273
class Matrix:
261274
"""A 3d , 4x4 transformation matrix.
@@ -400,6 +413,19 @@ def __repr__(self) -> str:
400413
matrix_str = ",\n ".join(str(matrix_transposed[i::4]) for i in range(4))
401414
return f"Matrix([{matrix_str}])"
402415

416+
def __getstate__(self) -> list[list[float]]:
417+
418+
trsf = self.wrapped
419+
return [[trsf.Value(i, j) for j in range(1, 5)] for i in range(1, 4)]
420+
421+
def __setstate__(self, state: list[list[float]]):
422+
423+
trsf = self.wrapped = gp_GTrsf()
424+
425+
for i in range(3):
426+
for j in range(4):
427+
trsf.SetValue(i + 1, j + 1, state[i][j])
428+
403429

404430
class Plane(object):
405431
"""A 2D coordinate system in space
@@ -577,6 +603,7 @@ def __init__(
577603
xDir = Vector(xDir)
578604
if xDir.Length == 0.0:
579605
raise ValueError("xDir should be non null")
606+
580607
self._setPlaneDir(xDir)
581608
self.origin = Vector(origin)
582609

@@ -787,6 +814,14 @@ def toPln(self) -> gp_Pln:
787814

788815
return gp_Pln(gp_Ax3(self.origin.toPnt(), self.zDir.toDir(), self.xDir.toDir()))
789816

817+
def __getstate__(self) -> Tuple[Vector, Vector, Vector, Vector]:
818+
819+
return (self.xDir, self.yDir, self.zDir, self._origin)
820+
821+
def __setstate__(self, data: Tuple[Vector, Vector, Vector, Vector]):
822+
823+
self.xDir, self.yDir, self.zDir, self.origin = data
824+
790825

791826
class BoundBox(object):
792827
"""A BoundingBox for an object or set of objects. Wraps the OCP one"""
@@ -1065,3 +1100,22 @@ def toTuple(self) -> Tuple[Tuple[float, float, float], Tuple[float, float, float
10651100
rx, ry, rz = rot.GetEulerAngles(gp_EulerSequence.gp_Extrinsic_XYZ)
10661101

10671102
return rv_trans, (degrees(rx), degrees(ry), degrees(rz))
1103+
1104+
def __getstate__(self) -> BytesIO:
1105+
1106+
rv = BytesIO()
1107+
1108+
ls = BinTools_LocationSet()
1109+
ls.Add(self.wrapped)
1110+
ls.Write(rv)
1111+
1112+
rv.seek(0)
1113+
1114+
return rv
1115+
1116+
def __setstate__(self, data: BytesIO):
1117+
1118+
ls = BinTools_LocationSet()
1119+
ls.Read(data)
1120+
1121+
self.wrapped = ls.Location(1)

cadquery/occ_impl/shapes.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1650,6 +1650,24 @@ def export(
16501650
self, fname, tolerance=tolerance, angularTolerance=angularTolerance, opt=opt
16511651
)
16521652

1653+
def __getstate__(self) -> Tuple[BytesIO, bool]:
1654+
1655+
data = BytesIO()
1656+
1657+
BinTools.Write_s(self.wrapped, data)
1658+
data.seek(0)
1659+
1660+
return (data, self.forConstruction)
1661+
1662+
def __setstate__(self, data: Tuple[BytesIO, bool]):
1663+
1664+
wrapped = TopoDS_Shape()
1665+
1666+
BinTools.Read_s(wrapped, data[0])
1667+
1668+
self.wrapped = wrapped
1669+
self.forConstruction = data[1]
1670+
16531671

16541672
class ShapeProtocol(Protocol):
16551673
@property

tests/test_pickle.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from pickle import loads, dumps
2+
3+
from cadquery import (
4+
Vector,
5+
Matrix,
6+
Plane,
7+
Location,
8+
Shape,
9+
Sketch,
10+
Assembly,
11+
Color,
12+
Workplane,
13+
)
14+
from cadquery.func import box
15+
16+
from pytest import mark
17+
18+
19+
@mark.parametrize(
20+
"obj",
21+
[
22+
Vector(2, 3, 4),
23+
Matrix(),
24+
Plane((-2, 1, 1)),
25+
Location(1, 2, 4),
26+
Sketch().rect(1, 1),
27+
Color("red"),
28+
Workplane().sphere(1),
29+
],
30+
)
31+
def test_simple(obj):
32+
33+
assert isinstance(loads(dumps(obj)), type(obj))
34+
35+
36+
def test_shape():
37+
38+
s = Shape(box(1, 1, 1).wrapped)
39+
40+
assert isinstance(loads(dumps(s)), Shape)
41+
42+
43+
def test_assy():
44+
45+
assy = Assembly().add(box(1, 1, 1), color=Color("blue")).add(box(2, 2, 2))
46+
47+
assert isinstance(loads(dumps(assy)), Assembly)

0 commit comments

Comments
 (0)