Compare commits

...

50 Commits

Author SHA1 Message Date
Dion Moult c352181f8c Fix bug where underlays didn't work 2022-05-22 21:32:57 +10:00
Thomas Krijnen 3a1416536a Oops 2022-05-17 22:31:37 +02:00
Thomas Krijnen 4eec863cad Example for turning IDS test cases into spec. Sorry for butchering your code @Moult 2022-05-17 22:21:11 +02:00
Thomas Krijnen 96bcd31d99 #2202 Check for nested compound of solid in boolean result handling 2022-05-16 14:24:57 +02:00
Thomas Krijnen b09ff828de #2186 rerun codegen 2022-05-16 13:07:40 +02:00
Thomas Krijnen 3cb68ca388 #2186 optional attribute setter fix 2022-05-16 13:07:40 +02:00
Dion Moult c95d3693ea OpenSourceBIM contributors badge now in README 2022-05-16 11:35:39 +10:00
Dion Moult 67d2ab2577 Write documentation on materials, locations, and measurements 2022-05-16 08:15:31 +10:00
Dion Moult 970b24b63b #1153 New annotation objects have their placement Z in the drawing direction to allow for 2D geometry. 2022-05-15 11:45:47 +10:00
Dion Moult fdaae048c2 Implement IDS partOf facet 2022-05-12 14:33:44 +10:00
Dion Moult 4da5a38237 Implement IDS material null checks, inheritance checks, and set item checks 2022-05-12 12:06:23 +10:00
Dion Moult 0cf2d7e05e Implement IDS uri/use/instructions for material facet 2022-05-11 23:30:10 +10:00
Dion Moult 3122cf21db Implement IDS property facet measure conversions 2022-05-11 19:59:10 +10:00
Dion Moult 4bf25d5d9c Minor fix 2022-05-11 18:46:27 +10:00
Dion Moult dc10c7a360 Implement null check for IDS properties, and support restrictions 2022-05-11 17:10:22 +10:00
Dion Moult 94e082fc7d Support IDS uri/use/instructions for property facets 2022-05-11 15:30:39 +10:00
Dion Moult 9671df8491 You can now choose between lightweight and full classifications 2022-05-11 15:19:31 +10:00
Dion Moult 8aa38917eb New classification utility module 2022-05-11 15:19:17 +10:00
Dion Moult 0774100606 IDS classification facets not handle inheritance, full refs, and restrictions 2022-05-11 14:49:47 +10:00
Dion Moult 95b48b02e4 Add support for IDS classification uri, use, and instructions 2022-05-11 13:27:19 +10:00
Dion Moult c5de1d8727 Add IDS support for restrictions in attribute facets and handle inheritance 2022-05-11 13:06:15 +10:00
Dion Moult 7e09ab0ced Add support for IDS attribute use/instructions 2022-05-11 10:12:36 +10:00
Dion Moult eb3e137a6e #2196 Get psets now handleds inheritance by default 2022-05-11 09:18:23 +10:00
Dion Moult f528ad9744 Minor fix 2022-05-10 23:49:14 +10:00
Dion Moult 650f9eb7f5 Attribute filtering in IDS now checks null-like values too. 2022-05-10 22:42:21 +10:00
Dion Moult a78d6f42e5 Allow IDS to audit any entity, not just IfcObjects 2022-05-10 22:13:28 +10:00
Dion Moult 4326a74c64 Fix IDS to not allow subclasses and allow restrictions in the class name 2022-05-10 21:40:35 +10:00
Dion Moult 08fc62d4d9 Add support for userdefined predefined types in IDS entity facets 2022-05-10 21:20:10 +10:00
Dion Moult ae298de8dc Implement identifier, desc, and instructions for specification node in IDS 2022-05-10 19:26:12 +10:00
Dion Moult a24c4b269a Fix mandatory title and optional date and check invalid dates in IDS 2022-05-10 17:36:24 +10:00
Dion Moult 083d9d3e3e Implement attribute facet for IDS 2022-05-10 16:23:07 +10:00
Dion Moult 12e7ed356c Fix IDS namespace bug that prevented XML encoding, simplify XML writing, and new to_string() method to decrease dependency on the filesystem 2022-05-10 16:22:38 +10:00
Dion Moult 3f15dd71b1 Run black on ids module 2022-05-10 16:18:08 +10:00
Adrien SCHVALBERG 42448cae4e Add missing exports for ifcopenshell-python when we build shared libs. 2022-05-09 14:13:47 +02:00
Dion Moult 7e28d193ec #2185 Document installation and hello world examples for IfcOpenShell-python 2022-05-09 19:27:33 +10:00
Thomas Krijnen 8f2b32532c validate.py text result formatting improvements 2022-05-09 11:00:04 +02:00
Dion Moult f0a00cbc94 #2185 Set up auto generated API reference documentation via sphinx-autoapi 2022-05-09 15:35:52 +10:00
Dion Moult fd73b2cf0a #2185 Migrate web files to website repo 2022-05-08 19:51:17 +10:00
Adrien SCHVALBERG e5b3455533 Fix memory leak for HeaderEntity instances:
the getArgumentCount() call in IfcEntityInstanceData destructor isn't virtually dispatched so it returns 0 instead of correct number of arguments => memory leak
added IfcEntityInstanceData::clearArguments() which clears all arguments and resets the attributes_ field to nullptr

Remove unnecessary copy in IfcEntityInstanceData::setArgument() when argument is created at the call site (no more leak of original argument)

