Here's a mockup of what an IFC-centric shape builder class might look like. There are a number of things that are quite IFC specific that makes other generators perhaps less suitable. Things already mentioned like arcs or circles, but there are also the concept of how each extrusion has its own extrusion vector and local position. These signatures are inspired by OpenSCAD, but tailored to an IFC geometry paradigm.
# Class mockup
ShapeBuilder()
__init__:
self.items = []
polyline(points) -> returns IfcIndexedPolyCurve
rectangle(x, y, size) -> returns IfcIndexedPolyCurve
circle(x, y, r) -> returns IfcCircle
profile(outer, inner=None, name=None, type="AREA") -> returns IfcArbitraryClosedProfileDef or IfcArbitraryProfileDefWithVoids
translate(curve_or_item, create_copy=False) -> returns None
rotate(curve_or_item, create_copy=False) -> returns None
mirror(curve_or_item, create_copy=False) -> returns None
extrude(profile, position, vector, magnitude) -> IfcExtrudedAreaSolid, will append to self.items
get_items()
get_representation(context)
# How IFC shapes are built (only basic extrusions will be supported as a first version)
IfcShapeRepresentation
-> Items (IfcExtrudedAreaSolid)
-> SweptArea (IfcArbitraryClosedProfileDef or IfcArbitraryProfileDefWithVoids)
-> IfcIndexedPolyCurve (subtype of IfcCurve)
-> IfcCircle (subtype of IfcCurve)
-> Position (IfcAxis2Placement3D)
-> ExtrudedDirection (IfcDirection - should default to +Z where possible, +Z is the local Z axis of the "Position" matrix, i.e. 0,0,1)
-> Depth
These functions could be used elsewhere too, like in IfcSverchok, other parametric IFC things like railing generators, door and window generators, fabrication MEP generation, etc. It also opens up a whole new opportunity of coding with IFC ... in the past, if you wanted to create geometry with IFC you really needed a graphical environment, this offers a whole new code-based geometry creator.
That said, I think it's also super important to support geometry nodes and Sverchok because they can run circles around code in many situations. I'm inspired by the ease at which @JanF and @Gorgious can create these things. So... why not support everything? All we really need is a standard way to run(inputs), and get_output(). For geometry nodes, it looks as though there is a clear input and outputs, and if the node inputs matched the BOLTS variables, then all that is needed is a little wrapper to run it. For Sverchok, I'm less certain. I noticed you can set the name of "Number" sliders, perhaps a convention can be that number sliders with a name matching the BOLTS input are used as inputs? For the output, I'm really not sure, I think we'd need verts, edges, and faces (v+e for 2D, v+f for 3D) ... but @JanF do you know how to nominate an "output" node? Or do we need to create our own special output node?
Here's some pseudocode, what do you guys think? If we standardise the class interface, then in theory this approach can then be easily reused for people not interested in IFC (e.g. if FreeCAD wanted pure OCC Breps), or cast down to triangulated meshes via IfcOpenShell and used generally across any platform.
def create_ifc_type(bolts_class, object_name, bolts_parameters):
engines = [ifc_code_engine, sverchok_engine, geometry_nodes_engine, openscad_engine, freecad_engine]
for engine in engines:
creator = engine.get_shape_creator(bolts_class, object_name)
if creator:
break
creator.run(self.file, bolts_class, object_name, bolts_parameters) # no return value, directly creates IFC data
class SverchokEngine:
**@classmethod**
def get_creator(cls, bolts_class, object_name):
if object_name in bpy.data.node_trees:
self.node_tree = bpy.data.node_trees[object_name]
return cls
if isfile('path/to/{object_name}.json'):
load_file(object_name.json)
self.node_tree = create_node_tree()
return cls
def run(...):
self.node_tree.run() (?)
geometry = get_v_e_f()
convert_to_ifc(geometry)
ifc.create_entity(ifc_class, ...)
class GeometryNodesEngine:
...