/////////////////////////////////////////////////////////////
// CINEMA SDK : OBJECT PLUGINS														 //
/////////////////////////////////////////////////////////////
// VERSION    : CINEMA 4D																	 //
/////////////////////////////////////////////////////////////
// (c) 1989-2002 MAXON Computer GmbH, all rights reserved	 //
/////////////////////////////////////////////////////////////

// deformer object example

#include "c4d.h"
#include "c4d_symbols.h"
#include "Ospherifydeformer.h"

class Spherify : public ObjectData
{
	public:
		virtual Bool Init(GeListNode *node);

		virtual Bool Message				(GeListNode *node, LONG type, void *data);
		virtual void GetDimension		(PluginObject *op, Vector *mp, Vector *rad);
		virtual Bool Draw						(PluginObject *op, LONG type, BaseDraw *bd, BaseDrawHelp *bh);
		virtual LONG DetectHandle		(PluginObject *op, BaseDraw *bd, LONG x, LONG y, LONG qualifier);
		virtual Bool MoveHandle			(PluginObject *op, PluginObject *undo, const Matrix &tm, LONG hit_id, LONG qualifier);
		virtual Bool ModifyObject   (PluginObject *op, BaseDocument *doc, BaseObject *mod, const Matrix &op_mg, const Matrix &mod_mg, Real lod, LONG flags, BaseThread *thread);

		static NodeData *Alloc(void) { return gNew Spherify; }
};

Bool Spherify::Message(GeListNode *node, LONG type, void *data)
{
	if (type==MSG_MENUPREPARE)
	{
		((BaseObject*)node)->SetDeformMode(TRUE);
	}
	return TRUE;
}

Bool Spherify::ModifyObject(PluginObject *mod, BaseDocument *doc, BaseObject *op, const Matrix &op_mg, const Matrix &mod_mg, Real lod, LONG flags, BaseThread *thread)
{
	BaseContainer *data = mod->GetDataInstance(); 

	Vector  p,*padr=NULL;
	Matrix  m,im;
	LONG    i,pcnt;
	Real		rad=data->GetReal(SPHERIFYDEFORMER_RADIUS),strength=data->GetReal(SPHERIFYDEFORMER_STRENGTH);
	Real		s,*weight=NULL;

	if (!op->IsInstanceOf(Opoint)) return TRUE;
	
	padr = ToPoint(op)->GetPoint();
	pcnt = ToPoint(op)->GetPointCount(); if (!pcnt) return TRUE;

	weight = ToPoint(op)->CalcVertexMap(mod);

	m  = (!mod_mg) * op_mg; // op  ->  world  ->  modifier
	im = !m;

	for (i=0; i<pcnt; i++)
	{
		if (thread && !(i&63) && thread->TestBreak()) break;
		p = m*padr[i];
		s = strength;
		if (weight) s*=weight[i];
		p = s*(!p*rad)+(1.0-s)*p;
		padr[i] = p*im;
	}

	GeFree(weight);
	op->Message(MSG_UPDATE);
	
	return TRUE;
}

void Spherify::GetDimension(PluginObject *op, Vector *mp, Vector *rad)
{
	BaseContainer *data = op->GetDataInstance(); 
	*mp =0.0;
	*rad=data->GetReal(SPHERIFYDEFORMER_RADIUS);
}

#define HANDLE_CNT 2

static Vector GetRTHandle(PluginObject *op, LONG id)
{
	BaseContainer *data = op->GetDataInstance();

	Real rad	    = data->GetReal(SPHERIFYDEFORMER_RADIUS);
	Real strength = data->GetReal(SPHERIFYDEFORMER_STRENGTH)*1000.0;

	switch (id)
	{
		case 0: return Vector(rad,0.0,0.0); break;
		case 1: return Vector(strength,0.0,0.0); break;
	}

	return 0.0;
}

Bool Spherify::Draw(PluginObject *op, LONG type, BaseDraw *bd, BaseDrawHelp *bh)
{
	if (type==DRAWPASS_OBJECT)
	{ 
		BaseContainer *data = op->GetDataInstance(); 
		Real   rad=data->GetReal(SPHERIFYDEFORMER_RADIUS);
		Vector h;
		Matrix m = bh->GetMg();
		
		m.v1*=rad;
		m.v2*=rad;
		m.v3*=rad;
		
		bd->SetPen(bd->GetObjectColor(bh,op));
		bd->Circle3D(m);
		h=m.v2; m.v2=m.v3; m.v3=h;
		bd->Circle3D(m);
		h=m.v1; m.v1=m.v3; m.v3=h;
		bd->Circle3D(m);
	}
	else if (type==DRAWPASS_HANDLES)
	{
		LONG   i;
		Matrix m=bh->GetMg();

		bd->SetPen(GetWorldColor(COLOR_ACTIVEPOINT));
		for (i=0; i<HANDLE_CNT; i++)
			bd->Handle3D(GetRTHandle(op,i)*m,HANDLE_BIG);
		
		bd->Line3D(GetRTHandle(op,1)*m,m.off);
	}
	return TRUE;
}

LONG Spherify::DetectHandle(PluginObject *op, BaseDraw *bd, LONG x, LONG y, LONG qualifier)
{
	if (qualifier&QUALIFIER_CTRL) return NOTOK;

	Matrix	mg = op->GetMg();
	LONG    i,ret=NOTOK;
	Vector	p;

	for (i=0; i<HANDLE_CNT; i++)
	{
		p = GetRTHandle(op,i);
		if (bd->PointInRange(p*mg,x,y)) 
		{
			ret=i;
			if (!(qualifier&QUALIFIER_SHIFT)) break;
		}
	}
	return ret;
}

Bool Spherify::MoveHandle(PluginObject *op, PluginObject *undo, const Matrix &tm, LONG hit_id, LONG qualifier)
{
	BaseContainer *src = undo->GetDataInstance();
	BaseContainer *dst = op  ->GetDataInstance();
	
	Real val = tm.off.x;

	switch (hit_id)
	{
		case 0: 
			val+=src->GetReal(SPHERIFYDEFORMER_RADIUS)+val;
			dst->SetReal(SPHERIFYDEFORMER_RADIUS,FCut(val,0.0,MAXRANGE)); 
			break;
		
		case 1: 
			val=src->GetReal(SPHERIFYDEFORMER_STRENGTH)+val*0.001;
			dst->SetReal(SPHERIFYDEFORMER_STRENGTH,FCut(val,0.0,1.0)); 
			break;
	}
	return TRUE;
}

Bool Spherify::Init(GeListNode *node)
{	
	BaseObject		*op   = (BaseObject*)node;
	BaseContainer *data = op->GetDataInstance();

	data->SetReal(SPHERIFYDEFORMER_RADIUS,200.0);
	data->SetReal(SPHERIFYDEFORMER_STRENGTH,0.5);

	return TRUE;
}

// be sure to use a unique ID obtained from www.plugincafe.com
#define ID_SPHERIFYOBJECT 1001158

Bool RegisterSpherify(void)
{
	// decide by name if the plugin shall be registered - just for user convenience
	String name=GeLoadString(IDS_SPHERIZE); if (!name.Content()) return TRUE;
	return RegisterObjectPlugin(ID_SPHERIFYOBJECT,name,OBJECT_MODIFIER,Spherify::Alloc,"Ospherifydeformer","spherify.tif","spherify_small.tif",0);
}
