# ##### 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 #####

__bpydoc__ = """\
This addon implements a smoothing algorithm for meshes based on Laplacian relaxation, but adapted
to have no shrinkage.


Documentation
0
First go to User Preferences->Addons and enable the LapRelax addon in the Mesh category.
Go to EditMode, select some vertices and invoke the addon (button in the Mesh Tool panel).
Set the amount of times you want the smoothing operation to be repeated.
The mesh will be smoothed.

If you wish to hotkey LapRelax:
In the Input section of User Preferences at the bottom of the 3D View > Mesh section click 'Add New' button.
In the Operator Identifier box put 'mesh.laprelax'.
Assign a hotkey.
Save as Default (Optional).
"""


bl_info = {
	"name": "LapRelax",
	"author": "Gert De Roost",
	"version": (0, 3, 6),
	"blender": (2, 80, 0),
	"location": "View3D > Tools",
	"description": "Smoothing mesh keeping volume",
	"warning": "",
	"wiki_url": "",
	"tracker_url": "",
	"category": "Mesh"}



import bpy
import bmesh
from mathutils import *
import math


class MyProps(bpy.types.PropertyGroup):
	Repeat: bpy.props.IntProperty(
		name = "Repeat", 
		description = "How many times to repeat",
		default = 1,
		min = 1,
		max = 100)


class MESH_OT_LapRelax(bpy.types.Operator):
	bl_idname = "mesh.laprelax"
	bl_label = "LapRelax"
	bl_description = "Smoothing mesh keeping volume"
	bl_options = {'REGISTER', 'UNDO'}
	

	@classmethod
	def poll(cls, context):
		obj = context.active_object
		return (obj and (obj.type in ['MESH']))

	def invoke(self, context, event):
		
		# smooth #Repeat times
		for i in range(context.scene.my_props.Repeat):
			self.do_laprelax()
		
		return {'FINISHED'}


	def do_laprelax(self):
	
		context = bpy.context
		scn = context.scene
		region = context.region	 
		area = context.area
		selobj = bpy.context.object
		mesh = selobj.data
		bpy.ops.object.editmode_toggle()
		bpy.ops.object.editmode_toggle()
		bm = bmesh.from_edit_mesh(mesh)
		bmprev = bmesh.new()
		bmprev.from_mesh(mesh, face_normals=True, vertex_normals=True, use_shape_key=False, shape_key_index=0)

		for v in bmprev.verts:
			if v.select:
				tot = Vector((0, 0, 0))
				cnt = 0
				for e in v.link_edges:
					for f in e.link_faces:
						if not(f.select):
							cnt = 1
					if len(e.link_faces) == 1:
						cnt = 1
						break
				if cnt:
					# dont affect border edges: they cause shrinkage
					continue
					
				# find Laplacian mean
				bm.verts.ensure_lookup_table()
				for e in v.link_edges:
					tot += bm.verts[e.other_vert(v).index].co
				if len(v.link_edges) != 0:
					tot /= len(v.link_edges)
				
				# cancel movement in direction of vertex normal
				delta = (tot - v.co)
				if delta.length != 0:
					ang = delta.angle(v.normal)
					deltanor = math.cos(ang) * delta.length
					nor = v.normal
					nor.length = abs(deltanor)
					bm.verts[v.index].co = tot + nor
			
			
		mesh.update()
		bm.free()
		bmprev.free()
		bpy.ops.object.editmode_toggle()
		bpy.ops.object.editmode_toggle()



class PANEL_PT_LapRelaxPanel(bpy.types.Panel):
	bl_label = "LapRelax"
	bl_space_type = 'VIEW_3D'
	bl_region_type = 'UI'
	bl_category = 'LapRelax'

	@classmethod
	def poll(cls, context):
		obj = context.active_object
		return (obj and (obj.type in ['MESH']) and context.mode == 'EDIT_MESH')

	def draw(self, context):
   
		self.layout.use_property_split = True
		scn = context.scene
		props = self.layout.operator("mesh.laprelax", text="Do Relax")
		self.layout.prop(scn.my_props, "Repeat")


classes = (
	MyProps,
	MESH_OT_LapRelax,
	PANEL_PT_LapRelaxPanel,
)

def register():
	from bpy.utils import register_class
	for cls in classes:
		register_class(cls)
	bpy.types.Scene.my_props = bpy.props.PointerProperty(type=MyProps)


def unregister():
	from bpy.utils import unregister_class
	for cls in classes:
		unregister_class(cls)


if __name__ == "__main__":
	register()




	
