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 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 81 PathListener::PathListener() 82 { 83 } 84 85 86 PathListener::~PathListener() 87 { 88 } 89 #endif 90 91 92 // #pragma mark - 93 94 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 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 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 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* 196 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 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 237 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& 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 322 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 350 VectorPath::MakeEmpty() 351 { 352 _SetPointCount(0); 353 } 354 355 356 // #pragma mark - 357 358 359 bool 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 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 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 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 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 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 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 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 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 567 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 580 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 593 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 606 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 624 VectorPath::CountPoints() const 625 { 626 return fPointCount; 627 } 628 629 630 // #pragma mark - 631 632 633 #ifdef ICON_O_MATIC 634 635 static float 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 675 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 706 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 737 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 772 VectorPath::SetClosed(bool closed) 773 { 774 if (fClosed != closed) { 775 fClosed = closed; 776 _NotifyClosedChanged(); 777 Notify(); 778 } 779 } 780 781 782 BRect 783 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 793 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 819 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 847 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 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 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 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 977 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 989 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 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 1010 VectorPath::RemoveListener(PathListener* listener) 1011 { 1012 return fListeners.RemoveItem((void*)listener); 1013 } 1014 1015 1016 int32 1017 VectorPath::CountListeners() const 1018 { 1019 return fListeners.CountItems(); 1020 } 1021 1022 1023 PathListener* 1024 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 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 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 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 1105 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 1117 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 1129 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 1141 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 1153 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 1165 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