#include "c4d_memory.h"
#include "c4d_basecontainer.h"
#include "c4d_baseobject.h"
#include "c4d_basetag.h"
#include "c4d_thread.h"
#include "c4d_tools.h"
#include "c4d_includes.h"
#include "c4d_baseselect.h"
#include "ge_sort.h"
#include "lib_ngon.h"

Bool BaseObject::SetPhong(Bool on, Bool anglelimit, Real angle)
{
	KillTag(Tphong);
	if (!on) return TRUE;
	BaseTag *tag=MakeTag(Tphong);
	if (!tag) return FALSE;
	BaseContainer bc;
	bc.SetBool(PHONGTAG_PHONG_ANGLELIMIT,anglelimit);
	bc.SetReal(PHONGTAG_PHONG_ANGLE,angle);
	tag->SetData(bc,FALSE);
	return TRUE;
}

Real BaseObject::GetVisibility(Real parent)
{
	return C4DOS.Bo->GetVisibility(this,parent);
}

void BaseObject::SetIsoparm(LineObject *l)
{
	C4DOS.Bo->SetIsoparm(this,l);
}

Matrix BaseObject::GetMln(void)
{
	return C4DOS.Bo->GetMln(this);
}

Matrix BaseObject::GetMgn(void)
{
	return C4DOS.Bo->GetMgn(this);
}

void BaseObject::SetOrigin(BaseObject *origin)
{
	C4DOS.Bo->SetOrigin(this,origin);
}

BaseObject* BaseObject::GetOrigin(Bool safe)
{
	return C4DOS.Bo->GetOrigin(this,safe);
}

BaseObject* BaseObject::GetTopOrigin(Bool parent, Bool safe)
{
	return C4DOS.Bo->GetTopOrigin(this,parent,safe);
}

BaseObject* BaseObject::GetEditObject(BaseObject **psds, LONG state)
{
	return C4DOS.Bo->GetEditObject(this,psds,state);
}

LONG BaseObject::GetUniqueIP(void)
{
	return C4DOS.Bo->GetUniqueIP(this);
}

void BaseObject::SetUniqueIP(LONG ip)
{
	C4DOS.Bo->SetUniqueIP(this,ip);
}

LONG BaseObject::GetEditorMode(void)
{
	return C4DOS.Bo->GetMode(this,MODE_EDITOR);
}

void BaseObject::SetEditorMode(LONG mode)
{
	C4DOS.Bo->SetMode(this,MODE_EDITOR,mode);
}

LONG BaseObject::GetRenderMode(void)
{
	return C4DOS.Bo->GetMode(this,MODE_RENDER);
}

void BaseObject::SetRenderMode(LONG mode)
{
	C4DOS.Bo->SetMode(this,MODE_RENDER,mode);
}

Bool BaseObject::GetDeformMode(void)
{
	return C4DOS.Bo->GetMode(this,MODE_DEFORM)!=0;
}

void BaseObject::SetDeformMode(Bool mode)
{
	C4DOS.Bo->SetMode(this,MODE_DEFORM,mode);
}

void BaseObject::InsertTag(BaseTag *tp, BaseTag *pred)
{
	C4DOS.Bo->InsertTag(this,tp,pred);
}

void BaseObject::KillTag (LONG type, LONG nr)
{
	C4DOS.Bo->KillTag(this,type,nr);
}

BaseTag *BaseObject::GetFirstTag(void)
{
	return C4DOS.Bo->GetFirstTag(this);
}

Bool BaseObject::CopyTagsTo(BaseObject *dest, LONG visible, LONG variable, LONG hierarchical, AliasTrans *trans)
{
	return C4DOS.Bo->CopyTagsTo(this,dest,visible,variable,hierarchical,trans);
}

BaseTag *BaseObject::MakeTag(LONG type, BaseTag *pred)
{
	BaseTag *tag = BaseTag::Alloc(type);
	if (tag) InsertTag(tag,pred);
	return tag;
}

VariableTag *BaseObject::MakeVariableTag(LONG type, LONG anz, BaseTag *pred)
{
	BaseTag *tag = VariableTag::Alloc(type,anz);
	if (tag) InsertTag(tag,pred);
	return (VariableTag*)tag;
}

BaseSelect *PointObject::GetPointS(void)
{
	return C4DOS.Bo->PoGetPointS(this);
}

