SVN maintenance.
[blender-addons-contrib.git] / mesh_solidify_wireframe_alt.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 bl_info = {
20     "name": "Solid Wire Frame ALT",
21     "author": "Campbell Barton",
22     "version": (1, 0),
23     "blender": (2, 6, 2),
24     "location": "3D View Toolbar",
25     "description": "Make solid wire",
26     "warning": "",  # used for warning icon and text in addons panel
27     # "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/"
28     #             "Scripts/Modeling/Mesh_WireFrane',
29     # "tracker_url": "https://projects.blender.org/tracker/index.php?"
30     #                "func=detail&aid=22929",
31     "category": "Mesh"}
32
33
34 import bmesh
35 import bpy
36
37
38 def add_nor(no_a, no_b):
39     if no_a.dot(no_b) > 0.0:
40         return no_a + no_b
41     else:
42         return no_a - no_b
43
44
45 def calc_edge_tangent(e):
46     # TODO, make builtin, would make loop the arg
47     l = e.link_loops[0]
48     return l.face.normal.cross((l.vert.co - l.link_loop_next.vert.co))
49
50
51 def calc_boundary_tangent(v):
52     e_a, e_b = [e for e in v.link_edges if e.is_boundary][0:2]
53
54     # average edge face normal
55     no_face = add_nor(e_a.link_loops[0].face.normal,
56                       e_b.link_loops[0].face.normal,
57                       )
58
59     # average edge direction
60     v_a = e_a.other_vert(v)
61     v_b = e_b.other_vert(v)
62
63     no_edge = (v_a.co - v.co).normalized() + (v.co - v_b.co).normalized()
64
65     # find the normal
66     no = no_edge.cross(no_face).normalized()
67
68     # check are we flipped the right way
69     ta = calc_edge_tangent(e_a) + calc_edge_tangent(e_b)
70     if no.dot(ta) < 0.0:
71         no.negate()
72
73     return no
74
75
76 import math
77
78
79 def shell_angle_to_dist(angle):
80     return 1.0 if angle < 0.000001 else abs(1.0 / math.cos(angle))
81
82
83 # first make 2 verts per vert
84 def solid_wire(bm_src, depth=0.01, use_boundary=True, use_even_offset=True):
85     bm_dst = bmesh.new()
86
87     bm_src.verts.index_update()
88
89     inset = depth
90
91     verts_neg = []
92     verts_pos = []
93
94     is_boundary = False
95
96     if use_boundary:
97         verts_boundary = [None] * len(bm_src.verts)
98
99     for v in bm_src.verts:
100         co = v.co
101         no_scale = v.normal * depth
102         verts_neg.append(bm_dst.verts.new(co - no_scale))
103         verts_pos.append(bm_dst.verts.new(co + no_scale))
104
105         if use_boundary:
106             v.tag = False
107             v.select = False
108
109     verts_loop = []
110
111     for f in bm_src.faces:
112         for l in f.loops:
113             l.index = len(verts_loop)
114             in_scale = l.calc_tangent() * inset
115
116             if use_even_offset:
117                 in_scale *= shell_angle_to_dist((math.pi - l.calc_angle()) * 0.5)
118
119             verts_loop.append(bm_dst.verts.new(l.vert.co + in_scale))
120
121             # boundary
122             if use_boundary:
123                 if l.edge.is_boundary:
124                     for v in (l.vert, l.link_loop_next.vert):
125                         if not v.tag:
126                             is_boundary = True
127                             v.tag = True             # don't copy the vert again
128                             l.edge.tag = False  # we didn't make a face yet
129
130                             v_boundary_tangent = calc_boundary_tangent(v)
131
132                             in_scale = v_boundary_tangent * inset
133
134                             if use_even_offset:
135                                 in_scale *= shell_angle_to_dist((math.pi - l.calc_angle()) * 0.5)
136
137                             v_boundary = verts_boundary[v.index] = bm_dst.verts.new(v.co + in_scale)
138
139                             # TODO, make into generic function
140
141     # build faces
142     for f in bm_src.faces:
143         for l in f.loops:
144             l_next = l.link_loop_next
145             v_l1 = verts_loop[l.index]
146             v_l2 = verts_loop[l_next.index]
147
148             v_src_l1 = l.vert
149             v_src_l2 = l_next.vert
150
151             i_1 = v_src_l1.index
152             i_2 = v_src_l2.index
153
154             v_neg1 = verts_neg[i_1]
155             v_neg2 = verts_neg[i_2]
156
157             v_pos1 = verts_pos[i_1]
158             v_pos2 = verts_pos[i_2]
159
160             bm_dst.faces.new((v_l1, v_l2, v_neg2, v_neg1), f)
161             bm_dst.faces.new((v_l2, v_l1, v_pos1, v_pos2), f)
162
163             #
164             if use_boundary:
165
166                 if v_src_l1.tag and v_src_l2.tag:
167                     # paranoid check, probably not needed
168                     assert(l.edge.is_boundary)
169
170                     # we know we only touch this edge/face once
171                     v_b1 = verts_boundary[i_1]
172                     v_b2 = verts_boundary[i_2]
173                     bm_dst.faces.new((v_b2, v_b1, v_neg1, v_neg2), f)
174                     bm_dst.faces.new((v_b1, v_b2, v_pos2, v_pos1), f)
175
176     return bm_dst
177
178
179 def main(context, kw):
180     scene = bpy.context.scene
181     obact = scene.objects.active
182     me = bpy.data.meshes.new(name=obact.name)
183     ob = bpy.data.objects.new(me.name, me)
184     scene.objects.link(ob)
185     # scene.objects.active = ob
186
187     # can work but messes with undo
188     '''
189     if ob.mode == 'EDIT':
190         bm_src = bmesh.from_edit_mesh(obact.data)
191     else:
192     '''
193     if 1:
194         bm_src = bmesh.new()
195         bm_src.from_mesh(obact.data)
196
197     bm_dst = solid_wire(bm_src,
198                         depth=kw["thickness"],
199                         use_boundary=kw["use_boundary"],
200                         use_even_offset=kw["use_even_offset"])
201     bm_src.free()
202
203     bm_dst.to_mesh(me)
204
205     # copy some settings.
206     ob.matrix_world = obact.matrix_world
207     ob.layers = obact.layers
208     scene.objects.active = ob
209     ob.select = True
210     obact.select = False
211
212
213 # ----------------------------------------------------------------------------
214 # boiler plate code from here on
215
216 from bpy.types import Operator, Panel
217 from bpy.props import BoolProperty, FloatProperty
218
219
220 class SolidWireFrameOperator(Operator):
221     ''''''
222     bl_idname = "mesh.solid_wire_frame"
223     bl_label = "Solid Wireframe"
224     bl_options = {'REGISTER', 'UNDO'}
225
226     thickness = FloatProperty(
227             name="Value",
228             description="Assignment value",
229             default=0.05,
230             min=0.0, max=100.0,
231             soft_min=0.0, soft_max=1.0,
232             )
233
234     use_boundary = BoolProperty(
235             description="Add wire to boundary faces",
236             default=True,
237             )
238
239     use_even_offset = BoolProperty(
240             description="Use even calculations",
241             default=True,
242             )
243
244     def execute(self, context):
245         main(context, self.as_keywords())
246         return {'FINISHED'}
247
248
249 class VIEW3D_PT_solid_wire(Panel):
250     bl_space_type = "VIEW_3D"
251     bl_region_type = "TOOLS"
252     bl_label = "Solid Wire"
253
254     def draw(self, context):
255         self.layout.operator(SolidWireFrameOperator.bl_idname)
256
257
258 '''
259 def menu_func(self, context):
260     self.layout.operator(SolidWireFrameOperator.bl_idname)
261 '''
262
263
264 def register():
265     bpy.utils.register_module(__name__)
266
267     # bpy.types.VIEW3D_MT_edit_mesh_faces.append(menu_func)
268
269
270 def unregister():
271     bpy.utils.unregister_module(__name__)
272
273     # bpy.types.VIEW3D_MT_edit_mesh_faces.remove(menu_func)
274
275 if __name__ == "__main__":
276     register()