From 760267495330263a388bf95d77614bef0d6f531f Mon Sep 17 00:00:00 2001 From: BOTAlex Date: Mon, 26 Aug 2024 05:57:52 +0200 Subject: [PATCH 01/13] Fixed issue #426 This might be a patch for the problem, but it works --- operators/add_workplane.py | 1 + 1 file changed, 1 insertion(+) diff --git a/operators/add_workplane.py b/operators/add_workplane.py index 34cfeebd..fd1c5532 100644 --- a/operators/add_workplane.py +++ b/operators/add_workplane.py @@ -137,6 +137,7 @@ def main(self, context: Context): self.target = sse.add_workplane(origin, nm) ignore_hover(self.target) + [a.tag_redraw() for a in bpy.context.screen.areas] # Force re-draw of UI (Blender doesn't update after tool usage) return True From 4528eec2916f32eb6892243159ca885e7908b622 Mon Sep 17 00:00:00 2001 From: BOTAlex Date: Tue, 27 Aug 2024 23:09:35 +0200 Subject: [PATCH 02/13] Now less redraws, but still working --- operators/add_workplane.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operators/add_workplane.py b/operators/add_workplane.py index fd1c5532..0c006209 100644 --- a/operators/add_workplane.py +++ b/operators/add_workplane.py @@ -137,7 +137,7 @@ def main(self, context: Context): self.target = sse.add_workplane(origin, nm) ignore_hover(self.target) - [a.tag_redraw() for a in bpy.context.screen.areas] # Force re-draw of UI (Blender doesn't update after tool usage) + context.area.tag_redraw() # Force re-draw of UI (Blender doesn't update after tool usage) return True From ac6f2b6e90998fa9a40056a4bb713536fe043d6e Mon Sep 17 00:00:00 2001 From: BOTAlex Date: Wed, 28 Aug 2024 04:04:02 +0200 Subject: [PATCH 03/13] mesh to workplane projection now working Also used as a backup for me --- operators/add_workplane.py | 64 +++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/operators/add_workplane.py b/operators/add_workplane.py index 0c006209..fb2057ae 100644 --- a/operators/add_workplane.py +++ b/operators/add_workplane.py @@ -2,6 +2,7 @@ import bpy from bpy.types import Operator, Context +from mathutils import Vector from .. import global_data from ..model.types import SlvsNormal3D @@ -17,6 +18,7 @@ from .constants import types_point_3d from .utilities import ignore_hover from ..utilities.view import get_placement_pos +from .utilities import activate_sketch, switch_sketch_mode logger = logging.getLogger(__name__) @@ -123,21 +125,61 @@ class View3D_OT_slvs_add_workplane_face(Operator, Operator3d): def main(self, context: Context): sse = context.scene.sketcher.entities - ob_name, face_index = self.get_state_pointer(index=0, implicit=True) - ob = get_evaluated_obj(context, bpy.data.objects[ob_name]) - mesh = ob.data - face = mesh.polygons[face_index] - - mat_obj = ob.matrix_world - quat = get_face_orientation(mesh, face) - quat.rotate(mat_obj) - pos = mat_obj @ face.center - origin = sse.add_point_3d(pos) + obj_name, clicked_face_index = self.get_state_pointer(index=0, implicit=True) + clicked_obj = get_evaluated_obj(context, bpy.data.objects[obj_name]) + clicked_mesh = clicked_obj.data + clicked_face = clicked_mesh.polygons[clicked_face_index] + + obj_translation = clicked_obj.matrix_world + quat = get_face_orientation(clicked_mesh, clicked_face) # Quternion + quat.rotate(obj_translation) + + workplane_origin = obj_translation @ clicked_face.center + print("1: " + str(obj_translation)) + print("2: " + str(clicked_face)) + print("2.1: " + str(clicked_face.center)) + origin = sse.add_point_3d(workplane_origin) nm = sse.add_normal_3d(quat) self.target = sse.add_workplane(origin, nm) - ignore_hover(self.target) + context.area.tag_redraw() # Force re-draw of UI (Blender doesn't update after tool usage) + + meshes = [o for o in context.scene.objects if o.type == 'MESH'] + # print(meshes) + + # Workplane normal in world coordinates + workplane_normal = quat @ Vector((0.0, 0.0, 1.0)) + + sketch = sse.add_sketch(self.target) + p = sse.add_point_2d((0.0, 0.0), sketch) + p.fixed = True + + activate_sketch(context, sketch.slvs_index, self) + self.target = sketch + + for clicked_mesh in meshes: + vertices = clicked_mesh.data.vertices + for vertex in vertices: + # Make vertex relative to plane + vertex_world = obj_translation @ vertex.co + translated = vertex_world - workplane_origin + + # Projection to plane + distance_to_plane = translated.dot(workplane_normal) + projection = translated - distance_to_plane * workplane_normal + + ## Used ChatGPT, quaternion rotations is too hard. + # To 2D projection relative to the workplane + # Use the workplane orientation (quat) to project into 2D + local_projection = projection.copy() + local_projection.rotate(quat.conjugated()) + x, y, _ = local_projection + + p = sse.add_point_2d((x, y), sketch) + + + return True From 721eb48a418270d573d6f79c900a4339db0a9d7b Mon Sep 17 00:00:00 2001 From: BOTAlex Date: Wed, 28 Aug 2024 04:33:09 +0200 Subject: [PATCH 04/13] Added limiter and semi-colon for readability --- operators/add_workplane.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/operators/add_workplane.py b/operators/add_workplane.py index fb2057ae..08fb0bc2 100644 --- a/operators/add_workplane.py +++ b/operators/add_workplane.py @@ -152,31 +152,37 @@ def main(self, context: Context): workplane_normal = quat @ Vector((0.0, 0.0, 1.0)) sketch = sse.add_sketch(self.target) - p = sse.add_point_2d((0.0, 0.0), sketch) - p.fixed = True + p = sse.add_point_2d((0.0, 0.0), sketch, fixed = True) activate_sketch(context, sketch.slvs_index, self) self.target = sketch + limitDist = 0.005; + connectLines = True; + for clicked_mesh in meshes: - vertices = clicked_mesh.data.vertices + vertices = clicked_mesh.data.vertices; for vertex in vertices: # Make vertex relative to plane - vertex_world = obj_translation @ vertex.co - translated = vertex_world - workplane_origin + vertex_world = obj_translation @ vertex.co; + translated = vertex_world - workplane_origin; # Projection to plane - distance_to_plane = translated.dot(workplane_normal) - projection = translated - distance_to_plane * workplane_normal + distance_to_plane = translated.dot(workplane_normal); + projection = translated - distance_to_plane * workplane_normal; + + if abs(distance_to_plane) > limitDist: + continue; + print(f"Vertex {vertex.index} distance to plane: {abs(distance_to_plane)}"); ## Used ChatGPT, quaternion rotations is too hard. # To 2D projection relative to the workplane # Use the workplane orientation (quat) to project into 2D - local_projection = projection.copy() - local_projection.rotate(quat.conjugated()) - x, y, _ = local_projection + local_projection = projection.copy(); + local_projection.rotate(quat.conjugated()); + x, y, _ = local_projection; - p = sse.add_point_2d((x, y), sketch) + p = sse.add_point_2d((x, y), sketch, fixed = True); From f3dce910e95e1436dcf54f01f5a3f4b8164597c2 Mon Sep 17 00:00:00 2001 From: BOTAlex Date: Wed, 28 Aug 2024 10:53:46 +0200 Subject: [PATCH 05/13] backup sync --- operators/add_workplane.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/operators/add_workplane.py b/operators/add_workplane.py index 08fb0bc2..5f536b72 100644 --- a/operators/add_workplane.py +++ b/operators/add_workplane.py @@ -158,8 +158,9 @@ def main(self, context: Context): self.target = sketch limitDist = 0.005; - connectLines = True; + connectLines = True; # May cause performance issues. idk + addedPoints = {} for clicked_mesh in meshes: vertices = clicked_mesh.data.vertices; for vertex in vertices: @@ -173,7 +174,7 @@ def main(self, context: Context): if abs(distance_to_plane) > limitDist: continue; - print(f"Vertex {vertex.index} distance to plane: {abs(distance_to_plane)}"); + # print(f"Vertex {vertex.index} distance to plane: {abs(distance_to_plane)}"); ## Used ChatGPT, quaternion rotations is too hard. # To 2D projection relative to the workplane @@ -182,7 +183,24 @@ def main(self, context: Context): local_projection.rotate(quat.conjugated()); x, y, _ = local_projection; - p = sse.add_point_2d((x, y), sketch, fixed = True); + point = sse.add_point_2d((x, y), sketch, fixed = True); + addedPoints[vertex.index] = point; + # print(point.location) + + if (connectLines != True): + continue; + + # print(addedPoints); + + compareSet = set(addedPoints.keys()) + edges = clicked_mesh.data.edges; + for edge in edges: + if (set(edge.vertices) & compareSet != True): continue + + + + + # print(f"Edge {edge.index} vertices: {[str(edge.vertices[x]) for x in range(2)]}"); From 60b18bb648cdb7cc1f50bbddcc84ab445e90e16d Mon Sep 17 00:00:00 2001 From: BOTAlex Date: Wed, 28 Aug 2024 21:07:25 +0200 Subject: [PATCH 06/13] Line projection working --- operators/add_workplane.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/operators/add_workplane.py b/operators/add_workplane.py index 5f536b72..ca963f15 100644 --- a/operators/add_workplane.py +++ b/operators/add_workplane.py @@ -135,9 +135,9 @@ def main(self, context: Context): quat.rotate(obj_translation) workplane_origin = obj_translation @ clicked_face.center - print("1: " + str(obj_translation)) - print("2: " + str(clicked_face)) - print("2.1: " + str(clicked_face.center)) + # print("1: " + str(obj_translation)) + # print("2: " + str(clicked_face)) + # print("2.1: " + str(clicked_face.center)) origin = sse.add_point_3d(workplane_origin) nm = sse.add_normal_3d(quat) @@ -157,7 +157,12 @@ def main(self, context: Context): activate_sketch(context, sketch.slvs_index, self) self.target = sketch - limitDist = 0.005; + # TODO: Only project selected mesh/face depending on checkbox + # TODO: Option to not project after creating workplane + # TODO: Option to choose if projected lines/points should be construction + + # Make these changable when creating face + limitDist = 0.025; connectLines = True; # May cause performance issues. idk addedPoints = {} @@ -183,24 +188,26 @@ def main(self, context: Context): local_projection.rotate(quat.conjugated()); x, y, _ = local_projection; - point = sse.add_point_2d((x, y), sketch, fixed = True); + point = sse.add_point_2d((x, y), sketch, fixed = True, index_reference = True); addedPoints[vertex.index] = point; # print(point.location) if (connectLines != True): continue; - - # print(addedPoints); compareSet = set(addedPoints.keys()) + # print(compareSet); edges = clicked_mesh.data.edges; for edge in edges: - if (set(edge.vertices) & compareSet != True): continue + if (set(edge.vertices).issubset(compareSet) != True): continue; - - + p1, p2 = [addedPoints[x] for x in edge.vertices]; + # print(p1.location); + # print(p2.location); + sse.add_line_2d(p1, p2, sketch, fixed = True, index_reference = True); # print(f"Edge {edge.index} vertices: {[str(edge.vertices[x]) for x in range(2)]}"); + # break; From c37eba07c0b85012be84410a1f43e9025bee4c51 Mon Sep 17 00:00:00 2001 From: BOTAlex Date: Wed, 28 Aug 2024 21:54:40 +0200 Subject: [PATCH 07/13] Seperated sketch on face to another button --- declarations.py | 2 + operators/add_sketch.py | 121 +++++++++++++++++++++++++++++- operators/add_workplane.py | 94 +++-------------------- workspacetools/__init__.py | 7 +- workspacetools/add_sketch_face.py | 20 +++++ 5 files changed, 160 insertions(+), 84 deletions(-) create mode 100644 workspacetools/add_sketch_face.py diff --git a/declarations.py b/declarations.py index 6660103a..372c715d 100644 --- a/declarations.py +++ b/declarations.py @@ -39,6 +39,7 @@ class Operators(str, Enum): AddRatio = "view3d.slvs_add_ratio" AddRectangle = "view3d.slvs_add_rectangle" AddSketch = "view3d.slvs_add_sketch" + AddSketchFace = "view3d.slvs_add_sketch_face" AddTangent = "view3d.slvs_add_tangent" AddVertical = "view3d.slvs_add_vertical" AddWorkPlane = "view3d.slvs_add_workplane" @@ -106,6 +107,7 @@ class WorkSpaceTools(str, Enum): AddPoint2D = "sketcher.slvs_add_point2d" AddPoint3D = "sketcher.slvs_add_point3d" AddRectangle = "sketcher.slvs_add_rectangle" + AddSketchFace = "sketcher.slvs_add_sketch_face" AddWorkplane = "sketcher.slvs_add_workplane" AddWorkplaneFace = "sketcher.slvs_add_workplane_face" Offset = "sketcher.slvs_offset" diff --git a/operators/add_sketch.py b/operators/add_sketch.py index 9813a9c0..ac836d6e 100644 --- a/operators/add_sketch.py +++ b/operators/add_sketch.py @@ -2,6 +2,7 @@ import bpy from bpy.types import Operator, Context, Event +from mathutils import Vector from ..model.types import SlvsWorkplane from ..declarations import Operators @@ -9,6 +10,8 @@ from ..stateful_operator.state import state_from_args from .base_3d import Operator3d from .utilities import activate_sketch, switch_sketch_mode +from ..stateful_operator.utilities.geometry import get_evaluated_obj, get_mesh_element +from ..utilities.geometry import get_face_orientation logger = logging.getLogger(__name__) @@ -71,4 +74,120 @@ def fini(self, context: Context, succeed: bool): switch_sketch_mode(self, context, to_sketch_mode=False) -register, unregister = register_stateops_factory((View3D_OT_slvs_add_sketch,)) + +class View3D_OT_slvs_add_sketch_face(Operator, Operator3d): + """Add a workplane and start sketch on mesh face""" + + bl_idname = Operators.AddSketchFace + bl_label = "Add sketch on mesh face" + bl_options = {"REGISTER", "UNDO"} + + wp_face_state1_doc = ( + "Face", + "Pick a mesh face to use as workplane's and sketch's surface.", + ) + + states = ( + state_from_args( + wp_face_state1_doc[0], + description=wp_face_state1_doc[1], + use_create=False, + pointer="face", + types=(bpy.types.MeshPolygon,), + interactive=True, + ), + ) + + def main(self, context: Context): + sse = context.scene.sketcher.entities + + obj_name, clicked_face_index = self.get_state_pointer(index=0, implicit=True) + clicked_obj = get_evaluated_obj(context, bpy.data.objects[obj_name]) + clicked_mesh = clicked_obj.data + clicked_face = clicked_mesh.polygons[clicked_face_index] + + obj_translation = clicked_obj.matrix_world + quat = get_face_orientation(clicked_mesh, clicked_face) # Quternion + quat.rotate(obj_translation) + + workplane_origin = obj_translation @ clicked_face.center + print("1: " + str(obj_translation)) + print("2: " + str(clicked_face)) + print("2.1: " + str(clicked_face.center)) + origin = sse.add_point_3d(workplane_origin) + nm = sse.add_normal_3d(quat) + + self.target = sse.add_workplane(origin, nm) + + context.area.tag_redraw() # Force re-draw of UI (Blender doesn't update after tool usage) + + meshes = [o for o in context.scene.objects if o.type == 'MESH'] + # print(meshes) + + # Workplane normal in world coordinates + workplane_normal = quat @ Vector((0.0, 0.0, 1.0)) + + sketch = sse.add_sketch(self.target) + p = sse.add_point_2d((0.0, 0.0), sketch, fixed = True) + + activate_sketch(context, sketch.slvs_index, self) + self.target = sketch + + # TODO: Only project selected mesh/face depending on checkbox + # TODO: Option to not project after creating workplane + # TODO: Option to choose if projected lines/points should be construction + + # Make these changable when creating face + limitDist = 0.025; + connectLines = True; # May cause performance issues. idk + + addedPoints = {} + for clicked_mesh in meshes: + vertices = clicked_mesh.data.vertices; + for vertex in vertices: + # Make vertex relative to plane + vertex_world = obj_translation @ vertex.co; + translated = vertex_world - workplane_origin; + + # Projection to plane + distance_to_plane = translated.dot(workplane_normal); + projection = translated - distance_to_plane * workplane_normal; + + if abs(distance_to_plane) > limitDist: + continue; + # print(f"Vertex {vertex.index} distance to plane: {abs(distance_to_plane)}"); + + ## Used ChatGPT, quaternion rotations is too hard. + # To 2D projection relative to the workplane + # Use the workplane orientation (quat) to project into 2D + local_projection = projection.copy(); + local_projection.rotate(quat.conjugated()); + x, y, _ = local_projection; + + point = sse.add_point_2d((x, y), sketch, fixed = True, index_reference = True); + addedPoints[vertex.index] = point; + # print(point.location) + + if (connectLines != True): + continue; + + compareSet = set(addedPoints.keys()) + # print(compareSet); + edges = clicked_mesh.data.edges; + for edge in edges: + if (set(edge.vertices).issubset(compareSet) != True): continue; + + p1, p2 = [addedPoints[x] for x in edge.vertices]; + # print(p1.location); + # print(p2.location); + sse.add_line_2d(p1, p2, sketch, fixed = True, index_reference = True); + + # print(f"Edge {edge.index} vertices: {[str(edge.vertices[x]) for x in range(2)]}"); + # break; + + + + return True + + +register, unregister = register_stateops_factory((View3D_OT_slvs_add_sketch,View3D_OT_slvs_add_sketch_face)) diff --git a/operators/add_workplane.py b/operators/add_workplane.py index ca963f15..b0e50021 100644 --- a/operators/add_workplane.py +++ b/operators/add_workplane.py @@ -125,95 +125,25 @@ class View3D_OT_slvs_add_workplane_face(Operator, Operator3d): def main(self, context: Context): sse = context.scene.sketcher.entities - obj_name, clicked_face_index = self.get_state_pointer(index=0, implicit=True) - clicked_obj = get_evaluated_obj(context, bpy.data.objects[obj_name]) - clicked_mesh = clicked_obj.data - clicked_face = clicked_mesh.polygons[clicked_face_index] - - obj_translation = clicked_obj.matrix_world - quat = get_face_orientation(clicked_mesh, clicked_face) # Quternion - quat.rotate(obj_translation) - - workplane_origin = obj_translation @ clicked_face.center - # print("1: " + str(obj_translation)) - # print("2: " + str(clicked_face)) - # print("2.1: " + str(clicked_face.center)) - origin = sse.add_point_3d(workplane_origin) + ob_name, face_index = self.get_state_pointer(index=0, implicit=True) + ob = get_evaluated_obj(context, bpy.data.objects[ob_name]) + mesh = ob.data + face = mesh.polygons[face_index] + + mat_obj = ob.matrix_world + quat = get_face_orientation(mesh, face) + quat.rotate(mat_obj) + pos = mat_obj @ face.center + origin = sse.add_point_3d(pos) nm = sse.add_normal_3d(quat) self.target = sse.add_workplane(origin, nm) - + ignore_hover(self.target) context.area.tag_redraw() # Force re-draw of UI (Blender doesn't update after tool usage) - - meshes = [o for o in context.scene.objects if o.type == 'MESH'] - # print(meshes) - - # Workplane normal in world coordinates - workplane_normal = quat @ Vector((0.0, 0.0, 1.0)) - - sketch = sse.add_sketch(self.target) - p = sse.add_point_2d((0.0, 0.0), sketch, fixed = True) - - activate_sketch(context, sketch.slvs_index, self) - self.target = sketch - - # TODO: Only project selected mesh/face depending on checkbox - # TODO: Option to not project after creating workplane - # TODO: Option to choose if projected lines/points should be construction - - # Make these changable when creating face - limitDist = 0.025; - connectLines = True; # May cause performance issues. idk - - addedPoints = {} - for clicked_mesh in meshes: - vertices = clicked_mesh.data.vertices; - for vertex in vertices: - # Make vertex relative to plane - vertex_world = obj_translation @ vertex.co; - translated = vertex_world - workplane_origin; - - # Projection to plane - distance_to_plane = translated.dot(workplane_normal); - projection = translated - distance_to_plane * workplane_normal; - - if abs(distance_to_plane) > limitDist: - continue; - # print(f"Vertex {vertex.index} distance to plane: {abs(distance_to_plane)}"); - - ## Used ChatGPT, quaternion rotations is too hard. - # To 2D projection relative to the workplane - # Use the workplane orientation (quat) to project into 2D - local_projection = projection.copy(); - local_projection.rotate(quat.conjugated()); - x, y, _ = local_projection; - - point = sse.add_point_2d((x, y), sketch, fixed = True, index_reference = True); - addedPoints[vertex.index] = point; - # print(point.location) - - if (connectLines != True): - continue; - - compareSet = set(addedPoints.keys()) - # print(compareSet); - edges = clicked_mesh.data.edges; - for edge in edges: - if (set(edge.vertices).issubset(compareSet) != True): continue; - - p1, p2 = [addedPoints[x] for x in edge.vertices]; - # print(p1.location); - # print(p2.location); - sse.add_line_2d(p1, p2, sketch, fixed = True, index_reference = True); - - # print(f"Edge {edge.index} vertices: {[str(edge.vertices[x]) for x in range(2)]}"); - # break; - - - return True + register, unregister = register_stateops_factory( (View3D_OT_slvs_add_workplane, View3D_OT_slvs_add_workplane_face) ) diff --git a/workspacetools/__init__.py b/workspacetools/__init__.py index 61818198..68b722c6 100644 --- a/workspacetools/__init__.py +++ b/workspacetools/__init__.py @@ -10,6 +10,7 @@ from .add_rectangle import VIEW3D_T_slvs_add_rectangle from .add_workplane import VIEW3D_T_slvs_add_workplane from .add_workplane_face import VIEW3D_T_slvs_add_workplane_face +from .add_sketch_face import VIEW3D_T_slvs_add_sketch_face from .bevel import VIEW3D_T_slvs_bevel from .offset import VIEW3D_T_slvs_offset from .select import VIEW3D_T_slvs_select @@ -38,7 +39,11 @@ (VIEW3D_T_slvs_trim, {"separator": True, "group": False}), (VIEW3D_T_slvs_bevel, {"separator": False, "group": False}), (VIEW3D_T_slvs_offset, {"separator": False, "group": False}), - (VIEW3D_T_slvs_add_workplane_face, {"separator": True, "group": True}), + (VIEW3D_T_slvs_add_sketch_face, {"separator": True, "group": True}), + ( + VIEW3D_T_slvs_add_workplane_face, + {"after": {VIEW3D_T_slvs_add_sketch_face.bl_idname}}, + ), ( VIEW3D_T_slvs_add_workplane, {"after": {VIEW3D_T_slvs_add_workplane_face.bl_idname}}, diff --git a/workspacetools/add_sketch_face.py b/workspacetools/add_sketch_face.py new file mode 100644 index 00000000..85eeb87e --- /dev/null +++ b/workspacetools/add_sketch_face.py @@ -0,0 +1,20 @@ +from bpy.types import WorkSpaceTool + +from ..declarations import GizmoGroups, Operators, WorkSpaceTools +from ..keymaps import tool_generic +from ..stateful_operator.tool import GenericStateTool +from ..stateful_operator.utilities.keymap import operator_access + + +class VIEW3D_T_slvs_add_sketch_face(GenericStateTool, WorkSpaceTool): + bl_space_type = "VIEW_3D" + bl_context_mode = "OBJECT" + bl_idname = WorkSpaceTools.AddSketchFace + bl_label = "Add a workplane and start sketch on mesh face" + bl_operator = Operators.AddSketchFace + bl_icon = "ops.mesh.primitive_grid_add_gizmo" + bl_widget = GizmoGroups.Preselection + bl_keymap = ( + *tool_generic, + *operator_access(Operators.AddSketchFace), + ) From b6e70dd564e34f245c3b165921cb673fc77641be Mon Sep 17 00:00:00 2001 From: BOTAlex Date: Wed, 28 Aug 2024 22:07:49 +0200 Subject: [PATCH 08/13] Disabled print statements and tweaked max dist --- operators/add_sketch.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/operators/add_sketch.py b/operators/add_sketch.py index ac836d6e..20501909 100644 --- a/operators/add_sketch.py +++ b/operators/add_sketch.py @@ -111,9 +111,9 @@ def main(self, context: Context): quat.rotate(obj_translation) workplane_origin = obj_translation @ clicked_face.center - print("1: " + str(obj_translation)) - print("2: " + str(clicked_face)) - print("2.1: " + str(clicked_face.center)) + # print("1: " + str(obj_translation)) + # print("2: " + str(clicked_face)) + # print("2.1: " + str(clicked_face.center)) origin = sse.add_point_3d(workplane_origin) nm = sse.add_normal_3d(quat) @@ -138,7 +138,7 @@ def main(self, context: Context): # TODO: Option to choose if projected lines/points should be construction # Make these changable when creating face - limitDist = 0.025; + limitDist = 0.001; connectLines = True; # May cause performance issues. idk addedPoints = {} From 2fcf9fb7374e09580b6047fc99a6ceeded32dcc0 Mon Sep 17 00:00:00 2001 From: BOTAlex Date: Sun, 1 Sep 2024 02:25:12 +0200 Subject: [PATCH 09/13] Seperated projection code from add sketch code --- operators/add_sketch.py | 121 ++++++++++++++++++++++++++++++---------- 1 file changed, 90 insertions(+), 31 deletions(-) diff --git a/operators/add_sketch.py b/operators/add_sketch.py index 20501909..e0ed1fdd 100644 --- a/operators/add_sketch.py +++ b/operators/add_sketch.py @@ -2,7 +2,8 @@ import bpy from bpy.types import Operator, Context, Event -from mathutils import Vector +from bpy.props import FloatProperty, BoolProperty, EnumProperty +from mathutils import Vector, Quaternion from ..model.types import SlvsWorkplane from ..declarations import Operators @@ -12,10 +13,26 @@ from .utilities import activate_sketch, switch_sketch_mode from ..stateful_operator.utilities.geometry import get_evaluated_obj, get_mesh_element from ..utilities.geometry import get_face_orientation +from ..model.group_entities import SlvsEntities, SlvsSketch logger = logging.getLogger(__name__) +class ProjectionData: + def __init__(self, + sketcherEntities: SlvsEntities, + sketch: SlvsSketch, + objectTranslation: bpy.types.TransformOrientation, + workplaneOrigin: tuple[float, float, float], + workplaneNormal: Vector, + quat: Quaternion): + + self.sketcherEntities = sketcherEntities + self.sketch = sketch + self.objectTranslation = objectTranslation + self.workplaneOrigin = workplaneOrigin + self.workplaneNormal = workplaneNormal + self.quat = quat # I forgot what quat was... Should've added more comments # TODO: # - Draw sketches @@ -77,40 +94,66 @@ def fini(self, context: Context, succeed: bool): class View3D_OT_slvs_add_sketch_face(Operator, Operator3d): """Add a workplane and start sketch on mesh face""" - + bl_idname = Operators.AddSketchFace bl_label = "Add sketch on mesh face" bl_options = {"REGISTER", "UNDO"} - wp_face_state1_doc = ( - "Face", - "Pick a mesh face to use as workplane's and sketch's surface.", + # Can't get default to work. idk why + projectDist: FloatProperty( + name="Project distance", + subtype="DISTANCE", + unit="LENGTH", + default=0.001, + step=0.01, + # precision=get_prefs().decimal_precision, + ) + + # # Idk why it doesn't work correctly + # connectLines: BoolProperty(name="Connect lines", description="May cause performance issues, idk", default=True) + connectLines = True + + projectFrom: EnumProperty( + name="My Search", + items=( + ('FACE', "Face", ""), + ('MESH', "Mesh", ""), + ('ALL', "All meshes", ""), + ), + default='ALL' ) states = ( state_from_args( - wp_face_state1_doc[0], - description=wp_face_state1_doc[1], + "Face", + description="Pick a mesh face to use as workplane's and sketch's surface.", use_create=False, pointer="face", types=(bpy.types.MeshPolygon,), interactive=True, ), + state_from_args( + "Additional projection distance", + description="Additional projection distance (default + extra)", + property="projectDist", + interactive=True, + no_event=True, + ), ) def main(self, context: Context): - sse = context.scene.sketcher.entities + sse: SlvsEntities = context.scene.sketcher.entities obj_name, clicked_face_index = self.get_state_pointer(index=0, implicit=True) clicked_obj = get_evaluated_obj(context, bpy.data.objects[obj_name]) clicked_mesh = clicked_obj.data - clicked_face = clicked_mesh.polygons[clicked_face_index] + clicked_face: bpy.types.MeshPolygon = clicked_mesh.polygons[clicked_face_index] - obj_translation = clicked_obj.matrix_world + obj_translation: bpy.types.TransformOrientation = clicked_obj.matrix_world quat = get_face_orientation(clicked_mesh, clicked_face) # Quternion quat.rotate(obj_translation) - workplane_origin = obj_translation @ clicked_face.center + workplane_origin: tuple[float, float, float] = obj_translation @ clicked_face.center # print("1: " + str(obj_translation)) # print("2: " + str(clicked_face)) # print("2.1: " + str(clicked_face.center)) @@ -118,10 +161,8 @@ def main(self, context: Context): nm = sse.add_normal_3d(quat) self.target = sse.add_workplane(origin, nm) - - context.area.tag_redraw() # Force re-draw of UI (Blender doesn't update after tool usage) - meshes = [o for o in context.scene.objects if o.type == 'MESH'] + # print(meshes) # Workplane normal in world coordinates @@ -130,30 +171,51 @@ def main(self, context: Context): sketch = sse.add_sketch(self.target) p = sse.add_point_2d((0.0, 0.0), sketch, fixed = True) - activate_sketch(context, sketch.slvs_index, self) - self.target = sketch + # print(self.projectFrom) + + # activate_sketch(context, sketch.slvs_index, self) + # self.target = sketch # TODO: Only project selected mesh/face depending on checkbox # TODO: Option to not project after creating workplane # TODO: Option to choose if projected lines/points should be construction + # TODO: Auto align view with sketch # Make these changable when creating face - limitDist = 0.001; - connectLines = True; # May cause performance issues. idk + limitDist = 0.001 + self.projectDist; + + projectionData = ProjectionData(sse, sketch, obj_translation, workplane_origin, workplane_normal, quat) + + if self.projectFrom == 'FACE': + logger.error("Project face is not implemented yet") + elif self.projectFrom == 'MESH': + self.ProjectFromMeshes(projectionData, [clicked_obj,], limitDist, self.connectLines) + elif self.projectFrom == 'ALL': # ALL doesn't actually work. I don't think its important to fix atm + allMeshesInScene = [o for o in context.scene.objects if o.type == 'MESH'] + self.ProjectFromMeshes(projectionData, allMeshesInScene, limitDist, self.connectLines) + context.area.tag_redraw() # Force re-draw of UI (Blender doesn't update after tool usage) + return True + + def ProjectFromMeshes(self, projectionData: ProjectionData, + meshes: list[bpy.types.Mesh], + maxDist: float, + connectLines: bool = True): + sse = projectionData.sketcherEntities + addedPoints = {} for clicked_mesh in meshes: vertices = clicked_mesh.data.vertices; for vertex in vertices: # Make vertex relative to plane - vertex_world = obj_translation @ vertex.co; - translated = vertex_world - workplane_origin; + vertex_world = projectionData.objectTranslation @ vertex.co; + translated = vertex_world - projectionData.workplaneOrigin; # Projection to plane - distance_to_plane = translated.dot(workplane_normal); - projection = translated - distance_to_plane * workplane_normal; + distance_to_plane = translated.dot(projectionData.workplaneNormal); + projection = translated - distance_to_plane * projectionData.workplaneNormal; - if abs(distance_to_plane) > limitDist: + if abs(distance_to_plane) > maxDist: continue; # print(f"Vertex {vertex.index} distance to plane: {abs(distance_to_plane)}"); @@ -161,10 +223,10 @@ def main(self, context: Context): # To 2D projection relative to the workplane # Use the workplane orientation (quat) to project into 2D local_projection = projection.copy(); - local_projection.rotate(quat.conjugated()); + local_projection.rotate(projectionData.quat.conjugated()); x, y, _ = local_projection; - point = sse.add_point_2d((x, y), sketch, fixed = True, index_reference = True); + point = sse.add_point_2d((x, y), projectionData.sketch, fixed = True, index_reference = True); addedPoints[vertex.index] = point; # print(point.location) @@ -180,14 +242,11 @@ def main(self, context: Context): p1, p2 = [addedPoints[x] for x in edge.vertices]; # print(p1.location); # print(p2.location); - sse.add_line_2d(p1, p2, sketch, fixed = True, index_reference = True); + sse.add_line_2d(p1, p2, projectionData.sketch, fixed = True, index_reference = True); # print(f"Edge {edge.index} vertices: {[str(edge.vertices[x]) for x in range(2)]}"); # break; - - - - return True - + pass + register, unregister = register_stateops_factory((View3D_OT_slvs_add_sketch,View3D_OT_slvs_add_sketch_face)) From 85e66860b101447fb7062987759a706cfc7ebf28 Mon Sep 17 00:00:00 2001 From: BOTAlex Date: Sun, 1 Sep 2024 02:29:03 +0200 Subject: [PATCH 10/13] Cleanup --- operators/add_sketch.py | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/operators/add_sketch.py b/operators/add_sketch.py index e0ed1fdd..61334b1c 100644 --- a/operators/add_sketch.py +++ b/operators/add_sketch.py @@ -91,7 +91,7 @@ def fini(self, context: Context, succeed: bool): switch_sketch_mode(self, context, to_sketch_mode=False) - +# TODO: Auto align view with sketch after creation class View3D_OT_slvs_add_sketch_face(Operator, Operator3d): """Add a workplane and start sketch on mesh face""" @@ -154,35 +154,21 @@ def main(self, context: Context): quat.rotate(obj_translation) workplane_origin: tuple[float, float, float] = obj_translation @ clicked_face.center - # print("1: " + str(obj_translation)) - # print("2: " + str(clicked_face)) - # print("2.1: " + str(clicked_face.center)) origin = sse.add_point_3d(workplane_origin) nm = sse.add_normal_3d(quat) self.target = sse.add_workplane(origin, nm) - - # print(meshes) - # Workplane normal in world coordinates workplane_normal = quat @ Vector((0.0, 0.0, 1.0)) sketch = sse.add_sketch(self.target) - p = sse.add_point_2d((0.0, 0.0), sketch, fixed = True) + sse.add_point_2d((0.0, 0.0), sketch, fixed = True) # Add face centrum point - # print(self.projectFrom) - - # activate_sketch(context, sketch.slvs_index, self) + # activate_sketch(context, sketch.slvs_index, self) # This hides the pop-up with the options for the projection # self.target = sketch - # TODO: Only project selected mesh/face depending on checkbox - # TODO: Option to not project after creating workplane - # TODO: Option to choose if projected lines/points should be construction - # TODO: Auto align view with sketch - - # Make these changable when creating face - limitDist = 0.001 + self.projectDist; + limitDist = 0.001 + self.projectDist; # Should just be the project dist, but couldn't get default in property to work projectionData = ProjectionData(sse, sketch, obj_translation, workplane_origin, workplane_normal, quat) @@ -217,7 +203,6 @@ def ProjectFromMeshes(self, projectionData: ProjectionData, if abs(distance_to_plane) > maxDist: continue; - # print(f"Vertex {vertex.index} distance to plane: {abs(distance_to_plane)}"); ## Used ChatGPT, quaternion rotations is too hard. # To 2D projection relative to the workplane @@ -228,24 +213,17 @@ def ProjectFromMeshes(self, projectionData: ProjectionData, point = sse.add_point_2d((x, y), projectionData.sketch, fixed = True, index_reference = True); addedPoints[vertex.index] = point; - # print(point.location) if (connectLines != True): continue; compareSet = set(addedPoints.keys()) - # print(compareSet); edges = clicked_mesh.data.edges; for edge in edges: if (set(edge.vertices).issubset(compareSet) != True): continue; p1, p2 = [addedPoints[x] for x in edge.vertices]; - # print(p1.location); - # print(p2.location); sse.add_line_2d(p1, p2, projectionData.sketch, fixed = True, index_reference = True); - - # print(f"Edge {edge.index} vertices: {[str(edge.vertices[x]) for x in range(2)]}"); - # break; pass From adede5a3027b9a7535c2d4122e8f8eb66123ef8f Mon Sep 17 00:00:00 2001 From: BOTAlex Date: Sun, 1 Sep 2024 02:34:34 +0200 Subject: [PATCH 11/13] Clean up --- operators/add_sketch.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/operators/add_sketch.py b/operators/add_sketch.py index 61334b1c..44d75e32 100644 --- a/operators/add_sketch.py +++ b/operators/add_sketch.py @@ -144,32 +144,36 @@ class View3D_OT_slvs_add_sketch_face(Operator, Operator3d): def main(self, context: Context): sse: SlvsEntities = context.scene.sketcher.entities + # Gets info about clicked object obj_name, clicked_face_index = self.get_state_pointer(index=0, implicit=True) clicked_obj = get_evaluated_obj(context, bpy.data.objects[obj_name]) clicked_mesh = clicked_obj.data clicked_face: bpy.types.MeshPolygon = clicked_mesh.polygons[clicked_face_index] + # Gets face rotation obj_translation: bpy.types.TransformOrientation = clicked_obj.matrix_world quat = get_face_orientation(clicked_mesh, clicked_face) # Quternion quat.rotate(obj_translation) + # Creates the workplane workplane_origin: tuple[float, float, float] = obj_translation @ clicked_face.center origin = sse.add_point_3d(workplane_origin) nm = sse.add_normal_3d(quat) - - self.target = sse.add_workplane(origin, nm) + workplane = sse.add_workplane(origin, nm) # Workplane normal in world coordinates workplane_normal = quat @ Vector((0.0, 0.0, 1.0)) - sketch = sse.add_sketch(self.target) + # Creates the sketch + sketch = sse.add_sketch(workplane) sse.add_point_2d((0.0, 0.0), sketch, fixed = True) # Add face centrum point - # activate_sketch(context, sketch.slvs_index, self) # This hides the pop-up with the options for the projection + # activate_sketch(context, sketch.slvs_index, self) # This hides the pop-up with the options for the projection. Idk why, so it is just like this # self.target = sketch limitDist = 0.001 + self.projectDist; # Should just be the project dist, but couldn't get default in property to work + # Prepares the data needed for the projection projectionData = ProjectionData(sse, sketch, obj_translation, workplane_origin, workplane_normal, quat) if self.projectFrom == 'FACE': @@ -200,7 +204,8 @@ def ProjectFromMeshes(self, projectionData: ProjectionData, # Projection to plane distance_to_plane = translated.dot(projectionData.workplaneNormal); projection = translated - distance_to_plane * projectionData.workplaneNormal; - + + # If vertex is too far from sketch, then don't create sketch point if abs(distance_to_plane) > maxDist: continue; @@ -217,6 +222,7 @@ def ProjectFromMeshes(self, projectionData: ProjectionData, if (connectLines != True): continue; + # Takes the edges of the object and checks if the earlier added sketch points are used in the edges. If yes, then create line from first point to second point compareSet = set(addedPoints.keys()) edges = clicked_mesh.data.edges; for edge in edges: From 1b33371907089af75b2b621669f2e160751f7fba Mon Sep 17 00:00:00 2001 From: BOTAlex Date: Sun, 1 Sep 2024 02:35:42 +0200 Subject: [PATCH 12/13] Slight cleanup --- operators/add_sketch.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/operators/add_sketch.py b/operators/add_sketch.py index 44d75e32..bef71f4c 100644 --- a/operators/add_sketch.py +++ b/operators/add_sketch.py @@ -92,6 +92,8 @@ def fini(self, context: Context, succeed: bool): # TODO: Auto align view with sketch after creation +# TODO: Make it auto enter sketch +# TODO: Make the properties work! class View3D_OT_slvs_add_sketch_face(Operator, Operator3d): """Add a workplane and start sketch on mesh face""" @@ -120,7 +122,7 @@ class View3D_OT_slvs_add_sketch_face(Operator, Operator3d): ('MESH', "Mesh", ""), ('ALL', "All meshes", ""), ), - default='ALL' + default='ALL' # Maybe should be 'MESH' instead for performance issues. Idk ) states = ( From 50fb8e50d114b2309221c66f4723e6a884ad742c Mon Sep 17 00:00:00 2001 From: BOTAlex Date: Sun, 1 Sep 2024 03:15:48 +0200 Subject: [PATCH 13/13] Changed the tool button text --- workspacetools/add_sketch_face.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workspacetools/add_sketch_face.py b/workspacetools/add_sketch_face.py index 85eeb87e..c36de50b 100644 --- a/workspacetools/add_sketch_face.py +++ b/workspacetools/add_sketch_face.py @@ -10,7 +10,7 @@ class VIEW3D_T_slvs_add_sketch_face(GenericStateTool, WorkSpaceTool): bl_space_type = "VIEW_3D" bl_context_mode = "OBJECT" bl_idname = WorkSpaceTools.AddSketchFace - bl_label = "Add a workplane and start sketch on mesh face" + bl_label = "Project to sketch from face" bl_operator = Operators.AddSketchFace bl_icon = "ops.mesh.primitive_grid_add_gizmo" bl_widget = GizmoGroups.Preselection