~ubuntu-branches/ubuntu/trusty/blender/trusty

« back to all changes in this revision

Viewing changes to release/scripts/addons_contrib/mesh_edge_intersection_tools.py

  • Committer: Package Import Robot
  • Author(s): Matteo F. Vescovi
  • Date: 2012-05-12 20:02:22 UTC
  • mfrom: (14.2.16 sid)
  • Revision ID: package-import@ubuntu.com-20120512200222-lznjs2cxzaq96wua
Tags: 2.63a-1
* New upstream bugfix release
  + debian/patches/: re-worked since source code changed

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
'''
2
 
BEGIN GPL LICENSE BLOCK
3
 
 
4
 
This program is free software; you can redistribute it and/or
5
 
modify it under the terms of the GNU General Public License
6
 
as published by the Free Software Foundation; either version 2
7
 
of the License, or (at your option) any later version.
8
 
 
9
 
This program is distributed in the hope that it will be useful,
10
 
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
12
 
GNU General Public License for more details.
13
 
 
14
 
You should have received a copy of the GNU General Public License
15
 
along with this program; if not, write to the Free Software Foundation,
16
 
Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17
 
 
18
 
END GPL LICENCE BLOCK
19
 
'''
20
 
 
21
 
bl_info = {
22
 
    "name": "Edge tools : tinyCAD VTX",
23
 
    "author": "zeffii",
24
 
    "version": (0, 5, 1),
25
 
    "blender": (2, 5, 6),
26
 
    "category": "Mesh",
27
 
    "location": "View3D > EditMode > (w) Specials",
28
 
    "warning": "Still under development",
29
 
    "wiki_url": "http://wiki.blender.org/index.php/"\
30
 
        "Extensions:2.5/Py/Scripts/Modeling/Edge_Slice",
31
 
    "tracker_url": "http://projects.blender.org/tracker/"\
32
 
        "?func=detail&aid=25227"
33
 
   }
34
 
 
35
 
'''
36
 
parts based on Keith (Wahooney) Boshoff, cursor to intersection script and
37
 
Paul Bourke's Shortest Line Between 2 lines, and thanks to PKHG from BA.org
38
 
for attempting to explain things to me that i'm not familiar with.
39
 
TODO: [ ] allow multi selection ( > 2 ) for Slice/Weld intersection mode
40
 
TODO: [ ] streamline this code !
41
 
 
42
 
1) Edge Extend To Edge ( T )
43
 
2) Edge Slice Intersecting ( X )
44
 
3) Edge Project Converging  ( V )
45
 
 
46
 
'''
47
 
 
48
 
import bpy
49
 
import sys
50
 
from mathutils import Vector, geometry
51
 
from mathutils.geometry import intersect_line_line as LineIntersect
52
 
 
53
 
VTX_PRECISION = 1.0e-5 # or 1.0e-6 ..if you need
54
 
 
55
 
#   returns distance between two given points
56
 
def mDist(A, B): return (A-B).length
57
 
 
58
 
 
59
 
#   returns True / False if a point happens to lie on an edge
60
 
def isPointOnEdge(point, A, B):
61
 
    eps = ((mDist(A, B) - mDist(point,B)) - mDist(A,point))
62
 
    if abs(eps) < VTX_PRECISION: return True
63
 
    else:
64
 
        print('distance is ' + str(eps))
65
 
        return False
66
 
 
67
 
 
68
 
#   returns the number of edges that a point lies on.
69
 
def CountPointOnEdges(point, outer_points):
70
 
    count = 0
71
 
    if(isPointOnEdge(point, outer_points[0][0], outer_points[0][1])): count+=1
72
 
    if(isPointOnEdge(point, outer_points[1][0], outer_points[1][1])): count+=1
73
 
    return count
74
 
 
75
 
 
76
 
#   takes Vector List and returns tuple of points in expected order. 
77
 
def edges_to_points(edges):
78
 
    (vp1, vp2) = (Vector((edges[0][0])), Vector((edges[0][1])))
79
 
    (vp3, vp4) = (Vector((edges[1][0])), Vector((edges[1][1])))
80
 
    return (vp1,vp2,vp3,vp4)
81
 
 
82
 
 
83
 
#   takes a list of 4 vectors and returns True or False depending on checks
84
 
