/**********************************************************************
 *<
	FILE: editspl.h

	DESCRIPTION:  Edit BezierShape OSM

	CREATED BY: Tom Hudson, Dan Silva & Rolf Berteig

	HISTORY: created 25 April 1995

 *>	Copyright (c) 1994, All Rights Reserved.
 **********************************************************************/

#ifndef __EDITSPLINE_H__
#define __EDITSPLINE_H__

#define Alert(x) MessageBox(GetActiveWindow(),x,_T("Alert"),MB_OK);

#include "shape.h"
#include "spline3d.h"
#include "splshape.h"

#define EDITSPL_CHANNELS (PART_GEOM|SELECT_CHANNEL|PART_SUBSEL_TYPE|PART_DISPLAY|PART_TOPO)

// These are values for selLevel.
#define ES_OBJECT	0
#define ES_VERTEX	1
#define ES_SEGMENT	2
#define ES_SPLINE	3

#define CID_OUTLINE		CID_USER + 201
#define CID_SEGBREAK	CID_USER + 202
#define CID_SEGREFINE	CID_USER + 203
#define CID_VERTCONNECT	CID_USER + 204
#define CID_VERTINSERT	CID_USER + 205
#define CID_BOOLEAN		CID_USER + 206
#define CID_CREATELINE	CID_USER + 207

#define UNDEFINED	0xffffffff

class VertInsertRecord;
class PickSplineAttach;

class EditSplineMod : public Modifier {
	friend BOOL CALLBACK ShapeObjectParamDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam );
	friend BOOL CALLBACK ShapePolygonParamDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam );
	friend BOOL CALLBACK ShapeSegmentParamDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam );
	friend BOOL CALLBACK ShapeVertexParamDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam );

	friend class ESTempData;
	friend class EditSplineData;
	friend class XFormProc;
	friend class OutlineCMode;
	friend class SegBreakCMode;
	friend class SegRefineCMode;
	friend class VertConnectCMode;
	friend class VertInsertCMode;
	friend class CreateLineCMode;
	friend class BooleanCMode;
	friend class OutlineMouseProc;
	friend class SegBreakMouseProc;
	friend class SegRefineMouseProc;
	friend class VertConnectMouseProc;
	friend class VertInsertMouseProc;
	friend class CreateLineMouseProc;
	friend class BooleanMouseProc;
	friend class ShapeRestore;
	friend class VertexRightMenu;
	friend class SegmentRightMenu;
	friend class PolygonRightMenu;
	friend class ESMBackspaceUser;
	friend class ESIBackspaceUser;
	friend class PickSplineAttach;

	private:
				
		static HWND hEditSplineParams;
		static IObjParam *iObjParams;		
		
		static MoveModBoxCMode *moveMode;
		static RotateModBoxCMode *rotMode;
		static UScaleModBoxCMode *uscaleMode;
		static NUScaleModBoxCMode *nuscaleMode;
		static SquashModBoxCMode *squashMode;
		static SelectModBoxCMode *selectMode;
		static OutlineCMode *outlineMode;
		static SegBreakCMode *segBreakMode;
		static SegRefineCMode *segRefineMode;
		static VertConnectCMode *vertConnectMode;
		static VertInsertCMode *vertInsertMode;
		static CreateLineCMode *createLineMode;
		static BooleanCMode *booleanMode;
		static ISpinnerControl *outlineSpin;
		static ISpinnerControl *weldSpin;
		static ICustButton *iUnion;
		static ICustButton *iSubtraction;
		static ICustButton *iIntersection;
		static ICustButton *iMirrorHorizontal;
		static ICustButton *iMirrorVertical;
		static ICustButton *iMirrorBoth;

		static BOOL inOutline;
		static BOOL inSegBreak;
		static int boolType;
		static int mirrorType;
		static PickSplineAttach pickCB;

		RefResult NotifyRefChanged( Interval changeInt,RefTargetHandle hTarget, 
		   PartID& partID, RefMessage message ) { return REF_SUCCEED; }
		
		int selLevel;

		// Vertex insertion information
		VertInsertRecord *insertRec;
		BezierShape *insertShape;
		Spline3D *insertSpline;
		int insertPoly;
		int insertVert;
		INode *insertNode;
		Matrix3 insertTM;	// Transform for the insert node
		EditSplineData *insertShapeData;

		// Create line data
		BezierShape *createShape;
		INode *createNode;
		Matrix3 createTM;	// Transform for the create node
		EditSplineData *createShapeData;

		// Boolean info
		BezierShape* boolShape;
		int boolPoly1;

		// Remembered info
		BezierShape *rememberedShape;	// NULL if using all selected verts
		int rememberedPoly;
		int rememberedIndex;
		int rememberedData;

	public:
		EditSplineMod();
		~EditSplineMod();

		Interval LocalValidity(TimeValue t);
		ChannelMask ChannelsUsed()  { return EDITSPL_CHANNELS; }
		ChannelMask ChannelsChanged() { return EDITSPL_CHANNELS; }
		void ModifyObject(TimeValue t, ModContext &mc, ObjectState *os, INode *node);
		void NotifyInputChanged(Interval changeInt, PartID partID, RefMessage message, ModContext *mc);
		Class_ID InputType() { return Class_ID(SPLINESHAPE_CLASS_ID,0); }
		
		int CompMatrix(TimeValue t, ModContext& mc, Matrix3& tm, Interval& valid);
		
		// From Animatable
		void DeleteThis() { delete this; }
		void GetClassName(TSTR& s) { s= TSTR(_T("EditSplineMod")); }
		Class_ID ClassID() { return Class_ID(EDITSPLINE_CLASS_ID,0);}

		// From BaseObject
		int HitTest(TimeValue t, INode* inode, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt, ModContext* mc);
		int Display(TimeValue t, INode* inode, ViewExp *vpt, int flagst, ModContext *mc);
		void GetWorldBoundBox(TimeValue t,INode* inode, ViewExp *vpt, Box3& box, ModContext *mc);

		void GetSubObjectCenters(SubObjAxisCallback *cb,TimeValue t,INode *node,ModContext *mc);
		void GetSubObjectTMs(SubObjAxisCallback *cb,TimeValue t,INode *node,ModContext *mc);
		int SubObjectIndex(HitRecord *hitRec);

 		BOOL DependOnTopology(ModContext &mc);

		// Generic xform procedure.
		void XFormVerts( XFormProc *xproc, TimeValue t, Matrix3& partm, Matrix3& tmAxis );

		// The following is called before the first Move(), Rotate() or Scale() call
		void TransformStart(TimeValue t);

		// The following is called after the user has completed the Move, Rotate or Scale operation and
		// the undo object has been accepted.
		void TransformFinish(TimeValue t);		

		// The following is called when the transform operation is cancelled by a right-click and the undo
		// has been cancelled.
		void TransformCancel(TimeValue t);		

		// Specialized xform for bezier handles
		void XFormHandles( XFormProc *xproc, TimeValue t, Matrix3& partm, Matrix3& tmAxis, int object );

		// Affine transform methods		
		void Move( TimeValue t, Matrix3& partm, Matrix3& tmAxis, Point3& val, BOOL localOrigin=FALSE );
		void Rotate( TimeValue t, Matrix3& partm, Matrix3& tmAxis, Quat& val, BOOL localOrigin=FALSE );
		void Scale( TimeValue t, Matrix3& partm, Matrix3& tmAxis, Point3& val, BOOL localOrigin=FALSE );

		void BeginOutlineMove(TimeValue t);
		void OutlineMove( TimeValue t, float amount );
		void EndOutlineMove(TimeValue t,BOOL accept=TRUE);

		void StartOutlineMode();
		void DoOutline();
		void StartSegBreakMode();
		void DoSegBreak(ViewExp *vpt, BezierShape *shape, int poly, int seg, IPoint2 p);
		void StartSegRefineMode(int type);
		void DoSegRefine(ViewExp *vpt, BezierShape *shape, int poly, int seg, IPoint2 p);
		void StartVertConnectMode();
		void DoVertConnect(ViewExp *vpt, BezierShape *shape, int poly1, int vert1, int poly2, int vert2);
		void StartVertInsertMode();
		int StartVertInsert(ViewExp *vpt, BezierShape *shape, int poly, int seg, int vert, EditSplineMod **mod);
		void EndVertInsert();
		void StartCreateLineMode();
		BOOL StartCreateLine(BezierShape **shape);
		void EndCreateLine();
		BOOL BooleanStartUp();
		void StartBooleanMode();
		void DoBoolean(int poly2);
		int DoAttach(INode *node, BezierShape *attShape);
		
