mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-22 21:42:28 +00:00
Add section cut multiprocessing, grids, hidden lines, stair arrows, text annotations, and leaders
This commit is contained in:
@@ -11,124 +11,128 @@ bl_info = {
|
|||||||
"category": "Import-Export"
|
"category": "Import-Export"
|
||||||
}
|
}
|
||||||
|
|
||||||
import bpy
|
# are we running inside Blender?
|
||||||
from . import ui, prop, operator
|
import sys
|
||||||
|
bpy = sys.modules.get('bpy')
|
||||||
|
if bpy is not None:
|
||||||
|
import bpy
|
||||||
|
from . import ui, prop, operator
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
operator.AssignClass,
|
operator.AssignClass,
|
||||||
operator.SelectClass,
|
operator.SelectClass,
|
||||||
operator.SelectType,
|
operator.SelectType,
|
||||||
operator.SelectFeaturesDir,
|
operator.SelectFeaturesDir,
|
||||||
operator.SelectDiffJsonFile,
|
operator.SelectDiffJsonFile,
|
||||||
operator.SelectDataDir,
|
operator.SelectDataDir,
|
||||||
operator.SelectSchemaDir,
|
operator.SelectSchemaDir,
|
||||||
operator.ExportIFC,
|
operator.ExportIFC,
|
||||||
operator.ImportIFC,
|
operator.ImportIFC,
|
||||||
operator.ColourByClass,
|
operator.ColourByClass,
|
||||||
operator.ResetObjectColours,
|
operator.ResetObjectColours,
|
||||||
operator.ApproveClass,
|
operator.ApproveClass,
|
||||||
operator.RejectClass,
|
operator.RejectClass,
|
||||||
operator.SelectAudited,
|
operator.SelectAudited,
|
||||||
operator.RejectElement,
|
operator.RejectElement,
|
||||||
operator.SelectExternalMaterialDir,
|
operator.SelectExternalMaterialDir,
|
||||||
operator.AddSweptSolid,
|
operator.AddSweptSolid,
|
||||||
operator.RemoveSweptSolid,
|
operator.RemoveSweptSolid,
|
||||||
operator.AssignSweptSolidOuterCurve,
|
operator.AssignSweptSolidOuterCurve,
|
||||||
operator.SelectSweptSolidOuterCurve,
|
operator.SelectSweptSolidOuterCurve,
|
||||||
operator.AddSweptSolidInnerCurve,
|
operator.AddSweptSolidInnerCurve,
|
||||||
operator.SelectSweptSolidInnerCurves,
|
operator.SelectSweptSolidInnerCurves,
|
||||||
operator.AssignSweptSolidExtrusion,
|
operator.AssignSweptSolidExtrusion,
|
||||||
operator.SelectSweptSolidExtrusion,
|
operator.SelectSweptSolidExtrusion,
|
||||||
operator.AssignPset,
|
operator.AssignPset,
|
||||||
operator.UnassignPset,
|
operator.UnassignPset,
|
||||||
operator.AddPset,
|
operator.AddPset,
|
||||||
operator.RemovePset,
|
operator.RemovePset,
|
||||||
operator.AddDocument,
|
operator.AddDocument,
|
||||||
operator.RemoveDocument,
|
operator.RemoveDocument,
|
||||||
operator.GenerateGlobalId,
|
operator.GenerateGlobalId,
|
||||||
operator.AddAttribute,
|
operator.AddAttribute,
|
||||||
operator.RemoveAttribute,
|
operator.RemoveAttribute,
|
||||||
operator.QuickProjectSetup,
|
operator.QuickProjectSetup,
|
||||||
operator.SelectGlobalId,
|
operator.SelectGlobalId,
|
||||||
operator.CreateAggregate,
|
operator.CreateAggregate,
|
||||||
operator.EditAggregate,
|
operator.EditAggregate,
|
||||||
operator.SaveAggregate,
|
operator.SaveAggregate,
|
||||||
operator.AssignClassification,
|
operator.AssignClassification,
|
||||||
operator.UnassignClassification,
|
operator.UnassignClassification,
|
||||||
operator.RemoveClassification,
|
operator.RemoveClassification,
|
||||||
operator.FetchLibraryInformation,
|
operator.FetchLibraryInformation,
|
||||||
operator.FetchExternalMaterial,
|
operator.FetchExternalMaterial,
|
||||||
operator.FetchObjectPassport,
|
operator.FetchObjectPassport,
|
||||||
operator.AddSubcontext,
|
operator.AddSubcontext,
|
||||||
operator.RemoveSubcontext,
|
operator.RemoveSubcontext,
|
||||||
operator.CutSection,
|
operator.CutSection,
|
||||||
operator.CreateSheets,
|
operator.CreateSheets,
|
||||||
prop.Subcontext,
|
prop.Subcontext,
|
||||||
prop.BIMProperties,
|
prop.BIMProperties,
|
||||||
prop.DocProperties,
|
prop.DocProperties,
|
||||||
prop.BIMLibrary,
|
prop.BIMLibrary,
|
||||||
prop.MapConversion,
|
prop.MapConversion,
|
||||||
prop.TargetCRS,
|
prop.TargetCRS,
|
||||||
prop.Attribute,
|
prop.Attribute,
|
||||||
prop.Pset,
|
prop.Pset,
|
||||||
prop.Document,
|
prop.Document,
|
||||||
prop.Classification,
|
prop.Classification,
|
||||||
prop.GlobalId,
|
prop.GlobalId,
|
||||||
prop.BIMObjectProperties,
|
prop.BIMObjectProperties,
|
||||||
prop.BIMMaterialProperties,
|
prop.BIMMaterialProperties,
|
||||||
prop.SweptSolid,
|
prop.SweptSolid,
|
||||||
prop.BIMMeshProperties,
|
prop.BIMMeshProperties,
|
||||||
ui.BIM_PT_documentation,
|
ui.BIM_PT_documentation,
|
||||||
ui.BIM_PT_bim,
|
ui.BIM_PT_bim,
|
||||||
ui.BIM_PT_context,
|
ui.BIM_PT_context,
|
||||||
ui.BIM_PT_qa,
|
ui.BIM_PT_qa,
|
||||||
ui.BIM_PT_library,
|
ui.BIM_PT_library,
|
||||||
ui.BIM_PT_gis,
|
ui.BIM_PT_gis,
|
||||||
ui.BIM_PT_diff,
|
ui.BIM_PT_diff,
|
||||||
ui.BIM_PT_mvd,
|
ui.BIM_PT_mvd,
|
||||||
ui.BIM_PT_material,
|
ui.BIM_PT_material,
|
||||||
ui.BIM_PT_mesh,
|
ui.BIM_PT_mesh,
|
||||||
ui.BIM_PT_object,
|
ui.BIM_PT_object,
|
||||||
)
|
)
|
||||||
|
|
||||||
def menu_func_export(self, context):
|
def menu_func_export(self, context):
|
||||||
self.layout.operator(operator.ExportIFC.bl_idname,
|
self.layout.operator(operator.ExportIFC.bl_idname,
|
||||||
text="Industry Foundation Classes (.ifc)")
|
text="Industry Foundation Classes (.ifc)")
|
||||||
|
|
||||||
def menu_func_import(self, context):
|
def menu_func_import(self, context):
|
||||||
self.layout.operator(operator.ImportIFC.bl_idname,
|
self.layout.operator(operator.ImportIFC.bl_idname,
|
||||||
text="Industry Foundation Classes (.ifc)")
|
text="Industry Foundation Classes (.ifc)")
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
for cls in classes:
|
for cls in classes:
|
||||||
bpy.utils.register_class(cls)
|
bpy.utils.register_class(cls)
|
||||||
bpy.app.handlers.load_post.append(prop.setDefaultProperties)
|
bpy.app.handlers.load_post.append(prop.setDefaultProperties)
|
||||||
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
|
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
|
||||||
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
|
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
|
||||||
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
|
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
|
||||||
bpy.types.Scene.DocProperties = bpy.props.PointerProperty(type=prop.DocProperties)
|
bpy.types.Scene.DocProperties = bpy.props.PointerProperty(type=prop.DocProperties)
|
||||||
bpy.types.Scene.BIMLibrary = bpy.props.PointerProperty(type=prop.BIMLibrary)
|
bpy.types.Scene.BIMLibrary = bpy.props.PointerProperty(type=prop.BIMLibrary)
|
||||||
bpy.types.Scene.MapConversion = bpy.props.PointerProperty(type=prop.MapConversion)
|
bpy.types.Scene.MapConversion = bpy.props.PointerProperty(type=prop.MapConversion)
|
||||||
bpy.types.Scene.TargetCRS = bpy.props.PointerProperty(type=prop.TargetCRS)
|
bpy.types.Scene.TargetCRS = bpy.props.PointerProperty(type=prop.TargetCRS)
|
||||||
bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
|
bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
|
||||||
bpy.types.Collection.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
|
bpy.types.Collection.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
|
||||||
bpy.types.Material.BIMMaterialProperties = bpy.props.PointerProperty(type=prop.BIMMaterialProperties)
|
bpy.types.Material.BIMMaterialProperties = bpy.props.PointerProperty(type=prop.BIMMaterialProperties)
|
||||||
bpy.types.Mesh.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
|
bpy.types.Mesh.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties)
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
for cls in reversed(classes):
|
for cls in reversed(classes):
|
||||||
bpy.utils.unregister_class(cls)
|
bpy.utils.unregister_class(cls)
|
||||||
bpy.app.handlers.load_post.remove(prop.setDefaultProperties)
|
bpy.app.handlers.load_post.remove(prop.setDefaultProperties)
|
||||||
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
|
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
|
||||||
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
|
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
|
||||||
del(bpy.types.Scene.BIMProperties)
|
del(bpy.types.Scene.BIMProperties)
|
||||||
del(bpy.types.Scene.DocProperties)
|
del(bpy.types.Scene.DocProperties)
|
||||||
del(bpy.types.Scene.MapConversion)
|
del(bpy.types.Scene.MapConversion)
|
||||||
del(bpy.types.Scene.TargetCRS)
|
del(bpy.types.Scene.TargetCRS)
|
||||||
del(bpy.types.Object.BIMObjectProperties)
|
del(bpy.types.Object.BIMObjectProperties)
|
||||||
del(bpy.types.Collection.BIMObjectProperties)
|
del(bpy.types.Collection.BIMObjectProperties)
|
||||||
del(bpy.types.Material.BIMMaterialProperties)
|
del(bpy.types.Material.BIMMaterialProperties)
|
||||||
del(bpy.types.Mesh.BIMMeshProperties)
|
del(bpy.types.Mesh.BIMMeshProperties)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
register()
|
register()
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import numpy
|
|||||||
import pickle
|
import pickle
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from mathutils import Vector
|
mathutils = sys.modules.get('mathutils')
|
||||||
|
if mathutils is not None:
|
||||||
|
from mathutils import Vector
|
||||||
from math import degrees
|
from math import degrees
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
@@ -42,6 +44,80 @@ from OCC.TopoDS import topods
|
|||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.geom
|
import ifcopenshell.geom
|
||||||
|
|
||||||
|
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
this_file = os.path.join(cwd, 'cut_ifc.py')
|
||||||
|
|
||||||
|
def get_booleaned_edges(shape):
|
||||||
|
edges = []
|
||||||
|
exp = OCC.TopExp.TopExp_Explorer(shape, OCC.TopAbs.TopAbs_EDGE)
|
||||||
|
while exp.More():
|
||||||
|
edges.append(topods.Edge(exp.Current()))
|
||||||
|
exp.Next()
|
||||||
|
return edges
|
||||||
|
|
||||||
|
def connect_edges_into_wires(unconnected_edges):
|
||||||
|
edges = OCC.TopTools.TopTools_HSequenceOfShape()
|
||||||
|
edges_handle = OCC.TopTools.Handle_TopTools_HSequenceOfShape(edges)
|
||||||
|
wires = OCC.TopTools.TopTools_HSequenceOfShape()
|
||||||
|
wires_handle = OCC.TopTools.Handle_TopTools_HSequenceOfShape(wires)
|
||||||
|
|
||||||
|
for edge in unconnected_edges:
|
||||||
|
edges.Append(edge)
|
||||||
|
|
||||||
|
OCC.ShapeAnalysis.ShapeAnalysis_FreeBounds.ConnectEdgesToWires(edges_handle, 1e-5, True, wires_handle)
|
||||||
|
return wires_handle.GetObject()
|
||||||
|
|
||||||
|
def do_cut(process_data):
|
||||||
|
global_id, shape, section, trsf_data = process_data
|
||||||
|
|
||||||
|
axis = OCC.gp.gp_Ax2(
|
||||||
|
OCC.gp.gp_Pnt(
|
||||||
|
trsf_data['top_left_corner'][0],
|
||||||
|
trsf_data['top_left_corner'][1],
|
||||||
|
trsf_data['top_left_corner'][2]),
|
||||||
|
OCC.gp.gp_Dir(
|
||||||
|
trsf_data['projection'][0],
|
||||||
|
trsf_data['projection'][1],
|
||||||
|
trsf_data['projection'][2]),
|
||||||
|
OCC.gp.gp_Dir(
|
||||||
|
trsf_data['x_axis'][0],
|
||||||
|
trsf_data['x_axis'][1],
|
||||||
|
trsf_data['x_axis'][2])
|
||||||
|
)
|
||||||
|
source = OCC.gp.gp_Ax3(axis)
|
||||||
|
destination = OCC.gp.gp_Ax3(
|
||||||
|
OCC.gp.gp_Pnt(0, 0, 0),
|
||||||
|
OCC.gp.gp_Dir(0, 0, -1),
|
||||||
|
OCC.gp.gp_Dir(1, 0, 0))
|
||||||
|
transformation = OCC.gp.gp_Trsf()
|
||||||
|
transformation.SetDisplacement(source, destination)
|
||||||
|
|
||||||
|
cut_polygons = []
|
||||||
|
section = OCC.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 = OCC.BRepBuilderAPI.BRepBuilderAPI_Transform(
|
||||||
|
wire_shape, transformation)
|
||||||
|
wire_shape = transformed_wire.Shape()
|
||||||
|
|
||||||
|
wire = topods.Wire(wire_shape)
|
||||||
|
face = OCC.BRepBuilderAPI.BRepBuilderAPI_MakeFace(wire).Face()
|
||||||
|
|
||||||
|
points = []
|
||||||
|
exp = OCC.BRepTools.BRepTools_WireExplorer(wire)
|
||||||
|
while exp.More():
|
||||||
|
point = OCC.BRep.BRep_Tool.Pnt(exp.CurrentVertex())
|
||||||
|
points.append((point.X(), -point.Y()))
|
||||||
|
exp.Next()
|
||||||
|
cut_polygons.append({ 'global_id': global_id, 'points': points })
|
||||||
|
return cut_polygons
|
||||||
|
|
||||||
|
|
||||||
class IfcCutter:
|
class IfcCutter:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.product_shapes = []
|
self.product_shapes = []
|
||||||
@@ -253,10 +329,16 @@ class IfcCutter:
|
|||||||
self.section_box['face'] = section_box.BottomFace()
|
self.section_box['face'] = section_box.BottomFace()
|
||||||
|
|
||||||
source = OCC.gp.gp_Ax3(axis)
|
source = OCC.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 = OCC.gp.gp_Ax3(
|
destination = OCC.gp.gp_Ax3(
|
||||||
OCC.gp.gp_Pnt(0, 0, 0),
|
OCC.gp.gp_Pnt(0, 0, 0),
|
||||||
OCC.gp.gp_Dir(0, 0, -1),
|
OCC.gp.gp_Dir(0, 0, -1),
|
||||||
OCC.gp.gp_Dir(1, 0, 0))
|
OCC.gp.gp_Dir(1, 0, 0))
|
||||||
|
self.transformation_dest = destination
|
||||||
self.transformation = OCC.gp.gp_Trsf()
|
self.transformation = OCC.gp.gp_Trsf()
|
||||||
self.transformation.SetDisplacement(source, destination)
|
self.transformation.SetDisplacement(source, destination)
|
||||||
|
|
||||||
@@ -489,51 +571,22 @@ class IfcCutter:
|
|||||||
return
|
return
|
||||||
|
|
||||||
total_product_shapes = len(self.product_shapes)
|
total_product_shapes = len(self.product_shapes)
|
||||||
n = 0
|
process_data = [(p.GlobalId, s, self.section_box['face'], self.transformation_data) for p, s in self.product_shapes]
|
||||||
for product, shape in self.product_shapes:
|
|
||||||
print('{}/{} cut elements processed ...'.format(n, total_product_shapes), end='\r', flush=True)
|
|
||||||
n += 1
|
|
||||||
section = OCC.BRepAlgoAPI.BRepAlgoAPI_Section(self.section_box['face'], shape).Shape()
|
|
||||||
section_edges = self.get_booleaned_edges(section)
|
|
||||||
if len(section_edges) <= 0:
|
|
||||||
continue
|
|
||||||
wires = self.connect_edges_into_wires(section_edges)
|
|
||||||
for i in range(wires.Length()):
|
|
||||||
wire_shape = wires.Value(i+1)
|
|
||||||
|
|
||||||
transformed_wire = OCC.BRepBuilderAPI.BRepBuilderAPI_Transform(
|
import multiprocessing
|
||||||
wire_shape, self.transformation)
|
import bpy
|
||||||
wire_shape = transformed_wire.Shape()
|
multiprocessing.set_executable(bpy.app.binary_path_python)
|
||||||
|
|
||||||
wire = topods.Wire(wire_shape)
|
with multiprocessing.Pool(9) as p:
|
||||||
face = OCC.BRepBuilderAPI.BRepBuilderAPI_MakeFace(wire).Face()
|
results = p.map(do_cut, process_data)
|
||||||
|
for result in results:
|
||||||
points = []
|
polygons = [p for p in result if p['points']]
|
||||||
exp = OCC.BRepTools.BRepTools_WireExplorer(wire)
|
self.cut_polygons.extend(polygons)
|
||||||
while exp.More():
|
|
||||||
point = OCC.BRep.BRep_Tool.Pnt(exp.CurrentVertex())
|
|
||||||
points.append((point.X(), -point.Y()))
|
|
||||||
exp.Next()
|
|
||||||
self.cut_polygons.append({
|
|
||||||
'global_id': product.GlobalId,
|
|
||||||
'points': points
|
|
||||||
})
|
|
||||||
|
|
||||||
if not os.path.isfile(self.cut_pickle_file):
|
if not os.path.isfile(self.cut_pickle_file):
|
||||||
with open(self.cut_pickle_file, 'wb') as pickle_file:
|
with open(self.cut_pickle_file, 'wb') as pickle_file:
|
||||||
pickle.dump(self.cut_polygons, pickle_file, protocol=pickle.HIGHEST_PROTOCOL)
|
pickle.dump(self.cut_polygons, pickle_file, protocol=pickle.HIGHEST_PROTOCOL)
|
||||||
|
|
||||||
def connect_edges_into_wires(self, unconnected_edges):
|
|
||||||
edges = OCC.TopTools.TopTools_HSequenceOfShape()
|
|
||||||
edges_handle = OCC.TopTools.Handle_TopTools_HSequenceOfShape(edges)
|
|
||||||
wires = OCC.TopTools.TopTools_HSequenceOfShape()
|
|
||||||
wires_handle = OCC.TopTools.Handle_TopTools_HSequenceOfShape(wires)
|
|
||||||
|
|
||||||
for edge in unconnected_edges:
|
|
||||||
edges.Append(edge)
|
|
||||||
|
|
||||||
OCC.ShapeAnalysis.ShapeAnalysis_FreeBounds.ConnectEdgesToWires(edges_handle, 1e-5, True, wires_handle)
|
|
||||||
return wires_handle.GetObject()
|
|
||||||
|
|
||||||
class IfcCutterDebug(IfcCutter):
|
class IfcCutterDebug(IfcCutter):
|
||||||
def cut(self):
|
def cut(self):
|
||||||
@@ -672,37 +725,132 @@ class SvgWriter():
|
|||||||
def draw_annotations(self):
|
def draw_annotations(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 edge in self.ifc_cutter.annotation_obj.data.edges:
|
if self.ifc_cutter.dimension_obj:
|
||||||
classes = ['annotation', 'dimension']
|
for edge in self.ifc_cutter.dimension_obj.data.edges:
|
||||||
v0 = self.ifc_cutter.annotation_obj.data.vertices[edge.vertices[0]].co
|
classes = ['annotation', 'dimension']
|
||||||
v1 = self.ifc_cutter.annotation_obj.data.vertices[edge.vertices[1]].co
|
v0 = self.ifc_cutter.dimension_obj.data.vertices[edge.vertices[0]].co
|
||||||
start = Vector(((x_offset + v0.x), (y_offset - v0.y)))
|
v1 = self.ifc_cutter.dimension_obj.data.vertices[edge.vertices[1]].co
|
||||||
end = Vector(((x_offset + v1.x), (y_offset - v1.y)))
|
start = Vector(((x_offset + v0.x), (y_offset - v0.y)))
|
||||||
mid = ((end - start) / 2) + start
|
end = Vector(((x_offset + v1.x), (y_offset - v1.y)))
|
||||||
# TODO: hardcoded meters to mm conversion, until I properly do units
|
mid = ((end - start) / 2) + start
|
||||||
vector = end - start
|
# TODO: hardcoded meters to mm conversion, until I properly do units
|
||||||
if (end.y - start.y) * self.scale > 1:
|
vector = end - start
|
||||||
perpendicular = Vector((-vector.y, vector.x)).normalized()
|
if (end.y - start.y) * self.scale > 1:
|
||||||
|
perpendicular = Vector((-vector.y, vector.x)).normalized()
|
||||||
|
else:
|
||||||
|
perpendicular = Vector((vector.y, -vector.x)).normalized()
|
||||||
|
text_position = (mid * self.scale) + perpendicular
|
||||||
|
dimension = vector.length * 1000
|
||||||
|
rotation = -degrees(vector.angle(Vector((1, 0))))
|
||||||
|
line = self.svg.add(self.svg.line(start=tuple(start * self.scale),
|
||||||
|
end=tuple(end * self.scale), class_=' '.join(classes)))
|
||||||
|
line['marker-start'] = 'url(#dimension-marker)'
|
||||||
|
line['marker-end'] = 'url(#dimension-marker)'
|
||||||
|
# Standard font sizes 1.8, 2.5, 3.5, 5, 7
|
||||||
|
# Equivalent for OpenGost Type B: 2.97, 4.13, 5.78, 8.25, 11.55
|
||||||
|
self.svg.add(self.svg.text(str(round(dimension)), insert=tuple(text_position), **{
|
||||||
|
'transform': 'rotate({} {} {})'.format(
|
||||||
|
rotation,
|
||||||
|
text_position.x,
|
||||||
|
text_position.y
|
||||||
|
),
|
||||||
|
'font-size': '4.13', # 2.5
|
||||||
|
'font-family': 'OpenGost Type B TT',
|
||||||
|
'text-anchor': 'middle'
|
||||||
|
}))
|
||||||
|
|
||||||
|
for grid_obj in self.ifc_cutter.grid_objs:
|
||||||
|
for edge in grid_obj.data.edges:
|
||||||
|
classes = ['annotation', 'grid']
|
||||||
|
v0 = grid_obj.data.vertices[edge.vertices[0]].co
|
||||||
|
v1 = grid_obj.data.vertices[edge.vertices[1]].co
|
||||||
|
start = Vector(((x_offset + v0.x), (y_offset - v0.y)))
|
||||||
|
end = Vector(((x_offset + v1.x), (y_offset - v1.y)))
|
||||||
|
vector = end - start
|
||||||
|
line = self.svg.add(self.svg.line(start=tuple(start * self.scale),
|
||||||
|
end=tuple(end * self.scale), class_=' '.join(classes)))
|
||||||
|
line['marker-start'] = 'url(#grid-marker)'
|
||||||
|
line['marker-end'] = 'url(#grid-marker)'
|
||||||
|
line['stroke-dasharray'] = '12.5, 3, 3, 3'
|
||||||
|
self.svg.add(self.svg.text(grid_obj.name.split('/')[1], insert=tuple(start * self.scale), **{
|
||||||
|
'font-size': '8.25', # 5
|
||||||
|
'font-family': 'OpenGost Type B TT',
|
||||||
|
'text-anchor': 'middle',
|
||||||
|
'alignment-baseline': 'middle',
|
||||||
|
'dominant-baseline': 'middle'
|
||||||
|
}))
|
||||||
|
self.svg.add(self.svg.text(grid_obj.name.split('/')[1], insert=tuple(end * self.scale), **{
|
||||||
|
'font-size': '8.25', # 5
|
||||||
|
'font-family': 'OpenGost Type B TT',
|
||||||
|
'text-anchor': 'middle',
|
||||||
|
'alignment-baseline': 'middle',
|
||||||
|
'dominant-baseline': 'middle'
|
||||||
|
}))
|
||||||
|
|
||||||
|
if self.ifc_cutter.hidden_obj:
|
||||||
|
for edge in self.ifc_cutter.hidden_obj.data.edges:
|
||||||
|
classes = ['annotation', 'hidden']
|
||||||
|
v0 = self.ifc_cutter.hidden_obj.data.vertices[edge.vertices[0]].co
|
||||||
|
v1 = self.ifc_cutter.hidden_obj.data.vertices[edge.vertices[1]].co
|
||||||
|
start = Vector(((x_offset + v0.x), (y_offset - v0.y)))
|
||||||
|
end = Vector(((x_offset + v1.x), (y_offset - v1.y)))
|
||||||
|
vector = end - start
|
||||||
|
line = self.svg.add(self.svg.line(start=tuple(start * self.scale),
|
||||||
|
end=tuple(end * self.scale), class_=' '.join(classes)))
|
||||||
|
line['stroke-dasharray'] = '3, 2'
|
||||||
|
|
||||||
|
if self.ifc_cutter.leader_obj:
|
||||||
|
for spline in self.ifc_cutter.leader_obj.data.splines:
|
||||||
|
classes = ['annotation', 'leader']
|
||||||
|
d = ' '.join(['L {} {}'.format((x_offset + p.co.x) * self.scale, (y_offset - p.co.y) * self.scale) for p in spline.points])
|
||||||
|
d = 'M{}'.format(d[1:])
|
||||||
|
path = self.svg.add(self.svg.path(d=d, class_=' '.join(classes)))
|
||||||
|
path['marker-end'] = 'url(#leader-marker)'
|
||||||
|
|
||||||
|
if self.ifc_cutter.stair_obj:
|
||||||
|
for spline in self.ifc_cutter.stair_obj.data.splines:
|
||||||
|
classes = ['annotation', 'stair']
|
||||||
|
d = ' '.join(['L {} {}'.format((x_offset + p.co.x) * self.scale, (y_offset - p.co.y) * self.scale) for p in spline.points])
|
||||||
|
d = 'M{}'.format(d[1:])
|
||||||
|
start = Vector(((x_offset + spline.points[0].co.x), (y_offset - spline.points[0].co.y)))
|
||||||
|
next_point = Vector(((x_offset + spline.points[1].co.x), (y_offset - spline.points[1].co.y)))
|
||||||
|
text_position = (start * self.scale) - ((next_point - start).normalized() * 5)
|
||||||
|
path = self.svg.add(self.svg.path(d=d, class_=' '.join(classes)))
|
||||||
|
path['marker-start'] = 'url(#stair-marker-start)'
|
||||||
|
path['marker-end'] = 'url(#stair-marker-end)'
|
||||||
|
self.svg.add(self.svg.text('UP', insert=tuple(text_position), **{
|
||||||
|
'font-size': '4.13', # 2.5
|
||||||
|
'font-family': 'OpenGost Type B TT',
|
||||||
|
'text-anchor': 'middle',
|
||||||
|
'alignment-baseline': 'middle',
|
||||||
|
'dominant-baseline': 'middle'
|
||||||
|
}))
|
||||||
|
|
||||||
|
for text_obj in self.ifc_cutter.text_objs:
|
||||||
|
loc, rot, scale = self.ifc_cutter.camera_obj.matrix_world.decompose()
|
||||||
|
pos = (text_obj.location - self.ifc_cutter.camera_obj.location) @ rot.to_matrix()
|
||||||
|
text_position = Vector(((x_offset + pos.x), (y_offset - pos.y)))
|
||||||
|
|
||||||
|
if text_obj.data.align_x == 'CENTER':
|
||||||
|
text_anchor = 'middle'
|
||||||
|
elif text_obj.data.align_x == 'RIGHT':
|
||||||
|
text_anchor = 'end'
|
||||||
else:
|
else:
|
||||||
perpendicular = Vector((vector.y, -vector.x)).normalized()
|
text_anchor = 'start'
|
||||||
text_position = (mid * self.scale) + perpendicular
|
|
||||||
dimension = vector.length * 1000
|
if text_obj.data.align_y == 'CENTER':
|
||||||
rotation = -degrees(vector.angle(Vector((1, 0))))
|
alignment_baseline = 'middle'
|
||||||
line = self.svg.add(self.svg.line(start=tuple(start * self.scale),
|
elif text_obj.data.align_y == 'TOP':
|
||||||
end=tuple(end * self.scale), class_=' '.join(classes)))
|
alignment_baseline = 'hanging'
|
||||||
line['marker-start'] = 'url(#dimension-marker)'
|
else:
|
||||||
line['marker-end'] = 'url(#dimension-marker)'
|
alignment_baseline = 'baseline'
|
||||||
# Standard font sizes 1.8, 2.5, 3.5, 5, 7
|
|
||||||
# Equivalent for OpenGost Type B: 2.97, 4.13, 5.78, 8.25, 11.55
|
self.svg.add(self.svg.text(text_obj.data.body, insert=tuple(text_position * self.scale), **{
|
||||||
self.svg.add(self.svg.text(str(round(dimension)), insert=tuple(text_position), **{
|
|
||||||
'transform': 'rotate({} {} {})'.format(
|
|
||||||
rotation,
|
|
||||||
text_position.x,
|
|
||||||
text_position.y
|
|
||||||
),
|
|
||||||
'font-size': '4.13', # 2.5
|
'font-size': '4.13', # 2.5
|
||||||
'font-family': 'OpenGost Type B TT',
|
'font-family': 'OpenGost Type B TT',
|
||||||
'text-anchor': 'middle'
|
'text-anchor': text_anchor,
|
||||||
|
'alignment-baseline': alignment_baseline,
|
||||||
|
'dominant-baseline': alignment_baseline
|
||||||
}))
|
}))
|
||||||
|
|
||||||
def draw_cut_polygons(self):
|
def draw_cut_polygons(self):
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
.cut { fill: black; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; }
|
.cut { fill: black; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; }
|
||||||
.background { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
|
.background { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
|
||||||
.annotation { stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
|
.annotation { stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
|
||||||
|
.annotation.leader { fill: none; }
|
||||||
|
.annotation.stair { fill: none; }
|
||||||
|
|||||||
@@ -1,8 +1,20 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<svg baseProfile="full" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<svg baseProfile="full" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<marker id="dimension-marker" markerHeight="20" markerWidth="20" refX="10" refY="15" orient="auto">
|
<marker id="dimension-marker" markerHeight="30" markerWidth="20" refX="10" refY="15" orient="auto">
|
||||||
<path d="M 0 15 L 20 15" class="annotation" style="stroke-width:1;" />
|
<path d="M 0 15 L 20 15" class="annotation" style="stroke-width:1;" />
|
||||||
<path d="M 10 0 L 10 30" class="annotation" style="stroke-width:1;" />
|
<path d="M 10 0 L 10 30" class="annotation" style="stroke-width:1;" />
|
||||||
<path d="M 5 20 L 15 10" class="annotation" style="stroke-width:3;" />
|
<path d="M 5 20 L 15 10" class="annotation" style="stroke-width:3;" />
|
||||||
</marker>
|
</marker>
|
||||||
|
<marker id="grid-marker" markerHeight="35" markerWidth="35" orient="auto" refX="17.5" refY="17.5">
|
||||||
|
<circle cx="17.5" cy="17.5" fill="white" stroke="black" r="15" style="stroke-width: 2"/>
|
||||||
|
</marker>
|
||||||
|
<marker id="leader-marker" markerHeight="7" markerWidth="10" orient="auto" refX="10" refY="3.5">
|
||||||
|
<path d="M 0 0 L 0 7 L 10 3.5" class="annotation" style="fill:black;" />
|
||||||
|
</marker>
|
||||||
|
<marker id="stair-marker-start" markerHeight="10" markerWidth="10" orient="auto" refX="5" refY="5">
|
||||||
|
<circle cx="5" cy="5" fill="black" stroke="none" r="5"/>
|
||||||
|
</marker>
|
||||||
|
<marker id="stair-marker-end" markerHeight="42" markerWidth="21" orient="auto" refX="21" refY="21">
|
||||||
|
<path d="M 0 0 L 21 21 L 0 42" class="annotation stair" style="stroke-width:2" />
|
||||||
|
</marker>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 572 B After Width: | Height: | Size: 1.4 KiB |
@@ -788,9 +788,27 @@ class CutSection(bpy.types.Operator):
|
|||||||
ifc_cutter.data_dir = bpy.context.scene.BIMProperties.data_dir
|
ifc_cutter.data_dir = bpy.context.scene.BIMProperties.data_dir
|
||||||
ifc_cutter.diagram_name = self.diagram_name
|
ifc_cutter.diagram_name = self.diagram_name
|
||||||
ifc_cutter.background_image = bpy.context.scene.render.filepath
|
ifc_cutter.background_image = bpy.context.scene.render.filepath
|
||||||
ifc_cutter.annotation_obj = bpy.data.objects['{}-Annotation'.format(
|
ifc_cutter.leader_obj = None
|
||||||
self.diagram_name
|
ifc_cutter.stair_obj = None
|
||||||
)]
|
ifc_cutter.dimension_obj = None
|
||||||
|
ifc_cutter.hidden_obj = None
|
||||||
|
ifc_cutter.grid_objs = []
|
||||||
|
ifc_cutter.text_objs = []
|
||||||
|
for obj in bpy.context.selected_objects:
|
||||||
|
if 'Leader' in obj.name:
|
||||||
|
ifc_cutter.leader_obj = obj
|
||||||
|
elif 'Stair' in obj.name:
|
||||||
|
ifc_cutter.stair_obj = obj
|
||||||
|
elif 'Dimension' in obj.name:
|
||||||
|
ifc_cutter.dimension_obj = obj
|
||||||
|
elif 'Hidden' in obj.name:
|
||||||
|
ifc_cutter.hidden_obj = obj
|
||||||
|
elif 'IfcGrid' in obj.name:
|
||||||
|
ifc_cutter.grid_objs.append(obj)
|
||||||
|
elif obj.type == 'CAMERA':
|
||||||
|
ifc_cutter.camera_obj = obj
|
||||||
|
elif obj.type == 'FONT':
|
||||||
|
ifc_cutter.text_objs.append(obj)
|
||||||
ifc_cutter.section_box = {
|
ifc_cutter.section_box = {
|
||||||
'projection': tuple(projection),
|
'projection': tuple(projection),
|
||||||
'x_axis': tuple(x_axis),
|
'x_axis': tuple(x_axis),
|
||||||
|
|||||||
Reference in New Issue
Block a user