diff --git a/src/blenderbim/blenderbim/bim/__init__.py b/src/blenderbim/blenderbim/bim/__init__.py index f5690d3c7e..d19273e9f4 100644 --- a/src/blenderbim/blenderbim/bim/__init__.py +++ b/src/blenderbim/blenderbim/bim/__init__.py @@ -6,7 +6,7 @@ bpy = sys.modules.get("bpy") if bpy is not None: import bpy import importlib - from . import handler, ui, prop, operator, gizmos + from . import handler, ui, prop, operator modules = { "project": None, @@ -63,50 +63,18 @@ if bpy is not None: operator.SelectExternalMaterialDir, operator.FetchExternalMaterial, operator.FetchObjectPassport, - operator.CutSection, - operator.AddSheet, - operator.OpenSheet, - operator.AddDrawingToSheet, - operator.CreateSheets, - operator.OpenView, - operator.OpenViewCamera, - operator.ActivateView, operator.OpenUpstream, operator.AddSectionPlane, operator.RemoveSectionPlane, operator.ReloadIfcFile, operator.AddIfcFile, operator.RemoveIfcFile, - operator.SelectDocIfcFile, - operator.GenerateReferences, - operator.ResizeText, - operator.AddVariable, - operator.RemoveVariable, - operator.PropagateTextData, operator.SetOverrideColour, - operator.RemoveDrawing, - operator.AddDrawingStyle, - operator.RemoveDrawingStyle, - operator.SaveDrawingStyle, - operator.ActivateDrawingStyle, - operator.EditVectorStyle, - operator.RemoveSheet, - operator.AddSchedule, - operator.RemoveSchedule, - operator.SelectScheduleFile, - operator.BuildSchedule, - operator.AddScheduleToSheet, operator.SetViewportShadowFromSun, - operator.AddDrawingStyleAttribute, - operator.RemoveDrawingStyleAttribute, operator.CopyPropertyToSelection, operator.CopyAttributeToSelection, - operator.RefreshDrawingList, - operator.CleanWireframes, operator.LinkIfc, operator.SnapSpacesTogether, - operator.CopyGrid, - operator.AddSectionsAnnotations, prop.StrProperty, prop.Attribute, prop.BIMProperties, @@ -118,21 +86,10 @@ if bpy is not None: prop.ItemSlotMap, prop.BIMMeshProperties, ui.BIM_PT_section_plane, - ui.BIM_PT_drawings, - ui.BIM_PT_schedules, - ui.BIM_PT_sheets, - ui.BIM_PT_text, - ui.BIM_PT_annotation_utilities, ui.BIM_PT_misc_utilities, ui.BIM_UL_generic, - ui.BIM_UL_drawinglist, ui.BIM_UL_topics, ui.BIM_ADDON_preferences, - gizmos.UglyDotGizmo, - gizmos.DotGizmo, - gizmos.DimensionLabelGizmo, - gizmos.ExtrusionGuidesGizmo, - gizmos.ExtrusionWidget ] for module in modules.values(): @@ -160,7 +117,9 @@ if bpy is not None: bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties) bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties) bpy.types.Material.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties) - bpy.types.Collection.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties) # Check if we need this + bpy.types.Collection.BIMObjectProperties = bpy.props.PointerProperty( + type=prop.BIMObjectProperties + ) # Check if we need this bpy.types.Material.BIMMaterialProperties = bpy.props.PointerProperty(type=prop.BIMMaterialProperties) bpy.types.Mesh.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties) bpy.types.Curve.BIMMeshProperties = bpy.props.PointerProperty(type=prop.BIMMeshProperties) @@ -170,8 +129,6 @@ if bpy is not None: for module in modules.values(): module.register() - bpy.app.handlers.depsgraph_update_pre.append(operator.depsgraph_update_pre_handler) - def unregister(): for cls in reversed(classes): bpy.utils.unregister_class(cls) @@ -183,7 +140,7 @@ if bpy is not None: del bpy.types.Scene.BIMProperties del bpy.types.Object.BIMObjectProperties del bpy.types.Material.BIMObjectProperties - del bpy.types.Collection.BIMObjectProperties # Check if we need this + del bpy.types.Collection.BIMObjectProperties # Check if we need this del bpy.types.Material.BIMMaterialProperties del bpy.types.Mesh.BIMMeshProperties del bpy.types.Curve.BIMMeshProperties @@ -192,5 +149,3 @@ if bpy is not None: for module in reversed(list(modules.values())): module.unregister() - - bpy.app.handlers.depsgraph_update_pre.remove(operator.depsgraph_update_pre_handler) diff --git a/src/blenderbim/blenderbim/bim/module/drawing/__init__.py b/src/blenderbim/blenderbim/bim/module/drawing/__init__.py index c2f248d55c..a871fad0c5 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/__init__.py @@ -1,10 +1,42 @@ import bpy -from . import ui, prop, operator, handler +from . import ui, prop, operator, handler, gizmos classes = ( operator.AddDrawing, operator.CreateDrawing, operator.AddAnnotation, + operator.CutSection, # TODO: To deprecate + operator.AddSheet, + operator.OpenSheet, + operator.AddDrawingToSheet, + operator.CreateSheets, + operator.OpenView, + operator.OpenViewCamera, + operator.ActivateView, + operator.SelectDocIfcFile, + operator.GenerateReferences, + operator.ResizeText, + operator.AddVariable, + operator.RemoveVariable, + operator.PropagateTextData, + operator.RemoveDrawing, + operator.AddDrawingStyle, + operator.RemoveDrawingStyle, + operator.SaveDrawingStyle, + operator.ActivateDrawingStyle, + operator.EditVectorStyle, + operator.RemoveSheet, + operator.AddSchedule, + operator.RemoveSchedule, + operator.SelectScheduleFile, + operator.BuildSchedule, + operator.AddScheduleToSheet, + operator.AddDrawingStyleAttribute, + operator.RemoveDrawingStyleAttribute, + operator.RefreshDrawingList, + operator.CleanWireframes, + operator.CopyGrid, + operator.AddSectionsAnnotations, prop.Variable, prop.Drawing, prop.Schedule, @@ -14,6 +46,17 @@ classes = ( prop.BIMCameraProperties, prop.BIMTextProperties, ui.BIM_PT_camera, + ui.BIM_PT_drawings, + ui.BIM_PT_schedules, + ui.BIM_PT_sheets, + ui.BIM_PT_text, + ui.BIM_PT_annotation_utilities, + ui.BIM_UL_drawinglist, + gizmos.UglyDotGizmo, + gizmos.DotGizmo, + gizmos.DimensionLabelGizmo, + gizmos.ExtrusionGuidesGizmo, + gizmos.ExtrusionWidget, ) @@ -22,6 +65,7 @@ def register(): bpy.types.Camera.BIMCameraProperties = bpy.props.PointerProperty(type=prop.BIMCameraProperties) bpy.types.TextCurve.BIMTextProperties = bpy.props.PointerProperty(type=prop.BIMTextProperties) bpy.app.handlers.load_post.append(handler.toggleDecorationsOnLoad) + bpy.app.handlers.depsgraph_update_pre.append(handler.depsgraph_update_pre_handler) def unregister(): @@ -29,3 +73,4 @@ def unregister(): del bpy.types.Camera.BIMCameraProperties del bpy.types.TextCurve.BIMTextProperties bpy.app.handlers.load_post.remove(handler.toggleDecorationsOnLoad) + bpy.app.handlers.depsgraph_update_pre.remove(handler.depsgraph_update_pre_handler) diff --git a/src/blenderbim/blenderbim/bim/cut_ifc.py b/src/blenderbim/blenderbim/bim/module/drawing/cut_ifc.py similarity index 100% rename from src/blenderbim/blenderbim/bim/cut_ifc.py rename to src/blenderbim/blenderbim/bim/module/drawing/cut_ifc.py diff --git a/src/blenderbim/blenderbim/bim/gizmos.py b/src/blenderbim/blenderbim/bim/module/drawing/gizmos.py similarity index 51% rename from src/blenderbim/blenderbim/bim/gizmos.py rename to src/blenderbim/blenderbim/bim/module/drawing/gizmos.py index e9abdb843d..65fbe7e339 100644 --- a/src/blenderbim/blenderbim/bim/gizmos.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/gizmos.py @@ -1,14 +1,12 @@ import bpy import blf -from bpy import types import math +import gpu, bgl +from bpy import types from mathutils import Vector, Matrix from mathutils import geometry -import gpu, bgl from bpy_extras import view3d_utils - - -from .shaders import DotsGizmoShader, ExtrusionGuidesShader, BaseLinesShader +from blenderbim.bim.module.drawing.shaders import DotsGizmoShader, ExtrusionGuidesShader, BaseLinesShader """Gizmos under the hood @@ -43,76 +41,196 @@ draw_select -- fake-draw of selection geometry for gpu-side cursor tracking # some geometries for Gizmo.custom_shape shaders CUBE = ( - (+1, +1, +1), (-1, +1, +1), (+1, -1, +1), # top - (+1, -1, +1), (-1, +1, +1), (-1, -1, +1), - (+1, +1, +1), (+1, -1, +1), (+1, +1, -1), # right - (+1, +1, -1), (+1, -1, +1), (+1, -1, -1), - (+1, +1, +1), (+1, +1, -1), (-1, +1, +1), # back - (-1, +1, +1), (+1, +1, -1), (-1, +1, -1), - (-1, -1, -1), (-1, +1, -1), (+1, -1, -1), # bot - (+1, -1, -1), (-1, +1, -1), (+1, +1, -1), - (-1, -1, -1), (-1, -1, +1), (-1, +1, -1), # left - (-1, +1, -1), (-1, -1, +1), (-1, +1, +1), - (-1, -1, -1), (+1, -1, -1), (-1, -1, +1), # front - (-1, -1, +1), (+1, -1, -1), (+1, -1, +1) + (+1, +1, +1), + (-1, +1, +1), + (+1, -1, +1), # top + (+1, -1, +1), + (-1, +1, +1), + (-1, -1, +1), + (+1, +1, +1), + (+1, -1, +1), + (+1, +1, -1), # right + (+1, +1, -1), + (+1, -1, +1), + (+1, -1, -1), + (+1, +1, +1), + (+1, +1, -1), + (-1, +1, +1), # back + (-1, +1, +1), + (+1, +1, -1), + (-1, +1, -1), + (-1, -1, -1), + (-1, +1, -1), + (+1, -1, -1), # bot + (+1, -1, -1), + (-1, +1, -1), + (+1, +1, -1), + (-1, -1, -1), + (-1, -1, +1), + (-1, +1, -1), # left + (-1, +1, -1), + (-1, -1, +1), + (-1, +1, +1), + (-1, -1, -1), + (+1, -1, -1), + (-1, -1, +1), # front + (-1, -1, +1), + (+1, -1, -1), + (+1, -1, +1), ) DISC = ( - (0.0, 0.0, 0.0), (1.0, 0.0, 0), (0.8660254037844387, 0.49999999999999994, 0), - (0.0, 0.0, 0.0), (0.8660254037844387, 0.49999999999999994, 0), (0.5000000000000001, 0.8660254037844386, 0), - (0.0, 0.0, 0.0), (0.5000000000000001, 0.8660254037844386, 0), (6.123233995736766e-17, 1.0, 0), - (0.0, 0.0, 0.0), (6.123233995736766e-17, 1.0, 0), (-0.4999999999999998, 0.8660254037844387, 0), - (0.0, 0.0, 0.0), (-0.4999999999999998, 0.8660254037844387, 0), (-0.8660254037844385, 0.5000000000000003, 0), - (0.0, 0.0, 0.0), (-0.8660254037844385, 0.5000000000000003, 0), (-1.0, 1.2246467991473532e-16, 0), - (0.0, 0.0, 0.0), (-1.0, 1.2246467991473532e-16, 0), (-0.8660254037844388, -0.4999999999999997, 0), - (0.0, 0.0, 0.0), (-0.8660254037844388, -0.4999999999999997, 0), (-0.5000000000000004, -0.8660254037844384, 0), - (0.0, 0.0, 0.0), (-0.5000000000000004, -0.8660254037844384, 0), (-1.8369701987210297e-16, -1.0, 0), - (0.0, 0.0, 0.0), (-1.8369701987210297e-16, -1.0, 0), (0.49999999999999933, -0.866025403784439, 0), - (0.0, 0.0, 0.0), (0.49999999999999933, -0.866025403784439, 0), (0.8660254037844384, -0.5000000000000004, 0), - (0.0, 0.0, 0.0), (0.8660254037844384, -0.5000000000000004, 0), (1.0, 0.0, 0), + (0.0, 0.0, 0.0), + (1.0, 0.0, 0), + (0.8660254037844387, 0.49999999999999994, 0), + (0.0, 0.0, 0.0), + (0.8660254037844387, 0.49999999999999994, 0), + (0.5000000000000001, 0.8660254037844386, 0), + (0.0, 0.0, 0.0), + (0.5000000000000001, 0.8660254037844386, 0), + (6.123233995736766e-17, 1.0, 0), + (0.0, 0.0, 0.0), + (6.123233995736766e-17, 1.0, 0), + (-0.4999999999999998, 0.8660254037844387, 0), + (0.0, 0.0, 0.0), + (-0.4999999999999998, 0.8660254037844387, 0), + (-0.8660254037844385, 0.5000000000000003, 0), + (0.0, 0.0, 0.0), + (-0.8660254037844385, 0.5000000000000003, 0), + (-1.0, 1.2246467991473532e-16, 0), + (0.0, 0.0, 0.0), + (-1.0, 1.2246467991473532e-16, 0), + (-0.8660254037844388, -0.4999999999999997, 0), + (0.0, 0.0, 0.0), + (-0.8660254037844388, -0.4999999999999997, 0), + (-0.5000000000000004, -0.8660254037844384, 0), + (0.0, 0.0, 0.0), + (-0.5000000000000004, -0.8660254037844384, 0), + (-1.8369701987210297e-16, -1.0, 0), + (0.0, 0.0, 0.0), + (-1.8369701987210297e-16, -1.0, 0), + (0.49999999999999933, -0.866025403784439, 0), + (0.0, 0.0, 0.0), + (0.49999999999999933, -0.866025403784439, 0), + (0.8660254037844384, -0.5000000000000004, 0), + (0.0, 0.0, 0.0), + (0.8660254037844384, -0.5000000000000004, 0), + (1.0, 0.0, 0), ) X3DISC = ( - (0.0, 0.0, 0.0), (1.0, 0.0, 0), (0.8660254037844387, 0.49999999999999994, 0), - (0.0, 0.0, 0.0), (0.8660254037844387, 0.49999999999999994, 0), (0.5000000000000001, 0.8660254037844386, 0), - (0.0, 0.0, 0.0), (0.5000000000000001, 0.8660254037844386, 0), (6.123233995736766e-17, 1.0, 0), - (0.0, 0.0, 0.0), (6.123233995736766e-17, 1.0, 0), (-0.4999999999999998, 0.8660254037844387, 0), - (0.0, 0.0, 0.0), (-0.4999999999999998, 0.8660254037844387, 0), (-0.8660254037844385, 0.5000000000000003, 0), - (0.0, 0.0, 0.0), (-0.8660254037844385, 0.5000000000000003, 0), (-1.0, 1.2246467991473532e-16, 0), - (0.0, 0.0, 0.0), (-1.0, 1.2246467991473532e-16, 0), (-0.8660254037844388, -0.4999999999999997, 0), - (0.0, 0.0, 0.0), (-0.8660254037844388, -0.4999999999999997, 0), (-0.5000000000000004, -0.8660254037844384, 0), - (0.0, 0.0, 0.0), (-0.5000000000000004, -0.8660254037844384, 0), (-1.8369701987210297e-16, -1.0, 0), - (0.0, 0.0, 0.0), (-1.8369701987210297e-16, -1.0, 0), (0.49999999999999933, -0.866025403784439, 0), - (0.0, 0.0, 0.0), (0.49999999999999933, -0.866025403784439, 0), (0.8660254037844384, -0.5000000000000004, 0), - (0.0, 0.0, 0.0), (0.8660254037844384, -0.5000000000000004, 0), (1.0, 0.0, 0), - (0.0, 0.0, 0.0), (0, 1.0, 0.0), (0, 0.8660254037844387, 0.49999999999999994), - (0.0, 0.0, 0.0), (0, 0.8660254037844387, 0.49999999999999994), (0, 0.5000000000000001, 0.8660254037844386), - (0.0, 0.0, 0.0), (0, 0.5000000000000001, 0.8660254037844386), (0, 6.123233995736766e-17, 1.0), - (0.0, 0.0, 0.0), (0, 6.123233995736766e-17, 1.0), (0, -0.4999999999999998, 0.8660254037844387), - (0.0, 0.0, 0.0), (0, -0.4999999999999998, 0.8660254037844387), (0, -0.8660254037844385, 0.5000000000000003), - (0.0, 0.0, 0.0), (0, -0.8660254037844385, 0.5000000000000003), (0, -1.0, 1.2246467991473532e-16), - (0.0, 0.0, 0.0), (0, -1.0, 1.2246467991473532e-16), (0, -0.8660254037844388, -0.4999999999999997), - (0.0, 0.0, 0.0), (0, -0.8660254037844388, -0.4999999999999997), (0, -0.5000000000000004, -0.8660254037844384), - (0.0, 0.0, 0.0), (0, -0.5000000000000004, -0.8660254037844384), (0, -1.8369701987210297e-16, -1.0), - (0.0, 0.0, 0.0), (0, -1.8369701987210297e-16, -1.0), (0, 0.49999999999999933, -0.866025403784439), - (0.0, 0.0, 0.0), (0, 0.49999999999999933, -0.866025403784439), (0, 0.8660254037844384, -0.5000000000000004), - (0.0, 0.0, 0.0), (0, 0.8660254037844384, -0.5000000000000004), (0, 1.0, 0.0), - (0.0, 0.0, 0.0), (0.0, 0, 1.0), (0.49999999999999994, 0, 0.8660254037844387), - (0.0, 0.0, 0.0), (0.49999999999999994, 0, 0.8660254037844387), (0.8660254037844386, 0, 0.5000000000000001), - (0.0, 0.0, 0.0), (0.8660254037844386, 0, 0.5000000000000001), (1.0, 0, 6.123233995736766e-17), - (0.0, 0.0, 0.0), (1.0, 0, 6.123233995736766e-17), (0.8660254037844387, 0, -0.4999999999999998), - (0.0, 0.0, 0.0), (0.8660254037844387, 0, -0.4999999999999998), (0.5000000000000003, 0, -0.8660254037844385), - (0.0, 0.0, 0.0), (0.5000000000000003, 0, -0.8660254037844385), (1.2246467991473532e-16, 0, -1.0), - (0.0, 0.0, 0.0), (1.2246467991473532e-16, 0, -1.0), (-0.4999999999999997, 0, -0.8660254037844388), - (0.0, 0.0, 0.0), (-0.4999999999999997, 0, -0.8660254037844388), (-0.8660254037844384, 0, -0.5000000000000004), - (0.0, 0.0, 0.0), (-0.8660254037844384, 0, -0.5000000000000004), (-1.0, 0, -1.8369701987210297e-16), - (0.0, 0.0, 0.0), (-1.0, 0, -1.8369701987210297e-16), (-0.866025403784439, 0, 0.49999999999999933), - (0.0, 0.0, 0.0), (-0.866025403784439, 0, 0.49999999999999933), (-0.5000000000000004, 0, 0.8660254037844384), - (0.0, 0.0, 0.0), (-0.5000000000000004, 0, 0.8660254037844384), (0.0, 0, 1.0), + (0.0, 0.0, 0.0), + (1.0, 0.0, 0), + (0.8660254037844387, 0.49999999999999994, 0), + (0.0, 0.0, 0.0), + (0.8660254037844387, 0.49999999999999994, 0), + (0.5000000000000001, 0.8660254037844386, 0), + (0.0, 0.0, 0.0), + (0.5000000000000001, 0.8660254037844386, 0), + (6.123233995736766e-17, 1.0, 0), + (0.0, 0.0, 0.0), + (6.123233995736766e-17, 1.0, 0), + (-0.4999999999999998, 0.8660254037844387, 0), + (0.0, 0.0, 0.0), + (-0.4999999999999998, 0.8660254037844387, 0), + (-0.8660254037844385, 0.5000000000000003, 0), + (0.0, 0.0, 0.0), + (-0.8660254037844385, 0.5000000000000003, 0), + (-1.0, 1.2246467991473532e-16, 0), + (0.0, 0.0, 0.0), + (-1.0, 1.2246467991473532e-16, 0), + (-0.8660254037844388, -0.4999999999999997, 0), + (0.0, 0.0, 0.0), + (-0.8660254037844388, -0.4999999999999997, 0), + (-0.5000000000000004, -0.8660254037844384, 0), + (0.0, 0.0, 0.0), + (-0.5000000000000004, -0.8660254037844384, 0), + (-1.8369701987210297e-16, -1.0, 0), + (0.0, 0.0, 0.0), + (-1.8369701987210297e-16, -1.0, 0), + (0.49999999999999933, -0.866025403784439, 0), + (0.0, 0.0, 0.0), + (0.49999999999999933, -0.866025403784439, 0), + (0.8660254037844384, -0.5000000000000004, 0), + (0.0, 0.0, 0.0), + (0.8660254037844384, -0.5000000000000004, 0), + (1.0, 0.0, 0), + (0.0, 0.0, 0.0), + (0, 1.0, 0.0), + (0, 0.8660254037844387, 0.49999999999999994), + (0.0, 0.0, 0.0), + (0, 0.8660254037844387, 0.49999999999999994), + (0, 0.5000000000000001, 0.8660254037844386), + (0.0, 0.0, 0.0), + (0, 0.5000000000000001, 0.8660254037844386), + (0, 6.123233995736766e-17, 1.0), + (0.0, 0.0, 0.0), + (0, 6.123233995736766e-17, 1.0), + (0, -0.4999999999999998, 0.8660254037844387), + (0.0, 0.0, 0.0), + (0, -0.4999999999999998, 0.8660254037844387), + (0, -0.8660254037844385, 0.5000000000000003), + (0.0, 0.0, 0.0), + (0, -0.8660254037844385, 0.5000000000000003), + (0, -1.0, 1.2246467991473532e-16), + (0.0, 0.0, 0.0), + (0, -1.0, 1.2246467991473532e-16), + (0, -0.8660254037844388, -0.4999999999999997), + (0.0, 0.0, 0.0), + (0, -0.8660254037844388, -0.4999999999999997), + (0, -0.5000000000000004, -0.8660254037844384), + (0.0, 0.0, 0.0), + (0, -0.5000000000000004, -0.8660254037844384), + (0, -1.8369701987210297e-16, -1.0), + (0.0, 0.0, 0.0), + (0, -1.8369701987210297e-16, -1.0), + (0, 0.49999999999999933, -0.866025403784439), + (0.0, 0.0, 0.0), + (0, 0.49999999999999933, -0.866025403784439), + (0, 0.8660254037844384, -0.5000000000000004), + (0.0, 0.0, 0.0), + (0, 0.8660254037844384, -0.5000000000000004), + (0, 1.0, 0.0), + (0.0, 0.0, 0.0), + (0.0, 0, 1.0), + (0.49999999999999994, 0, 0.8660254037844387), + (0.0, 0.0, 0.0), + (0.49999999999999994, 0, 0.8660254037844387), + (0.8660254037844386, 0, 0.5000000000000001), + (0.0, 0.0, 0.0), + (0.8660254037844386, 0, 0.5000000000000001), + (1.0, 0, 6.123233995736766e-17), + (0.0, 0.0, 0.0), + (1.0, 0, 6.123233995736766e-17), + (0.8660254037844387, 0, -0.4999999999999998), + (0.0, 0.0, 0.0), + (0.8660254037844387, 0, -0.4999999999999998), + (0.5000000000000003, 0, -0.8660254037844385), + (0.0, 0.0, 0.0), + (0.5000000000000003, 0, -0.8660254037844385), + (1.2246467991473532e-16, 0, -1.0), + (0.0, 0.0, 0.0), + (1.2246467991473532e-16, 0, -1.0), + (-0.4999999999999997, 0, -0.8660254037844388), + (0.0, 0.0, 0.0), + (-0.4999999999999997, 0, -0.8660254037844388), + (-0.8660254037844384, 0, -0.5000000000000004), + (0.0, 0.0, 0.0), + (-0.8660254037844384, 0, -0.5000000000000004), + (-1.0, 0, -1.8369701987210297e-16), + (0.0, 0.0, 0.0), + (-1.0, 0, -1.8369701987210297e-16), + (-0.866025403784439, 0, 0.49999999999999933), + (0.0, 0.0, 0.0), + (-0.866025403784439, 0, 0.49999999999999933), + (-0.5000000000000004, 0, 0.8660254037844384), + (0.0, 0.0, 0.0), + (-0.5000000000000004, 0, 0.8660254037844384), + (0.0, 0, 1.0), ) -class CustomGizmo(): +class CustomGizmo: # FIXME: highliting/selection doesnt work def draw_very_custom_shape(self, ctx, custom_shape, select_id=None): # similar to draw_custom_shape @@ -127,7 +245,7 @@ class CustomGizmo(): color = (*self.color_highlight, self.alpha_highlight) else: color = (*self.color, self.alpha) - shader.uniform_float('color', color) + shader.uniform_float("color", color) shape.glenable() shape.uniform_region(ctx) @@ -139,31 +257,32 @@ class CustomGizmo(): bgl.glDisable(bgl.GL_BLEND) -class OffsetHandle(): +class OffsetHandle: """Handling mouse to offset gizmo from base along Z axis""" + # FIXME: works a bit weird for rotated objects def invoke(self, ctx, event): - self.init_value = self.target_get_value('offset') / self.scale_value + self.init_value = self.target_get_value("offset") / self.scale_value coordz = self.project_mouse(ctx, event) if coordz is None: - return {'CANCELLED'} + return {"CANCELLED"} self.init_coordz = coordz - return {'RUNNING_MODAL'} + return {"RUNNING_MODAL"} def modal(self, ctx, event, tweak): coordz = self.project_mouse(ctx, event) if coordz is None: - return {'CANCELLED'} + return {"CANCELLED"} delta = coordz - self.init_coordz - if 'PRECISE' in tweak: + if "PRECISE" in tweak: delta /= 10.0 value = max(0, self.init_value + delta) value *= self.scale_value # ctx.area.header_text_set(f"coords: {self.init_coordz} - {coordz}, delta: {delta}, value: {value}") ctx.area.header_text_set(f"Depth: {value}") - self.target_set_value('offset', value) - return {'RUNNING_MODAL'} + self.target_set_value("offset", value) + return {"RUNNING_MODAL"} def project_mouse(self, ctx, event): """Projecting mouse coords to local axis Z""" @@ -189,30 +308,29 @@ class OffsetHandle(): def exit(self, ctx, cancel): if cancel: - self.target_set_value('offset', self.init_value) + self.target_set_value("offset", self.init_value) else: self.group.update(ctx) class UglyDotGizmo(OffsetHandle, types.Gizmo): """three orthogonal circles""" + bl_idname = "BIM_GT_uglydot_3d" - bl_target_properties = ( - {'id': 'offset', 'type': 'FLOAT', 'array_length': 1}, - ) + bl_target_properties = ({"id": "offset", "type": "FLOAT", "array_length": 1},) __slots__ = ( - 'scale_value', - 'custom_shape', - 'init_value', - 'init_coordz', + "scale_value", + "custom_shape", + "init_value", + "init_coordz", ) def setup(self): - self.custom_shape = self.new_custom_shape(type='TRIS', verts=X3DISC) + self.custom_shape = self.new_custom_shape(type="TRIS", verts=X3DISC) def refresh(self): - offset = self.target_get_value('offset') / self.scale_value + offset = self.target_get_value("offset") / self.scale_value self.matrix_offset.col[3][2] = offset # z-shift def draw(self, ctx): @@ -226,15 +344,14 @@ class UglyDotGizmo(OffsetHandle, types.Gizmo): class DotGizmo(CustomGizmo, OffsetHandle, types.Gizmo): """Single dot viewport-aligned""" + # FIXME: make it selectable bl_idname = "BIM_GT_dot_2d" - bl_target_properties = ( - {'id': 'offset', 'type': 'FLOAT', 'array_length': 1}, - ) + bl_target_properties = ({"id": "offset", "type": "FLOAT", "array_length": 1},) __slots__ = ( - 'scale_value', - 'custom_shape', + "scale_value", + "custom_shape", ) def setup(self): @@ -243,7 +360,7 @@ class DotGizmo(CustomGizmo, OffsetHandle, types.Gizmo): self.use_draw_scale = False def refresh(self): - offset = self.target_get_value('offset') / self.scale_value + offset = self.target_get_value("offset") / self.scale_value self.matrix_offset.col[3][2] = offset # z-shifted def draw(self, ctx): @@ -265,15 +382,11 @@ class ExtrusionGuidesGizmo(CustomGizmo, types.Gizmo): Noninteractive gizmo to indicate extrusion depth and planes. Draws main segment and orthogonal cross at endpoints. """ - bl_idname = "BIM_GT_extrusion_guides" - bl_target_properties = ( - {'id': 'depth', 'type': 'FLOAT', 'array_length': 1}, - ) - __slots__ = ( - 'scale_value', - 'custom_shape' - ) + bl_idname = "BIM_GT_extrusion_guides" + bl_target_properties = ({"id": "depth", "type": "FLOAT", "array_length": 1},) + + __slots__ = ("scale_value", "custom_shape") def setup(self): shader = ExtrusionGuidesShader() @@ -281,7 +394,7 @@ class ExtrusionGuidesGizmo(CustomGizmo, types.Gizmo): self.use_draw_scale = False def refresh(self): - depth = self.target_get_value('depth') / self.scale_value + depth = self.target_get_value("depth") / self.scale_value self.matrix_offset.col[2][2] = depth # z-scaled def draw(self, ctx): @@ -291,25 +404,22 @@ class ExtrusionGuidesGizmo(CustomGizmo, types.Gizmo): class DimensionLabelGizmo(types.Gizmo): """Text label for a dimension""" + # does not work properly, fonts are totally screwed up bl_idname = "BIM_GT_dimension_label" - bl_target_properties = ( - {'id': 'value', 'type': 'FLOAT', 'array_length': 1}, - ) + bl_target_properties = ({"id": "value", "type": "FLOAT", "array_length": 1},) - __slots__ = ( - 'text_label' - ) + __slots__ = "text_label" def setup(self): pass def refresh(self, ctx): - value = self.target_get_value('value') - self.matrix_offset.col[3][2] = value * .5 + value = self.target_get_value("value") + self.matrix_offset.col[3][2] = value * 0.5 unit_system = ctx.scene.unit_settings.system - self.text_label = bpy.utils.units.to_string(unit_system, 'LENGTH', value, 3, split_unit=False) + self.text_label = bpy.utils.units.to_string(unit_system, "LENGTH", value, 3, split_unit=False) def draw(self, ctx): self.refresh(ctx) @@ -337,41 +447,44 @@ class DimensionLabelGizmo(types.Gizmo): class ExtrusionWidget(types.GizmoGroup): bl_idname = "bim.extrusion_widget" bl_label = "Extrusion Gizmos" - bl_space_type = 'VIEW_3D' - bl_region_type = 'WINDOW' - bl_options = {'3D', 'PERSISTENT', 'SHOW_MODAL_ALL'} + bl_space_type = "VIEW_3D" + bl_region_type = "WINDOW" + bl_options = {"3D", "PERSISTENT", "SHOW_MODAL_ALL"} # FIXME: use proper scale from ifc value to blender units @classmethod def poll(cls, ctx): obj = ctx.object - return (obj and obj.type == 'MESH' - and obj.data.BIMMeshProperties.ifc_parameters.get('IfcExtrudedAreaSolid/Depth') is not None) + return ( + obj + and obj.type == "MESH" + and obj.data.BIMMeshProperties.ifc_parameters.get("IfcExtrudedAreaSolid/Depth") is not None + ) def setup(self, ctx): target = ctx.object - prop = target.data.BIMMeshProperties.ifc_parameters.get('IfcExtrudedAreaSolid/Depth') + prop = target.data.BIMMeshProperties.ifc_parameters.get("IfcExtrudedAreaSolid/Depth") basis = target.matrix_world.normalized() theme = ctx.preferences.themes[0].user_interface - gz = self.handle = self.gizmos.new('BIM_GT_uglydot_3d') + gz = self.handle = self.gizmos.new("BIM_GT_uglydot_3d") gz.matrix_basis = basis gz.scale_basis = 0.1 gz.color = gz.color_highlight = tuple(theme.gizmo_primary) gz.alpha = 0.5 gz.alpha_highlight = 1.0 gz.use_draw_modal = True - gz.target_set_prop('offset', prop, 'value') + gz.target_set_prop("offset", prop, "value") gz.scale_value = 1000 - gz = self.guides = self.gizmos.new('BIM_GT_extrusion_guides') + gz = self.guides = self.gizmos.new("BIM_GT_extrusion_guides") gz.matrix_basis = basis gz.color = gz.color_highlight = tuple(theme.gizmo_secondary) gz.alpha = gz.alpha_highlight = 0.5 gz.use_draw_modal = True - gz.target_set_prop('depth', prop, 'value') + gz.target_set_prop("depth", prop, "value") gz.scale_value = 1000 # gz = self.label = self.gizmos.new('GIZMO_GT_dimension_label') @@ -395,6 +508,6 @@ class ExtrusionWidget(types.GizmoGroup): # need to retrieve and rebind them again bpy.ops.bim.get_representation_ifc_parameters() target = ctx.object - prop = target.data.BIMMeshProperties.ifc_parameters.get('IfcExtrudedAreaSolid/Depth') - self.handle.target_set_prop('offset', prop, 'value') - self.guides.target_set_prop('depth', prop, 'value') + prop = target.data.BIMMeshProperties.ifc_parameters.get("IfcExtrudedAreaSolid/Depth") + self.handle.target_set_prop("offset", prop, "value") + self.guides.target_set_prop("depth", prop, "value") diff --git a/src/blenderbim/blenderbim/bim/module/drawing/handler.py b/src/blenderbim/blenderbim/bim/module/drawing/handler.py index 961c41c92d..ef581c6d78 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/handler.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/handler.py @@ -10,3 +10,23 @@ def toggleDecorationsOnLoad(*args): decoration.DecorationsHandler.install(bpy.context) else: decoration.DecorationsHandler.uninstall() + + +@persistent +def depsgraph_update_pre_handler(scene): + set_active_camera_resolution(scene) + + +def set_active_camera_resolution(scene): + if not scene.camera or "/" not in scene.camera.name or not scene.DocProperties.drawings: + return + if ( + scene.render.resolution_x != scene.camera.data.BIMCameraProperties.raster_x + or scene.render.resolution_y != scene.camera.data.BIMCameraProperties.raster_y + ): + scene.render.resolution_x = scene.camera.data.BIMCameraProperties.raster_x + scene.render.resolution_y = scene.camera.data.BIMCameraProperties.raster_y + current_drawing = scene.DocProperties.drawings[scene.DocProperties.current_drawing_index] + if scene.camera != current_drawing.camera: + scene.DocProperties.current_drawing_index = scene.DocProperties.drawings.find(scene.camera.name.split("/")[1]) + bpy.ops.bim.activate_view(drawing_index=scene.DocProperties.current_drawing_index) diff --git a/src/blenderbim/blenderbim/bim/module/drawing/operator.py b/src/blenderbim/blenderbim/bim/module/drawing/operator.py index ca2cb727e8..58b0258ac6 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/operator.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/operator.py @@ -1,11 +1,16 @@ import os import bpy import json +import bmesh import subprocess import webbrowser +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 from mathutils import Vector, Matrix, Euler, geometry from blenderbim.bim.ifc import IfcStore from ifcopenshell.api.group.data import Data as GroupData @@ -245,3 +250,955 @@ class AddAnnotation(bpy.types.Operator): obj.select_set(True) bpy.ops.object.mode_set(mode="EDIT") 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" + + def execute(self, context): + new = bpy.context.scene.DocProperties.sheets.add() + new.name = "{} - SHEET".format(len(bpy.context.scene.DocProperties.sheets)) + sheet_builder = sheeter.SheetBuilder() + sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir + sheet_builder.create(new.name, bpy.context.scene.DocProperties.titleblock) + return {"FINISHED"} + + +class OpenSheet(bpy.types.Operator): + bl_idname = "bim.open_sheet" + bl_label = "Open Sheet" + + def execute(self, context): + props = bpy.context.scene.DocProperties + open_with_user_command( + bpy.context.preferences.addons["blenderbim"].preferences.svg_command, + os.path.join( + bpy.context.scene.BIMProperties.data_dir, "sheets", props.sheets[props.active_sheet_index].name + ".svg" + ), + ) + return {"FINISHED"} + + +class AddDrawingToSheet(bpy.types.Operator): + bl_idname = "bim.add_drawing_to_sheet" + bl_label = "Add Drawing To Sheet" + + def execute(self, context): + props = bpy.context.scene.DocProperties + sheet_builder = sheeter.SheetBuilder() + sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir + try: + sheet_builder.add_drawing( + props.drawings[props.active_drawing_index].name, props.sheets[props.active_sheet_index].name + ) + except: + self.report({"ERROR"}, "Drawings need to be created before being added to a sheet") + return {"FINISHED"} + + +class CreateSheets(bpy.types.Operator): + bl_idname = "bim.create_sheets" + bl_label = "Create Sheets" + + def execute(self, context): + props = bpy.context.scene.DocProperties + name = props.sheets[props.active_sheet_index].name + sheet_builder = sheeter.SheetBuilder() + sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir + sheet_builder.build(name) + + svg2pdf_command = bpy.context.preferences.addons["blenderbim"].preferences.svg2pdf_command + svg2dxf_command = bpy.context.preferences.addons["blenderbim"].preferences.svg2dxf_command + + if svg2pdf_command: + path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "build", name) + svg = os.path.join(path, name + ".svg") + pdf = os.path.join(path, name + ".pdf") + # With great power comes great responsibility. Example: + # [['inkscape', svg, '-o', pdf]] + commands = eval(svg2pdf_command) + for command in commands: + subprocess.run(command) + + if svg2dxf_command: + path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "build", name) + svg = os.path.join(path, name + ".svg") + eps = os.path.join(path, name + ".eps") + dxf = os.path.join(path, name + ".dxf") + base = os.path.join(path, name) + # With great power comes great responsibility. Example: + # [['inkscape', svg, '-o', eps], ['pstoedit', '-dt', '-f', 'dxf:-polyaslines -mm', eps, dxf, '-psarg', '-dNOSAFER']] + commands = eval(svg2dxf_command) + for command in commands: + subprocess.run(command) + + if svg2pdf_command: + open_with_user_command(bpy.context.preferences.addons["blenderbim"].preferences.pdf_command, pdf) + else: + open_with_user_command( + bpy.context.preferences.addons["blenderbim"].preferences.svg_command, + os.path.join(bpy.context.scene.BIMProperties.data_dir, "build", name, name + ".svg"), + ) + return {"FINISHED"} + + +class OpenView(bpy.types.Operator): + bl_idname = "bim.open_view" + bl_label = "Open View" + view: bpy.props.StringProperty() + + def execute(self, context): + open_with_user_command( + bpy.context.preferences.addons["blenderbim"].preferences.svg_command, + os.path.join(bpy.context.scene.BIMProperties.data_dir, "diagrams", self.view + ".svg"), + ) + return {"FINISHED"} + + +class OpenViewCamera(bpy.types.Operator): + bl_idname = "bim.open_view_camera" + bl_label = "Open View Camera" + view_name: bpy.props.StringProperty() + + def execute(self, context): + new_drawing_index = bpy.context.scene.DocProperties.drawings.find(self.view_name) + bpy.context.scene.DocProperties.active_drawing_index = new_drawing_index + drawing = bpy.context.scene.DocProperties.drawings[new_drawing_index] + bpy.context.view_layer.objects.active = drawing.camera + bpy.ops.object.select_all(action="DESELECT") + drawing.camera.select_set(True) + for area in bpy.context.screen.areas: + if area.ui_type == "PROPERTIES": + for space in area.spaces: + space.context = "DATA" + return {"FINISHED"} + + +class ActivateView(bpy.types.Operator): + bl_idname = "bim.activate_view" + bl_label = "Activate View" + drawing_index: bpy.props.IntProperty() + + def execute(self, context): + camera = bpy.context.scene.DocProperties.drawings[self.drawing_index].camera + if not camera: + return {"FINISHED"} + bpy.context.scene.camera = camera + area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D") + area.spaces[0].region_3d.view_perspective = "CAMERA" + views_collection = bpy.data.collections.get("Views") + for collection in views_collection.children: + # We assume the project collection is at the top level + for project_collection in bpy.context.view_layer.layer_collection.children: + # We assume a convention that the 'Views' collection is directly + # in the project collection + if ( + "Views" in project_collection.children + and collection.name in project_collection.children["Views"].children + ): + project_collection.children["Views"].children[collection.name].hide_viewport = True + bpy.data.collections.get(collection.name).hide_render = True + bpy.context.view_layer.layer_collection.children["Views"].children[ + camera.users_collection[0].name + ].hide_viewport = False + bpy.data.collections.get(camera.users_collection[0].name).hide_render = False + bpy.ops.bim.activate_drawing_style() + return {"FINISHED"} + + +class SelectDocIfcFile(bpy.types.Operator): + bl_idname = "bim.select_doc_ifc_file" + bl_label = "Select Documentation IFC File" + filepath: bpy.props.StringProperty(subtype="FILE_PATH") + index: bpy.props.IntProperty() + + def execute(self, context): + bpy.context.scene.DocProperties.ifc_files[self.index].name = self.filepath + return {"FINISHED"} + + def invoke(self, context, event): + context.window_manager.fileselect_add(self) + return {"RUNNING_MODAL"} + + +class GenerateReferences(bpy.types.Operator): + bl_idname = "bim.generate_references" + bl_label = "Generate References" + + def execute(self, context): + self.camera = bpy.context.scene.camera + self.filter_potential_references() + if self.camera.data.BIMCameraProperties.target_view == "PLAN_VIEW": + self.generate_grids() + if self.camera.data.BIMCameraProperties.target_view == "ELEVATION_VIEW": + self.generate_grids() + self.generate_levels() + if self.camera.data.BIMCameraProperties.target_view == "SECTION_VIEW": + self.generate_grids() + self.generate_levels() + return {"FINISHED"} + + def filter_potential_references(self): + for name in ["grids", "levels"]: + setattr(self, name, []) + for obj in bpy.data.objects: + if "IfcGridAxis/" in obj.name: + self.grids.append(obj) + if "IfcBuildingStorey/" in obj.name: + self.levels.append(obj) + + def generate_grids(self): + # TODO + pass + + def generate_levels(self): + if self.camera.data.BIMCameraProperties.raster_x > self.camera.data.BIMCameraProperties.raster_y: + width = self.camera.data.ortho_scale + height = ( + width / self.camera.data.BIMCameraProperties.raster_x * self.camera.data.BIMCameraProperties.raster_y + ) + else: + height = self.camera.data.ortho_scale + width = ( + height / self.camera.data.BIMCameraProperties.raster_y * self.camera.data.BIMCameraProperties.raster_x + ) + level_obj = annotation.Annotator.get_annotation_obj("Section Level", "curve") + + width_in_mm = width * 1000 + if self.camera.data.BIMCameraProperties.diagram_scale == "CUSTOM": + human_scale, fraction = self.camera.data.BIMCameraProperties.custom_diagram_scale.split("|") + else: + human_scale, fraction = self.camera.data.BIMCameraProperties.diagram_scale.split("|") + numerator, denominator = fraction.split("/") + scale = float(numerator) / float(denominator) + real_world_width_in_mm = width_in_mm * scale + offset_in_mm = 20 + offset_percentage = offset_in_mm / real_world_width_in_mm + + for obj in self.levels: + projection = self.project_point_onto_camera(obj.location) + co1 = self.camera.matrix_world @ Vector((width / 2 - (offset_percentage * width), projection[1], -1)) + co2 = self.camera.matrix_world @ Vector((-(width / 2), projection[1], -1)) + annotation.Annotator.add_line_to_annotation(level_obj, co1, co2) + + def project_point_onto_camera(self, point): + projection = self.camera.matrix_world.to_quaternion() @ Vector((0, 0, -1)) + return self.camera.matrix_world.inverted() @ geometry.intersect_line_plane( + point.xyz, point.xyz - projection, self.camera.location, projection + ) + + +class ResizeText(bpy.types.Operator): + bl_idname = "bim.resize_text" + bl_label = "Resize Text" + + def execute(self, context): + for obj in bpy.context.scene.camera.users_collection[0].objects: + if isinstance(obj.data, bpy.types.TextCurve): + annotation.Annotator.resize_text(obj) + return {"FINISHED"} + + +class AddVariable(bpy.types.Operator): + bl_idname = "bim.add_variable" + bl_label = "Add Variable" + + def execute(self, context): + bpy.context.active_object.data.BIMTextProperties.variables.add() + return {"FINISHED"} + + +class RemoveVariable(bpy.types.Operator): + bl_idname = "bim.remove_variable" + bl_label = "Remove Variable" + index: bpy.props.IntProperty() + + def execute(self, context): + bpy.context.active_object.data.BIMTextProperties.variables.remove(self.index) + return {"FINISHED"} + + +class PropagateTextData(bpy.types.Operator): + bl_idname = "bim.propagate_text_data" + bl_label = "Propagate Text Data" + + def execute(self, context): + source = bpy.context.active_object + for obj in bpy.context.selected_objects: + if obj == source: + continue + obj.data.body = source.data.body + obj.data.align_x = source.data.align_x + obj.data.align_y = source.data.align_y + obj.data.BIMTextProperties.font_size = source.data.BIMTextProperties.font_size + obj.data.BIMTextProperties.symbol = source.data.BIMTextProperties.symbol + while len(obj.data.BIMTextProperties.variables) > 0: + obj.data.BIMTextProperties.variables.remove(0) + for variable in source.data.BIMTextProperties.variables: + new_variable = obj.data.BIMTextProperties.variables.add() + new_variable.name = variable.name + new_variable.prop_key = variable.prop_key + return {"FINISHED"} + + +class RemoveDrawing(bpy.types.Operator): + bl_idname = "bim.remove_drawing" + bl_label = "Remove Drawing" + index: bpy.props.IntProperty() + + def execute(self, context): + props = bpy.context.scene.DocProperties + camera = props.drawings[self.index].camera + collection = camera.users_collection[0] + for obj in collection.objects: + bpy.data.objects.remove(obj) + bpy.data.collections.remove(collection, do_unlink=True) + props.drawings.remove(self.index) + return {"FINISHED"} + + +class AddDrawingStyle(bpy.types.Operator): + bl_idname = "bim.add_drawing_style" + bl_label = "Add Drawing Style" + + def execute(self, context): + new = bpy.context.scene.DocProperties.drawing_styles.add() + new.name = "New Drawing Style" + return {"FINISHED"} + + +class RemoveDrawingStyle(bpy.types.Operator): + bl_idname = "bim.remove_drawing_style" + bl_label = "Remove Drawing Style" + index: bpy.props.IntProperty() + + def execute(self, context): + bpy.context.scene.DocProperties.drawing_styles.remove(self.index) + return {"FINISHED"} + + +class SaveDrawingStyle(bpy.types.Operator): + bl_idname = "bim.save_drawing_style" + bl_label = "Save Drawing Style" + index: bpy.props.StringProperty() + + def execute(self, context): + space = self.get_view_3d() + style = { + "bpy.data.worlds[0].color": tuple(bpy.data.worlds[0].color), + "bpy.context.scene.render.engine": bpy.context.scene.render.engine, + "bpy.context.scene.render.film_transparent": bpy.context.scene.render.film_transparent, + "bpy.context.scene.display.shading.show_object_outline": bpy.context.scene.display.shading.show_object_outline, + "bpy.context.scene.display.shading.show_cavity": bpy.context.scene.display.shading.show_cavity, + "bpy.context.scene.display.shading.cavity_type": bpy.context.scene.display.shading.cavity_type, + "bpy.context.scene.display.shading.curvature_ridge_factor": bpy.context.scene.display.shading.curvature_ridge_factor, + "bpy.context.scene.display.shading.curvature_valley_factor": bpy.context.scene.display.shading.curvature_valley_factor, + "bpy.context.scene.view_settings.view_transform": bpy.context.scene.view_settings.view_transform, + "bpy.context.scene.display.shading.light": bpy.context.scene.display.shading.light, + "bpy.context.scene.display.shading.color_type": bpy.context.scene.display.shading.color_type, + "bpy.context.scene.display.shading.single_color": tuple(bpy.context.scene.display.shading.single_color), + "bpy.context.scene.display.shading.show_shadows": bpy.context.scene.display.shading.show_shadows, + "bpy.context.scene.display.shading.shadow_intensity": bpy.context.scene.display.shading.shadow_intensity, + "bpy.context.scene.display.light_direction": tuple(bpy.context.scene.display.light_direction), + "bpy.context.scene.view_settings.use_curve_mapping": bpy.context.scene.view_settings.use_curve_mapping, + "space.overlay.show_wireframes": space.overlay.show_wireframes, + "space.overlay.wireframe_threshold": space.overlay.wireframe_threshold, + "space.overlay.show_floor": space.overlay.show_floor, + "space.overlay.show_axis_x": space.overlay.show_axis_x, + "space.overlay.show_axis_y": space.overlay.show_axis_y, + "space.overlay.show_axis_z": space.overlay.show_axis_z, + "space.overlay.show_object_origins": space.overlay.show_object_origins, + "space.overlay.show_relationship_lines": space.overlay.show_relationship_lines, + } + if self.index: + index = int(self.index) + else: + index = bpy.context.active_object.data.BIMCameraProperties.active_drawing_style_index + bpy.context.scene.DocProperties.drawing_styles[index].raster_style = json.dumps(style) + return {"FINISHED"} + + 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 ActivateDrawingStyle(bpy.types.Operator): + bl_idname = "bim.activate_drawing_style" + bl_label = "Activate Drawing Style" + + def execute(self, context): + if context.scene.camera.data.BIMCameraProperties.active_drawing_style_index < len( + bpy.context.scene.DocProperties.drawing_styles + ): + self.drawing_style = bpy.context.scene.DocProperties.drawing_styles[ + context.scene.camera.data.BIMCameraProperties.active_drawing_style_index + ] + self.set_raster_style() + self.set_query() + return {"FINISHED"} + + def set_raster_style(self): + space = self.get_view_3d() + style = json.loads(self.drawing_style.raster_style) + bpy.data.worlds[0].color = style["bpy.data.worlds[0].color"] + bpy.context.scene.render.engine = style["bpy.context.scene.render.engine"] + bpy.context.scene.render.film_transparent = style["bpy.context.scene.render.film_transparent"] + bpy.context.scene.display.shading.show_object_outline = style[ + "bpy.context.scene.display.shading.show_object_outline" + ] + bpy.context.scene.display.shading.show_cavity = style["bpy.context.scene.display.shading.show_cavity"] + bpy.context.scene.display.shading.cavity_type = style["bpy.context.scene.display.shading.cavity_type"] + bpy.context.scene.display.shading.curvature_ridge_factor = style[ + "bpy.context.scene.display.shading.curvature_ridge_factor" + ] + bpy.context.scene.display.shading.curvature_valley_factor = style[ + "bpy.context.scene.display.shading.curvature_valley_factor" + ] + bpy.context.scene.view_settings.view_transform = style["bpy.context.scene.view_settings.view_transform"] + bpy.context.scene.display.shading.light = style["bpy.context.scene.display.shading.light"] + bpy.context.scene.display.shading.color_type = style["bpy.context.scene.display.shading.color_type"] + bpy.context.scene.display.shading.single_color = style["bpy.context.scene.display.shading.single_color"] + bpy.context.scene.display.shading.show_shadows = style["bpy.context.scene.display.shading.show_shadows"] + bpy.context.scene.display.shading.shadow_intensity = style["bpy.context.scene.display.shading.shadow_intensity"] + bpy.context.scene.display.light_direction = style["bpy.context.scene.display.light_direction"] + + bpy.context.scene.view_settings.use_curve_mapping = style["bpy.context.scene.view_settings.use_curve_mapping"] + space.overlay.show_wireframes = style["space.overlay.show_wireframes"] + space.overlay.wireframe_threshold = style["space.overlay.wireframe_threshold"] + space.overlay.show_floor = style["space.overlay.show_floor"] + space.overlay.show_axis_x = style["space.overlay.show_axis_x"] + space.overlay.show_axis_y = style["space.overlay.show_axis_y"] + space.overlay.show_axis_z = style["space.overlay.show_axis_z"] + space.overlay.show_object_origins = style["space.overlay.show_object_origins"] + space.overlay.show_relationship_lines = style["space.overlay.show_relationship_lines"] + + def set_query(self): + self.selector = ifcopenshell.util.selector.Selector() + self.include_global_ids = [] + self.exclude_global_ids = [] + for ifc_file in bpy.context.scene.DocProperties.ifc_files: + try: + ifc = ifcopenshell.open(ifc_file.name) + except: + continue + if self.drawing_style.include_query: + results = self.selector.parse(ifc, self.drawing_style.include_query) + self.include_global_ids.extend([e.GlobalId for e in results]) + if self.drawing_style.exclude_query: + results = self.selector.parse(ifc, self.drawing_style.exclude_query) + self.exclude_global_ids.extend([e.GlobalId for e in results]) + if self.drawing_style.include_query: + self.parse_filter_query("INCLUDE") + if self.drawing_style.exclude_query: + self.parse_filter_query("EXCLUDE") + + def parse_filter_query(self, mode): + if mode == "INCLUDE": + objects = bpy.context.scene.objects + elif mode == "EXCLUDE": + objects = bpy.context.visible_objects + for obj in objects: + if mode == "INCLUDE": + obj.hide_viewport = False # Note: this breaks alt-H + global_id = obj.BIMObjectProperties.attributes.get("GlobalId") + if not global_id: + continue + global_id = global_id.string_value + if mode == "INCLUDE": + if global_id not in self.include_global_ids: + obj.hide_viewport = True # Note: this breaks alt-H + elif mode == "EXCLUDE": + if global_id in self.exclude_global_ids: + obj.hide_viewport = True # Note: this breaks alt-H + + 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 EditVectorStyle(bpy.types.Operator): + bl_idname = "bim.edit_vector_style" + bl_label = "Edit Vector Style" + + def execute(self, context): + camera = context.scene.camera + vector_style = context.scene.DocProperties.drawing_styles[ + camera.data.BIMCameraProperties.active_drawing_style_index + ].vector_style + bpy.data.texts.load(os.path.join(context.scene.BIMProperties.data_dir, "styles", vector_style + ".css")) + return {"FINISHED"} + + +class RemoveSheet(bpy.types.Operator): + bl_idname = "bim.remove_sheet" + bl_label = "Remove Sheet" + index: bpy.props.IntProperty() + + def execute(self, context): + props = bpy.context.scene.DocProperties + props.sheets.remove(self.index) + return {"FINISHED"} + + +class AddSchedule(bpy.types.Operator): + bl_idname = "bim.add_schedule" + bl_label = "Add Schedule" + + def execute(self, context): + new = bpy.context.scene.DocProperties.schedules.add() + new.name = "SCHEDULE {}".format(len(bpy.context.scene.DocProperties.schedules)) + return {"FINISHED"} + + +class RemoveSchedule(bpy.types.Operator): + bl_idname = "bim.remove_schedule" + bl_label = "Remove Schedule" + index: bpy.props.IntProperty() + + def execute(self, context): + props = bpy.context.scene.DocProperties + props.schedules.remove(self.index) + return {"FINISHED"} + + +class SelectScheduleFile(bpy.types.Operator): + bl_idname = "bim.select_schedule_file" + bl_label = "Select Documentation IFC File" + filepath: bpy.props.StringProperty(subtype="FILE_PATH") + filter_glob: bpy.props.StringProperty(default="*.ods", options={"HIDDEN"}) + index: bpy.props.IntProperty() + + def execute(self, context): + props = bpy.context.scene.DocProperties + props.schedules[props.active_schedule_index].file = self.filepath + return {"FINISHED"} + + def invoke(self, context, event): + context.window_manager.fileselect_add(self) + return {"RUNNING_MODAL"} + + +class BuildSchedule(bpy.types.Operator): + bl_idname = "bim.build_schedule" + bl_label = "Build Schedule" + + def execute(self, context): + props = bpy.context.scene.DocProperties + schedule = props.schedules[props.active_schedule_index] + schedule_creator = scheduler.Scheduler() + outfile = os.path.join(bpy.context.scene.BIMProperties.data_dir, "schedules", schedule.name + ".svg") + schedule_creator.schedule(schedule.file, outfile) + open_with_user_command(bpy.context.preferences.addons["blenderbim"].preferences.svg_command, outfile) + return {"FINISHED"} + + +class AddScheduleToSheet(bpy.types.Operator): + bl_idname = "bim.add_schedule_to_sheet" + bl_label = "Add Schedule To Sheet" + + def execute(self, context): + props = bpy.context.scene.DocProperties + sheet_builder = sheeter.SheetBuilder() + sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir + sheet_builder.add_schedule( + props.schedules[props.active_schedule_index].name, props.sheets[props.active_sheet_index].name + ) + return {"FINISHED"} + + +class AddDrawingStyleAttribute(bpy.types.Operator): + bl_idname = "bim.add_drawing_style_attribute" + bl_label = "Add Drawing Style Attribute" + + def execute(self, context): + props = bpy.context.scene.camera.data.BIMCameraProperties + context.scene.DocProperties.drawing_styles[props.active_drawing_style_index].attributes.add() + return {"FINISHED"} + + +class RemoveDrawingStyleAttribute(bpy.types.Operator): + bl_idname = "bim.remove_drawing_style_attribute" + bl_label = "Remove Drawing Style Attribute" + index: bpy.props.IntProperty() + + def execute(self, context): + props = bpy.context.scene.camera.data.BIMCameraProperties + context.scene.DocProperties.drawing_styles[props.active_drawing_style_index].attributes.remove(self.index) + return {"FINISHED"} + + +class RefreshDrawingList(bpy.types.Operator): + bl_idname = "bim.refresh_drawing_list" + bl_label = "Refresh Drawing List" + + def execute(self, context): + while len(bpy.context.scene.DocProperties.drawings) > 0: + bpy.context.scene.DocProperties.drawings.remove(0) + for obj in bpy.context.scene.objects: + if not isinstance(obj.data, bpy.types.Camera): + continue + if "IfcAnnotation/" in obj.name and obj.users_collection[0].name == obj.name: + new = bpy.context.scene.DocProperties.drawings.add() + new.name = obj.name.split("/")[1] + new.camera = obj + return {"FINISHED"} + + +class CleanWireframes(bpy.types.Operator): + bl_idname = "bim.clean_wireframes" + bl_label = "Clean Wireframes" + + def execute(self, context): + objects = bpy.data.objects + if bpy.context.selected_objects: + objects = bpy.context.selected_objects + for obj in objects: + if not isinstance(obj.data, bpy.types.Mesh): + continue + # TODO: probably breaks i18n + if not obj.modifiers.get("EdgeSplit"): + obj.modifiers.new("EdgeSplit", "EDGE_SPLIT") + return {"FINISHED"} + + +class CopyGrid(bpy.types.Operator): + bl_idname = "bim.add_grid" + bl_label = "Add Grid" + bl_options = {"UNDO"} + + def execute(self, context): + proj_coll = helper.get_project_collection(context.scene) + view_coll, camera = helper.get_active_drawing(context.scene) + if view_coll is None: + return {"CANCELLED"} + is_ortho = camera.data.type == "ORTHO" + bounds = helper.ortho_view_frame(camera.data) if is_ortho else None + clipping = is_ortho and camera.data.BIMCameraProperties.target_view in ("PLAN_VIEW", "REFLECTED_PLAN_VIEW") + elevating = is_ortho and camera.data.BIMCameraProperties.target_view in ("ELEVATION_VIEW", "SECTION_VIEW") + + def grep(coll): + return [obj for obj in coll.all_objects if obj.name.startswith("IfcGridAxis")] + + def clone(src): + dst = src.copy() + dst.data = dst.data.copy() + return dst + + def disassemble(obj): + mesh = bmesh.new() + mesh.verts.ensure_lookup_table() + mesh.from_mesh(obj.data) + return obj, mesh + + def assemble(obj, mesh): + mesh.to_mesh(obj.data) + return obj + + def localize(obj, mesh): + # convert to camera coords + mesh.transform(camera.matrix_world.inverted() @ obj.matrix_world) + obj.matrix_world = camera.matrix_world + return obj, mesh + + def clip(mesh): + # clip segment against camera view bounds + mesh.verts.ensure_lookup_table() + points = [v.co for v in mesh.verts[0:2]] + points = helper.clip_segment(bounds, points) + if points is None: + return None + mesh.verts[0].co = points[0] + mesh.verts[1].co = points[1] + return mesh + + def elev(mesh): + # put camera-perpendicular segments vertically + mesh.verts.ensure_lookup_table() + points = [v.co for v in mesh.verts[0:2]] + points = helper.elevate_segment(bounds, points) + if points is None: + return None + points = helper.clip_segment(bounds, points) + if points is None: + return None + mesh.verts[0].co = points[0] + mesh.verts[1].co = points[1] + return mesh + + for obj in grep(view_coll): + view_coll.objects.unlink(obj) + + grid = [localize(*disassemble(clone(obj))) for obj in grep(proj_coll)] + + if clipping: + grid = [(obj, clip(mesh)) for obj, mesh in grid] + elif elevating: + grid = [(obj, elev(mesh)) for obj, mesh in grid] + + for obj, mesh in grid: + if mesh is not None: + view_coll.objects.link(assemble(obj, mesh)) + + return {"FINISHED"} + + +class AddSectionsAnnotations(bpy.types.Operator): + bl_idname = "bim.add_sections_annotations" + bl_label = "Add Sections" + bl_options = {"UNDO"} + + def execute(self, context): + scene = context.scene + view_coll, camera = helper.get_active_drawing(scene) + is_ortho = camera.data.type == "ORTHO" + if not is_ortho: + return {"CANCELLED"} + bounds = helper.ortho_view_frame(camera.data) if is_ortho else None + + drawings = [ + d + for d in scene.DocProperties.drawings + if ( + d.camera is not camera + and d.camera.data.type == "ORTHO" + and d.camera.data.BIMCameraProperties.target_view == "SECTION_VIEW" + ) + ] + + def sideview(cam): + # leftmost and righmost points of camera view area, local coords + xmin, xmax, _, _, _, _ = helper.ortho_view_frame(cam.data, margin=0) + proj = camera.matrix_world.inverted() @ cam.matrix_world + p_l = proj @ Vector((xmin, 0, 0)) + p_r = proj @ Vector((xmax, 0, 0)) + return helper.clip_segment(bounds, (p_l, p_r)) + + def annotation(drawing, points): + # object with path geometry + name = drawing.name + curve = bpy.data.curves.new(f"Section/{name}", "CURVE") + spline = curve.splines.new("POLY") # has 1 initial point + spline.points.add(1) + p1, p2 = points + z = bounds[4] - 1e-5 # zmin + spline.points[0].co = (p1.x, p1.y, z, 1) + spline.points[1].co = (p2.x, p2.y, z, 1) + obj = bpy.data.objects.new(f"IfcAnnotation/Section/{name}", curve) + obj.matrix_world = camera.matrix_world + return obj + + for d in drawings: + points = sideview(d.camera) + if points is None: + continue + obj = annotation(d, points) + view_coll.objects.link(obj) + + return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/scheduler.py b/src/blenderbim/blenderbim/bim/module/drawing/scheduler.py similarity index 100% rename from src/blenderbim/blenderbim/bim/scheduler.py rename to src/blenderbim/blenderbim/bim/module/drawing/scheduler.py diff --git a/src/blenderbim/blenderbim/bim/shaders.py b/src/blenderbim/blenderbim/bim/module/drawing/shaders.py similarity index 91% rename from src/blenderbim/blenderbim/bim/shaders.py rename to src/blenderbim/blenderbim/bim/module/drawing/shaders.py index 051daab54e..8b9c79efc8 100644 --- a/src/blenderbim/blenderbim/bim/shaders.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/shaders.py @@ -1,10 +1,10 @@ -from mathutils import Matrix import bgl +from mathutils import Matrix from gpu.types import GPUShader from gpu_extras.batch import batch_for_shader -class BaseShader(): +class BaseShader: """Wrapepr for GPUShader To use for viewport decorations with geometry generated on GPU side. @@ -85,14 +85,15 @@ class BaseShader(): def __init__(self): # NB: libcode arg doesn't work - self.prog = GPUShader(vertexcode=self.VERT_GLSL, - fragcode=self.FRAG_GLSL, - geocode=self.LIB_GLSL + self.GEOM_GLSL, - defines=self.DEF_GLSL) + self.prog = GPUShader( + vertexcode=self.VERT_GLSL, + fragcode=self.FRAG_GLSL, + geocode=self.LIB_GLSL + self.GEOM_GLSL, + defines=self.DEF_GLSL, + ) def batch(self, indices=None, **data): - """Returns automatic GPUBatch filled with provided parameters - """ + """Returns automatic GPUBatch filled with provided parameters""" batch = batch_for_shader(self.prog, self.TYPE, data, indices=indices) batch.program_set(self.prog) return batch @@ -113,23 +114,26 @@ class BaseShader(): region = ctx.region region3d = ctx.region_data try: - self.prog.uniform_float('viewMatrix', region3d.perspective_matrix) + self.prog.uniform_float("viewMatrix", region3d.perspective_matrix) except ValueError: # unused uniform pass try: - self.prog.uniform_float('winSize', (region.width / 2, region.height / 2)) + self.prog.uniform_float("winSize", (region.width / 2, region.height / 2)) except ValueError: # unused uniform pass class BaseLinesShader(BaseShader): - """Draws line segments with gaps around vertices at endpoints - """ - TYPE = 'LINES' + """Draws line segments with gaps around vertices at endpoints""" - DEF_GLSL = BaseShader.DEF_GLSL + """ + TYPE = "LINES" + + DEF_GLSL = ( + BaseShader.DEF_GLSL + + """ #define GAP_SIZE {gap_size} """ + ) GEOM_GLSL = """ layout(lines) in; @@ -171,6 +175,7 @@ class GizmoShader(BaseShader): Scaling to match viewport is partially controlled by user preferences and gizmo code. """ + # TODO: add some magic to respect gizmo settings/params VERT_GLSL = """ @@ -187,12 +192,15 @@ class GizmoShader(BaseShader): class DotsGizmoShader(GizmoShader): """Draws circles of radius 1 around points""" - TYPE = 'POINTS' + TYPE = "POINTS" - DEF_GLSL = BaseShader.DEF_GLSL + """ + DEF_GLSL = ( + BaseShader.DEF_GLSL + + """ #define CIRCLE_SEGMENTS 12 #define CIRCLE_RADIUS 8 """ + ) GEOM_GLSL = """ layout(points) in; @@ -235,11 +243,14 @@ class DotsGizmoShader(GizmoShader): class ExtrusionGuidesShader(GizmoShader): """Draws lines and add cross in XY plane at endpoints""" - TYPE = 'LINES' + TYPE = "LINES" - DEF_GLSL = BaseShader.DEF_GLSL + """ + DEF_GLSL = ( + BaseShader.DEF_GLSL + + """ #define CROSS_SIZE .5 """ + ) GEOM_GLSL = """ uniform mat4 ModelViewProjectionMatrix; diff --git a/src/blenderbim/blenderbim/bim/sheeter.py b/src/blenderbim/blenderbim/bim/module/drawing/sheeter.py similarity index 100% rename from src/blenderbim/blenderbim/bim/sheeter.py rename to src/blenderbim/blenderbim/bim/module/drawing/sheeter.py diff --git a/src/blenderbim/blenderbim/bim/module/drawing/ui.py b/src/blenderbim/blenderbim/bim/module/drawing/ui.py index 655bd3c019..ba7e82ea94 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/ui.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/ui.py @@ -110,3 +110,213 @@ class BIM_PT_camera(Panel): row.operator("bim.create_drawing", text="Create Drawing 2.0") op = row.operator("bim.open_view", icon="URL", text="") op.view = context.active_object.name.split("/")[1] + + +class BIM_PT_drawings(Panel): + bl_label = "SVG Drawings" + bl_idname = "BIM_PT_drawings" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "output" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + props = bpy.context.scene.DocProperties + + row = layout.row(align=True) + row.operator("bim.add_drawing") + row.operator("bim.refresh_drawing_list", icon="FILE_REFRESH", text="") + + if props.drawings: + if props.active_drawing_index < len(props.drawings): + op = row.operator("bim.open_view", icon="URL", text="") + op.view = props.drawings[props.active_drawing_index].name + row.operator("bim.remove_drawing", icon="X", text="").index = props.active_drawing_index + layout.template_list("BIM_UL_generic", "", props, "drawings", props, "active_drawing_index") + + row = layout.row() + row.operator("bim.add_ifc_file") + + for index, ifc_file in enumerate(props.ifc_files): + row = layout.row(align=True) + row.prop(ifc_file, "name", text="IFC #{}".format(index + 1)) + row.operator("bim.select_doc_ifc_file", icon="FILE_FOLDER", text="").index = index + row.operator("bim.remove_ifc_file", icon="X", text="").index = index + + +class BIM_PT_schedules(Panel): + bl_label = "ODS Schedules" + bl_idname = "BIM_PT_schedules" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "output" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + props = bpy.context.scene.DocProperties + + row = layout.row(align=True) + row.operator("bim.add_schedule") + + if props.schedules: + row.operator("bim.build_schedule", icon="LINENUMBERS_ON", text="") + row.operator("bim.remove_schedule", icon="X", text="").index = props.active_schedule_index + + layout.template_list("BIM_UL_generic", "", props, "schedules", props, "active_schedule_index") + + row = layout.row() + row.prop(props.schedules[props.active_schedule_index], "file") + row.operator("bim.select_schedule_file", icon="FILE_FOLDER", text="") + + +class BIM_PT_sheets(Panel): + bl_label = "SVG Sheets" + bl_idname = "BIM_PT_sheets" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "output" + + def draw(self, context): + layout = self.layout + props = bpy.context.scene.DocProperties + + row = layout.row(align=True) + row.prop(props, "titleblock", text="") + row.operator("bim.add_sheet") + + if props.sheets: + row.operator("bim.open_sheet", icon="URL", text="") + row.operator("bim.remove_sheet", icon="X", text="").index = props.active_sheet_index + + layout.template_list("BIM_UL_generic", "", props, "sheets", props, "active_sheet_index") + + row = layout.row(align=True) + row.operator("bim.add_drawing_to_sheet") + row.operator("bim.add_schedule_to_sheet") + row = layout.row() + row.operator("bim.create_sheets") + + +class BIM_PT_text(Panel): + bl_label = "Text Paper Space" + bl_idname = "BIM_PT_text" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "data" + + @classmethod + def poll(cls, context): + return type(context.curve) is bpy.types.TextCurve + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + props = context.active_object.data.BIMTextProperties + + row = layout.row() + row.operator("bim.propagate_text_data") + + row = layout.row() + row.prop(props, "font_size") + row = layout.row() + row.prop(props, "symbol") + row = layout.row() + row.prop(props, "related_element") + + row = layout.row() + row.operator("bim.add_variable") + + for index, variable in enumerate(props.variables): + row = layout.row(align=True) + row.prop(variable, "name") + row.operator("bim.remove_variable", icon="X", text="").index = index + row = layout.row() + row.prop(variable, "prop_key") + + +class BIM_PT_annotation_utilities(Panel): + bl_idname = "BIM_PT_annotation_utilities" + bl_label = "Annotation" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "BlenderBIM" + + def draw(self, context): + layout = self.layout + + row = layout.row(align=True) + row.operator("bim.clean_wireframes") + row = layout.row(align=True) + row.operator("bim.link_ifc") + row = layout.row(align=True) + row.operator("bim.add_grid") + row = layout.row(align=True) + row.operator("bim.add_sections_annotations") + + row = layout.row(align=True) + op = row.operator("bim.add_annotation", text="Dim", icon="ARROW_LEFTRIGHT") + op.obj_name = "Dimension" + op.data_type = "curve" + op = row.operator("bim.add_annotation", text="Dim (Eq)", icon="ARROW_LEFTRIGHT") + op.obj_name = "Equal" + op.data_type = "curve" + + row = layout.row(align=True) + op = row.operator("bim.add_annotation", text="Text", icon="SMALL_CAPS") + op.data_type = "text" + op = row.operator("bim.add_annotation", text="Leader", icon="TRACKING_BACKWARDS") + op.obj_name = "Leader" + op.data_type = "curve" + + row = layout.row(align=True) + op = row.operator("bim.add_annotation", text="Stair Arrow", icon="SCREEN_BACK") + op.obj_name = "Stair" + op.data_type = "curve" + op = row.operator("bim.add_annotation", text="Hidden", icon="CON_TRACKTO") + op.obj_name = "Hidden" + op.data_type = "mesh" + + row = layout.row(align=True) + op = row.operator("bim.add_annotation", text="Level (Plan)", icon="SORTBYEXT") + op.obj_name = "Plan Level" + op.data_type = "curve" + op = row.operator("bim.add_annotation", text="Level (Section)", icon="TRIA_DOWN") + op.obj_name = "Section Level" + op.data_type = "curve" + + row = layout.row(align=True) + op = row.operator("bim.add_annotation", text="Breakline", icon="FCURVE") + op.obj_name = "Break" + op.data_type = "mesh" + op = row.operator("bim.add_annotation", text="Misc", icon="MESH_MONKEY") + op.obj_name = "Misc" + op.data_type = "mesh" + + props = bpy.context.scene.DocProperties + + row = layout.row(align=True) + row.operator("bim.add_drawing") + row.operator("bim.refresh_drawing_list", icon="FILE_REFRESH", text="") + + if props.drawings: + if props.active_drawing_index < len(props.drawings): + op = row.operator("bim.open_view", icon="URL", text="") + op.view = props.drawings[props.active_drawing_index].name + row.operator("bim.remove_drawing", icon="X", text="").index = props.active_drawing_index + layout.template_list("BIM_UL_drawinglist", "", props, "drawings", props, "active_drawing_index") + + layout.prop(props, "should_draw_decorations") + layout.prop(props, "decorations_colour") + + +class BIM_UL_drawinglist(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + if item: + row = layout.row(align=True) + row.prop(item, "name", text="", emboss=False) + op = row.operator("bim.open_view_camera", icon="OUTLINER_OB_CAMERA", text="") + op.view_name = item.name + else: + layout.label(text="", translate=False) diff --git a/src/blenderbim/blenderbim/bim/operator.py b/src/blenderbim/blenderbim/bim/operator.py index 4f7b8ba625..30116a540c 100644 --- a/src/blenderbim/blenderbim/bim/operator.py +++ b/src/blenderbim/blenderbim/bim/operator.py @@ -1,63 +1,19 @@ -from itertools import chain import os import bpy -import uuid import time import json import logging import webbrowser -import subprocess import ifcopenshell -import ifcopenshell.util.selector -import ifcopenshell.util.geolocation -import ifcopenshell.util.element -import ifcopenshell.util.representation -import ifcopenshell.util.schema + # Deleting the below drawing import breaks svgwrite's ElementTree appending because ... magic? import blenderbim.bim.module.drawing -import numpy as np from . import export_ifc from . import import_ifc -from . import cut_ifc -from . import sheeter -from . import scheduler from . import schema -from . import ifc -from . import helper from bpy_extras.io_utils import ImportHelper from mathutils import Vector, Matrix, Euler, geometry -import bmesh from math import radians, degrees, atan, tan, cos, sin -from bpy.app.handlers import persistent - - -@persistent -def depsgraph_update_pre_handler(scene): - set_active_camera_resolution(scene) - - -def set_active_camera_resolution(scene): - if not scene.camera or "/" not in scene.camera.name or not scene.DocProperties.drawings: - return - if ( - scene.render.resolution_x != scene.camera.data.BIMCameraProperties.raster_x - or scene.render.resolution_y != scene.camera.data.BIMCameraProperties.raster_y - ): - scene.render.resolution_x = scene.camera.data.BIMCameraProperties.raster_x - scene.render.resolution_y = scene.camera.data.BIMCameraProperties.raster_y - current_drawing = scene.DocProperties.drawings[scene.DocProperties.current_drawing_index] - if scene.camera != current_drawing.camera: - scene.DocProperties.current_drawing_index = scene.DocProperties.drawings.find(scene.camera.name.split("/")[1]) - bpy.ops.bim.activate_view(drawing_index=scene.DocProperties.current_drawing_index) - - -def open_with_user_command(user_command, path): - if user_command: - commands = eval(user_command) - for command in commands: - subprocess.run(command) - else: - webbrowser.open("file://" + path) class ExportIFC(bpy.types.Operator): @@ -129,7 +85,11 @@ class ImportIFC(bpy.types.Operator, ImportHelper): should_offset_model: bpy.props.BoolProperty(name="Import and Offset Model", default=False) model_offset_coordinates: bpy.props.StringProperty(name="Model Offset Coordinates", default="0,0,0") ifc_import_filter: bpy.props.EnumProperty( - items=[("NONE", "None", ""), ("WHITELIST", "Whitelist", ""), ("BLACKLIST", "Blacklist", ""),], + items=[ + ("NONE", "None", ""), + ("WHITELIST", "Whitelist", ""), + ("BLACKLIST", "Blacklist", ""), + ], name="Import Filter", ) ifc_selector: bpy.props.StringProperty(default="", name="IFC Selector") @@ -152,9 +112,7 @@ class ImportIFC(bpy.types.Operator, ImportHelper): settings.angular_tolerance = self.angular_tolerance settings.should_offset_model = self.should_offset_model settings.model_offset_coordinates = ( - [float(o) for o in self.model_offset_coordinates.split(",")] - if self.model_offset_coordinates - else (0, 0, 0) + [float(o) for o in self.model_offset_coordinates.split(",")] if self.model_offset_coordinates else (0, 0, 0) ) settings.ifc_import_filter = self.ifc_import_filter settings.ifc_selector = self.ifc_selector @@ -280,357 +238,6 @@ class FetchObjectPassport(bpy.types.Operator): bpy.context.active_object.data = bpy.data.meshes[reference.name] -class OpenView(bpy.types.Operator): - bl_idname = "bim.open_view" - bl_label = "Open View" - view: bpy.props.StringProperty() - - def execute(self, context): - open_with_user_command( - bpy.context.preferences.addons["blenderbim"].preferences.svg_command, - os.path.join(bpy.context.scene.BIMProperties.data_dir, "diagrams", self.view + ".svg"), - ) - return {"FINISHED"} - - -class OpenViewCamera(bpy.types.Operator): - bl_idname = "bim.open_view_camera" - bl_label = "Open View Camera" - view_name: bpy.props.StringProperty() - - def execute(self, context): - new_drawing_index = bpy.context.scene.DocProperties.drawings.find(self.view_name) - bpy.context.scene.DocProperties.active_drawing_index = new_drawing_index - drawing = bpy.context.scene.DocProperties.drawings[new_drawing_index] - bpy.context.view_layer.objects.active = drawing.camera - bpy.ops.object.select_all(action="DESELECT") - drawing.camera.select_set(True) - for area in bpy.context.screen.areas: - if area.ui_type == "PROPERTIES": - for space in area.spaces: - space.context = "DATA" - 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" - - def execute(self, context): - new = bpy.context.scene.DocProperties.sheets.add() - new.name = "{} - SHEET".format(len(bpy.context.scene.DocProperties.sheets)) - sheet_builder = sheeter.SheetBuilder() - sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir - sheet_builder.create(new.name, bpy.context.scene.DocProperties.titleblock) - return {"FINISHED"} - - -class OpenSheet(bpy.types.Operator): - bl_idname = "bim.open_sheet" - bl_label = "Open Sheet" - - def execute(self, context): - props = bpy.context.scene.DocProperties - open_with_user_command( - bpy.context.preferences.addons["blenderbim"].preferences.svg_command, - os.path.join( - bpy.context.scene.BIMProperties.data_dir, "sheets", props.sheets[props.active_sheet_index].name + ".svg" - ), - ) - return {"FINISHED"} - - -class AddDrawingToSheet(bpy.types.Operator): - bl_idname = "bim.add_drawing_to_sheet" - bl_label = "Add Drawing To Sheet" - - def execute(self, context): - props = bpy.context.scene.DocProperties - sheet_builder = sheeter.SheetBuilder() - sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir - try: - sheet_builder.add_drawing( - props.drawings[props.active_drawing_index].name, props.sheets[props.active_sheet_index].name - ) - except: - self.report({"ERROR"}, "Drawings need to be created before being added to a sheet") - return {"FINISHED"} - - -class CreateSheets(bpy.types.Operator): - bl_idname = "bim.create_sheets" - bl_label = "Create Sheets" - - def execute(self, context): - props = bpy.context.scene.DocProperties - name = props.sheets[props.active_sheet_index].name - sheet_builder = sheeter.SheetBuilder() - sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir - sheet_builder.build(name) - - svg2pdf_command = bpy.context.preferences.addons["blenderbim"].preferences.svg2pdf_command - svg2dxf_command = bpy.context.preferences.addons["blenderbim"].preferences.svg2dxf_command - - if svg2pdf_command: - path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "build", name) - svg = os.path.join(path, name + ".svg") - pdf = os.path.join(path, name + ".pdf") - # With great power comes great responsibility. Example: - # [['inkscape', svg, '-o', pdf]] - commands = eval(svg2pdf_command) - for command in commands: - subprocess.run(command) - - if svg2dxf_command: - path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "build", name) - svg = os.path.join(path, name + ".svg") - eps = os.path.join(path, name + ".eps") - dxf = os.path.join(path, name + ".dxf") - base = os.path.join(path, name) - # With great power comes great responsibility. Example: - # [['inkscape', svg, '-o', eps], ['pstoedit', '-dt', '-f', 'dxf:-polyaslines -mm', eps, dxf, '-psarg', '-dNOSAFER']] - commands = eval(svg2dxf_command) - for command in commands: - subprocess.run(command) - - if svg2pdf_command: - open_with_user_command(bpy.context.preferences.addons["blenderbim"].preferences.pdf_command, pdf) - else: - open_with_user_command( - bpy.context.preferences.addons["blenderbim"].preferences.svg_command, - os.path.join(bpy.context.scene.BIMProperties.data_dir, "build", name, name + ".svg"), - ) - return {"FINISHED"} - - -class ActivateView(bpy.types.Operator): - bl_idname = "bim.activate_view" - bl_label = "Activate View" - drawing_index: bpy.props.IntProperty() - - def execute(self, context): - camera = bpy.context.scene.DocProperties.drawings[self.drawing_index].camera - if not camera: - return {"FINISHED"} - bpy.context.scene.camera = camera - area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D") - area.spaces[0].region_3d.view_perspective = "CAMERA" - views_collection = bpy.data.collections.get("Views") - for collection in views_collection.children: - # We assume the project collection is at the top level - for project_collection in bpy.context.view_layer.layer_collection.children: - # We assume a convention that the 'Views' collection is directly - # in the project collection - if ( - "Views" in project_collection.children - and collection.name in project_collection.children["Views"].children - ): - project_collection.children["Views"].children[collection.name].hide_viewport = True - bpy.data.collections.get(collection.name).hide_render = True - bpy.context.view_layer.layer_collection.children["Views"].children[ - camera.users_collection[0].name - ].hide_viewport = False - bpy.data.collections.get(camera.users_collection[0].name).hide_render = False - bpy.ops.bim.activate_drawing_style() - return {"FINISHED"} - - class OpenUpstream(bpy.types.Operator): bl_idname = "bim.open_upstream" bl_label = "Open Upstream Reference" @@ -958,141 +565,6 @@ class RemoveIfcFile(bpy.types.Operator): return {"FINISHED"} -class SelectDocIfcFile(bpy.types.Operator): - bl_idname = "bim.select_doc_ifc_file" - bl_label = "Select Documentation IFC File" - filepath: bpy.props.StringProperty(subtype="FILE_PATH") - index: bpy.props.IntProperty() - - def execute(self, context): - bpy.context.scene.DocProperties.ifc_files[self.index].name = self.filepath - return {"FINISHED"} - - def invoke(self, context, event): - context.window_manager.fileselect_add(self) - return {"RUNNING_MODAL"} - - -class GenerateReferences(bpy.types.Operator): - bl_idname = "bim.generate_references" - bl_label = "Generate References" - - def execute(self, context): - self.camera = bpy.context.scene.camera - self.filter_potential_references() - if self.camera.data.BIMCameraProperties.target_view == "PLAN_VIEW": - self.generate_grids() - if self.camera.data.BIMCameraProperties.target_view == "ELEVATION_VIEW": - self.generate_grids() - self.generate_levels() - if self.camera.data.BIMCameraProperties.target_view == "SECTION_VIEW": - self.generate_grids() - self.generate_levels() - return {"FINISHED"} - - def filter_potential_references(self): - for name in ["grids", "levels"]: - setattr(self, name, []) - for obj in bpy.data.objects: - if "IfcGridAxis/" in obj.name: - self.grids.append(obj) - if "IfcBuildingStorey/" in obj.name: - self.levels.append(obj) - - def generate_grids(self): - # TODO - pass - - def generate_levels(self): - if self.camera.data.BIMCameraProperties.raster_x > self.camera.data.BIMCameraProperties.raster_y: - width = self.camera.data.ortho_scale - height = ( - width / self.camera.data.BIMCameraProperties.raster_x * self.camera.data.BIMCameraProperties.raster_y - ) - else: - height = self.camera.data.ortho_scale - width = ( - height / self.camera.data.BIMCameraProperties.raster_y * self.camera.data.BIMCameraProperties.raster_x - ) - level_obj = annotation.Annotator.get_annotation_obj("Section Level", "curve") - - width_in_mm = width * 1000 - if self.camera.data.BIMCameraProperties.diagram_scale == "CUSTOM": - human_scale, fraction = self.camera.data.BIMCameraProperties.custom_diagram_scale.split("|") - else: - human_scale, fraction = self.camera.data.BIMCameraProperties.diagram_scale.split("|") - numerator, denominator = fraction.split("/") - scale = float(numerator) / float(denominator) - real_world_width_in_mm = width_in_mm * scale - offset_in_mm = 20 - offset_percentage = offset_in_mm / real_world_width_in_mm - - for obj in self.levels: - projection = self.project_point_onto_camera(obj.location) - co1 = self.camera.matrix_world @ Vector((width / 2 - (offset_percentage * width), projection[1], -1)) - co2 = self.camera.matrix_world @ Vector((-(width / 2), projection[1], -1)) - annotation.Annotator.add_line_to_annotation(level_obj, co1, co2) - - def project_point_onto_camera(self, point): - projection = self.camera.matrix_world.to_quaternion() @ Vector((0, 0, -1)) - return self.camera.matrix_world.inverted() @ geometry.intersect_line_plane( - point.xyz, point.xyz - projection, self.camera.location, projection - ) - - -class ResizeText(bpy.types.Operator): - bl_idname = "bim.resize_text" - bl_label = "Resize Text" - - def execute(self, context): - for obj in bpy.context.scene.camera.users_collection[0].objects: - if isinstance(obj.data, bpy.types.TextCurve): - annotation.Annotator.resize_text(obj) - return {"FINISHED"} - - -class AddVariable(bpy.types.Operator): - bl_idname = "bim.add_variable" - bl_label = "Add Variable" - - def execute(self, context): - bpy.context.active_object.data.BIMTextProperties.variables.add() - return {"FINISHED"} - - -class RemoveVariable(bpy.types.Operator): - bl_idname = "bim.remove_variable" - bl_label = "Remove Variable" - index: bpy.props.IntProperty() - - def execute(self, context): - bpy.context.active_object.data.BIMTextProperties.variables.remove(self.index) - return {"FINISHED"} - - -class PropagateTextData(bpy.types.Operator): - bl_idname = "bim.propagate_text_data" - bl_label = "Propagate Text Data" - - def execute(self, context): - source = bpy.context.active_object - for obj in bpy.context.selected_objects: - if obj == source: - continue - obj.data.body = source.data.body - obj.data.align_x = source.data.align_x - obj.data.align_y = source.data.align_y - obj.data.BIMTextProperties.font_size = source.data.BIMTextProperties.font_size - obj.data.BIMTextProperties.symbol = source.data.BIMTextProperties.symbol - while len(obj.data.BIMTextProperties.variables) > 0: - obj.data.BIMTextProperties.variables.remove(0) - for variable in source.data.BIMTextProperties.variables: - new_variable = obj.data.BIMTextProperties.variables.add() - new_variable.name = variable.name - new_variable.prop_key = variable.prop_key - return {"FINISHED"} - - class SetOverrideColour(bpy.types.Operator): bl_idname = "bim.set_override_colour" bl_label = "Set Override Colour" @@ -1106,281 +578,6 @@ class SetOverrideColour(bpy.types.Operator): return {"FINISHED"} -class AddDrawingStyle(bpy.types.Operator): - bl_idname = "bim.add_drawing_style" - bl_label = "Add Drawing Style" - - def execute(self, context): - new = bpy.context.scene.DocProperties.drawing_styles.add() - new.name = "New Drawing Style" - return {"FINISHED"} - - -class RemoveDrawingStyle(bpy.types.Operator): - bl_idname = "bim.remove_drawing_style" - bl_label = "Remove Drawing Style" - index: bpy.props.IntProperty() - - def execute(self, context): - bpy.context.scene.DocProperties.drawing_styles.remove(self.index) - return {"FINISHED"} - - -class SaveDrawingStyle(bpy.types.Operator): - bl_idname = "bim.save_drawing_style" - bl_label = "Save Drawing Style" - index: bpy.props.StringProperty() - - def execute(self, context): - space = self.get_view_3d() - style = { - "bpy.data.worlds[0].color": tuple(bpy.data.worlds[0].color), - "bpy.context.scene.render.engine": bpy.context.scene.render.engine, - "bpy.context.scene.render.film_transparent": bpy.context.scene.render.film_transparent, - "bpy.context.scene.display.shading.show_object_outline": bpy.context.scene.display.shading.show_object_outline, - "bpy.context.scene.display.shading.show_cavity": bpy.context.scene.display.shading.show_cavity, - "bpy.context.scene.display.shading.cavity_type": bpy.context.scene.display.shading.cavity_type, - "bpy.context.scene.display.shading.curvature_ridge_factor": bpy.context.scene.display.shading.curvature_ridge_factor, - "bpy.context.scene.display.shading.curvature_valley_factor": bpy.context.scene.display.shading.curvature_valley_factor, - "bpy.context.scene.view_settings.view_transform": bpy.context.scene.view_settings.view_transform, - "bpy.context.scene.display.shading.light": bpy.context.scene.display.shading.light, - "bpy.context.scene.display.shading.color_type": bpy.context.scene.display.shading.color_type, - "bpy.context.scene.display.shading.single_color": tuple(bpy.context.scene.display.shading.single_color), - "bpy.context.scene.display.shading.show_shadows": bpy.context.scene.display.shading.show_shadows, - "bpy.context.scene.display.shading.shadow_intensity": bpy.context.scene.display.shading.shadow_intensity, - "bpy.context.scene.display.light_direction": tuple(bpy.context.scene.display.light_direction), - "bpy.context.scene.view_settings.use_curve_mapping": bpy.context.scene.view_settings.use_curve_mapping, - "space.overlay.show_wireframes": space.overlay.show_wireframes, - "space.overlay.wireframe_threshold": space.overlay.wireframe_threshold, - "space.overlay.show_floor": space.overlay.show_floor, - "space.overlay.show_axis_x": space.overlay.show_axis_x, - "space.overlay.show_axis_y": space.overlay.show_axis_y, - "space.overlay.show_axis_z": space.overlay.show_axis_z, - "space.overlay.show_object_origins": space.overlay.show_object_origins, - "space.overlay.show_relationship_lines": space.overlay.show_relationship_lines, - } - if self.index: - index = int(self.index) - else: - index = bpy.context.active_object.data.BIMCameraProperties.active_drawing_style_index - bpy.context.scene.DocProperties.drawing_styles[index].raster_style = json.dumps(style) - return {"FINISHED"} - - 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 ActivateDrawingStyle(bpy.types.Operator): - bl_idname = "bim.activate_drawing_style" - bl_label = "Activate Drawing Style" - - def execute(self, context): - if context.scene.camera.data.BIMCameraProperties.active_drawing_style_index < len( - bpy.context.scene.DocProperties.drawing_styles - ): - self.drawing_style = bpy.context.scene.DocProperties.drawing_styles[ - context.scene.camera.data.BIMCameraProperties.active_drawing_style_index - ] - self.set_raster_style() - self.set_query() - return {"FINISHED"} - - def set_raster_style(self): - space = self.get_view_3d() - style = json.loads(self.drawing_style.raster_style) - bpy.data.worlds[0].color = style["bpy.data.worlds[0].color"] - bpy.context.scene.render.engine = style["bpy.context.scene.render.engine"] - bpy.context.scene.render.film_transparent = style["bpy.context.scene.render.film_transparent"] - bpy.context.scene.display.shading.show_object_outline = style[ - "bpy.context.scene.display.shading.show_object_outline" - ] - bpy.context.scene.display.shading.show_cavity = style["bpy.context.scene.display.shading.show_cavity"] - bpy.context.scene.display.shading.cavity_type = style["bpy.context.scene.display.shading.cavity_type"] - bpy.context.scene.display.shading.curvature_ridge_factor = style[ - "bpy.context.scene.display.shading.curvature_ridge_factor" - ] - bpy.context.scene.display.shading.curvature_valley_factor = style[ - "bpy.context.scene.display.shading.curvature_valley_factor" - ] - bpy.context.scene.view_settings.view_transform = style["bpy.context.scene.view_settings.view_transform"] - bpy.context.scene.display.shading.light = style["bpy.context.scene.display.shading.light"] - bpy.context.scene.display.shading.color_type = style["bpy.context.scene.display.shading.color_type"] - bpy.context.scene.display.shading.single_color = style["bpy.context.scene.display.shading.single_color"] - bpy.context.scene.display.shading.show_shadows = style["bpy.context.scene.display.shading.show_shadows"] - bpy.context.scene.display.shading.shadow_intensity = style["bpy.context.scene.display.shading.shadow_intensity"] - bpy.context.scene.display.light_direction = style["bpy.context.scene.display.light_direction"] - - bpy.context.scene.view_settings.use_curve_mapping = style["bpy.context.scene.view_settings.use_curve_mapping"] - space.overlay.show_wireframes = style["space.overlay.show_wireframes"] - space.overlay.wireframe_threshold = style["space.overlay.wireframe_threshold"] - space.overlay.show_floor = style["space.overlay.show_floor"] - space.overlay.show_axis_x = style["space.overlay.show_axis_x"] - space.overlay.show_axis_y = style["space.overlay.show_axis_y"] - space.overlay.show_axis_z = style["space.overlay.show_axis_z"] - space.overlay.show_object_origins = style["space.overlay.show_object_origins"] - space.overlay.show_relationship_lines = style["space.overlay.show_relationship_lines"] - - def set_query(self): - self.selector = ifcopenshell.util.selector.Selector() - self.include_global_ids = [] - self.exclude_global_ids = [] - for ifc_file in bpy.context.scene.DocProperties.ifc_files: - try: - ifc = ifcopenshell.open(ifc_file.name) - except: - continue - if self.drawing_style.include_query: - results = self.selector.parse(ifc, self.drawing_style.include_query) - self.include_global_ids.extend([e.GlobalId for e in results]) - if self.drawing_style.exclude_query: - results = self.selector.parse(ifc, self.drawing_style.exclude_query) - self.exclude_global_ids.extend([e.GlobalId for e in results]) - if self.drawing_style.include_query: - self.parse_filter_query("INCLUDE") - if self.drawing_style.exclude_query: - self.parse_filter_query("EXCLUDE") - - def parse_filter_query(self, mode): - if mode == "INCLUDE": - objects = bpy.context.scene.objects - elif mode == "EXCLUDE": - objects = bpy.context.visible_objects - for obj in objects: - if mode == "INCLUDE": - obj.hide_viewport = False # Note: this breaks alt-H - global_id = obj.BIMObjectProperties.attributes.get("GlobalId") - if not global_id: - continue - global_id = global_id.string_value - if mode == "INCLUDE": - if global_id not in self.include_global_ids: - obj.hide_viewport = True # Note: this breaks alt-H - elif mode == "EXCLUDE": - if global_id in self.exclude_global_ids: - obj.hide_viewport = True # Note: this breaks alt-H - - 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 RemoveDrawing(bpy.types.Operator): - bl_idname = "bim.remove_drawing" - bl_label = "Remove Drawing" - index: bpy.props.IntProperty() - - def execute(self, context): - props = bpy.context.scene.DocProperties - camera = props.drawings[self.index].camera - collection = camera.users_collection[0] - for obj in collection.objects: - bpy.data.objects.remove(obj) - bpy.data.collections.remove(collection, do_unlink=True) - props.drawings.remove(self.index) - return {"FINISHED"} - - -class EditVectorStyle(bpy.types.Operator): - bl_idname = "bim.edit_vector_style" - bl_label = "Edit Vector Style" - - def execute(self, context): - camera = context.scene.camera - vector_style = context.scene.DocProperties.drawing_styles[ - camera.data.BIMCameraProperties.active_drawing_style_index - ].vector_style - bpy.data.texts.load(os.path.join(context.scene.BIMProperties.data_dir, "styles", vector_style + ".css")) - return {"FINISHED"} - - -class RemoveSheet(bpy.types.Operator): - bl_idname = "bim.remove_sheet" - bl_label = "Remove Sheet" - index: bpy.props.IntProperty() - - def execute(self, context): - props = bpy.context.scene.DocProperties - props.sheets.remove(self.index) - return {"FINISHED"} - - -class AddSchedule(bpy.types.Operator): - bl_idname = "bim.add_schedule" - bl_label = "Add Schedule" - - def execute(self, context): - new = bpy.context.scene.DocProperties.schedules.add() - new.name = "SCHEDULE {}".format(len(bpy.context.scene.DocProperties.schedules)) - return {"FINISHED"} - - -class RemoveSchedule(bpy.types.Operator): - bl_idname = "bim.remove_schedule" - bl_label = "Remove Schedule" - index: bpy.props.IntProperty() - - def execute(self, context): - props = bpy.context.scene.DocProperties - props.schedules.remove(self.index) - return {"FINISHED"} - - -class SelectScheduleFile(bpy.types.Operator): - bl_idname = "bim.select_schedule_file" - bl_label = "Select Documentation IFC File" - filepath: bpy.props.StringProperty(subtype="FILE_PATH") - filter_glob: bpy.props.StringProperty(default="*.ods", options={"HIDDEN"}) - index: bpy.props.IntProperty() - - def execute(self, context): - props = bpy.context.scene.DocProperties - props.schedules[props.active_schedule_index].file = self.filepath - return {"FINISHED"} - - def invoke(self, context, event): - context.window_manager.fileselect_add(self) - return {"RUNNING_MODAL"} - - -class BuildSchedule(bpy.types.Operator): - bl_idname = "bim.build_schedule" - bl_label = "Build Schedule" - - def execute(self, context): - props = bpy.context.scene.DocProperties - schedule = props.schedules[props.active_schedule_index] - schedule_creator = scheduler.Scheduler() - outfile = os.path.join(bpy.context.scene.BIMProperties.data_dir, "schedules", schedule.name + ".svg") - schedule_creator.schedule(schedule.file, outfile) - open_with_user_command(bpy.context.preferences.addons["blenderbim"].preferences.svg_command, outfile) - return {"FINISHED"} - - -class AddScheduleToSheet(bpy.types.Operator): - bl_idname = "bim.add_schedule_to_sheet" - bl_label = "Add Schedule To Sheet" - - def execute(self, context): - props = bpy.context.scene.DocProperties - sheet_builder = sheeter.SheetBuilder() - sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir - sheet_builder.add_schedule( - props.schedules[props.active_schedule_index].name, props.sheets[props.active_sheet_index].name - ) - return {"FINISHED"} - - class SetViewportShadowFromSun(bpy.types.Operator): bl_idname = "bim.set_viewport_shadow_from_sun" bl_label = "Set Viewport Shadow from Sun" @@ -1394,61 +591,6 @@ class SetViewportShadowFromSun(bpy.types.Operator): return {"FINISHED"} -class AddDrawingStyleAttribute(bpy.types.Operator): - bl_idname = "bim.add_drawing_style_attribute" - bl_label = "Add Drawing Style Attribute" - - def execute(self, context): - props = bpy.context.scene.camera.data.BIMCameraProperties - context.scene.DocProperties.drawing_styles[props.active_drawing_style_index].attributes.add() - return {"FINISHED"} - - -class RemoveDrawingStyleAttribute(bpy.types.Operator): - bl_idname = "bim.remove_drawing_style_attribute" - bl_label = "Remove Drawing Style Attribute" - index: bpy.props.IntProperty() - - def execute(self, context): - props = bpy.context.scene.camera.data.BIMCameraProperties - context.scene.DocProperties.drawing_styles[props.active_drawing_style_index].attributes.remove(self.index) - return {"FINISHED"} - - -class RefreshDrawingList(bpy.types.Operator): - bl_idname = "bim.refresh_drawing_list" - bl_label = "Refresh Drawing List" - - def execute(self, context): - while len(bpy.context.scene.DocProperties.drawings) > 0: - bpy.context.scene.DocProperties.drawings.remove(0) - for obj in bpy.context.scene.objects: - if not isinstance(obj.data, bpy.types.Camera): - continue - if "IfcAnnotation/" in obj.name and obj.users_collection[0].name == obj.name: - new = bpy.context.scene.DocProperties.drawings.add() - new.name = obj.name.split("/")[1] - new.camera = obj - return {"FINISHED"} - - -class CleanWireframes(bpy.types.Operator): - bl_idname = "bim.clean_wireframes" - bl_label = "Clean Wireframes" - - def execute(self, context): - objects = bpy.data.objects - if bpy.context.selected_objects: - objects = bpy.context.selected_objects - for obj in objects: - if not isinstance(obj.data, bpy.types.Mesh): - continue - # TODO: probably breaks i18n - if not obj.modifiers.get("EdgeSplit"): - obj.modifiers.new("EdgeSplit", "EDGE_SPLIT") - return {"FINISHED"} - - class LinkIfc(bpy.types.Operator): bl_idname = "bim.link_ifc" bl_label = "Link IFC" @@ -1516,135 +658,3 @@ class SnapSpacesTogether(bpy.types.Operator): for v in polygon.vertices: obj.data.vertices[v].co += offset return {"FINISHED"} - - -class CopyGrid(bpy.types.Operator): - bl_idname = "bim.add_grid" - bl_label = "Add Grid" - bl_options = {"UNDO"} - - def execute(self, context): - proj_coll = helper.get_project_collection(context.scene) - view_coll, camera = helper.get_active_drawing(context.scene) - if view_coll is None: - return {'CANCELLED'} - is_ortho = camera.data.type == "ORTHO" - bounds = helper.ortho_view_frame(camera.data) if is_ortho else None - clipping = is_ortho and camera.data.BIMCameraProperties.target_view in ('PLAN_VIEW', 'REFLECTED_PLAN_VIEW') - elevating = is_ortho and camera.data.BIMCameraProperties.target_view in ('ELEVATION_VIEW', 'SECTION_VIEW') - - def grep(coll): - return [obj for obj in coll.all_objects if obj.name.startswith("IfcGridAxis")] - - def clone(src): - dst = src.copy() - dst.data = dst.data.copy() - return dst - - def disassemble(obj): - mesh = bmesh.new() - mesh.verts.ensure_lookup_table() - mesh.from_mesh(obj.data) - return obj, mesh - - def assemble(obj, mesh): - mesh.to_mesh(obj.data) - return obj - - def localize(obj, mesh): - # convert to camera coords - mesh.transform(camera.matrix_world.inverted() @ obj.matrix_world) - obj.matrix_world = camera.matrix_world - return obj, mesh - - def clip(mesh): - # clip segment against camera view bounds - mesh.verts.ensure_lookup_table() - points = [v.co for v in mesh.verts[0:2]] - points = helper.clip_segment(bounds, points) - if points is None: - return None - mesh.verts[0].co = points[0] - mesh.verts[1].co = points[1] - return mesh - - def elev(mesh): - # put camera-perpendicular segments vertically - mesh.verts.ensure_lookup_table() - points = [v.co for v in mesh.verts[0:2]] - points = helper.elevate_segment(bounds, points) - if points is None: - return None - points = helper.clip_segment(bounds, points) - if points is None: - return None - mesh.verts[0].co = points[0] - mesh.verts[1].co = points[1] - return mesh - - for obj in grep(view_coll): - view_coll.objects.unlink(obj) - - grid = [localize(*disassemble(clone(obj))) for obj in grep(proj_coll)] - - if clipping: - grid = [(obj, clip(mesh)) for obj, mesh in grid] - elif elevating: - grid = [(obj, elev(mesh)) for obj, mesh in grid] - - for obj, mesh in grid: - if mesh is not None: - view_coll.objects.link(assemble(obj, mesh)) - - return {"FINISHED"} - - -class AddSectionsAnnotations(bpy.types.Operator): - bl_idname = "bim.add_sections_annotations" - bl_label = "Add Sections" - bl_options = {"UNDO"} - - def execute(self, context): - scene = context.scene - view_coll, camera = helper.get_active_drawing(scene) - is_ortho = camera.data.type == "ORTHO" - if not is_ortho: - return {'CANCELLED'} - bounds = helper.ortho_view_frame(camera.data) if is_ortho else None - - drawings = [d - for d in scene.DocProperties.drawings - if (d.camera is not camera and - d.camera.data.type == "ORTHO" and - d.camera.data.BIMCameraProperties.target_view == 'SECTION_VIEW')] - - def sideview(cam): - # leftmost and righmost points of camera view area, local coords - xmin, xmax, _, _, _, _ = helper.ortho_view_frame(cam.data, margin=0) - proj = camera.matrix_world.inverted() @ cam.matrix_world - p_l = proj @ Vector((xmin, 0, 0)) - p_r = proj @ Vector((xmax, 0, 0)) - return helper.clip_segment(bounds, (p_l, p_r)) - - def annotation(drawing, points): - # object with path geometry - name = drawing.name - curve = bpy.data.curves.new(f"Section/{name}", 'CURVE') - spline = curve.splines.new('POLY') # has 1 initial point - spline.points.add(1) - p1, p2 = points - z = bounds[4] - 1e-5 # zmin - spline.points[0].co = (p1.x, p1.y, z, 1) - spline.points[1].co = (p2.x, p2.y, z, 1) - obj = bpy.data.objects.new(f"IfcAnnotation/Section/{name}", curve) - obj.matrix_world = camera.matrix_world - return obj - - for d in drawings: - points = sideview(d.camera) - if points is None: - continue - obj = annotation(d, points) - view_coll.objects.link(obj) - - return {'FINISHED'} diff --git a/src/blenderbim/blenderbim/bim/ui.py b/src/blenderbim/blenderbim/bim/ui.py index c156781431..6242224e23 100644 --- a/src/blenderbim/blenderbim/bim/ui.py +++ b/src/blenderbim/blenderbim/bim/ui.py @@ -5,93 +5,6 @@ from bpy.types import Panel from bpy.props import StringProperty, BoolProperty -class BIM_PT_drawings(Panel): - bl_label = "SVG Drawings" - bl_idname = "BIM_PT_drawings" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "output" - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - props = bpy.context.scene.DocProperties - - row = layout.row(align=True) - row.operator("bim.add_drawing") - row.operator("bim.refresh_drawing_list", icon="FILE_REFRESH", text="") - - if props.drawings: - if props.active_drawing_index < len(props.drawings): - op = row.operator("bim.open_view", icon="URL", text="") - op.view = props.drawings[props.active_drawing_index].name - row.operator("bim.remove_drawing", icon="X", text="").index = props.active_drawing_index - layout.template_list("BIM_UL_generic", "", props, "drawings", props, "active_drawing_index") - - row = layout.row() - row.operator("bim.add_ifc_file") - - for index, ifc_file in enumerate(props.ifc_files): - row = layout.row(align=True) - row.prop(ifc_file, "name", text="IFC #{}".format(index + 1)) - row.operator("bim.select_doc_ifc_file", icon="FILE_FOLDER", text="").index = index - row.operator("bim.remove_ifc_file", icon="X", text="").index = index - - -class BIM_PT_schedules(Panel): - bl_label = "ODS Schedules" - bl_idname = "BIM_PT_schedules" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "output" - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - props = bpy.context.scene.DocProperties - - row = layout.row(align=True) - row.operator("bim.add_schedule") - - if props.schedules: - row.operator("bim.build_schedule", icon="LINENUMBERS_ON", text="") - row.operator("bim.remove_schedule", icon="X", text="").index = props.active_schedule_index - - layout.template_list("BIM_UL_generic", "", props, "schedules", props, "active_schedule_index") - - row = layout.row() - row.prop(props.schedules[props.active_schedule_index], "file") - row.operator("bim.select_schedule_file", icon="FILE_FOLDER", text="") - - -class BIM_PT_sheets(Panel): - bl_label = "SVG Sheets" - bl_idname = "BIM_PT_sheets" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "output" - - def draw(self, context): - layout = self.layout - props = bpy.context.scene.DocProperties - - row = layout.row(align=True) - row.prop(props, "titleblock", text="") - row.operator("bim.add_sheet") - - if props.sheets: - row.operator("bim.open_sheet", icon="URL", text="") - row.operator("bim.remove_sheet", icon="X", text="").index = props.active_sheet_index - - layout.template_list("BIM_UL_generic", "", props, "sheets", props, "active_sheet_index") - - row = layout.row(align=True) - row.operator("bim.add_drawing_to_sheet") - row.operator("bim.add_schedule_to_sheet") - row = layout.row() - row.operator("bim.create_sheets") - - class BIM_PT_section_plane(Panel): bl_label = "Temporary Section Cutaways" bl_idname = "BIM_PT_section_plane" @@ -115,43 +28,6 @@ class BIM_PT_section_plane(Panel): row.operator("bim.remove_section_plane") -class BIM_PT_text(Panel): - bl_label = "Text Paper Space" - bl_idname = "BIM_PT_text" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "data" - - @classmethod - def poll(cls, context): - return type(context.curve) is bpy.types.TextCurve - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - props = context.active_object.data.BIMTextProperties - - row = layout.row() - row.operator("bim.propagate_text_data") - - row = layout.row() - row.prop(props, "font_size") - row = layout.row() - row.prop(props, "symbol") - row = layout.row() - row.prop(props, "related_element") - - row = layout.row() - row.operator("bim.add_variable") - - for index, variable in enumerate(props.variables): - row = layout.row(align=True) - row.prop(variable, "name") - row.operator("bim.remove_variable", icon="X", text="").index = index - row = layout.row() - row.prop(variable, "prop_key") - - class BIM_UL_generic(bpy.types.UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): if item: @@ -160,17 +36,6 @@ class BIM_UL_generic(bpy.types.UIList): layout.label(text="", translate=False) -class BIM_UL_drawinglist(bpy.types.UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname): - if item: - row = layout.row(align=True) - row.prop(item, "name", text="", emboss=False) - op = row.operator("bim.open_view_camera", icon="OUTLINER_OB_CAMERA", text="") - op.view_name = item.name - else: - layout.label(text="", translate=False) - - class BIM_UL_topics(bpy.types.UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): ob = data @@ -224,81 +89,6 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences): row.prop(self, "should_hide_empty_props") -class BIM_PT_annotation_utilities(Panel): - bl_idname = "BIM_PT_annotation_utilities" - bl_label = "Annotation" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" - bl_category = "BlenderBIM" - - def draw(self, context): - layout = self.layout - - row = layout.row(align=True) - row.operator("bim.clean_wireframes") - row = layout.row(align=True) - row.operator("bim.link_ifc") - row = layout.row(align=True) - row.operator("bim.add_grid") - row = layout.row(align=True) - row.operator("bim.add_sections_annotations") - - row = layout.row(align=True) - op = row.operator("bim.add_annotation", text="Dim", icon="ARROW_LEFTRIGHT") - op.obj_name = "Dimension" - op.data_type = "curve" - op = row.operator("bim.add_annotation", text="Dim (Eq)", icon="ARROW_LEFTRIGHT") - op.obj_name = "Equal" - op.data_type = "curve" - - row = layout.row(align=True) - op = row.operator("bim.add_annotation", text="Text", icon="SMALL_CAPS") - op.data_type = "text" - op = row.operator("bim.add_annotation", text="Leader", icon="TRACKING_BACKWARDS") - op.obj_name = "Leader" - op.data_type = "curve" - - row = layout.row(align=True) - op = row.operator("bim.add_annotation", text="Stair Arrow", icon="SCREEN_BACK") - op.obj_name = "Stair" - op.data_type = "curve" - op = row.operator("bim.add_annotation", text="Hidden", icon="CON_TRACKTO") - op.obj_name = "Hidden" - op.data_type = "mesh" - - row = layout.row(align=True) - op = row.operator("bim.add_annotation", text="Level (Plan)", icon="SORTBYEXT") - op.obj_name = "Plan Level" - op.data_type = "curve" - op = row.operator("bim.add_annotation", text="Level (Section)", icon="TRIA_DOWN") - op.obj_name = "Section Level" - op.data_type = "curve" - - row = layout.row(align=True) - op = row.operator("bim.add_annotation", text="Breakline", icon="FCURVE") - op.obj_name = "Break" - op.data_type = "mesh" - op = row.operator("bim.add_annotation", text="Misc", icon="MESH_MONKEY") - op.obj_name = "Misc" - op.data_type = "mesh" - - props = bpy.context.scene.DocProperties - - row = layout.row(align=True) - row.operator("bim.add_drawing") - row.operator("bim.refresh_drawing_list", icon="FILE_REFRESH", text="") - - if props.drawings: - if props.active_drawing_index < len(props.drawings): - op = row.operator("bim.open_view", icon="URL", text="") - op.view = props.drawings[props.active_drawing_index].name - row.operator("bim.remove_drawing", icon="X", text="").index = props.active_drawing_index - layout.template_list("BIM_UL_drawinglist", "", props, "drawings", props, "active_drawing_index") - - layout.prop(props, "should_draw_decorations") - layout.prop(props, "decorations_colour") - - class BIM_PT_misc_utilities(Panel): bl_idname = "BIM_PT_misc_utilities" bl_label = "Miscellaneous"