gumyr / build123d

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

Add `BRepFill_Filling` based constrained surface creation #654

Open jdegenstein opened 3 days ago

jdegenstein commented 3 days ago

Currently the surface construction methods in build123d do not provide great control over adjacent face continuity requirements. I propose to add BRepFill_Filling as a build123d direct API Face method.

Further reading: https://neweopencascade.wordpress.com/2019/03/15/build-constrained-surfaces-with-brepoffsetapi_makefilling-part-i/

I was able to create a rough proof of concept, and it does function as expected:

image

jdegenstein commented 3 days ago

Working prototype:

from typing import Iterable, Union, Tuple
from enum import Enum, auto
from OCP.BRepFill import BRepFill_Filling
from OCP.GeomAbs import *  # import specific

# put in build_enums.py
class Continuity(Enum):
    """geometric continuity"""

    C0 = auto()
    G1 = auto()
    G2 = auto()

    def __repr__(self):
        return f"<{self.__class__.__name__}.{self.name}>"

# put in topology.py ?
_continuity_dict = {
    Continuity.C0: GeomAbs_C0,
    Continuity.G1: GeomAbs_G1,
    Continuity.G2: GeomAbs_G2,
}

# put in topology.py as a classmethod of Face
@classmethod
def make_surface_patch(
    cls,
    edge_face_constraint: Iterable[
        Union[
            Tuple[Edge, Face, Continuity],
            Tuple[Edge, Continuity],
        ]
    ],
) -> Face:
    # input validation
    for efc in edge_face_constraint:
        if not isinstance(efc[0], Edge):
            raise ValueError(f"Provided {type(efc[0])} is not an Edge or Wire")

    patch = BRepFill_Filling()
    for efc in edge_face_constraint:
        if isinstance(efc[1], Continuity) and len(efc) == 2:
            patch.Add(efc[0].wrapped, _continuity_dict[efc[1]])
        else:
            patch.Add(efc[0].wrapped, efc[1].wrapped, _continuity_dict[efc[2]])
    patch.Build()
    result = patch.Face()

    return cls(result)

Face.make_surface_patch = make_surface_patch
gumyr commented 3 days ago

This looks good to me. At some point we're going to need some good surface modelling documentation with all of these different methods to choose from.

jdegenstein commented 2 days ago

@gumyr a few more question for you -- do you think the "UI" makes sense? The tuples supply the e.g. edge/face/continuity pairing. Is this consistent with other parts of build123d? I wanted it to be very explicit which parts "go together". I also decided against any kind of topology exploration (to automatically pair edges/faces) to avoid complexity and not to mention that it might not even be possible.

There are a few other things I have noticed:

  1. According to the OCCT docs it is possible to add a punctual constraint via a gp_Pnt. I currently think it is probably worth adding support for this. This does not require a constraint.
  2. As far as I can tell (and I could be very wrong) I do not see the point for the user to be able to supply the continuity for unsupported edges, they seem to only use C0 continuity.
gumyr commented 2 days ago

Would you mind sharing the code you used to create the heart value example? It would be easier to discuss the API by looking at a real example.

One thing that might help is to rename the Continuity Enum to terms more commonly used but this is easier said than done.