A little progress on refactoring the old drawing module into an IfcConvert based system. See #1153.

This commit is contained in:
Dion Moult
2021-05-14 17:48:53 +10:00
parent c40f07f9b5
commit 13c642c6ab
9 changed files with 328 additions and 224 deletions
@@ -86,7 +86,6 @@ if bpy is not None:
operator.AddIfcFile, operator.AddIfcFile,
operator.RemoveIfcFile, operator.RemoveIfcFile,
operator.SelectDocIfcFile, operator.SelectDocIfcFile,
operator.AddAnnotation,
operator.GenerateReferences, operator.GenerateReferences,
operator.ResizeText, operator.ResizeText,
operator.AddVariable, operator.AddVariable,
-139
View File
@@ -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 # 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): def parse_diagram_scale(camera):
"""Returns numeric value of scale""" """Returns numeric value of scale"""
if camera.BIMCameraProperties.diagram_scale == "CUSTOM": if camera.BIMCameraProperties.diagram_scale == "CUSTOM":
@@ -4,6 +4,7 @@ from . import ui, operator
classes = ( classes = (
operator.AddDrawing, operator.AddDrawing,
operator.CreateDrawing, operator.CreateDrawing,
operator.AddAnnotation,
ui.BIM_PT_camera, 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 json
import subprocess import subprocess
import webbrowser 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.operator import open_with_user_command
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.group.data import Data as GroupData from ifcopenshell.api.group.data import Data as GroupData
@@ -57,33 +60,156 @@ class CreateDrawing(bpy.types.Operator):
bl_label = "Create Drawing" bl_label = "Create Drawing"
def execute(self, context): 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") ifcconvert_path = os.path.join(cwd, "..", "..", "..", "libs", "IfcConvert")
subprocess.run( subprocess.run(
[ [
ifcconvert_path, ifcconvert_path,
context.scene.BIMProperties.ifc_file, context.scene.BIMProperties.ifc_file,
"-yv", "-yv",
context.scene.BIMProperties.ifc_file[0:-4] + ".svg", svg_path,
"--plan", "--plan",
"--model", "--model",
"--section-height-from-storeys",
"--section-height=1.2",
"--door-arcs",
"--print-space-names",
"--print-space-areas",
"--bounds=1024x1024",
"--elevation-ref=DRAWING", "--elevation-ref=DRAWING",
"--svg-xmlns", "--svg-xmlns",
"--svg-project", "--svg-project",
"--svg-poly", "--svg-poly",
"--svg-without-storeys",
"--exclude", "--exclude",
"entities", "entities",
"IfcSpace", "IfcSpace",
"IfcOpeningElement", "IfcOpeningElement",
] ]
) )
open_with_user_command( return svg_path
bpy.context.preferences.addons["blenderbim"].preferences.svg_command,
context.scene.BIMProperties.ifc_file[0:-4] + ".svg", 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"} return {"FINISHED"}
@@ -6,8 +6,8 @@ import pystache
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
import svgwrite import svgwrite
import ifcopenshell import ifcopenshell
from . import annotation import blenderbim.bim.module.drawing.helper as helper
from . import helper import blenderbim.bim.module.drawing.annotation as annotation
from mathutils import Vector from mathutils import Vector
from mathutils import geometry from mathutils import geometry
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
@@ -39,14 +39,16 @@ class External(svgwrite.container.Group):
class SvgWriter: class SvgWriter:
def __init__(self, ifc_cutter): def __init__(self):
self.ifc_cutter = ifc_cutter self.output = "out.svg"
self.data_dir = None
self.vector_style = None
self.human_scale = "NTS" self.human_scale = "NTS"
self.annotations = {}
self.scale = 1 / 100 # 1:100 self.scale = 1 / 100 # 1:100
def write(self): def write(self):
self.calculate_scale() 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.svg = svgwrite.Drawing(
self.output, self.output,
debug=False, debug=False,
@@ -60,42 +62,43 @@ class SvgWriter:
self.add_markers() self.add_markers()
self.add_symbols() self.add_symbols()
self.add_patterns() self.add_patterns()
self.draw_background_image() # self.draw_background_image()
self.draw_background_elements() # self.draw_background_elements()
self.draw_cut_polygons() # self.draw_cut_polygons()
self.draw_annotations() self.draw_annotations()
self.svg.save(pretty=True) self.svg.save(pretty=True)
def calculate_scale(self): def calculate_scale(self):
self.scale *= 1000 # IFC is in meters, SVG is in mm self.scale *= 1000 # IFC is in meters, SVG is in mm
self.raw_width = self.ifc_cutter.section_box["x"] self.raw_width = self.camera_width
self.raw_height = self.ifc_cutter.section_box["y"] self.raw_height = self.camera_height
self.width = self.raw_width * self.scale self.width = self.raw_width * self.scale
self.height = self.raw_height * self.scale self.height = self.raw_height * self.scale
def add_stylesheet(self): 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())) self.svg.defs.add(self.svg.style(stylesheet.read()))
def add_markers(self): 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() root = tree.getroot()
for child in root.getchildren(): for child in root.getchildren():
self.svg.defs.add(External(child)) self.svg.defs.add(External(child))
def add_symbols(self): 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() root = tree.getroot()
for child in root.getchildren(): for child in root.getchildren():
self.svg.defs.add(External(child)) self.svg.defs.add(External(child))
def add_patterns(self): 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() root = tree.getroot()
for child in root.getchildren(): for child in root.getchildren():
self.svg.defs.add(External(child)) self.svg.defs.add(External(child))
def draw_background_image(self): def draw_background_image(self):
return # TODO reimplement for artistic drawing options
self.svg.add( self.svg.add(
self.svg.image( self.svg.image(
os.path.join("..", "diagrams", os.path.basename(self.ifc_cutter.background_image)), os.path.join("..", "diagrams", os.path.basename(self.ifc_cutter.background_image)),
@@ -104,6 +107,7 @@ class SvgWriter:
) )
def draw_background_elements(self): def draw_background_elements(self):
return # TODO purge?
for element in self.ifc_cutter.background_elements: for element in self.ifc_cutter.background_elements:
if element["type"] == "polygon": if element["type"] == "polygon":
self.draw_polygon(element, "background") self.draw_polygon(element, "background")
@@ -116,16 +120,16 @@ class SvgWriter:
x_offset = self.raw_width / 2 x_offset = self.raw_width / 2
y_offset = self.raw_height / 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") 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_dimension_annotations(obj)
self.draw_measureit_arch_dimension_annotations() self.draw_measureit_arch_dimension_annotations()
if self.ifc_cutter.break_obj: if self.annotations.get("break_obj"):
self.draw_break_annotations(self.ifc_cutter.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 matrix_world = grid_obj.matrix_world
for edge in grid_obj.data.edges: for edge in grid_obj.data.edges:
classes = ["annotation", "grid"] classes = ["annotation", "grid"]
@@ -174,21 +178,21 @@ class SvgWriter:
self.draw_ifc_annotation() 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"]) 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"]) 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"]) self.draw_line_annotation(obj_data, ["solid"])
if self.ifc_cutter.leader_obj: if self.annotations.get("leader_obj"):
self.draw_line_annotation(self.ifc_cutter.leader_obj, ["leader"]) self.draw_line_annotation(self.annotations["leader_obj"], ["leader"])
if self.ifc_cutter.plan_level_obj: if self.annotations.get("plan_level_obj"):
matrix_world = self.ifc_cutter.plan_level_obj.matrix_world matrix_world = self.annotations["plan_level_obj"].matrix_world
for spline in self.ifc_cutter.plan_level_obj.data.splines: for spline in self.annotations["plan_level_obj"].data.splines:
classes = ["annotation", "plan-level"] classes = ["annotation", "plan-level"]
points = self.get_spline_points(spline) points = self.get_spline_points(spline)
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points] 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 # 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": if bpy.context.scene.unit_settings.system == "IMPERIAL":
rl = helper.format_distance(rl) rl = helper.format_distance(rl)
else: else:
@@ -231,9 +235,9 @@ class SvgWriter:
) )
) )
if self.ifc_cutter.section_level_obj: if self.annotations.get("section_level_obj"):
matrix_world = self.ifc_cutter.section_level_obj.matrix_world matrix_world = self.annotations["section_level_obj"].matrix_world
for spline in self.ifc_cutter.section_level_obj.data.splines: for spline in self.annotations["section_level_obj"].data.splines:
classes = ["annotation", "section-level"] classes = ["annotation", "section-level"]
points = self.get_spline_points(spline) points = self.get_spline_points(spline)
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points] 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: if self.annotations.get("stair_obj"):
matrix_world = self.ifc_cutter.stair_obj.matrix_world matrix_world = self.annotations["stair_obj"].matrix_world
for spline in self.ifc_cutter.stair_obj.data.splines: for spline in self.annotations["stair_obj"].data.splines:
classes = ["annotation", "stair"] classes = ["annotation", "stair"]
points = self.get_spline_points(spline) points = self.get_spline_points(spline)
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points] 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): def draw_ifc_annotation(self):
x_offset = self.raw_width / 2 x_offset = self.raw_width / 2
y_offset = self.raw_height / 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"]: for edge in annotation["edges"]:
v0_global = annotation["vertices"][edge[0]] v0_global = annotation["vertices"][edge[0]]
v1_global = annotation["vertices"][edge[1]] 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))) 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 global_id = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId
classes.append("globalid-{}".format(global_id)) 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) result = self.get_obj_value(obj, attribute)
if result: if result:
classes.append( classes.append(
@@ -435,7 +439,7 @@ class SvgWriter:
x_offset = self.raw_width / 2 x_offset = self.raw_width / 2
y_offset = self.raw_height / 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 = self.project_point_onto_camera(text_obj.location)
text_position = Vector(((x_offset + text_position.x), (y_offset - text_position.y))) text_position = Vector(((x_offset + text_position.x), (y_offset - text_position.y)))
@@ -473,8 +477,8 @@ class SvgWriter:
alignment_baseline = "baseline" alignment_baseline = "baseline"
text_body = text_obj.data.body text_body = text_obj.data.body
if text_obj.name in self.ifc_cutter.template_variables: if text_obj.name in self.annotations.get("template_variables", {}):
text_body = pystache.render(text_body, self.ifc_cutter.template_variables[text_obj.name]) text_body = pystache.render(text_body, self.annotations["template_variables"][text_obj.name])
for line_number, text_line in enumerate(text_body.split("\n")): for line_number, text_line in enumerate(text_body.split("\n")):
self.svg.add( self.svg.add(
@@ -587,17 +591,18 @@ class SvgWriter:
) )
def project_point_onto_camera(self, point): 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,
point.xyz - Vector(self.ifc_cutter.section_box["projection"]), point.xyz - Vector(self.camera_projection),
self.ifc_cutter.camera_obj.location, self.camera.location,
Vector(self.ifc_cutter.section_box["projection"]), Vector(self.camera_projection),
) )
def get_spline_points(self, spline): def get_spline_points(self, spline):
return spline.bezier_points if spline.bezier_points else spline.points return spline.bezier_points if spline.bezier_points else spline.points
def draw_cut_polygons(self): def draw_cut_polygons(self):
return # deprecate?
for polygon in self.ifc_cutter.cut_polygons: for polygon in self.ifc_cutter.cut_polygons:
self.draw_polygon(polygon, "cut") self.draw_polygon(polygon, "cut")
-29
View File
@@ -16,12 +16,10 @@ import numpy as np
from . import export_ifc from . import export_ifc
from . import import_ifc from . import import_ifc
from . import cut_ifc from . import cut_ifc
from . import svgwriter
from . import sheeter from . import sheeter
from . import scheduler from . import scheduler
from . import schema from . import schema
from . import ifc from . import ifc
from . import annotation
from . import helper from . import helper
from bpy_extras.io_utils import ImportHelper from bpy_extras.io_utils import ImportHelper
from mathutils import Vector, Matrix, Euler, geometry from mathutils import Vector, Matrix, Euler, geometry
@@ -1102,33 +1100,6 @@ class SelectDocIfcFile(bpy.types.Operator):
return {"RUNNING_MODAL"} 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): class GenerateReferences(bpy.types.Operator):
bl_idname = "bim.generate_references" bl_idname = "bim.generate_references"
bl_label = "Generate References" bl_label = "Generate References"
+1 -1
View File
@@ -6,7 +6,7 @@ import ifcopenshell
from . import export_ifc from . import export_ifc
from . import schema from . import schema
from . import ifc from . import ifc
from . import annotation import blenderbim.bim.module.drawing.annotation as annotation
from . import decoration from . import decoration
import bpy import bpy
from blenderbim.bim.handler import purge_module_data from blenderbim.bim.handler import purge_module_data