void Surface_DifferentialProperties_Plugin::computeNormal(
	const QString& mapName,
	const QString& positionAttributeName,
	const QString& normalAttributeName,
	bool autoUpdate)
{
	MapHandler<PFP2>* mh = static_cast<MapHandler<PFP2>*>(m_schnapps->getMap(mapName));
	if(mh == NULL)
		return;

	VertexAttribute<PFP2::VEC3, PFP2::MAP> position = mh->getAttribute<PFP2::VEC3, VERTEX>(positionAttributeName);
	if(!position.isValid())
		return;

	VertexAttribute<PFP2::VEC3, PFP2::MAP> normal = mh->getAttribute<PFP2::VEC3, VERTEX>(normalAttributeName);
	if(!normal.isValid())
		normal = mh->addAttribute<PFP2::VEC3, VERTEX>(normalAttributeName);

	PFP2::MAP* map = mh->getMap();
	Algo::Surface::Geometry::computeNormalVertices<PFP2>(*map, position, normal);

	computeNormalLastParameters[mapName] =
		ComputeNormalParameters(positionAttributeName, normalAttributeName, autoUpdate);

	mh->notifyAttributeModification(normal);

	//this->pythonRecording("computeNormal", "", mh->getName(), QString(position.name().c_str()), QString(normal.name().c_str()), autoUpdate);
	this->pythonRecording("computeNormal", "", mh->getName(), positionAttributeName, normalAttributeName, autoUpdate);

}
void Surface_DifferentialProperties_Plugin::computeCurvature(
	const QString& mapName,
	const QString& positionAttributeName,
	const QString& normalAttributeName,
	const QString& KmaxAttributeName,
	const QString& kmaxAttributeName,
	const QString& KminAttributeName,
	const QString& kminAttributeName,
	const QString& KnormalAttributeName,
	bool compute_kmean,
	bool compute_kgaussian,
	bool autoUpdate)
{
	MapHandler<PFP2>* mh = static_cast<MapHandler<PFP2>*>(m_schnapps->getMap(mapName));
	if(mh == NULL)
		return;

	VertexAttribute<PFP2::VEC3, PFP2::MAP> position = mh->getAttribute<PFP2::VEC3, VERTEX>(positionAttributeName);
	if(!position.isValid())
		return;

	VertexAttribute<PFP2::VEC3, PFP2::MAP> normal = mh->getAttribute<PFP2::VEC3, VERTEX>(normalAttributeName);
	if(!normal.isValid())
		return;

	VertexAttribute<PFP2::VEC3, PFP2::MAP> Kmax = mh->getAttribute<PFP2::VEC3, VERTEX>(KmaxAttributeName);
	if(!Kmax.isValid())
		Kmax = mh->addAttribute<PFP2::VEC3, VERTEX>(KmaxAttributeName);

	VertexAttribute<PFP2::REAL, PFP2::MAP> kmax = mh->getAttribute<PFP2::REAL, VERTEX>(kmaxAttributeName);
	if(!kmax.isValid())
		kmax = mh->addAttribute<PFP2::REAL, VERTEX>(kmaxAttributeName);

	VertexAttribute<PFP2::VEC3, PFP2::MAP> Kmin = mh->getAttribute<PFP2::VEC3, VERTEX>(KminAttributeName);
	if(!Kmin.isValid())
		Kmin = mh->addAttribute<PFP2::VEC3, VERTEX>(KminAttributeName);

	VertexAttribute<PFP2::REAL, PFP2::MAP> kmin = mh->getAttribute<PFP2::REAL, VERTEX>(kminAttributeName);
	if(!kmin.isValid())
		kmin = mh->addAttribute<PFP2::REAL, VERTEX>(kminAttributeName);

	VertexAttribute<PFP2::VEC3, PFP2::MAP> Knormal = mh->getAttribute<PFP2::VEC3, VERTEX>(KnormalAttributeName);
	if(!Knormal.isValid())
		Knormal = mh->addAttribute<PFP2::VEC3, VERTEX>(KnormalAttributeName);

	EdgeAttribute<PFP2::REAL, PFP2::MAP> edgeAngle = mh->getAttribute<PFP2::REAL, EDGE>("edgeAngle");
	if(!edgeAngle.isValid())
		edgeAngle = mh->addAttribute<PFP2::REAL, EDGE>("edgeAngle");

	EdgeAttribute<PFP2::REAL, PFP2::MAP> edgeArea = mh->getAttribute<PFP2::REAL, EDGE>("edgeArea");
	if(!edgeArea.isValid())
		edgeArea = mh->addAttribute<PFP2::REAL, EDGE>("edgeArea");

	PFP2::MAP* map = mh->getMap();

	Algo::Surface::Geometry::computeAnglesBetweenNormalsOnEdges<PFP2>(*map, position, edgeAngle);
	Algo::Surface::Geometry::computeAreaEdges<PFP2>(*map, position, edgeArea);

	PFP2::REAL meanEdgeLength = Algo::Geometry::meanEdgeLength<PFP2>(*map, position);

	float radius = 2.0f * meanEdgeLength;
	Algo::Surface::Geometry::computeCurvatureVertices_NormalCycles_Projected<PFP2>(*map, radius, position, normal, edgeAngle, edgeArea, kmax, kmin, Kmax, Kmin, Knormal);

	computeCurvatureLastParameters[mapName] =
		ComputeCurvatureParameters(
			positionAttributeName, normalAttributeName,
			KmaxAttributeName, kmaxAttributeName, KminAttributeName, kminAttributeName, KnormalAttributeName,
			compute_kmean, compute_kgaussian, autoUpdate);

	mh->notifyAttributeModification(Kmax);
	mh->notifyAttributeModification(kmax);
	mh->notifyAttributeModification(Kmin);
	mh->notifyAttributeModification(kmin);
	mh->notifyAttributeModification(Knormal);

	if(compute_kmean)
	{
		VertexAttribute<PFP2::REAL, PFP2::MAP> kmean = mh->getAttribute<PFP2::REAL, VERTEX>("kmean");
		if(!kmean.isValid())
			kmean = mh->addAttribute<PFP2::REAL, VERTEX>("kmean");

		for(unsigned int i = kmin.begin(); i != kmin.end(); kmin.next(i))
			kmean[i] = (kmin[i] + kmax[i]) / 2.0;

		mh->notifyAttributeModification(kmean);
	}

	if(compute_kgaussian)
	{
		VertexAttribute<PFP2::REAL, PFP2::MAP> kgaussian = mh->getAttribute<PFP2::REAL, VERTEX>("kgaussian");
		if(!kgaussian.isValid())
			kgaussian = mh->addAttribute<PFP2::REAL, VERTEX>("kgaussian");

		for(unsigned int i = kmin.begin(); i != kmin.end(); kmin.next(i))
			kgaussian[i] = kmin[i] * kmax[i];

		mh->notifyAttributeModification(kgaussian);
	}

	this->pythonRecording("computeCurvature", "", mh->getName(),
		positionAttributeName, normalAttributeName, KmaxAttributeName, kmaxAttributeName, KminAttributeName, kminAttributeName, KnormalAttributeName, compute_kmean, compute_kgaussian, autoUpdate);
}