Merge branch 'v0.6.0' into hdf5

This commit is contained in:
johltn
2021-02-10 16:04:56 +01:00
36 changed files with 3175 additions and 204 deletions
+7
View File
@@ -12,6 +12,13 @@ __pycache__
.vscode
# PyCharm files
.idea
# Docs
/docs/output
/docs/rst_files
/docs/doxygen
/src/ifcblenderexport/docs/_build
# gettext binary translation files
*.mo
# Vim
+7
View File
@@ -36,6 +36,7 @@ OPTION(IFCXML_SUPPORT "Build IfcParse with ifcXML support (requires libxml2)." O
OPTION(ENABLE_BUILD_OPTIMIZATIONS "Enable certain compiler and linker optimizations on RelWithDebInfo and Release builds." OFF)
OPTION(IFCCONVERT_DOUBLE_PRECISION "IfcConvert: Use double precision floating-point numbers." ON)
OPTION(BUILD_IFCGEOM "Build IfcGeom." ON)
OPTION(BUILD_DOCUMENTATION "Build IfcOpenShell Documentation." OFF)
OPTION(BUILD_IFCPYTHON "Build IfcPython." ON)
OPTION(BUILD_EXAMPLES "Build example applications." ON)
OPTION(BUILD_GEOMSERVER "Build IfcGeomServer executable." ON)
@@ -685,6 +686,12 @@ INSTALL(TARGETS IfcGeomServer
endif()
# Documentation
IF(BUILD_DOCUMENTATION)
set(CMAKE_MODULE_PATH "../docs/cmake")
ADD_SUBDIRECTORY(../docs docs)
ENDIF()
IF(BUILD_IFCPYTHON)
ADD_SUBDIRECTORY(../src/ifcwrap ifcwrap)
ENDIF()
+40
View File
@@ -0,0 +1,40 @@
#Look for an executable called sphinx-build
find_program(SPHINX_EXECUTABLE
NAMES sphinx-build
DOC "Path to sphinx-build executable")
include(FindPackageHandleStandardArgs)
#Handle standard arguments to find_package like REQUIRED and QUIET
find_package_handle_standard_args(Sphinx
"Failed to find sphinx-build executable"
SPHINX_EXECUTABLE)
find_package(Doxygen REQUIRED)
#find_package(Sphinx REQUIRED)
set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR})
set(SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR}/docs/sphinx)
MESSAGE(STATUS "SPHINX BUILD ${CMAKE_CURRENT_BINARY_DIR}")
file(MAKE_DIRECTORY ./output/doxygen)
if (DOXYGEN_FOUND)
add_custom_target(Sphinx ALL
COMMAND
${SPHINX_EXECUTABLE} -v -T -b html
${SPHINX_SOURCE} ${CMAKE_CURRENT_SOURCE_DIR}/output
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/output
COMMENT "Generating documentation with Sphinx")
# add_custom_target(ifcopenshell_python_docs ALL
# COMMAND make html
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../src/ifcblenderexport/docs
# OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/../src/ifcblenderexport/docs
# COMMENT "Generating documentation with Sphinx")
else (DOXYGEN_FOUND)
message("Doxygen need to be installed to generate the doxygen documentation")
endif (DOXYGEN_FOUND)
+2531
View File
File diff suppressed because it is too large Load Diff
+11
View File
@@ -0,0 +1,11 @@
#Look for an executable called sphinx-build
find_program(SPHINX_EXECUTABLE
NAMES sphinx-build
DOC "Path to sphinx-build executable")
include(FindPackageHandleStandardArgs)
#Handle standard arguments to find_package like REQUIRED and QUIET
find_package_handle_standard_args(Sphinx
"Failed to find sphinx-build executable"
SPHINX_EXECUTABLE)
+11
View File
@@ -0,0 +1,11 @@
#Look for an executable called sphinx-build
find_program(SPHINX_EXECUTABLE
NAMES sphinx-build
DOC "Path to sphinx-build executable")
include(FindPackageHandleStandardArgs)
#Handle standard arguments to find_package like REQUIRED and QUIET
find_package_handle_standard_args(Sphinx
"Failed to find sphinx-build executable"
SPHINX_EXECUTABLE)
+37
View File
@@ -0,0 +1,37 @@
import textwrap
# The `extensions` list should already be in here from `sphinx-quickstart`
extensions = [
# there may be others here already, e.g. 'sphinx.ext.mathjax'
'breathe',
'exhale',
'sphinx.ext.intersphinx'
]
project = 'IfcOpenShell'
copyright = '2020, IfcOpenShell'
author = ''
# Setup the breathe extension
breathe_projects = {
"My Project": "./output/doxygen/xml"
}
breathe_default_project = "My Project"
# Setup the exhale extension
exhale_args = {
# These arguments are required
"containmentFolder": "./rst_files",
"rootFileName": "library_root.rst",
"rootFileTitle": "IfcOpenShell C++",
"doxygenStripFromPath": "..",
# Suggested optional arguments
"createTreeView": True,
# TIP: if using the sphinx-bootstrap-theme, you need
# "treeViewIsBootstrap": True,
"exhaleExecutesDoxygen": True,
# "exhaleDoxygenStdin": "INPUT = ../src/serializers/ ../src/ifcgeom",
"exhaleUseDoxyfile": True
}
cpp_id_attributes = ['IFC_PARSE_API', 'IFC_GEOM_API']
+10
View File
@@ -0,0 +1,10 @@
IfcOpenShell
============
.. toctree::
:maxdepth: 3
rst_files/library_root
* `ifcopenshell-python documentation <python/html/index.html>`_
+39
View File
@@ -0,0 +1,39 @@
# This program requires doxygen, sphinx, breathe and exhale.
import os
import sys
import shutil
import subprocess
import multiprocessing
# some extra check to see if we can find sphinx in pypy bin dir
sphinx_build = os.path.join(os.path.dirname(sys.executable), 'sphinx-build')
if not os.path.exists(sphinx_build):
sphinx_build = "sphinx-build"
# else:
# os.environ['PATH'] = os.pathsep + os.path.dirname(sys.executable)
# not used, -j caused issues in my sphinx install
j = str(multiprocessing.cpu_count())
if not os.path.isdir("./output/doxygen/xml"):
os.makedirs("output/doxygen/xml")
subprocess.check_call([sphinx_build, "-b", "html", ".", "output"])
if sys.platform == "linux" or sys.platform == "linux2":
if os.path.isdir("output/python"):
shutil.rmtree("output/python")
if not os.path.exists(os.path.join("../src/ifcblenderexport/docs", "contents.rst")):
subprocess.check_call(["ln", "index.rst", "contents.rst"], cwd="../src/ifcblenderexport/docs")
subprocess.check_call(["sed", "-i", "s/html_theme/# html_theme/g", "conf.py"], cwd="../src/ifcblenderexport/docs")
subprocess.check_call(["make", "html"], cwd="../src/ifcblenderexport/docs")
subprocess.check_call(["git", "checkout", "conf.py"], cwd="../src/ifcblenderexport/docs")
shutil.move("../src/ifcblenderexport/docs/_build", "./output")
os.rename("./output/_build", "./output/python")
elif sys.platform == "win32" or sys.platform == "win64":
subprocess.check_call(["make.bat", "html"])
+10
View File
@@ -0,0 +1,10 @@
IfcOpenShell
============
.. toctree::
:maxdepth: 3
rst_files/library_root
* `ifcopenshell-python documentation <python/html/index.html>`_
+4 -2
View File
@@ -33,12 +33,14 @@ class IfcStore:
@staticmethod
def link_element(element, obj):
IfcStore.id_map[element.id()] = obj
IfcStore.guid_map[element.GlobalId] = obj
if hasattr(element, "GlobalId"):
IfcStore.guid_map[element.GlobalId] = obj
obj.BIMObjectProperties.ifc_definition_id = element.id()
@staticmethod
def unlink_element(element, obj=None):
del IfcStore.id_map[element.id()]
del IfcStore.guid_map[element.GlobalId]
if hasattr(element, "GlobalId"):
del IfcStore.guid_map[element.GlobalId]
if obj:
obj.BIMObjectProperties.ifc_definition_id = 0
@@ -335,8 +335,6 @@ class IfcImporter:
self.profile_code("Create project")
self.create_spatial_hierarchy()
self.profile_code("Create spatial hierarchy")
self.create_type_products()
self.profile_code("Create type products")
self.create_aggregates()
self.profile_code("Create aggregates")
self.create_openings_collection()
@@ -347,14 +345,16 @@ class IfcImporter:
# self.parse_native_elements()
# self.profile_code("Parsing native elements")
self.create_grids()
self.profile_code("Creating grids")
self.profile_code("Create grids")
# TODO: Deprecate
# self.create_native_products()
# self.profile_code("Creating native products")
# self.profile_code("Create native products")
self.create_products()
self.profile_code("Creating meshified products")
self.profile_code("Create products")
self.create_type_products()
self.profile_code("Create type products")
self.create_annotation()
self.profile_code("Creating annotation")
self.profile_code("Create annotation")
self.place_objects_in_spatial_tree()
self.profile_code("Placing objects in spatial tree")
if self.ifc_import_settings.should_merge_by_class:
@@ -633,16 +633,17 @@ class IfcImporter:
return
representation_map = self.get_type_product_body_representation_map(element)
mesh = None
if self.ifc_import_settings.should_import_type_representations and representation_map:
try:
shape = ifcopenshell.geom.create_shape(self.settings, representation_map.MappedRepresentation)
mesh_name = f"mesh-{shape.id}"
mesh = self.meshes.get(mesh_name)
if mesh is None:
if representation_map:
representation = representation_map.MappedRepresentation
mesh_name = "{}/{}".format(representation.ContextOfItems.id(), representation.id())
mesh = self.meshes.get(mesh_name)
if mesh is None:
try:
shape = ifcopenshell.geom.create_shape(self.settings, representation_map.MappedRepresentation)
mesh = self.create_mesh(element, shape)
self.meshes[mesh_name] = mesh
except:
self.ifc_import_settings.logger.error("Failed to generate shape for %s", element)
except:
self.ifc_import_settings.logger.error("Failed to generate shape for %s", element)
obj = bpy.data.objects.new(self.get_name(element), mesh)
obj.BIMObjectProperties.ifc_definition_id = element.id()
self.material_creator.create(element, obj, mesh)
@@ -761,8 +762,7 @@ class IfcImporter:
self.ifc_import_settings.logger.info("Creating object %s", element)
if shape:
# TODO: make names more meaningful
mesh_name = f"mesh-{shape.geometry.id}"
mesh_name = self.get_mesh_name(shape.geometry)
mesh = self.meshes.get(mesh_name)
if mesh is None:
if element.GlobalId in self.native_elements:
@@ -1388,6 +1388,16 @@ class IfcImporter:
):
return representation.Items[0].MappingTarget
def get_mesh_name(self, geometry):
representation_id = geometry.id
if "-" in representation_id:
representation_id = int(re.sub(r"\D", "", representation_id.split("-")[0]))
else:
representation_id = int(re.sub(r"\D", "", representation_id))
representation = self.file.by_id(representation_id)
context_id = representation.ContextOfItems.id() if hasattr(representation, "ContextOfItems") else 0
return "{}/{}".format(context_id, representation_id)
def create_mesh(self, element, shape, is_curve=False):
try:
if hasattr(shape, "geometry"):
@@ -1398,14 +1408,7 @@ class IfcImporter:
if is_curve:
return self.create_curve(geometry)
representation_id = geometry.id
if "-" in representation_id:
representation_id = int(re.sub(r"\D", "", representation_id.split("-")[0]))
else:
representation_id = int(re.sub(r"\D", "", representation_id))
representation = self.file.by_id(representation_id)
context_id = representation.ContextOfItems.id() if hasattr(representation, "ContextOfItems") else 0
mesh = bpy.data.meshes.new("{}/{}".format(context_id, representation_id))
mesh = bpy.data.meshes.new(self.get_mesh_name(geometry))
props = bpy.context.scene.BIMGeoreferenceProperties
if props.has_blender_offset and props.blender_offset_type == "CARTESIAN_POINT":
@@ -1574,7 +1577,6 @@ class IfcImportSettings:
self.logger = None
self.input_file = None
self.diff_file = None
self.should_import_type_representations = False
self.should_import_spaces = False
self.should_auto_set_workarounds = True
self.should_use_cpu_multiprocessing = True
@@ -17,9 +17,12 @@ class Data:
return
cls.products[product_id] = []
product = file.by_id(product_id)
if not hasattr(product, "Representation") or not product.Representation:
return
for representation in product.Representation.Representations:
representations = []
if product.is_a("IfcProduct"):
representations = product.Representation.Representations
elif product.is_a("IfcTypeProduct"):
representations = [rm.MappedRepresentation for rm in product.RepresentationMaps]
for representation in representations:
c = representation.ContextOfItems
rep_id = int(representation.id())
cls.representations[rep_id] = {
@@ -1,26 +1,24 @@
class Usecase:
def __init__(self, file, settings=None):
self.file = file
self.settings = {
"product": None,
"representation": None,
}
self.settings = {"representation": None}
self.ifc_vertices = []
for key, value in settings.items():
self.settings[key] = value
def execute(self):
self.zero = self.file.createIfcCartesianPoint((0.0, 0.0, 0.0))
self.x_axis = self.file.createIfcDirection((1.0, 0.0, 0.0))
self.y_axis = self.file.createIfcDirection((0.0, 1.0, 0.0))
self.z_axis = self.file.createIfcDirection((0.0, 0.0, 1.0))
mapping_source = self.get_mapping_source()
self.get_mapping_source()
if not mapping_source:
return
mapping_target = self.file.createIfcCartesianTransformationOperator3D(
self.x_axis, self.y_axis, self.zero, 1, self.z_axis
)
mapped_item = self.file.createIfcMappedItem(self.get_mapping_source(), mapping_target)
zero = self.file.createIfcCartesianPoint((0.0, 0.0, 0.0))
x_axis = self.file.createIfcDirection((1.0, 0.0, 0.0))
y_axis = self.file.createIfcDirection((0.0, 1.0, 0.0))
z_axis = self.file.createIfcDirection((0.0, 0.0, 1.0))
mapping_target = self.file.createIfcCartesianTransformationOperator3D(x_axis, y_axis, zero, 1, z_axis)
mapped_item = self.file.createIfcMappedItem(mapping_source, mapping_target)
return self.file.create_entity(
"IfcShapeRepresentation",
**{
@@ -32,12 +30,6 @@ class Usecase:
)
def get_mapping_source(self):
if self.settings["representation"].RepresentationMap:
return self.settings["representation"].RepresentationMap[0]
return self.file.create_entity(
"IfcRepresentationMap",
**{
"MappingOrigin": self.file.createIfcAxis2Placement3D(self.zero, self.z_axis, self.x_axis),
"MappedRepresentation": self.settings["representation"],
}
)
for inverse in self.file.get_inverse(self.settings["representation"]):
if inverse.is_a("IfcRepresentationMap"):
return inverse
@@ -8,6 +8,7 @@ import blenderbim.bim.module.geometry.add_representation as add_representation
import blenderbim.bim.module.geometry.map_representation as map_representation
import blenderbim.bim.module.geometry.assign_styles as assign_styles
import blenderbim.bim.module.geometry.assign_representation as assign_representation
import blenderbim.bim.module.geometry.unassign_representation as unassign_representation
import blenderbim.bim.module.geometry.remove_representation as remove_representation
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim import import_ifc
@@ -202,13 +203,21 @@ class SwitchRepresentation(bpy.types.Operator):
class RemoveRepresentation(bpy.types.Operator):
bl_idname = "bim.remove_representation"
bl_label = "Remove Representation"
ifc_definition_id: bpy.props.IntProperty()
obj: bpy.props.StringProperty() # TODO
representation_id: bpy.props.IntProperty()
def execute(self, context):
self.file = IfcStore.get_file()
representation = self.file.by_id(self.ifc_definition_id)
representation = self.file.by_id(self.representation_id)
obj = bpy.context.active_object
mesh = bpy.data.meshes.get("{}/{}".format(representation.ContextOfItems.id(), representation.id()))
product = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
if representation.RepresentationType == "MappedRepresentation":
mesh_name = "{}/{}".format(
representation.ContextOfItems.id(), representation.Items[0].MappingSource.MappedRepresentation.id()
)
else:
mesh_name = "{}/{}".format(representation.ContextOfItems.id(), representation.id())
mesh = bpy.data.meshes.get(mesh_name)
if mesh:
if obj.data == mesh:
# TODO we can do better than this
@@ -216,9 +225,12 @@ class RemoveRepresentation(bpy.types.Operator):
if not void_mesh:
void_mesh = bpy.data.meshes.new("Void")
obj.data = void_mesh
bpy.data.meshes.remove(mesh)
if representation.RepresentationType != "MappedRepresentation":
bpy.data.meshes.remove(mesh)
product = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
unassign_representation.Usecase(self.file, {"product": product, "representation": representation}).execute()
remove_representation.Usecase(self.file, {"representation": representation}).execute()
Data.load(obj.BIMObjectProperties.ifc_definition_id)
Data.load(product.id())
return {"FINISHED"}
@@ -237,22 +249,10 @@ class MapRepresentation(bpy.types.Operator):
for obj in objs:
bpy.ops.bim.edit_object_placement(obj=obj.name)
product = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
if obj.data.BIMMeshProperties.ifc_definition_id:
old_representation = self.file.by_id(obj.data.BIMMeshProperties.ifc_definition_id)
else:
old_representation = None
target_representation = self.file.by_id(obj_data.BIMMeshProperties.ifc_definition_id)
obj.data = obj_data
result = map_representation.Usecase(
self.file,
{
"product": product,
"representation": target_representation,
},
).execute()
result = map_representation.Usecase(self.file, {"representation": target_representation}).execute()
assign_representation.Usecase(self.file, {"product": product, "representation": result}).execute()
if old_representation:
bpy.ops.bim.remove_representation(ifc_definition_id=old_representation.id())
Data.load(obj.BIMObjectProperties.ifc_definition_id)
return {"FINISHED"}
@@ -1,5 +1,6 @@
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, settings=None):
self.file = file
@@ -8,6 +9,20 @@ class Usecase:
self.settings[key] = value
def execute(self):
if self.settings["representation"].RepresentationType == "MappedRepresentation":
return self.remove_mapped_representation_portion_only()
return self.remove_entire_representation_tree()
def remove_mapped_representation_portion_only(self):
dummy_context = self.file.create_entity("IfcRepresentationContext")
dummy_representation_map = self.file.createIfcRepresentationMap()
self.settings["representation"].ContextOfItems = dummy_context
for item in self.settings["representation"].Items:
item.MappingSource = dummy_representation_map
ifcopenshell.util.element.remove_deep(self.file, self.settings["representation"])
self.file.remove(self.settings["representation"])
def remove_entire_representation_tree(self):
dummy_context = self.file.create_entity("IfcRepresentationContext")
for subelement in self.file.traverse(self.settings["representation"]):
if subelement.is_a("IfcRepresentationItem") and subelement.StyledByItem:
@@ -26,11 +41,4 @@ class Usecase:
self.file.remove(inverse)
else:
assigned_items.remove(element)
inverse.AssignedItems == list(assigned_items)
elif inverse.is_a("IfcProductRepresentation"):
representations = set(inverse.Representations)
if len(representations) == 1:
self.file.remove(inverse)
else:
representations.remove(element)
inverse.Representations = list(representations)
inverse.AssignedItems = list(assigned_items)
@@ -41,7 +41,7 @@ class BIM_PT_representations(Panel):
op = row.operator("bim.switch_representation", icon="OUTLINER_DATA_MESH", text="")
op.ifc_definition_id = ifc_definition_id
op.disable_opening_subtractions = False
row.operator("bim.remove_representation", icon="X", text="").ifc_definition_id = ifc_definition_id
row.operator("bim.remove_representation", icon="X", text="").representation_id = ifc_definition_id
class BIM_PT_mesh(Panel):
@@ -0,0 +1,57 @@
import blenderbim.bim.module.geometry.remove_representation as remove_representation
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, settings=None):
self.file = file
self.settings = {"product": None, "representation": None}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
if self.settings["product"].is_a("IfcProduct"):
self.unassign_product_representation(self.settings["product"], self.settings["representation"])
elif self.settings["product"].is_a("IfcTypeProduct"):
self.unassign_type_representation()
def unassign_product_representation(self, product, representation):
representations = list(product.Representation.Representations or [])
representations.remove(representation)
if not representations:
self.file.remove(product.Representation)
else:
product.Representation.Representations = representations
def unassign_type_representation(self):
for representation_map in self.settings["product"].RepresentationMaps or []:
if representation_map.MappedRepresentation == self.settings["representation"]:
self.unassign_products_using_mapped_representation(representation_map)
self.remove_representation_map_only(representation_map)
break
self.settings["product"].RepresentationMaps = self.settings["product"].RepresentationMaps or None
def remove_representation_map_only(self, representation_map):
dummy_representation = self.file.createIfcShapeRepresentation()
representation_map.MappedRepresentation = dummy_representation
ifcopenshell.util.element.remove_deep(self.file, representation_map)
self.file.remove(representation_map)
def unassign_products_using_mapped_representation(self, representation_map):
mapped_representations = []
just_representations = []
for map_usage in representation_map.MapUsage or []:
for inverse in self.file.get_inverse(map_usage):
if not inverse.is_a("IfcShapeRepresentation"):
continue
for definition in inverse.OfProductRepresentation or []:
for product in definition.ShapeOfProduct or []:
mapped_representations.append({
"product": product,
"representation": inverse
})
just_representations.append(inverse)
for item in mapped_representations:
self.unassign_product_representation(item["product"], item["representation"])
for representation in just_representations:
remove_representation.Usecase(self.file, {"representation": representation}).execute()
@@ -0,0 +1,53 @@
import ifcopenshell
import ifcopenshell.util.unit
import ifcopenshell.util.placement
from mathutils import Matrix # For now, we depend on Blender
from blenderbim.bim.module.owner.api import create_owner_history
class Usecase:
def __init__(self, file, settings=None):
self.file = file
self.settings = {
"AxisTag": "A",
"AxisCurve": None, # A Blender object
"SameSense": True,
"UVWAxes": "UAxes", # Choose which axes
"Grid": None,
}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
points = [
Matrix(ifcopenshell.util.placement.get_local_placement(self.settings["Grid"].ObjectPlacement)).inverted()
@ (self.settings["AxisCurve"].matrix_world @ v.co)
for v in self.settings["AxisCurve"].data.vertices[0:2]
]
self.settings["AxisCurve"] = self.file.createIfcPolyline(
[
self.create_cartesian_point(points[0][0], points[0][1], points[0][2]),
self.create_cartesian_point(points[1][0], points[1][1], points[1][2]),
]
)
element = self.file.create_entity("IfcGridAxis", **{
"AxisTag": self.settings["AxisTag"],
"AxisCurve": self.settings["AxisCurve"],
"SameSense": self.settings["SameSense"]
})
axes = list(getattr(self.settings["Grid"], self.settings["UVWAxes"]) or [])
axes.append(element)
setattr(self.settings["Grid"], self.settings["UVWAxes"], axes)
return element
def create_cartesian_point(self, x, y, z=None):
x = self.convert_si_to_unit(x)
y = self.convert_si_to_unit(y)
if z is None:
return self.file.createIfcCartesianPoint((x, y))
z = self.convert_si_to_unit(z)
return self.file.createIfcCartesianPoint((x, y, z))
def convert_si_to_unit(self, co):
return co / self.settings["unit_scale"]
@@ -3,8 +3,8 @@ import math
import ifcopenshell
from bpy.types import Operator
from bpy.props import FloatProperty
from bpy_extras.object_utils import AddObjectHelper, object_data_add
from mathutils import Vector
from blenderbim.bim.ifc import IfcStore
def add_object(self, context):
@@ -79,7 +79,11 @@ def add_object(self, context):
faces = [[0, 1, 2, 3, 4, 5]]
mesh = bpy.data.meshes.new(name="Dumb Door Profile")
mesh.from_pydata(verts, edges, faces)
obj = object_data_add(context, mesh, operator=self)
obj = bpy.data.objects.new("Door Profile", mesh)
context.view_layer.active_layer_collection.collection.objects.link(obj)
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
bpy.ops.object.convert(target="CURVE")
# Door lining sweep
@@ -93,7 +97,11 @@ def add_object(self, context):
faces = []
mesh = bpy.data.meshes.new(name="Dumb Door")
mesh.from_pydata(verts, edges, faces)
obj2 = object_data_add(context, mesh, operator=self)
obj2 = bpy.data.objects.new("Door", mesh)
context.view_layer.active_layer_collection.collection.objects.link(obj2)
bpy.context.view_layer.objects.active = obj2
obj2.select_set(True)
bpy.ops.object.convert(target="CURVE")
obj2.data.dimensions = "2D"
@@ -116,10 +124,14 @@ def add_object(self, context):
faces = [[0, 1, 2, 3]]
mesh = bpy.data.meshes.new(name="Dumb Door Panel")
mesh.from_pydata(verts, edges, faces)
obj3 = object_data_add(context, mesh, operator=self)
obj3 = bpy.data.objects.new("Door Panel", mesh)
modifier = obj3.modifiers.new("Panel Height", "SOLIDIFY")
modifier.offset = 1
modifier.thickness = self.overall_height - 0.045
context.view_layer.active_layer_collection.collection.objects.link(obj3)
bpy.context.view_layer.objects.active = obj3
obj3.select_set(True)
bpy.ops.object.convert(target="MESH")
ctx = bpy.context.copy()
@@ -138,11 +150,16 @@ def add_object(self, context):
faces = [[0, 1, 2, 3]]
mesh = bpy.data.meshes.new(name="Dumb Door Opening")
mesh.from_pydata(verts, edges, faces)
obj4 = object_data_add(context, mesh, operator=self)
obj4 = bpy.data.objects.new("Door Opening", mesh)
modifier = obj4.modifiers.new("Panel Height", "SOLIDIFY")
modifier.offset = -1
modifier.thickness = self.overall_height + 0.1
context.view_layer.active_layer_collection.collection.objects.link(obj4)
bpy.context.view_layer.objects.active = obj4
obj4.select_set(True)
bpy.ops.object.convert(target="MESH")
obj4.display_type = "WIRE"
obj4.parent = obj2
obj4.matrix_parent_inverse = obj2.matrix_world.inverted()
@@ -150,6 +167,11 @@ def add_object(self, context):
obj4.name = "Door Opening"
obj2.name = "Door"
if IfcStore.get_file():
bpy.ops.bim.assign_class(obj=obj2.name, ifc_class="IfcDoor")
obj2.location = context.scene.cursor.location
#obj2.data.name = "Model/Body/MODEL_VIEW/" + guid
#obj2.data.use_fake_user = True
@@ -165,10 +187,9 @@ def add_object(self, context):
#rep.target_view = "PLAN_VIEW"
class BIM_OT_add_object(Operator, AddObjectHelper):
class BIM_OT_add_object(Operator):
bl_idname = "mesh.add_door"
bl_label = "Dumb Door"
bl_options = {"REGISTER", "UNDO"}
overall_width: FloatProperty(name="Overall Width", default=0.85)
overall_height: FloatProperty(name="Overall Height", default=2.1)
@@ -1,19 +1,16 @@
import bpy
import blenderbim.bim.module.grid.create_grid_axis as create_grid_axis
from bpy.types import Operator
from bpy.props import FloatProperty, IntProperty
from bpy_extras.object_utils import AddObjectHelper, object_data_add
from mathutils import Vector
from blenderbim.bim.ifc import IfcStore
def add_object(self, context):
obj = object_data_add(context, None, operator=self)
obj.name = "IfcGrid/Grid"
name = obj.name.split("/")[1]
obj = bpy.data.objects.new("Grid", None)
obj.name = "Grid"
default_collection = obj.users_collection[0]
default_collection.objects.unlink(obj)
collection = bpy.data.collections.new("IfcGrid/" + name)
collection = bpy.data.collections.new(obj.name)
has_site_collection = False
for child in bpy.context.view_layer.layer_collection.children:
if "IfcProject/" not in child.name:
@@ -28,6 +25,13 @@ def add_object(self, context):
bpy.context.view_layer.active_layer_collection.collection.children.link(collection)
collection.objects.link(obj)
self.file = IfcStore.get_file()
if self.file:
bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcGrid")
grid = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
if has_site_collection:
bpy.ops.bim.assign_container(relating_structure=grandchild.name, related_element=obj.name)
axes_collection = bpy.data.collections.new("UAxes")
collection.children.link(axes_collection)
for i in range(0, self.total_u):
@@ -39,14 +43,16 @@ def add_object(self, context):
faces = []
mesh = bpy.data.meshes.new(name="Grid Axis")
mesh.from_pydata(verts, edges, faces)
obj = object_data_add(context, mesh, operator=self)
tag = chr(ord("A") + i)
obj.name = "IfcGridAxis/" + tag
default_collection.objects.unlink(obj)
obj = bpy.data.objects.new(f"IfcGridAxis/{tag}", mesh)
axes_collection.objects.link(obj)
attribute = obj.BIMObjectProperties.attributes.add()
attribute.name = "AxisTag"
attribute.string_value = tag
if self.file:
result = create_grid_axis.Usecase(
self.file, {"AxisTag": tag, "AxisCurve": obj, "UVWAxes": "UAxes", "Grid": grid}
).execute()
obj.BIMObjectProperties.ifc_definition_id = result.id()
axes_collection = bpy.data.collections.new("VAxes")
collection.children.link(axes_collection)
@@ -59,17 +65,19 @@ def add_object(self, context):
faces = []
mesh = bpy.data.meshes.new(name="Grid Axis")
mesh.from_pydata(verts, edges, faces)
obj = object_data_add(context, mesh, operator=self)
tag = str(i + 1).zfill(2)
obj.name = "IfcGridAxis/" + tag
default_collection.objects.unlink(obj)
obj = bpy.data.objects.new(f"IfcGridAxis/{tag}", mesh)
axes_collection.objects.link(obj)
attribute = obj.BIMObjectProperties.attributes.add()
attribute.name = "AxisTag"
attribute.string_value = tag
if IfcStore.get_file():
result = create_grid_axis.Usecase(
self.file, {"AxisTag": tag, "AxisCurve": obj, "UVWAxes": "VAxes", "Grid": grid}
).execute()
obj.BIMObjectProperties.ifc_definition_id = result.id()
class BIM_OT_add_object(Operator, AddObjectHelper):
class BIM_OT_add_object(Operator):
bl_idname = "mesh.add_grid"
bl_label = "Grid"
bl_options = {"REGISTER", "UNDO"}
@@ -1,8 +1,8 @@
import bpy
from bpy.types import Operator
from bpy.props import FloatProperty
from bpy_extras.object_utils import AddObjectHelper, object_data_add
from mathutils import Vector
from blenderbim.bim.ifc import IfcStore
def add_object(self, context):
@@ -17,18 +17,21 @@ def add_object(self, context):
mesh = bpy.data.meshes.new(name="Dumb Slab")
mesh.from_pydata(verts, edges, faces)
obj = object_data_add(context, mesh, operator=self)
obj = bpy.data.objects.new("Slab", mesh)
modifier = obj.modifiers.new("Slab Depth", "SOLIDIFY")
modifier.use_even_offset = True
modifier.offset = 1
modifier.thickness = self.depth
obj.name = "Slab"
context.view_layer.active_layer_collection.collection.objects.link(obj)
if IfcStore.get_file():
bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcSlab")
obj.location = context.scene.cursor.location
class BIM_OT_add_object(Operator, AddObjectHelper):
class BIM_OT_add_object(Operator):
bl_idname = "mesh.add_slab"
bl_label = "Dumb Slab"
bl_options = {"REGISTER", "UNDO"}
length: FloatProperty(name="Length", default=2)
width: FloatProperty(name="Width", default=2)
@@ -1,8 +1,8 @@
import bpy
from bpy.types import Operator
from bpy.props import FloatProperty, BoolProperty
from bpy_extras.object_utils import AddObjectHelper, object_data_add
from mathutils import Vector
from blenderbim.bim.ifc import IfcStore
def add_object(self, context):
@@ -25,7 +25,8 @@ def add_object(self, context):
mesh = bpy.data.meshes.new(name="Dumb Wall")
mesh.from_pydata(verts, edges, faces)
obj = object_data_add(context, mesh, operator=self)
obj = bpy.data.objects.new("Wall", mesh)
context.view_layer.active_layer_collection.collection.objects.link(obj)
if not self.use_plane:
modifier = obj.modifiers.new("Wall Height", "SCREW")
modifier.angle = 0
@@ -39,13 +40,15 @@ def add_object(self, context):
modifier.use_even_offset = True
modifier.thickness = self.width
obj.name = "Wall"
if IfcStore.get_file():
bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcWall")
obj.location = context.scene.cursor.location
return obj
class BIM_OT_add_object(Operator, AddObjectHelper):
class BIM_OT_add_object(Operator):
bl_idname = "mesh.add_wall"
bl_label = "Dumb Wall"
bl_options = {"REGISTER", "UNDO"}
height: FloatProperty(name="Height", default=3)
length: FloatProperty(name="Length", default=1)
@@ -3,8 +3,8 @@ import math
import ifcopenshell
from bpy.types import Operator
from bpy.props import FloatProperty
from bpy_extras.object_utils import AddObjectHelper, object_data_add
from mathutils import Vector
from blenderbim.bim.ifc import IfcStore
def add_object(self, context):
@@ -64,7 +64,11 @@ def add_object(self, context):
faces = [[0, 1, 2, 3]]
mesh = bpy.data.meshes.new(name="Dumb Window Profile")
mesh.from_pydata(verts, edges, faces)
obj = object_data_add(context, mesh, operator=self)
obj = bpy.data.objects.new("Window Profile", mesh)
context.view_layer.active_layer_collection.collection.objects.link(obj)
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
bpy.ops.object.convert(target="CURVE")
# Window lining sweep
@@ -78,7 +82,11 @@ def add_object(self, context):
faces = []
mesh = bpy.data.meshes.new(name="Dumb Window")
mesh.from_pydata(verts, edges, faces)
obj2 = object_data_add(context, mesh, operator=self)
obj2 = bpy.data.objects.new("Window", mesh)
context.view_layer.active_layer_collection.collection.objects.link(obj2)
bpy.context.view_layer.objects.active = obj2
obj2.select_set(True)
bpy.ops.object.convert(target="CURVE")
obj2.data.splines[0].use_cyclic_u = True
@@ -102,10 +110,14 @@ def add_object(self, context):
faces = [[0, 1, 2, 3]]
mesh = bpy.data.meshes.new(name="Dumb Window Panel")
mesh.from_pydata(verts, edges, faces)
obj3 = object_data_add(context, mesh, operator=self)
obj3 = bpy.data.objects.new("Window Panel", mesh)
modifier = obj3.modifiers.new("Panel Height", "SOLIDIFY")
modifier.offset = 1
modifier.thickness = self.overall_height - 0.08
context.view_layer.active_layer_collection.collection.objects.link(obj3)
bpy.context.view_layer.objects.active = obj3
obj3.select_set(True)
bpy.ops.object.convert(target="MESH")
ctx = bpy.context.copy()
@@ -124,10 +136,14 @@ def add_object(self, context):
faces = [[0, 1, 2, 3]]
mesh = bpy.data.meshes.new(name="Dumb Window Opening")
mesh.from_pydata(verts, edges, faces)
obj4 = object_data_add(context, mesh, operator=self)
obj4 = bpy.data.objects.new("Window Opening", mesh)
modifier = obj4.modifiers.new("Panel Height", "SOLIDIFY")
modifier.offset = -1
modifier.thickness = self.overall_height
context.view_layer.active_layer_collection.collection.objects.link(obj4)
bpy.context.view_layer.objects.active = obj4
obj4.select_set(True)
bpy.ops.object.convert(target="MESH")
obj4.display_type = "WIRE"
obj4.parent = obj2
@@ -135,7 +151,10 @@ def add_object(self, context):
obj4.hide_render = True
obj4.name = "Window Opening"
if IfcStore.get_file():
bpy.ops.bim.assign_class(obj=obj2.name, ifc_class="IfcWindow")
obj2.name = "Window"
obj2.location = context.scene.cursor.location
#obj2.data.name = "Model/Body/MODEL_VIEW/" + guid
#obj2.data.use_fake_user = True
@@ -150,10 +169,9 @@ def add_object(self, context):
#rep.target_view = "PLAN_VIEW"
class BIM_OT_add_object(Operator, AddObjectHelper):
class BIM_OT_add_object(Operator):
bl_idname = "mesh.add_window"
bl_label = "Dumb Window"
bl_options = {"REGISTER", "UNDO"}
overall_width: FloatProperty(name="Overall Width", default=0.7)
overall_height: FloatProperty(name="Overall Height", default=1)
@@ -63,6 +63,7 @@ class ExportIFC(bpy.types.Operator):
bl_idname = "export_ifc.bim"
bl_label = "Export IFC"
filename_ext = ".ifc"
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml;*.ifcjson", options={"HIDDEN"})
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
json_version: bpy.props.EnumProperty(items=[("4", "4", ""), ("5a", "5a", "")], name="IFC JSON Version")
json_compact: bpy.props.BoolProperty(name="Export Compact IFCJSON", default=False)
@@ -111,7 +112,6 @@ class ImportIFC(bpy.types.Operator, ImportHelper):
filename_ext = ".ifc"
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
should_import_type_representations: bpy.props.BoolProperty(name="Import Type Representations", default=False)
should_import_spaces: bpy.props.BoolProperty(name="Import Spaces", default=False)
should_auto_set_workarounds: bpy.props.BoolProperty(name="Automatically Set Vendor Workarounds", default=True)
should_use_cpu_multiprocessing: bpy.props.BoolProperty(name="Import with CPU Multiprocessing", default=True)
@@ -138,7 +138,6 @@ class ImportIFC(bpy.types.Operator, ImportHelper):
)
settings = import_ifc.IfcImportSettings.factory(context, self.filepath, logger)
settings.should_import_type_representations = self.should_import_type_representations
settings.should_import_spaces = self.should_import_spaces
settings.should_auto_set_workarounds = self.should_auto_set_workarounds
settings.should_use_cpu_multiprocessing = self.should_use_cpu_multiprocessing
@@ -10,6 +10,7 @@ from . import annotation
from . import helper
from mathutils import Vector
from mathutils import geometry
from blenderbim.bim.ifc import IfcStore
try:
from OCC.Core import BRep, BRepTools, TopExp, TopAbs
@@ -140,11 +141,7 @@ class SvgWriter:
line["marker-start"] = "url(#grid-marker)"
line["marker-end"] = "url(#grid-marker)"
line["stroke-dasharray"] = "12.5, 3, 3, 3"
axis_tag = grid_obj.BIMObjectProperties.attributes.get("AxisTag")
if axis_tag:
axis_tag = axis_tag.string_value
else:
axis_tag = grid_obj.name.split("/")[1]
axis_tag = IfcStore.get_file().by_id(grid_obj.BIMObjectProperties.ifc_definition_id).AxisTag
self.svg.add(
self.svg.text(
axis_tag,
+13 -1
View File
@@ -339,7 +339,13 @@ int main(int argc, char** argv) {
("default-material-file", new po::typed_value<path_t, char_t>(&default_material_filename),
"Specifies a material file that describes the material object types will have"
"if an object does not have any specified material in the IFC file.")
("validate", "Checks whether geometrical output conforms to the included explicit quantities.");
("validate", "Checks whether geometrical output conforms to the included explicit quantities.")
("no-wire-intersection-check", "Skip wire intersection check.")
("no-wire-intersection-tolerance", "Set wire intersection tolerance to 0.")
("strict-tolerance", "Use exact tolerance from model. Default is a 10 "
"times increase for more permissive edge curves and fewer artifacts after "
"boolean operations at the expense of geometric detail "
"due to vertex collapsing and wire intersection fuzziness.");
std::string bounds;
#ifdef HAVE_ICU
@@ -481,6 +487,9 @@ int main(int argc, char** argv) {
const bool generate_uvs = vmap.count("generate-uvs") != 0;
const bool validate = vmap.count("validate") != 0;
const bool edge_arrows = vmap.count("edge-arrows") != 0;
const bool no_wire_intersection_check = vmap.count("no-wire-intersection-check") != 0;
const bool no_wire_intersection_tolerance = vmap.count("no-wire-intersection-tolerance") != 0;
const bool strict_tolerance = vmap.count("strict-tolerance") != 0;
if (!quiet || vmap.count("version")) {
print_version();
@@ -741,6 +750,9 @@ int main(int argc, char** argv) {
settings.set(IfcGeom::IteratorSettings::SITE_LOCAL_PLACEMENT, site_local_placement);
settings.set(IfcGeom::IteratorSettings::BUILDING_LOCAL_PLACEMENT, building_local_placement);
settings.set(IfcGeom::IteratorSettings::VALIDATE_QUANTITIES, validate);
settings.set(IfcGeom::IteratorSettings::NO_WIRE_INTERSECTION_CHECK, no_wire_intersection_check);
settings.set(IfcGeom::IteratorSettings::NO_WIRE_INTERSECTION_TOLERANCE, no_wire_intersection_tolerance);
settings.set(IfcGeom::IteratorSettings::STRICT_TOLERANCE, strict_tolerance);
settings.set(SerializerSettings::USE_ELEMENT_NAMES, use_element_names);
settings.set(SerializerSettings::USE_ELEMENT_GUIDS, use_element_guids);
+6
View File
@@ -224,6 +224,9 @@ private:
double modelling_precision;
double dimensionality;
double layerset_first;
double no_wire_intersection_check;
double no_wire_intersection_tolerance;
double precision_factor;
// For stopping PlacementRelTo recursion in convert(const IfcSchema::IfcObjectPlacement* l, gp_Trsf& trsf)
const IfcParse::declaration* placement_rel_to;
@@ -256,6 +259,9 @@ public:
, faceset_helper_(nullptr)
, layerset_first(-1.)
, disable_boolean_result(-1.)
, no_wire_intersection_check(-1)
, no_wire_intersection_tolerance(-1)
, precision_factor(10.)
{}
MAKE_TYPE_NAME(Kernel)(const MAKE_TYPE_NAME(Kernel)& other)
+28 -5
View File
@@ -1415,6 +1415,15 @@ void IfcGeom::Kernel::setValue(GeomValue var, double value) {
case GV_DISABLE_BOOLEAN_RESULT:
disable_boolean_result = value;
break;
case GV_NO_WIRE_INTERSECTION_CHECK:
no_wire_intersection_check = value;
break;
case GV_PRECISION_FACTOR:
precision_factor = value;
break;
case GV_NO_WIRE_INTERSECTION_TOLERANCE:
no_wire_intersection_tolerance = value;
break;
default:
throw std::runtime_error("Invalid setting");
}
@@ -1444,6 +1453,12 @@ double IfcGeom::Kernel::getValue(GeomValue var) const {
return layerset_first;
case GV_DISABLE_BOOLEAN_RESULT:
return disable_boolean_result;
case GV_NO_WIRE_INTERSECTION_CHECK:
return no_wire_intersection_check;
case GV_PRECISION_FACTOR:
return precision_factor;
case GV_NO_WIRE_INTERSECTION_TOLERANCE:
return no_wire_intersection_tolerance;
}
throw std::runtime_error("Invalid setting");
}
@@ -3794,6 +3809,11 @@ namespace {
}
bool IfcGeom::Kernel::wire_intersections(const TopoDS_Wire& wire, TopTools_ListOfShape& wires) {
if (getValue(GV_NO_WIRE_INTERSECTION_CHECK) > 0.) {
return false;
}
if (!wire.Closed()) {
wires.Append(wire);
return false;
@@ -3836,11 +3856,14 @@ bool IfcGeom::Kernel::wire_intersections(const TopoDS_Wire& wire, TopTools_ListO
// TopoDS_Face face = BRepBuilderAPI_MakeFace(wire, true).Face();
// ShapeAnalysis_Wire saw(wd, face, getValue(GV_PRECISION));
const double eps = faceset_helper_
// eps is added to both ends of the parametric domain, so 3. is chosen to be on the safe side here.
? (faceset_helper_->epsilon() / 3.)
// @todo re-evaluate 2. here for the reasons above:
: (std::min)(min_edge_length(wire) / 2., getValue(GV_PRECISION) * 10.);
double eps = 0;
if (getValue(GV_NO_WIRE_INTERSECTION_TOLERANCE) < 0.) {
eps = faceset_helper_
// eps is added to both ends of the parametric domain, so 3. is chosen to be on the safe side here.
? (faceset_helper_->epsilon() / 3.)
// @todo re-evaluate 2. here for the reasons above:
: (std::min)(min_edge_length(wire) / 2., getValue(GV_PRECISION) * 10.);
}
for (int i = 2; i < n; ++i) {
+17 -1
View File
@@ -350,7 +350,7 @@ namespace IfcGeom {
if (any_precision_encountered) {
// Some arbitrary factor that has proven to work better for the models in the set of test files.
lowest_precision_encountered *= 10.;
lowest_precision_encountered *= kernel.getValue(IfcGeom::Kernel::GV_PRECISION_FACTOR);
lowest_precision_encountered *= unit_magnitude;
if (lowest_precision_encountered < 1.e-7) {
@@ -1003,6 +1003,22 @@ namespace IfcGeom {
? +1.0
: -1.0
);
kernel.setValue(IfcGeom::Kernel::GV_NO_WIRE_INTERSECTION_CHECK,
settings.get(IteratorSettings::NO_WIRE_INTERSECTION_CHECK)
? +1.0
: -1.0
);
kernel.setValue(IfcGeom::Kernel::GV_NO_WIRE_INTERSECTION_TOLERANCE,
settings.get(IteratorSettings::NO_WIRE_INTERSECTION_TOLERANCE)
? +1.0
: -1.0
);
kernel.setValue(IfcGeom::Kernel::GV_PRECISION_FACTOR,
settings.get(IteratorSettings::STRICT_TOLERANCE)
? 1.0
: 10.0
);
kernel.setValue(IfcGeom::Kernel::GV_DISABLE_BOOLEAN_RESULT,
settings.get(IteratorSettings::DISABLE_BOOLEAN_RESULT)
? +1.0
+7 -1
View File
@@ -94,8 +94,14 @@ namespace IfcGeom
EDGE_ARROWS = 1 << 19,
/// Disables the evaluation of IfcBooleanResult and simply returns FirstOperand
DISABLE_BOOLEAN_RESULT = 1 << 20,
// Disables wire intersection checks
NO_WIRE_INTERSECTION_CHECK = 1 << 21,
// Set wire intersection tolerance to 0
NO_WIRE_INTERSECTION_TOLERANCE = 1 << 22,
// Sets kernel precision factor to 1
STRICT_TOLERANCE = 1 << 23,
/// Number of different setting flags.
NUM_SETTINGS = 20
NUM_SETTINGS = 24,
};
/// Used to store logical OR combination of setting flags.
typedef unsigned SettingField;
+28 -11
View File
@@ -869,21 +869,38 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcGeometricSet* l, IfcRepresenta
if ( !elements->size() ) return false;
bool part_succes = false;
const IfcGeom::SurfaceStyle* parent_style = get_style(l);
for ( IfcEntityList::it it = elements->begin(); it != elements->end(); ++ it ) {
for (IfcEntityList::it it = elements->begin(); it != elements->end(); ++it) {
IfcSchema::IfcGeometricSetSelect* element = *it;
TopoDS_Shape s;
if (convert_shape(element, s)) {
part_succes = true;
const IfcGeom::SurfaceStyle* style = 0;
if (element->declaration().is(IfcSchema::IfcPoint::Class())) {
style = get_style((IfcSchema::IfcPoint*) element);
} else if (element->declaration().is(IfcSchema::IfcCurve::Class())) {
style = get_style((IfcSchema::IfcCurve*) element);
} else if (element->declaration().is(IfcSchema::IfcSurface::Class())) {
style = get_style((IfcSchema::IfcSurface*) element);
if (shape_type(element) == ST_SHAPELIST) {
IfcRepresentationShapeItems items;
if (!(convert_shapes(element, items) && flatten_shape_list(items, s, false))) {
continue;
}
shapes.push_back(IfcRepresentationShapeItem(l->data().id(), s, style ? style : parent_style));
} else if (shape_type(element) == ST_SHAPE) {
if (!convert_shape(element, s)) {
continue;
}
} else if (shape_type(element) == ST_WIRE) {
TopoDS_Wire w;
if (!convert_wire(element, w)) {
continue;
}
s = w;
}
part_succes = true;
const IfcGeom::SurfaceStyle* style = 0;
if (element->declaration().is(IfcSchema::IfcPoint::Class())) {
style = get_style((IfcSchema::IfcPoint*) element);
}
else if (element->declaration().is(IfcSchema::IfcCurve::Class())) {
style = get_style((IfcSchema::IfcCurve*) element);
}
else if (element->declaration().is(IfcSchema::IfcSurface::Class())) {
style = get_style((IfcSchema::IfcSurface*) element);
}
shapes.push_back(IfcRepresentationShapeItem(l->data().id(), s, style ? style : parent_style));
}
return part_succes;
}
+5 -2
View File
@@ -53,8 +53,11 @@ namespace IfcGeom {
GV_PRECISION,
// Whether to process shapes of type Face or higher (1) Wire or lower (-1) or all (0)
GV_DIMENSIONALITY,
GV_LAYERSET_FIRST,
GV_DISABLE_BOOLEAN_RESULT
GV_LAYERSET_FIRST,
GV_DISABLE_BOOLEAN_RESULT,
GV_NO_WIRE_INTERSECTION_CHECK,
GV_PRECISION_FACTOR,
GV_NO_WIRE_INTERSECTION_TOLERANCE,
};
Kernel(IfcParse::IfcFile* file_ = 0);
+69 -50
View File
@@ -788,73 +788,92 @@ void SvgSerializer::write(const geometry_data& data) {
auto ot_arg = data.product->get("ObjectType");
if (!ot_arg->isNull()) {
object_type = (std::string) *ot_arg;
object_type.erase(std::remove_if(object_type.begin(), object_type.end(), [](char c) { return !std::isalnum(c); }), object_type.end());
}
if (data.product->declaration().is("IfcAnnotation") && // is an Annotation
object_type == "Dimension" && // with ObjectType='Dimension'
(proj.Magnitude() > 1.e-5) && // when projected onto the view has a length
zmin >= range.first && zmin <= range.second) // the Z-coords are within the range of the building storey
{
auto svg_name = data.svg_name;
if (object_type.size()) {
// postfix the object_type for CSS matching
boost::replace_all(svg_name, "class=\"IfcAnnotation\"", "class=\"IfcAnnotation " + object_type + "\"");
}
if (po == nullptr) {
if (storey) {
po = &start_path(pln, storey, data.svg_name);
po = &start_path(pln, storey, svg_name);
} else {
po = &start_path(pln, drawing_name, data.svg_name);
po = &start_path(pln, drawing_name, svg_name);
}
}
TopExp_Explorer exp(subshape, TopAbs_EDGE, TopAbs_FACE);
for (; exp.More(); exp.Next()) {
const auto& e = TopoDS::Edge(exp.Current());
TopoDS_Vertex v0, v1;
TopExp::Vertices(e, v0, v1);
gp_Pnt p0 = BRep_Tool::Pnt(v0);
gp_Pnt p1 = BRep_Tool::Pnt(v1);
// @todo should we take the average parameter value instead?
gp_XYZ center = (p0.XYZ() + p1.XYZ()) / 2.;
BRep_Builder B;
TopoDS_Wire W;
B.MakeWire(W);
B.Add(W, e);
write(*po, W);
if (object_type == "Dimension") {
TopExp_Explorer exp(subshape, TopAbs_EDGE, TopAbs_FACE);
for (; exp.More(); exp.Next()) {
const auto& e = TopoDS::Edge(exp.Current());
TopoDS_Vertex v0, v1;
TopExp::Vertices(e, v0, v1);
gp_Pnt p0 = BRep_Tool::Pnt(v0);
gp_Pnt p1 = BRep_Tool::Pnt(v1);
BRep_Builder B;
TopoDS_Wire W;
B.MakeWire(W);
B.Add(W, e);
write(*po, W);
// @todo should we take the average parameter value instead?
gp_XYZ center = (p0.XYZ() + p1.XYZ()) / 2.;
util::string_buffer path;
// dominant-baseline="central" is not well supported in IE.
// so we add a 0.35 offset to the dy of the tspans
path.add(" <text class=\"IfcAnnotation\" text-anchor=\"middle\" x=\"");
xcoords.push_back(path.add(center.X()));
path.add("\" y=\"");
ycoords.push_back(path.add(center.Y()));
path.add("\">");
std::vector<std::string> labels{};
GProp_GProps prop;
BRepGProp::LinearProperties(e, prop);
const double area = prop.Mass();
std::stringstream ss;
ss << std::setprecision(2) << std::fixed << std::showpoint << area;
labels.push_back(ss.str() + "m");
for (auto lit = labels.begin(); lit != labels.end(); ++lit) {
const auto& l = *lit;
double dy = labels.begin() == lit
? 0.35 - (labels.size() - 1.) / 2.
: 1.0; // <- dy is relative to the previous text element, so
// always 1 for successive spans.
path.add("<tspan x=\"");
util::string_buffer path;
// dominant-baseline="central" is not well supported in IE.
// so we add a 0.35 offset to the dy of the tspans
path.add(" <text class=\"IfcAnnotation\" text-anchor=\"middle\" x=\"");
xcoords.push_back(path.add(center.X()));
path.add("\" dy=\"");
path.add(boost::lexical_cast<std::string>(dy));
path.add("em\">");
path.add(l);
path.add("</tspan>");
path.add("\" y=\"");
ycoords.push_back(path.add(center.Y()));
path.add("\">");
std::vector<std::string> labels{};
GProp_GProps prop;
BRepGProp::LinearProperties(e, prop);
const double area = prop.Mass();
std::stringstream ss;
ss << std::setprecision(2) << std::fixed << std::showpoint << area;
labels.push_back(ss.str() + "m");
for (auto lit = labels.begin(); lit != labels.end(); ++lit) {
const auto& l = *lit;
double dy = labels.begin() == lit
? 0.35 - (labels.size() - 1.) / 2.
: 1.0; // <- dy is relative to the previous text element, so
// always 1 for successive spans.
path.add("<tspan x=\"");
xcoords.push_back(path.add(center.X()));
path.add("\" dy=\"");
path.add(boost::lexical_cast<std::string>(dy));
path.add("em\">");
path.add(l);
path.add("</tspan>");
}
path.add("</text>");
po->second.push_back(path);
}
path.add("</text>");
po->second.push_back(path);
} else if (object_type == "Symbol") {
TopExp_Explorer exp(subshape, TopAbs_WIRE, TopAbs_FACE);
for (; exp.More(); exp.Next()) {
const auto& W = TopoDS::Wire(exp.Current());
write(*po, W);
}
}
// We're finished processing IfcAnnotation instances
continue;
}
@@ -1396,7 +1415,7 @@ void SvgSerializer::writeHeader() {
" .IfcSpace path {\n"
" fill-opacity: .2;\n"
" }\n"
" .IfcAnnotation path {\n"
" .dimension path {\n"
" marker-end: url(#arrowend);\n"
" marker-start: url(#arrowstart);\n"
" }\n";
+3 -3
View File
@@ -73,7 +73,7 @@ if not (%1)==() (
)
pushd ..
set CMAKE_INSTALL_PREFIX=%CD%\installed-%GEN_SHORTHAND%
set CMAKE_INSTALL_PREFIX=%CD%\_installed-%GEN_SHORTHAND%
popd
IF NOT EXIST ..\%BUILD_DIR%. mkdir ..\%BUILD_DIR%
@@ -136,9 +136,9 @@ if not (%1)==() if exist CMakeCache.txt. del /Q CMakeCache.txt
call cecho.cmd 0 13 "Running CMake for %PROJECT_NAME%."
IF DEFINED VS_TOOLSET (
cmake.exe %CMAKELISTS_DIR% -G %GENERATOR% -A %VS_PLATFORM% -T %VS_TOOLSET% -DCMAKE_INSTALL_PREFIX="%CMAKE_INSTALL_PREFIX%" %ARGUMENTS%
cmake.exe %CMAKELISTS_DIR% -G %GENERATOR% -A %VS_PLATFORM% -T %VS_TOOLSET% -DCMAKE_INSTALL_PREFIX="%CMAKE_INSTALL_PREFIX%" -DBoost_NO_BOOST_CMAKE=On %ARGUMENTS%
) ELSE (
cmake.exe %CMAKELISTS_DIR% -G %GENERATOR% -A %VS_PLATFORM% -DCMAKE_INSTALL_PREFIX="%CMAKE_INSTALL_PREFIX%" %ARGUMENTS%
cmake.exe %CMAKELISTS_DIR% -G %GENERATOR% -A %VS_PLATFORM% -DCMAKE_INSTALL_PREFIX="%CMAKE_INSTALL_PREFIX%" -DBoost_NO_BOOST_CMAKE=On %ARGUMENTS%
)
IF NOT %ERRORLEVEL%==0 GOTO :Error