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