xref: /haiku/src/libs/icon/shape/VectorPath.cpp (revision 36830615ddf125c44964bc21a8c2040058f97841)
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