#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, Float angle)
{
	Bool useEdgeBreak = true;
	BaseTag* phongTag = GetTag(Tphong);
	if (phongTag)
	{
		BaseContainer* tBc = phongTag->GetDataInstance();
		if (!tBc)
			return false;
		useEdgeBreak = tBc->GetBool(PHONGTAG_PHONG_USEEDGES, true);
	}
	
	KillTag(Tphong);
	if (!on) return true;
	BaseTag *tag=MakeTag(Tphong);
	if (!tag) return false;
	BaseContainer bc;
	bc.SetBool(PHONGTAG_PHONG_ANGLELIMIT,anglelimit);
	bc.SetFloat(PHONGTAG_PHONG_ANGLE,angle);
	bc.SetBool(PHONGTAG_PHONG_USEEDGES,useEdgeBreak);
	tag->SetData(bc,true);
	return true;
}

Float BaseObject::GetVisibility(Float parent)
{
	return C4DOS.Bo->GetVisibility(this, parent);
}

void BaseObject::SetIsoparm(LineObject* l)
{
	C4DOS.Bo->SetIsoparm(this, l);
}

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, DISPLAYEDITSTATE state)
{
	return C4DOS.Bo->GetEditObject(this, psds, state);
}

Int32 BaseObject::GetUniqueIP(void)
{
	return C4DOS.Bo->GetUniqueIP(this);
}

void BaseObject::SetUniqueIP(Int32 ip)
{
	C4DOS.Bo->SetUniqueIP(this, ip);
}

Int32 BaseObject::GetEditorMode(void)
{
	return C4DOS.Bo->GetMode(this, OBJECTSTATE_EDITOR);
}

void BaseObject::SetEditorMode(Int32 mode)
{
	C4DOS.Bo->SetMode(this, OBJECTSTATE_EDITOR, mode);
}

Int32 BaseObject::GetRenderMode(void)
{
	return C4DOS.Bo->GetMode(this, OBJECTSTATE_RENDER);
}

void BaseObject::SetRenderMode(Int32 mode)
{
	C4DOS.Bo->SetMode(this, OBJECTSTATE_RENDER, mode);
}

Bool BaseObject::GetDeformMode(void)
{
	return C4DOS.Bo->GetMode(this, OBJECTSTATE_DEFORM) != 0;
}

void BaseObject::SetDeformMode(Bool mode)
{
	C4DOS.Bo->SetMode(this, OBJECTSTATE_DEFORM, mode);
}

void BaseObject::InsertTag(BaseTag* tp, BaseTag* pred)
{
	C4DOS.Bo->InsertTag(this, tp, pred);
}

void BaseObject::KillTag (Int32 type, Int32 nr)
{
	C4DOS.Bo->KillTag(this, type, nr);
}

BaseTag* BaseObject::GetFirstTag(void)
{
	return C4DOS.Bo->GetFirstTag(this);
}

Bool BaseObject::CopyTagsTo(BaseObject* dest, Int32 visible, Int32 variable, Int32 hierarchical, AliasTrans* trans)
{
	return C4DOS.Bo->CopyTagsTo(this, dest, visible, variable, hierarchical, trans);
}

BaseTag* BaseObject::MakeTag(Int32 type, BaseTag* pred)
{
	BaseTag* tag = BaseTag::Alloc(type);
	if (tag)
		InsertTag(tag, pred);
	return tag;
}

VariableTag* BaseObject::MakeVariableTag(Int32 type, Int32 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(Int32 pcnt)
{
	return C4DOS.Bo->PoResizeObject(this, pcnt);
}

void PointObject::IncreaseBoundingBox(Int32 cnt, Vector* v)
{
	return C4DOS.Bo->PoIncreaseBoundingBox(this, cnt, v);
}

Float32* PointObject::CalcVertexMap(BaseObject* modifier)
{
	return C4DOS.Bo->PoCalcVertexMap(this, modifier);
}

Bool SplineObject::ResizeObject(Int32 pcnt, Int32 scnt)
{
	return C4DOS.Bo->SpResizeObject(this, pcnt, scnt);
}

Bool LineObject::ResizeObject(Int32 pcnt, Int32 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(Int32 pcnt, Int32 vcnt)
{
	return C4DOS.Bo->PyResizeObject(this, pcnt, vcnt);
}

Bool PolygonObject::ResizeObject(Int32 pcnt, Int32 vcnt, Int32 ncnt)
{
	return C4DOS.Bo->PyResizeObjectNgon(this, pcnt, vcnt, ncnt);
}

Bool PolygonObject::ResizeObject(Int32 pcnt, Int32 vcnt, Int32 ncnt, Int32 vc_flags)
{
	return C4DOS.Bo->PyResizeObjectNgonFlags(this, pcnt, vcnt, ncnt, vc_flags);
}

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);
}

