Skip to content

Commit

Permalink
0.9.0.1 Snap to target object added
Browse files Browse the repository at this point in the history
  • Loading branch information
jayanam committed Feb 1, 2019
1 parent b7d1b0f commit 1cfb471
Show file tree
Hide file tree
Showing 16 changed files with 183 additions and 38 deletions.
12 changes: 8 additions & 4 deletions __init__.py
Expand Up @@ -2,7 +2,7 @@
"name": "Fast Carve",
"description": "Hardsurface utility Blender addon for quick and easy boolean and bevel operations",
"author": "Jayanam",
"version": (0, 8, 1, 6),
"version": (0, 9, 0, 1),
"blender": (2, 80, 0),
"location": "View3D",
"category": "Object"}
Expand Down Expand Up @@ -47,18 +47,22 @@
description="Delete the object after apply",
default = True)

bpy.types.Scene.use_snapping = BoolProperty(name="Snapping",
bpy.types.Scene.use_snapping = BoolProperty(name="Snap to grid",
description="Use snapping to the grid",
default = True)

bpy.types.Scene.in_primitive_mode = BoolProperty(name="Primitive Mode",
default = False)
bpy.types.Scene.snap_to_target = BoolProperty(name="Snap to target",
description="Snap the primitive to the target",
default = True)

bpy.types.Scene.draw_distance = FloatProperty(
name="Draw Distance",
description="Distance of primitives to the origin",
default = 2.0)

bpy.types.Scene.in_primitive_mode = BoolProperty(name="Primitive Mode",
default = False)

bpy.types.Scene.extrude_mesh = BoolProperty(name="Extrude mesh",
description="Extrude the mesh after creation",
default = True)
Expand Down
Binary file modified __pycache__/__init__.cpython-37.pyc
Binary file not shown.
Binary file modified __pycache__/fc_immediate_mode_op.cpython-37.pyc
Binary file not shown.
Binary file modified __pycache__/fc_primitive_panel.cpython-37.pyc
Binary file not shown.
62 changes: 47 additions & 15 deletions fc_immediate_mode_op.py
Expand Up @@ -43,9 +43,17 @@ def __init__(self):
def invoke(self, context, event):
args = (self, context)

target_obj = context.scene.carver_target
snap_to_target = context.scene.snap_to_target

if target_obj is None:
self.report({'ERROR'}, 'Please define a target object.')
context.scene.in_primitive_mode = False
return {"FINISHED"}

context.scene.in_primitive_mode = True

self.create_shape(context)
self.create_shape(context, target_obj, snap_to_target)

self.register_handlers(args, context)

Expand All @@ -72,9 +80,19 @@ def unregister_handlers(self, context):
self.draw_handle_3d = None
self.draw_event = None

def get_3d_for_mouse(self, mouse_pos_2d, context):
if context.scene.snap_to_target:
mouse_pos_3d = self.shape.get_3d_for_2d(mouse_pos_2d, context)
else:
mouse_pos_3d = get_3d_vertex(context, mouse_pos_2d)
return mouse_pos_3d

def modal(self, context, event):
if context.area:
context.area.tag_redraw()

target_obj = context.scene.carver_target
snap_to_target = context.scene.snap_to_target

if event.type == "ESC" and event.value == "PRESS":

Expand All @@ -91,12 +109,14 @@ def modal(self, context, event):
return {'FINISHED'}

# The mouse is moved
if event.type == "MOUSEMOVE":
if event.type == "MOUSEMOVE" and not self.shape.is_none():

mouse_pos_2d = (event.mouse_region_x, event.mouse_region_y)
mouse_pos_3d = get_3d_vertex(context, mouse_pos_2d)

if context.scene.use_snapping:
mouse_pos_3d = self.get_3d_for_mouse(mouse_pos_2d, context)

if context.scene.use_snapping and mouse_pos_3d is not None:
mouse_pos_3d = get_snap_3d_vertex(context, mouse_pos_3d)
mouse_pos_2d = get_2d_vertex(context, mouse_pos_3d)

if self.shape.handle_mouse_move(mouse_pos_2d, mouse_pos_3d, event, context):
Expand All @@ -105,13 +125,21 @@ def modal(self, context, event):
# Left mouse button is pressed
if event.value == "PRESS" and event.type == "LEFTMOUSE":

if target_obj is None:
self.report({'ERROR'}, 'Please define a target object.')
return {"PASS_THROUGH"}

self.create_shape(context, target_obj, snap_to_target)

mouse_pos_2d = (event.mouse_region_x, event.mouse_region_y)
mouse_pos_3d = get_3d_vertex(context, mouse_pos_2d)

if context.scene.use_snapping:
mouse_pos_2d = get_2d_vertex(context, mouse_pos_3d)


self.create_shape(context)
mouse_pos_3d = self.get_3d_for_mouse(mouse_pos_2d, context)

if context.scene.use_snapping and mouse_pos_3d is not None:
mouse_pos_3d = get_snap_3d_vertex(context, mouse_pos_3d)
mouse_pos_2d = get_2d_vertex(context, mouse_pos_3d)

if self.shape.is_moving():
self.shape.stop_move(context)
Expand All @@ -124,7 +152,6 @@ def modal(self, context, event):

if self.shape.handle_mouse_press(mouse_pos_2d, mouse_pos_3d, event, context):
self.create_object(context)
# self.shape.reset()
else:
# So that the direction is defined during shape
# creation, not when it is extruded
Expand All @@ -140,24 +167,27 @@ def modal(self, context, event):
# try to move the shape
if event.type == "G":
mouse_pos_2d = (event.mouse_region_x, event.mouse_region_y)
mouse_pos_3d = get_3d_vertex(context, mouse_pos_2d)

mouse_pos_3d = self.get_3d_for_mouse(mouse_pos_2d, context)

if self.shape.start_move(mouse_pos_3d):
return {"RUNNING_MODAL"}

# try to rotate the shape
if event.type == "R":
mouse_pos_2d = (event.mouse_region_x, event.mouse_region_y)
mouse_pos_3d = get_3d_vertex(context, mouse_pos_2d)

mouse_pos_3d = self.get_3d_for_mouse(mouse_pos_2d, context)

if self.shape.start_rotate(mouse_pos_3d, context):
self.create_batch()
return {"RUNNING_MODAL"}

# try to extrude the shape
if event.type == "E":
mouse_pos_2d = (event.mouse_region_x, event.mouse_region_y)
mouse_pos_3d = get_3d_vertex(context, mouse_pos_2d)

if self.shape.start_extrude(mouse_pos_2d, mouse_pos_3d, context):
if self.shape.start_extrude(mouse_pos_2d, context):
self.create_batch()
return {"RUNNING_MODAL"}

Expand All @@ -174,12 +204,12 @@ def modal(self, context, event):
context.scene.primitive_type = next_enum(context.scene.primitive_type,
context.scene, "primitive_type")

self.create_shape(context)
self.create_shape(context, target_obj, snap_to_target)
return {"RUNNING_MODAL"}

return {"PASS_THROUGH"}

def create_shape(self, context):
def create_shape(self, context, target_obj, snap_to_target):
if self.shape.is_none():
if context.scene.primitive_type == "Circle":
self.shape = Circle_Shape()
Expand All @@ -188,6 +218,8 @@ def create_shape(self, context):
else:
self.shape = Rectangle_Shape()

self.shape.initialize(context, target_obj, snap_to_target)

def create_object(self, context):

# Create a mesh and an object and
Expand Down
3 changes: 3 additions & 0 deletions fc_primitive_panel.py
Expand Up @@ -27,6 +27,9 @@ def draw(self, context):
row = layout.row()
layout.prop(context.scene, "use_snapping")

row = layout.row()
layout.prop(context.scene, "snap_to_target")

row = layout.row()

if not context.scene.in_primitive_mode:
Expand Down
Binary file modified types/__pycache__/circle_shape.cpython-37.pyc
Binary file not shown.
Binary file modified types/__pycache__/polyline_shape.cpython-37.pyc
Binary file not shown.
Binary file modified types/__pycache__/rectangle_shape.cpython-37.pyc
Binary file not shown.
Binary file modified types/__pycache__/shape.cpython-37.pyc
Binary file not shown.
13 changes: 12 additions & 1 deletion types/circle_shape.py
Expand Up @@ -22,6 +22,9 @@ def handle_mouse_move(self, mouse_pos_2d, mouse_pos_3d, event, context):
return result

def create_circle(self, context):

from mathutils import Matrix

rv3d = context.space_data.region_3d
view_rot = rv3d.view_rotation

Expand All @@ -30,14 +33,22 @@ def create_circle(self, context):
points = [(sin(i * mul) * self._radius, cos(i * mul) * self._radius, 0)
for i in range(segments)]

self._vertices = [view_rot @ Vector(point) +
rot_mat = view_rot

if self._normal is not None:
rot_mat = self._normal.to_track_quat('Z', 'X').to_matrix()

self._vertices = [rot_mat @ Vector(point) +
self._center for point in points]

self._vertices_2d = [get_2d_vertex(context, vertex) for vertex in self._vertices]


def handle_mouse_press(self, mouse_pos_2d, mouse_pos_3d, event, context):

if mouse_pos_3d is None:
return False

if self.is_none() and event.ctrl:

self._center = mouse_pos_3d
Expand Down
3 changes: 3 additions & 0 deletions types/polyline_shape.py
Expand Up @@ -24,6 +24,9 @@ def get_vertices_copy(self, mouse_pos = None):

def handle_mouse_press(self, mouse_pos_2d, mouse_pos_3d, event, context):

if mouse_pos_3d is None:
return False

if (self.is_none() and event.ctrl) or (self.is_processing() and not event.ctrl):

self.add_vertex(mouse_pos_3d)
Expand Down
19 changes: 14 additions & 5 deletions types/rectangle_shape.py
Expand Up @@ -12,6 +12,9 @@ def __init__(self):

def handle_mouse_press(self, mouse_pos_2d, mouse_pos_3d, event, context):

if mouse_pos_3d is None:
return False

if self.is_none() and event.ctrl:
self._vertices_2d[0] = mouse_pos_2d

Expand Down Expand Up @@ -66,8 +69,12 @@ def create_rect(self, context):
self._vertices.clear()

# get missing 3d vertices
vertex2 = get_3d_vertex(context, self._vertices_2d[1])
vertex4 = get_3d_vertex(context, self._vertices_2d[3])
if self._normal is None:
vertex2 = get_3d_vertex(context, self._vertices_2d[1])
vertex4 = get_3d_vertex(context, self._vertices_2d[3])
else:
vertex2 = self.get_3d_for_2d(self._vertices_2d[1], context)
vertex4 = self.get_3d_for_2d(self._vertices_2d[3], context)

self._vertices.extend([self._vertex1, vertex2, self._vertex3, vertex4])

Expand All @@ -89,10 +96,12 @@ def start_rotate(self, mouse_pos, context):
y = oy + sin(angle) * (px - ox) + cos(angle) * (py - oy)

tmp_vertices_2d.append((x,y))

direction = get_view_direction_by_rot_matrix(self._view_context.view_rotation) * context.scene.draw_distance

self._vertices[i] = get_3d_vertex_for_2d(self._view_context, (x,y), -direction)
if self._normal is None:
direction = get_view_direction_by_rot_matrix(self._view_context.view_rotation) * context.scene.draw_distance
self._vertices[i] = get_3d_vertex_for_2d(self._view_context, (x,y), -direction)
else:
self._vertices[i] = self.get_3d_for_2d((x,y), context)

self._vertices_2d = tmp_vertices_2d

Expand Down
39 changes: 34 additions & 5 deletions types/shape.py
Expand Up @@ -4,9 +4,13 @@

from mathutils import Vector, geometry

from mathutils.geometry import intersect_line_plane

from ..utils.fc_view_3d_utils import *

from bpy_extras.view3d_utils import region_2d_to_location_3d, location_3d_to_region_2d
from bpy_extras.view3d_utils import (
region_2d_to_location_3d,
location_3d_to_region_2d )

class ShapeState(Enum):
NONE = 0
Expand Down Expand Up @@ -83,8 +87,31 @@ def __init__(self):
self._view_context = None
self._mouse_pos_2d = (0,0)
self._is_extruded = False
self._snap_to_target = True
self._bvhtree = None
self._hit = None
self._normal = None

def get_3d_for_2d(self, pos_2d, context):
origin, direction = get_origin_and_direction(pos_2d, context)
result = None

if self._hit is None:
self._hit, self._normal, *_ = self._bvhtree.ray_cast(origin, direction)
result = self._hit.copy()
else:
result = intersect_line_plane(origin, origin + direction, self._hit, self._normal)

if result is not None:
result += self._normal.normalized() * 0.01

return result


def initialize(self, context, target, snap_to_target):
self._bvhtree = bvhtree_from_object(context, target)
self._snap_to_target = snap_to_target

def is_none(self):
return self._state is ShapeState.NONE

Expand All @@ -107,9 +134,11 @@ def is_extruding(self):
return self._is_extruding

def get_dir(self):
view_rot = self._view_context.view_rotation

return get_view_direction_by_rot_matrix(view_rot)
if self._normal is None:
view_rot = self._view_context.view_rotation
return get_view_direction_by_rot_matrix(view_rot)

return -self._normal

def get_view_context(self):
return self._view_context
Expand Down Expand Up @@ -188,7 +217,7 @@ def stop_rotate(self, context):
self._is_rotating = False
self._rotation = 0.0

def start_extrude(self, mouse_pos_2d, mouse_pos_3d, context):
def start_extrude(self, mouse_pos_2d, context):
self._mouse_pos_2d = mouse_pos_2d
self._is_extruding = True
return True
Expand Down
Binary file modified utils/__pycache__/fc_view_3d_utils.cpython-37.pyc
Binary file not shown.

0 comments on commit 1cfb471

Please sign in to comment.