Skip to content

Instantly share code, notes, and snippets.

@zeffii
Forked from anonymous/offset.py
Last active August 29, 2015 14:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeffii/56ed885a3b4fd920f7f7 to your computer and use it in GitHub Desktop.
Save zeffii/56ed885a3b4fd920f7f7 to your computer and use it in GitHub Desktop.
# ##### 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 #####
import bpy
from bpy.props import BoolProperty, StringProperty, IntProperty, FloatProperty
import bmesh
from sverchok.utils.sv_bmesh_utils import bmesh_from_pydata
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import (changable_sockets, multi_socket,
fullList, dataCorrect, updateNode,
SvSetSocketAnyType, SvGetSocketAnyType,
Vector_generate)
from mathutils import Vector, Matrix
from math import tan, sin, cos, degrees, radians
class SvOffsetNode(bpy.types.Node, SverchCustomTreeNode):
''' Doing offset of polygons '''
bl_idname = 'SvOffsetNode'
bl_label = 'Offset Node'
bl_icon = 'OUTLINER_OB_EMPTY'
offset = FloatProperty(name='offset', description='distance of offset',
default=0.04,
options={'ANIMATABLE'}, update=updateNode)
nsides = IntProperty(name='nsides', description='number of sides',
default=1, min=1, max=64,
options={'ANIMATABLE'}, update=updateNode)
radius = FloatProperty(name='redius', description='radius of inset',
default=0.04, min=0.0001,
options={'ANIMATABLE'}, update=updateNode)
def draw_buttons(self, context, layout):
pass
def sv_init(self, context):
self.inputs.new('VerticesSocket', 'Vers', 'Vers')
self.inputs.new('StringsSocket', "Pols", "Pols")
self.inputs.new('StringsSocket', "Offset", "Offset").prop_name = 'offset'
self.inputs.new('StringsSocket', "N sides", "N sides").prop_name = 'nsides'
self.inputs.new('StringsSocket', "Radius", "Radius").prop_name = 'radius'
self.outputs.new('VerticesSocket', 'Vers', 'Vers')
self.outputs.new('StringsSocket', "Edgs", "Edgs")
self.outputs.new('StringsSocket', "OutPols", "OutPols")
self.outputs.new('StringsSocket', "InPols", "InPols")
def process(self):
if self.outputs['Vers'].links and self.inputs['Vers'].links:
vertices = Vector_generate(SvGetSocketAnyType(self, self.inputs['Vers']))
faces = SvGetSocketAnyType(self, self.inputs['Pols'])
offset = self.inputs['Offset'].sv_get()[0]
nsides = self.inputs['N sides'].sv_get()[0][0]
radius = self.inputs['Radius'].sv_get()[0]
#print(radius,nsides,offset)
outv = []
oute = []
outo = []
outn = []
for verts_obj, faces_obj in zip(vertices, faces):
# this is for one object
fullList(offset, len(faces_obj))
fullList(radius, len(faces_obj))
verlen = set(range(len(verts_obj)))
bme = bmesh_from_pydata(verts_obj, [], faces_obj)
geom_in = bme.verts[:]+bme.edges[:]+bme.faces[:]
bmesh.ops.recalc_face_normals(bme, faces=bme.faces[:])
list_0 = [ f.index for f in bme.faces ]
# calculation itself
result = \
self.Offset_pols(bme, list_0, offset, radius, nsides, verlen)
outv.append(result[0])
oute.append(result[1])
outo.append(result[2])
outn.append(result[3])
if self.outputs['Vers'].links:
SvSetSocketAnyType(self, 'Vers', outv)
if self.outputs['Edgs'].links:
SvSetSocketAnyType(self, 'Edgs', oute)
if self.outputs['OutPols'].links:
SvSetSocketAnyType(self, 'OutPols', outo)
if self.outputs['InPols'].links:
SvSetSocketAnyType(self, 'InPols', outn)
# #################
# part from ofset operator in extra tools
# that in own place based completely on addon by zmj100
# in russian is called "Круговая порука", Various artists
# #################
def a_rot(self, ang, rp, axis, q):
return (Matrix.Rotation(ang, 3, axis) * (q - rp)) + rp
# #################
# opp - offset amount
# n_ - number of sides
# adj1 - radius
# en0 - corner type bmesh or triangle, we need bmesh opt0
# kp - keep face, not delete it.
# #################
def Offset_pols(self, bme, list_0, offset, radius, n_, verlen):
#if hasattr(bme.verts, "ensure_lookup_table"):
# bme.verts.ensure_lookup_table()
# bme.faces.ensure_lookup_table()
list_del = [] # to delete old shape polygons
for q, fi in enumerate(list_0):
adj1 = radius[q]
opp = offset[q]
f = bme.faces[fi]
f.select_set(0)
list_del.append(f)
f.normal_update()
list_2 = [v.index for v in f.verts]
dict_0 = {}
list_1 = []
n = len(list_2)
for i in range(n):
dict_0[i] = []
p = (bme.verts[ list_2[i] ].co).copy()
p1 = (bme.verts[ list_2[(i - 1) % n] ].co).copy()
p2 = (bme.verts[ list_2[(i + 1) % n] ].co).copy()
dict_0[i].append(bme.verts[list_2[i]])
vec1 = p - p1
vec2 = p - p2
ang = vec1.angle(vec2)
adj = opp / tan(ang * 0.5)
h = (adj ** 2 + opp ** 2) ** 0.5
if round(degrees(ang)) == 180 or round(degrees(ang)) == 0.0:
p6 = self.a_rot(radians(90), p, vec1, p - \
((f.normal).normalized() * opp))
list_1.append(p6)
else:
p6 = self.a_rot(-radians(90), p, ((p - (vec1.normalized() * adj)) - \
(p - (vec2.normalized() * adj))), p - \
((f.normal).normalized() * h))
list_1.append(p6)
list_2 = []
n1_ = len(list_1)
for j in range(n1_):
q = list_1[j]
q1 = list_1[(j - 1) % n1_]
q2 = list_1[(j + 1) % n1_]
vec1_ = q - q1
vec2_ = q - q2
ang_ = vec1_.angle(vec2_)
if round(degrees(ang_)) == 180 or round(degrees(ang_)) == 0.0:
bme.verts.new(q)
bme.verts.index_update()
if hasattr(bme.verts, "ensure_lookup_table"):
bme.verts.ensure_lookup_table()
list_2.append(bme.verts[-1])
dict_0[j].append(bme.verts[-1])
else:
opp_ = adj1
# radius flag deleted - have to depennd on n_. if 0 no radius
if not n_:
h_ = adj1 * (1 / cos(ang_ * 0.5))
d = adj1
elif n_:
h_ = opp_ / sin(ang_ * 0.5)
d = opp_ / tan(ang_ * 0.5)
q3 = q - (vec1_.normalized() * d)
q4 = q - (vec2_.normalized() * d)
rp_ = q - ((q - ((q3 + q4) * 0.5)).normalized() * h_)
axis_ = vec1_.cross(vec2_)
vec3_ = rp_ - q3
vec4_ = rp_ - q4
rot_ang = vec3_.angle(vec4_)
list_3 = []
for o in range(n_ + 1):
q5 = self.a_rot((rot_ang * o / n_), rp_, axis_, q4)
bme.verts.new(q5)
bme.verts.index_update()
if hasattr(bme.verts, "ensure_lookup_table"):
bme.verts.ensure_lookup_table()
dict_0[j].append(bme.verts[-1])
list_3.append(bme.verts[-1])
list_3.reverse()
list_2.extend(list_3)
# if kp == True: #not solved
bme.faces.new(list_2)
# bme.faces.index_update()
if hasattr(bme.faces, "ensure_lookup_table"):
bme.faces.ensure_lookup_table()
bme.faces[-1].select_set(1)
n2_ = len(dict_0)
for o in range(n2_):
list_a = dict_0[o]
list_b = dict_0[(o + 1) % n2_]
bme.faces.new( [ list_a[0], list_b[0], list_b[-1], list_a[1] ] )
bme.faces.index_update()
# keeping triangulation of polygons commented
#if en0 == 'opt0':
for k in dict_0:
if len(dict_0[k]) > 2:
bme.faces.new(dict_0[k])
bme.faces.index_update()
if hasattr(bme.faces, "ensure_lookup_table"):
# print('yep')
bme.faces.ensure_lookup_table()
#if en0 == 'opt1':
# for k_ in dict_0:
# q_ = dict_0[k_][0]
# dict_0[k_].pop(0)
# n3_ = len(dict_0[k_])
# for kk in range(n3_ - 1):
# bme.faces.new( [ dict_0[k_][kk], dict_0[k_][(kk + 1) % n3_], q_ ] )
# bme.faces.index_update()
# this is old
del_ = [bme.faces.remove(f) for f in list_del]
del del_
# i think, it deletes doubling faces
# remove doubles
bmesh.ops.remove_doubles(bme, dist=0.0001)
# if radius 0 than cleaning loose
# from Linus Yng solidify example
edges = []
faces = []
newpols = []
# clean and assign
bme.verts.index_update()
bme.edges.index_update()
bme.faces.index_update()
for edge in bme.edges[:]:
edges.append([v.index for v in edge.verts[:]])
verts = [vert.co[:] for vert in bme.verts[:]]
for face in bme.faces:
indexes = [v.index for v in face.verts[:]]
if not verlen.intersection(indexes):
newpols.append(indexes)
else:
faces.append(indexes)
bme.clear()
bme.free()
return (verts, edges, faces, newpols)
def register():
bpy.utils.register_class(SvOffsetNode)
def unregister():
bpy.utils.unregister_class(SvOffsetNode)
if __name__ == '__main__':
register()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment