vfs: check userland buffers before reading them.
[haiku.git] / src / libs / icon / shape / VectorPath.cpp
blob4e4f7ba560d5363c84b0651c7a537a153b23d578
1 /*
2 * Copyright 2006-2012, Haiku.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Stephan Aßmus <superstippi@gmx.de>
7 */
9 #include "VectorPath.h"
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
15 #include <agg_basics.h>
16 #include <agg_bounding_rect.h>
17 #include <agg_conv_curve.h>
18 #include <agg_curves.h>
19 #include <agg_math.h>
21 #ifdef ICON_O_MATIC
22 # include <debugger.h>
23 # include <typeinfo>
24 #endif // ICON_O_MATIC
26 #include <Message.h>
27 #include <TypeConstants.h>
29 #ifdef ICON_O_MATIC
30 # include "support.h"
32 # include "CommonPropertyIDs.h"
33 # include "IconProperty.h"
34 # include "Icons.h"
35 # include "Property.h"
36 # include "PropertyObject.h"
37 #endif // ICON_O_MATIC
39 #include "Transformable.h"
42 #define obj_new(type, n) ((type *)malloc ((n) * sizeof(type)))
43 #define obj_renew(p, type, n) ((type *)realloc (p, (n) * sizeof(type)))
44 #define obj_free free
46 #define ALLOC_CHUNKS 20
49 bool
50 get_path_storage(agg::path_storage& path, const control_point* points,
51 int32 count, bool closed)
53 if (count > 1) {
54 path.move_to(points[0].point.x, points[0].point.y);
56 for (int32 i = 1; i < count; i++) {
57 path.curve4(points[i - 1].point_out.x, points[i - 1].point_out.y,
58 points[i].point_in.x, points[i].point_in.y,
59 points[i].point.x, points[i].point.y);
61 if (closed) {
62 // curve from last to first control point
63 path.curve4(
64 points[count - 1].point_out.x, points[count - 1].point_out.y,
65 points[0].point_in.x, points[0].point_in.y,
66 points[0].point.x, points[0].point.y);
67 path.close_polygon();
70 return true;
72 return false;
76 // #pragma mark -
79 #ifdef ICON_O_MATIC
80 PathListener::PathListener()
85 PathListener::~PathListener()
88 #endif
91 // #pragma mark -
94 VectorPath::VectorPath()
96 #ifdef ICON_O_MATIC
97 BArchivable(),
98 IconObject("<path>"),
99 fListeners(20),
100 #endif
101 fPath(NULL),
102 fClosed(false),
103 fPointCount(0),
104 fAllocCount(0),
105 fCachedBounds(0.0, 0.0, -1.0, -1.0)
110 VectorPath::VectorPath(const VectorPath& from)
112 #ifdef ICON_O_MATIC
113 BArchivable(),
114 IconObject(from),
115 fListeners(20),
116 #endif
117 fPath(NULL),
118 fClosed(false),
119 fPointCount(0),
120 fAllocCount(0),
121 fCachedBounds(0.0, 0.0, -1.0, -1.0)
123 *this = from;
127 VectorPath::VectorPath(BMessage* archive)
129 #ifdef ICON_O_MATIC
130 BArchivable(),
131 IconObject(archive),
132 fListeners(20),
133 #endif
134 fPath(NULL),
135 fClosed(false),
136 fPointCount(0),
137 fAllocCount(0),
138 fCachedBounds(0.0, 0.0, -1.0, -1.0)
140 if (!archive)
141 return;
143 type_code typeFound;
144 int32 countFound;
145 if (archive->GetInfo("point", &typeFound, &countFound) >= B_OK
146 && typeFound == B_POINT_TYPE
147 && _SetPointCount(countFound)) {
148 memset(fPath, 0, fAllocCount * sizeof(control_point));
150 BPoint point;
151 BPoint pointIn;
152 BPoint pointOut;
153 bool connected;
154 for (int32 i = 0; i < fPointCount
155 && archive->FindPoint("point", i, &point) >= B_OK
156 && archive->FindPoint("point in", i, &pointIn) >= B_OK
157 && archive->FindPoint("point out", i, &pointOut) >= B_OK
158 && archive->FindBool("connected", i, &connected) >= B_OK; i++) {
159 fPath[i].point = point;
160 fPath[i].point_in = pointIn;
161 fPath[i].point_out = pointOut;
162 fPath[i].connected = connected;
165 if (archive->FindBool("path closed", &fClosed) < B_OK)
166 fClosed = false;
171 VectorPath::~VectorPath()
173 if (fPath)
174 obj_free(fPath);
176 #ifdef ICON_O_MATIC
177 if (fListeners.CountItems() > 0) {
178 PathListener* listener = (PathListener*)fListeners.ItemAt(0);
179 char message[512];
180 sprintf(message, "VectorPath::~VectorPath() - "
181 "there are still listeners attached! %p/%s",
182 listener, typeid(*listener).name());
183 debugger(message);
185 #endif
189 // #pragma mark -
192 #ifdef ICON_O_MATIC
194 PropertyObject*
195 VectorPath::MakePropertyObject() const
197 PropertyObject* object = IconObject::MakePropertyObject();
198 if (!object)
199 return NULL;
201 // closed
202 object->AddProperty(new BoolProperty(PROPERTY_CLOSED, fClosed));
204 // archived path
205 BMessage* archive = new BMessage();
206 if (Archive(archive) == B_OK) {
207 object->AddProperty(new IconProperty(PROPERTY_PATH,
208 kPathPropertyIconBits, kPathPropertyIconWidth,
209 kPathPropertyIconHeight, kPathPropertyIconFormat, archive));
212 return object;
216 bool
217 VectorPath::SetToPropertyObject(const PropertyObject* object)
219 AutoNotificationSuspender _(this);
220 IconObject::SetToPropertyObject(object);
222 SetClosed(object->Value(PROPERTY_CLOSED, fClosed));
224 IconProperty* pathProperty = dynamic_cast<IconProperty*>(
225 object->FindProperty(PROPERTY_PATH));
226 if (pathProperty && pathProperty->Message()) {
227 VectorPath archivedPath(pathProperty->Message());
228 *this = archivedPath;
231 return HasPendingNotifications();
235 status_t
236 VectorPath::Archive(BMessage* into, bool deep) const
238 status_t ret = IconObject::Archive(into, deep);
239 if (ret != B_OK)
240 return ret;
242 if (fPointCount > 0) {
243 // improve BMessage efficency by preallocating storage for all points
244 // with the first call
245 ret = into->AddData("point", B_POINT_TYPE, &fPath[0].point,
246 sizeof(BPoint), true, fPointCount);
247 if (ret >= B_OK) {
248 ret = into->AddData("point in", B_POINT_TYPE, &fPath[0].point_in,
249 sizeof(BPoint), true, fPointCount);
251 if (ret >= B_OK) {
252 ret = into->AddData("point out", B_POINT_TYPE, &fPath[0].point_out,
253 sizeof(BPoint), true, fPointCount);
255 if (ret >= B_OK) {
256 ret = into->AddData("connected", B_BOOL_TYPE, &fPath[0].connected,
257 sizeof(bool), true, fPointCount);
260 // add the rest of the points
261 for (int32 i = 1; i < fPointCount && ret >= B_OK; i++) {
262 ret = into->AddData("point", B_POINT_TYPE, &fPath[i].point,
263 sizeof(BPoint));
264 if (ret >= B_OK) {
265 ret = into->AddData("point in", B_POINT_TYPE, &fPath[i].point_in,
266 sizeof(BPoint));
268 if (ret >= B_OK) {
269 ret = into->AddData("point out", B_POINT_TYPE,
270 &fPath[i].point_out, sizeof(BPoint));
272 if (ret >= B_OK) {
273 ret = into->AddData("connected", B_BOOL_TYPE,
274 &fPath[i].connected, sizeof(bool));
279 if (ret >= B_OK)
280 ret = into->AddBool("path closed", fClosed);
281 else
282 fprintf(stderr, "failed adding points!\n");
284 if (ret < B_OK)
285 fprintf(stderr, "failed adding close!\n");
287 // finish off
288 if (ret < B_OK)
289 ret = into->AddString("class", "VectorPath");
291 return ret;
294 #endif // ICON_O_MATIC
297 // #pragma mark -
300 VectorPath&
301 VectorPath::operator=(const VectorPath& from)
303 _SetPointCount(from.fPointCount);
304 fClosed = from.fClosed;
305 if (fPath) {
306 memcpy(fPath, from.fPath, fPointCount * sizeof(control_point));
307 fCachedBounds = from.fCachedBounds;
308 } else {
309 fprintf(stderr, "VectorPath() -> allocation failed in operator=!\n");
310 fAllocCount = 0;
311 fPointCount = 0;
312 fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
314 Notify();
316 return *this;
320 bool
321 VectorPath::operator==(const VectorPath& other) const
323 if (fClosed != other.fClosed)
324 return false;
326 if (fPointCount != other.fPointCount)
327 return false;
329 if (fPath == NULL && other.fPath == NULL)
330 return true;
332 if (fPath == NULL || other.fPath == NULL)
333 return false;
335 for (int32 i = 0; i < fPointCount; i++) {
336 if (fPath[i].point != other.fPath[i].point
337 || fPath[i].point_in != other.fPath[i].point_in
338 || fPath[i].point_out != other.fPath[i].point_out
339 || fPath[i].connected != other.fPath[i].connected) {
340 return false;
344 return true;
348 void
349 VectorPath::MakeEmpty()
351 _SetPointCount(0);
355 // #pragma mark -
358 bool
359 VectorPath::AddPoint(BPoint point)
361 int32 index = fPointCount;
363 if (_SetPointCount(fPointCount + 1)) {
364 _SetPoint(index, point);
365 _NotifyPointAdded(index);
366 return true;
369 return false;
373 bool
374 VectorPath::AddPoint(const BPoint& point, const BPoint& pointIn,
375 const BPoint& pointOut, bool connected)
377 int32 index = fPointCount;
379 if (_SetPointCount(fPointCount + 1)) {
380 _SetPoint(index, point, pointIn, pointOut, connected);
381 _NotifyPointAdded(index);
382 return true;
385 return false;
389 bool
390 VectorPath::AddPoint(BPoint point, int32 index)
392 if (index < 0)
393 index = 0;
394 if (index > fPointCount)
395 index = fPointCount;
397 if (_SetPointCount(fPointCount + 1)) {
398 // handle insert
399 if (index < fPointCount - 1) {
400 for (int32 i = fPointCount; i > index; i--) {
401 fPath[i].point = fPath[i - 1].point;
402 fPath[i].point_in = fPath[i - 1].point_in;
403 fPath[i].point_out = fPath[i - 1].point_out;
404 fPath[i].connected = fPath[i - 1].connected;
407 _SetPoint(index, point);
408 _NotifyPointAdded(index);
409 return true;
411 return false;
415 bool
416 VectorPath::RemovePoint(int32 index)
418 if (index >= 0 && index < fPointCount) {
419 if (index < fPointCount - 1) {
420 // move points
421 for (int32 i = index; i < fPointCount - 1; i++) {
422 fPath[i].point = fPath[i + 1].point;
423 fPath[i].point_in = fPath[i + 1].point_in;
424 fPath[i].point_out = fPath[i + 1].point_out;
425 fPath[i].connected = fPath[i + 1].connected;
428 fPointCount -= 1;
430 fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
432 _NotifyPointRemoved(index);
433 return true;
435 return false;
439 bool
440 VectorPath::SetPoint(int32 index, BPoint point)
442 if (index == fPointCount)
443 index = 0;
444 if (index >= 0 && index < fPointCount) {
445 BPoint offset = point - fPath[index].point;
446 fPath[index].point = point;
447 fPath[index].point_in += offset;
448 fPath[index].point_out += offset;
450 fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
452 _NotifyPointChanged(index);
453 return true;
455 return false;
459 bool
460 VectorPath::SetPoint(int32 index, BPoint point, BPoint pointIn, BPoint pointOut,
461 bool connected)
463 if (index == fPointCount)
464 index = 0;
465 if (index >= 0 && index < fPointCount) {
466 fPath[index].point = point;
467 fPath[index].point_in = pointIn;
468 fPath[index].point_out = pointOut;
469 fPath[index].connected = connected;
471 fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
473 _NotifyPointChanged(index);
474 return true;
476 return false;
480 bool
481 VectorPath::SetPointIn(int32 i, BPoint point)
483 if (i == fPointCount)
484 i = 0;
485 if (i >= 0 && i < fPointCount) {
486 // first, set the "in" point
487 fPath[i].point_in = point;
488 // now see what to do about the "out" point
489 if (fPath[i].connected) {
490 // keep all three points in one line
491 BPoint v = fPath[i].point - fPath[i].point_in;
492 float distIn = sqrtf(v.x * v.x + v.y * v.y);
493 if (distIn > 0.0) {
494 float distOut = agg::calc_distance(
495 fPath[i].point.x, fPath[i].point.y,
496 fPath[i].point_out.x, fPath[i].point_out.y);
497 float scale = (distIn + distOut) / distIn;
498 v.x *= scale;
499 v.y *= scale;
500 fPath[i].point_out = fPath[i].point_in + v;
504 fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
506 _NotifyPointChanged(i);
507 return true;
509 return false;
513 bool
514 VectorPath::SetPointOut(int32 i, BPoint point, bool mirrorDist)
516 if (i == fPointCount)
517 i = 0;
518 if (i >= 0 && i < fPointCount) {
519 // first, set the "out" point
520 fPath[i].point_out = point;
521 // now see what to do about the "out" point
522 if (mirrorDist) {
523 // mirror "in" point around main control point
524 BPoint v = fPath[i].point - fPath[i].point_out;
525 fPath[i].point_in = fPath[i].point + v;
526 } else if (fPath[i].connected) {
527 // keep all three points in one line
528 BPoint v = fPath[i].point - fPath[i].point_out;
529 float distOut = sqrtf(v.x * v.x + v.y * v.y);
530 if (distOut > 0.0) {
531 float distIn = agg::calc_distance(
532 fPath[i].point.x, fPath[i].point.y,
533 fPath[i].point_in.x, fPath[i].point_in.y);
534 float scale = (distIn + distOut) / distOut;
535 v.x *= scale;
536 v.y *= scale;
537 fPath[i].point_in = fPath[i].point_out + v;
541 fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
543 _NotifyPointChanged(i);
544 return true;
546 return false;
550 bool
551 VectorPath::SetInOutConnected(int32 index, bool connected)
553 if (index >= 0 && index < fPointCount) {
554 fPath[index].connected = connected;
555 _NotifyPointChanged(index);
556 return true;
558 return false;
562 // #pragma mark -
565 bool
566 VectorPath::GetPointAt(int32 index, BPoint& point) const
568 if (index == fPointCount)
569 index = 0;
570 if (index >= 0 && index < fPointCount) {
571 point = fPath[index].point;
572 return true;
574 return false;
578 bool
579 VectorPath::GetPointInAt(int32 index, BPoint& point) const
581 if (index == fPointCount)
582 index = 0;
583 if (index >= 0 && index < fPointCount) {
584 point = fPath[index].point_in;
585 return true;
587 return false;
591 bool
592 VectorPath::GetPointOutAt(int32 index, BPoint& point) const
594 if (index == fPointCount)
595 index = 0;
596 if (index >= 0 && index < fPointCount) {
597 point = fPath[index].point_out;
598 return true;
600 return false;
604 bool
605 VectorPath::GetPointsAt(int32 index, BPoint& point, BPoint& pointIn,
606 BPoint& pointOut, bool* connected) const
608 if (index >= 0 && index < fPointCount) {
609 point = fPath[index].point;
610 pointIn = fPath[index].point_in;
611 pointOut = fPath[index].point_out;
613 if (connected)
614 *connected = fPath[index].connected;
616 return true;
618 return false;
622 int32
623 VectorPath::CountPoints() const
625 return fPointCount;
629 // #pragma mark -
632 #ifdef ICON_O_MATIC
634 static float
635 distance_to_curve(const BPoint& p, const BPoint& a, const BPoint& aOut,
636 const BPoint& bIn, const BPoint& b)
638 agg::curve4_inc curve(a.x, a.y, aOut.x, aOut.y, bIn.x, bIn.y, b.x, b.y);
640 float segDist = FLT_MAX;
641 double x1, y1, x2, y2;
642 unsigned cmd = curve.vertex(&x1, &y1);
643 while (!agg::is_stop(cmd)) {
644 cmd = curve.vertex(&x2, &y2);
645 // first figure out if point is between segment start and end points
646 double a = agg::calc_distance(p.x, p.y, x2, y2);
647 double b = agg::calc_distance(p.x, p.y, x1, y1);
649 float currentDist = min_c(a, b);
651 if (a > 0.0 && b > 0.0) {
652 double c = agg::calc_distance(x1, y1, x2, y2);
654 double alpha = acos((b * b + c * c - a * a) / (2 * b * c));
655 double beta = acos((a * a + c * c - b * b) / (2 * a * c));
657 if (alpha <= M_PI_2 && beta <= M_PI_2) {
658 currentDist = fabs(agg::calc_line_point_distance(x1, y1, x2, y2,
659 p.x, p.y));
663 if (currentDist < segDist)
664 segDist = currentDist;
666 x1 = x2;
667 y1 = y2;
669 return segDist;
673 bool
674 VectorPath::GetDistance(BPoint p, float* distance, int32* index) const
676 if (fPointCount > 1) {
677 // generate a curve for each segment of the path
678 // then iterate over the segments of the curve measuring the distance
679 *distance = FLT_MAX;
681 for (int32 i = 0; i < fPointCount - 1; i++) {
682 float segDist = distance_to_curve(p, fPath[i].point,
683 fPath[i].point_out, fPath[i + 1].point_in, fPath[i + 1].point);
684 if (segDist < *distance) {
685 *distance = segDist;
686 *index = i + 1;
689 if (fClosed) {
690 float segDist = distance_to_curve(p, fPath[fPointCount - 1].point,
691 fPath[fPointCount - 1].point_out, fPath[0].point_in,
692 fPath[0].point);
693 if (segDist < *distance) {
694 *distance = segDist;
695 *index = fPointCount;
698 return true;
700 return false;
704 bool
705 VectorPath::FindBezierScale(int32 index, BPoint point, double* scale) const
707 if (index >= 0 && index < fPointCount && scale) {
708 int maxStep = 1000;
710 double t = 0.0;
711 double dt = 1.0 / maxStep;
713 *scale = 0.0;
714 double min = FLT_MAX;
716 BPoint curvePoint;
717 for (int step = 1; step < maxStep; step++) {
718 t += dt;
720 GetPoint(index, t, curvePoint);
721 double d = agg::calc_distance(curvePoint.x, curvePoint.y,
722 point.x, point.y);
724 if (d < min) {
725 min = d;
726 *scale = t;
729 return true;
731 return false;
735 bool
736 VectorPath::GetPoint(int32 index, double t, BPoint& point) const
738 if (index >= 0 && index < fPointCount) {
739 double t1 = (1 - t) * (1 - t) * (1 - t);
740 double t2 = (1 - t) * (1 - t) * t * 3;
741 double t3 = (1 - t) * t * t * 3;
742 double t4 = t * t * t;
744 if (index < fPointCount - 1) {
745 point.x = fPath[index].point.x * t1 + fPath[index].point_out.x * t2
746 + fPath[index + 1].point_in.x * t3
747 + fPath[index + 1].point.x * t4;
749 point.y = fPath[index].point.y * t1 + fPath[index].point_out.y * t2
750 + fPath[index + 1].point_in.y * t3
751 + fPath[index + 1].point.y * t4;
752 } else if (fClosed) {
753 point.x = fPath[fPointCount - 1].point.x * t1
754 + fPath[fPointCount - 1].point_out.x * t2
755 + fPath[0].point_in.x * t3 + fPath[0].point.x * t4;
757 point.y = fPath[fPointCount - 1].point.y * t1
758 + fPath[fPointCount - 1].point_out.y * t2
759 + fPath[0].point_in.y * t3 + fPath[0].point.y * t4;
762 return true;
764 return false;
767 #endif // ICON_O_MATIC
770 void
771 VectorPath::SetClosed(bool closed)
773 if (fClosed != closed) {
774 fClosed = closed;
775 _NotifyClosedChanged();
776 Notify();
781 BRect
782 VectorPath::Bounds() const
784 // the bounds of the actual curves, not the control points!
785 if (!fCachedBounds.IsValid())
786 fCachedBounds = _Bounds();
787 return fCachedBounds;
791 BRect
792 VectorPath::_Bounds() const
794 agg::path_storage path;
796 BRect b;
797 if (get_path_storage(path, fPath, fPointCount, fClosed)) {
798 agg::conv_curve<agg::path_storage> curve(path);
800 uint32 pathID[1];
801 pathID[0] = 0;
802 double left, top, right, bottom;
804 agg::bounding_rect(curve, pathID, 0, 1, &left, &top, &right, &bottom);
806 b.Set(left, top, right, bottom);
807 } else if (fPointCount == 1) {
808 b.Set(fPath[0].point.x, fPath[0].point.y, fPath[0].point.x,
809 fPath[0].point.y);
810 } else {
811 b.Set(0.0, 0.0, -1.0, -1.0);
813 return b;
817 BRect
818 VectorPath::ControlPointBounds() const
820 if (fPointCount > 0) {
821 BRect r(fPath[0].point, fPath[0].point);
822 for (int32 i = 0; i < fPointCount; i++) {
823 // include point
824 r.left = min_c(r.left, fPath[i].point.x);
825 r.top = min_c(r.top, fPath[i].point.y);
826 r.right = max_c(r.right, fPath[i].point.x);
827 r.bottom = max_c(r.bottom, fPath[i].point.y);
828 // include "in" point
829 r.left = min_c(r.left, fPath[i].point_in.x);
830 r.top = min_c(r.top, fPath[i].point_in.y);
831 r.right = max_c(r.right, fPath[i].point_in.x);
832 r.bottom = max_c(r.bottom, fPath[i].point_in.y);
833 // include "out" point
834 r.left = min_c(r.left, fPath[i].point_out.x);
835 r.top = min_c(r.top, fPath[i].point_out.y);
836 r.right = max_c(r.right, fPath[i].point_out.x);
837 r.bottom = max_c(r.bottom, fPath[i].point_out.y);
839 return r;
841 return BRect(0.0, 0.0, -1.0, -1.0);
845 void
846 VectorPath::Iterate(Iterator* iterator, float smoothScale) const
848 if (fPointCount > 1) {
849 // generate a curve for each segment of the path
850 // then iterate over the segments of the curve
851 agg::curve4_inc curve;
852 curve.approximation_scale(smoothScale);
854 for (int32 i = 0; i < fPointCount - 1; i++) {
855 iterator->MoveTo(fPath[i].point);
856 curve.init(fPath[i].point.x, fPath[i].point.y,
857 fPath[i].point_out.x, fPath[i].point_out.y,
858 fPath[i + 1].point_in.x, fPath[i + 1].point_in.y,
859 fPath[i + 1].point.x, fPath[i + 1].point.y);
861 double x, y;
862 unsigned cmd = curve.vertex(&x, &y);
863 while (!agg::is_stop(cmd)) {
864 BPoint p(x, y);
865 iterator->LineTo(p);
866 cmd = curve.vertex(&x, &y);
869 if (fClosed) {
870 iterator->MoveTo(fPath[fPointCount - 1].point);
871 curve.init(fPath[fPointCount - 1].point.x,
872 fPath[fPointCount - 1].point.y,
873 fPath[fPointCount - 1].point_out.x,
874 fPath[fPointCount - 1].point_out.y,
875 fPath[0].point_in.x, fPath[0].point_in.y,
876 fPath[0].point.x, fPath[0].point.y);
878 double x, y;
879 unsigned cmd = curve.vertex(&x, &y);
880 while (!agg::is_stop(cmd)) {
881 BPoint p(x, y);
882 iterator->LineTo(p);
883 cmd = curve.vertex(&x, &y);
890 void
891 VectorPath::CleanUp()
893 if (fPointCount == 0)
894 return;
896 bool notify = false;
898 // remove last point if it is coincident with the first
899 if (fClosed && fPointCount >= 1) {
900 if (fPath[0].point == fPath[fPointCount - 1].point) {
901 fPath[0].point_in = fPath[fPointCount - 1].point_in;
902 _SetPointCount(fPointCount - 1);
903 notify = true;
907 for (int32 i = 0; i < fPointCount; i++) {
908 // check for unnecessary, duplicate points
909 if (i > 0) {
910 if (fPath[i - 1].point == fPath[i].point
911 && fPath[i - 1].point == fPath[i - 1].point_out
912 && fPath[i].point == fPath[i].point_in) {
913 // the previous point can be removed
914 BPoint in = fPath[i - 1].point_in;
915 if (RemovePoint(i - 1)) {
916 i--;
917 fPath[i].point_in = in;
918 notify = true;
922 // re-establish connections of in-out control points if
923 // they line up with the main control point
924 if (fPath[i].point_in == fPath[i].point_out
925 || fPath[i].point == fPath[i].point_out
926 || fPath[i].point == fPath[i].point_in
927 || (fabs(agg::calc_line_point_distance(fPath[i].point_in.x,
928 fPath[i].point_in.y, fPath[i].point.x, fPath[i].point.y,
929 fPath[i].point_out.x, fPath[i].point_out.y)) < 0.01
930 && fabs(agg::calc_line_point_distance(fPath[i].point_out.x,
931 fPath[i].point_out.y, fPath[i].point.x, fPath[i].point.y,
932 fPath[i].point_in.x, fPath[i].point_in.y)) < 0.01)) {
933 fPath[i].connected = true;
934 notify = true;
938 if (notify)
939 _NotifyPathChanged();
943 void
944 VectorPath::Reverse()
946 VectorPath temp(*this);
947 int32 index = 0;
948 for (int32 i = fPointCount - 1; i >= 0; i--) {
949 temp.SetPoint(index, fPath[i].point, fPath[i].point_out,
950 fPath[i].point_in, fPath[i].connected);
951 index++;
953 *this = temp;
955 _NotifyPathReversed();
959 void
960 VectorPath::ApplyTransform(const Transformable& transform)
962 if (transform.IsIdentity())
963 return;
965 for (int32 i = 0; i < fPointCount; i++) {
966 transform.Transform(&(fPath[i].point));
967 transform.Transform(&(fPath[i].point_out));
968 transform.Transform(&(fPath[i].point_in));
971 _NotifyPathChanged();
975 void
976 VectorPath::PrintToStream() const
978 for (int32 i = 0; i < fPointCount; i++) {
979 printf("point %" B_PRId32 ": (%f, %f) -> (%f, %f) -> (%f, %f) (%d)\n", i,
980 fPath[i].point_in.x, fPath[i].point_in.y,
981 fPath[i].point.x, fPath[i].point.y,
982 fPath[i].point_out.x, fPath[i].point_out.y, fPath[i].connected);
987 bool
988 VectorPath::GetAGGPathStorage(agg::path_storage& path) const
990 return get_path_storage(path, fPath, fPointCount, fClosed);
994 // #pragma mark -
997 #ifdef ICON_O_MATIC
999 bool
1000 VectorPath::AddListener(PathListener* listener)
1002 if (listener && !fListeners.HasItem((void*)listener))
1003 return fListeners.AddItem((void*)listener);
1004 return false;
1008 bool
1009 VectorPath::RemoveListener(PathListener* listener)
1011 return fListeners.RemoveItem((void*)listener);
1015 int32
1016 VectorPath::CountListeners() const
1018 return fListeners.CountItems();
1022 PathListener*
1023 VectorPath::ListenerAtFast(int32 index) const
1025 return (PathListener*)fListeners.ItemAtFast(index);
1028 #endif // ICON_O_MATIC
1031 // #pragma mark -
1034 void
1035 VectorPath::_SetPoint(int32 index, BPoint point)
1037 fPath[index].point = point;
1038 fPath[index].point_in = point;
1039 fPath[index].point_out = point;
1041 fPath[index].connected = true;
1043 fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
1047 void
1048 VectorPath::_SetPoint(int32 index, const BPoint& point, const BPoint& pointIn,
1049 const BPoint& pointOut, bool connected)
1051 fPath[index].point = point;
1052 fPath[index].point_in = pointIn;
1053 fPath[index].point_out = pointOut;
1055 fPath[index].connected = connected;
1057 fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
1061 // #pragma mark -
1064 bool
1065 VectorPath::_SetPointCount(int32 count)
1067 // handle reallocation if we run out of room
1068 if (count >= fAllocCount) {
1069 fAllocCount = ((count) / ALLOC_CHUNKS + 1) * ALLOC_CHUNKS;
1070 if (fPath)
1071 fPath = obj_renew(fPath, control_point, fAllocCount);
1072 else
1073 fPath = obj_new(control_point, fAllocCount);
1075 if (fPath != NULL) {
1076 memset(fPath + fPointCount, 0,
1077 (fAllocCount - fPointCount) * sizeof(control_point));
1081 // update point count
1082 if (fPath) {
1083 fPointCount = count;
1084 } else {
1085 // reallocation might have failed
1086 fPointCount = 0;
1087 fAllocCount = 0;
1088 fprintf(stderr, "VectorPath::_SetPointCount(%" B_PRId32 ") - allocation failed!\n",
1089 count);
1092 fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
1094 return fPath != NULL;
1098 // #pragma mark -
1101 #ifdef ICON_O_MATIC
1103 void
1104 VectorPath::_NotifyPointAdded(int32 index) const
1106 BList listeners(fListeners);
1107 int32 count = listeners.CountItems();
1108 for (int32 i = 0; i < count; i++) {
1109 PathListener* listener = (PathListener*)listeners.ItemAtFast(i);
1110 listener->PointAdded(index);
1115 void
1116 VectorPath::_NotifyPointChanged(int32 index) const
1118 BList listeners(fListeners);
1119 int32 count = listeners.CountItems();
1120 for (int32 i = 0; i < count; i++) {
1121 PathListener* listener = (PathListener*)listeners.ItemAtFast(i);
1122 listener->PointChanged(index);
1127 void
1128 VectorPath::_NotifyPointRemoved(int32 index) const
1130 BList listeners(fListeners);
1131 int32 count = listeners.CountItems();
1132 for (int32 i = 0; i < count; i++) {
1133 PathListener* listener = (PathListener*)listeners.ItemAtFast(i);
1134 listener->PointRemoved(index);
1139 void
1140 VectorPath::_NotifyPathChanged() const
1142 BList listeners(fListeners);
1143 int32 count = listeners.CountItems();
1144 for (int32 i = 0; i < count; i++) {
1145 PathListener* listener = (PathListener*)listeners.ItemAtFast(i);
1146 listener->PathChanged();
1151 void
1152 VectorPath::_NotifyClosedChanged() const
1154 BList listeners(fListeners);
1155 int32 count = listeners.CountItems();
1156 for (int32 i = 0; i < count; i++) {
1157 PathListener* listener = (PathListener*)listeners.ItemAtFast(i);
1158 listener->PathClosedChanged();
1163 void
1164 VectorPath::_NotifyPathReversed() const
1166 BList listeners(fListeners);
1167 int32 count = listeners.CountItems();
1168 for (int32 i = 0; i < count; i++) {
1169 PathListener* listener = (PathListener*)listeners.ItemAtFast(i);
1170 listener->PathReversed();
1174 #endif // ICON_O_MATIC