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