BaseSelect *PointObject::GetPointH(void)
{
	return C4DOS.Bo->PoGetPointH(this);
}

Bool PointObject::ResizeObject(LONG pcnt)
{
	return C4DOS.Bo->PoResizeObject(this,pcnt);
}

Real *PointObject::CalcVertexMap(BaseObject *modifier)
{
	return C4DOS.Bo->PoCalcVertexMap(this,modifier);
}

Bool SplineObject::ResizeObject(LONG pcnt, LONG scnt)
{
	return C4DOS.Bo->SpResizeObject(this,pcnt,scnt);
}

Bool LineObject::ResizeObject(LONG pcnt, LONG scnt)
{
	return C4DOS.Bo->LoResizeObject(this,pcnt,scnt);
}

BaseSelect *PolygonObject::GetEdgeS(void)
{
	return C4DOS.Bo->PyGetEdgeS(this);
}

BaseSelect *PolygonObject::GetEdgeH(void)
{
	return C4DOS.Bo->PyGetEdgeH(this);
}

BaseSelect *PolygonObject::GetPhongBreak(void)
{
	return C4DOS.Bo->PyGetPhongBreak(this);
}

BaseSelect *PolygonObject::GetPolygonS(void)
{
	return C4DOS.Bo->PyGetPolygonS(this);
}

BaseSelect *PolygonObject::GetPolygonH(void)
{
	return C4DOS.Bo->PyGetPolygonH(this);
}

Bool PolygonObject::ResizeObject(LONG pcnt, LONG vcnt)
{
	return C4DOS.Bo->PyResizeObject(this,pcnt,vcnt);
}

Bool PolygonObject::ResizeObject(LONG pcnt, LONG vcnt, LONG ncnt)
{
	return C4DOS.Bo->PyResizeObjectNgon(this,pcnt,vcnt,ncnt);
}

Bool PolygonObject::ValidateEdgeSelection(BaseSelect* sel)
{
	return C4DOS.Bo->PyValidateEdgeSelection(this,sel);
}

Bool PolygonObject::ValidateEdgeSelection()
{
	return C4DOS.Bo->PyValidateEdgeSelectionA(this);
}

Bool PolygonObject::GetEdgeSelection(BaseSelect* sel, EdgeSelectionType type)
{
	return C4DOS.Bo->PyGetEdgeSelection(this,sel,type);
}

void PolygonObject::GetNgonEdgesCompact(UCHAR *&edges)
{
	C4DOS.Bo->PyGetNgonEdgesCompact(this,edges);
}


LONG SplineObject::GetInterpolationType(void)
{
	LONG type=GetDataInstance()->GetLong(SPLINEOBJECT_TYPE);
 	if (GetPointCount()>2 || type==Thermite)
		return type;
	else
		return Tlinear;
}

Bool SplineObject::IsClosed(void)
{
	return GetDataInstance()->GetBool(SPLINEOBJECT_CLOSED);
}

Vector SplineObject::GetSplinePoint(Real t, LONG segment, const Vector *padr)
{
	return C4DOS.Bo->SpGetSplinePoint(this,t,segment,padr);
}

Vector SplineObject::GetSplineTangent(Real t, LONG segment, const Vector *padr)
{
	return C4DOS.Bo->SpGetSplineTangent(this,t,segment,padr);
}

Bool SplineObject::InitLength(LONG segment, const Vector *padr)
{
	return C4DOS.Bo->SpInitLength(this,segment,padr);
}

void SplineObject::FreeLength(void)
{
	C4DOS.Bo->SpFreeLength(this);
}

Real SplineObject::UniformToNatural(Real t)
{
	return C4DOS.Bo->SpUniformToNatural(this,t);
}

Real SplineObject::GetLength(void)
{
	return C4DOS.Bo->SpGetLength(this);
}

Real SplineObject::GetSegmentLength(LONG a, LONG b)
{
	return C4DOS.Bo->SpGetSegmentLength(this,a,b);
}

LineObject *SplineObject::GetLineObject(BaseDocument *doc, Real lod, BaseThread *thread)
{
	return C4DOS.Bo->SpGetLineObject(this,doc,lod,thread);
}

Bool SplineObject::SetDefaultCoeff(void)
{
	return C4DOS.Bo->SpSetDefaultCoeff(this);
}

SplineObject *BaseObject::GetRealSpline(void)
{
	return C4DOS.Bo->SpGetRealSpline(this);
}

