CadQuery / cadquery

A python parametric CAD scripting framework based on OCCT
https://cadquery.readthedocs.io
Other
3.26k stars 295 forks source link

Fragment Operation #983

Open Edwaerd opened 2 years ago

Edwaerd commented 2 years ago

Hi, I’ve just started to discover open source modeling programs (Gmsh,FreeCad,Cadquery).I wonder , does cadquery has capability of fragment operation like Gmsh and FreeCad ?

thx

adam-urbanczyk commented 2 years ago

What is a fragment operation?

jmwright commented 2 years ago

@Edwaerd If you mean tessellation of a CadQuery object into a mesh, see here.

Edwaerd commented 2 years ago

For example, I have a cylindrical object and also I have smaller other objects inside the cylindrical object(let's say spheres).I want to generate conformal mesh

https://gmsh.info/doc/texinfo/gmsh.html#Boolean-operations https://wiki.freecadweb.org/Part_BooleanFragments

adam-urbanczyk commented 2 years ago

Something like boolean fragments (BRepAlgoAPI_BuilderAlgo to be specific) is not exposed.

bragostin commented 2 years ago

Some time ago I had written an implementation of fragment by adding a Workplane method fragment in cq.py:

def fragment(
        self,
        toFragment: Optional[Union["Workplane", Solid, Compound]] = None,
        clean: bool = True,
        glue: bool = False,
        tol: Optional[float] = None,
    ) -> "Workplane":
        """
        Fragment all of the items on the stack of toFragment with the current tool.
        If there is no current solid, the items in toFragment are fragmented together.

        :param toFragment:
        :type toFragment: a solid object, or a CQ object having a solid,
        :param boolean clean: call :py:meth:`clean` afterwards to have a clean shape (default True)
        :param boolean glue: use a faster gluing mode for non-overlapping shapes (default False)
        :param float tol: tolerance value for fuzzy bool operation mode (default None)
        :raises: ValueError if there is no solid to add to in the chain
        :return: a CQ object with the resulting object selected
        """

        # first collect all of the items together
        newS: List[Shape]
        if isinstance(toFragment, CQ):
            newS = cast(List[Shape], toFragment.solids().vals())
            if len(newS) < 1:
                raise ValueError(
                    "CQ object  must have at least one solid on the stack to union!"
                )
        elif isinstance(toFragment, (Solid, Compound)):
            newS = [toFragment]
        else:
            raise ValueError("Cannot fragment type '{}'".format(type(toFragment)))

        # now combine with existing solid, if there is one
        solidRef = self._findType(
            (Solid, Compound), searchStack=True, searchParents=True
        )
        if solidRef is not None:
            r = solidRef.fragment(*newS, glue=glue, tol=tol)
        elif len(newS) > 1:
            r = newS.pop(0).fragment(*newS, glue=glue, tol=tol)
        else:
            r = newS[0]

        if clean:
            r = r.clean()

        return r

and a Compound method fragment in shapes.py

def fragment(
        self, *toFragment: "Shape", glue: bool = False, tol: Optional[float] = None
    ) -> "Shape":
        """
        Fragment the positional arguments with this Shape.

        :param glue: Sets the glue option for the algorithm, which allows
            increasing performance of the intersection of the input shapes
        :param tol: Additional tolerance
        """

        fragment_op = BRepAlgoAPI_BuilderAlgo()
        toFragment = tuple(self) + toFragment
        arg = TopTools_ListOfShape()
        for obj in toFragment:
            arg.Append(obj.wrapped)
        fragment_op.SetArguments(arg)

        if glue:
            fragment_op.SetGlue(BOPAlgo_GlueEnum.BOPAlgo_GlueShift)
        if tol:
            fragment_op.SetFuzzyValue(tol)

        fragment_op.SetRunParallel(True)

        fragment_op.Build()

        if not fragment_op.IsDone():
            print("Fragment Error : ", fragment_op.IsDone())

        it = TopoDS_Iterator(fragment_op.Shape())
        los = tuple()

        while it.More():
            los = los + (Shape.cast(it.Value()),)
            it.Next()

        return Compound.makeCompound(los)

It returns a compound containing the fragments.

fedorkotov commented 2 years ago

@bragostin If you have the time, you can contribute it as a plugin to https://github.com/CadQuery/cadquery-plugins (README of that repository has detailed instructions on how to do it)

bragostin commented 2 years ago

@fedorkotov absolutely, I have never written a plugin but I will look at it. I think fragment probably does not deserve a PR (I have not found a compelling use case for it yet) but a plugin would make sense.

bragostin commented 2 years ago

@fedorkotov done in https://github.com/CadQuery/cadquery-plugins/pull/27#issue-1137766265