move to release
This commit is contained in:
parent
96a957faf3
commit
3ce7865694
|
@ -0,0 +1,103 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
|
||||
import bpy
|
||||
import bmesh
|
||||
from . import cad_module as cm
|
||||
|
||||
|
||||
def add_line_to_bisection(self):
|
||||
|
||||
obj = bpy.context.object
|
||||
me = obj.data
|
||||
bm = bmesh.from_edit_mesh(me)
|
||||
|
||||
if hasattr(bm.verts, "ensure_lookup_table"):
|
||||
bm.verts.ensure_lookup_table()
|
||||
bm.edges.ensure_lookup_table()
|
||||
|
||||
edges = [e for e in bm.edges if e.select and not e.hide]
|
||||
|
||||
if not len(edges) == 2:
|
||||
msg = "select two coplanar non parallel edges"
|
||||
self.report({"WARNING"}, msg)
|
||||
return
|
||||
|
||||
[[v1, v2], [v3, v4]] = [[v.co for v in e.verts] for e in edges]
|
||||
print('vectors found:\n', v1, '\n', v2, '\n', v3, '\n', v4)
|
||||
|
||||
dist1 = (v1 - v2).length
|
||||
dist2 = (v3 - v4).length
|
||||
bdist = min([dist1, dist2])
|
||||
edge1 = (v1, v2)
|
||||
edge2 = (v3, v4)
|
||||
|
||||
if not cm.test_coplanar(edge1, edge2):
|
||||
msg = "edges must be coplanar non parallel edges"
|
||||
self.report({"WARNING"}, msg)
|
||||
return
|
||||
|
||||
# get pt and pick fartest vertex from (projected) intersections
|
||||
pt = cm.get_intersection(edge1, edge2)
|
||||
far1 = v2 if (v1 - pt).length < (v2 - pt).length else v1
|
||||
far2 = v4 if (v3 - pt).length < (v4 - pt).length else v3
|
||||
# print('intersection: ', pt)
|
||||
|
||||
dex1 = far1 - pt
|
||||
dex2 = far2 - pt
|
||||
dex1 = dex1 * (bdist / dex1.length)
|
||||
dex2 = dex2 * (bdist / dex2.length)
|
||||
pt2 = pt + (dex1).lerp(dex2, 0.5)
|
||||
# print('bisector point:', pt2)
|
||||
|
||||
pt3 = pt2.lerp(pt, 2.0)
|
||||
|
||||
vec1 = bm.verts.new(pt2)
|
||||
vec2 = bm.verts.new(pt)
|
||||
vec3 = bm.verts.new(pt3)
|
||||
bm.edges.new((vec1, vec2))
|
||||
bm.edges.new((vec2, vec3))
|
||||
bmesh.update_edit_mesh(me)
|
||||
# print("done")
|
||||
|
||||
|
||||
class TCLineOnBisection(bpy.types.Operator):
|
||||
'''Generate the bisector of two selected edges'''
|
||||
bl_idname = 'tinycad.linetobisect'
|
||||
bl_label = 'BIX line to bisector'
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
obj = context.active_object
|
||||
return all([obj is not None, obj.type == 'MESH', obj.mode == 'EDIT'])
|
||||
|
||||
def execute(self, context):
|
||||
add_line_to_bisection(self)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_module(__name__)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_module(__name__)
|
|
@ -0,0 +1,167 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
|
||||
import math
|
||||
|
||||
import bpy
|
||||
import bmesh
|
||||
import mathutils
|
||||
from mathutils import geometry
|
||||
from mathutils import Vector
|
||||
|
||||
|
||||
def generate_bmesh_repr(p1, v1, axis, num_verts):
|
||||
'''
|
||||
p1: center of circle (local coordinates)
|
||||
v1: first vertex of circle in (local coordinates)
|
||||
axis: orientation matrix
|
||||
origin: obj.location
|
||||
'''
|
||||
props = bpy.context.scene.tinycad_props
|
||||
rescale = props.rescale
|
||||
|
||||
# generate geometry up front
|
||||
chain = []
|
||||
gamma = 2 * math.pi / num_verts
|
||||
for i in range(num_verts + 1):
|
||||
theta = gamma * i
|
||||
mat_rot = mathutils.Matrix.Rotation(theta, 4, axis)
|
||||
local_point = (mat_rot * ((v1 - p1) * rescale))
|
||||
chain.append(local_point + p1)
|
||||
|
||||
obj = bpy.context.edit_object
|
||||
me = obj.data
|
||||
bm = bmesh.from_edit_mesh(me)
|
||||
|
||||
# add verts
|
||||
v_refs = []
|
||||
for p in chain:
|
||||
v = bm.verts.new(p)
|
||||
v.select = False # this might be a default.. redundant?
|
||||
v_refs.append(v)
|
||||
|
||||
# join verts, daisy chain
|
||||
num_verts = len(v_refs)
|
||||
for i in range(num_verts):
|
||||
idx1 = i
|
||||
idx2 = (i + 1) % num_verts
|
||||
bm.edges.new([v_refs[idx1], v_refs[idx2]])
|
||||
|
||||
bmesh.update_edit_mesh(me, True)
|
||||
|
||||
|
||||
def generate_3PT(pts, obj, nv, mode=1):
|
||||
mw = obj.matrix_world
|
||||
V = Vector
|
||||
nv = max(3, nv)
|
||||
|
||||
# construction
|
||||
v1, v2, v3, v4 = V(pts[0]), V(pts[1]), V(pts[1]), V(pts[2])
|
||||
edge1_mid = v1.lerp(v2, 0.5)
|
||||
edge2_mid = v3.lerp(v4, 0.5)
|
||||
axis = geometry.normal(v1, v2, v4)
|
||||
mat_rot = mathutils.Matrix.Rotation(math.radians(90.0), 4, axis)
|
||||
|
||||
# triangle edges
|
||||
v1_ = ((v1 - edge1_mid) * mat_rot) + edge1_mid
|
||||
v2_ = ((v2 - edge1_mid) * mat_rot) + edge1_mid
|
||||
v3_ = ((v3 - edge2_mid) * mat_rot) + edge2_mid
|
||||
v4_ = ((v4 - edge2_mid) * mat_rot) + edge2_mid
|
||||
|
||||
r = geometry.intersect_line_line(v1_, v2_, v3_, v4_)
|
||||
if r:
|
||||
p1, _ = r
|
||||
cp = mw * p1
|
||||
bpy.context.scene.cursor_location = cp
|
||||
|
||||
if mode == 0:
|
||||
pass
|
||||
|
||||
elif mode == 1:
|
||||
generate_bmesh_repr(p1, v1, axis, nv)
|
||||
|
||||
else:
|
||||
print('not on a circle')
|
||||
|
||||
|
||||
def get_three_verts_from_selection(obj):
|
||||
me = obj.data
|
||||
bm = bmesh.from_edit_mesh(me)
|
||||
|
||||
bm.verts.ensure_lookup_table()
|
||||
bm.edges.ensure_lookup_table()
|
||||
|
||||
return [v.co[:] for v in bm.verts if v.select]
|
||||
|
||||
|
||||
def dispatch(context, mode=0):
|
||||
try:
|
||||
obj = context.edit_object
|
||||
pts = get_three_verts_from_selection(obj)
|
||||
props = context.scene.tinycad_props
|
||||
generate_3PT(pts, obj, props.num_verts, mode)
|
||||
except:
|
||||
print('dispatch failed', mode)
|
||||
|
||||
|
||||
class TCCallBackCCEN(bpy.types.Operator):
|
||||
bl_idname = 'tinycad.reset_circlescale'
|
||||
bl_label = 'CCEN circle reset'
|
||||
bl_options = {'REGISTER'}
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.tinycad_props.rescale = 1
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class TCCircleCenter(bpy.types.Operator):
|
||||
'''Recreate a Circle from 3 selected verts, move 3dcursor its center'''
|
||||
|
||||
bl_idname = 'tinycad.circlecenter'
|
||||
bl_label = 'CCEN circle center from selected'
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def draw(self, context):
|
||||
scn = context.scene
|
||||
l = self.layout
|
||||
col = l.column()
|
||||
|
||||
col.prop(scn.tinycad_props, 'num_verts', text='num verts')
|
||||
row = col.row(align=True)
|
||||
row.prop(scn.tinycad_props, 'rescale', text='rescale')
|
||||
row.operator('tinycad.reset_circlescale', text="", icon="LINK")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
obj = context.edit_object
|
||||
return obj is not None and obj.type == 'MESH'
|
||||
|
||||
def execute(self, context):
|
||||
dispatch(context, mode=1)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_module(__name__)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_module(__name__)
|
|
@ -0,0 +1,84 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
|
||||
import os
|
||||
import bpy
|
||||
|
||||
ICONS = 'BIX CCEN V2X VTX XALL E2F'.split(' ')
|
||||
icon_collection = {}
|
||||
|
||||
|
||||
class TinyCADProperties(bpy.types.PropertyGroup):
|
||||
|
||||
num_verts = bpy.props.IntProperty(
|
||||
min=3, max=60, default=12)
|
||||
|
||||
rescale = bpy.props.FloatProperty(
|
||||
default=1.0,
|
||||
precision=4,
|
||||
min=0.0001)
|
||||
|
||||
|
||||
class VIEW3D_MT_edit_mesh_tinycad(bpy.types.Menu):
|
||||
bl_label = "TinyCAD"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return bool(context.object)
|
||||
|
||||
def draw(self, context):
|
||||
|
||||
pcoll = icon_collection["main"]
|
||||
|
||||
def cicon(name):
|
||||
return pcoll[name].icon_id
|
||||
|
||||
op = self.layout.operator
|
||||
op('tinycad.autovtx', text='VTX | AUTO', icon_value=cicon('VTX'))
|
||||
op('tinycad.vertintersect', text='V2X | Vertex at intersection', icon_value=cicon('V2X'))
|
||||
op('tinycad.intersectall', text='XALL | Intersect selected edges', icon_value=cicon('XALL'))
|
||||
op('tinycad.linetobisect', text='BIX | Bisector of 2 planar edges', icon_value=cicon('BIX'))
|
||||
op('tinycad.circlecenter', text='CCEN | Resurrect circle center', icon_value=cicon('CCEN'))
|
||||
op('tinycad.edge_to_face', text='E2F | Extend Edge to Face', icon_value=cicon('E2F'))
|
||||
|
||||
|
||||
def register_icons():
|
||||
import bpy.utils.previews
|
||||
pcoll = bpy.utils.previews.new()
|
||||
icons_dir = os.path.join(os.path.dirname(__file__), "icons")
|
||||
for icon_name in ICONS:
|
||||
pcoll.load(icon_name, os.path.join(icons_dir, icon_name + '.png'), 'IMAGE')
|
||||
|
||||
icon_collection["main"] = pcoll
|
||||
|
||||
|
||||
def unregister_icons():
|
||||
for pcoll in icon_collection.values():
|
||||
bpy.utils.previews.remove(pcoll)
|
||||
icon_collection.clear()
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_module(__name__)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_module(__name__)
|
|
@ -0,0 +1,95 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
|
||||
import bpy
|
||||
import bmesh
|
||||
from mathutils.geometry import intersect_line_plane
|
||||
|
||||
|
||||
def failure_message(self):
|
||||
self.report({"WARNING"}, 'select 1 face and 1 detached edge')
|
||||
|
||||
|
||||
def extend_vertex(self):
|
||||
|
||||
obj = bpy.context.edit_object
|
||||
me = obj.data
|
||||
bm = bmesh.from_edit_mesh(me)
|
||||
verts = bm.verts
|
||||
faces = bm.faces
|
||||
|
||||
planes = [f for f in faces if f.select]
|
||||
if (len(planes) > 1) or (len(planes) == 0):
|
||||
failure_message(self)
|
||||
return
|
||||
|
||||
plane = planes[0]
|
||||
plane_vert_indices = [v for v in plane.verts[:]]
|
||||
all_selected_vert_indices = [v for v in verts if v.select]
|
||||
|
||||
M = set(plane_vert_indices)
|
||||
N = set(all_selected_vert_indices)
|
||||
O = N.difference(M)
|
||||
O = list(O)
|
||||
|
||||
if not len(O) == 2:
|
||||
failure_message(self)
|
||||
return
|
||||
|
||||
(v1_ref, v1), (v2_ref, v2) = [(i, i.co) for i in O]
|
||||
|
||||
plane_co = plane.calc_center_median()
|
||||
plane_no = plane.normal
|
||||
|
||||
new_co = intersect_line_plane(v1, v2, plane_co, plane_no, False)
|
||||
new_vertex = verts.new(new_co)
|
||||
|
||||
A_len = (v1 - new_co).length
|
||||
B_len = (v2 - new_co).length
|
||||
|
||||
vertex_reference = v1_ref if (A_len < B_len) else v2_ref
|
||||
bm.edges.new([vertex_reference, new_vertex])
|
||||
|
||||
bmesh.update_edit_mesh(me, True)
|
||||
|
||||
|
||||
class TCEdgeToFace(bpy.types.Operator):
|
||||
'''Extend selected edge towards projected intersection with a selected face'''
|
||||
bl_idname = 'tinycad.edge_to_face'
|
||||
bl_label = 'E2F edge to face'
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
ob = context.object
|
||||
return all([bool(ob), ob.type == 'MESH', ob.mode == 'EDIT'])
|
||||
|
||||
def execute(self, context):
|
||||
extend_vertex(self)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_module(__name__)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_module(__name__)
|
|
@ -0,0 +1,91 @@
|
|||
Blender CAD utils
|
||||
=================
|
||||
|
||||
A tiny subset of unmissable CAD functions for Blender 3d.
|
||||
Addon [page on blender.org/wiki](http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Modeling/mesh_tinyCAD) (Which has most of the same info]
|
||||
|
||||
### Installation
|
||||
|
||||
Download the [`installable stable release zip` here](https://github.com/zeffii/mesh_tinyCAD/archive/v1_2_4.zip)
|
||||
|
||||
|
||||
__________________
|
||||
|
||||
|
||||
### OK, what's this all about?
|
||||
|
||||
Dedicated CAD software speeds up drafting significantly with functions like: `Extend`, `Trim`, `Intersect`, `Fillet /w radius` and `Offset /w distance`. At the moment of this writing many of these functions aren't included by default in regular distributions on Blender.org, so i've coded scripts to perform a few of the main features that I missed most.
|
||||
|
||||
My scripts have shortnames: `VTX, V2X, XALL, BIX, CCEN` and are described separately in sections below. `Fillet` and `Offset` are written by zmj100 and can be found [here](http://blenderartists.org/forum/showthread.php?179375).
|
||||
|
||||
|
||||
Since I started this repository: Vertex Fillet / Bevel was added to master. So no more need for a separate addon. (Ctrl+Shift+b)
|
||||
|
||||
### VTX
|
||||
|
||||
The VTX script has lived in contrib distributions of Blender since 2010, with relatively minor changes. The feedback from BlenderArtists has been [overwhelmingly positive](http://blenderartists.org/forum/showthread.php?204836-CAD-Addon-Edge-Tools-(blender-2-6x)). I'm not going to claim it's bug free, but finding any showstopping issues has proven difficult. It now performs V, T or X selection automatically.
|
||||
|
||||
Expect full freedom of orientation, but stuff must really intersect within error margins (`1.5E-6` = tolerance). These kinds of functions are handy for drawing construction lines and fixing up geometry.
|
||||
|
||||
- V : extending two edges towards their _calculated_ intersection point.
|
||||
![V](http://i.imgur.com/zBSciFf.png)
|
||||
|
||||
- T : extending the path of one edge towards another edge.
|
||||
![T](http://i.imgur.com/CDH5oHm.png)
|
||||
|
||||
- X : two edges intersect, their intersection gets a weld vertex. You now have 4 edges and 5 vertices.
|
||||
![X](http://i.imgur.com/kqtX9OE.png)
|
||||
|
||||
|
||||
- Select two edges
|
||||
- hit `Spacebar` and type `vtx` ..select `autoVTX`
|
||||
- Bam. the rest is taken care of.
|
||||
|
||||
|
||||
### X ALL
|
||||
|
||||
Intersect all, it programatically goes through all selected edges and slices them all using any found intersections, then welds them.
|
||||
|
||||
- XALL is fast!
|
||||
![Imgur](http://i.imgur.com/1I7totI.gif)
|
||||
- Select as many edges as you want to intersect.
|
||||
- hit `spacebar` and type `xa` ..select `XALL intersect all edges`
|
||||
|
||||
### V2X (Vertex to Intersection)
|
||||
|
||||
This might be a niche accessory, but sometimes all you want is a vertex positioned on the intersection of two edges. Nothing fancy.
|
||||
|
||||
### BIX (generate Bisector)
|
||||
|
||||
Creates a single edge which is the bisect of two edges.
|
||||
![Imgur](http://i.imgur.com/uzyv1Mv.gif)
|
||||
|
||||
### CCEN (Circle Centers)
|
||||
|
||||
Given either
|
||||
|
||||
- two adjacent edges on the circumference of an incomplete circle
|
||||
- or three vertices (not required to be adjacent)
|
||||
|
||||
this operator will places the 3d cursor at original center of that circle.
|
||||
|
||||
![imgur](https://cloud.githubusercontent.com/assets/619340/5595657/2786f984-9279-11e4-9dff-9db5d5a52a52.gif)
|
||||
|
||||
updated version may become a modal operator to generate a full set of circle vertices, with variable vertex count.
|
||||
|
||||
![imgur demo](https://cloud.githubusercontent.com/assets/619340/5602194/ce613c96-933d-11e4-9879-d2cfc686cb69.gif)
|
||||
|
||||
### E2F (Extend Edge to Selected Face, Edge 2 Face)
|
||||
|
||||
Select a single Edge and a single Polygon (ngon, tri, quad) within the same Object. Execute `W > TinyCAD > E2F`
|
||||
|
||||
![image](https://cloud.githubusercontent.com/assets/619340/12091278/2884820e-b2f6-11e5-9f1b-37ebfdf10cfc.png)
|
||||
|
||||
|
||||
### Why on github?
|
||||
|
||||
The issue tracker, use it.
|
||||
|
||||
- Let me know if these things are broken in new releases. Why? I don't update Blender as often as some so am oblivious to the slow evolution.
|
||||
- If you can make a valid argument for extra functionality and it seems like something I might use or be able to implement for fun, it's going to happen.
|
||||
- I'm always open to pull requests (just don't expect instant approval of something massive, we can talk..you can use your gift of persuasion and sharp objectivism)
|
|
@ -0,0 +1,72 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
|
||||
import bpy
|
||||
import bmesh
|
||||
from mathutils import geometry
|
||||
|
||||
|
||||
def add_vertex_to_intersection():
|
||||
|
||||
obj = bpy.context.object
|
||||
me = obj.data
|
||||
bm = bmesh.from_edit_mesh(me)
|
||||
|
||||
edges = [e for e in bm.edges if e.select]
|
||||
|
||||
if len(edges) == 2:
|
||||
[[v1, v2], [v3, v4]] = [[v.co for v in e.verts] for e in edges]
|
||||
|
||||
iv = geometry.intersect_line_line(v1, v2, v3, v4)
|
||||
if iv:
|
||||
iv = (iv[0] + iv[1]) / 2
|
||||
bm.verts.new(iv)
|
||||
|
||||
# precaution?
|
||||
if hasattr(bm.verts, "ensure_lookup_table"):
|
||||
bm.verts.ensure_lookup_table()
|
||||
|
||||
bm.verts[-1].select = True
|
||||
bmesh.update_edit_mesh(me)
|
||||
|
||||
|
||||
class TCVert2Intersection(bpy.types.Operator):
|
||||
'''Add a vertex at the intersection (projected or real) of two selected edges'''
|
||||
bl_idname = 'tinycad.vertintersect'
|
||||
bl_label = 'V2X vertex to intersection'
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
obj = context.active_object
|
||||
return obj is not None and obj.type == 'MESH' and obj.mode == 'EDIT'
|
||||
|
||||
def execute(self, context):
|
||||
add_vertex_to_intersection()
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_module(__name__)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_module(__name__)
|
|
@ -0,0 +1,183 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
|
||||
import bpy
|
||||
import bmesh
|
||||
import sys
|
||||
|
||||
from . import cad_module as cm
|
||||
|
||||
messages = {
|
||||
'SHARED_VERTEX': 'Shared Vertex, no intersection possible',
|
||||
'PARALLEL_EDGES': 'Edges Parallel, no intersection possible',
|
||||
'NON_PLANAR_EDGES': 'Non Planar Edges, no clean intersection point'
|
||||
}
|
||||
|
||||
|
||||
def add_edges(bm, pt, idxs, fdp):
|
||||
'''
|
||||
this function is a disaster --
|
||||
index updates and ensure_lookup_table() are called before this function
|
||||
and after, and i've tried doing this less verbose but results tend to be
|
||||
less predictable. I'm obviously a terrible coder, but can only spend so
|
||||
much time figuring out this stuff.
|
||||
'''
|
||||
|
||||
v1 = bm.verts.new(pt)
|
||||
|
||||
bm.verts.ensure_lookup_table()
|
||||
bm.edges.ensure_lookup_table()
|
||||
bm.verts.index_update()
|
||||
|
||||
try:
|
||||
for e in idxs:
|
||||
bm.edges.index_update()
|
||||
v2 = bm.verts[e]
|
||||
bm.edges.new((v1, v2))
|
||||
|
||||
bm.edges.index_update()
|
||||
bm.verts.ensure_lookup_table()
|
||||
bm.edges.ensure_lookup_table()
|
||||
|
||||
except Exception as err:
|
||||
print('some failure: details')
|
||||
for l in fdp:
|
||||
print(l)
|
||||
|
||||
sys.stderr.write('ERROR: %s\n' % str(err))
|
||||
print(sys.exc_info()[-1].tb_frame.f_code)
|
||||
print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno))
|
||||
|
||||
|
||||
def remove_earmarked_edges(bm, earmarked):
|
||||
edges_select = [e for e in bm.edges if e.index in earmarked]
|
||||
bmesh.ops.delete(bm, geom=edges_select, context=2)
|
||||
|
||||
|
||||
def perform_vtx(bm, pt, edges, pts, vertex_indices):
|
||||
idx1, idx2 = edges[0].index, edges[1].index
|
||||
fdp = pt, edges, pts, vertex_indices
|
||||
|
||||
# this list will hold those edges that pt lies on
|
||||
edges_indices = cm.find_intersecting_edges(bm, pt, idx1, idx2)
|
||||
mode = 'VTX'[len(edges_indices)]
|
||||
|
||||
if mode == 'V':
|
||||
cl_vert1 = cm.closest_idx(pt, edges[0])
|
||||
cl_vert2 = cm.closest_idx(pt, edges[1])
|
||||
add_edges(bm, pt, [cl_vert1, cl_vert2], fdp)
|
||||
|
||||
elif mode == 'T':
|
||||
to_edge_idx = edges_indices[0]
|
||||
from_edge_idx = idx1 if to_edge_idx == idx2 else idx2
|
||||
|
||||
cl_vert = cm.closest_idx(pt, bm.edges[from_edge_idx])
|
||||
to_vert1, to_vert2 = cm.vert_idxs_from_edge_idx(bm, to_edge_idx)
|
||||
add_edges(bm, pt, [cl_vert, to_vert1, to_vert2], fdp)
|
||||
|
||||
elif mode == 'X':
|
||||
add_edges(bm, pt, vertex_indices, fdp)
|
||||
|
||||
# final refresh before returning to user.
|
||||
if edges_indices:
|
||||
remove_earmarked_edges(bm, edges_indices)
|
||||
|
||||
bm.edges.index_update()
|
||||
return bm
|
||||
|
||||
|
||||
def do_vtx_if_appropriate(bm, edges):
|
||||
vertex_indices = cm.get_vert_indices_from_bmedges(edges)
|
||||
|
||||
# test 1, are there shared vers? if so return non-viable
|
||||
if not len(set(vertex_indices)) == 4:
|
||||
return {'SHARED_VERTEX'}
|
||||
|
||||
# test 2, is parallel?
|
||||
p1, p2, p3, p4 = [bm.verts[i].co for i in vertex_indices]
|
||||
point = cm.get_intersection([p1, p2], [p3, p4])
|
||||
if not point:
|
||||
return {'PARALLEL_EDGES'}
|
||||
|
||||
# test 3, coplanar edges?
|
||||
coplanar = cm.test_coplanar([p1, p2], [p3, p4])
|
||||
if not coplanar:
|
||||
return {'NON_PLANAR_EDGES'}
|
||||
|
||||
# point must lie on an edge or the virtual extention of an edge
|
||||
bm = perform_vtx(bm, point, edges, (p1, p2, p3, p4), vertex_indices)
|
||||
return bm
|
||||
|
||||
|
||||
class TCAutoVTX(bpy.types.Operator):
|
||||
'''Weld intersecting edges, project converging edges towards their intersection'''
|
||||
bl_idname = 'tinycad.autovtx'
|
||||
bl_label = 'VTX autoVTX'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
obj = context.active_object
|
||||
return bool(obj) and obj.type == 'MESH'
|
||||
|
||||
def cancel_message(self, msg):
|
||||
print(msg)
|
||||
self.report({"WARNING"}, msg)
|
||||
return {'CANCELLED'}
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
# final attempt to enter unfragmented bm/mesh
|
||||
# ghastly, but what can I do? it works with these
|
||||
# fails without.
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
obj = context.active_object
|
||||
me = obj.data
|
||||
|
||||
bm = bmesh.from_edit_mesh(me)
|
||||
bm.verts.ensure_lookup_table()
|
||||
bm.edges.ensure_lookup_table()
|
||||
|
||||
edges = [e for e in bm.edges if e.select and not e.hide]
|
||||
|
||||
if len(edges) == 2:
|
||||
message = do_vtx_if_appropriate(bm, edges)
|
||||
if isinstance(message, set):
|
||||
msg = messages.get(message.pop())
|
||||
return self.cancel_message(msg)
|
||||
bm = message
|
||||
else:
|
||||
return self.cancel_message('select two edges!')
|
||||
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
bmesh.update_edit_mesh(me, True)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_module(__name__)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_module(__name__)
|
|
@ -0,0 +1,189 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
|
||||
import bpy
|
||||
import bmesh
|
||||
from mathutils.geometry import intersect_line_line as LineIntersect
|
||||
|
||||
import itertools
|
||||
from collections import defaultdict
|
||||
from . import cad_module as cm
|
||||
|
||||
|
||||
def order_points(edge, point_list):
|
||||
''' order these edges from distance to v1, then
|
||||
sandwich the sorted list with v1, v2 '''
|
||||
v1, v2 = edge
|
||||
def dist(co):
|
||||
return (v1 - co).length
|
||||
point_list = sorted(point_list, key=dist)
|
||||
return [v1] + point_list + [v2]
|
||||
|
||||
|
||||
def remove_permutations_that_share_a_vertex(bm, permutations):
|
||||
''' Get useful Permutations '''
|
||||
final_permutations = []
|
||||
for edges in permutations:
|
||||
raw_vert_indices = cm.vertex_indices_from_edges_tuple(bm, edges)
|
||||
if cm.duplicates(raw_vert_indices):
|
||||
continue
|
||||
|
||||
# reaches this point if they do not share.
|
||||
final_permutations.append(edges)
|
||||
|
||||
return final_permutations
|
||||
|
||||
|
||||
def get_valid_permutations(bm, edge_indices):
|
||||
raw_permutations = itertools.permutations(edge_indices, 2)
|
||||
permutations = [r for r in raw_permutations if r[0] < r[1]]
|
||||
return remove_permutations_that_share_a_vertex(bm, permutations)
|
||||
|
||||
|
||||
def can_skip(closest_points, vert_vectors):
|
||||
'''this checks if the intersection lies on both edges, returns True
|
||||
when criteria are not met, and thus this point can be skipped'''
|
||||
if not closest_points:
|
||||
return True
|
||||
if not isinstance(closest_points[0].x, float):
|
||||
return True
|
||||
if cm.num_edges_point_lies_on(closest_points[0], vert_vectors) < 2:
|
||||
return True
|
||||
|
||||
# if this distance is larger than than VTX_PRECISION, we can skip it.
|
||||
cpa, cpb = closest_points
|
||||
return (cpa - cpb).length > cm.CAD_prefs.VTX_PRECISION
|
||||
|
||||
|
||||
def get_intersection_dictionary(bm, edge_indices):
|
||||
|
||||
if hasattr(bm.verts, "ensure_lookup_table"):
|
||||
bm.verts.ensure_lookup_table()
|
||||
bm.edges.ensure_lookup_table()
|
||||
|
||||
permutations = get_valid_permutations(bm, edge_indices)
|
||||
|
||||
k = defaultdict(list)
|
||||
d = defaultdict(list)
|
||||
|
||||
for edges in permutations:
|
||||
raw_vert_indices = cm.vertex_indices_from_edges_tuple(bm, edges)
|
||||
vert_vectors = cm.vectors_from_indices(bm, raw_vert_indices)
|
||||
|
||||
points = LineIntersect(*vert_vectors)
|
||||
|
||||
# some can be skipped. (NaN, None, not on both edges)
|
||||
if can_skip(points, vert_vectors):
|
||||
continue
|
||||
|
||||
# reaches this point only when an intersection happens on both edges.
|
||||
[k[edge].append(points[0]) for edge in edges]
|
||||
|
||||
# k will contain a dict of edge indices and points found on those edges.
|
||||
for edge_idx, unordered_points in k.items():
|
||||
tv1, tv2 = bm.edges[edge_idx].verts
|
||||
v1 = bm.verts[tv1.index].co
|
||||
v2 = bm.verts[tv2.index].co
|
||||
ordered_points = order_points((v1, v2), unordered_points)
|
||||
d[edge_idx].extend(ordered_points)
|
||||
|
||||
return d
|
||||
|
||||
|
||||
def update_mesh(obj, d):
|
||||
''' Make new geometry (delete old first) '''
|
||||
|
||||
bpy.ops.mesh.delete(type='EDGE')
|
||||
bpy.ops.object.editmode_toggle()
|
||||
|
||||
oe = obj.data.edges
|
||||
ov = obj.data.vertices
|
||||
vert_count = len(ov)
|
||||
edge_count = len(oe)
|
||||
|
||||
for old_edge, point_list in d.items():
|
||||
num_points = len(point_list)
|
||||
num_edges_to_add = num_points - 1
|
||||
|
||||
for i in range(num_edges_to_add):
|
||||
oe.add(1)
|
||||
ov.add(2)
|
||||
|
||||
ov[vert_count].co = point_list[i]
|
||||
ov[vert_count + 1].co = point_list[i + 1]
|
||||
|
||||
oe[edge_count].vertices = [vert_count, vert_count + 1]
|
||||
vert_count = len(ov)
|
||||
edge_count = len(oe)
|
||||
|
||||
# set edit mode
|
||||
bpy.ops.object.editmode_toggle()
|
||||
bpy.ops.mesh.remove_doubles(
|
||||
threshold=cm.CAD_prefs.VTX_DOUBLES_THRSHLD,
|
||||
use_unselected=False)
|
||||
|
||||
|
||||
def unselect_nonintersecting(bm, d_edges, edge_indices):
|
||||
if len(edge_indices) > len(d_edges):
|
||||
reserved_edges = set(edge_indices) - set(d_edges)
|
||||
for edge in reserved_edges:
|
||||
bm.edges[edge].select = False
|
||||
print("unselected {}, non intersecting edges".format(reserved_edges))
|
||||
|
||||
|
||||
class TCIntersectAllEdges(bpy.types.Operator):
|
||||
'''Adds a vertex at the intersections of all selected edges'''
|
||||
bl_idname = 'tinycad.intersectall'
|
||||
bl_label = 'XALL intersect all edges'
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
obj = context.active_object
|
||||
return obj is not None and obj.type == 'MESH' and obj.mode == 'EDIT'
|
||||
|
||||
def execute(self, context):
|
||||
# must force edge selection mode here
|
||||
bpy.context.tool_settings.mesh_select_mode = (False, True, False)
|
||||
|
||||
obj = context.active_object
|
||||
if obj.mode == "EDIT":
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
|
||||
selected_edges = [edge for edge in bm.edges if edge.select]
|
||||
edge_indices = [i.index for i in selected_edges]
|
||||
|
||||
d = get_intersection_dictionary(bm, edge_indices)
|
||||
|
||||
unselect_nonintersecting(bm, d.keys(), edge_indices)
|
||||
update_mesh(obj, d)
|
||||
else:
|
||||
print('must be in edit mode')
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_module(__name__)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_module(__name__)
|
|
@ -0,0 +1,75 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
|
||||
bl_info = {
|
||||
"name": "tinyCAD Mesh tools",
|
||||
"author": "zeffii (aka Dealga McArdle)",
|
||||
"version": (1, 3, 0),
|
||||
"blender": (2, 7, 7),
|
||||
"category": "Mesh",
|
||||
"location": "View3D > EditMode > (w) Specials",
|
||||
"wiki_url": "http://zeffii.github.io/mesh_tiny_cad/",
|
||||
"tracker_url": "https://github.com/zeffii/mesh_tiny_cad/issues"
|
||||
}
|
||||
|
||||
|
||||
if "bpy" in locals():
|
||||
if 'VTX' in locals():
|
||||
|
||||
print('tinyCAD: detected reload event.')
|
||||
import importlib
|
||||
|
||||
try:
|
||||
modules = (CFG, VTX, V2X, XALL, BIX, CCEN, E2F)
|
||||
for m in modules:
|
||||
importlib.reload(m)
|
||||
print("tinyCAD: reloaded modules, all systems operational")
|
||||
|
||||
except Exception as E:
|
||||
print('reload failed with error:')
|
||||
print(E)
|
||||
|
||||
|
||||
import bpy
|
||||
|
||||
from .CFG import TinyCADProperties
|
||||
from .CFG import register_icons, unregister_icons
|
||||
from . import VTX, V2X, XALL, BIX, CCEN, E2F
|
||||
|
||||
|
||||
def menu_func(self, context):
|
||||
self.layout.menu("VIEW3D_MT_edit_mesh_tinycad")
|
||||
self.layout.separator()
|
||||
|
||||
|
||||
def register():
|
||||
register_icons()
|
||||
bpy.utils.register_module(__name__)
|
||||
bpy.types.Scene.tinycad_props = bpy.props.PointerProperty(
|
||||
name="TinyCAD props", type=TinyCADProperties)
|
||||
bpy.types.VIEW3D_MT_edit_mesh_specials.prepend(menu_func)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_func)
|
||||
bpy.utils.unregister_module(__name__)
|
||||
del bpy.types.Scene.tinycad_props
|
||||
unregister_icons()
|
|
@ -0,0 +1,172 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
|
||||
import bmesh
|
||||
|
||||
from mathutils import Vector, geometry
|
||||
from mathutils.geometry import intersect_line_line as LineIntersect
|
||||
from mathutils.geometry import intersect_point_line as PtLineIntersect
|
||||
|
||||
|
||||
class CAD_prefs:
|
||||
VTX_PRECISION = 1.0e-5
|
||||
VTX_DOUBLES_THRSHLD = 0.0001
|
||||
|
||||
|
||||
def point_on_edge(p, edge):
|
||||
'''
|
||||
> p: vector
|
||||
> edge: tuple of 2 vectors
|
||||
< returns: True / False if a point happens to lie on an edge
|
||||
'''
|
||||
pt, _percent = PtLineIntersect(p, *edge)
|
||||
on_line = (pt - p).length < CAD_prefs.VTX_PRECISION
|
||||
return on_line and (0.0 <= _percent <= 1.0)
|
||||
|
||||
|
||||
def line_from_edge_intersect(edge1, edge2):
|
||||
'''
|
||||
> takes 2 tuples, each tuple contains 2 vectors
|
||||
- prepares input for sending to intersect_line_line
|
||||
< returns output of intersect_line_line
|
||||
'''
|
||||
[p1, p2], [p3, p4] = edge1, edge2
|
||||
return LineIntersect(p1, p2, p3, p4)
|
||||
|
||||
|
||||
def get_intersection(edge1, edge2):
|
||||
'''
|
||||
> takes 2 tuples, each tuple contains 2 vectors
|
||||
< returns the point halfway on line. See intersect_line_line
|
||||
'''
|
||||
line = line_from_edge_intersect(edge1, edge2)
|
||||
if line:
|
||||
return (line[0] + line[1]) / 2
|
||||
|
||||
|
||||
def test_coplanar(edge1, edge2):
|
||||
'''
|
||||
the line that describes the shortest line between the two edges
|
||||
would be short if the lines intersect mathematically. If this
|
||||
line is longer than the VTX_PRECISION then they are either
|
||||
coplanar or parallel.
|
||||
'''
|
||||
line = line_from_edge_intersect(edge1, edge2)
|
||||
if line:
|
||||
return (line[0] - line[1]).length < CAD_prefs.VTX_PRECISION
|
||||
|
||||
|
||||
def closest_idx(pt, e):
|
||||
'''
|
||||
> pt: vector
|
||||
> e: bmesh edge
|
||||
< returns: returns index of vertex closest to pt.
|
||||
|
||||
if both points in e are equally far from pt, then v1 is returned.
|
||||
'''
|
||||
if isinstance(e, bmesh.types.BMEdge):
|
||||
ev = e.verts
|
||||
v1 = ev[0].co
|
||||
v2 = ev[1].co
|
||||
distance_test = (v1 - pt).length <= (v2 - pt).length
|
||||
return ev[0].index if distance_test else ev[1].index
|
||||
|
||||
print("received {0}, check expected input in docstring ".format(e))
|
||||
|
||||
|
||||
def closest_vector(pt, e):
|
||||
'''
|
||||
> pt: vector
|
||||
> e: 2 vector tuple
|
||||
< returns:
|
||||
pt, 2 vector tuple: returns closest vector to pt
|
||||
|
||||
if both points in e are equally far from pt, then v1 is returned.
|
||||
'''
|
||||
if isinstance(e, tuple) and all([isinstance(co, Vector) for co in e]):
|
||||
v1, v2 = e
|
||||
distance_test = (v1 - pt).length <= (v2 - pt).length
|
||||
return v1 if distance_test else v2
|
||||
|
||||
print("received {0}, check expected input in docstring ".format(e))
|
||||
|
||||
|
||||
def coords_tuple_from_edge_idx(bm, idx):
|
||||
''' bm is a bmesh representation '''
|
||||
return tuple(v.co for v in bm.edges[idx].verts)
|
||||
|
||||
|
||||
def vectors_from_indices(bm, raw_vert_indices):
|
||||
''' bm is a bmesh representation '''
|
||||
return [bm.verts[i].co for i in raw_vert_indices]
|
||||
|
||||
|
||||
def vertex_indices_from_edges_tuple(bm, edge_tuple):
|
||||
'''
|
||||
> bm: is a bmesh representation
|
||||
> edge_tuple: contains two edge indices.
|
||||
< returns the vertex indices of edge_tuple
|
||||
'''
|
||||
def k(v, w):
|
||||
return bm.edges[edge_tuple[v]].verts[w].index
|
||||
|
||||
return [k(i >> 1, i % 2) for i in range(4)]
|
||||
|
||||
|
||||
def get_vert_indices_from_bmedges(edges):
|
||||
'''
|
||||
> bmedges: a list of two bm edges
|
||||
< returns the vertex indices of edge_tuple as a flat list.
|
||||
'''
|
||||
temp_edges = []
|
||||
print(edges)
|
||||
for e in edges:
|
||||
for v in e.verts:
|
||||
temp_edges.append(v.index)
|
||||
return temp_edges
|
||||
|
||||
|
||||
def num_edges_point_lies_on(pt, edges):
|
||||
''' returns the number of edges that a point lies on. '''
|
||||
res = [point_on_edge(pt, edge) for edge in [edges[:2], edges[2:]]]
|
||||
return len([i for i in res if i])
|
||||
|
||||
|
||||
def find_intersecting_edges(bm, pt, idx1, idx2):
|
||||
'''
|
||||
> pt: Vector
|
||||
> idx1, ix2: edge indices
|
||||
< returns the list of edge indices where pt is on those edges
|
||||
'''
|
||||
if not pt:
|
||||
return []
|
||||
idxs = [idx1, idx2]
|
||||
edges = [coords_tuple_from_edge_idx(bm, idx) for idx in idxs]
|
||||
return [idx for edge, idx in zip(edges, idxs) if point_on_edge(pt, edge)]
|
||||
|
||||
|
||||
def duplicates(indices):
|
||||
return len(set(indices)) < 4
|
||||
|
||||
|
||||
def vert_idxs_from_edge_idx(bm, idx):
|
||||
edge = bm.edges[idx]
|
||||
return edge.verts[0].index, edge.verts[1].index
|
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
Loading…
Reference in New Issue