Safety::Safety(void)
{
	os=NULL;
}

Safety::~Safety(void)
{
	C4DOS.Bo->OsFree(os,TRUE);
}

Bool Safety::Init(BaseObject *op)
{
	os=C4DOS.Bo->OsAlloc(op);
	return os!=NULL;
}

void Safety::Detach(void)
{
	C4DOS.Bo->OsFree(os,FALSE);
}

LONG CameraObject::GetProjection(void)
{
	return C4DOS.Bo->CoGetProjection(this);
}

Real CameraObject::GetFocus(void)
{
	return C4DOS.Bo->CoGetFocus(this);
}

Real CameraObject::GetZoom(void)
{
	return C4DOS.Bo->CoGetZoom(this);
}

Vector CameraObject::GetOffset(void)
{
	return C4DOS.Bo->CoGetOffset(this);
}

Real CameraObject::GetAperture(void)
{
	return C4DOS.Bo->CoGetAperture(this);
}

Bool CameraObject::SetProjection(LONG projection)
{
	return C4DOS.Bo->CoSetProjection(this,projection);
}

Bool CameraObject::SetFocus(Real v)
{
	return C4DOS.Bo->CoSetFocus(this,v);
}

Bool CameraObject::SetAperture(Real v)
{
	return C4DOS.Bo->CoSetAperture(this,v);
}

Bool CameraObject::SetZoom(Real v)
{
	return C4DOS.Bo->CoSetZoom(this,v);
}

Bool CameraObject::SetOffset(const Vector &offset)
{
	return C4DOS.Bo->CoSetOffset(this,offset);
}

BaseObject *GeneratePrimitive(BaseDocument *doc, LONG type, const BaseContainer &bc, Real lod, Bool isoparm, BaseThread *bt)
{
	return C4DOS.Bo->GenPrimitive(doc,type,&bc,lod,isoparm,bt);
}

BaseObject *GenerateSplinePrimitive(BaseDocument *doc, LONG type, const BaseContainer &bc, Real lod, BaseThread *bt)
{
	return C4DOS.Bo->GenSplinePrimitive(doc,type,&bc,lod,bt);
}

LONG CalcLOD(LONG val, Real lod, LONG min, LONG max)
{
	val=LONG(Real(val)*lod+0.001);
	return LCut(val,min,max);
}

class DisjointNgonMesh : public GeSortAndSearch
{
public:
	LONG Compare(void *a, void *b) { return (*(LONG*)a)-(*(LONG*)b); }
};

#include "c4d_general.h"