UInt32 PolygonObject::VBOInitUpdate(BaseDraw* bd)
{
	return C4DOS.Bo->PyVBOInitUpdate(this, bd);
}

Bool PolygonObject::VBOStartUpdate(BaseDraw* bd, GlVertexBufferAccessFlags access, Bool latemap)
{
	return C4DOS.Bo->PyVBOStartUpdate(this, bd, access, latemap);
}

const GlVertexBufferAttributeInfo* PolygonObject::VBOUpdateVectorGetAttribute(UInt32 flags)
{
	return C4DOS.Bo->PyVBOUpdateVectorGetAttribute(this, flags);
}

void PolygonObject::VBOFreeUpdate()
{
	C4DOS.Bo->PyVBOFreeUpdate(this);
}

void PolygonObject::VBOEndUpdate(BaseDraw* bd)
{
	C4DOS.Bo->PyVBOEndUpdate(this, bd);
}


SPLINETYPE SplineObject::GetInterpolationType(void) const
{
	SPLINETYPE type = (SPLINETYPE)GetDataInstance()->GetInt32(SPLINEOBJECT_TYPE);
	if (GetPointCount() > 2 || type == SPLINETYPE_BEZIER)
		return type;
	else
		return SPLINETYPE_LINEAR;
}

Bool SplineObject::IsClosed(void)
{
	return GetDataInstance()->GetBool(SPLINEOBJECT_CLOSED);
}

Vector SplineObject::GetSplinePoint(Float t, Int32 segment, const Vector* padr)
{
	return C4DOS.Bo->SpGetSplinePoint(this, t, segment, padr);
}

Vector SplineObject::GetSplineTangent(Float t, Int32 segment, const Vector* padr)
{
	return C4DOS.Bo->SpGetSplineTangent(this, t, segment, padr);
}

Bool SplineLengthData::Init(SplineObject* op, Int32 segment, const Vector* padr)
{
	return C4DOS.Bo->SpInitLength(this, op, segment, padr);
}

SplineLengthData* SplineLengthData::Alloc()
{
	return C4DOS.Bo->SpLDAlloc();
}

void SplineLengthData::Free(SplineLengthData*& bl)
{
	return C4DOS.Bo->SpLDFree(bl);
}

Float SplineLengthData::UniformToNatural(Float t)
{
	return C4DOS.Bo->SpUniformToNatural(this, t);
}

Float SplineLengthData::GetLength(void)
{
	return C4DOS.Bo->SpGetLength(this);
}

Float SplineLengthData::GetSegmentLength(Int32 a, Int32 b)
{
	return C4DOS.Bo->SpGetSegmentLength(this, a, b);
}

LineObject* SplineObject::GetLineObject(BaseDocument* doc, Float 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 = nullptr;
}

Safety::~Safety(void)
{
	C4DOS.Bo->OsFree(os, true);
}

Bool Safety::Init(BaseObject* op)
{
	os = C4DOS.Bo->OsAlloc(op);
	return os != nullptr;
}

void Safety::Detach(void)
{
	C4DOS.Bo->OsFree(os, false);
}

Int32 CameraObject::GetProjection(void)
{
	return C4DOS.Bo->CoGetProjection(this);
}

Float CameraObject::GetFocus(void)
{
	return C4DOS.Bo->CoGetFocus(this);
}

Float CameraObject::GetZoom(void)
{
	return C4DOS.Bo->CoGetZoom(this);
}

Vector CameraObject::GetOffset(void)
{
	return C4DOS.Bo->CoGetOffset(this);
}

Float CameraObject::GetAperture(void)
{
	return C4DOS.Bo->CoGetAperture(this);
}

Bool CameraObject::SetProjection(Int32 projection)
{
	return C4DOS.Bo->CoSetProjection(this, projection);
}

Bool CameraObject::SetFocus(Float v)
{
	return C4DOS.Bo->CoSetFocus(this, v);
}

Bool CameraObject::SetAperture(Float v)
{
	return C4DOS.Bo->CoSetAperture(this, v);
}

