mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-14 03:14:23 +00:00
Purge OCC dependency and old drawing system. See #1153.
This commit is contained in:
@@ -1,45 +1,6 @@
|
||||
VERSION:=`date '+%y%m%d'`
|
||||
PYVERSION:=py37
|
||||
|
||||
ifeq ($(PLATFORM), win)
|
||||
ifeq ($(PYVERSION), py37)
|
||||
PYTHONOCC_URL:=https://anaconda.org/DLR-SC/pythonocc-core/0.17.3/download/win-64/pythonocc-core-0.17.3-py37he980bc4_10.tar.bz2
|
||||
OCE_URL:=https://anaconda.org/DLR-SC/oce/0.17.2/download/win-64/oce-0.17.2-he980bc4_14.tar.bz2
|
||||
TBB_URL:=https://anaconda.org/DLR-SC/tbb/2019.5/download/win-64/tbb-2019.5-he980bc4_0.tar.bz2
|
||||
endif
|
||||
ifeq ($(PYVERSION), py39)
|
||||
PYTHONOCC_URL:=https://anaconda.org/conda-forge/pythonocc-core/7.4.1/download/win-64/pythonocc-core-7.4.1-py39h3d1c7c5_0.tar.bz2
|
||||
OCE_URL:=https://anaconda.org/conda-forge/occt/7.4.0/download/win-64/occt-7.4.0-h823b557_3.tar.bz2
|
||||
TBB_URL:=https://anaconda.org/conda-forge/tbb/2020.2/download/win-64/tbb-2020.2-h2d74725_4.tar.bz2
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), macos)
|
||||
ifeq ($(PYVERSION), py37)
|
||||
PYTHONOCC_URL:=https://anaconda.org/DLR-SC/pythonocc-core/0.17.3/download/osx-64/pythonocc-core-0.17.3-py37h04f5b5a_10.tar.bz2
|
||||
OCE_URL:=https://anaconda.org/DLR-SC/oce/0.17.2/download/osx-64/oce-0.17.2-h04f5b5a_12.tar.bz2
|
||||
TBB_URL:=https://anaconda.org/DLR-SC/tbb/4.3.6/download/osx-64/tbb-4.3.6-0.tar.bz2
|
||||
endif
|
||||
ifeq ($(PYVERSION), py39)
|
||||
PYTHONOCC_URL:=https://anaconda.org/conda-forge/pythonocc-core/7.4.1/download/osx-64/pythonocc-core-7.4.1-py39h4d29fe3_0.tar.bz2
|
||||
OCE_URL:=https://anaconda.org/conda-forge/occt/7.4.0/download/osx-64/occt-7.4.0-hb9b6dc7_3.tar.bz2
|
||||
TBB_URL:=https://anaconda.org/conda-forge/tbb/2020.2/download/osx-64/tbb-2020.2-h940c156_4.tar.bz2
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), linux)
|
||||
ifeq ($(PYVERSION), py37)
|
||||
PYTHONOCC_URL:=https://anaconda.org/DLR-SC/pythonocc-core/0.17.3/download/linux-64/pythonocc-core-0.17.3-py37h6bb024c_10.tar.bz2
|
||||
OCE_URL:=https://anaconda.org/DLR-SC/oce/0.17.2/download/linux-64/oce-0.17.2-h6bb024c_14.tar.bz2
|
||||
TBB_URL:=https://anaconda.org/DLR-SC/tbb/4.3.6/download/linux-64/tbb-4.3.6-0.tar.bz2
|
||||
endif
|
||||
ifeq ($(PYVERSION), py39)
|
||||
PYTHONOCC_URL:=https://anaconda.org/conda-forge/pythonocc-core/7.4.1/download/linux-64/pythonocc-core-7.4.1-py39h465cb30_0.tar.bz2
|
||||
OCE_URL:=https://anaconda.org/conda-forge/occt/7.4.0/download/linux-64/occt-7.4.0-h9121d39_3.tar.bz2
|
||||
TBB_URL:=https://anaconda.org/conda-forge/tbb/2020.2/download/linux-64/tbb-2020.2-h4bd325d_4.tar.bz2
|
||||
endif
|
||||
endif
|
||||
|
||||
.PHONY: dist
|
||||
dist:
|
||||
ifndef PLATFORM
|
||||
@@ -76,66 +37,6 @@ else
|
||||
endif
|
||||
rm -rf dist/working
|
||||
|
||||
# Provides Python OCC functionality for cutting IFC geometry for construction documentation
|
||||
mkdir dist/working
|
||||
cd dist/working && wget $(PYTHONOCC_URL)
|
||||
cd dist/working && tar -xjvf pythonocc-core*
|
||||
ifeq ($(PLATFORM), win)
|
||||
cd dist/working && cp -r Lib/site-packages/OCC ../blenderbim/libs/site/packages/
|
||||
else
|
||||
ifeq ($(PYVERSION), py37)
|
||||
cd dist/working && cp -r lib/python3.7/site-packages/OCC ../blenderbim/libs/site/packages/
|
||||
endif
|
||||
ifeq ($(PYVERSION), py39)
|
||||
cd dist/working && cp -r lib/python3.9/site-packages/OCC ../blenderbim/libs/site/packages/
|
||||
endif
|
||||
endif
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by Python OCC
|
||||
mkdir dist/working
|
||||
cd dist/working && wget $(OCE_URL)
|
||||
cd dist/working && tar -xjvf oc*
|
||||
ifeq ($(PLATFORM), win)
|
||||
ifeq ($(PYVERSION), py37)
|
||||
cd dist/working && cp -r Library/bin/* ../blenderbim/libs/site/packages/OCC/
|
||||
endif
|
||||
ifeq ($(PYVERSION), py39)
|
||||
cd dist/working && cp -r Library/bin/* ../blenderbim/libs/site/packages/OCC/Core/
|
||||
endif
|
||||
else
|
||||
# Unix Conda builds of PythonOCC expect OCE libs to have a RPATH of ../../../
|
||||
cd dist/working && cp -r lib/* ../blenderbim/libs/
|
||||
endif
|
||||
ifeq ($(PLATFORM), linux)
|
||||
rm -rf dist/blenderbim/libs/oce-0.17
|
||||
endif
|
||||
rm -rf dist/working
|
||||
|
||||
# Required by OpenCascade
|
||||
mkdir dist/working
|
||||
cd dist/working && wget $(TBB_URL)
|
||||
cd dist/working && tar -xjvf tbb*
|
||||
ifeq ($(PLATFORM), win)
|
||||
ifeq ($(PYVERSION), py37)
|
||||
cd dist/working && cp -r Library/bin/* ../blenderbim/libs/site/packages/OCC/
|
||||
endif
|
||||
ifeq ($(PYVERSION), py39)
|
||||
cd dist/working && cp -r Library/bin/* ../blenderbim/libs/site/packages/OCC/Core/
|
||||
endif
|
||||
else
|
||||
cd dist/working && cp -r lib/* ../blenderbim/libs/
|
||||
endif
|
||||
rm -rf dist/working
|
||||
|
||||
ifeq ($(PLATFORM), macos)
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://blenderbim.org/builds/patch-blender28-bim-macos.zip
|
||||
cd dist/working && unzip patch*
|
||||
cd dist/working && cp -r *.dylib ../blenderbim/libs/
|
||||
rm -rf dist/working
|
||||
endif
|
||||
|
||||
# Provides dependencies that are part of IfcOpenShell
|
||||
mkdir dist/working
|
||||
cd dist/working && wget https://github.com/IfcOpenShell/IfcOpenShell/archive/v0.6.0.zip
|
||||
|
||||
@@ -15,6 +15,7 @@ import site
|
||||
|
||||
# process *.pth in /libs/site/packages to setup globally importable modules
|
||||
# 3 levels deep required by occ static ../../ path
|
||||
# TODO: 3 levels deep is no longer required as we no longer bundle OCC
|
||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||
site.addsitedir(os.path.join(cwd, "libs", "site", "packages"))
|
||||
|
||||
|
||||
@@ -73,107 +73,3 @@ def export_attributes(props, callback=None):
|
||||
elif attribute.data_type == "enum":
|
||||
attributes[attribute.name] = attribute.enum_value
|
||||
return attributes
|
||||
|
||||
|
||||
# TODO: migrate the below helper functions into the drawing module, since it is specific to that module
|
||||
|
||||
|
||||
def parse_diagram_scale(camera):
|
||||
"""Returns numeric value of scale"""
|
||||
if camera.BIMCameraProperties.diagram_scale == "CUSTOM":
|
||||
_, fraction = camera.BIMCameraProperties.custom_diagram_scale.split("|")
|
||||
else:
|
||||
_, fraction = camera.BIMCameraProperties.diagram_scale.split("|")
|
||||
numerator, denominator = fraction.split("/")
|
||||
return float(numerator) / float(denominator)
|
||||
|
||||
|
||||
def ortho_view_frame(camera, margin=0.015):
|
||||
"""Calculates 2d bounding box of camera view area.
|
||||
|
||||
Similar to `bpy.types.Camera.view_frame`
|
||||
|
||||
:arg camera: camera of drawing
|
||||
:type camera: bpy.types.Camera + BIMCameraProperties
|
||||
:arg margin: margins, in scene units
|
||||
:type margin: float
|
||||
:return: (xmin, xmax, ymin, ymax, zmin, zmax) in local camera coordinates
|
||||
"""
|
||||
aspect = camera.BIMCameraProperties.raster_y / camera.BIMCameraProperties.raster_x
|
||||
size = camera.ortho_scale
|
||||
hwidth = size * 0.5
|
||||
hheight = size * 0.5 * aspect
|
||||
scale = parse_diagram_scale(camera)
|
||||
xmarg = margin * scale
|
||||
ymarg = margin * scale * aspect
|
||||
return (-hwidth + xmarg, hwidth - xmarg, -hheight + ymarg, hheight - ymarg, -camera.clip_start, -camera.clip_end)
|
||||
|
||||
|
||||
def almost_zero(v):
|
||||
return abs(v) < 1e-5
|
||||
|
||||
|
||||
def clip_segment(bounds, segm):
|
||||
"""Clipping line segment to bounds
|
||||
|
||||
:arg bounds: (xmin, xmax, ymin, ymax)
|
||||
:arg segm: 2 vertices of the segment
|
||||
:return: 2 new vertices of segment or None if segment outside the bounding box
|
||||
"""
|
||||
# Liang–Barsky algorithm
|
||||
|
||||
xmin, xmax, ymin, ymax, _, _ = bounds
|
||||
p1, p2 = segm
|
||||
|
||||
def clip_side(p, q):
|
||||
if almost_zero(p): # ~= 0, parallel to the side
|
||||
if q < 0:
|
||||
return None # outside
|
||||
else:
|
||||
return 0, 1 # inside
|
||||
|
||||
t = q / p # the intersection point
|
||||
|
||||
if p < 0: # entering
|
||||
return t, 1
|
||||
else: # leaving
|
||||
return 0, t
|
||||
|
||||
dlt = p2 - p1
|
||||
|
||||
tt = (
|
||||
clip_side(-dlt.x, p1.x - xmin), # left
|
||||
clip_side(+dlt.x, xmax - p1.x), # right
|
||||
clip_side(-dlt.y, p1.y - ymin), # bottom
|
||||
clip_side(+dlt.y, ymax - p1.y), # top
|
||||
)
|
||||
|
||||
if None in tt:
|
||||
return None
|
||||
|
||||
t1 = max(0, max(t[0] for t in tt))
|
||||
t2 = min(1, min(t[1] for t in tt))
|
||||
|
||||
if t1 >= t2:
|
||||
return None
|
||||
|
||||
p1c = p1 + dlt * t1
|
||||
p2c = p1 + dlt * t2
|
||||
|
||||
return p1c, p2c
|
||||
|
||||
|
||||
def elevate_segment(bounds, segm):
|
||||
"""Elevate line xy-perpendicular segment vertically
|
||||
|
||||
:arg bounds: (xmin, xmax, ymin, ymax)
|
||||
:arg segm: 2 vertices of the segment
|
||||
:return: 2 new vertices of segment or None if segment outside the bounding box
|
||||
"""
|
||||
_, _, ymin, ymax, zmin, _ = bounds
|
||||
p1, p2 = segm
|
||||
dlt = p2 - p1
|
||||
if not (almost_zero(dlt.x) and almost_zero(dlt.y)):
|
||||
return None
|
||||
x = p1.x
|
||||
return [Vector((x, ymin, zmin)), Vector((x, ymax, zmin))]
|
||||
|
||||
@@ -5,7 +5,6 @@ classes = (
|
||||
operator.AddDrawing,
|
||||
operator.CreateDrawing,
|
||||
operator.AddAnnotation,
|
||||
operator.CutSection, # TODO: To deprecate
|
||||
operator.AddSheet,
|
||||
operator.OpenSheet,
|
||||
operator.AddDrawingToSheet,
|
||||
|
||||
@@ -1,552 +0,0 @@
|
||||
import os
|
||||
import re
|
||||
import math
|
||||
import time
|
||||
import numpy
|
||||
import pickle
|
||||
import multiprocessing
|
||||
|
||||
|
||||
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 (
|
||||
gp,
|
||||
Geom,
|
||||
Bnd,
|
||||
BRepBndLib,
|
||||
BRep,
|
||||
BRepPrimAPI,
|
||||
BRepAlgoAPI,
|
||||
BRepBuilderAPI,
|
||||
TopOpeBRepTool,
|
||||
TopOpeBRepBuild,
|
||||
ShapeExtend,
|
||||
GProp,
|
||||
BRepGProp,
|
||||
GC,
|
||||
ShapeAnalysis,
|
||||
TopTools,
|
||||
TopExp,
|
||||
TopAbs,
|
||||
HLRAlgo,
|
||||
HLRBRep,
|
||||
TopLoc,
|
||||
Bnd,
|
||||
BRepBndLib,
|
||||
BRepTools,
|
||||
TopoDS,
|
||||
GeomLProp,
|
||||
IntCurvesFace,
|
||||
)
|
||||
from OCC.Core.TopoDS import topods
|
||||
except ImportError:
|
||||
from OCC import (
|
||||
gp,
|
||||
Geom,
|
||||
Bnd,
|
||||
BRepBndLib,
|
||||
BRep,
|
||||
BRepPrimAPI,
|
||||
BRepAlgoAPI,
|
||||
BRepBuilderAPI,
|
||||
TopOpeBRepTool,
|
||||
TopOpeBRepBuild,
|
||||
ShapeExtend,
|
||||
GProp,
|
||||
BRepGProp,
|
||||
GC,
|
||||
ShapeAnalysis,
|
||||
TopTools,
|
||||
TopExp,
|
||||
TopAbs,
|
||||
HLRAlgo,
|
||||
HLRBRep,
|
||||
TopLoc,
|
||||
Bnd,
|
||||
BRepBndLib,
|
||||
BRepTools,
|
||||
TopoDS,
|
||||
GeomLProp,
|
||||
IntCurvesFace,
|
||||
)
|
||||
from OCC.TopoDS import topods
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.util.selector
|
||||
import ifcopenshell.util.element
|
||||
|
||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||
this_file = os.path.join(cwd, "cut_ifc.py")
|
||||
|
||||
|
||||
def get_booleaned_edges(shape):
|
||||
load_occ()
|
||||
edges = []
|
||||
exp = TopExp.TopExp_Explorer(shape, TopAbs.TopAbs_EDGE)
|
||||
while exp.More():
|
||||
edges.append(topods.Edge(exp.Current()))
|
||||
exp.Next()
|
||||
return edges
|
||||
|
||||
|
||||
def connect_edges_into_wires(unconnected_edges):
|
||||
load_occ()
|
||||
edges = TopTools.TopTools_HSequenceOfShape()
|
||||
edges_handle = TopTools.Handle_TopTools_HSequenceOfShape(edges)
|
||||
wires = TopTools.TopTools_HSequenceOfShape()
|
||||
wires_handle = TopTools.Handle_TopTools_HSequenceOfShape(wires)
|
||||
|
||||
for edge in unconnected_edges:
|
||||
edges.Append(edge)
|
||||
|
||||
ShapeAnalysis.ShapeAnalysis_FreeBounds.ConnectEdgesToWires(edges_handle, 1e-5, True, wires_handle)
|
||||
return wires_handle.GetObject()
|
||||
|
||||
|
||||
def do_cut(process_data):
|
||||
load_occ()
|
||||
global_id, shape, section, trsf_data = process_data
|
||||
|
||||
axis = gp.gp_Ax2(
|
||||
gp.gp_Pnt(trsf_data["top_left_corner"][0], trsf_data["top_left_corner"][1], trsf_data["top_left_corner"][2]),
|
||||
gp.gp_Dir(trsf_data["projection"][0], trsf_data["projection"][1], trsf_data["projection"][2]),
|
||||
gp.gp_Dir(trsf_data["x_axis"][0], trsf_data["x_axis"][1], trsf_data["x_axis"][2]),
|
||||
)
|
||||
source = gp.gp_Ax3(axis)
|
||||
destination = gp.gp_Ax3(gp.gp_Pnt(0, 0, 0), gp.gp_Dir(0, 0, -1), gp.gp_Dir(1, 0, 0))
|
||||
transformation = gp.gp_Trsf()
|
||||
transformation.SetDisplacement(source, destination)
|
||||
|
||||
cut_polygons = []
|
||||
section = BRepAlgoAPI.BRepAlgoAPI_Section(section, shape).Shape()
|
||||
section_edges = get_booleaned_edges(section)
|
||||
if len(section_edges) <= 0:
|
||||
return cut_polygons
|
||||
wires = connect_edges_into_wires(section_edges)
|
||||
for i in range(wires.Length()):
|
||||
wire_shape = wires.Value(i + 1)
|
||||
|
||||
transformed_wire = BRepBuilderAPI.BRepBuilderAPI_Transform(wire_shape, transformation)
|
||||
wire_shape = transformed_wire.Shape()
|
||||
|
||||
wire = topods.Wire(wire_shape)
|
||||
face = BRepBuilderAPI.BRepBuilderAPI_MakeFace(wire).Face()
|
||||
|
||||
points = []
|
||||
exp = BRepTools.BRepTools_WireExplorer(wire)
|
||||
while exp.More():
|
||||
point = BRep.BRep_Tool.Pnt(exp.CurrentVertex())
|
||||
points.append((point.X(), -point.Y()))
|
||||
exp.Next()
|
||||
cut_polygons.append({"global_id": global_id, "metadata": {}, "points": points})
|
||||
return cut_polygons
|
||||
|
||||
|
||||
class IfcCutter:
|
||||
def __init__(self):
|
||||
self.time = None
|
||||
self.selector = ifcopenshell.util.selector.Selector()
|
||||
self.product_shapes = []
|
||||
self.background_elements = []
|
||||
self.cut_polygons = []
|
||||
self.template_variables = {}
|
||||
self.metadata = {}
|
||||
self.data_dir = ""
|
||||
self.vector_style = ""
|
||||
self.ifc_filenames = []
|
||||
self.ifc_files = {}
|
||||
self.resolved_pixels = set()
|
||||
self.text_pickle_file = "text.pickle"
|
||||
self.metadata_pickle_file = "metadata.pickle"
|
||||
self.cut_pickle_file = "cut.pickle"
|
||||
self.should_recut = True
|
||||
self.should_recut_selected = True
|
||||
self.cut_objects = ""
|
||||
self.selected_global_ids = []
|
||||
self.should_extract = True
|
||||
self.diagram_name = None
|
||||
self.background_image = None
|
||||
self.section_box = {
|
||||
"projection": (0, 1, 0),
|
||||
"x_axis": (1, 0, 0),
|
||||
"y_axis": (0, 0, -1),
|
||||
"top_left_corner": (-2, 2, 8),
|
||||
"x": 14,
|
||||
"y": 9,
|
||||
"z": 2,
|
||||
"shape": None,
|
||||
"face": None,
|
||||
}
|
||||
|
||||
def cut(self):
|
||||
self.profile_code("Starting cut process")
|
||||
self.load_ifc_files()
|
||||
self.profile_code("Load IFC files")
|
||||
self.get_template_variables()
|
||||
self.profile_code("Get template variables")
|
||||
self.get_product_shapes()
|
||||
self.profile_code("Get product shapes")
|
||||
self.create_section_box()
|
||||
self.profile_code("Create section box")
|
||||
self.get_cut_polygons()
|
||||
self.profile_code("Get cut polygons")
|
||||
self.get_annotation()
|
||||
self.profile_code("Get annotation")
|
||||
self.get_cut_polygon_metadata()
|
||||
self.profile_code("Get cut polygon metadata")
|
||||
|
||||
def profile_code(self, message):
|
||||
if not self.time:
|
||||
self.time = time.time()
|
||||
print("{} :: {:.2f}".format(message, time.time() - self.time))
|
||||
self.time = time.time()
|
||||
|
||||
def load_ifc_files(self):
|
||||
if not self.should_recut and not self.should_extract:
|
||||
return
|
||||
|
||||
loaded_files = []
|
||||
for filename in self.ifc_filenames:
|
||||
print("Loading file {} ...".format(filename))
|
||||
if filename:
|
||||
self.ifc_files[filename] = ifcopenshell.open(filename)
|
||||
|
||||
def get_template_variables(self):
|
||||
if not self.should_extract:
|
||||
if os.path.isfile(self.text_pickle_file):
|
||||
with open(self.text_pickle_file, "rb") as text_file:
|
||||
self.template_variables = pickle.load(text_file)
|
||||
return
|
||||
|
||||
data = {}
|
||||
for text_obj in self.text_objs:
|
||||
text_obj_data = self.get_text_variables(text_obj)
|
||||
if text_obj_data:
|
||||
data[text_obj.name] = text_obj_data
|
||||
|
||||
with open(self.text_pickle_file, "wb") as text_file:
|
||||
pickle.dump(data, text_file, protocol=pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
self.template_variables = data
|
||||
|
||||
def get_text_variables(self, text_obj):
|
||||
text_obj_data = {}
|
||||
text_body = text_obj.data.body
|
||||
related_element = text_obj.data.BIMTextProperties.related_element
|
||||
if not related_element:
|
||||
return
|
||||
global_id = related_element.BIMObjectProperties.attributes.get("GlobalId")
|
||||
if not global_id:
|
||||
return
|
||||
element = self.get_ifc_element(global_id.string_value)
|
||||
for variable in text_obj.data.BIMTextProperties.variables:
|
||||
if element:
|
||||
if "{{" in variable.prop_key:
|
||||
prop_key = variable.prop_key.split("{{")[1].split("}}")[0]
|
||||
prop_value = self.selector.get_element_value(element, prop_key)
|
||||
variable_value = eval(variable.prop_key.replace("{{" + prop_key + "}}", str(prop_value)))
|
||||
else:
|
||||
variable_value = self.selector.get_element_value(element, variable.prop_key)
|
||||
text_obj_data[variable.name] = variable_value
|
||||
return text_obj_data
|
||||
|
||||
def get_product_shapes(self):
|
||||
if not self.should_recut:
|
||||
return
|
||||
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set(settings.USE_PYTHON_OPENCASCADE, True)
|
||||
products = []
|
||||
|
||||
for filename, ifc_file in self.ifc_files.items():
|
||||
shape_pickle = os.path.join(
|
||||
self.data_dir, "cache", "shapes", "{}.pickle".format(os.path.basename(filename))
|
||||
)
|
||||
shape_map = {}
|
||||
if self.should_recut_selected and os.path.isfile(shape_pickle):
|
||||
with open(shape_pickle, "rb") as shape_file:
|
||||
shape_map = pickle.load(shape_file)
|
||||
|
||||
products.extend(self.selector.parse(ifc_file, self.cut_objects))
|
||||
|
||||
selected_elements = []
|
||||
for i, product in enumerate(products):
|
||||
if (
|
||||
product.is_a("IfcOpeningElement")
|
||||
or product.is_a("IfcSite")
|
||||
or product.Representation is None
|
||||
or self.has_annotation(product)
|
||||
):
|
||||
continue
|
||||
try:
|
||||
if self.should_recut_selected and product.GlobalId in self.selected_global_ids:
|
||||
selected_elements.append(product)
|
||||
elif product.GlobalId in shape_map:
|
||||
shape = shape_map[product.GlobalId]
|
||||
self.add_product_shape(product, shape)
|
||||
else:
|
||||
selected_elements.append(product)
|
||||
except:
|
||||
print("Failed to create shape for {}".format(product))
|
||||
|
||||
if selected_elements:
|
||||
total = 0
|
||||
checkpoint = time.time()
|
||||
iterator = ifcopenshell.geom.iterator(
|
||||
settings, ifc_file, multiprocessing.cpu_count(), include=selected_elements
|
||||
)
|
||||
valid_file = iterator.initialize()
|
||||
if valid_file:
|
||||
while True:
|
||||
total += 1
|
||||
if total % 250 == 0:
|
||||
print("{} elements processed in {:.2f}s ...".format(total, time.time() - checkpoint))
|
||||
checkpoint = time.time()
|
||||
shape = iterator.get()
|
||||
shape_map[shape.data.guid] = shape.geometry
|
||||
self.add_product_shape(ifc_file.by_guid(shape.data.guid), shape.geometry)
|
||||
if not iterator.next():
|
||||
break
|
||||
|
||||
with open(shape_pickle, "wb") as shape_file:
|
||||
pickle.dump(shape_map, shape_file, protocol=pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
def add_product_shape(self, product, shape):
|
||||
self.product_shapes.append((product, shape))
|
||||
|
||||
def has_annotation(self, element):
|
||||
for representation in element.Representation.Representations:
|
||||
if (
|
||||
representation.ContextOfItems.ContextType == "Plan"
|
||||
and representation.ContextOfItems.ContextIdentifier == "Annotation"
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
def create_section_box(self):
|
||||
load_occ()
|
||||
top_left_corner = gp.gp_Pnt(
|
||||
self.section_box["top_left_corner"][0],
|
||||
self.section_box["top_left_corner"][1],
|
||||
self.section_box["top_left_corner"][2],
|
||||
)
|
||||
axis = gp.gp_Ax2(
|
||||
top_left_corner,
|
||||
gp.gp_Dir(
|
||||
self.section_box["projection"][0], self.section_box["projection"][1], self.section_box["projection"][2]
|
||||
),
|
||||
gp.gp_Dir(self.section_box["x_axis"][0], self.section_box["x_axis"][1], self.section_box["x_axis"][2]),
|
||||
)
|
||||
section_box = BRepPrimAPI.BRepPrimAPI_MakeBox(
|
||||
axis, self.section_box["x"], self.section_box["y"], self.section_box["z"]
|
||||
)
|
||||
self.section_box["shape"] = section_box.Shape()
|
||||
self.section_box["face"] = section_box.BottomFace()
|
||||
|
||||
source = gp.gp_Ax3(axis)
|
||||
self.transformation_data = {
|
||||
"top_left_corner": self.section_box["top_left_corner"],
|
||||
"projection": self.section_box["projection"],
|
||||
"x_axis": self.section_box["x_axis"],
|
||||
}
|
||||
destination = gp.gp_Ax3(gp.gp_Pnt(0, 0, 0), gp.gp_Dir(0, 0, -1), gp.gp_Dir(1, 0, 0))
|
||||
self.transformation_dest = destination
|
||||
self.transformation = gp.gp_Trsf()
|
||||
self.transformation.SetDisplacement(source, destination)
|
||||
|
||||
def get_bbox(self, shape):
|
||||
load_occ()
|
||||
bbox = Bnd.Bnd_Box()
|
||||
BRepBndLib.brepbndlib_Add(shape, bbox)
|
||||
return bbox
|
||||
|
||||
def calculate_face_zpos(self, face):
|
||||
bbox = self.get_bbox(face)
|
||||
xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get()
|
||||
zpos = zmin + ((zmax - zmin) / 2)
|
||||
return zpos, zmax
|
||||
|
||||
def get_booleaned_edges(self, shape):
|
||||
load_occ()
|
||||
edges = []
|
||||
exp = TopExp.TopExp_Explorer(shape, TopAbs.TopAbs_EDGE)
|
||||
while exp.More():
|
||||
edges.append(topods.Edge(exp.Current()))
|
||||
exp.Next()
|
||||
return edges
|
||||
|
||||
def get_cut_polygons(self):
|
||||
if self.should_recut:
|
||||
self.get_fresh_cut_polygons()
|
||||
self.pickle_cut_polygons()
|
||||
else:
|
||||
self.get_pickled_cut_polygons()
|
||||
|
||||
def get_annotation(self):
|
||||
import mathutils
|
||||
load_occ()
|
||||
|
||||
self.annotation_objs = []
|
||||
settings_2d = ifcopenshell.geom.settings()
|
||||
settings_2d.set(settings_2d.INCLUDE_CURVES, True)
|
||||
settings_py = ifcopenshell.geom.settings()
|
||||
settings_py.set(settings_py.USE_PYTHON_OPENCASCADE, True)
|
||||
for ifc_file in self.ifc_files.values():
|
||||
for element in ifc_file.by_type("IfcElement"):
|
||||
annotation_representation = None
|
||||
box_representation = None
|
||||
if not element.Representation:
|
||||
continue # This can occur for aggregates
|
||||
for representation in element.Representation.Representations:
|
||||
if (
|
||||
representation.ContextOfItems.ContextType == "Plan"
|
||||
and representation.ContextOfItems.ContextIdentifier == "Annotation"
|
||||
):
|
||||
annotation_representation = representation
|
||||
elif (
|
||||
representation.ContextOfItems.ContextType == "Model"
|
||||
and representation.ContextOfItems.ContextIdentifier == "Box"
|
||||
):
|
||||
box_representation = representation
|
||||
if not annotation_representation or not box_representation:
|
||||
continue
|
||||
|
||||
# This is bad code. See bug #85 to make it slightly less bad.
|
||||
# Effectively if the bbox does not intersect with the camera
|
||||
# plane, then we should "continue" and not process the 2D
|
||||
# wireframe. This approach works but is not very smart.
|
||||
for subelement in ifc_file.traverse(box_representation):
|
||||
if subelement.is_a("IfcBoundingBox"):
|
||||
block = ifc_file.createIfcBlock(
|
||||
ifc_file.createIfcAxis2Placement3D(subelement.Corner, None, None),
|
||||
subelement.XDim,
|
||||
subelement.YDim,
|
||||
subelement.ZDim,
|
||||
)
|
||||
for inverse in ifc_file.get_inverse(subelement):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, subelement, block)
|
||||
element.Representation.Representations = [box_representation]
|
||||
shape = ifcopenshell.geom.create_shape(settings_py, element)
|
||||
|
||||
section = BRepAlgoAPI.BRepAlgoAPI_Section(self.section_box["face"], shape.geometry).Shape()
|
||||
section_edges = get_booleaned_edges(section)
|
||||
|
||||
if len(section_edges) <= 0:
|
||||
# The bounding box of the annotation object does not
|
||||
# intersect with the camera plane, so don't bother drawing
|
||||
# the annotation
|
||||
continue
|
||||
|
||||
# Monkey patch - see bug #771.
|
||||
element.Representation.Representations = [annotation_representation]
|
||||
shape = ifcopenshell.geom.create_shape(settings_2d, element)
|
||||
if hasattr(shape, "geometry"):
|
||||
geometry = shape.geometry
|
||||
else:
|
||||
geometry = shape
|
||||
e = geometry.edges
|
||||
v = geometry.verts
|
||||
m = shape.transformation.matrix.data
|
||||
mat = mathutils.Matrix(
|
||||
([m[0], m[1], m[2], 0], [m[3], m[4], m[5], 0], [m[6], m[7], m[8], 0], [m[9], m[10], m[11], 1])
|
||||
)
|
||||
mat.transpose()
|
||||
self.annotation_objs.append(
|
||||
{
|
||||
"raw": element,
|
||||
"classes": self.get_classes(element, "annotation"),
|
||||
"edges": [[e[i], e[i + 1]] for i in range(0, len(e), 2)],
|
||||
"vertices": [mat @ mathutils.Vector((v[i], v[i + 1], v[i + 2])) for i in range(0, len(v), 3)],
|
||||
}
|
||||
)
|
||||
|
||||
def get_cut_polygon_metadata(self):
|
||||
if not self.should_extract:
|
||||
if os.path.isfile(self.metadata_pickle_file):
|
||||
with open(self.metadata_pickle_file, "rb") as metadata_file:
|
||||
self.metadata = pickle.load(metadata_file)
|
||||
|
||||
for polygon in self.cut_polygons:
|
||||
if polygon["global_id"] in self.metadata:
|
||||
polygon["metadata"] = self.metadata[polygon["global_id"]]
|
||||
return
|
||||
|
||||
for polygon in self.cut_polygons:
|
||||
metadata = {"classes": self.get_classes(self.get_ifc_element(polygon["global_id"]), "cut")}
|
||||
self.metadata[polygon["global_id"]] = metadata
|
||||
polygon["metadata"] = metadata
|
||||
|
||||
with open(self.metadata_pickle_file, "wb") as metadata_file:
|
||||
pickle.dump(self.metadata, metadata_file, protocol=pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
def pickle_cut_polygons(self):
|
||||
with open(self.cut_pickle_file, "wb") as pickle_file:
|
||||
pickle.dump(self.cut_polygons, pickle_file, protocol=pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
def get_fresh_cut_polygons(self):
|
||||
process_data = [
|
||||
(p.GlobalId, s, self.section_box["face"], self.transformation_data) for p, s in self.product_shapes
|
||||
]
|
||||
|
||||
import bpy
|
||||
|
||||
if bpy.app.version > (2, 90, 0) and os.name == 'nt':
|
||||
# See bug #1148
|
||||
for data in process_data:
|
||||
results = do_cut(data)
|
||||
polygons = [r for r in results if r["points"]]
|
||||
self.cut_polygons.extend(polygons)
|
||||
else:
|
||||
multiprocessing.set_executable(bpy.app.binary_path_python)
|
||||
|
||||
with multiprocessing.Pool(9) as p:
|
||||
results = p.map(do_cut, process_data)
|
||||
for result in results:
|
||||
polygons = [p for p in result if p["points"]]
|
||||
self.cut_polygons.extend(polygons)
|
||||
|
||||
def get_polygon_metadata(self, polygon, position):
|
||||
polygon["metadata"] = {"classes": self.get_classes(self.get_ifc_element(polygon["global_id"]), position)}
|
||||
return polygon
|
||||
|
||||
def get_ifc_element(self, global_id):
|
||||
# TODO: make this less bad
|
||||
element = None
|
||||
for ifc_file in self.ifc_files.values():
|
||||
try:
|
||||
element = ifc_file.by_id(global_id)
|
||||
return element
|
||||
except:
|
||||
pass
|
||||
|
||||
def get_classes(self, element, position):
|
||||
classes = [position, element.is_a()]
|
||||
material = ifcopenshell.util.element.get_material(element)
|
||||
if material:
|
||||
classes.append(
|
||||
"material-{}".format(
|
||||
re.sub("[^0-9a-zA-Z]+", "", self.get_material_name(material))
|
||||
)
|
||||
)
|
||||
classes.append("globalid-{}".format(element.GlobalId))
|
||||
for attribute in self.attributes:
|
||||
result = self.selector.get_element_value(element, attribute)
|
||||
if result:
|
||||
classes.append(
|
||||
"{}-{}".format(re.sub("[^0-9a-zA-Z]+", "", attribute), re.sub("[^0-9a-zA-Z]+", "", result))
|
||||
)
|
||||
return classes
|
||||
|
||||
def get_material_name(self, element):
|
||||
if hasattr(element, "Name") and element.Name:
|
||||
return element.Name
|
||||
elif hasattr(element, "LayerSetName") and element.LayerSetName:
|
||||
return element.LayerSetName
|
||||
return "mat-" + str(element.id())
|
||||
|
||||
def get_pickled_cut_polygons(self):
|
||||
if os.path.isfile(self.cut_pickle_file):
|
||||
with open(self.cut_pickle_file, "rb") as pickle_file:
|
||||
self.cut_polygons = pickle.load(pickle_file)
|
||||
@@ -257,3 +257,104 @@ def get_project_collection(scene):
|
||||
if len(colls) != 1:
|
||||
raise RuntimeError("project collection missing or not unique")
|
||||
return colls[0]
|
||||
|
||||
|
||||
def parse_diagram_scale(camera):
|
||||
"""Returns numeric value of scale"""
|
||||
if camera.BIMCameraProperties.diagram_scale == "CUSTOM":
|
||||
_, fraction = camera.BIMCameraProperties.custom_diagram_scale.split("|")
|
||||
else:
|
||||
_, fraction = camera.BIMCameraProperties.diagram_scale.split("|")
|
||||
numerator, denominator = fraction.split("/")
|
||||
return float(numerator) / float(denominator)
|
||||
|
||||
|
||||
def ortho_view_frame(camera, margin=0.015):
|
||||
"""Calculates 2d bounding box of camera view area.
|
||||
|
||||
Similar to `bpy.types.Camera.view_frame`
|
||||
|
||||
:arg camera: camera of drawing
|
||||
:type camera: bpy.types.Camera + BIMCameraProperties
|
||||
:arg margin: margins, in scene units
|
||||
:type margin: float
|
||||
:return: (xmin, xmax, ymin, ymax, zmin, zmax) in local camera coordinates
|
||||
"""
|
||||
aspect = camera.BIMCameraProperties.raster_y / camera.BIMCameraProperties.raster_x
|
||||
size = camera.ortho_scale
|
||||
hwidth = size * 0.5
|
||||
hheight = size * 0.5 * aspect
|
||||
scale = parse_diagram_scale(camera)
|
||||
xmarg = margin * scale
|
||||
ymarg = margin * scale * aspect
|
||||
return (-hwidth + xmarg, hwidth - xmarg, -hheight + ymarg, hheight - ymarg, -camera.clip_start, -camera.clip_end)
|
||||
|
||||
|
||||
def almost_zero(v):
|
||||
return abs(v) < 1e-5
|
||||
|
||||
|
||||
def clip_segment(bounds, segm):
|
||||
"""Clipping line segment to bounds
|
||||
|
||||
:arg bounds: (xmin, xmax, ymin, ymax)
|
||||
:arg segm: 2 vertices of the segment
|
||||
:return: 2 new vertices of segment or None if segment outside the bounding box
|
||||
"""
|
||||
# Liang–Barsky algorithm
|
||||
|
||||
xmin, xmax, ymin, ymax, _, _ = bounds
|
||||
p1, p2 = segm
|
||||
|
||||
def clip_side(p, q):
|
||||
if almost_zero(p): # ~= 0, parallel to the side
|
||||
if q < 0:
|
||||
return None # outside
|
||||
else:
|
||||
return 0, 1 # inside
|
||||
|
||||
t = q / p # the intersection point
|
||||
|
||||
if p < 0: # entering
|
||||
return t, 1
|
||||
else: # leaving
|
||||
return 0, t
|
||||
|
||||
dlt = p2 - p1
|
||||
|
||||
tt = (
|
||||
clip_side(-dlt.x, p1.x - xmin), # left
|
||||
clip_side(+dlt.x, xmax - p1.x), # right
|
||||
clip_side(-dlt.y, p1.y - ymin), # bottom
|
||||
clip_side(+dlt.y, ymax - p1.y), # top
|
||||
)
|
||||
|
||||
if None in tt:
|
||||
return None
|
||||
|
||||
t1 = max(0, max(t[0] for t in tt))
|
||||
t2 = min(1, min(t[1] for t in tt))
|
||||
|
||||
if t1 >= t2:
|
||||
return None
|
||||
|
||||
p1c = p1 + dlt * t1
|
||||
p2c = p1 + dlt * t2
|
||||
|
||||
return p1c, p2c
|
||||
|
||||
|
||||
def elevate_segment(bounds, segm):
|
||||
"""Elevate line xy-perpendicular segment vertically
|
||||
|
||||
:arg bounds: (xmin, xmax, ymin, ymax)
|
||||
:arg segm: 2 vertices of the segment
|
||||
:return: 2 new vertices of segment or None if segment outside the bounding box
|
||||
"""
|
||||
_, _, ymin, ymax, zmin, _ = bounds
|
||||
p1, p2 = segm
|
||||
dlt = p2 - p1
|
||||
if not (almost_zero(dlt.x) and almost_zero(dlt.y)):
|
||||
return None
|
||||
x = p1.x
|
||||
return [Vector((x, ymin, zmin)), Vector((x, ymax, zmin))]
|
||||
|
||||
@@ -10,7 +10,6 @@ import ifcopenshell.util.selector
|
||||
import ifcopenshell.util.representation
|
||||
import blenderbim.bim.module.drawing.svgwriter as svgwriter
|
||||
import blenderbim.bim.module.drawing.annotation as annotation
|
||||
import blenderbim.bim.module.drawing.cut_ifc as cut_ifc # TODO: deprecate
|
||||
import blenderbim.bim.module.drawing.sheeter as sheeter
|
||||
import blenderbim.bim.module.drawing.scheduler as scheduler
|
||||
import blenderbim.bim.module.drawing.helper as helper
|
||||
@@ -353,202 +352,6 @@ class AddAnnotation(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class CutSection(bpy.types.Operator):
|
||||
bl_idname = "bim.cut_section"
|
||||
bl_label = "Cut Section"
|
||||
|
||||
def execute(self, context):
|
||||
self.file = ifc.IfcStore.get_file()
|
||||
camera = bpy.context.scene.camera
|
||||
if not (camera.type == "CAMERA" and camera.data.type == "ORTHO"):
|
||||
return {"FINISHED"}
|
||||
if not bpy.context.scene.DocProperties.ifc_files and bpy.context.scene.BIMProperties.ifc_file:
|
||||
new = bpy.context.scene.DocProperties.ifc_files.add()
|
||||
new.name = bpy.context.scene.BIMProperties.ifc_file
|
||||
bpy.ops.bim.activate_view(
|
||||
drawing_index=bpy.context.scene.DocProperties.drawings.find(camera.name.split("/")[1])
|
||||
)
|
||||
drawing_style = bpy.context.scene.DocProperties.drawing_styles[
|
||||
camera.data.BIMCameraProperties.active_drawing_style_index
|
||||
]
|
||||
self.diagram_name = camera.name.split("/")[1]
|
||||
bpy.context.scene.render.filepath = os.path.join(
|
||||
bpy.context.scene.BIMProperties.data_dir, "diagrams", "{}.png".format(self.diagram_name)
|
||||
)
|
||||
self.create_raster(camera, drawing_style)
|
||||
location = camera.location
|
||||
render = bpy.context.scene.render
|
||||
if self.is_landscape():
|
||||
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
|
||||
depth = camera.data.clip_end
|
||||
projection = camera.matrix_world.to_quaternion() @ Vector((0, 0, -1))
|
||||
x_axis = camera.matrix_world.to_quaternion() @ Vector((1, 0, 0))
|
||||
y_axis = camera.matrix_world.to_quaternion() @ Vector((0, -1, 0))
|
||||
top_left_corner = location - (width / 2 * x_axis) - (height / 2 * y_axis)
|
||||
ifc_cutter = cut_ifc.IfcCutter()
|
||||
|
||||
ifc_cutter.ifc_filenames = [i.name for i in bpy.context.scene.DocProperties.ifc_files]
|
||||
ifc_cutter.data_dir = bpy.context.scene.BIMProperties.data_dir
|
||||
ifc_cutter.vector_style = drawing_style.vector_style
|
||||
ifc_cutter.diagram_name = self.diagram_name
|
||||
ifc_cutter.background_image = bpy.context.scene.render.filepath
|
||||
if camera.data.BIMCameraProperties.cut_objects == "CUSTOM":
|
||||
ifc_cutter.cut_objects = camera.data.BIMCameraProperties.cut_objects_custom
|
||||
else:
|
||||
ifc_cutter.cut_objects = camera.data.BIMCameraProperties.cut_objects
|
||||
ifc_cutter.leader_obj = None
|
||||
ifc_cutter.stair_obj = None
|
||||
ifc_cutter.dimension_objs = []
|
||||
ifc_cutter.break_obj = None
|
||||
ifc_cutter.equal_objs = []
|
||||
ifc_cutter.hidden_objs = []
|
||||
ifc_cutter.solid_objs = []
|
||||
ifc_cutter.plan_level_obj = None
|
||||
ifc_cutter.section_level_obj = None
|
||||
ifc_cutter.grid_objs = []
|
||||
ifc_cutter.text_objs = []
|
||||
ifc_cutter.misc_objs = []
|
||||
ifc_cutter.attributes = [a.name for a in drawing_style.attributes]
|
||||
for obj in camera.users_collection[0].objects:
|
||||
if "IfcGrid" in obj.name:
|
||||
ifc_cutter.grid_objs.append(obj)
|
||||
elif "IfcGroup" in obj.name and obj.type == "CAMERA":
|
||||
ifc_cutter.camera_obj = obj
|
||||
|
||||
if "IfcAnnotation/" not in obj.name:
|
||||
continue
|
||||
|
||||
if "Leader" in obj.name:
|
||||
ifc_cutter.leader_obj = (obj, obj.data)
|
||||
elif "Stair" in obj.name:
|
||||
ifc_cutter.stair_obj = obj
|
||||
elif "Equal" in obj.name:
|
||||
ifc_cutter.equal_objs.append(obj)
|
||||
elif "Dimension" in obj.name:
|
||||
ifc_cutter.dimension_objs.append(obj)
|
||||
elif "Break" in obj.name:
|
||||
ifc_cutter.break_obj = obj
|
||||
elif "Hidden" in obj.name:
|
||||
ifc_cutter.hidden_objs.append((obj, obj.data))
|
||||
elif "Solid" in obj.name:
|
||||
ifc_cutter.solid_objs.append((obj, obj.data))
|
||||
elif "Plan Level" in obj.name:
|
||||
ifc_cutter.plan_level_obj = obj
|
||||
elif "Section Level" in obj.name:
|
||||
ifc_cutter.section_level_obj = obj
|
||||
elif obj.type == "FONT":
|
||||
ifc_cutter.text_objs.append(obj)
|
||||
else:
|
||||
ifc_cutter.misc_objs.append(obj)
|
||||
|
||||
ifc_cutter.section_box = {
|
||||
"projection": tuple(projection),
|
||||
"x_axis": tuple(x_axis),
|
||||
"y_axis": tuple(y_axis),
|
||||
"top_left_corner": tuple(top_left_corner),
|
||||
"x": width,
|
||||
"y": height,
|
||||
"z": depth,
|
||||
"shape": None,
|
||||
"face": None,
|
||||
}
|
||||
ifc_cutter.cut_pickle_file = os.path.join(ifc_cutter.data_dir, "{}-cut.pickle".format(self.diagram_name))
|
||||
ifc_cutter.text_pickle_file = os.path.join(ifc_cutter.data_dir, "{}-text.pickle".format(self.diagram_name))
|
||||
ifc_cutter.metadata_pickle_file = os.path.join(
|
||||
ifc_cutter.data_dir, "{}-metadata.pickle".format(self.diagram_name)
|
||||
)
|
||||
ifc_cutter.should_recut = bpy.context.scene.DocProperties.should_recut
|
||||
ifc_cutter.should_recut_selected = bpy.context.scene.DocProperties.should_recut_selected
|
||||
selected_global_ids = []
|
||||
for obj in bpy.context.selected_objects:
|
||||
if "Ifc" not in obj.name:
|
||||
continue
|
||||
for attribute in obj.BIMObjectProperties.attributes:
|
||||
if attribute.name == "GlobalId":
|
||||
selected_global_ids.append(attribute.string_value)
|
||||
break
|
||||
ifc_cutter.selected_global_ids = selected_global_ids
|
||||
ifc_cutter.should_extract = bpy.context.scene.DocProperties.should_extract
|
||||
svg_writer = svgwriter.SvgWriter(ifc_cutter)
|
||||
if camera.data.BIMCameraProperties.diagram_scale == "CUSTOM":
|
||||
human_scale, fraction = camera.data.BIMCameraProperties.custom_diagram_scale.split("|")
|
||||
else:
|
||||
human_scale, fraction = camera.data.BIMCameraProperties.diagram_scale.split("|")
|
||||
numerator, denominator = fraction.split("/")
|
||||
if camera.data.BIMCameraProperties.is_nts:
|
||||
svg_writer.human_scale = "NTS"
|
||||
else:
|
||||
svg_writer.human_scale = human_scale
|
||||
svg_writer.scale = float(numerator) / float(denominator)
|
||||
ifc_cutter.cut()
|
||||
svg_writer.write()
|
||||
bpy.ops.bim.open_view(view=self.diagram_name)
|
||||
return {"FINISHED"}
|
||||
|
||||
def create_raster(self, camera, drawing_style):
|
||||
if drawing_style.render_type == "NONE":
|
||||
return
|
||||
|
||||
if drawing_style.render_type == "DEFAULT":
|
||||
return bpy.ops.render.render(write_still=True)
|
||||
|
||||
previous_visibility = {}
|
||||
for obj in camera.users_collection[0].objects:
|
||||
previous_visibility[obj.name] = obj.hide_get()
|
||||
obj.hide_set(True)
|
||||
for obj in bpy.context.visible_objects:
|
||||
if (
|
||||
(not obj.data and not obj.instance_collection)
|
||||
or isinstance(obj.data, bpy.types.Camera)
|
||||
or "IfcGrid/" in obj.name
|
||||
or "IfcGridAxis/" in obj.name
|
||||
or "IfcOpeningElement/" in obj.name
|
||||
or self.does_obj_have_target_view_representation(obj, camera)
|
||||
):
|
||||
previous_visibility[obj.name] = obj.hide_get()
|
||||
obj.hide_set(True)
|
||||
|
||||
space = self.get_view_3d()
|
||||
previous_shading = space.shading.type
|
||||
previous_format = bpy.context.scene.render.image_settings.file_format
|
||||
space.shading.type = "RENDERED"
|
||||
bpy.context.scene.render.image_settings.file_format = "PNG"
|
||||
bpy.ops.render.opengl(write_still=True)
|
||||
space.shading.type = previous_shading
|
||||
bpy.context.scene.render.image_settings.file_format = previous_format
|
||||
|
||||
for name, value in previous_visibility.items():
|
||||
bpy.data.objects[name].hide_set(value)
|
||||
|
||||
def does_obj_have_target_view_representation(self, obj, camera):
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
return False
|
||||
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
if not element.Representation:
|
||||
return False
|
||||
for representation in element.Representation.Representations:
|
||||
if ifcopenshell.util.representation.is_representation_of_context(
|
||||
representation, "Plan", "Annotation", camera.data.BIMCameraProperties.target_view
|
||||
):
|
||||
return True
|
||||
|
||||
def is_landscape(self):
|
||||
return bpy.context.scene.render.resolution_x > bpy.context.scene.render.resolution_y
|
||||
|
||||
def get_view_3d(self):
|
||||
for area in bpy.context.screen.areas:
|
||||
if area.type != "VIEW_3D":
|
||||
continue
|
||||
for space in area.spaces:
|
||||
if space.type != "VIEW_3D":
|
||||
continue
|
||||
return space
|
||||
|
||||
|
||||
class AddSheet(bpy.types.Operator):
|
||||
bl_idname = "bim.add_sheet"
|
||||
bl_label = "Add Sheet"
|
||||
|
||||
Reference in New Issue
Block a user