update for new api changes
[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_boundary_tangent(v):
46     e_a, e_b = [e for e in v.link_edges if e.is_boundary][0:2]
47
48     l_a = e_a.link_loops[0]
49     l_b = e_b.link_loops[0]
50
51     # average edge face normal
52     no_face = add_nor(l_a.face.normal, l_b.face.normal)
53
54     # average edge direction
55     v_a = e_a.other_vert(v)
56     v_b = e_b.other_vert(v)
57
58     no_edge = (v_a.co - v.co).normalized() + (v.co - v_b.co).normalized()
59
60     # find the normal
61     no = no_edge.cross(no_face).normalized()
62
63     # check are we flipped the right way
64     ta = e_a.calc_tangent(l_a) + e_b.calc_tangent(l_b)
65     if no.dot(ta) < 0.0:
66         no.negate()
67
68     return no
69
70
71 import math
72
73
74 def shell_angle_to_dist(angle):
75     return 1.0 if angle < 0.000001 else abs(1.0 / math.cos(angle))
76
77
78 # first make 2 verts per vert
79 def solid_wire(bm_src, depth=0.01, use_boundary=True, use_even_offset=True):
80     bm_dst = bmesh.new()
81
82     bm_src.verts.index_update()
83
84     inset = depth
85
86     verts_neg = []
87     verts_pos = []
88
89     is_boundary = False
90
91     if use_boundary:
92         verts_boundary = [None] * len(bm_src.verts)
93
94     for v in bm_src.verts:
95         co = v.co
96         no_scale = v.normal * depth
97         verts_neg.append(bm_dst.verts.new(co - no_scale))
98         verts_pos.append(bm_dst.verts.new(co + no_scale))
99
100         if use_boundary:
101             v.tag = False
102             v.select = False
103
104     verts_loop = []
105
106     for f in bm_src.faces:
107         for l in f.loops:
108             l.index = len(verts_loop)
109             in_scale = l.calc_tangent() * inset
110
111             if use_even_offset:
112                 in_scale *= shell_angle_to_dist((math.pi - l.calc_angle()) * 0.5)
113
114             verts_loop.append(bm_dst.verts.new(l.vert.co + in_scale))
115
116             # boundary
117             if use_boundary:
118                 if l.edge.is_boundary:
119                     for v in (l.vert, l.link_loop_next.vert):
120                         if not v.tag:
121                             is_boundary = True
122                             v.tag = True             # don't copy the vert again
123                             l.edge.tag = False  # we didn't make a face yet
124
125                             v_boundary_tangent = calc_boundary_tangent(v)
126
127                             in_scale = v_boundary_tangent * inset
128
129                             if use_even_offset:
130                                 in_scale *= shell_angle_to_dist((math.pi - l.calc_angle()) * 0.5)
131
132                             v_boundary = verts_boundary[v.index] = bm_dst.verts.new(v.co + in_scale)
133
134                             # TODO, make into generic function
135
136     # build faces
137     for f in bm_src.faces:
138         for l in f.loops:
139             l_next = l.link_loop_next
140             v_l1 = verts_loop[l.index]
141             v_l2 = verts_loop[l_next.index]
142
143             v_src_l1 = l.vert
144             v_src_l2 = l_next.vert
145
146             i_1 = v_src_l1.index
147             i_2 = v_src_l2.index
148
149             v_neg1 = verts_neg[i_1]
150             v_neg2 = verts_neg[i_2]
151
152             v_pos1 = verts_pos[i_1]
153             v_pos2 = verts_pos[i_2]
154
155             bm_dst.faces.new((v_l1, v_l2, v_neg2, v_neg1), f)
156             bm_dst.faces.new((v_l2, v_l1, v_pos1, v_pos2), f)
157
158             #
159             if use_boundary:
160
161                 if v_src_l1.tag and v_src_l2.tag:
162                     # paranoid check, probably not needed
163                     assert(l.edge.is_boundary)
164
165                     # we know we only touch this edge/face once
166                     v_b1 = verts_boundary[i_1]
167                     v_b2 = verts_boundary[i_2]
168                     bm_dst.faces.new((v_b2, v_b1, v_neg1, v_neg2), f)
169                     bm_dst.faces.new((v_b1, v_b2, v_pos2, v_pos1), f)
170
171     return bm_dst
172
173
174 def main(context, kw):
175     scene = bpy.context.scene
176     obact = scene.objects.active
177     me = bpy.data.meshes.new(name=obact.name)
178     ob = bpy.data.objects.new(me.name, me)
179     scene.objects.link(ob)
180     # scene.objects.active = ob
181
182     # can work but messes with undo
183     '''
184     if ob.mode == 'EDIT':
185         bm_src = bmesh.from_edit_mesh(obact.data)
186     else:
187     '''
188     if 1:
189         bm_src = bmesh.new()
190         bm_src.from_mesh(obact.data)
191
192     bm_dst = solid_wire(bm_src,
193                         depth=kw["thickness"],
194                         use_boundary=kw["use_boundary"],
195                         use_even_offset=kw["use_even_offset"])
196     bm_src.free()
197
198     bm_dst.to_mesh(me)
199
200     # copy some settings.
201     ob.matrix_world = obact.matrix_world
202     ob.layers = obact.layers
203     scene.objects.active = ob
204     ob.select = True
205     obact.select = False
206
207
208 # ----------------------------------------------------------------------------
209 # boiler plate code from here on
210
211 from bpy.types import Operator, Panel
212 from bpy.props import BoolProperty, FloatProperty
213
214
215 class SolidWireFrameOperator(Operator):
216     ''''''
217     bl_idname = "mesh.solid_wire_frame"
218     bl_label = "Solid Wireframe"
219     bl_options = {'REGISTER', 'UNDO'}
220
221     thickness = FloatProperty(
222             name="Value",
223             description="Assignment value",
224             default=0.05,
225             min=0.0, max=100.0,
226             soft_min=0.0, soft_max=1.0,
227             )
228
229     use_boundary = BoolProperty(
230             description="Add wire to boundary faces",
231             default=True,
232             )
233
234     use_even_offset = BoolProperty(
235             description="Use even calculations",
236             default=True,
237             )
238
239     def execute(self, context):
240         main(context, self.as_keywords())
241         return {'FINISHED'}
242
243
244 class VIEW3D_PT_solid_wire(Panel):
245     bl_space_type = "VIEW_3D"
246     bl_region_type = "TOOLS"
247     bl_label = "Solid Wire"
248
249     def draw(self, context):
250         self.layout.operator(SolidWireFrameOperator.bl_idname)
251
252
253 '''
254 def menu_func(self, context):
255     self.layout.operator(SolidWireFrameOperator.bl_idname)
256 '''
257
258
259 def register():
260     bpy.utils.register_module(__name__)
261
262     # bpy.types.VIEW3D_MT_edit_mesh_faces.append(menu_func)
263
264
265 def unregister():
266     bpy.utils.unregister_module(__name__)
267
268     # bpy.types.VIEW3D_MT_edit_mesh_faces.remove(menu_func)
269
270 if __name__ == "__main__":
271     register()