Just press ALT+E (it’s in extrude menu)

An internal solution is in the works.
https://developer.blender.org/D5336
Yep, indeed. So extrude and reshape could be like temporal solution until built-in version.
Before “Extrude and reshape” was working better
and “Destructive Extrude” worked perfectly.
Now is working nothing.
Try this.
Save it as ExtrudePull.py
import bpy
from bpy_extras import view3d_utils
from mathutils import Vector, kdtree
from mathutils.geometry import intersect_line_plane
from mathutils.bvhtree import BVHTree
import numpy as np
bl_info = {
"name": "Extrude Pull",
"location": "Edit Mode: Mesh > Extrude > Extrude Pull Geometry",
"description": "Extrude unwanted geometry away",
"author": "Vladislav Kindushov, Martin Capitanio",
"version": (1, 0, 6),
"blender": (2, 80, 0),
"category": "Mesh",
}
def Snap(self, context, location, normal, index, object, matrix):
# Find nearest element for snap.
location = object.matrix_world @ location
BestLocation, tresh4, tresh5 = self.KDTreeSnap.find(location)
# Find nearest direction.
tresh1, BestDirection, tresh2, tresh3 = self.BVHTree.find_nearest(BestLocation)
BestVertex, tresh4, tresh5 = self.KDTree.find(BestLocation)
ToVertex = BestLocation
FromVertex = BestVertex
dvec = ToVertex - BestDirection
dnormal = np.dot(dvec, BestDirection)
SnapPoint = FromVertex + Vector(dnormal * BestDirection)
SnapDistance = (FromVertex - SnapPoint).length
if self.NormalMove:
return SnapDistance
else:
return SnapPoint
def RayCast(self, event, context):
scene = context.scene
region = context.region
rv3d = context.region_data
coord = event.mouse_region_x, event.mouse_region_y
# Get the ray from the viewport and mouse.
view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord).normalized()
ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord)
ray_target = ray_origin + (view_vector * 10000)
matrix = self.MainObject.matrix_world
matrix_inv = matrix.inverted()
ray_origin_obj = matrix_inv @ ray_origin
ray_target_obj = matrix_inv @ ray_target
ray_direction_obj = ray_target_obj - ray_origin_obj
ray_direction_obj.normalize()
result, location, normal, index = self.MainObject.ray_cast(
ray_origin_obj, ray_direction_obj
)
if result:
value = Snap(self, context, location, normal, index, self.MainObject, matrix)
if value is None:
return GetMouseLocation(self, event, context) - self.StartMouseLocation
else:
return value
else:
return GetMouseLocation(self, event, context) - self.StartMouseLocation
def CreateBVHTree(self, context):
bvh = BVHTree.FromObject(
self.ExtrudeObject,
context.evaluated_depsgraph_get(),
deform=False,
cage=False,
epsilon=0.0
)
self.BVHTree = bvh
size = len(self.ExtrudeObject.data.vertices)
kd = kdtree.KDTree(size)
for i in self.ExtrudeObject.data.vertices:
kd.insert(self.ExtrudeObject.matrix_world @ i.co.copy(), i.index)
kd.balance()
self.KDTree = kd
size = len(self.MainObject.data.vertices)
size2 = len(self.MainObject.data.edges)
size3 = len(self.MainObject.data.polygons)
kd2 = kdtree.KDTree(size + size2 + size3)
for i in self.MainObject.data.vertices:
kd2.insert(self.MainObject.matrix_world @ i.co, i.index)
for i in self.MainObject.data.edges:
pos = (
self.MainObject.data.vertices[i.vertices[0]].co +
self.MainObject.data.vertices[i.vertices[1]].co
) / 2
kd2.insert(self.MainObject.matrix_world @ pos, i.index + size)
for i in self.MainObject.data.polygons:
kd2.insert(self.MainObject.matrix_world @ i.center, i.index + size + size2)
kd2.balance()
self.KDTreeSnap = kd2
def CursorPosition(self, context, is_Set=False):
if is_Set and self.CursorLocation != 'NONE':
context.scene.cursor.location = self.CursorLocation
bpy.context.scene.tool_settings.transform_pivot_point = self.PivotPoint
else:
self.CursorLocation = context.scene.cursor.location
self.PivotPoint = context.scene.tool_settings.transform_pivot_point
def CreateNewObject(self, context):
# Duplicate the object.
bpy.ops.mesh.duplicate_move()
bpy.ops.mesh.separate(type='SELECTED')
bpy.ops.object.mode_set(mode='OBJECT')
self.ExtrudeObject = context.selected_objects[-1]
# Clear modifiers.
while len(self.ExtrudeObject.modifiers) != 0:
self.ExtrudeObject.modifiers.remove(self.ExtrudeObject.modifiers[0])
def GetVisualSetings(self, context, isSet=False):
if isSet:
context.active_object.show_all_edges = self.ShowAllEdges
context.active_object.show_wire = self.ShowAllEdges
else:
self.ShowAllEdges = context.active_object.show_all_edges
self.ShowAllEdges = context.active_object.show_wire
def SetVisualSetings(self, context):
self.MainObject.show_all_edges = True
self.MainObject.show_wire = True
self.ExtrudeObject.display_type = 'WIRE'
def GetVisualModifiers(self, context, isSet=False):
if isSet:
for i in self.MainObject.modifiers:
if i.name in self.VisibilityModifiers:
i.show_viewport = True
else:
for i in self.MainObject.modifiers:
if i.show_viewport:
self.VisibilityModifiers.append(i.name)
i.show_viewport = False
def CreateModifier(self, context):
# Set Boolean.
context.view_layer.objects.active = self.MainObject
self.bool = context.object.modifiers.new('DestructiveBoolean', 'BOOLEAN')
bpy.context.object.modifiers["DestructiveBoolean"].operation = 'DIFFERENCE'
bpy.context.object.modifiers["DestructiveBoolean"].object = self.ExtrudeObject
bpy.context.object.modifiers["DestructiveBoolean"].show_viewport = True
# Set Solidify.
context.view_layer.objects.active = self.ExtrudeObject
context.object.modifiers.new('DestructiveSolidify', 'SOLIDIFY')
context.object.modifiers['DestructiveSolidify'].use_even_offset = True
context.object.modifiers['DestructiveSolidify'].offset = -0.99959
def GetMouseLocation(self, event, context):
region = bpy.context.region
rv3d = bpy.context.region_data
coord = event.mouse_region_x, event.mouse_region_y
view_vector_mouse = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord)
ray_origin_mouse = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord)
V_a = ray_origin_mouse + view_vector_mouse
V_b = rv3d.view_rotation @ Vector((0.0, 0.0, -1.0))
pointLoc = intersect_line_plane(ray_origin_mouse, V_a, context.object.location, V_b)
loc = (self.GeneralNormal @ pointLoc) * -1
return loc
def SetSolidifyValue(self, context, value):
self.ExtrudeObject.modifiers[-1].thickness = value
def CalculateNormal(self, context):
for i in self.ExtrudeObject.data.polygons:
self.GeneralNormal += i.normal.copy()
def TransformObject(self, context):
selObj = context.selected_objects
bpy.ops.object.select_all(action='DESELECT')
self.ExtrudeObject.select_set(True)
context.view_layer.objects.active = self.ExtrudeObject
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS')
bpy.ops.view3d.snap_cursor_to_selected()
bpy.context.scene.tool_settings.transform_pivot_point = 'CURSOR'
self.ExtrudeObject.scale = Vector((1.001, 1.001, 1.001))
for i in selObj:
i.select_set(True)
context.view_layer.objects.active = self.MainObject
def GetFaceNormal(self, context):
for i in self.ExtrudeObject.data.polygons:
self.FaceNormal.append(i.normal.copy())
def GetMainVertsIndex(self, context):
for i in self.ExtrudeObject.data.vertices:
self.MainVertsIndex.append(i.index)
def SetForAxis(self, context):
GetMainVertsIndex(self, context)
context.view_layer.objects.active = self.ExtrudeObject
for i in range(0, len(self.MainVertsIndex) - 1):
self.StartVertsPos.append(self.ExtrudeObject.data.vertices[i].co.copy())
index = []
for f in self.ExtrudeObject.data.polygons:
normal = f.normal
for v in f.vertices:
if v not in index:
self.ExtrudeObject.data.vertices[
v
].co = normal * 0.02 + self.ExtrudeObject.data.vertices[v].co.copy()
index.append(v)
self.ExtrudeObject.modifiers[0].thickness = 0.00
self.ExtrudeObject.modifiers[0].offset = 0
bpy.ops.object.modifier_apply(
apply_as='DATA', modifier=self.ExtrudeObject.modifiers[0].name
)
for i in range(len(self.MainVertsIndex) - 1, len(self.ExtrudeObject.data.vertices)):
self.StartVertsPos.append(self.ExtrudeObject.data.vertices[i].co.copy())
context.view_layer.objects.active = self.MainObject
def ReturnStartPosition(self, context):
for i in range(len(self.MainVertsIndex) - 1, len(self.ExtrudeObject.data.vertices)):
self.ExtrudeObject.data.vertices[i].co = self.StartVertsPos[i]
def AxisMove(self, context, value):
axis = Vector()
if self.AxisMove == 'X':
axis = Vector((-1.0, 0.0, 0.0))
elif self.AxisMove == 'Y':
axis = Vector((0.0, -1.0, 0.0))
elif self.AxisMove == 'Z':
axis = Vector((0.0, 0.0, -1.0))
for i in range(len(self.MainVertsIndex), len(self.ExtrudeObject.data.vertices)):
vertPos = ((axis * value) + self.StartVertsPos[i])
self.ExtrudeObject.data.vertices[i].co = vertPos
def Cancel(self, context):
bpy.data.objects.remove(self.ExtrudeObject)
context.view_layer.objects.active = self.MainObject
bpy.ops.object.modifier_remove(modifier='DestructiveBoolean')
GetVisualSetings(self, context, True)
GetVisualModifiers(self, context, True)
bpy.ops.object.mode_set(mode='EDIT')
def Finish(self, context, BevelUpdate=False):
rayCastFace = []
if self.NormalMove:
context.view_layer.objects.active = self.ExtrudeObject
GetMainVertsIndex(self, context)
bpy.ops.object.modifier_apply(
apply_as='DATA', modifier=self.ExtrudeObject.modifiers[0].name
)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.object.mode_set(mode='OBJECT')
context.view_layer.objects.active = self.MainObject
bpy.ops.object.modifier_apply(apply_as='DATA', modifier='DestructiveBoolean')
bpy.context.view_layer.update()
context.active_object.data.update()
context.active_object.data.update(calc_edges=False)
context.active_object.update_tag(refresh={'OBJECT', 'DATA', 'TIME'})
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.object.mode_set(mode='OBJECT')
for f in self.ExtrudeObject.data.polygons:
faceCenter = self.ExtrudeObject.matrix_world @ f.center
faceNormal = self.ExtrudeObject.matrix_world @ f.normal
StartPoint = ((faceNormal * -1) * 0.003) + faceCenter
center = self.MainObject.matrix_world.inverted() @ StartPoint
normal = self.MainObject.matrix_world.inverted() @ faceNormal
result, location, normal, index = self.MainObject.ray_cast(
center, normal, distance=0.005
)
if result:
rayCastFace.append(index)
self.MainObject.data.polygons[index].select = True
bpy.context.scene.tool_settings.transform_pivot_point = 'CURSOR'
bpy.ops.transform.resize(value=(1 - 0.001, 1 - 0.001, 1 - 0.001))
bpy.context.scene.tool_settings.transform_pivot_point = 'MEDIAN_POINT'
for f in self.MainObject.data.polygons:
f.select = False
for f in self.ExtrudeObject.data.polygons:
lose = False
for v in f.vertices:
if v in self.MainVertsIndex:
lose = True
break
if lose:
continue
else:
faceCenter = self.ExtrudeObject.matrix_world @ f.center
faceNormal = self.ExtrudeObject.matrix_world @ f.normal
StartPoint = ((faceNormal * -1) * 0.003) + faceCenter
center = self.MainObject.matrix_world.inverted() @ StartPoint
normal = self.MainObject.matrix_world.inverted() @ faceNormal
result, location, normal, index = self.MainObject.ray_cast(
center, normal, distance=0.005
)
if result:
self.MainObject.data.polygons[index].select = True
bpy.data.objects.remove(self.ExtrudeObject)
GetVisualSetings(self, context, True)
GetVisualModifiers(self, context, True)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.remove_doubles(threshold=0.001, use_unselected=True)
class ExtrudePull(bpy.types.Operator):
bl_idname = "mesh.extrude_pull"
bl_label = "Extrude Pull Geometry"
bl_options = {"REGISTER", "UNDO", "GRAB_CURSOR", "BLOCKING"}
bl_description = "Extrude unwanted geometry away"
@classmethod
def poll(cls, context):
# Disable for Vertex and Edge select mode.
if tuple(bpy.context.tool_settings.mesh_select_mode) == (False, False, True):
if context.active_object.data.count_selected_items()[2] > 0:
return (context.mode == "EDIT_MESH")
return False
def modal(self, context, event):
if event.type == 'MOUSEMOVE':
value = GetMouseLocation(self, event, context) - self.StartMouseLocation
if self.NormalMove:
SetSolidifyValue(self, context, value)
else:
AxisMove(self, context, value)
if event.ctrl:
value = RayCast(self, event, context)
if self.NormalMove:
SetSolidifyValue(self, context, value)
else:
AxisMove(self, context, value)
if event.type == 'X':
if self.NormalMove:
SetForAxis(self, context)
self.NormalMove = False
ReturnStartPosition(self, context)
self.AxisMove = 'X'
if event.type == 'Y':
if self.NormalMove:
SetForAxis(self, context)
self.NormalMove = False
ReturnStartPosition(self, context)
self.AxisMove = 'Y'
if event.type == 'Z':
if self.NormalMove:
SetForAxis(self, context)
self.NormalMove = False
ReturnStartPosition(self, context)
self.AxisMove = 'Z'
if event.type == 'LEFTMOUSE':
Finish(self, context, BevelUpdate=False)
return {'FINISHED'}
if event.type in {'RIGHTMOUSE', 'ESC'}:
Cancel(self, context)
return {'CANCELLED'}
return {'RUNNING_MODAL'}
def invoke(self, context, event):
if context.space_data.type == 'VIEW_3D':
self.KDTreeSnap = None
self.KDTree = None
self.BVHTree = None
self.PivotPoint = None
self.MainVertsIndex = []
self.AxisMove = 'Z'
self.StartVertsPos = []
self.NormalMove = True
self.GeneralNormal = Vector((0.0, 0.0, 0.0))
self.FaceNormal = []
self.ShowAllEdges = None
self.ShowWire = None
self.CursorLocation = None
self.VisibilityModifiers = []
self.MainObject = context.active_object
self.ExtrudeObject = None
self.SaveSelectFaceForCancel = None
GetVisualModifiers(self, context)
GetVisualSetings(self, context)
CursorPosition(self, context)
CreateNewObject(self, context)
CreateBVHTree(self, context)
CreateModifier(self, context)
SetVisualSetings(self, context)
TransformObject(self, context)
CalculateNormal(self, context)
self.StartMouseLocation = GetMouseLocation(self, event, context)
# print('StartMouseLocation', self.StartMouseLocation)
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
else:
self.report({'WARNING'}, "The operator is not called in 3D Viewport.")
return {'CANCELLED'}
classes = (ExtrudePull)
def operator_draw(self, context):
layout = self.layout
col = layout.column(align=True)
self.layout.operator_context = 'INVOKE_REGION_WIN'
col.operator("mesh.extrude_pull", text="Extrude Pull Geometry")
def register():
bpy.utils.register_class(classes)
bpy.types.VIEW3D_MT_edit_mesh_extrude.append(operator_draw)
def unregister():
bpy.utils.unregister_class(classes)
bpy.types.VIEW3D_MT_edit_mesh_extrude.remove(operator_draw)
if __name__ == "__main__":
register()
Thanks, partially works, but there are a lot of mistakes.
Edit: but for easy tasks it is working well.
Specially making holes through is really good.
But still it is very easy to make bad topology.
Did you make that one by yourself? (Don’t recognize the names in the info block)
No, I didn’t write the script.
But I just see that there is a new version (1.07).
It would be nice to get an updated video showing how the same features of the deprecated paid addon can be accessed in blender stock version if they have been merged. It’s a little confusing as it is right now
Hopefully when the re-do the boolean operations in blender, that will fix the extrude issues as well…
Although Undo is a bigger issue for me than anything else… waiting 10-20-30 seconds per object undo.
That’s fixed for me not for all case but in the 2.83 beta , i can see big improvements in undo speed.
The last operation that are problematic are describe in their to do (undo duplicate object and delete are still slow in some case). But i can model on big object and undo modeling task as quick as i was on a low poly.
Thank you for the awesome addon! It’s really useful.
Is there a way to remap hotkeys for Y/Z/X constraints?
I tried to change all the values from ‘Y’,‘Z’,‘X’ to ‘A’,'S,‘D’ in two files: common_classes.py and drawing_utilities.py. However it cause some errors.
Hello, I’m currently using this add-on in Blender 2.82, and I cannot apply textures to anything I make with it.
If I use the Add function in Edit mode to make a Plane, I can apply textures to it just fine with this method shown here: https://www.youtube.com/watch?v=r5YNJghc81U
If I use the Make Line tool to make almost the exact same plane (with the Create Faces function on), doing that method just results in weird colors and no visible texture.
Do I need to use a different method to apply textures with the Make Lines Tool?
The move tool from this addon is exactly what I have been looking for having come to Blender from CAD software. I see that the Snap Utilities addon is no longer available. Does anyone know of another addon that has similar functionality as the move tool (and perhaps the rotate tool)?
Hi Xayzer,
look at this
Thank you very much, @renderthings! This looks just like what I’m looking for!
@mano-wii
Hello,
thanks for addon!
Can it make perpendicular cut from starting point? like it does to the destination edge
and option to cut only mesh will be very useful (to not make points in space)
Hi mano,
the addon-on looks great.
If I understood correctly, the full add-on will be intergrated into a future Blender release?
That’s the reason purchasing of the add-on is disabled on gumroad? Is there any way to test?
Thanks
I can’t speak for @mano-wii. The line part of the add-on is integrated in Blender. You can activate it in the add-on page. It’s not perfect and is probably not developed anymore, but I use it often. The other parts are nowhere to be seen.
There is a similar add-on called Construction Lines that may or may not be new to you. It’s similar to the full CAD Snap Utilities and with version 0.9.5 that is soon to be released, Construction Lines will probably cover a lot of your needs in a easy way. You can find it in this forum on this page.
As for CAD Snap Utilities, I would love to see it being developed as a add-on or a part of Blender, but it does not seam lightly.

