From b13719758695e8cc40dc7164a9c23854e9dca6c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Perdig=C3=A3o?= Date: Tue, 13 Aug 2024 17:32:57 -0300 Subject: [PATCH] Changed input validantion to Snap tool --- .../bonsai/bim/module/model/decorator.py | 42 ++++++++++++++-- src/bonsai/bonsai/bim/module/model/wall.py | 14 +++--- src/bonsai/bonsai/tool/snap.py | 50 +++++++++++++++++++ 3 files changed, 94 insertions(+), 12 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/model/decorator.py b/src/bonsai/bonsai/bim/module/model/decorator.py index 4884879ed3..08f3155d5e 100644 --- a/src/bonsai/bonsai/bim/module/model/decorator.py +++ b/src/bonsai/bonsai/bim/module/model/decorator.py @@ -21,7 +21,7 @@ import gpu import gpu_extras import bmesh import bonsai.tool as tool -from math import sin, cos, radians +from math import sin, cos, radians, degrees, atan2, acos from bpy.types import SpaceView3D from mathutils import Vector, Matrix from gpu_extras.batch import batch_for_shader @@ -343,12 +343,22 @@ class WallPolylineDecorator: @classmethod def calculate_distance_and_angle(cls, context, is_input_on): + def calculate_angle(snap_vector, last_point, second_to_last_point): + v1 = snap_vector - last_point + v2 = last_point - second_to_last_point + cos_angle = (v1.dot(v2)) / (v1.length * v2.length) + if cos_angle > 1: + cos_angle = 1 + angle = acos(cos_angle) + return degrees(angle) + try: polyline_data = context.scene.BIMModelProperties.polyline_point default_container_elevation = tool.Ifc.get_object(tool.Root.get_default_container()).location.z last_point_data = polyline_data[len(polyline_data) - 1] except: last_point_data = None + second_to_last_point_data = None default_container_elevation = 0 snap_prop = context.scene.BIMModelProperties.snap_mouse_point[0] @@ -358,6 +368,11 @@ class WallPolylineDecorator: else: last_point = Vector((0, 0, 0)) + second_to_last_point = None + if len(polyline_data) > 1: + second_to_last_point_data = polyline_data[len(polyline_data) - 2] + second_to_last_point = Vector((second_to_last_point_data.x, second_to_last_point_data.y, second_to_last_point_data.z)) + if is_input_on: snap_vector = Vector( (float(cls.input_panel["X"]), float(cls.input_panel["Y"]), default_container_elevation) @@ -373,12 +388,22 @@ class WallPolylineDecorator: Vector((0, 0)), Vector((1, 0)), ) + if second_to_last_point: + previous_axis = ( + Vector((second_to_last_point.x, second_to_last_point.y)), + Vector((last_point.x, last_point.y)), + ) + else: + second_to_last_point = Vector((last_point.x + 10, last_point.y, last_point.z)) + previous_axis = x_axis_edge + current_axis = ( Vector((last_point.x, last_point.y)), Vector((snap_vector.x, snap_vector.y)), ) if distance > 0: - angle = tool.Cad.angle_edges(x_axis_edge, current_axis, degrees=True, signed=True) + angle = tool.Cad.angle_3_vectors(snap_vector, last_point, second_to_last_point, degrees=True) + angle = 180 - angle if cls.input_panel: cls.input_panel["X"] = str(round(snap_vector.x, 4)) cls.input_panel["Y"] = str(round(snap_vector.y, 4)) @@ -400,11 +425,20 @@ class WallPolylineDecorator: return last_point = Vector((last_point_data.x, last_point_data.y, last_point_data.z)) + second_to_last_point = None + if len(polyline_data) > 2: + second_to_last_point_data = polyline_data[len(polyline_data) - 2] + second_to_last_point = Vector((second_to_last_point_data.x, second_to_last_point_data.y, second_to_last_point_data.z)) + else: + second_to_last_point = Vector((0, 0, 0)) + distance = float(cls.input_panel["D"]) if distance < 0 or distance > 0: angle_degrees = float(cls.input_panel["A"]) - angle_radians = radians(360 - angle_degrees) # Substracting from 360 to make it clockwise + reference_vector = second_to_last_point - last_point + reference_angle = degrees(atan2(reference_vector[1], reference_vector[0])) + angle_radians = radians(360 - (angle_degrees - reference_angle)) # Substracting from 360 to make it clockwise x = last_point[0] + distance * cos(angle_radians) y = last_point[1] + distance * sin(angle_radians) if cls.input_panel: @@ -503,11 +537,9 @@ class WallPolylineDecorator: # Line between last polyline point and mouse edges = [[0, 1]] if polyline_points: - print("PP", polyline_points) if snap_prop.snap_type != "Plane" and projection_point: self.draw_batch("LINES", [polyline_points[-1]] + projection_point, decorator_color_unselected, edges) else: - print("FOI") self.draw_batch("LINES", [polyline_points[-1]] + mouse_point, decorator_color_unselected, edges) # Line for angle axis snap diff --git a/src/bonsai/bonsai/bim/module/model/wall.py b/src/bonsai/bonsai/bim/module/model/wall.py index 8ecfe74e76..e2032a3b9e 100644 --- a/src/bonsai/bonsai/bim/module/model/wall.py +++ b/src/bonsai/bonsai/bim/module/model/wall.py @@ -285,7 +285,6 @@ class DrawPolylineWall(bpy.types.Operator): bl_label = "Draw Polyline Wall" bl_options = {"REGISTER", "UNDO"} - @classmethod def poll(cls, context): return context.space_data.type == "VIEW_3D" @@ -482,14 +481,18 @@ class DrawPolylineWall(bpy.types.Operator): def recalculate_inputs(self, context): if self.number_input: - is_valid, self.number_output = self.validate_input(self.number_output) + is_valid, self.number_output = tool.Snap.validate_input(self.number_output) self.input_panel[self.input_type] = self.number_output - if is_valid: + if not is_valid: + self.report({"WARNING"}, "The number typed is not valid.") + else: if self.input_type in {"X", "Y"}: self.input_panel = WallPolylineDecorator.calculate_distance_and_angle(context, self.is_input_on) elif self.input_type in {"D", "A"}: self.input_panel = WallPolylineDecorator.calculate_x_and_y(context) + self.input_panel[self.input_type] = self.number_output + WallPolylineDecorator.set_input_panel(self.input_panel, self.input_type) tool.Blender.update_viewport() @@ -509,15 +512,12 @@ class DrawPolylineWall(bpy.types.Operator): relating_type = tool.Ifc.get().by_id(int(relating_type_id)) walls, is_polyline_closed = DumbWallGenerator(relating_type).generate(True) - print(is_polyline_closed) if walls: if is_polyline_closed: for wall1, wall2 in zip(walls, walls[1:] + [walls[0]]): - print(wall1["obj"], wall2["obj"]) DumbWallJoiner().join_V(wall1["obj"], wall2["obj"]) else: for wall1, wall2 in zip(walls[:-1], walls[1:]): - print(wall1["obj"], wall2["obj"]) DumbWallJoiner().join_V(wall1["obj"], wall2["obj"]) def modal(self, context, event): @@ -660,7 +660,7 @@ class DrawPolylineWall(bpy.types.Operator): WallPolylineDecorator.install(context) tool.Snap.set_use_default_container(True) WallPolylineDecorator.set_use_default_container(True) - tool.Snap.set_snap_plane_method('XY') + tool.Snap.set_snap_plane_method("XY") WallPolylineDecorator.set_input_panel(self.input_panel, self.input_type) self.visible_objs = tool.Raycast.get_visible_objects(context) for obj in self.visible_objs: diff --git a/src/bonsai/bonsai/tool/snap.py b/src/bonsai/bonsai/tool/snap.py index c20dbcee67..a9534091c5 100644 --- a/src/bonsai/bonsai/tool/snap.py +++ b/src/bonsai/bonsai/tool/snap.py @@ -23,6 +23,7 @@ from bonsai.bim.module.model.decorator import WallPolylineDecorator import math import mathutils from mathutils import Matrix, Vector +from lark import Lark, Transformer class Snap(bonsai.core.tool.Snap): @@ -327,3 +328,52 @@ class Snap(bonsai.core.tool.Snap): cls.update_snaping_point(rot_intersection, "Axis") else: cls.update_snaping_point(intersection, "Plane") + + + @classmethod + def validate_input(cls, input_number): + grammar = """ + start: dim expr? + dim: NUMBER + expr: (ADD | SUB | MUL | DIV) NUMBER + + NUMBER: /-?\\d+(?:\\.\\d+)?/ + ADD: "+" + SUB: "-" + MUL: "*" + DIV: "/" + + %ignore " " + """ + + class InputTransform(Transformer): + def dim(self, args): + return float(args[0]) + + def expr(self, args): + op = args[0] + value = float(args[1]) + if op == "+": + return lambda x: x + value + elif op == "-": + return lambda x: x - value + elif op == "*": + return lambda x: x * value + elif op == "/": + return lambda x: x / value + + def start(self, args): + dimension = args[0] + if len(args) > 1: + expression = args[1] + return expression(dimension) + else: + return dimension + + try: + parser = Lark(grammar, parser="lalr", transformer=InputTransform()) + result = parser.parse(input_number) + return True, str(result) + except: + return False, "0" +