def checkIsMatrixCoplanar(verti):
85
 
    (v1, v2, v3, v4) = edges_to_points(verti)   #unpack
86
 
    shortest_line = LineIntersect(v1, v2, v3, v4)
87
 
    if mDist(shortest_line[1], shortest_line[0]) > VTX_PRECISION: return False
88
 
    else: return True
89
 
 
90
 
 
91
 
#   point = the halfway mark on the shortlest line between two lines
92
 
def checkEdges(Edge, obj):
93
 
    (p1, p2, p3, p4) = edges_to_points(Edge)
94
 
    line = LineIntersect(p1, p2, p3, p4)
95
 
    point = ((line[0] + line[1]) / 2) # or point = line[0]
96
 
    return point
97
 
 
98
 
#   returns (object, number of verts, number of edges) && object mode == True
99
 
def GetActiveObject():
100
 
    bpy.ops.object.mode_set(mode='EDIT')
101
 
    bpy.ops.mesh.delete(type='EDGE') # removes edges + verts
102
 
    (vert_count, edge_count) = getVertEdgeCount()
103
 
    (vert_num, edge_num) = (len(vert_count),len(edge_count))
104
 
 
105
 
    bpy.ops.object.mode_set(mode='OBJECT') # to be sure.
106
 
    o = bpy.context.active_object
107
 
    return (o, vert_num, edge_num)
108
 
 
109
 
 
110
 
def AddVertsToObject(vert_count, o, mvX, mvA, mvB, mvC, mvD):
111
 
    o.data.vertices.add(5)
112
 
    pointlist = [mvX, mvA, mvB, mvC, mvD]
113
 
    for vpoint in range(len(pointlist)):
114
 
        o.data.vertices[vert_count+vpoint].co = pointlist[vpoint]
115
 
 
116
 
 
117
 
#   Used when the user chooses to slice/Weld, vX is intersection point
118
 
def makeGeometryWeld(vX,outer_points):
119
 
    (o, vert_count, edge_count) =  GetActiveObject()
120
 
    (vA, vB, vC, vD) =  edges_to_points(outer_points)
121
 
    AddVertsToObject(vert_count, o, vA, vX, vB, vC, vD) # o is the object
122
 
 
123
 
    oe = o.data.edges
124
 
    oe.add(4)
125
 
    oe[edge_count].vertices = [vert_count,vert_count+1]
126
 
    oe[edge_count+1].vertices = [vert_count+2,vert_count+1]
127
 
    oe[edge_count+2].vertices = [vert_count+3,vert_count+1]
128
 
    oe[edge_count+3].vertices = [vert_count+4,vert_count+1]
129
 
 
130
 
 
131
 
#   Used for extending an edge to a point on another edge.
132
 
def ExtendEdge(vX, outer_points, count):
133
 
    (o, vert_count, edge_count) =  GetActiveObject()
134
 
    (vA, vB, vC, vD) =  edges_to_points(outer_points)
135
 
    AddVertsToObject(vert_count, o, vX, vA, vB, vC, vD)
136
 
 
137
 
    oe = o.data.edges
138
 
    oe.add(4)
139
 
    # Candidate for serious optimization.
140
 
    if isPointOnEdge(vX, vA, vB):
141
 
        oe[edge_count].vertices = [vert_count, vert_count+1]
142
 
        oe[edge_count+1].vertices = [vert_count, vert_count+2]
143
 
        # find which of C and D is farthest away from X
144
 
        if mDist(vD, vX) > mDist(vC, vX):
145
 
            oe[edge_count+2].vertices = [vert_count, vert_count+3]
146
 
            oe[edge_count+3].vertices = [vert_count+3, vert_count+4]
147
 
        if mDist(vC, vX) > mDist(vD, vX):
148
 
            oe[edge_count+2].vertices = [vert_count, vert_count+4]
149
 
            oe[edge_count+3].vertices = [vert_count+3, vert_count+4]
150
 
 
151
 
    if isPointOnEdge(vX, vC, vD):
152
 
        oe[edge_count].vertices = [vert_count, vert_count+3]
153
 
        oe[edge_count+1].vertices = [vert_count, vert_count+4]
154
 
        # find which of A and B is farthest away from X 
155
 
        if mDist(vB, vX) > mDist(vA, vX):
156
 
            oe[edge_count+2].vertices = [vert_count, vert_count+1]