Bool DisjointMesh(PointObject *op)
{
	Bool		same;
	LONG		pcnt=op->GetPointCount(),i,npcnt=0;
	Vector	*padr=op->GetPointW(),*sttpadr=NULL,*opadr=NULL,*xadr=NULL;
	LONG *ngonpointmap=NULL;

	if (pcnt==0) return TRUE;

	if (op->GetType()==Opolygon)
	{
		StickTextureTag *stt=(StickTextureTag*)op->GetTag(Tsticktexture);
		LONG		vcnt=ToPoly(op)->GetPolygonCount(),pid,n,p,maxngon=0,ply,pnt;
		CPolygon *vadr=ToPoly(op)->GetPolygonW();
		NgonBase *ngons=ToPoly(op)->GetNgonBase();
		Pgon *pgon=ToPoly(op)->GetAndBuildNgon();
		DisjointNgonMesh pointsort;

		for (i=0; i<vcnt; i++)
		{
			if (ngons && (pid=ngons->FindPolygon(i))!=NOTOK)
			{
				if (i!=pgon[pid].GetIdentity()) continue;

				LONG nplycnt=pgon[pid].GetPolygonCount();
				if (nplycnt>maxngon) maxngon=nplycnt;

				npcnt+=pgon[pid].GetPointCount();
			}
			else if (vadr[i].c==vadr[i].d)
				npcnt+=3;
			else
				npcnt+=4;
		}

		if (stt && !stt->Record(op,FALSE)) goto Error;
		if (stt && stt->GetDataAddressR() && stt->GetDataCount()==pcnt)
		{
			sttpadr=(Vector*)GeAlloc(sizeof(Vector)*pcnt); if (!sttpadr) goto Error;
			CopyMem(stt->GetDataAddressR(),sttpadr,sizeof(Vector)*pcnt);
		}

		opadr=(Vector*)GeAlloc(sizeof(Vector)*pcnt); if (!opadr) goto Error;
		CopyMem(padr,opadr,sizeof(Vector)*pcnt);

		if (maxngon>0)
		{
			ngonpointmap=(LONG*)GeAlloc(sizeof(LONG)*maxngon*8);
			if (!ngonpointmap) goto Error;
		}

		if (!ToPoly(op)->ResizeObject(npcnt,NOTOK)) goto Error;
		if (stt && !stt->Record(op,TRUE)) goto Error;

		xadr = stt ? (Vector*)stt->GetDataAddressW():NULL;
		padr = op->GetPointW();
		npcnt=0;

		for (i=0; i<vcnt; i++)
		{
			if (ngons && (pid=ngons->FindPolygon(i))!=NOTOK)
			{
				if (i!=pgon[pid].GetIdentity()) continue;

				p=0;
				for (n=0;n<pgon[pid].GetCount();n++)
				{
					if (!pgon[pid].IsFirst(n)) continue;

					ply=pgon[pid].ID(n);

					ngonpointmap[p++]=vadr[ply].a;
					ngonpointmap[p++]=ply*4;
					ngonpointmap[p++]=vadr[ply].b;
					ngonpointmap[p++]=ply*4+1;
					ngonpointmap[p++]=vadr[ply].c;
					ngonpointmap[p++]=ply*4+2;
					ngonpointmap[p++]=vadr[ply].d;
					ngonpointmap[p++]=ply*4+3;
				}

				LONG arraycnt=p,lastp;
				if (arraycnt>0)
				{
					pointsort.Sort(ngonpointmap,arraycnt/2,sizeof(LONG)*2);

					p=0;
					do
					{
						lastp=ngonpointmap[p];

						if (xadr) xadr[npcnt] = sttpadr[lastp];
						padr[npcnt] = opadr[lastp];

						while (p<arraycnt && ngonpointmap[p]==lastp)
						{
							ply=ngonpointmap[p+1]/4;
							pnt=ngonpointmap[p+1]&3;

							switch (pnt)
							{
								case 0: vadr[ply].a=npcnt; break;
								case 1: vadr[ply].b=npcnt; break;
								case 2: vadr[ply].c=npcnt; break;
								case 3: vadr[ply].d=npcnt; break;
							}

							p+=2;
						}

						npcnt++;

					} while (p<arraycnt);
				}
			}
			else
			{
				same=vadr[i].c!=vadr[i].d;

				if (sttpadr)
				{
					xadr[npcnt+0] = sttpadr[vadr[i].a];
					xadr[npcnt+1] = sttpadr[vadr[i].b];
					xadr[npcnt+2] = sttpadr[vadr[i].c];
					if (same)
						xadr[npcnt+3] = sttpadr[vadr[i].d];
				}

				padr[npcnt] = opadr[vadr[i].a]; vadr[i].a=npcnt++;
				padr[npcnt] = opadr[vadr[i].b]; vadr[i].b=npcnt++;
				padr[npcnt] = opadr[vadr[i].c]; vadr[i].c=npcnt++;

				if (same)
				{
					padr[npcnt] = opadr[vadr[i].d]; vadr[i].d=npcnt++;
				}
				else
					vadr[i].d=vadr[i].c;
			}
		}

		GeFree(ngonpointmap);
		GeFree(sttpadr);
	}
	else if (op->GetType()==Oline)
	{
		Segment	*sadr = ((LineObject*)op)->GetSegmentW();
		LONG		scnt  = ((LineObject*)op)->GetSegmentCount(),off,j;

		for (i=0; i<scnt; i++)
		{
			if (sadr[i].closed)
				npcnt+=sadr[i].cnt*2;
			else
				npcnt+=(sadr[i].cnt-1)*2;
		}

		opadr=(Vector*)GeAlloc(sizeof(Vector)*pcnt);
		if (!opadr) return FALSE;
		CopyMem(padr,opadr,sizeof(Vector)*pcnt);

		// crashbugfix: copy also the segments because the array can get smaller
		Segment *osadr=(Segment*)GeAlloc(sizeof(Segment)*scnt);
		if (!osadr) return FALSE;
		CopyMem(sadr,osadr,sizeof(Segment)*scnt);

		if (!((LineObject*)op)->ResizeObject(npcnt,npcnt/2))
		{
			GeFree(opadr);
			GeFree(osadr);
			return FALSE;
		}

		padr = op->GetPointW();
		sadr = ((LineObject*)op)->GetSegmentW();
		LONG xcnt=0;
		off=0;
		for (i=0; i<scnt; i++)
		{
			for (j=0; j+1<osadr[i].cnt; j++)
			{
#ifdef _DEBUG
	if (xcnt+1>=npcnt || off+j+1>=pcnt) { GeBoom(); break; }
#endif
				padr[xcnt++] = opadr[off+j];
				padr[xcnt++] = opadr[off+j+1];
			}
			if (osadr[i].closed)
			{
#ifdef _DEBUG
	if (xcnt+1>=npcnt || off+j>=pcnt) { GeBoom(); break; }
#endif
				padr[xcnt++] = opadr[off+j];
				padr[xcnt++] = opadr[off];
			}

			off+=osadr[i].cnt;
		}

		for (i=0; i<npcnt/2; i++)
		{
			sadr[i].closed = FALSE;
			sadr[i].cnt    = 2;
		}
		GeFree(osadr);
	}
	GeFree(opadr);
	return TRUE;

Error:
	GeFree(opadr);
	GeFree(sttpadr);
	GeFree(ngonpointmap);
	return FALSE;
}

