diff --git a/src/blenderbim/blenderbim/bim/module/model/__init__.py b/src/blenderbim/blenderbim/bim/module/model/__init__.py index 8c9330465b..f8217c8313 100644 --- a/src/blenderbim/blenderbim/bim/module/model/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/model/__init__.py @@ -117,7 +117,8 @@ classes = ( space.ToggleHideSpaces, mep.FitFlowSegments, mep.RegenerateDistributionElement, - prop.SnapVertex, + prop.SnapMousePoint, + prop.PolylinePoint, prop.BIMModelProperties, prop.BIMArrayProperties, prop.BIMStairProperties, diff --git a/src/blenderbim/blenderbim/bim/module/model/decorator.py b/src/blenderbim/blenderbim/bim/module/model/decorator.py index c6703bd500..0458881737 100644 --- a/src/blenderbim/blenderbim/bim/module/model/decorator.py +++ b/src/blenderbim/blenderbim/bim/module/model/decorator.py @@ -337,9 +337,26 @@ class WallPolylineDecorator: self.shader = gpu.shader.from_builtin("UNIFORM_COLOR") gpu.state.point_size_set(10) - snap_prop = context.scene.BIMModelProperties.snap_vertex[0] - points = [Vector((snap_prop.x, snap_prop.y, snap_prop.z))] + snap_prop = context.scene.BIMModelProperties.snap_mouse_point[0] + point = [Vector((snap_prop.x, snap_prop.y, snap_prop.z))] if snap_prop.snap_type in ["Face", "Plane"]: - self.draw_batch("POINTS", points, decorator_color_special) + self.draw_batch("POINTS", point, decorator_color_special) else: - self.draw_batch("POINTS", points, decorator_color_selected) + self.draw_batch("POINTS", point, decorator_color_selected) + + polyline_data = context.scene.BIMModelProperties.polyline_point + polyline_points = [] + polyline_edges = [] + for point_prop in polyline_data: + point = Vector((point_prop.x, point_prop.y, point_prop.z)) + polyline_points.append(point) + + for i in range(len(polyline_points) - 1): + polyline_edges.append([i, i+1]) + + self.draw_batch("POINTS", polyline_points, decorator_color_selected) + if len(polyline_points) > 1: + self.draw_batch("LINES", polyline_points, decorator_color_selected, polyline_edges) + + self.line_shader.uniform_float("lineWidth", 2.0) + diff --git a/src/blenderbim/blenderbim/bim/module/model/prop.py b/src/blenderbim/blenderbim/bim/module/model/prop.py index 2318c529c5..ee806596e3 100644 --- a/src/blenderbim/blenderbim/bim/module/model/prop.py +++ b/src/blenderbim/blenderbim/bim/module/model/prop.py @@ -99,11 +99,16 @@ def is_object_array_applicable(self, obj): return ifcopenshell.util.element.get_pset(element, "BBIM_Array") -class SnapVertex(PropertyGroup): +class SnapMousePoint(PropertyGroup): x: bpy.props.FloatProperty(name="X") y: bpy.props.FloatProperty(name="Y") z: bpy.props.FloatProperty(name="Z") snap_type: bpy.props.StringProperty(name="Snap Type") + +class PolylinePoint(PropertyGroup): + x: bpy.props.FloatProperty(name="X") + y: bpy.props.FloatProperty(name="Y") + z: bpy.props.FloatProperty(name="Z") class BIMModelProperties(PropertyGroup): @@ -197,7 +202,8 @@ class BIMModelProperties(PropertyGroup): type_predefined_type: bpy.props.EnumProperty(items=get_type_predefined_type, name="Predefined Type", default=None) type_name: bpy.props.StringProperty(name="Name", default="TYPEX") boundary_class: bpy.props.EnumProperty(items=get_boundary_class, name="Boundary Class") - snap_vertex: bpy.props.CollectionProperty(type=SnapVertex) + snap_mouse_point: bpy.props.CollectionProperty(type=SnapMousePoint) + polyline_point: bpy.props.CollectionProperty(type=PolylinePoint) class BIMArrayProperties(PropertyGroup): diff --git a/src/blenderbim/blenderbim/bim/module/model/wall.py b/src/blenderbim/blenderbim/bim/module/model/wall.py index 6af355a5b4..1cc60eb7dd 100644 --- a/src/blenderbim/blenderbim/bim/module/model/wall.py +++ b/src/blenderbim/blenderbim/bim/module/model/wall.py @@ -338,7 +338,7 @@ class DrawPolylineWall(bpy.types.Operator): return (obj, bbox_2d) - def main(self, context, event): + def snaping_movement(self, context, event): scene = context.scene region = context.region rv3d = context.region_data @@ -506,35 +506,27 @@ class DrawPolylineWall(bpy.types.Operator): obj, hit, face_index = cast_rays_and_get_best_object() intersection = ray_cast_to_plane(context, plane_origin, plane_normal) - try: - snap_vertex = context.scene.BIMModelProperties.snap_vertex[0] - except: - snap_vertex = context.scene.BIMModelProperties.snap_vertex.add() if obj is not None: original_obj = obj.original - snap_points = get_snap_points(obj, face_index) - snap_point = select_snap_point(snap_points, hit, snap_threshold) + snap_points = tool.Snaping.get_snap_points_on_raycasted_obj(obj, face_index) + snap_point = tool.Snaping.select_snap_point(snap_points, hit, snap_threshold) if snap_point: - snap_vertex.x = snap_point[0][0] - snap_vertex.y = snap_point[0][1] - snap_vertex.z = snap_point[0][2] - snap_vertex.snap_type = snap_point[1] + tool.Snaping.update_snaping_point(snap_point[0], snap_point[1]) + else: - snap_vertex.x = hit[0] - snap_vertex.y = hit[1] - snap_vertex.z = hit[2] - snap_vertex.snap_type = "Face" + tool.Snaping.update_snaping_point(hit, "Face") else: - snap_vertex.x = intersection[0] - snap_vertex.y = intersection[1] - snap_vertex.z = intersection[2] - snap_vertex.snap_type = "Plane" + tool.Snaping.update_snaping_point(intersection, "Plane") + def draw_polyline(self, context): + tool.Snaping.insert_polyline_point() + + def modal(self, context, event): if event.type == 'MOUSEMOVE': @@ -551,16 +543,20 @@ class DrawPolylineWall(bpy.types.Operator): if self.mousemove_count > 3: start_time = time() - self.main(context, event) + self.snaping_movement(context, event) operator_time = time() - start_time print(f"main function was finished in {operator_time:.2f} seconds") return {'RUNNING_MODAL'} + + if event.type == 'LEFTMOUSE': + self.draw_polyline(context) if event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}: return {'PASS_THROUGH'} if event.type in {'RIGHTMOUSE', 'ESC'}: WallPolylineDecorator.uninstall() + tool.Snaping.clear_polyline() tool.Blender.update_viewport() print("CANCELLED") return {'CANCELLED'} diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py index 2eb783a9d8..d19f460566 100644 --- a/src/blenderbim/blenderbim/core/tool.py +++ b/src/blenderbim/blenderbim/core/tool.py @@ -934,6 +934,10 @@ class Spatial: class Covering: def get_z_from_ceiling_height(cls): pass +@interface +class Snaping: + pass + @interface class Structural: def disable_editing_structural_analysis_model(cls): pass diff --git a/src/blenderbim/blenderbim/tool/__init__.py b/src/blenderbim/blenderbim/tool/__init__.py index 3f80a685bc..097b2059d3 100644 --- a/src/blenderbim/blenderbim/tool/__init__.py +++ b/src/blenderbim/blenderbim/tool/__init__.py @@ -50,6 +50,7 @@ from blenderbim.tool.qto import Qto from blenderbim.tool.resource import Resource from blenderbim.tool.root import Root from blenderbim.tool.sequence import Sequence +from blenderbim.tool.snaping import Snaping from blenderbim.tool.spatial import Spatial from blenderbim.tool.covering import Covering from blenderbim.tool.structural import Structural diff --git a/src/blenderbim/blenderbim/tool/snaping.py b/src/blenderbim/blenderbim/tool/snaping.py new file mode 100644 index 0000000000..ab8844c2be --- /dev/null +++ b/src/blenderbim/blenderbim/tool/snaping.py @@ -0,0 +1,99 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2022 Cyril Waechter +# +# This file is part of BlenderBIM Add-on. +# +# BlenderBIM Add-on is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BlenderBIM Add-on is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BlenderBIM Add-on. If not, see . + +import bpy +import blenderbim.core.tool +import mathutils +from mathutils import Matrix, Vector + + +class Snaping(blenderbim.core.tool.Snaping): + @classmethod + def get_snap_points_on_raycasted_obj(cls, obj, face_index): + snap_points = {} + matrix = obj.matrix_world.copy() + face = obj.data.polygons[face_index] + vertices = [] + for i in face.vertices: + vertices.append(matrix @ obj.data.vertices[i].co) + + edges_center = [] + for v1, v2 in zip(vertices, vertices[1:] + [vertices[0]]): + middle_dist = (v2-v1) / 2 + edges_center.append(v1 + middle_dist) + + snap_points.update({tuple(vertex): "Vertices" for vertex in vertices}) + snap_points.update({tuple(edge_center): "Edge Center" for edge_center in edges_center}) + + return snap_points + + @classmethod + def select_snap_point(cls, snap_points, hit, threshold): + shortest_distance = None + snap_point = None + for point, snap_type in snap_points.items(): + point = Vector(point) + distance = (point - hit).length + if distance > threshold: + continue + if shortest_distance and distance < shortest_distance: + shortest_distance = distance + snap_point = (point, snap_type) + + elif not shortest_distance: + shortest_distance = distance + snap_point = (point, snap_type) + else: + pass + + return snap_point + + @classmethod + def update_snaping_point(cls, snap_point, snap_type): + try: + snap_vertex = bpy.context.scene.BIMModelProperties.snap_mouse_point[0] + except: + snap_vertex = bpy.context.scene.BIMModelProperties.snap_mouse_point.add() + + snap_vertex.x = snap_point[0] + snap_vertex.y = snap_point[1] + snap_vertex.z = snap_point[2] + snap_vertex.snap_type = snap_type + + @classmethod + def insert_polyline_point(cls): + snap_vertex = bpy.context.scene.BIMModelProperties.snap_mouse_point[0] + polyline_data = bpy.context.scene.BIMModelProperties.polyline_point + print(len(polyline_data)) + if len(polyline_data) > 0: + print("X", snap_vertex.x, polyline_data[-1].x) + if snap_vertex.x == polyline_data[-1].x and\ + snap_vertex.y == polyline_data[-1].y and\ + snap_vertex.z == polyline_data[-1].z: + return + + polyline_point = bpy.context.scene.BIMModelProperties.polyline_point.add() + polyline_point.x = snap_vertex.x + polyline_point.y = snap_vertex.y + polyline_point.z = 0 # TODO Update to the the default container height + + @classmethod + def clear_polyline(cls): + bpy.context.scene.BIMModelProperties.polyline_point.clear() + +