Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions declarations.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down
166 changes: 165 additions & 1 deletion operators/add_sketch.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,37 @@

import bpy
from bpy.types import Operator, Context, Event
from bpy.props import FloatProperty, BoolProperty, EnumProperty
from mathutils import Vector, Quaternion

from ..model.types import SlvsWorkplane
from ..declarations import Operators
from ..stateful_operator.utilities.register import register_stateops_factory
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
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
Expand Down Expand Up @@ -71,4 +91,148 @@ 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,))
# 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"""

bl_idname = Operators.AddSketchFace
bl_label = "Add sketch on mesh face"
bl_options = {"REGISTER", "UNDO"}

# 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' # Maybe should be 'MESH' instead for performance issues. Idk
)

states = (
state_from_args(
"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: 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)
workplane = sse.add_workplane(origin, nm)

# Workplane normal in world coordinates
workplane_normal = quat @ Vector((0.0, 0.0, 1.0))

# 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. 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':
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 = projectionData.objectTranslation @ vertex.co;
translated = vertex_world - projectionData.workplaneOrigin;

# 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;

## 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(projectionData.quat.conjugated());
x, y, _ = local_projection;

point = sse.add_point_2d((x, y), projectionData.sketch, fixed = True, index_reference = True);
addedPoints[vertex.index] = point;

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:
if (set(edge.vertices).issubset(compareSet) != True): continue;

p1, p2 = [addedPoints[x] for x in edge.vertices];
sse.add_line_2d(p1, p2, projectionData.sketch, fixed = True, index_reference = True);
pass


register, unregister = register_stateops_factory((View3D_OT_slvs_add_sketch,View3D_OT_slvs_add_sketch_face))
4 changes: 4 additions & 0 deletions operators/add_workplane.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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__)

Expand Down Expand Up @@ -137,9 +139,11 @@ def main(self, context: Context):

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)
return True



register, unregister = register_stateops_factory(
(View3D_OT_slvs_add_workplane, View3D_OT_slvs_add_workplane_face)
)
7 changes: 6 additions & 1 deletion workspacetools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}},
Expand Down
20 changes: 20 additions & 0 deletions workspacetools/add_sketch_face.py
Original file line number Diff line number Diff line change
@@ -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 = "Project to sketch from 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),
)