Bool CameraObject::SetZoom(Float v)
{
	return C4DOS.Bo->CoSetZoom(this, v);
}

Bool CameraObject::SetOffset(const Vector& offset)
{
	return C4DOS.Bo->CoSetOffset(this, offset);
}

Int32 CameraObject::StereoGetCameraCount(BaseDocument* doc, BaseDraw* bd, RenderData* rd, Int32 flags) const
{
	return C4DOS.Bo->CoStereoGetCameraCount(this, doc, bd, rd, flags);
}

Bool CameraObject::StereoGetCameraInfo(BaseDocument* doc, BaseDraw* bd, RenderData* rd, Int32 n, StereoCameraInfo& info, Int32 flags) const
{
	return C4DOS.Bo->CoStereoGetCameraInfo(this, doc, bd, rd, n, info, flags);
}

BaseObject* GeneratePrimitive(BaseDocument* doc, Int32 type, const BaseContainer& bc, Float lod, Bool isoparm, BaseThread* bt)
{
	return C4DOS.Bo->GenPrimitive(doc, type, &bc, lod, isoparm, bt);
}

BaseObject* GenerateSplinePrimitive(BaseDocument* doc, Int32 type, const BaseContainer& bc, Float lod, BaseThread* bt)
{
	return C4DOS.Bo->GenSplinePrimitive(doc, type, &bc, lod, bt);
}

Int32 CalcLOD(Int32 val, Float lod, Int32 min, Int32 max)
{
	val = Int32(Float(val) * lod + 0.001);
	return LCut(val, min, max);
}

struct LL2
{
	Int32 val;
	Int32 other;
};

class DisjointNgonMesh : public maxon::BaseSort<DisjointNgonMesh>
{
public:
	static Bool LessThan(const LL2& a, const LL2& b)
	{
		return a.val < b.val;
	}
};

#include "c4d_general.h"