BaseObject *BaseObject::GetAndCheckHierarchyClone(HierarchyHelp *hh, BaseObject *op, LONG flags, Bool *dirty, AliasTrans *trans, Bool allchilds)
{
	BaseObject *res=NULL,*tp=NULL,*dp=NULL;

	*dirty = *dirty || CheckCache(hh) || IsDirty(DIRTY_DATA);
	if (!(*dirty))
	{
		NewDependenceList();

		if (!allchilds)
			GetHierarchyClone(hh,op,flags,dirty,trans);
		else
		{
			for (tp=op;tp;tp=tp->GetNext())
				GetHierarchyClone(hh,tp,flags,dirty,trans);
		}
	}
	*dirty = *dirty || !CompareDependenceList();

	if (!(*dirty))
	{
		TouchDependenceList();
		return GetCache(hh);
	}

	NewDependenceList();
	if (!allchilds)
		res = GetHierarchyClone(hh,op,flags,NULL,trans);
	else
	{
		res = BaseObject::Alloc(Onull); if (!res) return NULL;
		res->SetName(op->GetName());
		for (tp=op;tp;tp=tp->GetNext())
		{
			dp = GetHierarchyClone(hh,tp,flags,NULL,trans);
			if (dp) dp->InsertUnderLast(res);
		}
	}
	return res;
}

Bool AddTextureString(GetAllStringData *gd, const Filename &fn, BaseList2D *bl)
{
	return C4DOS.Bo->AddTexture(bl,&fn,gd);
}

void BaseObject::NewDependenceList(void)
{
	C4DOS.Bo->NewDependenceList(this);
}

Bool BaseObject::CompareDependenceList(void)
{
	return C4DOS.Bo->CmpDependenceList(this);
}

void BaseObject::TouchDependenceList(void)
{
	C4DOS.Bo->TouchDependenceList(this);
}

void BaseObject::AddDependence(HierarchyHelp *hh, BaseObject *op)
{
	C4DOS.Bo->AddDependence(this,hh,op);
}

BaseObject *BaseObject::GetHierarchyClone(HierarchyHelp *hh, BaseObject *op, LONG flags, Bool *dirty, AliasTrans *trans)
{
	return C4DOS.Bo->GetHierarchyClone(this,hh,op,flags,dirty,trans);
}

Neighbor::Neighbor(void)
{
	ee=NULL;
}

Neighbor::~Neighbor(void)
{
	C4DOS.Nb->Free(ee);
}

Bool Neighbor::Init(LONG pcnt, const CPolygon *vadr, LONG vcnt, BaseSelect *bs)
{
	C4DOS.Nb->Free(ee);
	if (!vadr) return TRUE;
	ee=C4DOS.Nb->Alloc(pcnt,vadr,vcnt,bs);
	return ee!=NULL;
}

LONG Neighbor::GetEdgeCount(void)
{
	if (!ee) return 0;
	return C4DOS.Nb->GetEdgeCount(ee);
}