//		void BeginNormalMove(TimeValue t);
//		void NormalMove( TimeValue t, float amount );
//		void EndNormalMove(TimeValue t,BOOL accept=TRUE);

		void DoVertBreak();
		void DoVertWeld();
		void DoMakeFirst();
		void DoVertDelete();
		void DoSegDelete();
		void DoSegDetach(int copy, int reorient);
		void DoPolyClose();
		void DoPolyDetach(int copy, int reorient);
		void DoPolyMirror(int type, int copy);
		void DoPolyDelete();
		void SetBoolOperation(int type) { boolType = type; }
		void SetMirrorOperation(int type) { mirrorType = type; }
		int GetBoolOperation() { return boolType; }
		int GetMirrorOperation() { return mirrorType; }
		int GetBoolCursorID();
		int GetBoolMirrString(int type);
		void SetBooleanButton();
		void SetMirrorButton();
		void ChangeSelVerts(int type);
		void ChangeRememberedVert(int type);
		int RememberVertThere(HWND hWnd, IPoint2 m);
		void SetRememberedVertType(int type);
		void ChangeSelSegs(int type);
		void ChangeRememberedSeg(int type);
		int RememberSegThere(HWND hWnd, IPoint2 m);
		void SetRememberedSegType(int type);
		void ChangeSelPolys(int type);
		void ChangeRememberedPoly(int type);
		int RememberPolyThere(HWND hWnd, IPoint2 m);
		void SetRememberedPolyType(int type);
		BOOL SingleObjectMod();		// returns TRUE if just one input object
		BezierShape *SingleObjectShape(INode **node = NULL); // returns the shape for the one input object

		void ClearShapeDataFlag(ModContextList& mcList,DWORD f);
		void DeleteShapeDataTempData();		
		void CreateShapeDataTempData();

		int NumRefs() { return 0; }
		RefTargetHandle GetReference(int i) { return NULL; }
		void SetReference(int i, RefTargetHandle rtarg) {}

		// IO
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		IOResult SaveLocalData(ISave *isave, LocalModData *ld);
		IOResult LoadLocalData(ILoad *iload, LocalModData **pld);

		CreateMouseCallBack* GetCreateMouseCallBack() { return NULL; } 
		void BeginEditParams( IObjParam  *ip, ULONG flags,Animatable *prev);
		void EndEditParams( IObjParam *ip, ULONG flags,Animatable *next);
		RefTargetHandle Clone(RemapDir& remap = NoRemap());
		TCHAR *GetObjectName() { return GetString(IDS_TH_EDITSPLINE); }
		void ActivateSubobjSel(int level, XFormModes& modes );
		int NeedUseSubselButton() { return 0; }
		void SelectSubComponent( HitRecord *hitRec, BOOL selected, BOOL all, BOOL invert );
		void ClearSelection(int selLevel);		
		void SelectAll(int selLevel);
		void InvertSelection(int selLevel);

		void UpdatePolyVertCount(HWND hwnd = NULL);

		void SetRollupPage(IObjParam *ip);
		void RemoveRollupPage(IObjParam *ip);

		void RescaleWorldUnits(float f);
	};

class PickSplineAttach : 
		public PickModeCallback,
		public PickNodeCallback {
	public:		
		EditSplineMod *es;
		
		PickSplineAttach() {es=NULL;}

		BOOL HitTest(IObjParam *ip,HWND hWnd,ViewExp *vpt,IPoint2 m,int flags);
		BOOL Pick(IObjParam *ip,ViewExp *vpt);

		void EnterMode(IObjParam *ip);
		void ExitMode(IObjParam *ip);

		HCURSOR GetHitCursor(IObjParam *ip);

		BOOL Filter(INode *node);
		
		PickNodeCallback *GetFilter() {return this;}

		BOOL RightClick(IObjParam *ip,ViewExp *vpt)	{return TRUE;}
	};

// Table to convert selLevel values to shape selLevel flags.
const int shapeLevel[] = {SHAPE_OBJECT,SHAPE_VERTEX,SHAPE_SEGMENT,SHAPE_SPLINE};

// Get display flags based on selLevel.
const DWORD shapeLevelDispFlags[] = {0,DISP_VERTTICKS|DISP_SELVERTS,DISP_VERTTICKS|DISP_SELSEGMENTS,DISP_SELPOLYS};

// For hit testing...
const int shapeHitLevel[] = {0,SUBHIT_SHAPE_VERTS,SUBHIT_SHAPE_SEGMENTS,SUBHIT_SHAPE_POLYS};

const int shapeDlg[] = {IDD_ESPARAM_OBJECT,IDD_ESPARAM_VERTEX,IDD_ESPARAM_SEGMENT,IDD_ESPARAM_POLYGON};
const int shapeDlgString[] = {IDS_TH_EDITOBJECT,IDS_TH_EDITVERTEX,IDS_TH_EDITSEGMENT,IDS_TH_EDITSPLINE};
const DLGPROC shapeDlgProc[] = {ShapeObjectParamDlgProc,ShapeVertexParamDlgProc,ShapeSegmentParamDlgProc,ShapePolygonParamDlgProc};

class EditSplineClassDesc:public ClassDesc {
	public:
	int 			IsPublic() { return 1; }
	void *			Create(BOOL loading = FALSE ) { return new EditSplineMod; }
	const TCHAR *	ClassName() { return GetString(IDS_TH_EDITSPLINE_CLASS); }
	SClass_ID		SuperClassID() { return OSM_CLASS_ID; }
	Class_ID		ClassID() { return Class_ID(EDITSPLINE_CLASS_ID,0); }
	const TCHAR* 	Category() { return GetString(IDS_RB_DEFEDIT);}
	};

typedef Tab<Point3> Point3Tab;
typedef Tab<int> SegTab;

class XFormProc {
	public:
		virtual Point3 proc(Point3& p, Matrix3 &mat, Matrix3 &imat)=0;
		virtual void SetMat( Matrix3& mat ) {}
	};

class MoveXForm : public XFormProc {
	private:
		Point3 delta, tdelta;		
	public:
		Point3 proc(Point3& p, Matrix3 &mat, Matrix3 &imat) 
			{ return p + tdelta; }
		void SetMat( Matrix3& mat ) 
			{ tdelta = VectorTransform(Inverse(mat),delta); }
		MoveXForm(Point3 d) { delta = d; }
	};

class RotateXForm : public XFormProc {
	private:
		Matrix3 rot, trot;
	public:
		Point3 proc(Point3& p, Matrix3 &mat, Matrix3 &imat) 
			{ return (trot*p)*imat; }
		void SetMat( Matrix3& mat ) 
			{ trot = mat * rot; }
		RotateXForm(Quat q) { q.MakeMatrix(rot); }
	};

class ScaleXForm : public XFormProc {
	private:
		Matrix3 scale, tscale;
	public:
		Point3 proc(Point3& p, Matrix3 &mat, Matrix3 &imat) 
			{ return (p*tscale)*imat; }
		void SetMat( Matrix3& mat ) 
			{ tscale = mat*scale; }
		ScaleXForm(Point3 s) { scale = ScaleMatrix(s); }
	};

typedef Tab<int> IntTab;

