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