Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c352181f8c | |||
| 3a1416536a | |||
| 4eec863cad | |||
| 96bcd31d99 | |||
| b09ff828de | |||
| 3cb68ca388 | |||
| c95d3693ea | |||
| 67d2ab2577 | |||
| 970b24b63b | |||
| fdaae048c2 | |||
| 4da5a38237 | |||
| 0cf2d7e05e | |||
| 3122cf21db | |||
| 4bf25d5d9c | |||
| dc10c7a360 | |||
| 94e082fc7d | |||
| 9671df8491 | |||
| 8aa38917eb | |||
| 0774100606 | |||
| 95b48b02e4 | |||
| c5de1d8727 | |||
| 7e09ab0ced | |||
| eb3e137a6e | |||
| f528ad9744 | |||
| 650f9eb7f5 | |||
| a78d6f42e5 | |||
| 4326a74c64 | |||
| 08fc62d4d9 | |||
| ae298de8dc | |||
| a24c4b269a | |||
| 083d9d3e3e | |||
| 12e7ed356c | |||
| 3f15dd71b1 | |||
| 42448cae4e | |||
| 7e28d193ec | |||
| 8f2b32532c | |||
| f0a00cbc94 | |||
| fd73b2cf0a | |||
| e5b3455533 | |||
| e677355438 | |||
| 93fd3f97e3 | |||
| 910d189f35 | |||
| c8d6d12442 | |||
| 1f3ba3d09d | |||
| f67941221e | |||
| 8252ef82ad | |||
| 3b825e35ae | |||
| 4a4912e817 | |||
| 5a38604ec2 | |||
| b15e0d0e2b |
@@ -11,6 +11,8 @@ For more information, see
|
||||
|
||||
[](https://travis-ci.org/IfcOpenShell/IfcOpenShell)
|
||||
|
||||
[](https://opencollective.com/opensourcebim/)
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
* Git
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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::
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 193 KiB |
|
After Width: | Height: | Size: 198 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 81 KiB |
@@ -29,3 +29,4 @@ markers =
|
||||
type
|
||||
unit
|
||||
void
|
||||
addopts = --color=yes --code-highlight=yes
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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:
|
||||
|
After Width: | Height: | Size: 182 KiB |
|
After Width: | Height: | Size: 114 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
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>`_
|
||||
@@ -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 = (
|
||||
|
||||