157
 
            oe[edge_count+3].vertices = [vert_count+1, vert_count+2]
158
 
        if mDist(vA, vX) > mDist(vB, vX):
159
 
            oe[edge_count+2].vertices = [vert_count, vert_count+2]
160
 
            oe[edge_count+3].vertices = [vert_count+1, vert_count+2]
161
 
 
162
 
 
163
 
#   ProjectGeometry is used to extend two edges to their intersection point.
164
 
def ProjectGeometry(vX, opoint):
165
 
 
166
 
    def return_distance_checked(X, A, B):
167
 
        dist1 = mDist(X, A)
168
 
        dist2 = mDist(X, B)
169
 
        point_choice = min(dist1, dist2)
170
 
        if point_choice == dist1: return A, B
171
 
        else: return B, A
172
 
 
173
 
    (o, vert_count, edge_count) =  GetActiveObject()
174
 
    vA, vB = return_distance_checked(vX, Vector((opoint[0][0])), Vector((opoint[0][1])))
175
 
    vC, vD = return_distance_checked(vX, Vector((opoint[1][0])), Vector((opoint[1][1])))
176
 
    AddVertsToObject(vert_count, o, vX, vA, vB, vC, vD)
177
 
 
178
 
    oe = o.data.edges
179
 
    oe.add(4)
180
 
    oe[edge_count].vertices = [vert_count, vert_count+1]
181
 
    oe[edge_count+1].vertices = [vert_count, vert_count+3]
182
 
    oe[edge_count+2].vertices = [vert_count+1, vert_count+2]
183
 
    oe[edge_count+3].vertices = [vert_count+3, vert_count+4]
184
 
 
185
 
 
186
 
def getMeshMatrix(obj):
187
 
    is_editmode = (obj.mode == 'EDIT')
188
 
    if is_editmode:
189
 
        bpy.ops.object.mode_set(mode='OBJECT')
190
 
 
191
 
    (edges, meshMatrix) = ([],[])
192
 
    mesh = obj.data
193
 
    verts = mesh.vertices
194
 
    for e in mesh.edges:
195
 
        if e.select:
196
 
            edges.append(e)
197
 
 
198
 
    edgenum = 0
199
 
    for edge_to_test in edges:
200
 
        p1 = verts[edge_to_test.vertices[0]].co
201
 
        p2 = verts[edge_to_test.vertices[1]].co
202
 
        meshMatrix.append([Vector(p1),Vector(p2)])
203
 
        edgenum += 1
204
 
 
205
 
    return meshMatrix
206
 
 
207
 
 
208
 
def getVertEdgeCount():
209
 
    bpy.ops.object.mode_set(mode='OBJECT')
210
 
    vert_count = bpy.context.active_object.data.vertices
211
 
    edge_count = bpy.context.active_object.data.edges
212
 
    return (vert_count, edge_count)
213
 
 
214
 
 
215
 
def runCleanUp():
216
 
    bpy.ops.object.mode_set(mode='EDIT')
217
 
    bpy.ops.mesh.select_all(action='TOGGLE')
218
 
    bpy.ops.mesh.select_all(action='TOGGLE')
219
 
    bpy.ops.mesh.remove_doubles(mergedist=VTX_PRECISION)
220
 
    bpy.ops.mesh.select_all(action='TOGGLE') #unselect all
221
 
 
222
 
 
223
 
def initScriptV(context, self):
224
 
    obj = bpy.context.active_object
225
 
    meshMatrix = getMeshMatrix(obj)
226
 
    (vert_count, edge_count) = getVertEdgeCount()
227
 
 
228
 
    #need 2 edges to be of any use.
229
 
    if len(meshMatrix) < 2: 
230
 
        print(str(len(meshMatrix)) +" select, make sure (only) 2 are selected")
231
 
        return
232
 
 
233
 
    #dont go any further if the verts are not coplanar
234
 
    if checkIsMatrixCoplanar(meshMatrix): print("seems within tolerance, proceed")
235
 
    else: 
236
 
        print("check your geometry, or decrease tolerance value")
237
 
        return
238
 
 
239
 
    # if we reach this point, the edges are coplanar
240
 
    # force edit mode
241
 
    bpy.ops.object.mode_set(mode='EDIT')