LONG Neighbor::GetNeighbor(LONG a, LONG b, LONG poly)
{
  LONG l1, l2;
  GetEdgePolys(a, b, &l1, &l2);
  if (l1 == poly) return l2;
  else if (l2 == poly) return l1;
  return NOTOK;
}


SplineObject *FitCurve(Vector *padr, LONG pcnt, Real error, BaseThread *bt)
{
	return C4DOS.Bo->FitCurve(padr,pcnt,error,bt);
}

Bool Triangulate(const Vector *padr, LONG pcnt, CPolygon **vadr, LONG *vcnt)
{
	return C4DOS.Bo->Triangulate(padr,pcnt,vadr,vcnt);
}

Bool TriangulateStandard(const Vector *padr, LONG pcnt, LONG *list, LONG lanz, CPolygon *&vadr, LONG &vcnt, BaseThread *thread)
{
	return C4DOS.Bo->TriangulateStandard(padr,pcnt,list,lanz,vadr,vcnt,thread);
}

Bool TriangulateRegular(const Vector *pinp, LONG pinp_cnt, LONG *list, LONG lanz, Vector *&padr, LONG &pcnt, CPolygon *&vadr, LONG &vcnt, Real regular_width, BaseThread *thread)
{
	return C4DOS.Bo->TriangulateRegular(pinp,pinp_cnt,list,lanz,padr,pcnt,vadr,vcnt,regular_width,thread);
}

BaseObject *GenerateText(BaseContainer *cp, BaseThread *bt, Bool separate)
{
	return C4DOS.Bo->GenerateText(cp,bt,separate);
}

PolygonObject *LineObject::Triangulate(Real regular, BaseThread *bt)
{
	return C4DOS.Bo->TriangulateLine(this,regular,bt);
}

BaseSelect* PolygonObject::GetSelectedEdges(Neighbor *e, EdgeSelectionType ltype)
{
  BaseSelect *ope = NULL;
	if (ltype==EDGES_HIDDEN)
		ope=GetEdgeH();
	else if (ltype==EDGES_SELECTION)
		ope=GetEdgeS();
	else if (ltype==EDGES_PHONG)
		ope=GetPhongBreak();
	if (!ope)
		return NULL;

	return GetSelectedEdges(e, ope);
}

BaseSelect* PolygonObject::GetSelectedEdges(Neighbor *e, BaseSelect* ope)
{
	BaseSelect *xsel = BaseSelect::Alloc();
	if (!xsel || !ope) return NULL;
  const CPolygon* p = GetPolygonR();
  if (!p) return xsel;

	LONG i,j,a,b,edge;
	for (i=0; i<ope->GetSegments(); i++)
	{
		if (!ope->GetRange(i,&a,&b)) continue;
		for (j = a; j <= b; j++)
		{
      if ((j & 3) == 2 && p[j >> 2].c == p[j >> 2].d) continue;

			edge = e->GetPolyInfo(j>>2)->edge[j&3];
			if (edge != NOTOK)
				xsel->Select(edge);
		}
	}

	return xsel;
}

Bool PolygonObject::SetSelectedEdges(Neighbor *e, BaseSelect* pSel, EdgeSelectionType ltype)
{
  const CPolygon* pPolys = GetPolygonR();
  if (!pPolys) return FALSE;

  BaseSelect *ope = NULL;
	if (ltype==EDGES_HIDDEN)
		ope=GetEdgeH();
	else if (ltype==EDGES_SELECTION)
		ope=GetEdgeS();
	else if (ltype==EDGES_PHONG)
		ope=GetPhongBreak();

  if (!ope) return FALSE;
  ope->FlushAll();

  LONG a;
  for (a = 0; a < GetPolygonCount(); a++)
  {
    PolyInfo* p = e->GetPolyInfo(a);
    if (!p) continue;

    if (pSel->IsSelected(p->edge[0])) ope->Select(a * 4);
    if (pSel->IsSelected(p->edge[1])) ope->Select(a * 4 + 1);
    if (pPolys[a].c != pPolys[a].d)
    {
      if (pSel->IsSelected(p->edge[2])) ope->Select(a * 4 + 2);
    }
    if (pSel->IsSelected(p->edge[3])) ope->Select(a * 4 + 3);
  }
  return TRUE;
}


BaseObject *GetVirtualLineObject(BaseObject *op, HierarchyHelp *hh, const Matrix &mloc, Bool keep_spline, Bool recurse, Matrix *mres, Bool *dirty)
{
	return C4DOS.Bo->GetVirtualLineObject(op,hh,mloc,keep_spline,recurse,mres,dirty);
}

