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


bl_info = {
    "name": "Retopology Tools",
    "author": "Cédric Lepiller", "Gert De Roost for the Laprelax code" 
    "version": (0, 1, 0),
    "blender": (2, 7, 0),
    "location": "View 3D > Toolbar > Tools tab > Retopology (panel)",
    "description": "Tools for fast retopology",
    "category": "3D View"}

############### Operators

import bpy
import bmesh
from mathutils import *
import math

#Double Threshold 0.001
class DoubleThreshold0001(bpy.types.Operator):
    bl_idname = "double.threshold0001"
    bl_label = "Double Threshold 0001"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None
    def execute(self, context):
        bpy.context.scene.tool_settings.double_threshold = 0.001
        return {'FINISHED'}

#Double Threshold 0.1
class DoubleThreshold01(bpy.types.Operator):
    bl_idname = "double.threshold01"
    bl_label = "Double Threshold 01"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None
    def execute(self, context):
        bpy.context.scene.tool_settings.double_threshold = 0.1
        return {'FINISHED'}

#LapRelax
class LapRelax(bpy.types.Operator):
	bl_idname = "mesh.laprelax"
	bl_label = "LapRelax"
	bl_description = "Smoothing mesh keeping volume"
	bl_options = {'REGISTER', 'UNDO'}
	
	Repeat = bpy.props.IntProperty(
		name = "Repeat", 
		description = "Repeat how many times",
		default = 1,
		min = 1,
		max = 100)


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

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


	def do_laprelax(self):
	
		context = bpy.context
		region = context.region  
		area = context.area
		selobj = bpy.context.active_object
		mesh = selobj.data
		bm = bmesh.from_edit_mesh(mesh)
		bmprev = bm.copy()
	
		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
				for e in v.link_edges:
					tot += e.other_vert(v).co
				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()

#Wire on all objects
class Wire_All(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.wire_all"
    bl_label = "Wire on All Objects"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        
        for obj in bpy.data.objects:
            if obj.show_wire:
                obj.show_all_edges = False
                obj.show_wire = False
            
            else:
                obj.show_all_edges = True
                obj.show_wire = True
               
                
        return {'FINISHED'}

#Align to X
class AlignToX(bpy.types.Operator):  
    bl_idname = "object.align2x"  
    bl_label = "Align To X"  
  
    def execute(self, context):
        bpy.ops.object.mode_set(mode = 'OBJECT')
        bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)

        for vert in bpy.context.object.data.vertices:
            if vert.select: 
                vert.co[0] = 0
        bpy.ops.object.editmode_toggle() 
        return {'FINISHED'} 
"""
#Select Ngons
class SelectNGons1(bpy.types.Operator):  
    bl_idname = "object.sngons1"  
    bl_label = "Select Ngons"  
  
    def execute(self, context):
        print("----- SCRIPT -----")
            
        for ob in bpy.data.objects:
            if ob.select:
                if ob.type == "MESH":
                    ob.select = True
                    m = ob.data
                    faces = []
                    for face in m.polygons:
                        face.select = False
                        verts_on_face = face.vertices[:] 
                        if len(verts_on_face) > 4:
                            face.select = True
                            faces.append(face.index)
                                              
                    if len(faces) > 0:
                        ob.select = False
                        print(ob.name)                
                        print(faces)
                        bpy.context.scene.objects.active = ob                  
                        
            bpy.ops.object.mode_set(mode = 'EDIT')
            return {'FINISHED'} 
"""
#################### Panel
  
