Hi @d_schmidt,

thank you for reaching out to us. And thank you for pointing out this false information in the C++ SDK documentation. In fact you cannot return a `BaseObject`

in `ObjectData::GetContour`

, instead you should return an empty spline. I added a modified double circle example at the end to demonstrate that approach.

About your code, there are also two other things that stand out to me. I was actually surprised that `static_cast<SplineObject*>(BaseObject::Alloc(Onull))`

did compile for you, because there is no direct implicit or explicit conversion from `BaseObject`

to `SplineObject`

. @m_adam then pointed out that this probably does compile because you can convert via `BaseList2D`

and the type symbols to a `SplineObject`

. But this cast is probably still the reason why Cinema 4D does crash for you, since this casting probably produces garbage. There is also the fact that you allocate `bp`

and leave it behind in the case when `CIRCLEOBJECT_RAD > 500.0`

.

I have added the documentation issue to our task list and it will be reflected in an upcoming release.

I hope this helps and cheers,

Ferdinand

The relevant bits:

```
static SplineObject* GenerateCircle(Float rad)
{
#define TANG 0.415
// Our little radius cutoff value.
#define MAX_RADIUS 500.0
// If the passed radius exceeds our MAX_RADIUS, bail and return an
// empty spline.
if (rad > MAX_RADIUS) {
SplineObject* dummy = SplineObject::Alloc(0, SPLINETYPE::LINEAR);
dummy->Message(MSG_UPDATE);
return dummy;
}
...
SplineObject* DoubleCircleData::GetContour(BaseObject* op, BaseDocument* doc, Float lod, BaseThread* bt)
{
BaseContainer* bc = op->GetDataInstance();
if (!bc)
return nullptr;
// This will now return an empty spline when CIRCLEOBJECT_RAD > MAX_RADIUS
SplineObject* bp = GenerateCircle(bc->GetFloat(CIRCLEOBJECT_RAD));
...
```

The whole file:

```
// spline example
#include "c4d.h"
#include "c4d_symbols.h"
#include "odoublecircle.h"
#include "main.h"
class DoubleCircleData : public ObjectData
{
INSTANCEOF(DoubleCircleData, ObjectData)
public:
virtual Bool Init(GeListNode* node);
virtual Bool Message (GeListNode* node, Int32 type, void* data);
virtual Int32 GetHandleCount(BaseObject* op);
virtual void GetHandle(BaseObject* op, Int32 i, HandleInfo& info);
virtual void SetHandle(BaseObject* op, Int32 i, Vector p, const HandleInfo& info);
virtual SplineObject* GetContour(BaseObject* op, BaseDocument* doc, Float lod, BaseThread* bt);
virtual Bool GetDEnabling(GeListNode* node, const DescID& id, const GeData& t_data, DESCFLAGS_ENABLE flags, const BaseContainer* itemdesc);
static NodeData* Alloc() { return NewObjClear(DoubleCircleData); }
};
Bool DoubleCircleData::Message(GeListNode* node, Int32 type, void* data)
{
if (type == MSG_MENUPREPARE)
{
BaseDocument* doc = (BaseDocument*)data;
((BaseObject*)node)->GetDataInstance()->SetInt32(PRIM_PLANE, doc->GetSplinePlane());
}
return true;
}
Bool DoubleCircleData::Init(GeListNode* node)
{
BaseObject* op = (BaseObject*)node;
BaseContainer* data = op->GetDataInstance();
if (!data)
return false;
data->SetFloat(CIRCLEOBJECT_RAD, 200.0);
data->SetInt32(PRIM_PLANE, 0);
data->SetBool(PRIM_REVERSE, false);
data->SetInt32(SPLINEOBJECT_INTERPOLATION, SPLINEOBJECT_INTERPOLATION_ADAPTIVE);
data->SetInt32(SPLINEOBJECT_SUB, 8);
data->SetFloat(SPLINEOBJECT_ANGLE, DegToRad(5.0));
data->SetFloat(SPLINEOBJECT_MAXIMUMLENGTH, 5.0);
return true;
}
static Vector SwapPoint(const Vector& p, Int32 plane)
{
switch (plane)
{
case 1: return Vector(-p.z, p.y, p.x); break;
case 2: return Vector(p.x, -p.z, p.y); break;
}
return p;
}
Int32 DoubleCircleData::GetHandleCount(BaseObject* op)
{
return 1;
}
void DoubleCircleData::GetHandle(BaseObject* op, Int32 i, HandleInfo& info)
{
BaseContainer* data = op->GetDataInstance();
if (!data)
return;
Float rad = data->GetFloat(CIRCLEOBJECT_RAD);
Int32 plane = data->GetInt32(PRIM_PLANE);
info.position = SwapPoint(Vector(rad, 0.0, 0.0), plane);
info.direction = !SwapPoint(Vector(1.0, 0.0, 0.0), plane);
info.type = HANDLECONSTRAINTTYPE::LINEAR;
}
void DoubleCircleData::SetHandle(BaseObject* op, Int32 i, Vector p, const HandleInfo& info)
{
BaseContainer* data = op->GetDataInstance();
if (!data)
return;
Float val = Dot(p, info.direction);
data->SetFloat(CIRCLEOBJECT_RAD, ClampValue(val, 0.0_f, (Float) MAXRANGE));
}
// --- Modifications start here. ---
static SplineObject* GenerateCircle(Float rad)
{
#define TANG 0.415
// Our little radius cutoff value.
#define MAX_RADIUS 500.0
// If the passed radius exceeds our MAX_RADIUS, bail and return an
// empty spline.
if (rad > MAX_RADIUS) {
SplineObject* dummy = SplineObject::Alloc(0, SPLINETYPE::LINEAR);
dummy->Message(MSG_UPDATE);
return dummy;
}
// --- Modifications end here ---
Float sn, cs;
Int32 i, sub = 4;
SplineObject* op = SplineObject::Alloc(sub * 2, SPLINETYPE::BEZIER);
if (!op || !op->MakeVariableTag(Tsegment, 2))
{
blDelete(op); return nullptr;
}
op->GetDataInstance()->SetBool(SPLINEOBJECT_CLOSED, true);
Vector* padr = op->GetPointW();
Tangent* hadr = op->GetTangentW();
Segment* sadr = op->GetSegmentW();
if (sadr)
{
sadr[0].closed = true;
sadr[0].cnt = sub;
sadr[1].closed = true;
sadr[1].cnt = sub;
}
if (hadr && padr)
{
for (i = 0; i < sub; i++)
{
SinCos(2.0 * PI * i / Float(sub), sn, cs);
padr[i] = Vector(cs * rad, sn * rad, 0.0);
hadr[i].vl = Vector(sn * rad * TANG, -cs * rad * TANG, 0.0);
hadr[i].vr = -hadr[i].vl;
padr[i + sub] = Vector(cs * rad, sn * rad, 0.0) * 0.5;
hadr[i + sub].vl = Vector(sn * rad * TANG, -cs * rad * TANG, 0.0) * 0.5;
hadr[i + sub].vr = -hadr[i + sub].vl;
}
}
op->Message(MSG_UPDATE);
return op;
}
static void OrientObject(SplineObject* op, Int32 plane, Bool reverse)
{
Vector* padr = ToPoint(op)->GetPointW();
Tangent* hadr = ToSpline(op)->GetTangentW(), h;
Int32 pcnt = ToPoint(op)->GetPointCount(), i;
if (!hadr && ToSpline(op)->GetTangentCount())
return;
if (!padr && ToPoint(op)->GetPointCount())
return;
if (plane >= 1)
{
switch (plane)
{
case 1: // ZY
for (i = 0; i < pcnt; i++)
{
padr[i] = Vector(-padr[i].z, padr[i].y, padr[i].x);
if (!hadr)
continue;
hadr[i].vl = Vector(-hadr[i].vl.z, hadr[i].vl.y, hadr[i].vl.x);
hadr[i].vr = Vector(-hadr[i].vr.z, hadr[i].vr.y, hadr[i].vr.x);
}
break;
case 2: // XZ
for (i = 0; i < pcnt; i++)
{
padr[i] = Vector(padr[i].x, -padr[i].z, padr[i].y);
if (!hadr)
continue;
hadr[i].vl = Vector(hadr[i].vl.x, -hadr[i].vl.z, hadr[i].vl.y);
hadr[i].vr = Vector(hadr[i].vr.x, -hadr[i].vr.z, hadr[i].vr.y);
}
break;
}
}
if (reverse)
{
Vector p;
Int32 to = pcnt / 2;
if (pcnt % 2)
to++;
for (i = 0; i < to; i++)
{
p = padr[i]; padr[i] = padr[pcnt - 1 - i]; padr[pcnt - 1 - i] = p;
if (!hadr)
continue;
h = hadr[i];
hadr[i].vl = hadr[pcnt - 1 - i].vr;
hadr[i].vr = hadr[pcnt - 1 - i].vl;
hadr[pcnt - 1 - i].vl = h.vr;
hadr[pcnt - 1 - i].vr = h.vl;
}
}
op->Message(MSG_UPDATE);
}
SplineObject* DoubleCircleData::GetContour(BaseObject* op, BaseDocument* doc, Float lod, BaseThread* bt)
{
BaseContainer* bc = op->GetDataInstance();
if (!bc)
return nullptr;
// This will now return an empty spline when CIRCLEOBJECT_RAD > MAX_RADIUS
SplineObject* bp = GenerateCircle(bc->GetFloat(CIRCLEOBJECT_RAD));
if (!bp)
return nullptr;
BaseContainer* bb = bp->GetDataInstance();
bb->SetInt32(SPLINEOBJECT_INTERPOLATION, bc->GetInt32(SPLINEOBJECT_INTERPOLATION));
bb->SetInt32(SPLINEOBJECT_SUB, bc->GetInt32(SPLINEOBJECT_SUB));
bb->SetFloat(SPLINEOBJECT_ANGLE, bc->GetFloat(SPLINEOBJECT_ANGLE));
bb->SetFloat(SPLINEOBJECT_MAXIMUMLENGTH, bc->GetFloat(SPLINEOBJECT_MAXIMUMLENGTH));
OrientObject(bp, bc->GetInt32(PRIM_PLANE), bc->GetBool(PRIM_REVERSE));
return bp;
}
Bool DoubleCircleData::GetDEnabling(GeListNode* node, const DescID& id, const GeData& t_data, DESCFLAGS_ENABLE flags, const BaseContainer* itemdesc)
{
Int32 inter;
BaseContainer* data = ((BaseObject*)node)->GetDataInstance();
if (!data)
return false;
switch (id[0].id)
{
case SPLINEOBJECT_SUB:
inter = data->GetInt32(SPLINEOBJECT_INTERPOLATION);
return inter == SPLINEOBJECT_INTERPOLATION_NATURAL || inter == SPLINEOBJECT_INTERPOLATION_UNIFORM;
case SPLINEOBJECT_ANGLE:
inter = data->GetInt32(SPLINEOBJECT_INTERPOLATION);
return inter == SPLINEOBJECT_INTERPOLATION_ADAPTIVE || inter == SPLINEOBJECT_INTERPOLATION_SUBDIV;
case SPLINEOBJECT_MAXIMUMLENGTH:
return data->GetInt32(SPLINEOBJECT_INTERPOLATION) == SPLINEOBJECT_INTERPOLATION_SUBDIV;
}
return true;
}
// be sure to use a unique ID obtained from www.plugincafe.com
#define ID_CIRCLEOBJECT 1001154
Bool RegisterCircle()
{
return RegisterObjectPlugin(ID_CIRCLEOBJECT, GeLoadString(IDS_CIRCLE), OBJECT_GENERATOR | OBJECT_ISSPLINE, DoubleCircleData::Alloc, "Odoublecircle"_s, AutoBitmap("circle.tif"_s), 0);
}
```