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

#  Filename : Functions0D.py
#  Authors  : Fredo Durand, Stephane Grabli, Francois Sillion, Emmanuel Turquin 
#  Date     : 30/06/2005
#  Purpose  : Functions (functors) to be used for 0D elements

from freestyle import Curvature2DAngleF0D, CurvePoint, ReadCompleteViewMapPixelF0D, \
    ReadSteerableViewMapPixelF0D, UnaryFunction0DDouble, UnaryFunction0DMaterial, \
    UnaryFunction0DVec2f
from freestyle import ContextFunctions as CF

import math
import mathutils

class CurveMaterialF0D(UnaryFunction0DMaterial):
	# A replacement of the built-in MaterialF0D for stroke creation.
	# MaterialF0D does not work with Curves and Strokes.
	def __call__(self, inter):
		cp = inter.object
		assert(isinstance(cp, CurvePoint))
		fe = cp.first_svertex.get_fedge(cp.second_svertex)
		assert(fe is not None)
		return fe.material if fe.is_smooth else fe.material_left

class pyInverseCurvature2DAngleF0D(UnaryFunction0DDouble):
	def __call__(self, inter):
		func = Curvature2DAngleF0D()
		c = func(inter)
		return (3.1415 - c)

class pyCurvilinearLengthF0D(UnaryFunction0DDouble):
	def __call__(self, inter):
		cp = inter.object		
		assert(isinstance(cp, CurvePoint))
		return cp.t2d

## estimate anisotropy of density
class pyDensityAnisotropyF0D(UnaryFunction0DDouble):
	def __init__(self,level):
		UnaryFunction0DDouble.__init__(self)
		self.IsoDensity = ReadCompleteViewMapPixelF0D(level)
		self.d0Density = ReadSteerableViewMapPixelF0D(0, level)
		self.d1Density = ReadSteerableViewMapPixelF0D(1, level)
		self.d2Density = ReadSteerableViewMapPixelF0D(2, level)
		self.d3Density = ReadSteerableViewMapPixelF0D(3, level)
	def __call__(self, inter):
		c_iso = self.IsoDensity(inter) 
		c_0 = self.d0Density(inter) 
		c_1 = self.d1Density(inter) 
		c_2 = self.d2Density(inter) 
		c_3 = self.d3Density(inter) 
		cMax = max(max(c_0,c_1), max(c_2,c_3))
		cMin = min(min(c_0,c_1), min(c_2,c_3))
		if c_iso == 0:
			v = 0
		else:
			v = (cMax-cMin)/c_iso
		return v

## Returns the gradient vector for a pixel 
## 	l
##	  the level at which one wants to compute the gradient
class pyViewMapGradientVectorF0D(UnaryFunction0DVec2f):
	def __init__(self, l):
		UnaryFunction0DVec2f.__init__(self)
		self._l = l
		self._step = math.pow(2,self._l)
	def __call__(self, iter):
		p = iter.object.point_2d
		gx = CF.read_complete_view_map_pixel(self._l, int(p.x+self._step), int(p.y)) - \
		    CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
		gy = CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y+self._step)) - \
		    CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
		return mathutils.Vector([gx, gy])

class pyViewMapGradientNormF0D(UnaryFunction0DDouble):
	def __init__(self, l):
		UnaryFunction0DDouble.__init__(self)
		self._l = l
		self._step = math.pow(2,self._l)
	def __call__(self, iter):
		p = iter.object.point_2d
		gx = CF.read_complete_view_map_pixel(self._l, int(p.x+self._step), int(p.y)) - \
		    CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
		gy = CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y+self._step)) - \
		    CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
		grad = mathutils.Vector([gx, gy])
		return grad.length