Bool DisjointMesh(PointObject* op)
{
	Bool		same;
	Int32		pcnt = op->GetPointCount(), i, npcnt = 0;
	Vector* padr = op->GetPointW(), *sttpadr = nullptr, *opadr = nullptr, *xadr = nullptr;
	Int32*	ngonpointmap = nullptr;

	if (pcnt == 0)
		return true;

	if (op->GetType() == Opolygon)
	{
		StickTextureTag* stt	 = (StickTextureTag*)op->GetTag(Tsticktexture);
		Int32						 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;

				Int32 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 = NewMem(Vector, pcnt);
			if (!sttpadr)
				goto Error;
			CopyMemType(stt->GetDataAddressR(), sttpadr, pcnt);
		}

		opadr = NewMem(Vector, pcnt);
		if (!opadr)
			goto Error;
		CopyMemType(padr, opadr, pcnt);

		if (maxngon > 0)
		{
			ngonpointmap = NewMemClear(Int32, maxngon * 8);
			if (!ngonpointmap)
				goto Error;
		}

		if (!ToPoly(op)->ResizeObject(npcnt, NOTOK))
			goto Error;
		if (stt && !stt->Record(op, true))
			goto Error;

		xadr	= stt ? stt->GetDataAddressW() : nullptr;
		padr	= op->GetPointW();
		npcnt = 0;

		for (i = 0; i < vcnt; i++)
		{
			if (ngons && (pid = ngons->FindPolygon(i)) != NOTOK)
			{
				if (i != pgon[pid].GetIdentity())
					continue;

				Int32 ncnt = pgon[pid].GetPointCount(), nncnt = 0;

				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;
				}

				Int32 arraycnt = p, lastp;
				if (arraycnt > 0)
				{
					pointsort.Sort((LL2*)ngonpointmap, arraycnt / 2);

					p = 0;
					do
					{
						DebugAssert(nncnt < ncnt);
						if (nncnt >= ncnt)
							goto Error;		// FIX[27630] - invalid/illegal ngon

						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++;
						nncnt++;

					} while (p < arraycnt);

					DebugAssert(nncnt == ncnt);
					if (nncnt != ncnt)
						goto Error;		// FIX[27630] - invalid/illegal ngon
				}
			}
			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;
				}
			}
		}

		DeleteMem(ngonpointmap);
		DeleteMem(sttpadr);
	}
	else if (op->GetType() == Oline)
	{
		Segment* sadr = ((LineObject*)op)->GetSegmentW();
		Int32		 scnt = ((LineObject*)op)->GetSegmentCount(), off, j;
		if (scnt == 0)
			return false;

		for (i = 0; i < scnt; i++)
		{
			if (!sadr[i].cnt)
				continue;
			if (sadr[i].closed)
				npcnt += sadr[i].cnt * 2;
			else
				npcnt += (sadr[i].cnt - 1) * 2;
		}

		opadr = NewMem(Vector, pcnt);
		if (!opadr)
			return false;
		CopyMemType(padr, opadr, pcnt);

		// crashbugfix: copy also the segments because the array can get smaller
		Segment* osadr = NewMem(Segment, scnt);
		if (!osadr)
		{
			DeleteMem(opadr);
			return false;
		}
		CopyMemType(sadr, osadr, scnt);

		if (!((LineObject*)op)->ResizeObject(npcnt, npcnt / 2))
		{
			DeleteMem(opadr);
			DeleteMem(osadr);
			return false;
		}

		padr = op->GetPointW();
		sadr = ((LineObject*)op)->GetSegmentW();
		Int32 xcnt = 0;
		off = 0;
		for (i = 0; i < scnt; i++)
		{
			if (!osadr[i].cnt)
				continue;

			for (j = 0; j + 1 < osadr[i].cnt; j++)
			{
#ifdef _DEBUG
				if (xcnt + 1 >= npcnt || off + j + 1 >= pcnt)
				{
					CriticalStop(); 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)
				{
					CriticalStop(); 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;
		}
		DeleteMem(osadr);
	}
	DeleteMem(opadr);
	return true;

Error:
	DeleteMem(opadr);
	DeleteMem(sttpadr);
	DeleteMem(ngonpointmap);
	return false;
}

BaseObject* BaseObject::GetAndCheckHierarchyClone(HierarchyHelp* hh, BaseObject* op, HIERARCHYCLONEFLAGS flags, Bool* dirty, AliasTrans* trans, Bool allchildren)
{
	BaseObject* res = nullptr, *tp = nullptr, *dp = nullptr;

	*dirty = *dirty || CheckCache(hh) || IsDirty(DIRTYFLAGS_DATA);
	if (!(*dirty))
	{
		NewDependenceList();

		if (!allchildren)
		{
			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 (!allchildren)
	{
		res = GetHierarchyClone(hh, op, flags, nullptr, trans);
	}
	else
	{
		res = BaseObject::Alloc(Onull);
		if (!res)
			return nullptr;
		res->SetName(op->GetName());
		for (tp = op; tp; tp = tp->GetNext())
		{
			dp = GetHierarchyClone(hh, tp, flags, nullptr, trans);
			if (dp)
				dp->InsertUnderLast(res);
		}
	}
	return res;
}

Bool AssetData::Add(const Filename& fn, BaseList2D* bl, Bool netRequestOnDemand)
{
	return C4DOS.Bo->AddTexture(bl, &fn, netRequestOnDemand, this);
}

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, HIERARCHYCLONEFLAGS flags, Bool* dirty, AliasTrans* trans)
{
	return C4DOS.Bo->GetHierarchyClone(this, hh, op, flags, dirty, trans);
}

void BaseObject::RemoveFromCache()
{
	C4DOS.Bo->RemoveFromCache(this);
}

Neighbor::Neighbor(void)
{
	ee = nullptr;
}

Neighbor::~Neighbor(void)
{
	C4DOS.Nb->Free(ee);
}

Bool Neighbor::Init(Int32 pcnt, const CPolygon* vadr, Int32 vcnt, BaseSelect* bs)
{
	C4DOS.Nb->Free(ee);
	if (!vadr)
		return true;
	ee = C4DOS.Nb->Alloc(pcnt, vadr, vcnt, bs);
	return ee != nullptr;
}

Int32 Neighbor::GetEdgeCount(void)
{
	if (!ee)
		return 0;
	return C4DOS.Nb->GetEdgeCount(ee);
}

Int32 Neighbor::GetNeighbor(Int32 a, Int32 b, Int32 poly)
{
	Int32 l1, l2;
	GetEdgePolys(a, b, &l1, &l2);
	if (l1 == poly)
		return l2;
	else if (l2 == poly)
		return l1;
	return NOTOK;
}


SplineObject* FitCurve(Vector* padr, Int32 pcnt, Float error, BaseThread* bt)
{
	return C4DOS.Bo->FitCurve(padr, pcnt, error, bt);
}

Bool Triangulate(const Vector* padr, Int32 pcnt, CPolygon** vadr, Int32* vcnt)
{
	return C4DOS.Bo->Triangulate(padr, pcnt, vadr, vcnt);
}

Bool TriangulateStandard(const Vector* padr, Int32 pcnt, Int32* list, Int32 lanz, CPolygon*& vadr, Int32& vcnt, BaseThread* thread)
{
	return C4DOS.Bo->TriangulateStandard(padr, pcnt, list, lanz, vadr, vcnt, thread);
}

Bool TriangulateRegular(const Vector* pinp, Int32 pinp_cnt, Int32* list, Int32 lanz, Vector*& padr, Int32& pcnt, CPolygon*& vadr, Int32& vcnt, Float 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(Float regular, BaseThread* bt)
{
	return C4DOS.Bo->TriangulateLine(this, regular, bt);
}

BaseSelect* PolygonObject::GetSelectedEdges(Neighbor* e, EDGESELECTIONTYPE ltype)
{
	BaseSelect* ope = nullptr;
	if (ltype == EDGESELECTIONTYPE_HIDDEN)
		ope = GetEdgeH();
	else if (ltype == EDGESELECTIONTYPE_SELECTION)
		ope = GetEdgeS();
	else if (ltype == EDGESELECTIONTYPE_PHONG)
		ope = GetPhongBreak();
	if (!ope)
		return nullptr;

	return GetSelectedEdges(e, ope);
}

BaseSelect* PolygonObject::GetSelectedEdges(Neighbor* e, BaseSelect* ope)
{
	BaseSelect* xsel = BaseSelect::Alloc();
	if (!xsel || !ope)
		return nullptr;
	const CPolygon* p = GetPolygonR();
	Int32 vcnt4 = GetPolygonCount() * 4;
	if (!p)
		return xsel;

	Int32 i, j, a, b, edge;
	for (i = 0; i < ope->GetSegments(); i++)
	{
		if (!ope->GetRange(i, vcnt4, &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 = nullptr;
	if (ltype == EDGESELECTIONTYPE_HIDDEN)
		ope = GetEdgeH();
	else if (ltype == EDGESELECTIONTYPE_SELECTION)
		ope = GetEdgeS();
	else if (ltype == EDGESELECTIONTYPE_PHONG)
		ope = GetPhongBreak();

	if (!ope)
		return false;
	ope->DeselectAll();

	Int32 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, Int32 id, Float min, Float max)
{
	data.SetFloat(id, ClampValue(data.GetFloat(id), min, max));
}

void CutVector(BaseContainer& data, Int32 id, Float min, Float max)
{
	Vector v = data.GetVector(id);
	data.SetVector(id, Vector(ClampValue(v.x, min, max), ClampValue(v.y, min, max), ClampValue(v.z, min, max)));
}

BaseObject* BaseObject::Alloc(Int32 type)
{
	return C4DOS.Bo->Alloc(type);
}

void BaseObject::Free(BaseObject*& bl)
{
	C4DOS.Bl->Free(bl);
	bl = nullptr;
}

CameraObject* CameraObject::Alloc()
{
	return (CameraObject*)C4DOS.Bo->Alloc(Ocamera);
}

void CameraObject::Free(CameraObject*& bl)
{
	C4DOS.Bl->Free(bl);
	bl = nullptr;
}

LineObject* LineObject::Alloc(Int32 pcnt, Int32 scnt)
{
	LineObject* op = (LineObject*)BaseObject::Alloc(Oline);
	if (!op)
		return nullptr;

	if (!op->MakeVariableTag(Tpoint, pcnt))
		goto Error;
	if (!op->MakeVariableTag(Tsegment, scnt))
		goto Error;
	op->Message(MSG_UPDATE);

	return op;
Error:
	blDelete(op);
	return nullptr;
}

void LineObject::Free(LineObject*& bl)
{
	C4DOS.Bl->Free(bl);
	bl = nullptr;
}

PolygonObject* PolygonObject::Alloc(Int32 pcnt, Int32 vcnt)
{
	PolygonObject* op = (PolygonObject*)BaseObject::Alloc(Opolygon);
	if (!op)
		return nullptr;
	if (!op->ResizeObject(pcnt, vcnt))
		blDelete(op);
	return op;
}

void PolygonObject::Free(PolygonObject*& bl)
{
	C4DOS.Bl->Free(bl);
	bl = nullptr;
}

SplineObject* SplineObject::Alloc(Int32 pcnt, SPLINETYPE type)
{
	return C4DOS.Bo->AllocSplineObject(pcnt, type);
}

void SplineObject::Free(SplineObject*& bl)
{
	C4DOS.Bl->Free(bl);
	bl = nullptr;
}

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, Int32 filter)
{
	return C4DOS.Bo->CheckDisplayFilter(op, filter);
}

Int32 IntersectionTest(PolygonObject* op, BaseDraw* bd, Float x, Float y, const Matrix& mg, Float* z, MODELINGCOMMANDMODE mode, UChar* pPointSelect, Int32 lSelectCount)
{
	return C4DOS.Bo->IntersectionTest(op, bd, x, y, mg, z, mode, pPointSelect, lSelectCount);
}

Vector32* PolygonObject::CreatePhongNormals()
{
	return C4DOS.Bo->CreatePhongNormals(this);
}

Bool PolygonObject::GetPolygonTranslationMap(Int32& ngoncnt, Int32*& polymap)
{
	return C4DOS.Bo->PyGetPolygonTranslationMap(this, ngoncnt, polymap);
}

NgonBase* PolygonObject::GetNgonBase()
{
	return C4DOS.Bo->PyGetNgonBase(this);
}

Bool PolygonObject::GetNGonTranslationMap(Int32 ngoncnt, Int32* polymap, Int32**& ngons)
{
	return C4DOS.Bo->PyGetNGonTranslationMap(this, ngoncnt, polymap, ngons);
}

Pgon* PolygonObject::GetAndBuildNgon(void)
{
	return C4DOS.Bo->PyGetNgon(this);
}

Int32 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, Int32 lPointCount, const Int32* plSegments, Int32 lSegCnt,
																	CPolygon*& pPolys, Int32& lPolyCount, Int32 lFlags, const Int32* plMap, BaseThread* pThread,
																	Int32 lConstraints, const Int32* plConstrainedEdges)
{
	return C4DOS.Bo->PolyTriangTriang(this, pvPoints, lPointCount, plSegments, lSegCnt, pPolys, lPolyCount, lFlags,
					 plMap, pThread, lConstraints, plConstrainedEdges);
}

#ifdef __FLOAT_32_BIT
Bool PolyTriangulate::Triangulate(const Vector64* pvPoints, Int32 lPointCount, const Int32* plSegments, Int32 lSegCnt,
																	CPolygon*& pPolys, Int32& lPolyCount, Int32 lFlags, const Int32* plMap, BaseThread* pThread,
																	Int32 lConstraints, const Int32* plConstrainedEdges)
{
	return C4DOS.Bo->PolyTriangTriangA(this, pvPoints, lPointCount, plSegments, lSegCnt, pPolys, lPolyCount, lFlags,
					 plMap, pThread, lConstraints, plConstrainedEdges);
}
#endif

Bool PolyTriangulate::TriangulateRelease(const Vector* pvPoints, Int32 lPointCount, const Int32* plSegments, Int32 lSegCnt,
																				 CPolygon*& pPolys, Int32& lPolyCount, Int32 lFlags, const Int32* plMap, BaseThread* pThread,
																				 Int32 lConstraints, const Int32* plConstrainedEdges)
{
	return C4DOS.Bo->PolyTriangTriangRelease(this, pvPoints, lPointCount, plSegments, lSegCnt, pPolys, lPolyCount, lFlags,
					 plMap, pThread, lConstraints, plConstrainedEdges);
}

#ifdef __FLOAT_32_BIT
Bool PolyTriangulate::TriangulateRelease(const Vector64* pvPoints, Int32 lPointCount, const Int32* plSegments, Int32 lSegCnt,
																				 CPolygon*& pPolys, Int32& lPolyCount, Int32 lFlags, const Int32* plMap, BaseThread* pThread,
																				 Int32 lConstraints, const Int32* plConstrainedEdges)
{
	return C4DOS.Bo->PolyTriangTriangReleaseA(this, pvPoints, lPointCount, plSegments, lSegCnt, pPolys, lPolyCount, lFlags,
					 plMap, pThread, lConstraints, plConstrainedEdges);
}
#endif

Bool PolyTriangulate::HasIdentical()
{
	return C4DOS.Bo->PolyTriangHasIdentical(this);
}

void PolyTriangulate::SetPolygonMatrix(Matrix64* m)
{
	C4DOS.Bo->PolyTriangSetPolygonMatrix(this, m);
}

UInt32 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);
}
