About
Methods for representing shape data using Deep Neural Networks have recently begun to be proposed, such as BrepGen, which generates data that directly represents shapes in Brep format, and DeepCAD, which generates sequences of CAD commands to describe such data.
Checking Core Part of Shape Generation
Top Flow of the Code
It is a simple process of generating CAD data (STEP files) from a CADSequence
class object via Open CASCADE Technology.
cad_seq = CADSequence.from_dict(data)
cad_seq.normalize()
out_shape = create_CAD(cad_seq)
How it Composes shape as an object
Shapes are constructed through combinations of Boolean operations between shapes. Each shape is described using the common sketch-and-extrude method.
https://github.com/ChrisWu1997/DeepCAD/blob/master/cadlib/visualize.py#L25
def create_CAD(cad_seq: CADSequence):
"""create a 3D CAD model from CADSequence. Only support extrude with boolean operation."""
body = create_by_extrude(cad_seq.seq[0])
for extrude_op in cad_seq.seq[1:]:
new_body = create_by_extrude(extrude_op)
if extrude_op.operation == EXTRUDE_OPERATIONS.index("NewBodyFeatureOperation") or \
extrude_op.operation == EXTRUDE_OPERATIONS.index("JoinFeatureOperation"):
body = BRepAlgoAPI_Fuse(body, new_body).Shape()
elif extrude_op.operation == EXTRUDE_OPERATIONS.index("CutFeatureOperation"):
body = BRepAlgoAPI_Cut(body, new_body).Shape()
elif extrude_op.operation == EXTRUDE_OPERATIONS.index("IntersectFeatureOperation"):
body = BRepAlgoAPI_Common(body, new_body).Shape()
return body
def create_by_extrude(extrude_op: Extrude):
"""create a solid body from Extrude instance."""
profile = copy(extrude_op.profile) # use copy to prevent changing extrude_op internally
profile.denormalize(extrude_op.sketch_size)
sketch_plane = copy(extrude_op.sketch_plane)
sketch_plane.origin = extrude_op.sketch_pos
face = create_profile_face(profile, sketch_plane)
normal = gp_Dir(*extrude_op.sketch_plane.normal)
ext_vec = gp_Vec(normal).Multiplied(extrude_op.extent_one)
body = BRepPrimAPI_MakePrism(face, ext_vec).Shape()
if extrude_op.extent_type == EXTENT_TYPE.index("SymmetricFeatureExtentType"):
body_sym = BRepPrimAPI_MakePrism(face, ext_vec.Reversed()).Shape()
body = BRepAlgoAPI_Fuse(body, body_sym).Shape()
if extrude_op.extent_type == EXTENT_TYPE.index("TwoSidesFeatureExtentType"):
ext_vec = gp_Vec(normal.Reversed()).Multiplied(extrude_op.extent_two)
body_two = BRepPrimAPI_MakePrism(face, ext_vec).Shape()
body = BRepAlgoAPI_Fuse(body, body_two).Shape()
return body
Sketch
How is the sketch in the sketch-and-extrude process structured?
The sketch is defined by an object created from the Profile
class within the Extrude
class. Furthermore, this Profile
class consists of a list of multiple Loop
class objects, as shown below.
In other words, a Loop consists of multiple curves that together form a closed region.
https://github.com/ChrisWu1997/DeepCAD/blob/master/cadlib/sketch.py#L211
class Loop(SketchBase):
"""Sketch loop, a sequence of connected curves."""
@staticmethod
def from_dict(stat):
all_curves = [construct_curve_from_dict(item) for item in stat['profile_curves']]
this_loop = Loop(all_curves)
this_loop.is_outer = stat['is_outer']
return this_loop
...
class Profile(SketchBase):
"""Sketch profile,a closed region formed by one or more loops.
The outer-most loop is placed at first."""
@staticmethod
def from_dict(stat):
all_loops = [Loop.from_dict(item) for item in stat['loops']]
return Profile(all_loops)
class Extrude(object):
"""Single extrude operation with corresponding a sketch profile.
NOTE: only support single sketch profile. Extrusion with multiple profiles is decomposed."""
def __init__(self, profile: Profile, sketch_plane: CoordSystem,
operation, extent_type, extent_one, extent_two, sketch_pos, sketch_size):
"""
Args:
profile (Profile): normalized sketch profile
sketch_plane (CoordSystem): coordinate system for sketch plane
operation (int): index of EXTRUDE_OPERATIONS, see macro.py
extent_type (int): index of EXTENT_TYPE, see macro.py
extent_one (float): extrude distance in normal direction (NOTE: it's negative in some data)
extent_two (float): extrude distance in opposite direction
sketch_pos (np.array): the global 3D position of sketch starting point
sketch_size (float): size of the sketch
"""
self.profile = profile # normalized sketch
self.sketch_plane = sketch_plane
self.operation = operation
self.extent_type = extent_type
self.extent_one = extent_one
self.extent_two = extent_two
Lines (Curves)
The curve mentioned above belongs to one of three classes—line, circle, or circular arc—each of which inherits from the CurveBase
class.
https://github.com/ChrisWu1997/DeepCAD/blob/master/cadlib/curves.py#L9
def construct_curve_from_dict(stat):
if stat['type'] == "Line3D":
return Line.from_dict(stat)
elif stat['type'] == "Circle3D":
return Circle.from_dict(stat)
elif stat['type'] == "Arc3D":
return Arc.from_dict(stat)
else:
raise NotImplementedError("curve type not supported yet: {}".format(stat['type']))
Thoughts
The approach of constructing the final geometry by combining very simple shapes may currently face challenges in representing complex shapes. However, this simple composition aligns well with the research objective of clearly defining what is possible and to what extent. I believe the approach they have demonstrated will have a significant impact on future research and development. Evaluating the generation of 3D shapes based on “accuracy” is particularly intriguing, and further advancements in this area are highly anticipated. Additionally, their code is remarkably refined and elegant, making it truly impressive.
Reference
[1] https://arxiv.org/abs/2105.09492