/*
* Copyright 2006-2012, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "VectorPath.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <agg_basics.h>
#include <agg_bounding_rect.h>
#include <agg_conv_curve.h>
#include <agg_curves.h>
#include <agg_math.h>
#ifdef ICON_O_MATIC
# include <debugger.h>
# include <typeinfo>
#endif // ICON_O_MATIC
#include <Message.h>
#include <TypeConstants.h>
#ifdef ICON_O_MATIC
# include "support.h"
# include "CommonPropertyIDs.h"
# include "IconProperty.h"
# include "Icons.h"
# include "Property.h"
# include "PropertyObject.h"
#endif // ICON_O_MATIC
#include "Transformable.h"
#define obj_new(type, n) ((type *)malloc ((n) * sizeof(type)))
#define obj_renew(p, type, n) ((type *)realloc ((void *)p, (n) * sizeof(type)))
#define obj_free free
#define ALLOC_CHUNKS 20
bool
get_path_storage(agg::path_storage& path, const control_point* points,
int32 count, bool closed)
{
if (count > 1) {
path.move_to(points[0].point.x, points[0].point.y);
for (int32 i = 1; i < count; i++) {
path.curve4(points[i - 1].point_out.x, points[i - 1].point_out.y,
points[i].point_in.x, points[i].point_in.y,
points[i].point.x, points[i].point.y);
}
if (closed) {
// curve from last to first control point
path.curve4(
points[count - 1].point_out.x, points[count - 1].point_out.y,
points[0].point_in.x, points[0].point_in.y,
points[0].point.x, points[0].point.y);
path.close_polygon();
}
return true;
}
return false;
}
// #pragma mark -
#ifdef ICON_O_MATIC
PathListener::PathListener()
{
}
PathListener::~PathListener()
{
}
#endif
// #pragma mark -
VectorPath::VectorPath()
:
#ifdef ICON_O_MATIC
BArchivable(),
IconObject("<path>"),
fListeners(20),
#endif
fPath(NULL),
fClosed(false),
fPointCount(0),
fAllocCount(0),
fCachedBounds(0.0, 0.0, -1.0, -1.0)
{
}
VectorPath::VectorPath(const VectorPath& from)
:
#ifdef ICON_O_MATIC
BArchivable(),
IconObject(from),
fListeners(20),
#endif
fPath(NULL),
fClosed(false),
fPointCount(0),
fAllocCount(0),
fCachedBounds(0.0, 0.0, -1.0, -1.0)
{
*this = from;
}
VectorPath::VectorPath(BMessage* archive)
:
#ifdef ICON_O_MATIC
BArchivable(),
IconObject(archive),
fListeners(20),
#endif
fPath(NULL),
fClosed(false),
fPointCount(0),
fAllocCount(0),
fCachedBounds(0.0, 0.0, -1.0, -1.0)
{
if (!archive)
return;
type_code typeFound;
int32 countFound;
if (archive->GetInfo("point", &typeFound, &countFound) >= B_OK
&& typeFound == B_POINT_TYPE
&& _SetPointCount(countFound)) {
memset((void*)fPath, 0, fAllocCount * sizeof(control_point));
BPoint point;
BPoint pointIn;
BPoint pointOut;
bool connected;
for (int32 i = 0; i < fPointCount
&& archive->FindPoint("point", i, &point) >= B_OK
&& archive->FindPoint("point in", i, &pointIn) >= B_OK
&& archive->FindPoint("point out", i, &pointOut) >= B_OK
&& archive->FindBool("connected", i, &connected) >= B_OK; i++) {
fPath[i].point = point;
fPath[i].point_in = pointIn;
fPath[i].point_out = pointOut;
fPath[i].connected = connected;
}
}
if (archive->FindBool("path closed", &fClosed) < B_OK)
fClosed = false;
}
VectorPath::~VectorPath()
{
if (fPath)
obj_free(fPath);
#ifdef ICON_O_MATIC
if (fListeners.CountItems() > 0) {
PathListener* listener = (PathListener*)fListeners.ItemAt(0);
char message[512];
sprintf(message, "VectorPath::~VectorPath() - "
"there are still listeners attached! %p/%s",
listener, typeid(*listener).name());
debugger(message);
}
#endif
}
// #pragma mark -
#ifdef ICON_O_MATIC
PropertyObject*
VectorPath::MakePropertyObject() const
{
PropertyObject* object = IconObject::MakePropertyObject();
if (!object)
return NULL;
// closed
object->AddProperty(new BoolProperty(PROPERTY_CLOSED, fClosed));
// archived path
BMessage* archive = new BMessage();
if (Archive(archive) == B_OK) {
object->AddProperty(new IconProperty(PROPERTY_PATH,
kPathPropertyIconBits, kPathPropertyIconWidth,
kPathPropertyIconHeight, kPathPropertyIconFormat, archive));
}
return object;
}
bool
VectorPath::SetToPropertyObject(const PropertyObject* object)
{
AutoNotificationSuspender _(this);
IconObject::SetToPropertyObject(object);
SetClosed(object->Value(PROPERTY_CLOSED, fClosed));
IconProperty* pathProperty = dynamic_cast<IconProperty*>(
object->FindProperty(PROPERTY_PATH));
if (pathProperty && pathProperty->Message()) {
VectorPath archivedPath(pathProperty->Message());
*this = archivedPath;
}
return HasPendingNotifications();
}
status_t
VectorPath::Archive(BMessage* into, bool deep) const
{
status_t ret = IconObject::Archive(into, deep);
if (ret != B_OK)
return ret;
if (fPointCount > 0) {
// improve BMessage efficency by preallocating storage for all points
// with the first call
ret = into->AddData("point", B_POINT_TYPE, &fPath[0].point,
sizeof(BPoint), true, fPointCount);
if (ret >= B_OK) {
ret = into->AddData("point in", B_POINT_TYPE, &fPath[0].point_in,
sizeof(BPoint), true, fPointCount);
}
if (ret >= B_OK) {
ret = into->AddData("point out", B_POINT_TYPE, &fPath[0].point_out,
sizeof(BPoint), true, fPointCount);
}
if (ret >= B_OK) {
ret = into->AddData("connected", B_BOOL_TYPE, &fPath[0].connected,
sizeof(bool), true, fPointCount);
}
// add the rest of the points
for (int32 i = 1; i < fPointCount && ret >= B_OK; i++) {
ret = into->AddData("point", B_POINT_TYPE, &fPath[i].point,
sizeof(BPoint));
if (ret >= B_OK) {
ret = into->AddData("point in", B_POINT_TYPE, &fPath[i].point_in,
sizeof(BPoint));
}
if (ret >= B_OK) {
ret = into->AddData("point out", B_POINT_TYPE,
&fPath[i].point_out, sizeof(BPoint));
}
if (ret >= B_OK) {
ret = into->AddData("connected", B_BOOL_TYPE,
&fPath[i].connected, sizeof(bool));
}
}
}
if (ret >= B_OK)
ret = into->AddBool("path closed", fClosed);
else
fprintf(stderr, "failed adding points!\n");
if (ret < B_OK)
fprintf(stderr, "failed adding close!\n");
// finish off
if (ret < B_OK)
ret = into->AddString("class", "VectorPath");
return ret;
}
#endif // ICON_O_MATIC
// #pragma mark -
VectorPath&
VectorPath::operator=(const VectorPath& from)
{
_SetPointCount(from.fPointCount);
fClosed = from.fClosed;
if (fPath) {
memcpy((void*)fPath, from.fPath, fPointCount * sizeof(control_point));
fCachedBounds = from.fCachedBounds;
} else {
fprintf(stderr, "VectorPath() -> allocation failed in operator=!\n");
fAllocCount = 0;
fPointCount = 0;
fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
}
Notify();
return *this;
}
bool
VectorPath::operator==(const VectorPath& other) const
{
if (fClosed != other.fClosed)
return false;
if (fPointCount != other.fPointCount)
return false;
if (fPath == NULL && other.fPath == NULL)
return true;
if (fPath == NULL || other.fPath == NULL)
return false;
for (int32 i = 0; i < fPointCount; i++) {
if (fPath[i].point != other.fPath[i].point
|| fPath[i].point_in != other.fPath[i].point_in
|| fPath[i].point_out != other.fPath[i].point_out
|| fPath[i].connected != other.fPath[i].connected) {
return false;
}
}
return true;
}
void
VectorPath::MakeEmpty()
{
_SetPointCount(0);
}
// #pragma mark -
bool
VectorPath::AddPoint(BPoint point)
{
int32 index = fPointCount;
if (_SetPointCount(fPointCount + 1)) {
_SetPoint(index, point);
_NotifyPointAdded(index);
return true;
}
return false;
}
bool
VectorPath::AddPoint(const BPoint& point, const BPoint& pointIn,
const BPoint& pointOut, bool connected)
{
int32 index = fPointCount;
if (_SetPointCount(fPointCount + 1)) {
_SetPoint(index, point, pointIn, pointOut, connected);
_NotifyPointAdded(index);
return true;
}
return false;
}
bool
VectorPath::AddPoint(BPoint point, int32 index)
{
if (index < 0)
index = 0;
if (index > fPointCount)
index = fPointCount;
if (_SetPointCount(fPointCount + 1)) {
// handle insert
if (index < fPointCount - 1) {
for (int32 i = fPointCount; i > index; i--) {
fPath[i].point = fPath[i - 1].point;
fPath[i].point_in = fPath[i - 1].point_in;
fPath[i].point_out = fPath[i - 1].point_out;
fPath[i].connected = fPath[i - 1].connected;
}
}
_SetPoint(index, point);
_NotifyPointAdded(index);
return true;
}
return false;
}
bool
VectorPath::RemovePoint(int32 index)
{
if (index >= 0 && index < fPointCount) {
if (index < fPointCount - 1) {
// move points
for (int32 i = index; i < fPointCount - 1; i++) {
fPath[i].point = fPath[i + 1].point;
fPath[i].point_in = fPath[i + 1].point_in;
fPath[i].point_out = fPath[i + 1].point_out;
fPath[i].connected = fPath[i + 1].connected;
}
}
fPointCount -= 1;
fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
_NotifyPointRemoved(index);
return true;
}
return false;
}
bool
VectorPath::SetPoint(int32 index, BPoint point)
{
if (index == fPointCount)
index = 0;
if (index >= 0 && index < fPointCount) {
BPoint offset = point - fPath[index].point;
fPath[index].point = point;
fPath[index].point_in += offset;
fPath[index].point_out += offset;
fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
_NotifyPointChanged(index);
return true;
}
return false;
}
bool
VectorPath::SetPoint(int32 index, BPoint point, BPoint pointIn, BPoint pointOut,
bool connected)
{
if (index == fPointCount)
index = 0;
if (index >= 0 && index < fPointCount) {
fPath[index].point = point;
fPath[index].point_in = pointIn;
fPath[index].point_out = pointOut;
fPath[index].connected = connected;
fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
_NotifyPointChanged(index);
return true;
}
return false;
}
bool
VectorPath::SetPointIn(int32 i, BPoint point)
{
if (i == fPointCount)
i = 0;
if (i >= 0 && i < fPointCount) {
// first, set the "in" point
fPath[i].point_in = point;
// now see what to do about the "out" point
if (fPath[i].connected) {
// keep all three points in one line
BPoint v = fPath[i].point - fPath[i].point_in;
float distIn = sqrtf(v.x * v.x + v.y * v.y);
if (distIn > 0.0) {
float distOut = agg::calc_distance(
fPath[i].point.x, fPath[i].point.y,
fPath[i].point_out.x, fPath[i].point_out.y);
float scale = (distIn + distOut) / distIn;
v.x *= scale;
v.y *= scale;
fPath[i].point_out = fPath[i].point_in + v;
}
}
fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
_NotifyPointChanged(i);
return true;
}
return false;
}
bool
VectorPath::SetPointOut(int32 i, BPoint point, bool mirrorDist)
{
if (i == fPointCount)
i = 0;
if (i >= 0 && i < fPointCount) {
// first, set the "out" point
fPath[i].point_out = point;
// now see what to do about the "out" point
if (mirrorDist) {
// mirror "in" point around main control point
BPoint v = fPath[i].point - fPath[i].point_out;
fPath[i].point_in = fPath[i].point + v;
} else if (fPath[i].connected) {
// keep all three points in one line
BPoint v = fPath[i].point - fPath[i].point_out;
float distOut = sqrtf(v.x * v.x + v.y * v.y);
if (distOut > 0.0) {
float distIn = agg::calc_distance(
fPath[i].point.x, fPath[i].point.y,
fPath[i].point_in.x, fPath[i].point_in.y);
float scale = (distIn + distOut) / distOut;
v.x *= scale;
v.y *= scale;
fPath[i].point_in = fPath[i].point_out + v;
}
}
fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
_NotifyPointChanged(i);
return true;
}
return false;
}
bool
VectorPath::SetInOutConnected(int32 index, bool connected)
{
if (index >= 0 && index < fPointCount) {
fPath[index].connected = connected;
_NotifyPointChanged(index);
return true;
}
return false;
}
// #pragma mark -
bool
VectorPath::GetPointAt(int32 index, BPoint& point) const
{
if (index == fPointCount)
index = 0;
if (index >= 0 && index < fPointCount) {
point = fPath[index].point;
return true;
}
return false;
}
bool
VectorPath::GetPointInAt(int32 index, BPoint& point) const
{
if (index == fPointCount)
index = 0;
if (index >= 0 && index < fPointCount) {
point = fPath[index].point_in;
return true;
}
return false;
}
bool
VectorPath::GetPointOutAt(int32 index, BPoint& point) const
{
if (index == fPointCount)
index = 0;
if (index >= 0 && index < fPointCount) {
point = fPath[index].point_out;
return true;
}
return false;
}
bool
VectorPath::GetPointsAt(int32 index, BPoint& point, BPoint& pointIn,
BPoint& pointOut, bool* connected) const
{
if (index >= 0 && index < fPointCount) {
point = fPath[index].point;
pointIn = fPath[index].point_in;
pointOut = fPath[index].point_out;
if (connected)
*connected = fPath[index].connected;
return true;
}
return false;
}
int32
VectorPath::CountPoints() const
{
return fPointCount;
}
// #pragma mark -
#ifdef ICON_O_MATIC
static float
distance_to_curve(const BPoint& p, const BPoint& a, const BPoint& aOut,
const BPoint& bIn, const BPoint& b)
{
agg::curve4_inc curve(a.x, a.y, aOut.x, aOut.y, bIn.x, bIn.y, b.x, b.y);
float segDist = FLT_MAX;
double x1, y1, x2, y2;
unsigned cmd = curve.vertex(&x1, &y1);
while (!agg::is_stop(cmd)) {
cmd = curve.vertex(&x2, &y2);
// first figure out if point is between segment start and end points
double a = agg::calc_distance(p.x, p.y, x2, y2);
double b = agg::calc_distance(p.x, p.y, x1, y1);
float currentDist = min_c(a, b);
if (a > 0.0 && b > 0.0) {
double c = agg::calc_distance(x1, y1, x2, y2);
double alpha = acos((b * b + c * c - a * a) / (2 * b * c));
double beta = acos((a * a + c * c - b * b) / (2 * a * c));
if (alpha <= M_PI_2 && beta <= M_PI_2) {
currentDist = fabs(agg::calc_line_point_distance(x1, y1, x2, y2,
p.x, p.y));
}
}
if (currentDist < segDist)
segDist = currentDist;
x1 = x2;
y1 = y2;
}
return segDist;
}
bool
VectorPath::GetDistance(BPoint p, float* distance, int32* index) const
{
if (fPointCount > 1) {
// generate a curve for each segment of the path
// then iterate over the segments of the curve measuring the distance
*distance = FLT_MAX;
for (int32 i = 0; i < fPointCount - 1; i++) {
float segDist = distance_to_curve(p, fPath[i].point,
fPath[i].point_out, fPath[i + 1].point_in, fPath[i + 1].point);
if (segDist < *distance) {
*distance = segDist;
*index = i + 1;
}
}
if (fClosed) {
float segDist = distance_to_curve(p, fPath[fPointCount - 1].point,
fPath[fPointCount - 1].point_out, fPath[0].point_in,
fPath[0].point);
if (segDist < *distance) {
*distance = segDist;
*index = fPointCount;
}
}
return true;
}
return false;
}
bool
VectorPath::FindBezierScale(int32 index, BPoint point, double* scale) const
{
if (index >= 0 && index < fPointCount && scale) {
int maxStep = 1000;
double t = 0.0;
double dt = 1.0 / maxStep;
*scale = 0.0;
double min = FLT_MAX;
BPoint curvePoint;
for (int step = 1; step < maxStep; step++) {
t += dt;
GetPoint(index, t, curvePoint);
double d = agg::calc_distance(curvePoint.x, curvePoint.y,
point.x, point.y);
if (d < min) {
min = d;
*scale = t;
}
}
return true;
}
return false;
}
bool
VectorPath::GetPoint(int32 index, double t, BPoint& point) const
{
if (index >= 0 && index < fPointCount) {
double t1 = (1 - t) * (1 - t) * (1 - t);
double t2 = (1 - t) * (1 - t) * t * 3;
double t3 = (1 - t) * t * t * 3;
double t4 = t * t * t;
if (index < fPointCount - 1) {
point.x = fPath[index].point.x * t1 + fPath[index].point_out.x * t2
+ fPath[index + 1].point_in.x * t3
+ fPath[index + 1].point.x * t4;
point.y = fPath[index].point.y * t1 + fPath[index].point_out.y * t2
+ fPath[index + 1].point_in.y * t3
+ fPath[index + 1].point.y * t4;
} else if (fClosed) {
point.x = fPath[fPointCount - 1].point.x * t1
+ fPath[fPointCount - 1].point_out.x * t2
+ fPath[0].point_in.x * t3 + fPath[0].point.x * t4;
point.y = fPath[fPointCount - 1].point.y * t1
+ fPath[fPointCount - 1].point_out.y * t2
+ fPath[0].point_in.y * t3 + fPath[0].point.y * t4;
}
return true;
}
return false;
}
#endif // ICON_O_MATIC
void
VectorPath::SetClosed(bool closed)
{
if (fClosed != closed) {
fClosed = closed;
_NotifyClosedChanged();
Notify();
}
}
BRect
VectorPath::Bounds() const
{
// the bounds of the actual curves, not the control points!
if (!fCachedBounds.IsValid())
fCachedBounds = _Bounds();
return fCachedBounds;
}
BRect
VectorPath::_Bounds() const
{
agg::path_storage path;
BRect b;
if (get_path_storage(path, fPath, fPointCount, fClosed)) {
agg::conv_curve<agg::path_storage> curve(path);
uint32 pathID[1];
pathID[0] = 0;
double left, top, right, bottom;
agg::bounding_rect(curve, pathID, 0, 1, &left, &top, &right, &bottom);
b.Set(left, top, right, bottom);
} else if (fPointCount == 1) {
b.Set(fPath[0].point.x, fPath[0].point.y, fPath[0].point.x,
fPath[0].point.y);
} else {
b.Set(0.0, 0.0, -1.0, -1.0);
}
return b;
}
BRect
VectorPath::ControlPointBounds() const
{
if (fPointCount > 0) {
BRect r(fPath[0].point, fPath[0].point);
for (int32 i = 0; i < fPointCount; i++) {
// include point
r.left = min_c(r.left, fPath[i].point.x);
r.top = min_c(r.top, fPath[i].point.y);
r.right = max_c(r.right, fPath[i].point.x);
r.bottom = max_c(r.bottom, fPath[i].point.y);
// include "in" point
r.left = min_c(r.left, fPath[i].point_in.x);
r.top = min_c(r.top, fPath[i].point_in.y);
r.right = max_c(r.right, fPath[i].point_in.x);
r.bottom = max_c(r.bottom, fPath[i].point_in.y);
// include "out" point
r.left = min_c(r.left, fPath[i].point_out.x);
r.top = min_c(r.top, fPath[i].point_out.y);
r.right = max_c(r.right, fPath[i].point_out.x);
r.bottom = max_c(r.bottom, fPath[i].point_out.y);
}
return r;
}
return BRect(0.0, 0.0, -1.0, -1.0);
}
void
VectorPath::Iterate(Iterator* iterator, float smoothScale) const
{
if (fPointCount > 1) {
// generate a curve for each segment of the path
// then iterate over the segments of the curve
agg::curve4_inc curve;
curve.approximation_scale(smoothScale);
for (int32 i = 0; i < fPointCount - 1; i++) {
iterator->MoveTo(fPath[i].point);
curve.init(fPath[i].point.x, fPath[i].point.y,
fPath[i].point_out.x, fPath[i].point_out.y,
fPath[i + 1].point_in.x, fPath[i + 1].point_in.y,
fPath[i + 1].point.x, fPath[i + 1].point.y);
double x, y;
unsigned cmd = curve.vertex(&x, &y);
while (!agg::is_stop(cmd)) {
BPoint p(x, y);
iterator->LineTo(p);
cmd = curve.vertex(&x, &y);
}
}
if (fClosed) {
iterator->MoveTo(fPath[fPointCount - 1].point);
curve.init(fPath[fPointCount - 1].point.x,
fPath[fPointCount - 1].point.y,
fPath[fPointCount - 1].point_out.x,
fPath[fPointCount - 1].point_out.y,
fPath[0].point_in.x, fPath[0].point_in.y,
fPath[0].point.x, fPath[0].point.y);
double x, y;
unsigned cmd = curve.vertex(&x, &y);
while (!agg::is_stop(cmd)) {
BPoint p(x, y);
iterator->LineTo(p);
cmd = curve.vertex(&x, &y);
}
}
}
}
void
VectorPath::CleanUp()
{
if (fPointCount == 0)
return;
bool notify = false;
// remove last point if it is coincident with the first
if (fClosed && fPointCount >= 1) {
if (fPath[0].point == fPath[fPointCount - 1].point) {
fPath[0].point_in = fPath[fPointCount - 1].point_in;
_SetPointCount(fPointCount - 1);
notify = true;
}
}
for (int32 i = 0; i < fPointCount; i++) {
// check for unnecessary, duplicate points
if (i > 0) {
if (fPath[i - 1].point == fPath[i].point
&& fPath[i - 1].point == fPath[i - 1].point_out
&& fPath[i].point == fPath[i].point_in) {
// the previous point can be removed
BPoint in = fPath[i - 1].point_in;
if (RemovePoint(i - 1)) {
i--;
fPath[i].point_in = in;
notify = true;
}
}
}
// re-establish connections of in-out control points if
// they line up with the main control point
if (fPath[i].point_in == fPath[i].point_out
|| fPath[i].point == fPath[i].point_out
|| fPath[i].point == fPath[i].point_in
|| (fabs(agg::calc_line_point_distance(fPath[i].point_in.x,
fPath[i].point_in.y, fPath[i].point.x, fPath[i].point.y,
fPath[i].point_out.x, fPath[i].point_out.y)) < 0.01
&& fabs(agg::calc_line_point_distance(fPath[i].point_out.x,
fPath[i].point_out.y, fPath[i].point.x, fPath[i].point.y,
fPath[i].point_in.x, fPath[i].point_in.y)) < 0.01)) {
fPath[i].connected = true;
notify = true;
}
}
if (notify)
_NotifyPathChanged();
}
void
VectorPath::Reverse()
{
VectorPath temp(*this);
int32 index = 0;
for (int32 i = fPointCount - 1; i >= 0; i--) {
temp.SetPoint(index, fPath[i].point, fPath[i].point_out,
fPath[i].point_in, fPath[i].connected);
index++;
}
*this = temp;
_NotifyPathReversed();
}
void
VectorPath::ApplyTransform(const Transformable& transform)
{
if (transform.IsIdentity())
return;
for (int32 i = 0; i < fPointCount; i++) {
transform.Transform(&(fPath[i].point));
transform.Transform(&(fPath[i].point_out));
transform.Transform(&(fPath[i].point_in));
}
_NotifyPathChanged();
}
void
VectorPath::PrintToStream() const
{
for (int32 i = 0; i < fPointCount; i++) {
printf("point %" B_PRId32 ": (%f, %f) -> (%f, %f) -> (%f, %f) (%d)\n", i,
fPath[i].point_in.x, fPath[i].point_in.y,
fPath[i].point.x, fPath[i].point.y,
fPath[i].point_out.x, fPath[i].point_out.y, fPath[i].connected);
}
}
bool
VectorPath::GetAGGPathStorage(agg::path_storage& path) const
{
return get_path_storage(path, fPath, fPointCount, fClosed);
}
// #pragma mark -
#ifdef ICON_O_MATIC
bool
VectorPath::AddListener(PathListener* listener)
{
if (listener && !fListeners.HasItem((void*)listener))
return fListeners.AddItem((void*)listener);
return false;
}
bool
VectorPath::RemoveListener(PathListener* listener)
{
return fListeners.RemoveItem((void*)listener);
}
int32
VectorPath::CountListeners() const
{
return fListeners.CountItems();
}
PathListener*
VectorPath::ListenerAtFast(int32 index) const
{
return (PathListener*)fListeners.ItemAtFast(index);
}
#endif // ICON_O_MATIC
// #pragma mark -
void
VectorPath::_SetPoint(int32 index, BPoint point)
{
fPath[index].point = point;
fPath[index].point_in = point;
fPath[index].point_out = point;
fPath[index].connected = true;
fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
}
void
VectorPath::_SetPoint(int32 index, const BPoint& point, const BPoint& pointIn,
const BPoint& pointOut, bool connected)
{
fPath[index].point = point;
fPath[index].point_in = pointIn;
fPath[index].point_out = pointOut;
fPath[index].connected = connected;
fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
}
// #pragma mark -
bool
VectorPath::_SetPointCount(int32 count)
{
// handle reallocation if we run out of room
if (count >= fAllocCount) {
fAllocCount = ((count) / ALLOC_CHUNKS + 1) * ALLOC_CHUNKS;
if (fPath)
fPath = obj_renew(fPath, control_point, fAllocCount);
else
fPath = obj_new(control_point, fAllocCount);
if (fPath != NULL) {
memset((void*)(fPath + fPointCount), 0,
(fAllocCount - fPointCount) * sizeof(control_point));
}
}
// update point count
if (fPath) {
fPointCount = count;
} else {
// reallocation might have failed
fPointCount = 0;
fAllocCount = 0;
fprintf(stderr, "VectorPath::_SetPointCount(%" B_PRId32 ") - allocation failed!\n",
count);
}
fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
return fPath != NULL;
}
// #pragma mark -
#ifdef ICON_O_MATIC
void
VectorPath::_NotifyPointAdded(int32 index) const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
PathListener* listener = (PathListener*)listeners.ItemAtFast(i);
listener->PointAdded(index);
}
}
void
VectorPath::_NotifyPointChanged(int32 index) const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
PathListener* listener = (PathListener*)listeners.ItemAtFast(i);
listener->PointChanged(index);
}
}
void
VectorPath::_NotifyPointRemoved(int32 index) const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
PathListener* listener = (PathListener*)listeners.ItemAtFast(i);
listener->PointRemoved(index);
}
}
void
VectorPath::_NotifyPathChanged() const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
PathListener* listener = (PathListener*)listeners.ItemAtFast(i);
listener->PathChanged();
}
}
void
VectorPath::_NotifyClosedChanged() const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
PathListener* listener = (PathListener*)listeners.ItemAtFast(i);
listener->PathClosedChanged();
}
}
void
VectorPath::_NotifyPathReversed() const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
PathListener* listener = (PathListener*)listeners.ItemAtFast(i);
listener->PathReversed();
}
}
#endif // ICON_O_MATIC
↑ V630 The 'malloc' function is used to allocate memory for an array of objects which are classes containing constructors.
↑ V630 The 'realloc' function is used to allocate memory for an array of objects which are classes containing constructors.