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