mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 02:47:48 +00:00
A little progress on refactoring the old drawing module into an IfcConvert based system. See #1153.
This commit is contained in:
@@ -86,7 +86,6 @@ if bpy is not None:
|
||||
operator.AddIfcFile,
|
||||
operator.RemoveIfcFile,
|
||||
operator.SelectDocIfcFile,
|
||||
operator.AddAnnotation,
|
||||
operator.GenerateReferences,
|
||||
operator.ResizeText,
|
||||
operator.AddVariable,
|
||||
|
||||
@@ -78,145 +78,6 @@ def export_attributes(props, callback=None):
|
||||
# TODO: migrate the below helper functions into the drawing module, since it is specific to that module
|
||||
|
||||
|
||||
# This function stolen from https://github.com/kevancress/MeasureIt_ARCH/blob/dcf607ce0896aa2284463c6b4ae9cd023fc54cbe/measureit_arch_baseclass.py
|
||||
# MeasureIt-ARCH is GPL-v3
|
||||
# In the future I will need to rewrite this to allow the user to have custom
|
||||
# settings for each annotation object, not read from Blender.
|
||||
def format_distance(value, isArea=False, hide_units=True):
|
||||
s_code = "\u00b2" # Superscript two THIS IS LEGACY (but being kept for when Area Measurements are re-implimented)
|
||||
|
||||
# Get Scene Unit Settings
|
||||
scaleFactor = bpy.context.scene.unit_settings.scale_length
|
||||
unit_system = bpy.context.scene.unit_settings.system
|
||||
unit_length = bpy.context.scene.unit_settings.length_unit
|
||||
|
||||
toInches = 39.3700787401574887
|
||||
inPerFoot = 11.999
|
||||
|
||||
if isArea:
|
||||
toInches = 1550
|
||||
inPerFoot = 143.999
|
||||
|
||||
value *= scaleFactor
|
||||
|
||||
# Imperial Formating
|
||||
if unit_system == "IMPERIAL":
|
||||
precision = bpy.context.scene.BIMProperties.imperial_precision
|
||||
if precision == "NONE":
|
||||
precision = 256
|
||||
elif precision == "1":
|
||||
precision = 1
|
||||
elif "/" in precision:
|
||||
precision = int(precision.split("/")[1])
|
||||
|
||||
base = int(precision)
|
||||
decInches = value * toInches
|
||||
|
||||
# Seperate ft and inches
|
||||
# Unless Inches are the specified Length Unit
|
||||
if unit_length != "INCHES":
|
||||
feet = math.floor(decInches / inPerFoot)
|
||||
decInches -= feet * inPerFoot
|
||||
else:
|
||||
feet = 0
|
||||
|
||||
# Seperate Fractional Inches
|
||||
inches = math.floor(decInches)
|
||||
if inches != 0:
|
||||
frac = round(base * (decInches - inches))
|
||||
else:
|
||||
frac = round(base * (decInches))
|
||||
|
||||
# Set proper numerator and denominator
|
||||
if frac != base:
|
||||
numcycles = int(math.log2(base))
|
||||
for i in range(numcycles):
|
||||
if frac % 2 == 0:
|
||||
frac = int(frac / 2)
|
||||
base = int(base / 2)
|
||||
else:
|
||||
break
|
||||
else:
|
||||
frac = 0
|
||||
inches += 1
|
||||
|
||||
# Check values and compose string
|
||||
if inches == 12:
|
||||
feet += 1
|
||||
inches = 0
|
||||
|
||||
if not isArea:
|
||||
tx_dist = ""
|
||||
if feet:
|
||||
tx_dist += str(feet) + "'"
|
||||
if feet and inches:
|
||||
tx_dist += " - "
|
||||
if inches:
|
||||
tx_dist += str(inches)
|
||||
if inches and frac:
|
||||
tx_dist += " "
|
||||
if frac:
|
||||
tx_dist += str(frac) + "/" + str(base)
|
||||
if inches or frac:
|
||||
tx_dist += '"'
|
||||
else:
|
||||
tx_dist = str("%1.3f" % (value * toInches / inPerFoot)) + " sq. ft."
|
||||
|
||||
# METRIC FORMATING
|
||||
elif unit_system == "METRIC":
|
||||
precision = bpy.context.scene.BIMProperties.metric_precision
|
||||
if precision != 0:
|
||||
value = precision * round(float(value) / precision)
|
||||
|
||||
# Meters
|
||||
if unit_length == "METERS":
|
||||
fmt = "%1.3f"
|
||||
if hide_units is False:
|
||||
fmt += " m"
|
||||
tx_dist = fmt % value
|
||||
# Centimeters
|
||||
elif unit_length == "CENTIMETERS":
|
||||
fmt = "%1.1f"
|
||||
if hide_units is False:
|
||||
fmt += " cm"
|
||||
d_cm = value * (100)
|
||||
tx_dist = fmt % d_cm
|
||||
# Millimeters
|
||||
elif unit_length == "MILLIMETERS":
|
||||
fmt = "%1.0f"
|
||||
if hide_units is False:
|
||||
fmt += " mm"
|
||||
d_mm = value * (1000)
|
||||
tx_dist = fmt % d_mm
|
||||
|
||||
# Otherwise Use Adaptive Units
|
||||
else:
|
||||
if round(value, 2) >= 1.0:
|
||||
fmt = "%1.3f"
|
||||
if hide_units is False:
|
||||
fmt += " m"
|
||||
tx_dist = fmt % value
|
||||
else:
|
||||
if round(value, 2) >= 0.01:
|
||||
fmt = "%1.1f"
|
||||
if hide_units is False:
|
||||
fmt += " cm"
|
||||
d_cm = value * (100)
|
||||
tx_dist = fmt % d_cm
|
||||
else:
|
||||
fmt = "%1.0f"
|
||||
if hide_units is False:
|
||||
fmt += " mm"
|
||||
d_mm = value * (1000)
|
||||
tx_dist = fmt % d_mm
|
||||
if isArea:
|
||||
tx_dist += s_code
|
||||
else:
|
||||
tx_dist = fmt % value
|
||||
|
||||
return tx_dist
|
||||
|
||||
|
||||
def parse_diagram_scale(camera):
|
||||
"""Returns numeric value of scale"""
|
||||
if camera.BIMCameraProperties.diagram_scale == "CUSTOM":
|
||||
|
||||
@@ -4,6 +4,7 @@ from . import ui, operator
|
||||
classes = (
|
||||
operator.AddDrawing,
|
||||
operator.CreateDrawing,
|
||||
operator.AddAnnotation,
|
||||
ui.BIM_PT_camera,
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
import bpy
|
||||
import math
|
||||
|
||||
|
||||
# This function stolen from https://github.com/kevancress/MeasureIt_ARCH/blob/dcf607ce0896aa2284463c6b4ae9cd023fc54cbe/measureit_arch_baseclass.py
|
||||
# MeasureIt-ARCH is GPL-v3
|
||||
# In the future I will need to rewrite this to allow the user to have custom
|
||||
# settings for each annotation object, not read from Blender.
|
||||
def format_distance(value, isArea=False, hide_units=True):
|
||||
s_code = "\u00b2" # Superscript two THIS IS LEGACY (but being kept for when Area Measurements are re-implimented)
|
||||
|
||||
# Get Scene Unit Settings
|
||||
scaleFactor = bpy.context.scene.unit_settings.scale_length
|
||||
unit_system = bpy.context.scene.unit_settings.system
|
||||
unit_length = bpy.context.scene.unit_settings.length_unit
|
||||
|
||||
toInches = 39.3700787401574887
|
||||
inPerFoot = 11.999
|
||||
|
||||
if isArea:
|
||||
toInches = 1550
|
||||
inPerFoot = 143.999
|
||||
|
||||
value *= scaleFactor
|
||||
|
||||
# Imperial Formating
|
||||
if unit_system == "IMPERIAL":
|
||||
precision = bpy.context.scene.BIMProperties.imperial_precision
|
||||
if precision == "NONE":
|
||||
precision = 256
|
||||
elif precision == "1":
|
||||
precision = 1
|
||||
elif "/" in precision:
|
||||
precision = int(precision.split("/")[1])
|
||||
|
||||
base = int(precision)
|
||||
decInches = value * toInches
|
||||
|
||||
# Seperate ft and inches
|
||||
# Unless Inches are the specified Length Unit
|
||||
if unit_length != "INCHES":
|
||||
feet = math.floor(decInches / inPerFoot)
|
||||
decInches -= feet * inPerFoot
|
||||
else:
|
||||
feet = 0
|
||||
|
||||
# Seperate Fractional Inches
|
||||
inches = math.floor(decInches)
|
||||
if inches != 0:
|
||||
frac = round(base * (decInches - inches))
|
||||
else:
|
||||
frac = round(base * (decInches))
|
||||
|
||||
# Set proper numerator and denominator
|
||||
if frac != base:
|
||||
numcycles = int(math.log2(base))
|
||||
for i in range(numcycles):
|
||||
if frac % 2 == 0:
|
||||
frac = int(frac / 2)
|
||||
base = int(base / 2)
|
||||
else:
|
||||
break
|
||||
else:
|
||||
frac = 0
|
||||
inches += 1
|
||||
|
||||
# Check values and compose string
|
||||
if inches == 12:
|
||||
feet += 1
|
||||
inches = 0
|
||||
|
||||
if not isArea:
|
||||
tx_dist = ""
|
||||
if feet:
|
||||
tx_dist += str(feet) + "'"
|
||||
if feet and inches:
|
||||
tx_dist += " - "
|
||||
if inches:
|
||||
tx_dist += str(inches)
|
||||
if inches and frac:
|
||||
tx_dist += " "
|
||||
if frac:
|
||||
tx_dist += str(frac) + "/" + str(base)
|
||||
if inches or frac:
|
||||
tx_dist += '"'
|
||||
else:
|
||||
tx_dist = str("%1.3f" % (value * toInches / inPerFoot)) + " sq. ft."
|
||||
|
||||
# METRIC FORMATING
|
||||
elif unit_system == "METRIC":
|
||||
precision = bpy.context.scene.BIMProperties.metric_precision
|
||||
if precision != 0:
|
||||
value = precision * round(float(value) / precision)
|
||||
|
||||
# Meters
|
||||
if unit_length == "METERS":
|
||||
fmt = "%1.3f"
|
||||
if hide_units is False:
|
||||
fmt += " m"
|
||||
tx_dist = fmt % value
|
||||
# Centimeters
|
||||
elif unit_length == "CENTIMETERS":
|
||||
fmt = "%1.1f"
|
||||
if hide_units is False:
|
||||
fmt += " cm"
|
||||
d_cm = value * (100)
|
||||
tx_dist = fmt % d_cm
|
||||
# Millimeters
|
||||
elif unit_length == "MILLIMETERS":
|
||||
fmt = "%1.0f"
|
||||
if hide_units is False:
|
||||
fmt += " mm"
|
||||
d_mm = value * (1000)
|
||||
tx_dist = fmt % d_mm
|
||||
|
||||
# Otherwise Use Adaptive Units
|
||||
else:
|
||||
if round(value, 2) >= 1.0:
|
||||
fmt = "%1.3f"
|
||||
if hide_units is False:
|
||||
fmt += " m"
|
||||
tx_dist = fmt % value
|
||||
else:
|
||||
if round(value, 2) >= 0.01:
|
||||
fmt = "%1.1f"
|
||||
if hide_units is False:
|
||||
fmt += " cm"
|
||||
d_cm = value * (100)
|
||||
tx_dist = fmt % d_cm
|
||||
else:
|
||||
fmt = "%1.0f"
|
||||
if hide_units is False:
|
||||
fmt += " mm"
|
||||
d_mm = value * (1000)
|
||||
tx_dist = fmt % d_mm
|
||||
if isArea:
|
||||
tx_dist += s_code
|
||||
else:
|
||||
tx_dist = fmt % value
|
||||
|
||||
return tx_dist
|
||||
@@ -3,6 +3,9 @@ import bpy
|
||||
import json
|
||||
import subprocess
|
||||
import webbrowser
|
||||
import blenderbim.bim.module.drawing.svgwriter as svgwriter
|
||||
import blenderbim.bim.module.drawing.annotation as annotation
|
||||
from mathutils import Vector, Matrix, Euler, geometry
|
||||
from blenderbim.bim.operator import open_with_user_command
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.group.data import Data as GroupData
|
||||
@@ -57,33 +60,156 @@ class CreateDrawing(bpy.types.Operator):
|
||||
bl_label = "Create Drawing"
|
||||
|
||||
def execute(self, context):
|
||||
base_svg = self.ifc_to_svg(context)
|
||||
annotation_svg = self.annotation_to_svg(context)
|
||||
svg_path = self.combine_svgs(context, base_svg, annotation_svg)
|
||||
open_with_user_command(bpy.context.preferences.addons["blenderbim"].preferences.svg_command, svg_path)
|
||||
return {"FINISHED"}
|
||||
|
||||
def combine_svgs(self, context, base, annotation):
|
||||
# Hacky :)
|
||||
svg_path = context.scene.BIMProperties.ifc_file[0:-4] + ".svg"
|
||||
with open(svg_path, 'w') as outfile:
|
||||
with open(base) as infile:
|
||||
for line in infile:
|
||||
if "</svg>" in line:
|
||||
continue
|
||||
outfile.write(line)
|
||||
with open(annotation) as infile:
|
||||
for i, line in enumerate(infile):
|
||||
if i == 0 or i == 1:
|
||||
continue
|
||||
outfile.write(line)
|
||||
return svg_path
|
||||
|
||||
def ifc_to_svg(self, context):
|
||||
svg_path = context.scene.BIMProperties.ifc_file[0:-4] + "-base.svg"
|
||||
ifcconvert_path = os.path.join(cwd, "..", "..", "..", "libs", "IfcConvert")
|
||||
subprocess.run(
|
||||
[
|
||||
ifcconvert_path,
|
||||
context.scene.BIMProperties.ifc_file,
|
||||
"-yv",
|
||||
context.scene.BIMProperties.ifc_file[0:-4] + ".svg",
|
||||
svg_path,
|
||||
"--plan",
|
||||
"--model",
|
||||
"--section-height-from-storeys",
|
||||
"--section-height=1.2",
|
||||
"--door-arcs",
|
||||
"--print-space-names",
|
||||
"--print-space-areas",
|
||||
"--bounds=1024x1024",
|
||||
"--elevation-ref=DRAWING",
|
||||
"--svg-xmlns",
|
||||
"--svg-project",
|
||||
"--svg-poly",
|
||||
"--svg-without-storeys",
|
||||
"--exclude",
|
||||
"entities",
|
||||
"IfcSpace",
|
||||
"IfcOpeningElement",
|
||||
]
|
||||
)
|
||||
open_with_user_command(
|
||||
bpy.context.preferences.addons["blenderbim"].preferences.svg_command,
|
||||
context.scene.BIMProperties.ifc_file[0:-4] + ".svg",
|
||||
)
|
||||
return svg_path
|
||||
|
||||
def annotation_to_svg(self, context):
|
||||
camera = context.scene.camera
|
||||
|
||||
if not (camera.type == "CAMERA" and camera.data.type == "ORTHO"):
|
||||
return
|
||||
|
||||
svg_writer = svgwriter.SvgWriter()
|
||||
|
||||
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
|
||||
|
||||
drawing_style = bpy.context.scene.DocProperties.drawing_styles[
|
||||
camera.data.BIMCameraProperties.active_drawing_style_index
|
||||
]
|
||||
|
||||
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
|
||||
|
||||
svg_writer.scale = float(numerator) / float(denominator)
|
||||
svg_writer.output = context.scene.BIMProperties.ifc_file[0:-4] + "-annotation.svg"
|
||||
svg_writer.data_dir = bpy.context.scene.BIMProperties.data_dir
|
||||
svg_writer.vector_style = drawing_style.vector_style
|
||||
svg_writer.camera = camera
|
||||
svg_writer.camera_width = width
|
||||
svg_writer.camera_height = height
|
||||
svg_writer.camera_projection = tuple(camera.matrix_world.to_quaternion() @ Vector((0, 0, -1)))
|
||||
|
||||
for obj in camera.users_collection[0].objects:
|
||||
if "IfcGrid" in obj.name:
|
||||
svg_writer.annotations.setdefault("grid_objs", []).append(obj)
|
||||
elif obj.type == "CAMERA":
|
||||
continue
|
||||
|
||||
if "IfcAnnotation/" not in obj.name:
|
||||
continue
|
||||
|
||||
if "Leader" in obj.name:
|
||||
svg_writer.annotations["leader_obj"] = (obj, obj.data)
|
||||
elif "Stair" in obj.name:
|
||||
svg_writer.annotations["stair_obj"] = obj
|
||||
elif "Equal" in obj.name:
|
||||
svg_writer.annotations.setdefault("equal_objs", []).append(obj)
|
||||
elif "Dimension" in obj.name:
|
||||
svg_writer.annotations.setdefault("dimension_objs", []).append(obj)
|
||||
elif "Break" in obj.name:
|
||||
svg_writer.annotations["break_obj"] = obj
|
||||
elif "Hidden" in obj.name:
|
||||
svg_writer.annotations.setdefault("hidden_objs", []).append((obj, obj.data))
|
||||
elif "Solid" in obj.name:
|
||||
svg_writer.annotations.setdefault("solid_objs", []).append((obj, obj.data))
|
||||
elif "Plan Level" in obj.name:
|
||||
svg_writer.annotations["plan_level_obj"] = obj
|
||||
elif "Section Level" in obj.name:
|
||||
svg_writer.annotations["section_level_obj"] = obj
|
||||
elif obj.type == "FONT":
|
||||
svg_writer.annotations.setdefault("text_objs", []).append(obj)
|
||||
else:
|
||||
svg_writer.annotations.setdefault("misc_objs", []).append(obj)
|
||||
|
||||
svg_writer.annotations["attributes"] = [a.name for a in drawing_style.attributes]
|
||||
|
||||
svg_writer.write()
|
||||
return svg_writer.output
|
||||
|
||||
def is_landscape(self):
|
||||
return bpy.context.scene.render.resolution_x > bpy.context.scene.render.resolution_y
|
||||
|
||||
|
||||
class AddAnnotation(bpy.types.Operator):
|
||||
bl_idname = "bim.add_annotation"
|
||||
bl_label = "Add Annotation"
|
||||
obj_name: bpy.props.StringProperty()
|
||||
data_type: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
if not bpy.context.scene.camera:
|
||||
return {"FINISHED"}
|
||||
if self.data_type == "text":
|
||||
if bpy.context.selected_objects:
|
||||
for selected_object in bpy.context.selected_objects:
|
||||
obj = annotation.Annotator.add_text(related_element=selected_object)
|
||||
else:
|
||||
obj = annotation.Annotator.add_text()
|
||||
else:
|
||||
obj = annotation.Annotator.get_annotation_obj(self.obj_name, self.data_type)
|
||||
if self.obj_name == "Break":
|
||||
obj = annotation.Annotator.add_plane_to_annotation(obj)
|
||||
else:
|
||||
obj = annotation.Annotator.add_line_to_annotation(obj)
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
return {"FINISHED"}
|
||||
|
||||
+48
-43
@@ -6,8 +6,8 @@ import pystache
|
||||
import xml.etree.ElementTree as ET
|
||||
import svgwrite
|
||||
import ifcopenshell
|
||||
from . import annotation
|
||||
from . import helper
|
||||
import blenderbim.bim.module.drawing.helper as helper
|
||||
import blenderbim.bim.module.drawing.annotation as annotation
|
||||
from mathutils import Vector
|
||||
from mathutils import geometry
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
@@ -39,14 +39,16 @@ class External(svgwrite.container.Group):
|
||||
|
||||
|
||||
class SvgWriter:
|
||||
def __init__(self, ifc_cutter):
|
||||
self.ifc_cutter = ifc_cutter
|
||||
def __init__(self):
|
||||
self.output = "out.svg"
|
||||
self.data_dir = None
|
||||
self.vector_style = None
|
||||
self.human_scale = "NTS"
|
||||
self.annotations = {}
|
||||
self.scale = 1 / 100 # 1:100
|
||||
|
||||
def write(self):
|
||||
self.calculate_scale()
|
||||
self.output = os.path.join(self.ifc_cutter.data_dir, "diagrams", self.ifc_cutter.diagram_name + ".svg")
|
||||
self.svg = svgwrite.Drawing(
|
||||
self.output,
|
||||
debug=False,
|
||||
@@ -60,42 +62,43 @@ class SvgWriter:
|
||||
self.add_markers()
|
||||
self.add_symbols()
|
||||
self.add_patterns()
|
||||
self.draw_background_image()
|
||||
self.draw_background_elements()
|
||||
self.draw_cut_polygons()
|
||||
# self.draw_background_image()
|
||||
# self.draw_background_elements()
|
||||
# self.draw_cut_polygons()
|
||||
self.draw_annotations()
|
||||
self.svg.save(pretty=True)
|
||||
|
||||
def calculate_scale(self):
|
||||
self.scale *= 1000 # IFC is in meters, SVG is in mm
|
||||
self.raw_width = self.ifc_cutter.section_box["x"]
|
||||
self.raw_height = self.ifc_cutter.section_box["y"]
|
||||
self.raw_width = self.camera_width
|
||||
self.raw_height = self.camera_height
|
||||
self.width = self.raw_width * self.scale
|
||||
self.height = self.raw_height * self.scale
|
||||
|
||||
def add_stylesheet(self):
|
||||
with open("{}styles/{}.css".format(self.ifc_cutter.data_dir, self.ifc_cutter.vector_style), "r") as stylesheet:
|
||||
with open("{}styles/{}.css".format(self.data_dir, self.vector_style), "r") as stylesheet:
|
||||
self.svg.defs.add(self.svg.style(stylesheet.read()))
|
||||
|
||||
def add_markers(self):
|
||||
tree = ET.parse("{}templates/markers.svg".format(self.ifc_cutter.data_dir))
|
||||
tree = ET.parse("{}templates/markers.svg".format(self.data_dir))
|
||||
root = tree.getroot()
|
||||
for child in root.getchildren():
|
||||
self.svg.defs.add(External(child))
|
||||
|
||||
def add_symbols(self):
|
||||
tree = ET.parse("{}templates/symbols.svg".format(self.ifc_cutter.data_dir))
|
||||
tree = ET.parse("{}templates/symbols.svg".format(self.data_dir))
|
||||
root = tree.getroot()
|
||||
for child in root.getchildren():
|
||||
self.svg.defs.add(External(child))
|
||||
|
||||
def add_patterns(self):
|
||||
tree = ET.parse("{}templates/patterns.svg".format(self.ifc_cutter.data_dir))
|
||||
tree = ET.parse("{}templates/patterns.svg".format(self.data_dir))
|
||||
root = tree.getroot()
|
||||
for child in root.getchildren():
|
||||
self.svg.defs.add(External(child))
|
||||
|
||||
def draw_background_image(self):
|
||||
return # TODO reimplement for artistic drawing options
|
||||
self.svg.add(
|
||||
self.svg.image(
|
||||
os.path.join("..", "diagrams", os.path.basename(self.ifc_cutter.background_image)),
|
||||
@@ -104,6 +107,7 @@ class SvgWriter:
|
||||
)
|
||||
|
||||
def draw_background_elements(self):
|
||||
return # TODO purge?
|
||||
for element in self.ifc_cutter.background_elements:
|
||||
if element["type"] == "polygon":
|
||||
self.draw_polygon(element, "background")
|
||||
@@ -116,16 +120,16 @@ class SvgWriter:
|
||||
x_offset = self.raw_width / 2
|
||||
y_offset = self.raw_height / 2
|
||||
|
||||
for obj in self.ifc_cutter.equal_objs:
|
||||
for obj in self.annotations.get("equal_objs", []):
|
||||
self.draw_dimension_annotations(obj, text_override="EQ")
|
||||
for obj in self.ifc_cutter.dimension_objs:
|
||||
for obj in self.annotations.get("dimension_objs", []):
|
||||
self.draw_dimension_annotations(obj)
|
||||
self.draw_measureit_arch_dimension_annotations()
|
||||
|
||||
if self.ifc_cutter.break_obj:
|
||||
self.draw_break_annotations(self.ifc_cutter.break_obj)
|
||||
if self.annotations.get("break_obj"):
|
||||
self.draw_break_annotations(self.annotations["break_obj"])
|
||||
|
||||
for grid_obj in self.ifc_cutter.grid_objs:
|
||||
for grid_obj in self.annotations.get("grid_objs", []):
|
||||
matrix_world = grid_obj.matrix_world
|
||||
for edge in grid_obj.data.edges:
|
||||
classes = ["annotation", "grid"]
|
||||
@@ -174,21 +178,21 @@ class SvgWriter:
|
||||
|
||||
self.draw_ifc_annotation()
|
||||
|
||||
for obj in self.ifc_cutter.misc_objs:
|
||||
for obj in self.annotations.get("misc_objs", []):
|
||||
self.draw_misc_annotation(obj, ["IfcAnnotation"])
|
||||
|
||||
for obj_data in self.ifc_cutter.hidden_objs:
|
||||
for obj_data in self.annotations.get("hidden_objs", []):
|
||||
self.draw_line_annotation(obj_data, ["hidden"])
|
||||
|
||||
for obj_data in self.ifc_cutter.solid_objs:
|
||||
for obj_data in self.annotations.get("solid_objs", []):
|
||||
self.draw_line_annotation(obj_data, ["solid"])
|
||||
|
||||
if self.ifc_cutter.leader_obj:
|
||||
self.draw_line_annotation(self.ifc_cutter.leader_obj, ["leader"])
|
||||
if self.annotations.get("leader_obj"):
|
||||
self.draw_line_annotation(self.annotations["leader_obj"], ["leader"])
|
||||
|
||||
if self.ifc_cutter.plan_level_obj:
|
||||
matrix_world = self.ifc_cutter.plan_level_obj.matrix_world
|
||||
for spline in self.ifc_cutter.plan_level_obj.data.splines:
|
||||
if self.annotations.get("plan_level_obj"):
|
||||
matrix_world = self.annotations["plan_level_obj"].matrix_world
|
||||
for spline in self.annotations["plan_level_obj"].data.splines:
|
||||
classes = ["annotation", "plan-level"]
|
||||
points = self.get_spline_points(spline)
|
||||
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
|
||||
@@ -208,7 +212,7 @@ class SvgWriter:
|
||||
)
|
||||
)
|
||||
# TODO: allow metric to be configurable
|
||||
rl = ((matrix_world @ points[0].co).xyz + self.ifc_cutter.plan_level_obj.location).z
|
||||
rl = ((matrix_world @ points[0].co).xyz + self.annotations["plan_level_obj"].location).z
|
||||
if bpy.context.scene.unit_settings.system == "IMPERIAL":
|
||||
rl = helper.format_distance(rl)
|
||||
else:
|
||||
@@ -231,9 +235,9 @@ class SvgWriter:
|
||||
)
|
||||
)
|
||||
|
||||
if self.ifc_cutter.section_level_obj:
|
||||
matrix_world = self.ifc_cutter.section_level_obj.matrix_world
|
||||
for spline in self.ifc_cutter.section_level_obj.data.splines:
|
||||
if self.annotations.get("section_level_obj"):
|
||||
matrix_world = self.annotations["section_level_obj"].matrix_world
|
||||
for spline in self.annotations["section_level_obj"].data.splines:
|
||||
classes = ["annotation", "section-level"]
|
||||
points = self.get_spline_points(spline)
|
||||
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
|
||||
@@ -273,9 +277,9 @@ class SvgWriter:
|
||||
)
|
||||
)
|
||||
|
||||
if self.ifc_cutter.stair_obj:
|
||||
matrix_world = self.ifc_cutter.stair_obj.matrix_world
|
||||
for spline in self.ifc_cutter.stair_obj.data.splines:
|
||||
if self.annotations.get("stair_obj"):
|
||||
matrix_world = self.annotations["stair_obj"].matrix_world
|
||||
for spline in self.annotations["stair_obj"].data.splines:
|
||||
classes = ["annotation", "stair"]
|
||||
points = self.get_spline_points(spline)
|
||||
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
|
||||
@@ -308,7 +312,7 @@ class SvgWriter:
|
||||
def draw_ifc_annotation(self):
|
||||
x_offset = self.raw_width / 2
|
||||
y_offset = self.raw_height / 2
|
||||
for annotation in self.ifc_cutter.annotation_objs:
|
||||
for annotation in self.annotations.get("annotation_objs", []):
|
||||
for edge in annotation["edges"]:
|
||||
v0_global = annotation["vertices"][edge[0]]
|
||||
v1_global = annotation["vertices"][edge[1]]
|
||||
@@ -357,7 +361,7 @@ class SvgWriter:
|
||||
classes.append("material-{}".format(re.sub("[^0-9a-zA-Z]+", "", slot.material.name)))
|
||||
global_id = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId
|
||||
classes.append("globalid-{}".format(global_id))
|
||||
for attribute in self.ifc_cutter.attributes:
|
||||
for attribute in self.annotations.get("attributes", []):
|
||||
result = self.get_obj_value(obj, attribute)
|
||||
if result:
|
||||
classes.append(
|
||||
@@ -435,7 +439,7 @@ class SvgWriter:
|
||||
x_offset = self.raw_width / 2
|
||||
y_offset = self.raw_height / 2
|
||||
|
||||
for text_obj in self.ifc_cutter.text_objs:
|
||||
for text_obj in self.annotations.get("text_objs", []):
|
||||
text_position = self.project_point_onto_camera(text_obj.location)
|
||||
text_position = Vector(((x_offset + text_position.x), (y_offset - text_position.y)))
|
||||
|
||||
@@ -473,8 +477,8 @@ class SvgWriter:
|
||||
alignment_baseline = "baseline"
|
||||
|
||||
text_body = text_obj.data.body
|
||||
if text_obj.name in self.ifc_cutter.template_variables:
|
||||
text_body = pystache.render(text_body, self.ifc_cutter.template_variables[text_obj.name])
|
||||
if text_obj.name in self.annotations.get("template_variables", {}):
|
||||
text_body = pystache.render(text_body, self.annotations["template_variables"][text_obj.name])
|
||||
|
||||
for line_number, text_line in enumerate(text_body.split("\n")):
|
||||
self.svg.add(
|
||||
@@ -587,17 +591,18 @@ class SvgWriter:
|
||||
)
|
||||
|
||||
def project_point_onto_camera(self, point):
|
||||
return self.ifc_cutter.camera_obj.matrix_world.inverted() @ geometry.intersect_line_plane(
|
||||
return self.camera.matrix_world.inverted() @ geometry.intersect_line_plane(
|
||||
point.xyz,
|
||||
point.xyz - Vector(self.ifc_cutter.section_box["projection"]),
|
||||
self.ifc_cutter.camera_obj.location,
|
||||
Vector(self.ifc_cutter.section_box["projection"]),
|
||||
point.xyz - Vector(self.camera_projection),
|
||||
self.camera.location,
|
||||
Vector(self.camera_projection),
|
||||
)
|
||||
|
||||
def get_spline_points(self, spline):
|
||||
return spline.bezier_points if spline.bezier_points else spline.points
|
||||
|
||||
def draw_cut_polygons(self):
|
||||
return # deprecate?
|
||||
for polygon in self.ifc_cutter.cut_polygons:
|
||||
self.draw_polygon(polygon, "cut")
|
||||
|
||||
@@ -16,12 +16,10 @@ import numpy as np
|
||||
from . import export_ifc
|
||||
from . import import_ifc
|
||||
from . import cut_ifc
|
||||
from . import svgwriter
|
||||
from . import sheeter
|
||||
from . import scheduler
|
||||
from . import schema
|
||||
from . import ifc
|
||||
from . import annotation
|
||||
from . import helper
|
||||
from bpy_extras.io_utils import ImportHelper
|
||||
from mathutils import Vector, Matrix, Euler, geometry
|
||||
@@ -1102,33 +1100,6 @@ class SelectDocIfcFile(bpy.types.Operator):
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
|
||||
class AddAnnotation(bpy.types.Operator):
|
||||
bl_idname = "bim.add_annotation"
|
||||
bl_label = "Add Annotation"
|
||||
obj_name: bpy.props.StringProperty()
|
||||
data_type: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
if not bpy.context.scene.camera:
|
||||
return {"FINISHED"}
|
||||
if self.data_type == "text":
|
||||
if bpy.context.selected_objects:
|
||||
for selected_object in bpy.context.selected_objects:
|
||||
obj = annotation.Annotator.add_text(related_element=selected_object)
|
||||
else:
|
||||
obj = annotation.Annotator.add_text()
|
||||
else:
|
||||
obj = annotation.Annotator.get_annotation_obj(self.obj_name, self.data_type)
|
||||
if self.obj_name == "Break":
|
||||
obj = annotation.Annotator.add_plane_to_annotation(obj)
|
||||
else:
|
||||
obj = annotation.Annotator.add_line_to_annotation(obj)
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class GenerateReferences(bpy.types.Operator):
|
||||
bl_idname = "bim.generate_references"
|
||||
bl_label = "Generate References"
|
||||
|
||||
@@ -6,7 +6,7 @@ import ifcopenshell
|
||||
from . import export_ifc
|
||||
from . import schema
|
||||
from . import ifc
|
||||
from . import annotation
|
||||
import blenderbim.bim.module.drawing.annotation as annotation
|
||||
from . import decoration
|
||||
import bpy
|
||||
from blenderbim.bim.handler import purge_module_data
|
||||
|
||||
Reference in New Issue
Block a user