class RetopologyTools(bpy.types.Panel): 
    
    bl_label = "Retopology Tools"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'
    bl_category = "Retopology"
  
    def draw(self, context): 
        layout = self.layout 
        
        #Tools
        layout.label(text="Tools :")
        
        row = layout.row()
        row.operator("mesh.laprelax")
        row = layout.row()
        row.operator("object.align2x", icon='MOD_WIREFRAME')
        row = layout.row()
        #row.operator("object.sngons1", icon='MESH_ICOSPHERE')
        split = layout.split()
        split = layout.split()
        row = layout.row()
        row.operator("mesh.flip_normals", icon = 'FULLSCREEN_ENTER')
        row = layout.row()
        row.operator("mesh.normals_make_consistent", icon = 'MATCUBE')
        row = layout.row()
        row.operator("mesh.remove_doubles",icon='X_VEC')
        row = layout.row()
        #row.operator("object.join", icon = 'ROTATECENTER')
        #row.operator("mesh.separate", icon = 'ROTATECOLLECTION').type = "LOOSE"
        row = layout.row()
        #Shading
        layout.label(text="Modifiers :")
        row = layout.row()
        row.operator("object.modifier_apply", text="Apply Subsurf", icon='MOD_SUBSURF').modifier="Subsurf"
        row.operator("object.modifier_apply", text="Apply Mirror", icon='MOD_MIRROR').modifier="Mirror"
        
        row = layout.row()
        row.operator("object.modifier_remove", text="Del Subsurf", icon='MOD_SUBSURF').modifier="Subsurf"
        row.operator("object.modifier_remove", text="Del Mirror", icon='MOD_MIRROR').modifier="Mirror"

        #Shading
        layout.label(text="Shading :")
        row = layout.row()
        row.operator("wm.context_toggle", text="Xray", icon='META_CUBE').data_path = "object.show_x_ray"
        row.operator("wm.context_toggle", text="HIWR", icon='GHOST_ENABLED').data_path = "space_data.show_occlude_wire"
        row.operator("wm.context_toggle", text="L2V", icon='ORTHO').data_path = "space_data.use_occlude_geometry"
        row = layout.row()
        
        row.operator("object.wire_all", text="Wire All", icon='WIRE')
        row.operator("object.wire_selected", text="Wire Selected", icon='WIRE')
        row = layout.row()
        row.operator("mesh.faces_shade_smooth", text= "Smooth", icon = 'ANTIALIASED')
        row.operator("mesh.faces_shade_flat", text= "Flat", icon = 'ALIASED')
        
        row = layout.row()
        view = context.space_data
        obj = context.object
        col = layout.column()
        col.prop(view, "show_backface_culling")
        
        row.operator("wm.context_toggle", text="D Sided", icon='MESH_DATA').data_path = "object.data.show_double_sided"
        row.operator("wm.context_toggle", text="A Smooth", icon='MESH_DATA').data_path = "object.data.use_auto_smooth"

        
        #Properties
        layout.label(text="Properties :")
        ob = context.active_object

        tool_settings = context.tool_settings
        mesh = ob.data

        col = layout.column(align=True)
        col.prop(mesh, "use_mirror_x")
        
        row = layout.row()
        row.operator("double.threshold01", text= "TSHD 0.1")
        row.operator("double.threshold0001", text= "TSHD 0.001")
        
        row = layout.row()
        col.label("Double Threshold:")
        col.prop(tool_settings, "double_threshold", text="")
        col = layout.column(align=True)
        
        row = layout.row()
        row.operator("wm.context_toggle", text="Auto Merge", icon='AUTOMERGE_ON').data_path = "scene.tool_settings.use_mesh_automerge"
        
        
  
def register(): 
    bpy.utils.register_class(RetopologyTools) 
    #bpy.utils.register_class(SelectNGons1)
    bpy.utils.register_class(AlignToX)
    bpy.utils.register_class(Wire_All)
    bpy.utils.register_class(LapRelax)
    bpy.utils.register_class(DoubleThreshold01)
    bpy.utils.register_class(DoubleThreshold0001)
    
    
    
    
def unregister(): 
    bpy.utils.unregister_class(RetopologyTools) 
    #bpy.utils.unregister_class(SelectNGons1)
    bpy.utils.unregister_class(AlignToX)
    bpy.utils.unregister_class(Wire_All)
    bpy.utils.unregister_class(LapRelax)
    bpy.utils.unregister_class(DoubleThreshold01)
    bpy.utils.unregister_class(DoubleThreshold0001)
    
    
if __name__ == "__main__": 
    register()