Fix memory leak in IfcFile: delete entities from both byid and entity_file_map
2022-05-08 10:21:46 +02:00
Adrien SCHVALBERG e677355438 Add IfcParse::clear_schemas() and individual Ifc___::clear_schema() to clear any resource allocated by registered schemas
Add missing virtual destructors in IfcSchema.h
2022-05-08 10:21:46 +02:00
Dion Moult 93fd3f97e3 #2111 New feature to select by style and see style usage 2022-05-08 18:03:18 +10:00
Dion Moult 910d189f35 #2188 support displaying inherited materials 2022-05-08 16:27:35 +10:00
Dion Moult c8d6d12442 The material manager now groups materials into categories 2022-05-08 15:48:09 +10:00
Dion Moult 1f3ba3d09d New makefile targets to specifically run tests of single modules 2022-05-08 15:45:47 +10:00
Dion Moult f67941221e Enable syntax highlighting in test suite for easier debugging 2022-05-08 15:24:06 +10:00
Dion Moult 8252ef82ad #2185 Write documentation about viewing construction types 2022-05-07 21:50:38 +10:00
Dion Moult 3b825e35ae #1153 You can now provide custom stylesheets, markers, symbols, and hatches 2022-05-07 20:28:24 +10:00
Dion Moult 4a4912e817 Editing URIReference properties now provides a file browse button 2022-05-07 20:26:25 +10:00
Dion Moult 5a38604ec2 #1153 Boilerplate styles and definitions now can be used with any drawing layer combination 2022-05-07 17:57:05 +10:00
Dion Moult b15e0d0e2b #2185 Commit logos and homepage mockup by Paulina Godoy and Meetlat 2022-05-07 11:48:20 +10:00
173 changed files with 9397 additions and 14185 deletions
+2
View File
@@ -11,6 +11,8 @@ For more information, see
[![Build Status](https://travis-ci.org/IfcOpenShell/IfcOpenShell.svg?branch=v0.6.0)](https://travis-ci.org/IfcOpenShell/IfcOpenShell)
[![Financial Contributors](https://opencollective.com/opensourcebim/tiers/badge.svg)](https://opencollective.com/opensourcebim/)
Prerequisites
-------------
* Git
+8
View File
@@ -534,11 +534,19 @@ test-core:
.PHONY: test-bim
test-bim:
ifndef MODULE
pytest test/bim
else
pytest test/bim -m "$(MODULE)" ./ --maxfail=1
endif
.PHONY: test-tool
test-tool:
ifndef MODULE
pytest test/tool
else
pytest test/tool/test_$(MODULE).py
endif
.PHONY: qa
qa:
@@ -5,7 +5,7 @@ FILE_NAME('EPset_Drawing.ifc','2020-01-01T00:00:00',(),(),'EPset_Drawing','EPset
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROPERTYSETTEMPLATE('2JhNIvqZrFnAgxfhK0XVQX',$,'EPset_Drawing','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation',(#2,#3,#4,#5,#6,#7,#8,#9,#10,#11));
#1=IFCPROPERTYSETTEMPLATE('2JhNIvqZrFnAgxfhK0XVQX',$,'EPset_Drawing','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation',(#2,#3,#4,#5,#6,#7,#8,#9,#10,#11,#12,#13,#14,#15));
#2=IFCSIMPLEPROPERTYTEMPLATE('23JavTMk98ZxXhrUEnjAcf',$,'TargetView','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#3=IFCSIMPLEPROPERTYTEMPLATE('1yVWUt5H9DAOuu0OaMMLpe',$,'Scale','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#4=IFCSIMPLEPROPERTYTEMPLATE('3gsuPBtU93b8f0gg1pjkq6',$,'HumanScale','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
@@ -16,5 +16,9 @@ DATA;
#9=IFCSIMPLEPROPERTYTEMPLATE('10hT_1zrzEbRRKMXYAWvtD',$,'Metadata','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
#10=IFCSIMPLEPROPERTYTEMPLATE('3Z0BXPSG5CWgtI33ioV7aj',$,'Include','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
#11=IFCSIMPLEPROPERTYTEMPLATE('1RVts_g3PAw98PJA2yL3bO',$,'Exclude','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
#12=IFCSIMPLEPROPERTYTEMPLATE('0c1$8NpYDEaBiJrj16jHIo',$,'Stylesheet','',.P_SINGLEVALUE.,'IfcURIReference',$,$,$,$,$,.READWRITE.);
#13=IFCSIMPLEPROPERTYTEMPLATE('3mRF52q81FQB$h4oTh7M45',$,'Markers','',.P_SINGLEVALUE.,'IfcURIReference',$,$,$,$,$,.READWRITE.);
#14=IFCSIMPLEPROPERTYTEMPLATE('1rhr_0N3LDtuORcEJP0KXM',$,'Symbols','',.P_SINGLEVALUE.,'IfcURIReference',$,$,$,$,$,.READWRITE.);
#15=IFCSIMPLEPROPERTYTEMPLATE('2sHDBuW7P4TROy$hL2w7ct',$,'Patterns','',.P_SINGLEVALUE.,'IfcURIReference',$,$,$,$,$,.READWRITE.);
ENDSEC;
END-ISO-10303-21;
@@ -35,7 +35,7 @@ text { fill: black; stroke: none; }
.PredefinedType-RADIUS { marker-end: url(#radius-marker-end); }
.PredefinedType-DIAMETER { marker-start: url(#diameter-marker-start); marker-end: url(#diameter-marker-end); }
.PredefinedType-HIDDENLINE { stroke-dasharray: 3, 2; }
.PredefinedType-STAIR { marker-start: url(#stair-marker-start); marker-end: url(#stair-marker-end); }
.PredefinedType-STAIRARROW { marker-start: url(#stair-marker-start); marker-end: url(#stair-marker-end); }
.PredefinedType-BOUNDARY { fill: none; stroke: red; stroke-width: 1; stroke-dasharray: 12, 4, 3, 4, 3, 4; }
.PredefinedType-BATTING { marker-start: url(#batting); stroke: none; }
.PredefinedType-SEALANT { fill: url(#crosshatch1); stroke-width: 0.25; }
@@ -151,10 +151,17 @@ class Annotator:
return obj
@staticmethod
def get_annotation_obj(object_type, data_type):
collection = bpy.context.scene.camera.users_collection[0]
def get_annotation_obj(drawing, object_type, data_type):
camera = tool.Ifc.get_object(drawing)
co1, _, _, _ = Annotator.get_placeholder_coords(camera)
matrix_world = camera.matrix_world.copy()
matrix_world[0][3] = co1.x
matrix_world[1][3] = co1.y
matrix_world[2][3] = co1.z
collection = camera.users_collection[0]
if object_type == "TEXT":
obj = bpy.data.objects.new(object_type, None)
obj.matrix_world = matrix_world
collection.objects.link(obj)
return obj
elif object_type in ("TEXT_LEADER", "SECTION_LEVEL"):
@@ -162,6 +169,7 @@ class Annotator:
data.dimensions = "3D"
data.resolution_u = 2
obj = bpy.data.objects.new(object_type, data)
obj.matrix_world = matrix_world
collection.objects.link(obj)
return obj
if object_type != "ANGLE":
@@ -176,12 +184,14 @@ class Annotator:
data.dimensions = "3D"
data.resolution_u = 2
obj = bpy.data.objects.new(object_type, data)
obj.matrix_world = matrix_world
collection.objects.link(obj)
return obj
@staticmethod
def get_placeholder_coords():
camera = bpy.context.scene.camera
def get_placeholder_coords(camera=None):
if not camera:
camera = bpy.context.scene.camera
z_offset = camera.matrix_world.to_quaternion() @ Vector((0, 0, -1))
if bpy.context.scene.render.resolution_x > bpy.context.scene.render.resolution_y:
y = (
@@ -124,6 +124,15 @@ class CreateDrawing(bpy.types.Operator):
self.get_scale()
if self.cprops.update_representation(self.camera):
bpy.ops.bim.update_representation(obj=self.camera.name, ifc_representation_class="")
self.svg_writer = svgwriter.SvgWriter()
self.svg_writer.human_scale = self.human_scale
self.svg_writer.scale = self.scale
self.svg_writer.data_dir = context.scene.BIMProperties.data_dir
self.svg_writer.camera = self.camera
self.svg_writer.camera_width, self.svg_writer.camera_height = self.get_camera_dimensions()
self.svg_writer.camera_projection = tuple(self.camera.matrix_world.to_quaternion() @ Vector((0, 0, -1)))
underlay_svg = self.generate_underlay(context)
self.profile_code("Generate underlay")
linework_svg = self.generate_linework(context)
@@ -136,6 +145,16 @@ class CreateDrawing(bpy.types.Operator):
print("Total Time: {:.2f}".format(time.time() - start))
return {"FINISHED"}
def get_camera_dimensions(self):
render = bpy.context.scene.render
if self.is_landscape(render):
width = self.camera.data.ortho_scale
height = width / render.resolution_x * render.resolution_y
else:
height = self.camera.data.ortho_scale
width = height / render.resolution_y * render.resolution_x
return width, height
def profile_code(self, message):
if not self.time:
self.time = time.time()
@@ -146,22 +165,26 @@ class CreateDrawing(bpy.types.Operator):
# Hacky :)
svg_path = os.path.join(context.scene.BIMProperties.data_dir, "diagrams", self.drawing_name + ".svg")
with open(svg_path, "w") as outfile:
has_boilerplate = False
pset = ifcopenshell.util.element.get_psets(self.camera_element)["EPset_Drawing"]
self.svg_writer.create_blank_svg(svg_path).define_boilerplate(
pset.get("Stylesheet"), pset.get("Markers"), pset.get("Symbols"), pset.get("Patterns")
)
boilerplate = self.svg_writer.svg.tostring()
outfile.write(boilerplate.replace("</svg>", ""))
if underlay:
with open(underlay) as infile:
for line in infile:
if "<svg " in line:
line = line.replace('">', '" xmlns:ifc="http://www.ifcopenshell.org/ns">')
if "</svg>" in line:
for i, line in enumerate(infile):
if i < 2:
continue
elif "</svg>" in line:
continue
outfile.write(line)
shutil.copyfile(underlay[0:-4] + ".png", svg_path[0:-4] + "-underlay.png")
has_boilerplate = True
if linework:
with open(linework) as infile:
should_skip = False
for i, line in enumerate(infile):
if has_boilerplate and i == 0:
if i == 0:
continue
if "</svg>" in line:
continue
@@ -174,11 +197,10 @@ class CreateDrawing(bpy.types.Operator):
elif should_skip:
continue
outfile.write(line)
has_boilerplate = True
if annotation:
with open(annotation) as infile:
for i, line in enumerate(infile):
if has_boilerplate and i in [0, 1]:
if i < 2:
continue
if "</svg>" in line:
continue
@@ -223,25 +245,7 @@ class CreateDrawing(bpy.types.Operator):
for name, value in previous_visibility.items():
bpy.data.objects[name].hide_set(value)
svg_writer = svgwriter.SvgWriter()
render = context.scene.render
if self.is_landscape(render):
width = self.camera.data.ortho_scale
height = width / render.resolution_x * render.resolution_y
else:
height = self.camera.data.ortho_scale
width = height / render.resolution_y * render.resolution_x
svg_writer.human_scale = self.human_scale
svg_writer.scale = self.scale
svg_writer.output = svg_path
svg_writer.data_dir = context.scene.BIMProperties.data_dir
svg_writer.vector_style = drawing_style.vector_style
svg_writer.camera = self.camera
svg_writer.camera_width = width
svg_writer.camera_height = height
svg_writer.camera_projection = tuple(self.camera.matrix_world.to_quaternion() @ Vector((0, 0, -1)))
svg_writer.background_image = context.scene.render.filepath
svg_writer.write("underlay")
self.svg_writer.create_blank_svg(svg_path).draw_underlay(context.scene.render.filepath).save()
return svg_path
def generate_linework(self, context):
@@ -415,35 +419,13 @@ class CreateDrawing(bpy.types.Operator):
if os.path.isfile(svg_path) and self.props.should_use_annotation_cache:
return svg_path
camera = self.camera
svg_writer = svgwriter.SvgWriter()
drawing_style = context.scene.DocProperties.drawing_styles[self.cprops.active_drawing_style_index]
render = context.scene.render
if self.is_landscape(render):
width = camera.data.ortho_scale
height = width / render.resolution_x * render.resolution_y
else:
height = camera.data.ortho_scale
width = height / render.resolution_y * render.resolution_x
svg_writer.human_scale = self.human_scale
svg_writer.scale = self.scale
svg_writer.output = svg_path
svg_writer.data_dir = context.scene.BIMProperties.data_dir
svg_writer.vector_style = drawing_style.vector_style
svg_writer.camera = camera
svg_writer.camera_width = width
svg_writer.camera_height = height
svg_writer.camera_projection = tuple(camera.matrix_world.to_quaternion() @ Vector((0, 0, -1)))
elements = tool.Drawing.get_group_elements(tool.Drawing.get_drawing_group(self.camera_element))
svg_writer.annotations = sorted(elements, key=lambda a: tool.Drawing.get_annotation_z_index(a))
svg_writer.metadata = tool.Drawing.get_drawing_metadata(self.camera_element)
annotations = sorted(elements, key=lambda a: tool.Drawing.get_annotation_z_index(a))
svg_writer.write("annotation")
return svg_writer.output
self.svg_writer.metadata = tool.Drawing.get_drawing_metadata(self.camera_element)
self.svg_writer.create_blank_svg(svg_path).draw_annotations(annotations).save()
return svg_path
def get_scale(self):
if self.camera.data.BIMCameraProperties.diagram_scale == "CUSTOM":
@@ -36,14 +36,6 @@ from mathutils import geometry
from blenderbim.bim.ifc import IfcStore
def load_occ():
# Don't import until we really need to, as a temporary step before we can purge OCC
try:
from OCC.Core import BRep, BRepTools, TopExp, TopAbs
except ImportError:
from OCC import BRep, BRepTools, TopExp, TopAbs
class External(svgwrite.container.Group):
def __init__(self, xml, **extra):
self.xml = xml
@@ -63,87 +55,76 @@ class External(svgwrite.container.Group):
class SvgWriter:
def __init__(self):
self.output = "out.svg"
self.data_dir = None
self.vector_style = None
self.human_scale = "NTS"
self.annotations = []
self.metadata = []
self.background_image = None
self.scale = 1 / 100 # 1:100
self.camera_width = None
self.camera_height = None
def write(self, layer):
def create_blank_svg(self, output_path):
self.calculate_scale()
self.svg = svgwrite.Drawing(
self.output,
debug=False,
output_path,
size=("{}mm".format(self.width), "{}mm".format(self.height)),
viewBox=("0 0 {} {}".format(self.width, self.height)),
id="root",
data_scale=self.human_scale,
debug=False, # Disable validation so that we can insert the IFC namespace
)
self.svg.attribs["xmlns:ifc"] = "http://www.ifcopenshell.org/ns"
return self
if layer == "underlay":
self.draw_background_image()
elif layer == "annotation":
self.add_stylesheet()
self.add_markers()
self.add_symbols()
self.add_patterns()
# self.draw_background_elements()
# self.draw_cut_polygons()
self.draw_annotations()
def save(self):
self.svg.save(pretty=True)
def calculate_scale(self):
self.scale *= 1000 # IFC is in meters, SVG is in mm
self.raw_width = self.camera_width
self.raw_height = self.camera_height
self.width = self.raw_width * self.scale
self.height = self.raw_height * self.scale
def add_stylesheet(self):
with open(os.path.join(self.data_dir, "styles", f"{self.vector_style}.css"), "r") as stylesheet:
self.svg.defs.add(self.svg.style(stylesheet.read()))
def add_markers(self):
tree = ET.parse(os.path.join(self.data_dir, "templates", "markers.svg"))
root = tree.getroot()
for child in root:
self.svg.defs.add(External(child))
def add_symbols(self):
tree = ET.parse(os.path.join(self.data_dir, "templates", "symbols.svg"))
root = tree.getroot()
for child in root:
self.svg.defs.add(External(child))
def add_patterns(self):
tree = ET.parse(os.path.join(self.data_dir, "templates", "patterns.svg"))
root = tree.getroot()
for child in root:
self.svg.defs.add(External(child))
def draw_background_image(self):
def draw_underlay(self, image):
self.svg.add(
self.svg.image(
os.path.join("..", "diagrams", os.path.basename(self.background_image)),
**{"width": self.width, "height": self.height},
os.path.join("..", "diagrams", os.path.basename(image)), width=self.width, height=self.height,
)
)
return self
def draw_background_elements(self):
return # TODO purge?
for element in self.ifc_cutter.background_elements:
if element["type"] == "polygon":
self.draw_polygon(element, "background")
elif element["type"] == "polyline":
self.draw_polyline(element, "background")
elif element["type"] == "line":
self.draw_line(element, "background")
def define_boilerplate(self, stylesheet=None, markers=None, symbols=None, patterns=None):
self.add_stylesheet(stylesheet)
self.add_markers(markers)
self.add_symbols(symbols)
self.add_patterns(patterns)
return self
def draw_annotations(self):
for element in self.annotations:
def calculate_scale(self):
self.svg_scale = self.scale * 1000 # IFC is in meters, SVG is in mm
self.raw_width = self.camera_width
self.raw_height = self.camera_height
self.width = self.raw_width * self.svg_scale
self.height = self.raw_height * self.svg_scale
def add_stylesheet(self, uri):
default_stylesheet = os.path.join(self.data_dir, "styles", f"default.css")
with open(tool.Ifc.resolve_uri(uri) or default_stylesheet, "r") as stylesheet:
self.svg.defs.add(self.svg.style(stylesheet.read()))
def add_markers(self, uri):
tree = ET.parse(tool.Ifc.resolve_uri(uri) or os.path.join(self.data_dir, "templates", "markers.svg"))
root = tree.getroot()
for child in root:
self.svg.defs.add(External(child))
def add_symbols(self, uri):
tree = ET.parse(tool.Ifc.resolve_uri(uri) or os.path.join(self.data_dir, "templates", "symbols.svg"))
root = tree.getroot()
for child in root:
self.svg.defs.add(External(child))
def add_patterns(self, uri):
tree = ET.parse(tool.Ifc.resolve_uri(uri) or os.path.join(self.data_dir, "templates", "patterns.svg"))
root = tree.getroot()
for child in root:
self.svg.defs.add(External(child))
def draw_annotations(self, annotations):
for element in annotations:
obj = tool.Ifc.get_object(element)
if not obj or element.ObjectType == "DRAWING":
continue
@@ -180,6 +161,7 @@ class SvgWriter:
# Experimental integration with the MeasureIt-ARCH Add-on
self.draw_measureit_arch_dimension_annotations()
return self
def draw_section_level_annotation(self, obj):
x_offset = self.raw_width / 2
@@ -194,7 +176,7 @@ class SvgWriter:
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
d = " ".join(
[
"L {} {}".format((x_offset + p.x) * self.scale, (y_offset - p.y) * self.scale)
"L {} {}".format((x_offset + p.x) * self.svg_scale, (y_offset - p.y) * self.svg_scale)
for p in projected_points
]
)
@@ -202,8 +184,8 @@ class SvgWriter:
path = self.svg.add(self.svg.path(d=d, class_=" ".join(classes)))
text_position = Vector(
(
(x_offset + projected_points[0].x) * self.scale,
((y_offset - projected_points[0].y) * self.scale) - 3.5,
(x_offset + projected_points[0].x) * self.svg_scale,
((y_offset - projected_points[0].y) * self.svg_scale) - 3.5,
)
)
# TODO: allow metric to be configurable
@@ -233,14 +215,14 @@ class SvgWriter:
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
d = " ".join(
[
"L {} {}".format((x_offset + p.x) * self.scale, (y_offset - p.y) * self.scale)
"L {} {}".format((x_offset + p.x) * self.svg_scale, (y_offset - p.y) * self.svg_scale)
for p in projected_points
]
)
d = "M{}".format(d[1:])
start = Vector(((x_offset + projected_points[0].x), (y_offset - projected_points[0].y)))
next_point = Vector(((x_offset + projected_points[1].x), (y_offset - projected_points[1].y)))
text_position = (start * self.scale) - ((next_point - start).normalized() * 5)
text_position = (start * self.svg_scale) - ((next_point - start).normalized() * 5)
path = self.svg.add(self.svg.path(d=d, class_=" ".join(classes)))
self.svg.add(
self.svg.text(
@@ -270,14 +252,14 @@ class SvgWriter:
end = Vector(((x_offset + v1.x), (y_offset - v1.y)))
vector = end - start
line = self.svg.add(
self.svg.line(start=tuple(start * self.scale), end=tuple(end * self.scale), class_=" ".join(classes))
self.svg.line(start=tuple(start * self.svg_scale), end=tuple(end * self.svg_scale), class_=" ".join(classes))
)
line["stroke-dasharray"] = "12.5, 3, 3, 3"
axis_tag = tool.Ifc.get_entity(obj).Name
self.svg.add(
self.svg.text(
axis_tag,
insert=tuple(start * self.scale),
insert=tuple(start * self.svg_scale),
**{
"font-size": annotation.Annotator.get_svg_text_size(5.0),
"font-family": "OpenGost Type B TT",
@@ -290,7 +272,7 @@ class SvgWriter:
self.svg.add(
self.svg.text(
axis_tag,
insert=tuple(end * self.scale),
insert=tuple(end * self.svg_scale),
**{
"font-size": annotation.Annotator.get_svg_text_size(5.0),
"font-family": "OpenGost Type B TT",
@@ -320,7 +302,7 @@ class SvgWriter:
projected_points.append(projected_points[0])
d = " ".join(
[
"L {} {}".format((x_offset + p.x) * self.scale, (y_offset - p.y) * self.scale)
"L {} {}".format((x_offset + p.x) * self.svg_scale, (y_offset - p.y) * self.svg_scale)
for p in projected_points
]
)
@@ -357,7 +339,7 @@ class SvgWriter:
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
d = " ".join(
[
"L {} {}".format((x_offset + p.x) * self.scale, (y_offset - p.y) * self.scale)
"L {} {}".format((x_offset + p.x) * self.svg_scale, (y_offset - p.y) * self.svg_scale)
for p in projected_points
]
)
@@ -379,7 +361,7 @@ class SvgWriter:
end = Vector(((x_offset + v1.x), (y_offset - v1.y)))
vector = end - start
line = self.svg.add(
self.svg.line(start=tuple(start * self.scale), end=tuple(end * self.scale), class_=" ".join(classes))
self.svg.line(start=tuple(start * self.svg_scale), end=tuple(end * self.svg_scale), class_=" ".join(classes))
)
def draw_leader_annotation(self, obj):
@@ -408,17 +390,17 @@ class SvgWriter:
symbol_position = Vector(((x_offset + symbol_position.x), (y_offset - symbol_position.y)))
transform = "rotate({}, {}, {})".format(
angle,
(symbol_position * self.scale)[0],
(symbol_position * self.scale)[1],
(symbol_position * self.svg_scale)[0],
(symbol_position * self.svg_scale)[1],
)
self.svg.add(
self.svg.use("#section-arrow", insert=tuple(symbol_position * self.scale), transform=transform)
self.svg.use("#section-arrow", insert=tuple(symbol_position * self.svg_scale), transform=transform)
)
self.svg.add(self.svg.use("#section-tag", insert=tuple(symbol_position * self.scale)))
self.svg.add(self.svg.use("#section-tag", insert=tuple(symbol_position * self.svg_scale)))
reference_id, sheet_id = self.get_reference_and_sheet_id_from_annotation(tool.Ifc.get_entity(obj))
text_position = list(symbol_position * self.scale)
text_position = list(symbol_position * self.svg_scale)
text_style = {
"font-size": annotation.Annotator.get_svg_text_size(2.5),
"font-family": "OpenGost Type B TT",
@@ -441,15 +423,15 @@ class SvgWriter:
transform = "rotate({}, {}, {})".format(
angle,
(symbol_position * self.scale)[0],
(symbol_position * self.scale)[1],
(symbol_position * self.svg_scale)[0],
(symbol_position * self.svg_scale)[1],
)
self.svg.add(self.svg.use("#elevation-arrow", insert=tuple(symbol_position * self.scale), transform=transform))
self.svg.add(self.svg.use("#elevation-tag", insert=tuple(symbol_position * self.scale)))
self.svg.add(self.svg.use("#elevation-arrow", insert=tuple(symbol_position * self.svg_scale), transform=transform))
self.svg.add(self.svg.use("#elevation-tag", insert=tuple(symbol_position * self.svg_scale)))
reference_id, sheet_id = self.get_reference_and_sheet_id_from_annotation(tool.Ifc.get_entity(obj))
text_position = list(symbol_position * self.scale)
text_position = list(symbol_position * self.svg_scale)
text_style = {
"font-size": annotation.Annotator.get_svg_text_size(2.5),
"font-family": "OpenGost Type B TT",
@@ -497,13 +479,13 @@ class SvgWriter:
transform = "rotate({}, {}, {})".format(
angle,
(text_position * self.scale)[0],
(text_position * self.scale)[1],
(text_position * self.svg_scale)[0],
(text_position * self.svg_scale)[1],
)
symbol = tool.Drawing.get_annotation_symbol(element)
if symbol:
self.svg.add(self.svg.use(f"#{symbol}", insert=tuple(text_position * self.scale)))
self.svg.add(self.svg.use(f"#{symbol}", insert=tuple(text_position * self.svg_scale)))
if text_literal.BoxAlignment == "top-left":
alignment_baseline = "hanging"
@@ -535,7 +517,7 @@ class SvgWriter:
literal = text_literal.Literal
product = tool.Drawing.get_text_product(element)
product = tool.Drawing.get_assigned_product(element)
selector = ifcopenshell.util.selector.Selector
variables = {}
for variable in re.findall("{{.*?}}", literal):
@@ -545,7 +527,7 @@ class SvgWriter:
self.svg.add(
self.svg.text(
text_line,
insert=tuple((text_position * self.scale) + Vector((0, 3.5 * line_number))),
insert=tuple((text_position * self.svg_scale) + Vector((0, 3.5 * line_number))),
class_=" ".join(self.get_attribute_classes(text_obj)),
**{
"font-size": annotation.Annotator.get_svg_text_size(text_obj.BIMTextProperties.font_size),
@@ -574,7 +556,7 @@ class SvgWriter:
]
d = " ".join(
[
"L {} {}".format((x_offset + p.x) * self.scale, (y_offset - p.y) * self.scale)
"L {} {}".format((x_offset + p.x) * self.svg_scale, (y_offset - p.y) * self.svg_scale)
for p in projected_points
]
)
@@ -591,7 +573,7 @@ class SvgWriter:
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
d = " ".join(
[
"L {} {}".format((x_offset + p.x) * self.scale, (y_offset - p.y) * self.scale)
"L {} {}".format((x_offset + p.x) * self.svg_scale, (y_offset - p.y) * self.svg_scale)
for p in projected_points
]
)
@@ -599,8 +581,8 @@ class SvgWriter:
path = self.svg.add(self.svg.path(d=d, class_=" ".join(classes)))
text_position = Vector(
(
(x_offset + projected_points[0].x) * self.scale,
((y_offset - projected_points[0].y) * self.scale) - 2.5,
(x_offset + projected_points[0].x) * self.svg_scale,
((y_offset - projected_points[0].y) * self.svg_scale) - 2.5,
)
)
# TODO: allow metric to be configurable
@@ -698,11 +680,11 @@ class SvgWriter:
arc_midpoint = center + ((cog - center).normalized() * radius)
text_position = self.project_point_onto_camera(arc_midpoint)
text_position = Vector(((x_offset + text_position.x) * self.scale, (y_offset - text_position.y) * self.scale))
text_position = Vector(((x_offset + text_position.x) * self.svg_scale, (y_offset - text_position.y) * self.svg_scale))
center_projected = self.project_point_onto_camera(center)
center_position = Vector(
((x_offset + center_projected.x) * self.scale, (y_offset - center_projected.y) * self.scale)
((x_offset + center_projected.x) * self.svg_scale, (y_offset - center_projected.y) * self.svg_scale)
)
text_offset = (text_position - center_position).xy.normalized() * 5
text_position += text_offset
@@ -722,9 +704,9 @@ class SvgWriter:
# Draw SVG arc, see for details: http://xahlee.info/js/svg_circle_arc.html
arc_proj_end_pts = [self.project_point_onto_camera(v) for v in arc_end_pts]
p1 = Vector(((x_offset + arc_proj_end_pts[0].x) * self.scale, (y_offset - arc_proj_end_pts[0].y) * self.scale))
p2 = Vector(((x_offset + arc_proj_end_pts[1].x) * self.scale, (y_offset - arc_proj_end_pts[1].y) * self.scale))
r = radius * self.scale
p1 = Vector(((x_offset + arc_proj_end_pts[0].x) * self.svg_scale, (y_offset - arc_proj_end_pts[0].y) * self.svg_scale))
p2 = Vector(((x_offset + arc_proj_end_pts[1].x) * self.svg_scale, (y_offset - arc_proj_end_pts[1].y) * self.svg_scale))
r = radius * self.svg_scale
# reflex = 1 if angle > math.pi else 0
reflex = is_reflex
if reflex:
@@ -745,7 +727,7 @@ class SvgWriter:
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
d = " ".join(
[
"L {} {}".format((x_offset + p.x) * self.scale, (y_offset - p.y) * self.scale)
"L {} {}".format((x_offset + p.x) * self.svg_scale, (y_offset - p.y) * self.svg_scale)
for p in projected_points
]
)
@@ -753,15 +735,15 @@ class SvgWriter:
path = self.svg.add(self.svg.path(d=d, class_=" ".join(classes)))
p0 = Vector(
((x_offset + projected_points[0].x) * self.scale, (y_offset - projected_points[0].y) * self.scale)
((x_offset + projected_points[0].x) * self.svg_scale, (y_offset - projected_points[0].y) * self.svg_scale)
)
p1 = Vector(
((x_offset + projected_points[1].x) * self.scale, (y_offset - projected_points[1].y) * self.scale)
((x_offset + projected_points[1].x) * self.svg_scale, (y_offset - projected_points[1].y) * self.svg_scale)
)
text_offset = (p0 - p1).xy.normalized() * 5
text_position = projected_points[0]
text_position = Vector(
((x_offset + text_position.x) * self.scale, (y_offset - text_position.y) * self.scale)
((x_offset + text_position.x) * self.svg_scale, (y_offset - text_position.y) * self.svg_scale)
)
text_position += text_offset
@@ -833,15 +815,15 @@ class SvgWriter:
perpendicular = Vector((vector.y, -vector.x)).normalized()
dimension = (v1_global - v0_global).length
dimension = helper.format_distance(dimension)
sheet_dimension = ((end * self.scale) - (start * self.scale)).length
sheet_dimension = ((end * self.svg_scale) - (start * self.svg_scale)).length
if sheet_dimension < 5: # annotation can't fit
# offset text to right of marker
text_position = (end * self.scale) + perpendicular + (3 * vector.normalized())
text_position = (end * self.svg_scale) + perpendicular + (3 * vector.normalized())
else:
text_position = (mid * self.scale) + perpendicular
text_position = (mid * self.svg_scale) + perpendicular
rotation = math.degrees(vector.angle_signed(Vector((1, 0))))
line = self.svg.add(
self.svg.line(start=tuple(start * self.scale), end=tuple(end * self.scale), class_=" ".join(classes))
self.svg.line(start=tuple(start * self.svg_scale), end=tuple(end * self.svg_scale), class_=" ".join(classes))
)
if text_override is not None:
text = text_override
@@ -873,35 +855,8 @@ class SvgWriter:
def get_spline_points(self, spline):
return spline.bezier_points if spline.bezier_points else spline.points
def draw_cut_polygons(self):
return # deprecate?
for polygon in self.ifc_cutter.cut_polygons:
self.draw_polygon(polygon, "cut")
def draw_polyline(self, element, position):
load_occ()
classes = self.get_classes(element["raw"], position)
exp = BRepTools.BRepTools_WireExplorer(element["geometry"])
points = []
while exp.More():
point = BRep.BRep_Tool.Pnt(exp.CurrentVertex())
points.append((point.X() * self.scale, -point.Y() * self.scale))
exp.Next()
self.svg.add(self.svg.polyline(points=points, class_=" ".join(classes)))
def draw_line(self, element, position):
load_occ()
classes = self.get_classes(element["raw"], position)
exp = TopExp.TopExp_Explorer(element["geometry"], TopAbs.TopAbs_VERTEX)
points = []
while exp.More():
point = BRep.BRep_Tool.Pnt(topods.Vertex(exp.Current()))
points.append((point.X() * self.scale, -point.Y() * self.scale))
exp.Next()
self.svg.add(self.svg.line(start=points[0], end=points[1], class_=" ".join(classes)))
def draw_polygon(self, polygon, position):
points = [(p[0] * self.scale, p[1] * self.scale) for p in polygon["points"]]
points = [(p[0] * self.svg_scale, p[1] * self.svg_scale) for p in polygon["points"]]
if "classes" in polygon["metadata"]:
classes = " ".join(polygon["metadata"]["classes"])
else:
@@ -28,6 +28,7 @@ classes = (
operator.AddProfile,
operator.AssignMaterial,
operator.AssignParameterizedProfile,
operator.ContractMaterialCategory,
operator.CopyMaterial,
operator.DisableEditingAssignedMaterial,
operator.DisableEditingMaterialSetItem,
@@ -36,6 +37,7 @@ classes = (
operator.EditMaterialSetItem,
operator.EnableEditingAssignedMaterial,
operator.EnableEditingMaterialSetItem,
operator.ExpandMaterialCategory,
operator.LoadMaterials,
operator.RemoveConstituent,
operator.RemoveLayer,
@@ -78,9 +78,24 @@ class ObjectMaterialData:
def load(cls):
cls.data = {
"materials": cls.materials(),
"type_material": cls.type_material(),
}
cls.is_loaded = True
@classmethod
def materials(cls):
return sorted([(str(m.id()), m.Name or "Unnamed", "") for m in tool.Ifc.get().by_type("IfcMaterial")], key=lambda x: x[1])
@classmethod
def type_material(cls):
element = tool.Ifc.get_entity(bpy.context.active_object)
element_type = ifcopenshell.util.element.get_type(element)
if element_type and element_type != element:
material = ifcopenshell.util.element.get_material(element_type)
if not material:
return
if material.is_a() in ("IfcMaterialLayerSetUsage", "IfcMaterialLayerSet"):
name_attr = "LayerSetName"
else:
name_attr = "Name"
return getattr(material, name_attr, "Unnamed") or "Unnamed"
@@ -778,3 +778,31 @@ class CopyMaterial(bpy.types.Operator):
if material.id() in object_material_ids:
return
obj.data.materials.append(IfcStore.get_element(material.id()))
class ExpandMaterialCategory(bpy.types.Operator):
bl_idname = "bim.expand_material_category"
bl_label = "Expand Material Category"
bl_options = {"REGISTER", "UNDO"}
category: bpy.props.StringProperty()
def execute(self, context):
props = context.scene.BIMMaterialProperties
for category in [c for c in props.materials if c.is_category and c.name == self.category]:
category.is_expanded = True
core.load_materials(tool.Material, props.material_type)
return {"FINISHED"}
class ContractMaterialCategory(bpy.types.Operator):
bl_idname = "bim.contract_material_category"
bl_label = "Contract Material Category"
bl_options = {"REGISTER", "UNDO"}
category: bpy.props.StringProperty()
def execute(self, context):
props = context.scene.BIMMaterialProperties
for category in [c for c in props.materials if c.is_category and c.name == self.category]:
category.is_expanded = False
core.load_materials(tool.Material, props.material_type)
return {"FINISHED"}
@@ -109,6 +109,8 @@ def get_material_types(self, context):
class Material(PropertyGroup):
name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID")
is_category: BoolProperty(name="Is Category", default=False)
is_expanded: BoolProperty(name="Is Expanded", default=True)
total_elements: IntProperty(name="Total Elements")
@@ -61,16 +61,18 @@ class BIM_PT_materials(Panel):
row.operator("bim.add_material", text="", icon="ADD")
if self.props.materials and self.props.active_material_index < len(self.props.materials):
material = self.props.materials[self.props.active_material_index]
op = row.operator("bim.select_by_material", text="", icon="RESTRICT_SELECT_OFF")
op.material = material.ifc_definition_id
row.operator("bim.remove_material", text="", icon="X").material = material.ifc_definition_id
if material.ifc_definition_id:
op = row.operator("bim.select_by_material", text="", icon="RESTRICT_SELECT_OFF")
op.material = material.ifc_definition_id
row.operator("bim.remove_material", text="", icon="X").material = material.ifc_definition_id
else:
row.operator("bim.add_material_set", text="", icon="ADD").set_type = self.props.material_type
if self.props.materials and self.props.active_material_index < len(self.props.materials):
material = self.props.materials[self.props.active_material_index]
op = row.operator("bim.select_by_material", text="", icon="RESTRICT_SELECT_OFF")
op.material = material.ifc_definition_id
row.operator("bim.remove_material_set", text="", icon="X").material = material.ifc_definition_id
if material.ifc_definition_id:
op = row.operator("bim.select_by_material", text="", icon="RESTRICT_SELECT_OFF")
op.material = material.ifc_definition_id
row.operator("bim.remove_material_set", text="", icon="X").material = material.ifc_definition_id
self.layout.template_list("BIM_UL_materials", "", self.props, "materials", self.props, "active_material_index")
@@ -139,6 +141,10 @@ class BIM_PT_object_material(Panel):
row.operator("bim.add_material", icon="ADD", text="").obj = ""
return
if ObjectMaterialData.data["type_material"]:
row = self.layout.row(align=True)
row.label(text="Inherited Material: " + ObjectMaterialData.data["type_material"], icon="FILE_PARENT")
if self.product_data:
if self.product_data["type"] == "IfcMaterialConstituentSet":
self.material_set_id = self.product_data["id"]
@@ -395,7 +401,21 @@ class BIM_UL_materials(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
row = layout.row(align=True)
row.label(text=item.name)
row2 = row.row()
row2.alignment = "RIGHT"
row2.label(text=str(item.total_elements))
if item.is_category:
if item.is_expanded:
row.operator(
"bim.contract_material_category", text="", emboss=False, icon="DISCLOSURE_TRI_DOWN"
).category = item.name
else:
row.operator(
"bim.expand_material_category", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT"
).category = item.name
row.label(text=item.name or "Uncategorised")
else:
row.label(text="", icon="BLANK1")
row.label(text=item.name, icon="MATERIAL")
row2 = row.row()
row2.alignment = "RIGHT"
row2.label(text=str(item.total_elements))
@@ -148,6 +148,7 @@ class EnablePsetEditing(bpy.types.Operator):
new.is_null = data.get(prop_template.Name, None) is None
new.is_optional = True
new.data_type = data_type
new.is_uri = prop_template.PrimaryMeasureType == "IfcURIReference"
if data_type == "string":
new.string_value = "" if new.is_null else data[prop_template.Name]
@@ -147,7 +147,7 @@ class BIM_PT_object_psets(Panel):
draw_psetqto_ui(context, pset["id"], pset, props, self.layout, "Object")
if ObjectPsetsData.data["inherited_psets"]:
self.layout.label(text="Inherited Psets:")
self.layout.label(text="Inherited Psets:", icon="FILE_PARENT")
for pset in ObjectPsetsData.data["inherited_psets"]:
draw_psetqto_ui(context, pset["id"], pset, props, self.layout, "Object")
@@ -27,6 +27,7 @@ classes = (
operator.EnableEditingStyle,
operator.LoadStyles,
operator.RemoveStyle,
operator.SelectByStyle,
operator.UnlinkStyle,
operator.UpdateStyleColours,
operator.UpdateStyleTextures,
@@ -24,14 +24,7 @@ import ifcopenshell.util.representation
from blenderbim.bim.ifc import IfcStore
class Operator:
def execute(self, context):
IfcStore.execute_ifc_operator(self, context)
blenderbim.bim.handler.refresh_ui_data()
return {"FINISHED"}
class UpdateStyleColours(bpy.types.Operator, Operator):
class UpdateStyleColours(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.update_style_colours"
bl_label = "Update Style Colours"
bl_options = {"REGISTER", "UNDO"}
@@ -40,7 +33,7 @@ class UpdateStyleColours(bpy.types.Operator, Operator):
core.update_style_colours(tool.Ifc, tool.Style, obj=context.active_object.active_material)
class UpdateStyleTextures(bpy.types.Operator, Operator):
class UpdateStyleTextures(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.update_style_textures"
bl_label = "Update Style Textures"
bl_options = {"REGISTER", "UNDO"}
@@ -55,7 +48,7 @@ class UpdateStyleTextures(bpy.types.Operator, Operator):
)
class RemoveStyle(bpy.types.Operator, Operator):
class RemoveStyle(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.remove_style"
bl_label = "Remove Style"
bl_options = {"REGISTER", "UNDO"}
@@ -65,7 +58,7 @@ class RemoveStyle(bpy.types.Operator, Operator):
core.remove_style(tool.Ifc, tool.Material, tool.Style, style=tool.Ifc.get().by_id(self.style))
class AddStyle(bpy.types.Operator, Operator):
class AddStyle(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_style"
bl_label = "Add Style"
bl_options = {"REGISTER", "UNDO"}
@@ -74,7 +67,7 @@ class AddStyle(bpy.types.Operator, Operator):
core.add_style(tool.Ifc, tool.Style, obj=context.active_object.active_material)
class UnlinkStyle(bpy.types.Operator, Operator):
class UnlinkStyle(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.unlink_style"
bl_label = "Unlink Style"
bl_options = {"REGISTER", "UNDO"}
@@ -83,7 +76,7 @@ class UnlinkStyle(bpy.types.Operator, Operator):
core.unlink_style(tool.Ifc, tool.Style, obj=context.active_object.active_material)
class EnableEditingStyle(bpy.types.Operator, Operator):
class EnableEditingStyle(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.enable_editing_style"
bl_label = "Enable Editing Style"
bl_options = {"REGISTER", "UNDO"}
@@ -92,7 +85,7 @@ class EnableEditingStyle(bpy.types.Operator, Operator):
core.enable_editing_style(tool.Style, obj=context.active_object.active_material)
class DisableEditingStyle(bpy.types.Operator, Operator):
class DisableEditingStyle(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.disable_editing_style"
bl_options = {"REGISTER", "UNDO"}
bl_label = "Disable Editing Style"
@@ -101,7 +94,7 @@ class DisableEditingStyle(bpy.types.Operator, Operator):
core.disable_editing_style(tool.Style, obj=context.active_object.active_material)
class EditStyle(bpy.types.Operator, Operator):
class EditStyle(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.edit_style"
bl_label = "Edit Style"
bl_options = {"REGISTER", "UNDO"}
@@ -110,7 +103,7 @@ class EditStyle(bpy.types.Operator, Operator):
core.edit_style(tool.Ifc, tool.Style, obj=context.active_object.active_material)
class DisableEditingStyles(bpy.types.Operator, Operator):
class DisableEditingStyles(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.disable_editing_styles"
bl_options = {"REGISTER", "UNDO"}
bl_label = "Disable Editing Styles"
@@ -119,7 +112,7 @@ class DisableEditingStyles(bpy.types.Operator, Operator):
core.disable_editing_styles(tool.Style)
class LoadStyles(bpy.types.Operator, Operator):
class LoadStyles(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.load_styles"
bl_label = "Load Styles"
bl_options = {"REGISTER", "UNDO"}
@@ -127,3 +120,13 @@ class LoadStyles(bpy.types.Operator, Operator):
def _execute(self, context):
core.load_styles(tool.Style, style_type=self.style_type)
class SelectByStyle(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.select_by_style"
bl_label = "Select By Material"
bl_options = {"REGISTER", "UNDO"}
style: bpy.props.IntProperty()
def _execute(self, context):
core.select_by_style(tool.Style, style=tool.Ifc.get().by_id(self.style))
@@ -57,8 +57,8 @@ class BIM_PT_styles(Panel):
#row.operator("bim.add_presentation_style", text="", icon="ADD")
if self.props.styles and self.props.active_style_index < len(self.props.styles):
style = self.props.styles[self.props.active_style_index]
# op = row.operator("bim.select_by_style", text="", icon="RESTRICT_SELECT_OFF")
# op.style = style.ifc_definition_id
op = row.operator("bim.select_by_style", text="", icon="RESTRICT_SELECT_OFF")
op.style = style.ifc_definition_id
row.operator("bim.remove_style", text="", icon="X").style = style.ifc_definition_id
self.layout.template_list("BIM_UL_styles", "", self.props, "styles", self.props, "active_style_index")
@@ -138,6 +138,6 @@ class BIM_UL_styles(UIList):
if item:
row = layout.row(align=True)
row.label(text=item.name)
#row2 = row.row()
#row2.alignment = "RIGHT"
#row2.label(text=str(item.total_elements))
row2 = row.row()
row2.alignment = "RIGHT"
row2.label(text=str(item.total_elements))
+1 -1
View File
@@ -192,7 +192,7 @@ def add_annotation(ifc, collector, drawing_tool, drawing=None, object_type=None)
if not context:
return
drawing_tool.show_decorations()
obj = drawing_tool.create_annotation_object(object_type)
obj = drawing_tool.create_annotation_object(drawing, object_type)
element = ifc.get_entity(obj)
if not element:
element = drawing_tool.run_root_assign_class(
+4
View File
@@ -110,3 +110,7 @@ def load_styles(style, style_type=None):
def disable_editing_styles(style):
style.disable_editing_styles()
def select_by_style(style_tool, style=None):
style_tool.select_elements(style_tool.get_elements_by_style(style))
+3 -1
View File
@@ -154,7 +154,7 @@ class Document:
@interface
class Drawing:
def create_annotation_object(cls, object_type): pass
def create_annotation_object(cls, drawing, object_type): pass
def create_camera(cls, name, matrix): pass
def create_svg_schedule(cls, schedule): pass
def create_svg_sheet(cls, document, titleblock): pass
@@ -443,6 +443,7 @@ class Style:
def export_surface_attributes(cls, obj): pass
def get_active_style_type(cls): pass
def get_context(cls, obj): pass
def get_elements_by_style(cls, style): pass
def get_name(cls, obj): pass
def get_style(cls, obj): pass
def get_surface_rendering_attributes(cls, obj): pass
@@ -454,6 +455,7 @@ class Style:
def import_presentation_styles(cls, style_type): pass
def import_surface_attributes(cls, style, obj): pass
def is_editing_styles(cls): pass
def select_elements(cls, elements): pass
@interface
+2 -2
View File
@@ -36,7 +36,7 @@ import blenderbim.bim.module.drawing.helper as helper
class Drawing(blenderbim.core.tool.Drawing):
@classmethod
def create_annotation_object(cls, object_type):
def create_annotation_object(cls, drawing, object_type):
data_type = {
"ANGLE": "mesh",
"BREAKLINE": "mesh",
@@ -52,7 +52,7 @@ class Drawing(blenderbim.core.tool.Drawing):
"TEXT": "empty",
"TEXT_LEADER": "curve",
}[object_type]
obj = annotation.Annotator.get_annotation_obj(object_type, data_type)
obj = annotation.Annotator.get_annotation_obj(drawing, object_type, data_type)
if object_type == "FILL_AREA":
obj = annotation.Annotator.add_plane_to_annotation(obj)
elif object_type != "TEXT":
+5
View File
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import os
import bpy
import numpy as np
import ifcopenshell.api
@@ -91,6 +92,10 @@ class Ifc(blenderbim.core.tool.Ifc):
def delete(cls, element):
IfcStore.delete_element(element)
@classmethod
def resolve_uri(cls, uri):
return uri if not uri or os.path.isabs(uri) else os.path.join(os.path.dirname(cls.get_path()), uri)
@classmethod
def unlink(cls, element=None, obj=None):
IfcStore.unlink_element(element, obj)
+17 -2
View File
@@ -46,7 +46,7 @@ class Material(blenderbim.core.tool.Material):
@classmethod
def get_elements_by_material(cls, material):
return set(ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material))
return ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material)
@classmethod
def get_name(cls, obj):
@@ -55,6 +55,7 @@ class Material(blenderbim.core.tool.Material):
@classmethod
def import_material_definitions(cls, material_type):
props = bpy.context.scene.BIMMaterialProperties
expanded_categories = {m.name for m in props.materials if m.is_expanded}
props.materials.clear()
get_name = lambda x: x.Name or "Unnamed"
if material_type == "IfcMaterialLayerSet":
@@ -62,11 +63,25 @@ class Material(blenderbim.core.tool.Material):
elif material_type == "IfcMaterialList":
get_name = lambda x: "Unnamed"
materials = sorted(tool.Ifc.get().by_type(material_type), key=get_name)
categories = {}
if material_type == "IfcMaterial":
[categories.setdefault(m.Category, []).append(m) for m in materials]
for category, mats in categories.items():
cat = props.materials.add()
cat.name = category or ""
cat.is_category = True
cat.is_expanded = cat.name in expanded_categories
for material in mats if cat.is_expanded else []:
new = props.materials.add()
new.ifc_definition_id = material.id()
new.name = get_name(material)
new.total_elements = len(ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material))
return
for material in materials:
new = props.materials.add()
new.ifc_definition_id = material.id()
new.name = get_name(material)
new.total_elements = len(set(ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material)))
new.total_elements = len(ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material))
@classmethod
def is_editing_materials(cls):
+12
View File
@@ -56,6 +56,10 @@ class Style(blenderbim.core.tool.Style):
def get_context(cls, obj):
return ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
@classmethod
def get_elements_by_style(cls, style):
return ifcopenshell.util.element.get_elements_by_style(tool.Ifc.get(), style)
@classmethod
def get_name(cls, obj):
return obj.name
@@ -188,6 +192,7 @@ class Style(blenderbim.core.tool.Style):
new = props.styles.add()
new.ifc_definition_id = style.id()
new.name = style.Name or "Unnamed"
new.total_elements = len(ifcopenshell.util.element.get_elements_by_style(tool.Ifc.get(), style))
@classmethod
def import_surface_attributes(cls, style, obj):
@@ -197,3 +202,10 @@ class Style(blenderbim.core.tool.Style):
@classmethod
def is_editing_styles(cls):
return bpy.context.scene.BIMStylesProperties.is_editing
@classmethod
def select_elements(cls, elements):
for element in elements:
obj = tool.Ifc.get_object(element)
if obj:
obj.select_set(True)
+6 -4
View File
@@ -11,7 +11,7 @@ smoke tests. These tests use ``pytest`` as the test framework and runner.
All development is expected to use test driven development, and so we expect
test coverage to be 100% where it is technically possible to test.
When running tests, a Makefile target is provided for convenience so you can
When running tests, Makefile targets are provided for convenience so you can
type in a simple command without knowing the internals.
Because the BlenderBIM Add-on depends on IfcOpenShell, it is advised to also run
@@ -57,13 +57,14 @@ of all concrete functions.
.. code-block:: bash
$ cd src/blenderbim/
$ make test-tool
$ make test-tool # Test everything
$ make test-tool MODULE=foo # Only test a single module
BlenderBIM Add-on tests
-----------------------
The BIM layer acts as a full integration test. It is not possible to fully test
the UI, as we cannot reliable emit interface signals, nor read the interface as
the UI, as we cannot reliably emit interface signals, nor read the interface as
a DOM of sorts. The best we can do is to call Blender operators as a smoke test,
and also check simple property and scene changes. It has the same dependencies
as the tool tests.
@@ -71,4 +72,5 @@ as the tool tests.
.. code-block:: bash
$ cd src/blenderbim/
$ make test-bim
$ make test-bim # Test everything
$ make test-bim MODULE=foo # Only test a single module
@@ -29,7 +29,7 @@ The bottom right **Properties** panel shows data and relationships.
The **Properties** panel has tabs to switch between different types of
properties. Make sure you have the **Scene Properties** tab activated, and find
the **IFC Project** subpanel. Click on the **Load Project** and browse to your
the **IFC Project** subpanel. Click on **Load Project** and browse to your
``.ifc`` file.
.. image:: properties-loadproject.png
@@ -206,11 +206,17 @@ classification. In this case, it distinguishes our object as a roof slab,
compared to other types of slabs. The **Predefined Type** is optional so you may
not see it all the time.
.. seealso::
You can use the `Search IFC Class
<https://blenderbim.org/search-ifc-class.html>`__ tool to learn the correct
classes and predefined types you should see.
Press the **Select Icon** to select all objects that are of the same
**IfcSlab** **Class**. Then, you can isolate these elements by going to ``Object
> Show/Hide > Hide Unselected`` (hotkey ``Shift-H``). To show all elements again, you can use
``Object > Show/Hide > Show Hidden Objects`` (hotkey ``Alt-H``). If you want to
hide elements instead, you can use ``Object > Show/Hiden > Hide Selected``
hide elements instead, you can use ``Object > Show/Hidden > Hide Selected``
(hotkey ``H``).
.. image:: element-class-select.png
@@ -295,12 +301,149 @@ You can find **Quantities** in the **IFC Quantity Sets** subpanel.
.. image:: qtos.png
Viewing construction types
Finding the location of objects
-------------------------------
Every object in the built environment has a location in the world. For example,
a chair will be located in a space, and a wall is typically located in a
building storey. You've already seen this hierarchy of spaces in the
**Outliner** panel, where an IFC project is broken down into site, building,
storeys, and spaces.
Sometimes, objects may have multiple relevant locations, such
as a multi-storey column which can be related to multiple building storeys.
Even in these cases, IFC enforces one location to be its primary
location, known as its **Spatial Container**.
If you click on any object, you can see its location in the **IFC Spatial
Container** subpanel in the **Object Properties** tab.
Press the **Select Icon** to select all objects that are in the same location.
.. image:: spatial-container.png
Checking construction types
---------------------------
Almost everything in the built environment will have a **Construction Type**.
For example, an architect will specify a door type for every door in a project.
You can see a list of **Construction Types** in the **Outliner** panel in the
**Types** collection. For example, if the architect has a wall types schedule
with the wall type names of ``WT01``, ``WT02``, and ``WT03``, you should see
three **IfcWallType** objects with those same names in the **Outliner**.
You can click on these types to see more details about them in the **Object
Properties** panel.
.. image:: outliner-types.png
When selecting an object, you can also see its construction type in **Object
Properties** under the **IFC Construction Type** subpanel. You can press the
**Select Icon** to select all objects that are of the same **Construction
Type**. You can use the hide and isolate hotkeys to quickly view them in the
model.
.. image:: properties-types.png
A **Construction Type** defines properties that are common to all occurrences of
that type. For example, if a wall type specifies a fire rating property, then
all walls of that wall type will inherit that fire rating too.
A **Construction Type** may also specify geometry or geometric rules that are
common to all occurrences of the type. For example, a pump type will define the
geometry of the pump, so all occurrences of that pump will have the same
geometry.
You can visually inspect types in isolation to the rest of the model. Types are
hidden by default, so first enable the visibility of the **Types** collection in
the **Outliner** by pressing the **Visibility Icon**. Then, select a type, and
click on ``View > Local View > Toggle Local View`` (hotkey ``/``) in the
**Viewport**. Toggle the view to see the entire model again.
.. image:: type-local-view.png
.. note::
Only **Construction Types** where the geometry is exactly the same for all
occurrences will specify geometry. When the geometry varies based on the
occurrence (such as a wall, which varies based on the wall length), the
**Construction Type** will typically have no geometry.
Filtering by materials
----------------------
Everything in the built environment is made from a physical raw **Material**
resources. For example, a **Material** might be a 140mm core filled block.
Another **Material** might be a 190mm hollowcore block. **Materials** are
grouped into categories like steel, concrete, brick, block, and so on.
We can see a list of **Materials** used in the project in the **IFC Materials**
subpanel in the **Scene Properties** tab.
Press the **Select Icon** to select all objects that are of the selected
material.
.. image:: materials.png
Taking simple measurements
--------------------------
TODO
The simplest form of measurement is the one that's already taken for you. The
**Viewing attributes and properties** section describes how to view
pre-calculated **Quantities**.
Viewing materials
-----------------
Sometimes, you may wish to take manual measurements yourself. To view the
overall dimensions of an object, first select an object, then expand out the
viewport **Sidebar**.
TODO
.. image:: viewport-sidebar.png
In the **Item** tab, you can find the X, Y, and Z overall **Dimensions** of the
currently selected object.
.. image:: viewport-dimensions.png
Another way to manually measure from two points is to use the **Measure** tool.
First, press the **Snap Icon** to enable snapping. Then choose snap targets in
the **Snap Menu** in the top middle section of the **Viewport** panel.
.. image:: snap-targets.png
.. tip::
It is recommended to choose multiple snap targets, like **Vertex**, **Edge**, and
**Face**, and **Edge Center**. You can use the ``Shift`` key to select
multiple snap targets. For example, the **Face** snap target means that your
measurements will automatically snap to the nearest object's surface.
Now that you have configured snapping, press the **Measure Tool Icon** on the
left of the **Viewport** panel. **Click** and **Drag** in the 3D viewport to
take a measurement. A circle will appear guiding the first point of your
measurement. While **Dragging**, press the ``X`` key to lock the measurement
line along the X axis. Alternatively, press the ``Y`` or ``Z`` key to lock the
measurement line along the Y or Z axis. Let go of the mouse to finish your
measurement.
.. image:: measure-tool.png
To delete a measurement, just click on one point of the measurement, and press
the ``Delete`` key. You can also click and drag the ends of your measurement
lines to measure to another location.
What else is there?
-------------------
Congratulations, and welcome to the digital built environment!
We've barely scratched the surface of the data and relationships available in an
IFC model. We've yet to cover documents and drawings, clearance zones, tasks,
cost items, structural loads and forces, analytical models, distribution system
connectivity, energy analysis, rendering textures, and so much more. Our built
environment and its relationships are vast and complex and it is exciting that
you can join us on its digital journey!
Please do not hesitate to reach out with any questions.
- `OSArch live chat <https://osarch.org/chat>`__
- `OSArch community forum <https://community.osarch.org>`__
@@ -15,7 +15,7 @@ described are:
These objects may have lots of data and relationships. Examples of data might be
classification systems, physical materials, associated documents, simulation
results, and construction types. The data may be relevant to multiple
disciplines, such a architecture, engineering, and construction.
disciplines, such as architecture, engineering, and construction.
.. note::
Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

+1
View File
@@ -29,3 +29,4 @@ markers =
type
unit
void
addopts = --color=yes --code-highlight=yes
+1
View File
@@ -25,3 +25,4 @@ subprocess.call([py_exec, "-m", "pip", "install", "--upgrade", "pip"])
subprocess.call([py_exec, "-m", "pip", "install", "pytest"])
subprocess.call([py_exec, "-m", "pip", "install", "pytest-blender"])
subprocess.call([py_exec, "-m", "pip", "install", "pytest-bdd"])
subprocess.call([py_exec, "-m", "pip", "install", "pygments"])
@@ -118,3 +118,18 @@ Scenario: Select by material
And the variable "material" is "{ifc}.by_type('IfcMaterial')[0].id()"
When I press "bim.select_by_material(material={material})"
Then nothing happens
Scenario: Expand material category
Given an empty IFC project
And I press "bim.load_materials"
And I press "bim.add_material(obj='')"
When I press "bim.expand_material_category(category='')"
Then nothing happens
Scenario: Contract material category
Given an empty IFC project
And I press "bim.load_materials"
And I press "bim.add_material(obj='')"
And I press "bim.expand_material_category(category='')"
When I press "bim.contract_material_category(category='')"
Then nothing happens
@@ -75,3 +75,13 @@ Scenario: Disable editing styles
And I press "bim.load_styles(style_type='IfcSurfaceStyle')"
When I press "bim.disable_editing_style"
Then nothing happens
Scenario: Select by style
Given an empty IFC project
And I add a cube
And I add a material
And I press "bim.add_style"
And I press "bim.load_styles(style_type='IfcSurfaceStyle')"
And the variable "style" is "{ifc}.by_type('IfcSurfaceStyle')[0].id()"
When I press "bim.select_by_style(style={style})"
Then nothing happens
+1 -1
View File
@@ -281,7 +281,7 @@ class TestAddAnnotation:
drawing.show_decorations().should_be_called()
drawing.get_drawing_target_view("drawing").should_be_called().will_return("target_view")
drawing.get_annotation_context("target_view").should_be_called().will_return("context")
drawing.create_annotation_object("object_type").should_be_called().will_return("obj")
drawing.create_annotation_object("drawing", "object_type").should_be_called().will_return("obj")
ifc.get_entity("obj").should_be_called().will_return(None)
drawing.get_ifc_representation_class("object_type").should_be_called().will_return("ifc_representation_class")
drawing.run_root_assign_class(
+20 -5
View File
@@ -84,15 +84,30 @@ class TestGetName(NewFile):
class TestImportMaterialDefinitions(NewFile):
def test_import_materials(self):
def test_import_materials_grouped_by_categories(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material = ifc.createIfcMaterial(Name="Name")
material = ifc.createIfcMaterial(Name="Name", Category="Category")
subject.import_material_definitions("IfcMaterial")
props = bpy.context.scene.BIMMaterialProperties
assert props.materials[0].ifc_definition_id == material.id()
assert props.materials[0].name == "Name"
assert props.materials[0].total_elements == 0
assert props.materials[0].ifc_definition_id == 0
assert props.materials[0].name == "Category"
assert props.materials[0].is_category is True
assert props.materials[0].is_expanded is False
assert len(props.materials) == 1
def test_importing_expanded_material_categories(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
material = ifc.createIfcMaterial(Name="Name", Category="Category")
subject.import_material_definitions("IfcMaterial")
props = bpy.context.scene.BIMMaterialProperties
props.materials[0].is_expanded = True
subject.import_material_definitions("IfcMaterial")
assert len(props.materials) == 2
assert props.materials[1].ifc_definition_id == material.id()
assert props.materials[1].name == "Name"
assert props.materials[1].total_elements == 0
def test_import_material_layer_sets(self):
ifc = ifcopenshell.file()
+26
View File
@@ -95,6 +95,20 @@ class TestGetContext(NewFile):
assert subject.get_context("obj") == context
class TestGetElementsByStyle(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
style = ifc.createIfcSurfaceStyle()
item = ifc.createIfcExtrudedAreaSolid()
ifc.createIfcStyledItem(Item=item, Styles=[style])
element.Representation = ifc.createIfcProductDefinitionShape(
Representations=[ifc.createIfcShapeRepresentation(Items=[item])]
)
assert subject.get_elements_by_style(style) == {element}
class TestGetName(NewFile):
def test_run(self):
assert subject.get_name(bpy.data.materials.new("Material")) == "Material"
@@ -426,3 +440,15 @@ class TestIsEditingStyles(NewFile):
subject.is_editing_styles() is False
bpy.context.scene.BIMStylesProperties.is_editing = True
subject.is_editing_styles() is True
class TestSelectElements(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcPump")
obj = bpy.data.objects.new("Object", None)
bpy.context.scene.collection.objects.link(obj)
tool.Ifc.link(element, obj)
subject.select_elements([element])
assert obj in bpy.context.selected_objects
+19 -1
View File
@@ -541,6 +541,24 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcShellBasedSurfaceModel* l, Ifc
return true;
}
namespace {
bool is_nested_compound_of_solid(const TopoDS_Shape& s, int depth=0) {
if (s.ShapeType() == TopAbs_COMPOUND) {
TopoDS_Iterator it(s);
for (; it.More(); it.Next()) {
if (!is_nested_compound_of_solid(it.Value(), depth + 1)) {
return false;
}
}
return true;
} else if (s.ShapeType() == TopAbs_SOLID) {
return depth > 0;
} else {
return false;
}
}
}
bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape& shape) {
TopoDS_Shape s1;
@@ -697,7 +715,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape
bool valid_result;
if (s1.ShapeType() == TopAbs_COMPOUND && TopoDS_Iterator(s1).More() && TopoDS_Iterator(s1).Value().ShapeType() == TopAbs_SOLID) {
if (s1.ShapeType() == TopAbs_COMPOUND && TopoDS_Iterator(s1).More() && is_nested_compound_of_solid(s1)) {
TopoDS_Compound C;
BRep_Builder B;
B.MakeCompound(C);
+17
View File
@@ -0,0 +1,17 @@
.blockbutton {
border: 1px solid var(--color-admonition-title--seealso);
background-color: var(--color-admonition-title-background--seealso);
padding: 5px;
text-align: center;
border-radius: 5px;
max-width: 500px;
margin-left: auto;
margin-right: auto;
}
section img {
display: block;
margin-left: auto;
margin-right: auto;
box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
border-radius: 5px;
}
+35 -6
View File
@@ -28,19 +28,19 @@
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
import os
import sys
sys.path.insert(0, os.path.abspath('..'))
# -- Project information -----------------------------------------------------
project = "IfcOpenShell"
copyright = "2020, IfcOpenShell Contributors"
copyright = "2020-2022, IfcOpenShell Contributors"
author = "IfcOpenShell Contributors"
# The full version, including alpha/beta/rc tags
release = "0.0.1"
release = "0.7.0"
# -- General configuration ---------------------------------------------------
@@ -48,7 +48,34 @@ release = "0.0.1"
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ["sphinx.ext.autodoc"]
# I considered autodoc+autosummary but it had showstopper glitches:
# - Some modules couldn't be accessed https://github.com/sphinx-doc/sphinx/issues/7912#issuecomment-1120508738
# - No subnav making it really hard to navigate
# - Kinda hacky setup https://stackoverflow.com/questions/2701998/sphinx-autodoc-is-not-automatic-enough
# - I couldn't customise the template to show submodules above members which makes API discovery hard for users
extensions = ["autoapi.extension"]
# We're only documenting Python here
autoapi_type = 'python'
# autoapi works by reading source code instead of importing modules
autoapi_dirs = ['../ifcopenshell']
# autoapi_options doesn't have show-module-summary, as it tends to create one
# page per function which contradicts the presentation of showing all functions
# as a list. This creates two possible locations where a function is documented
# which is really disorienting. I also exclude imported-members. For example,
# ifcopenshell.file is imported from ifcopenshell.file.file, but it gets pretty
# confusing to see the docs again in multiple places (seriously,
# ifcopenshell.file.file is everywhere).
autoapi_options = ['members', 'undoc-members', 'private-members', 'special-members', 'show-inheritance']
# This option is set to both to allow both class docstrings and __init__ docstrings.
autoapi_python_class_content = 'both'
# Group by type (e.g. attribute, class, function, etc) then alphabetically.
autoapi_member_order = "groupwise"
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
@@ -70,3 +97,5 @@ html_theme = "furo"
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
html_css_files = ["custom.css"]
@@ -1,19 +1,15 @@
IfcOpenShell-Python
===================
This documentation is free software! You are free to contribute and help write
this document.
IfcOpenShell-Python provides Python bindings to the core IfcOpenShell C++
system, as well as high level analysis and authoring functions.
.. toctree::
:hidden:
:maxdepth: 1
:caption: Contents:
ifcopenshell-python/quickstart
ifcopenshell-python/api-documentation
Indices and tables
------------------
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
ifcopenshell-python/installation
ifcopenshell-python/hello_world
ifcopenshell-python/code_examples
ifcopenshell-python/developer_guide
@@ -1,21 +0,0 @@
API Documentation
=================
.. automodule:: ifcopenshell.entity_instance
:members:
.. automodule:: ifcopenshell.file
:members:
.. automodule:: ifcopenshell.guid
:members:
.. automodule:: ifcopenshell.template
:members:
.. automodule:: ifcopenshell.validate
:members:
.. automodule:: ifcopenshell.ids
:members:
Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

@@ -0,0 +1,4 @@
Code examples
=============
TODO
@@ -0,0 +1,15 @@
Developer Guide
===============
The core module implements low-level functionality to read and write IFC data. This includes:
- Reading IFC data from different serialisations into Python objects
- Accessing direct and indirect attributes of IFC entities
- Creating IFC entities
- Generating GlobalIds
- Removing IFC entities and all references
- Modifying IFC direct attributes
- Checking IFC class inheritance
- Validating IFC data
TODO
@@ -0,0 +1,219 @@
Hello, world!
=============
What's inside an IFC?
---------------------
TODO
Core functionality crash course
-------------------------------
This crash course guides you through basic code snippets that give you a general
idea of the low-level functionality that IfcOpenShell-python provides. You'll
need to have IfcOpenShell installed and a sample IFC model. To get the most out
of it, try out the code yourself and see what results you get!
If you don't have an IFC model available, here's a small one for your
convenience provided by the Institute for Automation and Applied Informatics
(IAI) / Karlsruhe Institute of Technology. It's in German, so you may need to
use some creativity when reading the data :)
.. container:: blockbutton
`Download sample IFC <https://www.ifcwiki.org/images/e/e3/AC20-FZK-Haus.ifc>`__
.. seealso::
You can find more sample models online in the `OSArch Open Data Directory
<https://wiki.osarch.org/index.php?title=AEC_Open_Data_directory>`__
Let's start with loading the model. Import the IfcOpenShell module, then use the
``open`` function to load the model into a variable called ``model``.
.. code-block:: python
import ifcopenshell
model = ifcopenshell.open('/path/to/your/model.ifc')
Let's see what IFC schema we are using:
.. code-block:: python
print(model.schema) # May return IFC2X3 or IFC4
Let's get the first piece of data in our IFC file:
.. code-block:: python
print(model.by_id(1))
But getting data from beginning to end isn't too meaningful to humans. What if we knew a ``GlobalId`` value instead?
.. code-block:: python
print(model.by_guid('0EI0MSHbX9gg8Fxwar7lL8'))
If we're not looking specifically for a single element, perhaps let's see how many walls are in our file, and count them:
.. code-block:: python
walls = model.by_type('IfcWall')
print(len(walls))
Once we have an element, we can see what IFC class it is:
.. code-block:: python
wall = model.by_type('IfcWall')[0]
print(wall.is_a()) # Returns 'IfcWall'
You can also test if it is a certain class, as well as check for parent classes too:
.. code-block:: python
print(wall.is_a('IfcWall')) # Returns True
print(wall.is_a('IfcElement')) # Returns True
print(wall.is_a('IfcWindow')) # Returns False
Let's quickly check the STEP ID of our element:
.. code-block:: python
print(wall.id())
Let's get some attributes of an element. IFC attributes have a particular order. We can access it just like a list, so let's get the first and third attribute:
.. code-block:: python
print(wall[0]) # The first attribute is the GlobalId
print(wall[2]) # The third attribute is the Name
Knowing the order of attributes is boring and technical. We can access them by name too:
.. code-block:: python
print(wall.GlobalId)
print(wall.Name)
Getting attributes one by one is tedious. Let's grab them all:
.. code-block:: python
# Gives us a dictionary of attributes, such as:
# {'id': 8, 'type': 'IfcWall', 'GlobalId': '2_qMTAIHrEYu0vYcqK8cBX', ... }
print(wall.get_info())
Let's see all the properties and quantities associated with this wall:
.. code-block:: python
import ifcopenshell.util
import ifcopenshell.util.element
print(ifcopenshell.util.element.get_psets(wall))
Some attributes are special, called "inverse attributes". They happen when another element is referencing our element. They can reference it for many reasons, like to define a relationship, such as if they create a void in our wall, join our wall, or define a quantity take-off value for our wall, among others. Just treat them like regular attributes:
.. code-block:: python
print(wall.IsDefinedBy)
Perhaps we want to see all elements which are referencing our wall?
.. code-block:: python
print(model.get_inverse(wall))
Let's do the opposite, let's see all the elements which our wall references instead:
.. code-block:: python
print(model.traverse(wall))
# Or, let's just go down one level deep
print(model.traverse(wall, max_levels=1))
If you want to modify data, just assign it to the relevant attribute:
.. code-block:: python
wall.Name = 'My new wall name'
You can also generate a new ``GlobalId``:
.. code-block:: python
wall.GlobalId = ifcopenshell.guid.new()
After modifying some IFC data, you can save it to a new IFC-SPF file:
.. code-block:: python
model.write('/path/to/a/new.ifc')
You can generate a new IFC from scratch too, instead of reading an existing one:
.. code-block:: python
ifc = ifcopenshell.file()
# Or if you want a particular schema:
ifc = ifcopenshell.file(schema='IFC4')
You can create new IFC elements, and add it either to an existing or newly created IFC file object:
.. code-block:: python
# Will return #1=IfcWall($,$,$,$,$,$,$,$,$) - notice all of the attributes are blank!
new_wall = model.createIfcWall()
# Will return a list with our wall in it: [#1=IfcWall($,$,$,$,$,$,$,$,$)]
print(model.by_type('IfcWall'))
Alternatively, you can also use this way to create new elements:
.. code-block:: python
model.create_entity('IfcWall')
Specifying more arguments lets you fill in attributes while creating the element instead of assigning them separately. You specify them in the order of the attributes.
.. code-block:: python
# Gives us #1=IfcWall('0EI0MSHbX9gg8Fxwar7lL8',$,$,$,$,$,$,$,$)
model.create_entity('IfcWall', ifcopenshell.guid.new())
Again, knowing the order of attributes is difficult, so you can use keyword arguments instead:
.. code-block:: python
# Gives us #1=IfcWall('0EI0MSHbX9gg8Fxwar7lL8',$,'Wall Name',$,$,$,$,$,$)
model.create_entity('IfcWall', GlobalId=ifcopenshell.guid.new(), Name='Wall Name')
Sometimes, it's easier to expand a dictionary:
.. code-block:: python
data = {
'GlobalId': ifcopenshell.guid.new(),
'Name': 'Wall Name'
}
model.create_entity('IfcWall', **data)
Some attributes of an element aren't just text, they may be a reference to another element. Easy:
.. code-block:: python
wall = model.createIfcWall()
wall.OwnerHistory = model.createIfcOwnerHistory()
What if we already have an element from one IFC file and want to add it to another?
.. code-block:: python
wall = model.by_type('IfcWall')[0]
new_model = ifcopenshell.file()
new_model.add(wall)
Fed up with an object? Let's delete it:
.. code-block:: python
model.remove(wall)
@@ -0,0 +1,139 @@
Installation
============
There are different methods of installation, depending on your situation.
1. **Pre-built packages** is recommended for users wanting to use the latest IfcOpenShell builds.
2. **Pip** is recommended for managing a more stable IfcOpenShell version.
3. **Conda** is recommended for developers using Anaconda.
4. **Using the BlenderBIM Add-on** is recommended for non-developers wanting a graphical interface.
5. **Compiling from source** is recommended for developers actively working with the C++ core.
Pre-built packages
------------------
1. Choose which version to download based on your operating system, Python
version, and computer architecture.
+-------------+----------------+----------------+----------------+----------------+
| | Linux 64bit | Windows 32bit | Windows 64bit | MacOS 64bit |
+=============+================+================+================+================+
| Python 3.6 | py36-linux64_ | py36-win32_ | py36-win64_ | py36-macos64_ |
+-------------+----------------+----------------+----------------+----------------+
| Python 3.7 | py37-linux64_ | py37-win32_ | py37-win64_ | py37-macos64_ |
+-------------+----------------+----------------+----------------+----------------+
| Python 3.8 | py38-linux64_ | py38-win32_ | py38-win64_ | py38-macos64_ |
+-------------+----------------+----------------+----------------+----------------+
| Python 3.9 | py39-linux64_ | py39-win32_ | py39-win64_ | py39-macos64_ |
+-------------+----------------+----------------+----------------+----------------+
| Python 3.10 | py31-linux64_ | py31-win32_ | py31-win64_ | py31-macos64_ |
+-------------+----------------+----------------+----------------+----------------+
.. _py36-linux64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-36-v0.7.0-1b1fd1e-linux64.zip
.. _py37-linux64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-37-v0.7.0-1b1fd1e-linux64.zip
.. _py38-linux64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-38-v0.7.0-1b1fd1e-linux64.zip
.. _py39-linux64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-39-v0.7.0-1b1fd1e-linux64.zip
.. _py31-linux64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-31-v0.7.0-1b1fd1e-linux64.zip
.. _py36-win32: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-36-v0.7.0-1b1fd1e-win64.zip
.. _py37-win32: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-37-v0.7.0-1b1fd1e-win64.zip
.. _py38-win32: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-38-v0.7.0-1b1fd1e-win64.zip
.. _py39-win32: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-39-v0.7.0-1b1fd1e-win64.zip
.. _py31-win32: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-31-v0.7.0-1b1fd1e-win64.zip
.. _py36-win64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-36-v0.7.0-1b1fd1e-win64.zip
.. _py37-win64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-37-v0.7.0-1b1fd1e-win64.zip
.. _py38-win64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-38-v0.7.0-1b1fd1e-win64.zip
.. _py39-win64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-39-v0.7.0-1b1fd1e-win64.zip
.. _py31-win64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-31-v0.7.0-1b1fd1e-win64.zip
.. _py36-macos64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-36-v0.7.0-1b1fd1e-macos64.zip
.. _py37-macos64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-37-v0.7.0-1b1fd1e-macos64.zip
.. _py38-macos64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-38-v0.7.0-1b1fd1e-macos64.zip
.. _py39-macos64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-39-v0.7.0-1b1fd1e-macos64.zip
.. _py31-macos64: https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-31-v0.7.0-1b1fd1e-macos64.zip
.. warning::
Versions for Mac ARM devices (M1 chip) are not yet available. You are free to
compile it yourself manually, but this requires a level of technical
expertise.
2. Copy the ``ifcopenshell`` directory into your Python path. If you're not sure
where your Python path is, run the following code in Python:
.. code-block:: python
import sys
print(sys.path)
This will give you a list of possible directories that you can install the
IfcOpenShell module into. Most commonly, you will want to copy the
``ifcopenshell`` directory into one of these called ``site-packages``.
3. Test importing the module in a Python session or script to make sure it works.
.. code-block:: python
import ifcopenshell
print(ifcopenshell.version)
model = ifcopenshell.file()
Pip
---
TODO
Conda
-----
TODO
Using the BlenderBIM Add-on
---------------------------
The BlenderBIM Add-on is a Blender based graphical interface to IfcOpenShell.
Other than providing a graphical IFC authoring platform, it also comes with
IfcOpenShell and a Python shell built-in. This means you don't need to install
Python first, and you also can compare your IfcOpenShell scripting to what you
see with a visual model viewer.
1. Install the BlenderBIM Add-on by following the `BlenderBIM Add-on
installation documentation
<https://blenderbim.org/docs/users/installation.html>`_.
2. Launch Blender. On the top left of the Viewport panel, click the **Editor
Type** icon to change the viewport into a **Python Console**.
.. image:: blenderbim-python-console-1.png
3. Make sure you can import IfcOpenShell successfully with the following script.
.. image:: blenderbim-python-console-2.png
.. tip::
Before changing the **Editor Type** to a **Python Console**, you can click on
the ``View > Area > Vertical Split`` menu which will divide your viewport.
This allows you to write scripts next to the 3D view of a model.
Blender also comes with a text editor so you can write longer scripts. Instead
of choosing the **Python Console**, choose the **Text Editor**.
.. image:: blenderbim-text-editor-1.png
You can now create a new text file for your script by clicking ``Text > New``,
and run your script using the **Text > Run Script** menu or by clicking on the
**Play Icon**.
.. image:: blenderbim-text-editor-2.png
.. seealso::
You may be interested in learning how to graphically explore an IFC model in
Blender. This can help when learning how to write scripts as you can double
check the results of your scripts with what you see in the graphical
interface. `Read more
<https://blenderbim.org/docs/users/exploring_an_ifc_model.html>`_.
Compiling from source
---------------------
TODO
@@ -1,5 +0,0 @@
Quickstart
==========
For starters, you can read `Using IfcOpenShell to parse IFC files with Python
<https://thinkmoult.com/using-ifcopenshell-parse-ifc-files-python.html>`_
+4 -4
View File
@@ -1,20 +1,20 @@
Let's learn IfcOpenShell
========================
IfcOpenShell is a
IfcOpenShell is a suite of developer libraries and utilities to manipulate OpenBIM data.
A coloured icon: :octicon:`report;1em;sd-text-info`, some more text.
.. note::
:bdg:`plain badge`
This documentation is incomplete. Would you like to help write more? `Get in touch! <https://osarch.org/chat/>`__
.. toctree::
:hidden:
:maxdepth: 1
:caption: Contents:
ifcopenshell
ifcopenshell-python
ifcconvert
blenderbim
bimtester
ifcdiff
ifcclash
@@ -16,6 +16,17 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
"""The entry module for IfcOpenShell
Typically used for opening an IFC via a filepath, or accessing one of the
submodules.
Example::
import ifcopenshell
print(ifcopenshell.version) # v0.7.0-1b1fd1e6
model = ifcopenshell.open("/path/to/model.ifc")
"""
from __future__ import absolute_import
from __future__ import division
@@ -60,14 +71,27 @@ UNSUPPORTED_SCHEMA = ifcopenshell_wrapper.file_open_status.UNSUPPORTED_SCHEMA
class Error(Exception):
"""Error used when a generic problem occurs"""
pass
class SchemaError(Error):
"""Error used when an IFC schema related problem occurs"""
pass
def open(fn):
"""Loads an IFC dataset from a filepath
:param fn: Filepath to the IFC model
:type fn: string
:returns: A file object
:rtype: ifcopenshell.file.file
Example::
ifc_file = ifcopenshell.open("/path/to/model.ifc")
"""
f = ifcopenshell_wrapper.open(os.path.abspath(fn))
if f.good():
return file(f)
@@ -84,6 +108,26 @@ def open(fn):
def create_entity(type, schema="IFC4", *args, **kwargs):
"""Creates a new IFC entity that does not belong to an IFC file object
Note that it is more common to create entities within a existing file
object. See :meth:`ifcopenshell.file.file.create_entity`.
:param type: Case insensitive name of the IFC class
:type type: string
:param schema: The IFC schema identifier
:type schema: string
:param args: The positional arguments of the IFC class
:param kwargs: The keyword arguments of the IFC class
:returns: An entity instance
:rtype: ifcopenshell.entity_instance.entity_instance
Example::
person = ifcopenshell.create_entity("IfcPerson") # #0=IfcPerson($,$,$,$,$,$,$,$)
model = ifcopenshell.file()
model.add(person) # #1=IfcPerson($,$,$,$,$,$,$,$)
"""
e = entity_instance((schema, type))
attrs = list(enumerate(args)) + [(e.wrapped_data.get_argument_index(name), arg) for name, arg in kwargs.items()]
for idx, arg in attrs:
@@ -95,6 +139,17 @@ gcroot = []
def register_schema(schema):
"""Registers a custom IFC schema
:param schema: A schema object
:type schema: ifcopenshell.express.schema_class.SchemaClass
Example::
schema = ifcopenshell.express.parse("/path/to/ifc-custom.exp")
ifcopenshell.register_schema(schema)
ifcopenshell.file(schema="IFC_CUSTOM")
"""
gcroot.append(schema)
ifcopenshell_wrapper.register_schema(schema.schema)
register_schema_attributes(schema.schema)
@@ -16,6 +16,8 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
"""High level user-oriented IFC authoring capabilities"""
import json
import numpy
import importlib
@@ -0,0 +1,24 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
"""Aggregates are the concept of breaking down larger wholes into smaller parts.
One common use is spatial elements, such as how a site has multiple buildings,
and a building has multiple storeys. Another is for regular elements, such as
how a wall is made out of members and coverings.
"""
@@ -21,14 +21,24 @@ import ifcopenshell.api
class Usecase:
def __init__(self, file, **settings):
def __init__(self, file, product=None, relating_object=None):
"""Assigns an object as an aggregate to a product
:param product: The part of the aggregate
:param relating_object: The whole of the aggregate
:return: entity: The aggregate relationship
Example::
element = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite")
subelement = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding")
ifcopenshell.api.run("aggregate.assign_object", model, product=subelement, relating_object=element)
"""
self.file = file
self.settings = {
"product": None,
"relating_object": None,
"product": product,
"relating_object": relating_object,
}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
decomposes = None
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,21 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
"""Boundaries are primarily used for representing virtual interfaces between
spaces for energy analysis.
"""
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -27,6 +27,7 @@ class Usecase:
"product": None,
"reference": None,
"classification": None,
"is_lightweight": True,
}
for key, value in settings.items():
self.settings[key] = value
@@ -57,12 +58,13 @@ class Usecase:
return
migrator = ifcopenshell.util.schema.Migrator()
# This removal patch is to support a lightweight classification
old_referenced_source = self.settings["reference"].ReferencedSource
self.settings["reference"].ReferencedSource = None
if self.settings["is_lightweight"]:
old_referenced_source = self.settings["reference"].ReferencedSource
self.settings["reference"].ReferencedSource = None
relating_classification = migrator.migrate(self.settings["reference"], self.file)
relating_classification.ReferencedSource = self.settings["classification"]
self.settings["reference"].ReferencedSource = old_referenced_source
if self.settings["is_lightweight"]:
relating_classification.ReferencedSource = self.settings["classification"]
self.settings["reference"].ReferencedSource = old_referenced_source
self.file.create_entity(
"IfcRelAssociatesClassification",
**{
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,17 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
"""2D drawing generation and serialisation"""
import math
import json
@@ -88,7 +88,7 @@ for nm in ifcopenshell_wrapper.schema_names():
class entity_instance(object):
"""This is the base Python class for all IFC objects.
"""Base class for all IFC objects.
An instantiated entity_instance will have methods of Python and the IFC class itself.
@@ -159,6 +159,8 @@ class Implementation(codegen.Base):
"type": arg["full_type"].replace("::Value", ""),
"non_optional_type": arg["non_optional_type"].replace("::Value", ""),
"star_if_optional": "*" if "boost::optional" in arg["full_type"] else "",
"check_optional_set_begin": "if (v) {" if "boost::optional" in arg["full_type"] else "",
"check_optional_set_end": "}" if "boost::optional" in arg["full_type"] else "",
},
)
@@ -283,10 +283,22 @@ __attribute__((optnone))
self.statements.extend(
(
"const schema_definition& %s::get_schema() {" % schema_name_title,
"static std::unique_ptr<schema_definition> schema;",
"",
" static const schema_definition* s = %(schema_name)s_populate_schema();" % locals(),
" return *s;",
"void %s::clear_schema() {" % schema_name_title,
" schema.reset();",
"}",
"",
)
)
self.statements.extend(
(
"const schema_definition& %s::get_schema() {" % schema_name_title,
" if (!schema) {",
" schema.reset(%(schema_name)s_populate_schema());" % locals(),
" }",
" return *schema;",
"}",
"",
"",
@@ -38,6 +38,8 @@ struct %(schema_name)s {
IFC_PARSE_API static const IfcParse::schema_definition& get_schema();
IFC_PARSE_API static void clear_schema();
static const char* const Identifier;
// Forward definitions
@@ -257,13 +259,13 @@ get_attr_stmt_nested_array = "%(null_check)s aggregate_of_aggregate_of_instance:
get_inverse = "return data_->getInverse(%(schema_name_upper)s_%(type)s_type, %(index)d)->as<%(type)s>();"
set_attr_stmt = (
"{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(%(star_if_optional)sv"
+ ");data_->setArgument(%(index)d,attr);}"
"{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();%(check_optional_set_begin)sattr->set(%(star_if_optional)sv"
+ ");%(check_optional_set_end)sdata_->setArgument(%(index)d,attr);}"
)
set_attr_stmt_enum = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(IfcWrite::IfcWriteArgument::EnumerationReference(%(star_if_optional)sv,%(non_optional_type)s::ToString(%(star_if_optional)sv)));data_->setArgument(%(index)d,attr);}"
set_attr_stmt_enum = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();%(check_optional_set_begin)sattr->set(IfcWrite::IfcWriteArgument::EnumerationReference(%(star_if_optional)sv,%(non_optional_type)s::ToString(%(star_if_optional)sv)));%(check_optional_set_end)sdata_->setArgument(%(index)d,attr);}"
set_attr_stmt_array = (
"{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set((%(star_if_optional)sv)->generalize()"
+ ");data_->setArgument(%(index)d,attr);}"
"{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();%(check_optional_set_begin)sattr->set((%(star_if_optional)sv)->generalize()"
+ ");%(check_optional_set_end)sdata_->setArgument(%(index)d,attr);}"
)
constructor_stmt = (

Some files were not shown because too many files have changed in this diff Show More