242
 
    vSel = bpy.context.active_object.data.total_vert_sel
243
 
 
244
 
    if checkEdges(meshMatrix, obj) == None: print("lines dont intersect")
245
 
    else:
246
 
        count = CountPointOnEdges(checkEdges(meshMatrix, obj), meshMatrix)
247
 
        if count == 0:
248
 
            ProjectGeometry(checkEdges(meshMatrix, obj), meshMatrix)
249
 
            runCleanUp()
250
 
        else:
251
 
            print("The intersection seems to lie on 1 or 2 edges already")
252
 
 
253
 
 
254
 
def initScriptT(context, self):
255
 
    obj = bpy.context.active_object
256
 
    meshMatrix = getMeshMatrix(obj)
257
 
    ## force edit mode
258
 
    bpy.ops.object.mode_set(mode='EDIT')
259
 
    vSel = bpy.context.active_object.data.total_vert_sel
260
 
 
261
 
    if len(meshMatrix) != 2:
262
 
        print(str(len(meshMatrix)) +" select 2 edges")
263
 
    else:
264
 
        count = CountPointOnEdges(checkEdges(meshMatrix, obj), meshMatrix)
265
 
        if count == 1:
266
 
            print("Good, Intersection point lies on one of the two edges!")
267
 
            ExtendEdge(checkEdges(meshMatrix, obj), meshMatrix, count)
268
 
            runCleanUp()    #neutral function, for removing potential doubles
269
 
        else:
270
 
            print("Intersection point not on chosen edges")
271
 
 
272
 
 
273
 
def initScriptX(context, self):
274
 
    obj = bpy.context.active_object
275
 
    meshMatrix = getMeshMatrix(obj)
276
 
    ## force edit mode
277
 
    bpy.ops.object.mode_set(mode='EDIT')
278
 
 
279
 
    if len(meshMatrix) != 2:
280
 
        print(str(len(meshMatrix)) +" select, make sure (only) 2 are selected")
281
 
    else:
282
 
        if checkEdges(meshMatrix, obj) == None:
283
 
            print("lines dont intersect")
284
 
        else: 
285
 
            count = CountPointOnEdges(checkEdges(meshMatrix, obj), meshMatrix)
286
 
            if count == 2:
287
 
                makeGeometryWeld(checkEdges(meshMatrix, obj), meshMatrix)
288
 
                runCleanUp()
289
 
 
290
 
 
291
 
class EdgeIntersections(bpy.types.Operator):
292
 
    '''Makes a weld/slice/extend to intersecting edges/lines'''
293
 
    bl_idname = 'mesh.intersections'
294
 
    bl_label = 'Edge tools : tinyCAD VTX'
295
 
    # bl_options = {'REGISTER', 'UNDO'}
296
 
 
297
 
    mode = bpy.props.IntProperty(name = "Mode",
298
 
                    description = "switch between intersection modes",
299
 
                    default = 2)
300
 
 
301
 
    @classmethod
302
 
    def poll(self, context):
303
 
        obj = context.active_object
304
 
        return obj != None and obj.type == 'MESH'
305
 
 
306
 
    def execute(self, context):
307
 
        if self.mode == -1:
308
 
            initScriptV(context, self)
309
 
        if self.mode == 0:
310
 
            initScriptT(context, self)
311
 
        if self.mode == 1:
312
 
            initScriptX(context, self)
313
 
        if self.mode == 2:
314
 
            print("something undefined happened, send me a test case!")
315
 
        return {'FINISHED'}
316
 
 
317
 
 
318
 
def menu_func(self, context):
319
 
    self.layout.operator(EdgeIntersections.bl_idname, text="Edges V Intersection").mode = -1
320
 
    self.layout.operator(EdgeIntersections.bl_idname, text="Edges T Intersection").mode = 0
321
 
    self.layout.operator(EdgeIntersections.bl_idname, text="Edges X Intersection").mode = 1
322
 
 
323
 
def register():
324
 
    bpy.utils.register_class(EdgeIntersections)
325
 
    bpy.types.VIEW3D_MT_edit_mesh_specials.append(menu_func)
326
 
 
327
 
def unregister():
328
 
    bpy.utils.unregister_class(EdgeIntersections)
329
 
    bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_func)
330
 
 
331
 
if __name__ == "__main__":
332
 
    register()