1 /*
2 * Copyright 2006-2012, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Stephan Aßmus <superstippi@gmx.de>
7 */
8
9 #include "VectorPath.h"
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14
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>
20
21 #ifdef ICON_O_MATIC
22 # include <debugger.h>
23 # include <typeinfo>
24 #endif // ICON_O_MATIC
25
26 #include <Message.h>
27 #include <TypeConstants.h>
28
29 #ifdef ICON_O_MATIC
30 # include "support.h"
31
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
38
39 #include "Transformable.h"
40
41 #define obj_new(type, n) ((type *)malloc ((n) * sizeof(type)))
42 #define obj_renew(p, type, n) ((type *)realloc ((void *)p, (n) * sizeof(type)))
43 #define obj_free free
44
45 #define ALLOC_CHUNKS 20
46
47 _USING_ICON_NAMESPACE
48
49
50 bool
get_path_storage(agg::path_storage & path,const control_point * points,int32 count,bool closed)51 get_path_storage(agg::path_storage& path, const control_point* points,
52 int32 count, bool closed)
53 {
54 if (count > 1) {
55 path.move_to(points[0].point.x, points[0].point.y);
56
57 for (int32 i = 1; i < count; i++) {
58 path.curve4(points[i - 1].point_out.x, points[i - 1].point_out.y,
59 points[i].point_in.x, points[i].point_in.y,
60 points[i].point.x, points[i].point.y);
61 }
62 if (closed) {
63 // curve from last to first control point
64 path.curve4(
65 points[count - 1].point_out.x, points[count - 1].point_out.y,
66 points[0].point_in.x, points[0].point_in.y,
67 points[0].point.x, points[0].point.y);
68 path.close_polygon();
69 }
70
71 return true;
72 }
73 return false;
74 }
75
76
77 // #pragma mark -
78
79
80 #ifdef ICON_O_MATIC
PathListener()81 PathListener::PathListener()
82 {
83 }
84
85
~PathListener()86 PathListener::~PathListener()
87 {
88 }
89 #endif
90
91
92 // #pragma mark -
93
94
VectorPath()95 VectorPath::VectorPath()
96 :
97 #ifdef ICON_O_MATIC
98 BArchivable(),
99 IconObject("<path>"),
100 fListeners(20),
101 #endif
102 fPath(NULL),
103 fClosed(false),
104 fPointCount(0),
105 fAllocCount(0),
106 fCachedBounds(0.0, 0.0, -1.0, -1.0)
107 {
108 }
109
110
VectorPath(const VectorPath & from)111 VectorPath::VectorPath(const VectorPath& from)
112 :
113 #ifdef ICON_O_MATIC
114 BArchivable(),
115 IconObject(from),
116 fListeners(20),
117 #endif
118 fPath(NULL),
119 fClosed(false),
120 fPointCount(0),
121 fAllocCount(0),
122 fCachedBounds(0.0, 0.0, -1.0, -1.0)
123 {
124 *this = from;
125 }
126
127
VectorPath(BMessage * archive)128 VectorPath::VectorPath(BMessage* archive)
129 :
130 #ifdef ICON_O_MATIC
131 BArchivable(),
132 IconObject(archive),
133 fListeners(20),
134 #endif
135 fPath(NULL),
136 fClosed(false),
137 fPointCount(0),
138 fAllocCount(0),
139 fCachedBounds(0.0, 0.0, -1.0, -1.0)
140 {
141 if (!archive)
142 return;
143
144 type_code typeFound;
145 int32 countFound;
146 if (archive->GetInfo("point", &typeFound, &countFound) >= B_OK
147 && typeFound == B_POINT_TYPE
148 && _SetPointCount(countFound)) {
149 memset((void*)fPath, 0, fAllocCount * sizeof(control_point));
150
151 BPoint point;
152 BPoint pointIn;
153 BPoint pointOut;
154 bool connected;
155 for (int32 i = 0; i < fPointCount
156 && archive->FindPoint("point", i, &point) >= B_OK
157 && archive->FindPoint("point in", i, &pointIn) >= B_OK
158 && archive->FindPoint("point out", i, &pointOut) >= B_OK
159 && archive->FindBool("connected", i, &connected) >= B_OK; i++) {
160 fPath[i].point = point;
161 fPath[i].point_in = pointIn;
162 fPath[i].point_out = pointOut;
163 fPath[i].connected = connected;
164 }
165 }
166 if (archive->FindBool("path closed", &fClosed) < B_OK)
167 fClosed = false;
168
169 }
170
171
~VectorPath()172 VectorPath::~VectorPath()
173 {
174 if (fPath)
175 obj_free(fPath);
176
177 #ifdef ICON_O_MATIC
178 if (fListeners.CountItems() > 0) {
179 PathListener* listener = (PathListener*)fListeners.ItemAt(0);
180 char message[512];
181 sprintf(message, "VectorPath::~VectorPath() - "
182 "there are still listeners attached! %p/%s",
183 listener, typeid(*listener).name());
184 debugger(message);
185 }
186 #endif
187 }
188
189
190 // #pragma mark -
191
192
193 #ifdef ICON_O_MATIC
194
195 PropertyObject*
MakePropertyObject() const196 VectorPath::MakePropertyObject() const
197 {
198 PropertyObject* object = IconObject::MakePropertyObject();
199 if (!object)
200 return NULL;
201
202 // closed
203 object->AddProperty(new BoolProperty(PROPERTY_CLOSED, fClosed));
204
205 // archived path
206 BMessage* archive = new BMessage();
207 if (Archive(archive) == B_OK) {
208 object->AddProperty(new IconProperty(PROPERTY_PATH,
209 kPathPropertyIconBits, kPathPropertyIconWidth,
210 kPathPropertyIconHeight, kPathPropertyIconFormat, archive));
211 }
212
213 return object;
214 }
215
216
217 bool
SetToPropertyObject(const PropertyObject * object)218 VectorPath::SetToPropertyObject(const PropertyObject* object)
219 {
220 AutoNotificationSuspender _(this);
221 IconObject::SetToPropertyObject(object);
222
223 SetClosed(object->Value(PROPERTY_CLOSED, fClosed));
224
225 IconProperty* pathProperty = dynamic_cast<IconProperty*>(
226 object->FindProperty(PROPERTY_PATH));
227 if (pathProperty && pathProperty->Message()) {
228 VectorPath archivedPath(pathProperty->Message());
229 *this = archivedPath;
230 }
231
232 return HasPendingNotifications();
233 }
234
235
236 status_t
Archive(BMessage * into,bool deep) const237 VectorPath::Archive(BMessage* into, bool deep) const
238 {
239 status_t ret = IconObject::Archive(into, deep);
240 if (ret != B_OK)
241 return ret;
242
243 if (fPointCount > 0) {
244 // improve BMessage efficency by preallocating storage for all points
245 // with the first call
246 ret = into->AddData("point", B_POINT_TYPE, &fPath[0].point,
247 sizeof(BPoint), true, fPointCount);
248 if (ret >= B_OK) {
249 ret = into->AddData("point in", B_POINT_TYPE, &fPath[0].point_in,
250 sizeof(BPoint), true, fPointCount);
251 }
252 if (ret >= B_OK) {
253 ret = into->AddData("point out", B_POINT_TYPE, &fPath[0].point_out,
254 sizeof(BPoint), true, fPointCount);
255 }
256 if (ret >= B_OK) {
257 ret = into->AddData("connected", B_BOOL_TYPE, &fPath[0].connected,
258 sizeof(bool), true, fPointCount);
259 }
260
261 // add the rest of the points
262 for (int32 i = 1; i < fPointCount && ret >= B_OK; i++) {
263 ret = into->AddData("point", B_POINT_TYPE, &fPath[i].point,
264 sizeof(BPoint));
265 if (ret >= B_OK) {
266 ret = into->AddData("point in", B_POINT_TYPE, &fPath[i].point_in,
267 sizeof(BPoint));
268 }
269 if (ret >= B_OK) {
270 ret = into->AddData("point out", B_POINT_TYPE,
271 &fPath[i].point_out, sizeof(BPoint));
272 }
273 if (ret >= B_OK) {
274 ret = into->AddData("connected", B_BOOL_TYPE,
275 &fPath[i].connected, sizeof(bool));
276 }
277 }
278 }
279
280 if (ret >= B_OK)
281 ret = into->AddBool("path closed", fClosed);
282 else
283 fprintf(stderr, "failed adding points!\n");
284
285 if (ret < B_OK)
286 fprintf(stderr, "failed adding close!\n");
287
288 // finish off
289 if (ret < B_OK)
290 ret = into->AddString("class", "VectorPath");
291
292 return ret;
293 }
294
295 #endif // ICON_O_MATIC
296
297
298 // #pragma mark -
299
300
301 VectorPath&
operator =(const VectorPath & from)302 VectorPath::operator=(const VectorPath& from)
303 {
304 _SetPointCount(from.fPointCount);
305 fClosed = from.fClosed;
306 if (fPath) {
307 memcpy((void*)fPath, from.fPath, fPointCount * sizeof(control_point));
308 fCachedBounds = from.fCachedBounds;
309 } else {
310 fprintf(stderr, "VectorPath() -> allocation failed in operator=!\n");
311 fAllocCount = 0;
312 fPointCount = 0;
313 fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
314 }
315 Notify();
316
317 return *this;
318 }
319
320
321 bool
operator ==(const VectorPath & other) const322 VectorPath::operator==(const VectorPath& other) const
323 {
324 if (fClosed != other.fClosed)
325 return false;
326
327 if (fPointCount != other.fPointCount)
328 return false;
329
330 if (fPath == NULL && other.fPath == NULL)
331 return true;
332
333 if (fPath == NULL || other.fPath == NULL)
334 return false;
335
336 for (int32 i = 0; i < fPointCount; i++) {
337 if (fPath[i].point != other.fPath[i].point
338 || fPath[i].point_in != other.fPath[i].point_in
339 || fPath[i].point_out != other.fPath[i].point_out
340 || fPath[i].connected != other.fPath[i].connected) {
341 return false;
342 }
343 }
344
345 return true;
346 }
347
348
349 void
MakeEmpty()350 VectorPath::MakeEmpty()
351 {
352 _SetPointCount(0);
353 }
354
355
356 // #pragma mark -
357
358
359 bool
AddPoint(BPoint point)360 VectorPath::AddPoint(BPoint point)
361 {
362 int32 index = fPointCount;
363
364 if (_SetPointCount(fPointCount + 1)) {
365 _SetPoint(index, point);
366 _NotifyPointAdded(index);
367 return true;
368 }
369
370 return false;
371 }
372
373
374 bool
AddPoint(const BPoint & point,const BPoint & pointIn,const BPoint & pointOut,bool connected)375 VectorPath::AddPoint(const BPoint& point, const BPoint& pointIn,
376 const BPoint& pointOut, bool connected)
377 {
378 int32 index = fPointCount;
379
380 if (_SetPointCount(fPointCount + 1)) {
381 _SetPoint(index, point, pointIn, pointOut, connected);
382 _NotifyPointAdded(index);
383 return true;
384 }
385
386 return false;
387 }
388
389
390 bool
AddPoint(BPoint point,int32 index)391 VectorPath::AddPoint(BPoint point, int32 index)
392 {
393 if (index < 0)
394 index = 0;
395 if (index > fPointCount)
396 index = fPointCount;
397
398 if (_SetPointCount(fPointCount + 1)) {
399 // handle insert
400 if (index < fPointCount - 1) {
401 for (int32 i = fPointCount; i > index; i--) {
402 fPath[i].point = fPath[i - 1].point;
403 fPath[i].point_in = fPath[i - 1].point_in;
404 fPath[i].point_out = fPath[i - 1].point_out;
405 fPath[i].connected = fPath[i - 1].connected;
406 }
407 }
408 _SetPoint(index, point);
409 _NotifyPointAdded(index);
410 return true;
411 }
412 return false;
413 }
414
415
416 bool
RemovePoint(int32 index)417 VectorPath::RemovePoint(int32 index)
418 {
419 if (index >= 0 && index < fPointCount) {
420 if (index < fPointCount - 1) {
421 // move points
422 for (int32 i = index; i < fPointCount - 1; i++) {
423 fPath[i].point = fPath[i + 1].point;
424 fPath[i].point_in = fPath[i + 1].point_in;
425 fPath[i].point_out = fPath[i + 1].point_out;
426 fPath[i].connected = fPath[i + 1].connected;
427 }
428 }
429 fPointCount -= 1;
430
431 fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
432
433 _NotifyPointRemoved(index);
434 return true;
435 }
436 return false;
437 }
438
439
440 bool
SetPoint(int32 index,BPoint point)441 VectorPath::SetPoint(int32 index, BPoint point)
442 {
443 if (index == fPointCount)
444 index = 0;
445 if (index >= 0 && index < fPointCount) {
446 BPoint offset = point - fPath[index].point;
447 fPath[index].point = point;
448 fPath[index].point_in += offset;
449 fPath[index].point_out += offset;
450
451 fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
452
453 _NotifyPointChanged(index);
454 return true;
455 }
456 return false;
457 }
458
459
460 bool
SetPoint(int32 index,BPoint point,BPoint pointIn,BPoint pointOut,bool connected)461 VectorPath::SetPoint(int32 index, BPoint point, BPoint pointIn, BPoint pointOut,
462 bool connected)
463 {
464 if (index == fPointCount)
465 index = 0;
466 if (index >= 0 && index < fPointCount) {
467 fPath[index].point = point;
468 fPath[index].point_in = pointIn;
469 fPath[index].point_out = pointOut;
470 fPath[index].connected = connected;
471
472 fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
473
474 _NotifyPointChanged(index);
475 return true;
476 }
477 return false;
478 }
479
480
481 bool
SetPointIn(int32 i,BPoint point)482 VectorPath::SetPointIn(int32 i, BPoint point)
483 {
484 if (i == fPointCount)
485 i = 0;
486 if (i >= 0 && i < fPointCount) {
487 // first, set the "in" point
488 fPath[i].point_in = point;
489 // now see what to do about the "out" point
490 if (fPath[i].connected) {
491 // keep all three points in one line
492 BPoint v = fPath[i].point - fPath[i].point_in;
493 float distIn = sqrtf(v.x * v.x + v.y * v.y);
494 if (distIn > 0.0) {
495 float distOut = agg::calc_distance(
496 fPath[i].point.x, fPath[i].point.y,
497 fPath[i].point_out.x, fPath[i].point_out.y);
498 float scale = (distIn + distOut) / distIn;
499 v.x *= scale;
500 v.y *= scale;
501 fPath[i].point_out = fPath[i].point_in + v;
502 }
503 }
504
505 fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
506
507 _NotifyPointChanged(i);
508 return true;
509 }
510 return false;
511 }
512
513
514 bool
SetPointOut(int32 i,BPoint point,bool mirrorDist)515 VectorPath::SetPointOut(int32 i, BPoint point, bool mirrorDist)
516 {
517 if (i == fPointCount)
518 i = 0;
519 if (i >= 0 && i < fPointCount) {
520 // first, set the "out" point
521 fPath[i].point_out = point;
522 // now see what to do about the "out" point
523 if (mirrorDist) {
524 // mirror "in" point around main control point
525 BPoint v = fPath[i].point - fPath[i].point_out;
526 fPath[i].point_in = fPath[i].point + v;
527 } else if (fPath[i].connected) {
528 // keep all three points in one line
529 BPoint v = fPath[i].point - fPath[i].point_out;
530 float distOut = sqrtf(v.x * v.x + v.y * v.y);
531 if (distOut > 0.0) {
532 float distIn = agg::calc_distance(
533 fPath[i].point.x, fPath[i].point.y,
534 fPath[i].point_in.x, fPath[i].point_in.y);
535 float scale = (distIn + distOut) / distOut;
536 v.x *= scale;
537 v.y *= scale;
538 fPath[i].point_in = fPath[i].point_out + v;
539 }
540 }
541
542 fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
543
544 _NotifyPointChanged(i);
545 return true;
546 }
547 return false;
548 }
549
550
551 bool
SetInOutConnected(int32 index,bool connected)552 VectorPath::SetInOutConnected(int32 index, bool connected)
553 {
554 if (index >= 0 && index < fPointCount) {
555 fPath[index].connected = connected;
556 _NotifyPointChanged(index);
557 return true;
558 }
559 return false;
560 }
561
562
563 // #pragma mark -
564
565
566 bool
GetPointAt(int32 index,BPoint & point) const567 VectorPath::GetPointAt(int32 index, BPoint& point) const
568 {
569 if (index == fPointCount)
570 index = 0;
571 if (index >= 0 && index < fPointCount) {
572 point = fPath[index].point;
573 return true;
574 }
575 return false;
576 }
577
578
579 bool
GetPointInAt(int32 index,BPoint & point) const580 VectorPath::GetPointInAt(int32 index, BPoint& point) const
581 {
582 if (index == fPointCount)
583 index = 0;
584 if (index >= 0 && index < fPointCount) {
585 point = fPath[index].point_in;
586 return true;
587 }
588 return false;
589 }
590
591
592 bool
GetPointOutAt(int32 index,BPoint & point) const593 VectorPath::GetPointOutAt(int32 index, BPoint& point) const
594 {
595 if (index == fPointCount)
596 index = 0;
597 if (index >= 0 && index < fPointCount) {
598 point = fPath[index].point_out;
599 return true;
600 }
601 return false;
602 }
603
604
605 bool
GetPointsAt(int32 index,BPoint & point,BPoint & pointIn,BPoint & pointOut,bool * connected) const606 VectorPath::GetPointsAt(int32 index, BPoint& point, BPoint& pointIn,
607 BPoint& pointOut, bool* connected) const
608 {
609 if (index >= 0 && index < fPointCount) {
610 point = fPath[index].point;
611 pointIn = fPath[index].point_in;
612 pointOut = fPath[index].point_out;
613
614 if (connected)
615 *connected = fPath[index].connected;
616
617 return true;
618 }
619 return false;
620 }
621
622
623 int32
CountPoints() const624 VectorPath::CountPoints() const
625 {
626 return fPointCount;
627 }
628
629
630 // #pragma mark -
631
632
633 #ifdef ICON_O_MATIC
634
635 static float
distance_to_curve(const BPoint & p,const BPoint & a,const BPoint & aOut,const BPoint & bIn,const BPoint & b)636 distance_to_curve(const BPoint& p, const BPoint& a, const BPoint& aOut,
637 const BPoint& bIn, const BPoint& b)
638 {
639 agg::curve4_inc curve(a.x, a.y, aOut.x, aOut.y, bIn.x, bIn.y, b.x, b.y);
640
641 float segDist = FLT_MAX;
642 double x1, y1, x2, y2;
643 unsigned cmd = curve.vertex(&x1, &y1);
644 while (!agg::is_stop(cmd)) {
645 cmd = curve.vertex(&x2, &y2);
646 // first figure out if point is between segment start and end points
647 double a = agg::calc_distance(p.x, p.y, x2, y2);
648 double b = agg::calc_distance(p.x, p.y, x1, y1);
649
650 float currentDist = min_c(a, b);
651
652 if (a > 0.0 && b > 0.0) {
653 double c = agg::calc_distance(x1, y1, x2, y2);
654
655 double alpha = acos((b * b + c * c - a * a) / (2 * b * c));
656 double beta = acos((a * a + c * c - b * b) / (2 * a * c));
657
658 if (alpha <= M_PI_2 && beta <= M_PI_2) {
659 currentDist = fabs(agg::calc_line_point_distance(x1, y1, x2, y2,
660 p.x, p.y));
661 }
662 }
663
664 if (currentDist < segDist)
665 segDist = currentDist;
666
667 x1 = x2;
668 y1 = y2;
669 }
670 return segDist;
671 }
672
673
674 bool
GetDistance(BPoint p,float * distance,int32 * index) const675 VectorPath::GetDistance(BPoint p, float* distance, int32* index) const
676 {
677 if (fPointCount > 1) {
678 // generate a curve for each segment of the path
679 // then iterate over the segments of the curve measuring the distance
680 *distance = FLT_MAX;
681
682 for (int32 i = 0; i < fPointCount - 1; i++) {
683 float segDist = distance_to_curve(p, fPath[i].point,
684 fPath[i].point_out, fPath[i + 1].point_in, fPath[i + 1].point);
685 if (segDist < *distance) {
686 *distance = segDist;
687 *index = i + 1;
688 }
689 }
690 if (fClosed) {
691 float segDist = distance_to_curve(p, fPath[fPointCount - 1].point,
692 fPath[fPointCount - 1].point_out, fPath[0].point_in,
693 fPath[0].point);
694 if (segDist < *distance) {
695 *distance = segDist;
696 *index = fPointCount;
697 }
698 }
699 return true;
700 }
701 return false;
702 }
703
704
705 bool
FindBezierScale(int32 index,BPoint point,double * scale) const706 VectorPath::FindBezierScale(int32 index, BPoint point, double* scale) const
707 {
708 if (index >= 0 && index < fPointCount && scale) {
709 int maxStep = 1000;
710
711 double t = 0.0;
712 double dt = 1.0 / maxStep;
713
714 *scale = 0.0;
715 double min = FLT_MAX;
716
717 BPoint curvePoint;
718 for (int step = 1; step < maxStep; step++) {
719 t += dt;
720
721 GetPoint(index, t, curvePoint);
722 double d = agg::calc_distance(curvePoint.x, curvePoint.y,
723 point.x, point.y);
724
725 if (d < min) {
726 min = d;
727 *scale = t;
728 }
729 }
730 return true;
731 }
732 return false;
733 }
734
735
736 bool
GetPoint(int32 index,double t,BPoint & point) const737 VectorPath::GetPoint(int32 index, double t, BPoint& point) const
738 {
739 if (index >= 0 && index < fPointCount) {
740 double t1 = (1 - t) * (1 - t) * (1 - t);
741 double t2 = (1 - t) * (1 - t) * t * 3;
742 double t3 = (1 - t) * t * t * 3;
743 double t4 = t * t * t;
744
745 if (index < fPointCount - 1) {
746 point.x = fPath[index].point.x * t1 + fPath[index].point_out.x * t2
747 + fPath[index + 1].point_in.x * t3
748 + fPath[index + 1].point.x * t4;
749
750 point.y = fPath[index].point.y * t1 + fPath[index].point_out.y * t2
751 + fPath[index + 1].point_in.y * t3
752 + fPath[index + 1].point.y * t4;
753 } else if (fClosed) {
754 point.x = fPath[fPointCount - 1].point.x * t1
755 + fPath[fPointCount - 1].point_out.x * t2
756 + fPath[0].point_in.x * t3 + fPath[0].point.x * t4;
757
758 point.y = fPath[fPointCount - 1].point.y * t1
759 + fPath[fPointCount - 1].point_out.y * t2
760 + fPath[0].point_in.y * t3 + fPath[0].point.y * t4;
761 }
762
763 return true;
764 }
765 return false;
766 }
767
768 #endif // ICON_O_MATIC
769
770
771 void
SetClosed(bool closed)772 VectorPath::SetClosed(bool closed)
773 {
774 if (fClosed != closed) {
775 fClosed = closed;
776 _NotifyClosedChanged();
777 Notify();
778 }
779 }
780
781
782 BRect
Bounds() const783 VectorPath::Bounds() const
784 {
785 // the bounds of the actual curves, not the control points!
786 if (!fCachedBounds.IsValid())
787 fCachedBounds = _Bounds();
788 return fCachedBounds;
789 }
790
791
792 BRect
_Bounds() const793 VectorPath::_Bounds() const
794 {
795 agg::path_storage path;
796
797 BRect b;
798 if (get_path_storage(path, fPath, fPointCount, fClosed)) {
799 agg::conv_curve<agg::path_storage> curve(path);
800
801 uint32 pathID[1];
802 pathID[0] = 0;
803 double left, top, right, bottom;
804
805 agg::bounding_rect(curve, pathID, 0, 1, &left, &top, &right, &bottom);
806
807 b.Set(left, top, right, bottom);
808 } else if (fPointCount == 1) {
809 b.Set(fPath[0].point.x, fPath[0].point.y, fPath[0].point.x,
810 fPath[0].point.y);
811 } else {
812 b.Set(0.0, 0.0, -1.0, -1.0);
813 }
814 return b;
815 }
816
817
818 BRect
ControlPointBounds() const819 VectorPath::ControlPointBounds() const
820 {
821 if (fPointCount > 0) {
822 BRect r(fPath[0].point, fPath[0].point);
823 for (int32 i = 0; i < fPointCount; i++) {
824 // include point
825 r.left = min_c(r.left, fPath[i].point.x);
826 r.top = min_c(r.top, fPath[i].point.y);
827 r.right = max_c(r.right, fPath[i].point.x);
828 r.bottom = max_c(r.bottom, fPath[i].point.y);
829 // include "in" point
830 r.left = min_c(r.left, fPath[i].point_in.x);
831 r.top = min_c(r.top, fPath[i].point_in.y);
832 r.right = max_c(r.right, fPath[i].point_in.x);
833 r.bottom = max_c(r.bottom, fPath[i].point_in.y);
834 // include "out" point
835 r.left = min_c(r.left, fPath[i].point_out.x);
836 r.top = min_c(r.top, fPath[i].point_out.y);
837 r.right = max_c(r.right, fPath[i].point_out.x);
838 r.bottom = max_c(r.bottom, fPath[i].point_out.y);
839 }
840 return r;
841 }
842 return BRect(0.0, 0.0, -1.0, -1.0);
843 }
844
845
846 void
Iterate(Iterator * iterator,float smoothScale) const847 VectorPath::Iterate(Iterator* iterator, float smoothScale) const
848 {
849 if (fPointCount > 1) {
850 // generate a curve for each segment of the path
851 // then iterate over the segments of the curve
852 agg::curve4_inc curve;
853 curve.approximation_scale(smoothScale);
854
855 for (int32 i = 0; i < fPointCount - 1; i++) {
856 iterator->MoveTo(fPath[i].point);
857 curve.init(fPath[i].point.x, fPath[i].point.y,
858 fPath[i].point_out.x, fPath[i].point_out.y,
859 fPath[i + 1].point_in.x, fPath[i + 1].point_in.y,
860 fPath[i + 1].point.x, fPath[i + 1].point.y);
861
862 double x, y;
863 unsigned cmd = curve.vertex(&x, &y);
864 while (!agg::is_stop(cmd)) {
865 BPoint p(x, y);
866 iterator->LineTo(p);
867 cmd = curve.vertex(&x, &y);
868 }
869 }
870 if (fClosed) {
871 iterator->MoveTo(fPath[fPointCount - 1].point);
872 curve.init(fPath[fPointCount - 1].point.x,
873 fPath[fPointCount - 1].point.y,
874 fPath[fPointCount - 1].point_out.x,
875 fPath[fPointCount - 1].point_out.y,
876 fPath[0].point_in.x, fPath[0].point_in.y,
877 fPath[0].point.x, fPath[0].point.y);
878
879 double x, y;
880 unsigned cmd = curve.vertex(&x, &y);
881 while (!agg::is_stop(cmd)) {
882 BPoint p(x, y);
883 iterator->LineTo(p);
884 cmd = curve.vertex(&x, &y);
885 }
886 }
887 }
888 }
889
890
891 void
CleanUp()892 VectorPath::CleanUp()
893 {
894 if (fPointCount == 0)
895 return;
896
897 bool notify = false;
898
899 // remove last point if it is coincident with the first
900 if (fClosed && fPointCount >= 1) {
901 if (fPath[0].point == fPath[fPointCount - 1].point) {
902 fPath[0].point_in = fPath[fPointCount - 1].point_in;
903 _SetPointCount(fPointCount - 1);
904 notify = true;
905 }
906 }
907
908 for (int32 i = 0; i < fPointCount; i++) {
909 // check for unnecessary, duplicate points
910 if (i > 0) {
911 if (fPath[i - 1].point == fPath[i].point
912 && fPath[i - 1].point == fPath[i - 1].point_out
913 && fPath[i].point == fPath[i].point_in) {
914 // the previous point can be removed
915 BPoint in = fPath[i - 1].point_in;
916 if (RemovePoint(i - 1)) {
917 i--;
918 fPath[i].point_in = in;
919 notify = true;
920 }
921 }
922 }
923 // re-establish connections of in-out control points if
924 // they line up with the main control point
925 if (fPath[i].point_in == fPath[i].point_out
926 || fPath[i].point == fPath[i].point_out
927 || fPath[i].point == fPath[i].point_in
928 || (fabs(agg::calc_line_point_distance(fPath[i].point_in.x,
929 fPath[i].point_in.y, fPath[i].point.x, fPath[i].point.y,
930 fPath[i].point_out.x, fPath[i].point_out.y)) < 0.01
931 && fabs(agg::calc_line_point_distance(fPath[i].point_out.x,
932 fPath[i].point_out.y, fPath[i].point.x, fPath[i].point.y,
933 fPath[i].point_in.x, fPath[i].point_in.y)) < 0.01)) {
934 fPath[i].connected = true;
935 notify = true;
936 }
937 }
938
939 if (notify)
940 _NotifyPathChanged();
941 }
942
943
944 void
Reverse()945 VectorPath::Reverse()
946 {
947 VectorPath temp(*this);
948 int32 index = 0;
949 for (int32 i = fPointCount - 1; i >= 0; i--) {
950 temp.SetPoint(index, fPath[i].point, fPath[i].point_out,
951 fPath[i].point_in, fPath[i].connected);
952 index++;
953 }
954 *this = temp;
955
956 _NotifyPathReversed();
957 }
958
959
960 void
ApplyTransform(const Transformable & transform)961 VectorPath::ApplyTransform(const Transformable& transform)
962 {
963 if (transform.IsIdentity())
964 return;
965
966 for (int32 i = 0; i < fPointCount; i++) {
967 transform.Transform(&(fPath[i].point));
968 transform.Transform(&(fPath[i].point_out));
969 transform.Transform(&(fPath[i].point_in));
970 }
971
972 _NotifyPathChanged();
973 }
974
975
976 void
PrintToStream() const977 VectorPath::PrintToStream() const
978 {
979 for (int32 i = 0; i < fPointCount; i++) {
980 printf("point %" B_PRId32 ": (%f, %f) -> (%f, %f) -> (%f, %f) (%d)\n", i,
981 fPath[i].point_in.x, fPath[i].point_in.y,
982 fPath[i].point.x, fPath[i].point.y,
983 fPath[i].point_out.x, fPath[i].point_out.y, fPath[i].connected);
984 }
985 }
986
987
988 bool
GetAGGPathStorage(agg::path_storage & path) const989 VectorPath::GetAGGPathStorage(agg::path_storage& path) const
990 {
991 return get_path_storage(path, fPath, fPointCount, fClosed);
992 }
993
994
995 // #pragma mark -
996
997
998 #ifdef ICON_O_MATIC
999
1000 bool
AddListener(PathListener * listener)1001 VectorPath::AddListener(PathListener* listener)
1002 {
1003 if (listener && !fListeners.HasItem((void*)listener))
1004 return fListeners.AddItem((void*)listener);
1005 return false;
1006 }
1007
1008
1009 bool
RemoveListener(PathListener * listener)1010 VectorPath::RemoveListener(PathListener* listener)
1011 {
1012 return fListeners.RemoveItem((void*)listener);
1013 }
1014
1015
1016 int32
CountListeners() const1017 VectorPath::CountListeners() const
1018 {
1019 return fListeners.CountItems();
1020 }
1021
1022
1023 PathListener*
ListenerAtFast(int32 index) const1024 VectorPath::ListenerAtFast(int32 index) const
1025 {
1026 return (PathListener*)fListeners.ItemAtFast(index);
1027 }
1028
1029 #endif // ICON_O_MATIC
1030
1031
1032 // #pragma mark -
1033
1034
1035 void
_SetPoint(int32 index,BPoint point)1036 VectorPath::_SetPoint(int32 index, BPoint point)
1037 {
1038 fPath[index].point = point;
1039 fPath[index].point_in = point;
1040 fPath[index].point_out = point;
1041
1042 fPath[index].connected = true;
1043
1044 fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
1045 }
1046
1047
1048 void
_SetPoint(int32 index,const BPoint & point,const BPoint & pointIn,const BPoint & pointOut,bool connected)1049 VectorPath::_SetPoint(int32 index, const BPoint& point, const BPoint& pointIn,
1050 const BPoint& pointOut, bool connected)
1051 {
1052 fPath[index].point = point;
1053 fPath[index].point_in = pointIn;
1054 fPath[index].point_out = pointOut;
1055
1056 fPath[index].connected = connected;
1057
1058 fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
1059 }
1060
1061
1062 // #pragma mark -
1063
1064
1065 bool
_SetPointCount(int32 count)1066 VectorPath::_SetPointCount(int32 count)
1067 {
1068 // handle reallocation if we run out of room
1069 if (count >= fAllocCount) {
1070 fAllocCount = ((count) / ALLOC_CHUNKS + 1) * ALLOC_CHUNKS;
1071 if (fPath)
1072 fPath = obj_renew(fPath, control_point, fAllocCount);
1073 else
1074 fPath = obj_new(control_point, fAllocCount);
1075
1076 if (fPath != NULL) {
1077 memset((void*)(fPath + fPointCount), 0,
1078 (fAllocCount - fPointCount) * sizeof(control_point));
1079 }
1080 }
1081
1082 // update point count
1083 if (fPath) {
1084 fPointCount = count;
1085 } else {
1086 // reallocation might have failed
1087 fPointCount = 0;
1088 fAllocCount = 0;
1089 fprintf(stderr, "VectorPath::_SetPointCount(%" B_PRId32 ") - allocation failed!\n",
1090 count);
1091 }
1092
1093 fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
1094
1095 return fPath != NULL;
1096 }
1097
1098
1099 // #pragma mark -
1100
1101
1102 #ifdef ICON_O_MATIC
1103
1104 void
_NotifyPointAdded(int32 index) const1105 VectorPath::_NotifyPointAdded(int32 index) const
1106 {
1107 BList listeners(fListeners);
1108 int32 count = listeners.CountItems();
1109 for (int32 i = 0; i < count; i++) {
1110 PathListener* listener = (PathListener*)listeners.ItemAtFast(i);
1111 listener->PointAdded(index);
1112 }
1113 }
1114
1115
1116 void
_NotifyPointChanged(int32 index) const1117 VectorPath::_NotifyPointChanged(int32 index) const
1118 {
1119 BList listeners(fListeners);
1120 int32 count = listeners.CountItems();
1121 for (int32 i = 0; i < count; i++) {
1122 PathListener* listener = (PathListener*)listeners.ItemAtFast(i);
1123 listener->PointChanged(index);
1124 }
1125 }
1126
1127
1128 void
_NotifyPointRemoved(int32 index) const1129 VectorPath::_NotifyPointRemoved(int32 index) const
1130 {
1131 BList listeners(fListeners);
1132 int32 count = listeners.CountItems();
1133 for (int32 i = 0; i < count; i++) {
1134 PathListener* listener = (PathListener*)listeners.ItemAtFast(i);
1135 listener->PointRemoved(index);
1136 }
1137 }
1138
1139
1140 void
_NotifyPathChanged() const1141 VectorPath::_NotifyPathChanged() const
1142 {
1143 BList listeners(fListeners);
1144 int32 count = listeners.CountItems();
1145 for (int32 i = 0; i < count; i++) {
1146 PathListener* listener = (PathListener*)listeners.ItemAtFast(i);
1147 listener->PathChanged();
1148 }
1149 }
1150
1151
1152 void
_NotifyClosedChanged() const1153 VectorPath::_NotifyClosedChanged() const
1154 {
1155 BList listeners(fListeners);
1156 int32 count = listeners.CountItems();
1157 for (int32 i = 0; i < count; i++) {
1158 PathListener* listener = (PathListener*)listeners.ItemAtFast(i);
1159 listener->PathClosedChanged();
1160 }
1161 }
1162
1163
1164 void
_NotifyPathReversed() const1165 VectorPath::_NotifyPathReversed() const
1166 {
1167 BList listeners(fListeners);
1168 int32 count = listeners.CountItems();
1169 for (int32 i = 0; i < count; i++) {
1170 PathListener* listener = (PathListener*)listeners.ItemAtFast(i);
1171 listener->PathReversed();
1172 }
1173 }
1174
1175 #endif // ICON_O_MATIC
1176