// General-purpose shape point table -- Maintains point table for each of n polygons
class ShapePointTab {
	public:
		int polys;
		int *pUsed;	// Tells whether polygon is affected
		Point3Tab *ptab;
		IntTab *ktab;
		IntTab *ltab;
		ShapePointTab();
		~ShapePointTab();
		void Empty();
		void Zero();
		void MakeCompatible(BezierShape& shape, BOOL clear=TRUE);
		ShapePointTab& operator=(ShapePointTab& from);
		BOOL IsCompatible(BezierShape &shape);
		void RescaleWorldUnits(float f);
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class ShapeVertexDelta {
	public:
		ShapePointTab dtab;

		void SetSize(BezierShape& shape, BOOL load=TRUE);
		void Empty() { dtab.Empty(); }
		void Zero() { dtab.Zero(); }
		void SetPoint(int poly, int i, const Point3& p) { dtab.pUsed[poly] = 1; dtab.ptab[poly][i] = p; }
		void SetKType(int poly, int i, int k) { dtab.pUsed[poly] = 1; dtab.ktab[poly][i] = k; }
		void SetLType(int poly, int i, int l) { dtab.pUsed[poly] = 1; dtab.ltab[poly][i] = l; }
		void Move(int poly, int i, const Point3& p) { dtab.pUsed[poly] = 1; dtab.ptab[poly][i] += p; }
		void Apply(BezierShape& shape);
		void UnApply(BezierShape& shape);
		void ClearUsed(int poly) { dtab.pUsed[poly] = 0; }
		void SetUsed(int poly) { dtab.pUsed[poly] = 1; }
		int IsUsed(int poly) { return dtab.pUsed[poly] ? 1 : 0; }
		ShapeVertexDelta& operator=(ShapeVertexDelta& from) { dtab = from.dtab; return *this; }
		void ApplyHandlesAndZero(BezierShape &shape, int handlePoly, int handleVert);
		BOOL IsCompatible(BezierShape &shape) { return dtab.IsCompatible(shape); }
		void RescaleWorldUnits(float f) { dtab.RescaleWorldUnits(f); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class AdjEdgeList;
class ESTempData;

/*-------------------------------------------------------------------*/

// Class for recording shape changes -- This is used to reconstruct an object from the original whenever
// the modifier is re-entered or whenever the system needs to reconstruct an object's cache.  This may be
// slow if a lot of changes have been recorded, but it's about the only way to properly reconstruct an object
// because the output of one operation becomes the input of the next.

// These are used as follows:
// When a user makes a modification to an object, a StartChangeGroup call needs to be made to the EditSplineData
// object.  Then a change record needs to be added for each sub-operation that makes up the modification.  These
// records are owned by the EditSplineData object, but they should also be referenced by the undo object for that
// operation.  If an undo is done, ownership of the modification record transfers to the undo/redo object and the
// record is REMOVED (NOT DELETED) from the EditSplineData object.  This keeps the record around for a redo operation
// but removes it from the list of records for the modifier.  If the undo is redone, ownership transfers back to
// the modifier, when it is re-added to the modification record list.

// Note that this class contains load and save methods, necessary because the modifier needs to be able to save
// and load them.  When you subclass off of this, be sure your load and save methods call the base class's first!

class ShapeRestore;

class ModRecord {
	public:
		ModRecord() { groupNumber = -1; serialNumber = -1; restoreObj = NULL; }
		virtual ~ModRecord();
		int groupNumber;
		int serialNumber;
		ShapeRestore *restoreObj;
		void ClearRestore() { restoreObj = NULL; }
		void SetRestore(ShapeRestore *r) { restoreObj = r; }
		virtual BOOL Undo(BezierShape *shape)=0;
		virtual BOOL Redo(BezierShape *shape,int reRecord)=0;
		virtual DWORD Parts()=0;				// Flags of parts modified by this
		virtual int ChunkID()=0;
		virtual ModRecord* Clone()=0;
		virtual IOResult Save(ISave *isave)=0;
		virtual IOResult Load(ILoad *iload)=0;
		virtual void RescaleWorldUnits(float f) {}
	};

typedef ModRecord* PModRecord;
typedef Tab<PModRecord> ModRecordTab;

/*-------------------------------------------------------------------*/

// Here are the types of modification records we use!

#define CLEARVERTSELRECORD_CHUNK	0x2000
#define SETVERTSELRECORD_CHUNK		0x2001
#define INVERTVERTSELRECORD_CHUNK	0x2002
#define CLEARSEGSELRECORD_CHUNK		0x2010
#define SETSEGSELRECORD_CHUNK		0x2011
#define INVERTSEGSELRECORD_CHUNK	0x2012
#define CLEARPOLYSELRECORD_CHUNK	0x2020
#define SETPOLYSELRECORD_CHUNK		0x2021
#define INVERTPOLYSELRECORD_CHUNK	0x2022
#define VERTSELRECORD_CHUNK			0x2030
#define SEGSELRECORD_CHUNK			0x2040
#define POLYSELRECORD_CHUNK			0x2050
#define POLYCLOSERECORD_CHUNK		0x2060
#define POLYREVERSERECORD_CHUNK		0x2068
#define OUTLINERECORD_CHUNK			0x2070
#define POLYDETACHRECORD_CHUNK		0x2080
#define POLYDELETERECORD_CHUNK		0x2090
#define VERTMOVERECORD_CHUNK		0x20A0
#define SEGDELETERECORD_CHUNK		0x20B0
#define SEGDETACHRECORD_CHUNK		0x20C0
#define POLYFIRSTRECORD_CHUNK		0x20C8
#define SEGBREAKRECORD_CHUNK		0x20D0
#define SEGREFINERECORD_CHUNK		0x20E0
#define VERTBREAKRECORD_CHUNK		0x20F0
#define VERTCONNECTRECORD_CHUNK		0x2100
#define VERTINSERTRECORD_CHUNK		0x2110
#define VERTWELDRECORD_CHUNK		0x2120
#define BOOLEANRECORD_CHUNK			0x2130
#define ATTACHRECORD_CHUNK			0x2140
#define VERTCHANGERECORD_CHUNK		0x2150
#define SEGCHANGERECORD_CHUNK		0x2160
#define POLYCHANGERECORD_CHUNK		0x2165
#define VERTDELETERECORD_CHUNK		0x2170
#define CREATELINERECORD_CHUNK		0x2180
#define POLYMIRRORRECORD_CHUNK		0x2190
#define POLYENDATTACHRECORD_CHUNK	0x21A0
#define POLYCOPYRECORD_CHUNK		0x21B0
#define SEGCOPYRECORD_CHUNK			0x21C0
										 
class ClearVertSelRecord : public ModRecord {
	public:
		ShapeVSel sel;	// Old state
		ClearVertSelRecord() : ModRecord() { }
		ClearVertSelRecord(ShapeVSel& oldsel) : ModRecord() { sel = oldsel; }
		ClearVertSelRecord(ClearVertSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			sel = from->sel; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return CLEARVERTSELRECORD_CHUNK; }
		ModRecord* Clone() { return new ClearVertSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class SetVertSelRecord : public ModRecord {
	public:
		ShapeVSel sel;	// Old state
		SetVertSelRecord() : ModRecord() { }
		SetVertSelRecord(ShapeVSel& oldsel) : ModRecord() { sel = oldsel; }
		SetVertSelRecord(SetVertSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			sel = from->sel; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return SETVERTSELRECORD_CHUNK; }
		ModRecord* Clone() { return new SetVertSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class InvertVertSelRecord : public ModRecord {
	public:
		InvertVertSelRecord() : ModRecord() { }
		InvertVertSelRecord(InvertVertSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return INVERTVERTSELRECORD_CHUNK; }
		ModRecord* Clone() { return new InvertVertSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class ClearSegSelRecord : public ModRecord {
	public:
		ShapeSSel sel;	// Old state
		ClearSegSelRecord() : ModRecord() { }
		ClearSegSelRecord(ShapeSSel& oldsel) : ModRecord() { sel = oldsel; }
		ClearSegSelRecord(ClearSegSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			sel = from->sel; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return CLEARSEGSELRECORD_CHUNK; }
		ModRecord* Clone() { return new ClearSegSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class SetSegSelRecord : public ModRecord {
	public:
		ShapeSSel sel;	// Old state
		SetSegSelRecord() : ModRecord() { }
		SetSegSelRecord(ShapeSSel& oldsel) : ModRecord() { sel = oldsel; }
		SetSegSelRecord(SetSegSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			sel = from->sel; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return SETSEGSELRECORD_CHUNK; }
		ModRecord* Clone() { return new SetSegSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class InvertSegSelRecord : public ModRecord {
	public:
		InvertSegSelRecord() : ModRecord() { }
		InvertSegSelRecord(InvertSegSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return INVERTSEGSELRECORD_CHUNK; }
		ModRecord* Clone() { return new InvertSegSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class ClearPolySelRecord : public ModRecord {
	public:
		ShapePSel sel;	// Old state
		ClearPolySelRecord() : ModRecord() { }
		ClearPolySelRecord(ShapePSel& oldsel) : ModRecord() { sel = oldsel; }
		ClearPolySelRecord(ClearPolySelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			sel = from->sel; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return CLEARPOLYSELRECORD_CHUNK; }
		ModRecord* Clone() { return new ClearPolySelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class SetPolySelRecord : public ModRecord {
	public:
		ShapePSel sel;	// Old state
		SetPolySelRecord() : ModRecord() { }
		SetPolySelRecord(ShapePSel& oldsel) : ModRecord() { sel = oldsel; }
		SetPolySelRecord(SetPolySelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			sel = from->sel; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return SETPOLYSELRECORD_CHUNK; }
		ModRecord* Clone() { return new SetPolySelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class InvertPolySelRecord : public ModRecord {
	public:
		InvertPolySelRecord() : ModRecord() { }
		InvertPolySelRecord(InvertPolySelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return INVERTPOLYSELRECORD_CHUNK; }
		ModRecord* Clone() { return new InvertPolySelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class VertSelRecord : public ModRecord {
	public:
		ShapeVSel oldSel;	// Old state
		ShapeVSel newSel;	// New state
		VertSelRecord() : ModRecord() {}
		VertSelRecord(VertSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			oldSel = from->oldSel; newSel = from->newSel; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return VERTSELRECORD_CHUNK; }
		ModRecord* Clone() { return new VertSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class SegSelRecord : public ModRecord {
	public:
		ShapeSSel oldSel;	// Old state
		ShapeSSel newSel;	// New state
		SegSelRecord() : ModRecord() {}
		SegSelRecord(SegSelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			oldSel = from->oldSel; newSel = from->newSel; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return SEGSELRECORD_CHUNK; }
		ModRecord* Clone() { return new SegSelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class PolySelRecord : public ModRecord {
	public:
		ShapePSel oldSel;	// Old state
		ShapePSel newSel;	// New state
		PolySelRecord() : ModRecord() {}
		PolySelRecord(PolySelRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			oldSel = from->oldSel; newSel = from->newSel; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_SELECT; }
		int ChunkID() { return POLYSELRECORD_CHUNK; }
		ModRecord* Clone() { return new PolySelRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class PolyCloseRecord : public ModRecord {
	public:
		int poly;
		PolyCloseRecord() : ModRecord() { }
		PolyCloseRecord(int poly) : ModRecord() { this->poly = poly; }
		PolyCloseRecord(PolyCloseRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			poly = from->poly; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_TOPO; }
		int ChunkID() { return POLYCLOSERECORD_CHUNK; }
		ModRecord* Clone() { return new PolyCloseRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class PolyReverseRecord : public ModRecord {
	public:
		int poly;
		PolyReverseRecord() : ModRecord() { }
		PolyReverseRecord(int poly) : ModRecord() { this->poly = poly; }
		PolyReverseRecord(PolyReverseRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			poly = from->poly; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_TOPO | PART_SELECT; }
		int ChunkID() { return POLYREVERSERECORD_CHUNK; }
		ModRecord* Clone() { return new PolyReverseRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class PolyMirrorRecord : public ModRecord {
	public:
		int poly;
		int type;
		BOOL copy;
		PolyMirrorRecord() : ModRecord() { }
		PolyMirrorRecord(int poly, int type, BOOL copy) : ModRecord() { this->poly = poly; this->copy = copy; this->type = type; }
		PolyMirrorRecord(PolyMirrorRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			poly = from->poly; type = from->type; copy = from->copy; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_TOPO | PART_SELECT; }
		int ChunkID() { return POLYMIRRORRECORD_CHUNK; }
		ModRecord* Clone() { return new PolyMirrorRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class PolyEndAttachRecord : public ModRecord {
	public:
		int poly1;				// First poly
		int vert1;				// Vertex on first poly
		int poly2;				// Second poly (may be same as first)
		int vert2;				// Vertex on second poly
		Spline3D oldSpline1;	// How the first spline looked before connect
		Spline3D oldSpline2;	// How the first spline looked before connect
		BitArray oldVSel1;		// Vertex selections before op
		BitArray oldVSel2;		// Vertex selections before op
		BitArray oldSSel1;		// Segment selections before op
		BitArray oldSSel2;		// Segment selections before op
		int selected2;			// TRUE if spline #2 was selected before op
		PolyEndAttachRecord() : ModRecord() { }
		PolyEndAttachRecord(BezierShape *shape, int poly1, int vert1, int poly2, int vert2);
		PolyEndAttachRecord(PolyEndAttachRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			poly1 = from->poly1; vert1 = from->vert1; poly2 = from->poly2; vert2 = from->vert2; 
			oldSpline1 = from->oldSpline1; oldSpline2 = from->oldSpline2;
			oldVSel1 = from->oldVSel1; oldVSel2 = from->oldVSel2;
			oldSSel1 = from->oldSSel1; oldSSel2 = from->oldSSel2; selected2 = from->selected2; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_TOPO; }
		int ChunkID() { return POLYENDATTACHRECORD_CHUNK; }
		ModRecord* Clone() { return new PolyEndAttachRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldSpline1.Transform(&stm);
			oldSpline2.Transform(&stm);
			}
	};

class OutlineRecord : public ModRecord {
	public:
		BOOL newType;			// Present in MAX 2.0 and up
		int poly;
		int centered;
		float size;
		int oldSplineCount;		// The number of splines present before it was outlined
		BitArray oldVSel;		// Vertex selections before op
		BitArray oldSSel;		// Segment selections before op
		Spline3D oldSpline;		// The original spline
		OutlineRecord() : ModRecord() { newType = TRUE; }
		OutlineRecord(BezierShape *shape, int poly, int centered);
		OutlineRecord(OutlineRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			poly = from->poly; centered = from->centered; size = from->size; oldSplineCount = from->oldSplineCount;
			oldVSel = from->oldVSel; oldSSel = from->oldSSel; oldSpline = from->oldSpline; newType = TRUE;}
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_TOPO; }
		int ChunkID() { return OUTLINERECORD_CHUNK; }
		void SetOutlineSize(float amount) { size = amount; }
		ModRecord* Clone() { return new OutlineRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldSpline.Transform(&stm);
			}
	};

class PolyDetachRecord : public ModRecord {
	public:
		int poly;
		int copy;
		BitArray oldVSel;		// Vertex selections before op
		BitArray oldSSel;		// Segment selections before op
		Spline3D spline;
		PolyDetachRecord() : ModRecord() {}
		PolyDetachRecord(BezierShape *shape, int poly, int copy);
		PolyDetachRecord(PolyDetachRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			poly = from->poly; copy = from->copy; oldVSel = from->oldVSel; oldSSel = from->oldSSel;
			spline = from->spline; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_TOPO; }
		int ChunkID() { return POLYDETACHRECORD_CHUNK; }
		ModRecord* Clone() { return new PolyDetachRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			spline.Transform(&stm);
			}
	};

class PolyDeleteRecord : public ModRecord {
	public:
		int poly;
		BitArray oldVSel;		// Vertex selections before op
		BitArray oldSSel;		// Segment selections before op
		Spline3D spline;
		PolyDeleteRecord() : ModRecord() {}
		PolyDeleteRecord(BezierShape *shape, int poly);
		PolyDeleteRecord(PolyDeleteRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			poly = from->poly; oldVSel = from->oldVSel; oldSSel = from->oldSSel; spline = from->spline; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_TOPO; }
		int ChunkID() { return POLYDELETERECORD_CHUNK; }
		ModRecord* Clone() { return new PolyDeleteRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			spline.Transform(&stm);
			}
	};

class VertMoveRecord : public ModRecord {
	public:
		ShapeVertexDelta delta;	// Position changes for each vertex (Wasteful!  Change later?)
		VertMoveRecord() : ModRecord() {}
		VertMoveRecord(VertMoveRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			delta = from->delta; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_GEOM; }
		int ChunkID() { return VERTMOVERECORD_CHUNK; }
		ModRecord* Clone() { return new VertMoveRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { delta.RescaleWorldUnits(f); }
	};

class SegDeleteRecord : public ModRecord {
	public:
		int poly;
		Spline3D oldSpline;		// How the spline looked before the delete
		BitArray oldVSel;		// Vertex selections before op
		BitArray oldSSel;		// Segment selections before op
		int selected;			// TRUE if spline was selected before op
		int deleted;			// TRUE if segment delete results in this polygon being deleted
		int oldSplineCount;		// The number of splines present before it was broken up
		SegDeleteRecord() : ModRecord() {}
		SegDeleteRecord(BezierShape *shape, int poly);
		SegDeleteRecord(SegDeleteRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			poly = from->poly; oldSpline = from->oldSpline; oldVSel = from->oldVSel; oldSSel = from->oldSSel;
			selected = from->selected; deleted = from->deleted; oldSplineCount = from->oldSplineCount; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_SELECT; }
		int ChunkID() { return SEGDELETERECORD_CHUNK; }
		ModRecord* Clone() { return new SegDeleteRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldSpline.Transform(&stm);
			}
	};

class SegDetachRecord : public ModRecord {
	public:
		int poly;
		int copy;				// TRUE if copying segments
		Spline3D oldSpline;		// How the spline looked before the delete
		BitArray oldVSel;		// Vertex selections before op
		BitArray oldSSel;		// Segment selections before op
		int selected;			// TRUE if spline was selected before op
		int deleted;			// TRUE if segment delete results in this polygon being deleted
		int oldSplineCount;		// The number of splines present before it was broken up
		SegDetachRecord() : ModRecord() {}
		SegDetachRecord(BezierShape *shape, int poly, int copy);
		SegDetachRecord(SegDetachRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			poly = from->poly; copy = from->copy; oldSpline = from->oldSpline;
			oldVSel = from->oldVSel; oldSSel = from->oldSSel; selected = from->selected;
			deleted = from->deleted; oldSplineCount = from->oldSplineCount; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_SELECT; }
		int ChunkID() { return SEGDETACHRECORD_CHUNK; }
		ModRecord* Clone() { return new SegDetachRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldSpline.Transform(&stm);
			}
	};

class PolyFirstRecord : public ModRecord {
	public:
		int poly;
		int vertex;				// The new first vertex
		Spline3D oldSpline;		// How the spline looked before the new first vertex
		BitArray oldVSel;		// Vertex selections before op
		BitArray oldSSel;		// Segment selections before op
		PolyFirstRecord() : ModRecord() {}
		PolyFirstRecord(BezierShape *shape, int poly, int vertex);
		PolyFirstRecord(PolyFirstRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			poly = from->poly; vertex = from->vertex; oldSpline = from->oldSpline; oldVSel = from->oldVSel; 
			oldSSel = from->oldSSel; }
		void SetNew(BezierShape *shape);
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_SELECT; }
		int ChunkID() { return POLYFIRSTRECORD_CHUNK; }
		ModRecord* Clone() { return new PolyFirstRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldSpline.Transform(&stm);
			}
	};

class SegBreakRecord : public ModRecord {
	public:
		int poly;				// The polygon being refined
		int segment;			// The segment being refined
		float param;			// The point on the segment (0-1) where the new point is inserted
		Spline3D oldSpline;		// The spline before we refined it
		BitArray oldVSel;		// Vertex selections before op
		BitArray oldSSel;		// Segment selections before op
		int selected;			// TRUE if spline was selected before op
		int oldSplineCount;		// The number of splines present before it was broken up
		SegBreakRecord() : ModRecord() {}
		SegBreakRecord(BezierShape *shape, int poly, int segment, float param);
		SegBreakRecord(SegBreakRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			poly = from->poly; segment = from->segment; param = from->param;
			oldSpline = from->oldSpline; oldVSel = from->oldVSel; oldSSel = from->oldSSel; selected = from->selected;
			oldSplineCount = from->oldSplineCount; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_SELECT; }
		int ChunkID() { return SEGBREAKRECORD_CHUNK; }
		ModRecord* Clone() { return new SegBreakRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldSpline.Transform(&stm);
			}
	};

class SegRefineRecord : public ModRecord {
	public:
		int poly;				// The polygon being refined
		int segment;			// The segment being refined
		float param;			// The point on the segment (0-1) where the new point is inserted
		Spline3D oldSpline;		// The spline before we refined it
		BitArray oldVSel;		// Vertex selections before op
		BitArray oldSSel;		// Segment selections before op
		SegRefineRecord() : ModRecord() {}
		SegRefineRecord(BezierShape *shape, int poly, int segment, float param);
		SegRefineRecord(SegRefineRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			poly = from->poly; segment = from->segment; param = from->param;
			oldSpline = from->oldSpline; oldVSel = from->oldVSel; oldSSel = from->oldSSel; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_SELECT; }
		int ChunkID() { return SEGREFINERECORD_CHUNK; }
		ModRecord* Clone() { return new SegRefineRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldSpline.Transform(&stm);
			}
	};

class VertBreakRecord : public ModRecord {
	public:
		int poly;
		Spline3D oldSpline;		// How the spline looked before the break
		BitArray oldVSel;		// Vertex selections before op
		BitArray oldSSel;		// Segment selections before op
		int selected;			// TRUE if spline was selected before op
		int oldSplineCount;		// The number of splines present before it was broken up
		VertBreakRecord() : ModRecord() {}
		VertBreakRecord(BezierShape *shape, int poly);
		VertBreakRecord(VertBreakRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			poly = from->poly; 
			oldSpline = from->oldSpline; oldVSel = from->oldVSel; oldSSel = from->oldSSel; selected = from->selected;
			oldSplineCount = from->oldSplineCount; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_SELECT; }
		int ChunkID() { return VERTBREAKRECORD_CHUNK; }
		ModRecord* Clone() { return new VertBreakRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldSpline.Transform(&stm);
			}
	};

class VertConnectRecord : public ModRecord {
	public:
		int poly1;				// First poly
		int vert1;				// Vertex on first poly
		int poly2;				// Second poly (may be same as first)
		int vert2;				// Vertex on second poly
		Spline3D oldSpline1;	// How the first spline looked before connect
		Spline3D oldSpline2;	// How the first spline looked before connect
		BitArray oldVSel1;		// Vertex selections before op
		BitArray oldVSel2;		// Vertex selections before op
		BitArray oldSSel1;		// Segment selections before op
		BitArray oldSSel2;		// Segment selections before op
		int selected2;			// TRUE if spline #2 was selected before op
		VertConnectRecord() : ModRecord() {}
		VertConnectRecord(BezierShape *shape, int poly1, int vert1, int poly2, int vert2);
		VertConnectRecord(VertConnectRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			poly1 = from->poly1; vert1 = from->vert1; poly2 = from->poly2; vert2 = from->vert2; 
			oldSpline1 = from->oldSpline1; oldSpline2 = from->oldSpline2;
			oldVSel1 = from->oldVSel1; oldVSel2 = from->oldVSel2;
			oldSSel1 = from->oldSSel1; oldSSel2 = from->oldSSel2; selected2 = from->selected2; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_SELECT; }
		int ChunkID() { return VERTCONNECTRECORD_CHUNK; }
		ModRecord* Clone() { return new VertConnectRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldSpline1.Transform(&stm);
			oldSpline2.Transform(&stm);
			}
	};

class VertInsertRecord : public ModRecord {
	public:
		int poly;				// Poly	being inserted
		Spline3D oldSpline;		// How the spline looked before insert
		Spline3D newSpline;		// How the spline looked after insert
		BitArray oldVSel;		// Vertex selections before op
		BitArray oldSSel;		// Segment selections before op
		BitArray newVSel;		// Vertex selections after op
		BitArray newSSel;		// Segment selections after op
		VertInsertRecord() : ModRecord() {}
		VertInsertRecord(BezierShape *shape, int poly);
		VertInsertRecord(VertInsertRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			poly = from->poly; oldSpline = from->oldSpline; newSpline = from->newSpline;
			oldVSel = from->oldVSel; oldSSel = from->oldSSel;
			newVSel = from->newVSel; newSSel = from->newSSel; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_SELECT; }
		int ChunkID() { return VERTINSERTRECORD_CHUNK; }
		ModRecord* Clone() { return new VertInsertRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldSpline.Transform(&stm);
			newSpline.Transform(&stm);
			}
	};

class VertWeldRecord : public ModRecord {
	public:
		int poly;
		float thresh;			// Weld threshold
		Spline3D oldSpline;		// How the spline looked before the break
		BitArray oldVSel;		// Vertex selections before op
		BitArray oldSSel;		// Segment selections before op
		int selected;			// TRUE if spline was selected before op
		BOOL deleted;			// TRUE if spline was deleted as a result of the weld
		VertWeldRecord() : ModRecord() {}
		VertWeldRecord(BezierShape *shape, int poly, float thresh);
		VertWeldRecord(VertWeldRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			poly = from->poly; thresh = from->thresh;
			oldSpline = from->oldSpline; oldVSel = from->oldVSel; oldSSel = from->oldSSel;
			selected = from->selected; deleted = from->deleted; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_SELECT; }
		int ChunkID() { return VERTWELDRECORD_CHUNK; }
		ModRecord* Clone() { return new VertWeldRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldSpline.Transform(&stm);
			}
	};

// The boolean operations
#define BOOL_UNION 0
#define BOOL_SUBTRACTION 1
#define BOOL_INTERSECTION 2

// The mirror operations
#define MIRROR_HORIZONTAL 3
#define MIRROR_VERTICAL 4
#define MIRROR_BOTH 5

// Flags used for boolean polygons
#define POLYBOOL (1 << 0)
#define POLYOUTSIDE (1 << 1)
#define POLYINSIDE (1 << 2)

class BooleanRecord : public ModRecord {
	public:
		int poly1;				// Poly 1 of boolean pair
		int poly2;				// Poly 2 of boolean pair
		int operation;			// Boolean operation (see above)
		int oldSplineCount;		// The number of splines present before it was broken up
		Spline3D oldSpline1;	// How spline 1 looked before boolean
		BitArray oldVSel1;		// Vertex selections before op
		BitArray oldSSel1;		// Segment selections before op
		Spline3D oldSpline2;	// How spline 2 looked before boolean
		BitArray oldVSel2;		// Vertex selections before op
		BitArray oldSSel2;		// Segment selections before op
		BooleanRecord() : ModRecord() {}
		BooleanRecord(BezierShape *shape, int poly1, int poly2, int op);
		BooleanRecord(BooleanRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			poly1 = from->poly1; poly2 = from->poly2; operation = from->operation;
			oldSplineCount = from->oldSplineCount;
			oldSpline1 = from->oldSpline1; oldVSel1 = from->oldVSel1; oldSSel1 = from->oldSSel1;
			oldSpline2 = from->oldSpline2; oldVSel2 = from->oldVSel2; oldSSel2 = from->oldSSel2; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_SELECT; }
		int ChunkID() { return BOOLEANRECORD_CHUNK; }
		ModRecord* Clone() { return new BooleanRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldSpline1.Transform(&stm);
			oldSpline2.Transform(&stm);
			}
	};
 
class AttachRecord : public ModRecord {
	public:
		BezierShape attShape;			// The shape we're attaching
		int oldSplineCount;		// The number of splines present before attaching
		AttachRecord() : ModRecord() {}
		AttachRecord(BezierShape *shape, BezierShape *attShape);
		AttachRecord(AttachRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			attShape = from->attShape; oldSplineCount = from->oldSplineCount; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_SELECT; }
		int ChunkID() { return ATTACHRECORD_CHUNK; }
		ModRecord* Clone() { return new AttachRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			attShape.Transform(stm);
			}
	};

class VertChangeRecord : public ModRecord {
	public:
		Spline3D oldSpline;		// How the spline looked before the change
		int poly;
		int vertex;
		int type;
		VertChangeRecord() : ModRecord() {}
		VertChangeRecord(BezierShape *shape, int poly, int vertex, int type);
		VertChangeRecord(VertChangeRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			oldSpline = from->oldSpline; poly = from->poly;
			vertex = from->vertex; type = from->type; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_SELECT; }
		int ChunkID() { return VERTCHANGERECORD_CHUNK; }
		ModRecord* Clone() { return new VertChangeRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldSpline.Transform(&stm);
			}
	};

class SegChangeRecord : public ModRecord {
	public:
		Spline3D oldSpline;		// How the spline looked before the change
		int poly;
		int segment;
		int type;
		SegChangeRecord() : ModRecord() {}
		SegChangeRecord(BezierShape *shape, int poly, int segment, int type);
		SegChangeRecord(SegChangeRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			oldSpline = from->oldSpline; poly = from->poly; segment = from->segment; type = from->type; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_SELECT; }
		int ChunkID() { return SEGCHANGERECORD_CHUNK; }
		ModRecord* Clone() { return new SegChangeRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldSpline.Transform(&stm);
			}
	};

class PolyChangeRecord : public ModRecord {
	public:
		Spline3D oldSpline;		// How the spline looked before the change
		int poly;
		int type;
		PolyChangeRecord() : ModRecord() {}
		PolyChangeRecord(BezierShape *shape, int poly, int type);
		PolyChangeRecord(PolyChangeRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			oldSpline = from->oldSpline; poly = from->poly; type = from->type; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_SELECT; }
		int ChunkID() { return POLYCHANGERECORD_CHUNK; }
		ModRecord* Clone() { return new PolyChangeRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldSpline.Transform(&stm);
			}
	};

class VertDeleteRecord : public ModRecord {
	public:
		int poly;
		Spline3D oldSpline;		// How the spline looked before the delete
		BitArray oldVSel;		// Vertex selections before op
		BitArray oldSSel;		// Segment selections before op
		int selected;			// TRUE if spline was selected before op
		int deleted;			// TRUE if segment delete results in this polygon being deleted
		int oldSplineCount;		// The number of splines present before it was broken up
		VertDeleteRecord() : ModRecord() {}
		VertDeleteRecord(BezierShape *shape, int poly);
		VertDeleteRecord(VertDeleteRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			poly = from->poly; oldSpline = from->oldSpline;
			oldVSel = from->oldVSel; oldSSel = from->oldSSel; selected = from->selected; deleted = from->deleted;
			oldSplineCount = from->oldSplineCount; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_SELECT; }
		int ChunkID() { return VERTDELETERECORD_CHUNK; }
		ModRecord* Clone() { return new VertDeleteRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			oldSpline.Transform(&stm);
			}
	};

class CreateLineRecord : public ModRecord {
	public:
		Spline3D spline;		// The spline we created that will be added
		CreateLineRecord() : ModRecord() {}
		CreateLineRecord(Spline3D *s);
		CreateLineRecord(CreateLineRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			spline = from->spline; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_SELECT; }
		int ChunkID() { return CREATELINERECORD_CHUNK; }
		ModRecord* Clone() { return new CreateLineRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
		void RescaleWorldUnits(float f) { 
			Matrix3 stm = ScaleMatrix(Point3(f, f, f));
			spline.Transform(&stm);
			}
	};

class PolyCopyRecord : public ModRecord {
	public:
		int poly;
		BOOL selOrig;
		BOOL selCopy;
		PolyCopyRecord() : ModRecord() {}
		PolyCopyRecord(BezierShape *shape, int poly, BOOL selOrig, BOOL selCopy);
		PolyCopyRecord(PolyCopyRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			poly = from->poly; selOrig = from->selOrig; selCopy = from->selCopy; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_SELECT; }
		int ChunkID() { return POLYCOPYRECORD_CHUNK; }
		ModRecord* Clone() { return new PolyCopyRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

class SegCopyRecord : public ModRecord {
	public:
		int oldSplineCount;
		ShapeSSel oldSSel;	// Old segment selection sets
		BOOL selCopy;
		SegCopyRecord() : ModRecord() {}
		SegCopyRecord(BezierShape *shape, BOOL selCopy);
		SegCopyRecord(SegCopyRecord* from) { groupNumber = from->groupNumber; serialNumber = from->serialNumber;
			oldSplineCount = from->oldSplineCount; oldSSel = from->oldSSel; selCopy = from->selCopy; }
		BOOL Undo(BezierShape *shape);
		BOOL Redo(BezierShape *shape,int reRecord);
		DWORD Parts() { return PART_GEOM | PART_TOPO | PART_SELECT; }
		int ChunkID() { return SEGCOPYRECORD_CHUNK; }
		ModRecord* Clone() { return new SegCopyRecord(this); }
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

/*-------------------------------------------------------------------*/

// EditSplineData flags
#define ESD_BEENDONE			(1<<0)
#define ESD_UPDATING_CACHE		(1<<1)
#define ESD_HASDATA				(1<<2)

typedef OutlineRecord* POutlineRecord;
typedef Tab<POutlineRecord> OutlineRecordTab;

// This is the data that each mod app will have.
class EditSplineData : public LocalModData {
	public:
		int changeGroup;	// Current change group
		int changeSerial;	// Current change group's serial number
		BOOL handleFlag;
		int handlePoly, handleVert;

		OutlineRecordTab outRecs;

		DWORD flags;

		// This records the changes to the incoming object.
		ModRecordTab changes;

		// A pointer to the change record's vertex delta object
		ShapeVertexDelta *vdelta;

		// While an object is being edited, this exists.
		ESTempData *tempData;

		EditSplineData();
		EditSplineData(EditSplineData& esc);
		~EditSplineData() { ResetChanges(); }
		
		// Applies modifications to a triOb
		void Apply(TimeValue t,SplineShape *splShape,int selLevel);

		// Invalidates any caches affected by the change.
		void Invalidate(PartID part,BOOL meshValid=TRUE);
		
		// If this is the first edit, then the delta arrays will be allocated
		void BeginEdit(TimeValue t);

		LocalModData *Clone() { return new EditSplineData(*this); }
		
		void SetFlag(DWORD f,BOOL on) 
			{ 
			if ( on ) {
				flags|=f;
			} else {
				flags&=~f; 
				}
			}
		DWORD GetFlag(DWORD f) { return flags&f; }

		ESTempData *TempData(EditSplineMod *mod);

		// Change recording functions:
		int StartChangeGroup();					// Start a new group of changes
		int AddChangeRecord(ModRecord *ptr);	// Add a change record to the end of the list & serialize
		int AdoptChangeRecord(ModRecord *ptr);	// Add a change record to the end of the list (don't serialize)
		int RemoveChangeRecord();				// Remove the last change record from the list (don't delete)
		int RemoveChangeGroup();				// Remove the last change group from the list (don't delete)
		int DeleteChangeRecord();				// Delete last change record from the list
		int DeleteChangeGroup();				// Delete last change group from the list
		int ResetChanges();						// Delete all changes
		int FindChangeGroup(int group);			// Locate first entry for this group
		void ClearHandleFlag() { handleFlag = FALSE; }
		void SetHandleFlag(int poly, int vert) { handlePoly = poly; handleVert = vert; handleFlag = TRUE; }
		BOOL DoingHandles() { return handleFlag; }
		void ApplyHandlesAndZero(BezierShape &shape) { vdelta->ApplyHandlesAndZero(shape, handlePoly, handleVert); }
		void RescaleWorldUnits(float f);

		// IO
		IOResult Save(ISave *isave);
		IOResult Load(ILoad *iload);
	};

// My generic restore class

class ShapeRestore : public RestoreObj {
	public:
		ModRecord *rec;		// Modification record
		BOOL iOwn;			// Does restore object own it?

		TimeValue t;

		EditSplineData *shapeData;
		EditSplineMod	 *mod;

		ShapeRestore(EditSplineData* md, EditSplineMod* mod, ModRecord *rec);
		~ShapeRestore();

		void Restore(int isUndo);
		void Redo();
		int Size() { return 1; }
		void EndHold() {mod->ClearAFlag(A_HELD);}
		void RemoveRecord() { rec = NULL; }	
		TSTR Description() { return TSTR(_T("Generic shape restore")); }
	};

class MEdge {
	public:
		DWORD f[2];
		DWORD v[2];
	};

class AdjEdgeList {
	public:
		DWORDTab *list;
		Tab<MEdge> edges;
		int nverts;

		AdjEdgeList( BezierShape& ashape );
		~AdjEdgeList();
		AdjEdgeList& operator=(AdjEdgeList& a) {assert(0);return *this;}
		void AddEdge( DWORD fi, DWORD v1, DWORD v2 );
		DWORDTab& operator[](int i) { return list[i]; }
	};

class OutlineTransformer : public Transformer {
 	public:
 		CoreExport Point3 Eval(ViewExp *vpt);
		OutlineTransformer(IObjParam *i) : Transformer(i) {}
 		};	

class OutlineMouseProc : public MouseCallBack {
	private:
		OutlineTransformer outlineTrans;
		EditSplineMod *es;
		IObjParam *ip;
		Point3 p0, p1;
		IPoint2 sp0;
	public:
		OutlineMouseProc(EditSplineMod* mod, IObjParam *i)
			: outlineTrans(i) {es=mod;ip=i;}
		int proc( 
			HWND hwnd, 
			int msg, 
			int point, 
			int flags, 
			IPoint2 m );
	};

class OutlineSelectionProcessor : public GenModSelectionProcessor {
	protected:
		HCURSOR GetTransformCursor();
		
	public:
		OutlineSelectionProcessor(OutlineMouseProc *mc, Modifier *m, IObjParam *i) 
			: GenModSelectionProcessor(mc,m,i) {}
	};


class OutlineCMode : public CommandMode {
	private:
		ChangeFGObject fgProc;
		OutlineSelectionProcessor mouseProc;
		OutlineMouseProc eproc;
		EditSplineMod* es;

	public:
		OutlineCMode(EditSplineMod* mod, IObjParam *i) :
			fgProc(mod), mouseProc(&eproc,mod,i), eproc(mod,i) {es=mod;}

		int Class() { return MODIFY_COMMAND; }
		int ID() { return CID_OUTLINE; }
		MouseCallBack *MouseProc(int *numPoints) { *numPoints=2; return &mouseProc; }
		ChangeForegroundCallback *ChangeFGProc() { return &fgProc; }
		BOOL ChangeFG( CommandMode *oldMode ) { return oldMode->ChangeFGProc() != &fgProc; }
		void EnterMode();
		void ExitMode();
	};

/*-------------------------------------------------------------------*/

class SegBreakTransformer : public Transformer {
 	public:
 		CoreExport Point3 Eval(ViewExp *vpt);
		SegBreakTransformer(IObjParam *i) : Transformer(i) {}
 		};	

class SegBreakMouseProc : public MouseCallBack {
	private:
		EditSplineMod *es;
		IObjParam *iObjParams;
		IPoint2 om;

	protected:
		HCURSOR GetTransformCursor();
		BOOL HitTest( ViewExp *vpt, IPoint2 *p, int type, int flags );
		BOOL AnyHits( ViewExp *vpt ) { return vpt->NumSubObjHits(); }		

	public:
		SegBreakMouseProc(EditSplineMod* mod, IObjParam *i) { es=mod; iObjParams=i; }
		int proc( 
			HWND hwnd, 
			int msg, 
			int point, 
			int flags, 
			IPoint2 m );
//		int override(int mode) { return CLICK_DOWN_POINT; }
	};

class SegBreakCMode : public CommandMode {
	private:
		ChangeFGObject fgProc;
		SegBreakMouseProc eproc;
		EditSplineMod* es;

	public:
		SegBreakCMode(EditSplineMod* mod, IObjParam *i) :
			fgProc(mod), eproc(mod,i) {es=mod;}

		int Class() { return MODIFY_COMMAND; }
		int ID() { return CID_SEGBREAK; }
		MouseCallBack *MouseProc(int *numPoints) { *numPoints=1; return &eproc; }
		ChangeForegroundCallback *ChangeFGProc() { return &fgProc; }
		BOOL ChangeFG( CommandMode *oldMode ) { return oldMode->ChangeFGProc() != &fgProc; }
		void EnterMode();
		void ExitMode();
	};

/*-------------------------------------------------------------------*/

#define REFINE_VERT 0
#define REFINE_SEG 1

class SegRefineMouseProc : public MouseCallBack {
	private:
		EditSplineMod *es;
		IObjParam *iObjParams;
		IPoint2 om;
		int type; // See above
	
	protected:
		HCURSOR GetTransformCursor();
		BOOL HitTest( ViewExp *vpt, IPoint2 *p, int type, int flags );
		BOOL AnyHits( ViewExp *vpt ) { return vpt->NumSubObjHits(); }		

	public:
		SegRefineMouseProc(EditSplineMod* mod, IObjParam *i) { es=mod; iObjParams=i; }
		int proc( 
			HWND hwnd, 
			int msg, 
			int point, 
			int flags, 
			IPoint2 m );
		void SetType(int type) { this->type = type; }
	};

class SegRefineCMode : public CommandMode {
	private:
		ChangeFGObject fgProc;
		SegRefineMouseProc eproc;
		EditSplineMod* es;
		int type; // See above

	public:
		SegRefineCMode(EditSplineMod* mod, IObjParam *i) :
			fgProc(mod), eproc(mod,i) {es=mod;}

		int Class() { return MODIFY_COMMAND; }
		int ID() { return CID_SEGREFINE; }
		MouseCallBack *MouseProc(int *numPoints) { *numPoints=1; return &eproc; }
		ChangeForegroundCallback *ChangeFGProc() { return &fgProc; }
		BOOL ChangeFG( CommandMode *oldMode ) { return oldMode->ChangeFGProc() != &fgProc; }
		void EnterMode();
		void ExitMode();
		void SetType(int type) { this->type = type; eproc.SetType(type); }
	};

/*-------------------------------------------------------------------*/

class VertConnectMouseProc : public MouseCallBack {
	private:
		EditSplineMod *es;
		IObjParam *iObjParams;
		IPoint2 om;

	protected:
		HCURSOR GetTransformCursor();
		BOOL HitTest( ViewExp *vpt, IPoint2 *p, int type, int flags );
		BOOL AnyHits( ViewExp *vpt ) { return vpt->NumSubObjHits(); }		
		BOOL HitAnEndpoint(ViewExp *vpt, IPoint2 *p, BezierShape *shape, int poly, int vert,
			BezierShape **shapeOut, int *polyOut, int *vertOut);
	public:
		VertConnectMouseProc(EditSplineMod* mod, IObjParam *i) { es=mod; iObjParams=i; }
		int proc( 
			HWND hwnd, 
			int msg, 
			int point, 
			int flags, 
			IPoint2 m );
//		int override(int mode) { return CLICK_DOWN_POINT; }
	};

class VertConnectCMode : public CommandMode {
	private:
		ChangeFGObject fgProc;
		VertConnectMouseProc eproc;
		EditSplineMod* es;

	public:
		VertConnectCMode(EditSplineMod* mod, IObjParam *i) :
			fgProc(mod), eproc(mod,i) {es=mod;}

		int Class() { return MODIFY_COMMAND; }
		int ID() { return CID_VERTCONNECT; }
		MouseCallBack *MouseProc(int *numPoints) { *numPoints=2; return &eproc; }
		ChangeForegroundCallback *ChangeFGProc() { return &fgProc; }
		BOOL ChangeFG( CommandMode *oldMode ) { return oldMode->ChangeFGProc() != &fgProc; }
		void EnterMode();
		void ExitMode();
	};

/*-------------------------------------------------------------------*/

class VertInsertMouseProc : public MouseCallBack {
	private:
		EditSplineMod *es;
		IObjParam *iObjParams;
		IPoint2 om;

	protected:
		HCURSOR GetTransformCursor();
		BOOL HitTest( ViewExp *vpt, IPoint2 *p, int type, int flags, int hitType );
		BOOL AnyHits( ViewExp *vpt ) { return vpt->NumSubObjHits(); }		
		BOOL InsertWhere(ViewExp *vpt, IPoint2 *p, BezierShape **shapeOut, int *polyOut,int *segOut, int *vertOut);
	public:
		VertInsertMouseProc(EditSplineMod* mod, IObjParam *i) { es=mod; iObjParams=i; }
		int proc( 
			HWND hwnd, 
			int msg, 
			int point, 
			int flags, 
			IPoint2 m );
		int override(int mode) { return CLICK_DOWN_POINT; }
	};

class VertInsertCMode : public CommandMode {
	private:
		ChangeFGObject fgProc;
		VertInsertMouseProc eproc;
		EditSplineMod* es;

	public:
		VertInsertCMode(EditSplineMod* mod, IObjParam *i) :
			fgProc(mod), eproc(mod,i) {es=mod;}

		int Class() { return MODIFY_COMMAND; }
		int ID() { return CID_VERTINSERT; }
		MouseCallBack *MouseProc(int *numPoints) { *numPoints=999999; return &eproc; }
		ChangeForegroundCallback *ChangeFGProc() { return &fgProc; }
		BOOL ChangeFG( CommandMode *oldMode ) { return oldMode->ChangeFGProc() != &fgProc; }
		void EnterMode();
		void ExitMode();
	};

/*-------------------------------------------------------------------*/

class CreateLineMouseProc : public MouseCallBack {
	private:
		EditSplineMod *es;
		IObjParam *iObjParams;
		IPoint2 om;

	protected:
	public:
		CreateLineMouseProc(EditSplineMod* mod, IObjParam *i) { es=mod; iObjParams=i; }
		int proc( 
			HWND hwnd, 
			int msg, 
			int point, 
			int flags, 
			IPoint2 m );
		int override(int mode) { return CLICK_DOWN_POINT; }
	};

class CreateLineCMode : public CommandMode {
	private:
		ChangeFGObject fgProc;
		CreateLineMouseProc eproc;
		EditSplineMod* es;

	public:
		CreateLineCMode(EditSplineMod* mod, IObjParam *i) :
			fgProc(mod), eproc(mod,i) {es=mod;}

		int Class() { return MODIFY_COMMAND; }
		int ID() { return CID_CREATELINE; }
		MouseCallBack *MouseProc(int *numPoints) { *numPoints=999999; return &eproc; }
		ChangeForegroundCallback *ChangeFGProc() { return &fgProc; }
		BOOL ChangeFG( CommandMode *oldMode ) { return oldMode->ChangeFGProc() != &fgProc; }
		void EnterMode();
		void ExitMode();
	};

/*-------------------------------------------------------------------*/

class BooleanMouseProc : public MouseCallBack {
	private:
		EditSplineMod *es;
		IObjParam *iObjParams;
		IPoint2 om;

	protected:
		HCURSOR GetTransformCursor();
		BOOL HitTest( ViewExp *vpt, IPoint2 *p, int type, int flags );
		BOOL AnyHits( ViewExp *vpt ) { return vpt->NumSubObjHits(); }		
	public:
		BooleanMouseProc(EditSplineMod* mod, IObjParam *i) { es=mod; iObjParams=i; }
		int proc( 
			HWND hwnd, 
			int msg, 
			int point, 
			int flags, 
			IPoint2 m );
		int override(int mode) { return CLICK_DOWN_POINT; }
	};

class BooleanCMode : public CommandMode {
	private:
		ChangeFGObject fgProc;
		BooleanMouseProc eproc;
		EditSplineMod* es;

	public:
		BooleanCMode(EditSplineMod* mod, IObjParam *i) :
			fgProc(mod), eproc(mod,i) {es=mod;}

		int Class() { return MODIFY_COMMAND; }
		int ID() { return CID_BOOLEAN; }
		MouseCallBack *MouseProc(int *numPoints) { *numPoints=9999; return &eproc; }
		ChangeForegroundCallback *ChangeFGProc() { return &fgProc; }
		BOOL ChangeFG( CommandMode *oldMode ) { return oldMode->ChangeFGProc() != &fgProc; }
		void EnterMode();
		void ExitMode();
	};


/*-------------------------------------------------------------------*/

class ESTempData {
	private:
		// Data to keep cached while
		AdjEdgeList 	*adjList;
		DWORDTab		*vertCluster;
		Point3Tab		*cnormals;
		Point3Tab       *ccenters;
		Interval alValid;

		BezierShape			*shape;
		Point3Tab 		*vnormals;
		Interval 		shapeValid;
		
		EditSplineMod 	*mod;
		EditSplineData 	*shapeData;

	public:		
		
		~ESTempData();
		ESTempData(EditSplineMod *m,EditSplineData *md);
		void Invalidate(PartID part,BOOL meshValid=TRUE);
		
		AdjEdgeList 	*AdjList(TimeValue t);
		BezierShape		*GetShape(TimeValue t);
		
		BOOL ShapeCached(TimeValue t);
		void UpdateCache(SplineShape *splShape);
	};


// Spline hit override functions

extern void SetSplineHitOverride(int value);
extern void ClearSplineHitOverride();

extern BOOL ValidBooleanPolygon(IObjParam *ip, Spline3D *spline);

#ifdef LATER
void BuildClusterNormals( Mesh& mesh, FaceClusterList& clust, 
	Point3Tab& normals, Point3Tab& centers );

void BuildAverageNormals( Mesh& mesh, Point3Tab& normals );

void MatrixFromNormal( Point3& normal, Matrix3& mat );

void PolyFromFace(Mesh& mesh,AdjFaceList &al,DWORD f, DWORDTab& sel,float thresh);


#endif

#endif // __EDITSPLINE_H__