void BaseObject::Touch(void)
{
	C4DOS.Bo->Touch(this);
}

UVWTag *GenerateUVW(BaseObject *op, const Matrix &opmg, TextureTag *tp, const Matrix &texopmg, BaseView *view)
{
	return C4DOS.Bo->GenerateUVW(op,opmg,tp,texopmg,view);
}

void CutReal(BaseContainer &data, LONG id, Real min, Real max)
{
	data.SetReal(id,FCut(data.GetReal(id),min,max));
}

void CutVector(BaseContainer &data, LONG id, Real min, Real max)
{
	Vector v = data.GetVector(id);
	data.SetVector(id,Vector(FCut(v.x,min,max),FCut(v.y,min,max),FCut(v.z,min,max)));
}

BaseObject *BaseObject::Alloc(LONG type)
{
	return C4DOS.Bo->Alloc(type,0);
}

void BaseObject::Free(BaseObject *&bl)
{
	C4DOS.Bl->Free(bl);
	bl=NULL;
}

CameraObject *CameraObject::Alloc()
{
	return (CameraObject*)C4DOS.Bo->Alloc(Ocamera,0);
}

void CameraObject::Free(CameraObject *&bl)
{
	C4DOS.Bl->Free(bl);
	bl=NULL;
}

LineObject *LineObject::Alloc(LONG pcnt, LONG scnt)
{
	LineObject *op = (LineObject*)BaseObject::Alloc(Oline);
	if (!op) return NULL;

	if (!op->MakeVariableTag(Tpoint,pcnt)) goto Error;
	if (!op->MakeVariableTag(Tsegment,scnt)) goto Error;
	op->Message(MSG_UPDATE);

	return op;
Error:
	blDelete(op);
	return NULL;
}

void LineObject::Free(LineObject *&bl)
{
	C4DOS.Bl->Free(bl);
	bl=NULL;
}

PolygonObject *PolygonObject::Alloc(LONG pcnt, LONG vcnt)
{
	PolygonObject *op = (PolygonObject*)BaseObject::Alloc(Opolygon);
	if (!op) return NULL;
	if (!op->ResizeObject(pcnt,vcnt)) blDelete(op);
	return op;
}

void PolygonObject::Free(PolygonObject *&bl)
{
	C4DOS.Bl->Free(bl);
	bl=NULL;
}

SplineObject *SplineObject::Alloc(LONG pcnt, LONG type)
{
	return C4DOS.Bo->AllocSplineObject(pcnt,type);
}

void SplineObject::Free(SplineObject *&bl)
{
	C4DOS.Bl->Free(bl);
	bl=NULL;
}

void BaseObject::SetColorProperties(ObjectColorProperties *prop)
{
	C4DOS.Bo->SetColorProperties(this,prop);
}

void BaseObject::GetColorProperties(ObjectColorProperties *prop)
{
	C4DOS.Bo->GetColorProperties(this,prop);
}

BaseObject *BaseObject::GetCacheParent(void)
{
	return C4DOS.Bo->GetCacheParent(this);
}

Bool CheckDisplayFilter(BaseObject *op, LONG filter)
{
	return C4DOS.Bo->CheckDisplayFilter(op,filter);
}

LONG IntersectionTest(PolygonObject *op, BaseDraw *bd, Real x, Real y, const Matrix &mg, Real *z, LONG mode, UCHAR *pPointSelect, LONG lSelectCount)
{
	return C4DOS.Bo->IntersectionTest(op,bd,x,y,mg,z,mode,pPointSelect,lSelectCount);
}

Vector *PolygonObject::CreatePhongNormals()
{
	return C4DOS.Bo->CreatePhongNormals(this);
}

Bool PolygonObject::GetPolygonTranslationMap(LONG &ngoncnt, LONG *&polymap)
{
	return C4DOS.Bo->PyGetPolygonTranslationMap(this, ngoncnt, polymap);
}

NgonBase *PolygonObject::GetNgonBase()
{
	return C4DOS.Bo->PyGetNgonBase(this);
}

Bool PolygonObject::GetNGonTranslationMap(LONG ngoncnt, LONG *polymap, LONG **&ngons)
{
	return C4DOS.Bo->PyGetNGonTranslationMap(this, ngoncnt, polymap, ngons);
}

