gumyr / build123d

A python CAD programming library
Apache License 2.0
387 stars 72 forks source link

pickling broken for STLs imported through Mesher #460

Open MatthiasJ1 opened 6 months ago

MatthiasJ1 commented 6 months ago
from build123d import *
import pickle

part = Box(1,1,1) - Cylinder(0.3, 2)
part.export_stl("test.stl")
stl = Mesher().read("test.stl")

dump = pickle.dumps(part)
part = pickle.loads(dump)

dump = pickle.dumps(stl)
stl = pickle.loads(dump)
    stl = pickle.loads(dump)
          ^^^^^^^^^^^^^^^^^^
OCP.Standard.Standard_Failure: EXCEPTION in BinTools_ShapeSet::ReadGeometry(S,OS)
0x16bdbc190 : Standard_Failure: BinTools_SurfaceSet::ReadGeometry: UnExpected BRep_PointRepresentation = 131
jdegenstein commented 6 months ago

Probably related to https://github.com/gumyr/build123d/issues/350

bernhard-42 commented 6 months ago

If it helps, this is not a OCCT problem:

In [115]: from build123d import *
     ...: import pickle
     ...: 
     ...: part = Box(1,1,1) - Cylinder(0.3, 2)
     ...: part.export_stl("test.stl")
     ...: stl = Mesher().read("test.stl")

In [116]: from ocp_tessellate.ocp_utils import serialize, deserialize
In [117]: dump = serialize(stl[0].wrapped)
In [118]: stl2 = deserialize(dump)
In [119]: stl2
Out[119]: <OCP.TopoDS.TopoDS_Shape at 0x2a70fe770>

So the issue is hidden in the pickle code in persistence.py, maybe some encoding issues?

bernhard-42 commented 6 months ago

I remember the issue now: BinTools sometimes crash with BytesIO. So in ocp_tessellate I use:

def serialize(shape):
    if shape is None:
        return None

    try:
        bio = io.BytesIO()
        BinTools.Write_s(shape, bio)
        buffer = bio.getvalue()
    except Exception:
        with tempfile.NamedTemporaryFile() as tf:
            BinTools.Write_s(shape, tf.name)
            with open(tf.name, "rb") as fd:
                buffer = fd.read()
    return buffer

def deserialize(buffer):
    if buffer is None:
        return None

    shape = TopoDS_Shape()
    try:
        bio = io.BytesIO(buffer)
        BinTools.Read_s(shape, bio)
    except Exception:
        with tempfile.NamedTemporaryFile() as tf:
            with open(tf.name, "wb") as fd:
                fd.write(buffer)
            BinTools.Read_s(shape, tf.name)
    return shape

So, whenever I detect a crash I take the slower route via the file system.

If I replace the code in persistence.py with the above, the example of this issue works

bernhard-42 commented 6 months ago

CC: @jdegenstein @Jojain

Jojain commented 6 months ago

We might have found out this issue (on the work on measure tool of OCP CAD Viewer) after I proposed the PR to build123d.

As far as we know there is not really any other solution that doing what we did, i.e using the filesystem.