Pgon* PolygonObject::GetAndBuildNgon(void)
{
  return C4DOS.Bo->PyGetNgon(this);
}

LONG PolygonObject::GetNgonCount(void)
{
  return C4DOS.Bo->PyGetNgonCount(this);
}

void PolygonObject::GetSelectedNgons(BaseSelect* sel)
{
  C4DOS.Bo->PyGetSelectedNgons(this, sel);
}

void PolygonObject::GetHiddenNgons(BaseSelect* sel)
{
  C4DOS.Bo->PyGetHiddenNgons(this, sel);
}


PolyTriangulate::PolyTriangulate()
{
}

PolyTriangulate::~PolyTriangulate()
{
}

PolyTriangulate* PolyTriangulate::Alloc()
{
	return C4DOS.Bo->PolyTriangAlloc();
}

void PolyTriangulate::Free(PolyTriangulate *&pTriang)
{
  C4DOS.Bo->PolyTriangFree(pTriang);
}

Bool PolyTriangulate::Triangulate(const Vector* pvPoints, LONG lPointCount, const LONG* plSegments, LONG lSegCnt,
    CPolygon*& pPolys, LONG& lPolyCount, LONG lFlags, const LONG* plMap, BaseThread* pThread,
    LONG lConstraints, const LONG* plConstrainedEdges)
{
  return C4DOS.Bo->PolyTriangTriang(this, pvPoints, lPointCount, plSegments, lSegCnt, pPolys, lPolyCount, lFlags,
    plMap, pThread, lConstraints, plConstrainedEdges);
}

Bool PolyTriangulate::Triangulate(const LVector* pvPoints, LONG lPointCount, const LONG* plSegments, LONG lSegCnt,
    CPolygon*& pPolys, LONG& lPolyCount, LONG lFlags, const LONG* plMap, BaseThread* pThread,
    LONG lConstraints, const LONG* plConstrainedEdges)
{
  return C4DOS.Bo->PolyTriangTriangA(this, pvPoints, lPointCount, plSegments, lSegCnt, pPolys, lPolyCount, lFlags,
    plMap, pThread, lConstraints, plConstrainedEdges);
}

Bool PolyTriangulate::TriangulateRelease(const Vector* pvPoints, LONG lPointCount, const LONG* plSegments, LONG lSegCnt,
    CPolygon*& pPolys, LONG& lPolyCount, LONG lFlags, const LONG* plMap, BaseThread* pThread,
    LONG lConstraints, const LONG* plConstrainedEdges)
{
  return C4DOS.Bo->PolyTriangTriangRelease(this, pvPoints, lPointCount, plSegments, lSegCnt, pPolys, lPolyCount, lFlags,
    plMap, pThread, lConstraints, plConstrainedEdges);
}

Bool PolyTriangulate::TriangulateRelease(const LVector* pvPoints, LONG lPointCount, const LONG* plSegments, LONG lSegCnt,
    CPolygon*& pPolys, LONG& lPolyCount, LONG lFlags, const LONG* plMap, BaseThread* pThread,
    LONG lConstraints, const LONG* plConstrainedEdges)
{
  return C4DOS.Bo->PolyTriangTriangReleaseA(this, pvPoints, lPointCount, plSegments, lSegCnt, pPolys, lPolyCount, lFlags,
    plMap, pThread, lConstraints, plConstrainedEdges);
}

Bool PolyTriangulate::HasIdentical()
{
	return C4DOS.Bo->PolyTriangHasIdentical(this);
}

void PolyTriangulate::SetPolygonMatrix(LMatrix* m)
{
	C4DOS.Bo->PolyTriangSetPolygonMatrix(this, m);
}

ULONG PolyTriangulate::GetState()
{
	return C4DOS.Bo->PolyTriangGetState(this);
}

Bool CalculateVisiblePoints(BaseDraw *bd, PolygonObject *op, Vector *padr, UCHAR *pset, Bool select_visibonly)
{
	return C4DOS.Bo->CalculateVisiblePoints(bd,op,padr,pset,select_visibonly);
}

Bool HNSubdivideObject(BaseDocument *doc, BaseObject *pop, LONG sub, LONG subiso, LONG generatebits, BaseThread *thread)
{
	return C4DOS.Bo->HNSubdivideObject(doc, pop, sub, subiso, generatebits, thread);
}