1 /* 2 * Copyright 2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>. 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 #include "ComplexLayouter.h" 7 8 #include <math.h> 9 #include <stdio.h> 10 #include <string.h> 11 12 #include <new> 13 14 #include <OS.h> 15 #include <Size.h> 16 17 #include <AutoDeleter.h> 18 19 #include "LayoutOptimizer.h" 20 #include "SimpleLayouter.h" 21 22 23 //#define TRACE_COMPLEX_LAYOUTER 1 24 #if TRACE_COMPLEX_LAYOUTER 25 # define TRACE(format...) printf(format); 26 # define TRACE_ONLY(x) x 27 #else 28 # define TRACE(format...) 29 # define TRACE_ONLY(x) 30 #endif 31 32 using std::nothrow; 33 34 35 // MyLayoutInfo 36 class ComplexLayouter::MyLayoutInfo : public LayoutInfo { 37 public: 38 MyLayoutInfo(int32 elementCount, int32 spacing) 39 : fCount(elementCount), 40 fSpacing(spacing) 41 { 42 // We also store the location of the virtual elementCountth element. 43 // Thus fLocation[i + 1] - fLocation[i] is the size of the ith element 44 // (not considering spacing). 45 fLocations = new(nothrow) int32[elementCount + 1]; 46 } 47 48 ~MyLayoutInfo() 49 { 50 delete[] fLocations; 51 } 52 53 void InitFromSizes(int32* sizes) 54 { 55 fLocations[0] = 0; 56 for (int32 i = 0; i < fCount; i++) 57 fLocations[i + 1] = fLocations[i] + sizes[i] + fSpacing; 58 } 59 60 virtual float ElementLocation(int32 element) 61 { 62 if (element < 0 || element >= fCount) 63 return 0; 64 65 return fLocations[element]; 66 } 67 68 virtual float ElementSize(int32 element) 69 { 70 if (element < 0 || element >= fCount) 71 return -1; 72 73 return fLocations[element + 1] - fLocations[element] - 1 74 - fSpacing; 75 } 76 77 virtual float ElementRangeSize(int32 position, int32 length) 78 { 79 if (position < 0 || length < 0 || position + length > fCount) 80 return -1; 81 82 return fLocations[position + length] - fLocations[position] - 1 83 - fSpacing; 84 } 85 86 void Dump() 87 { 88 printf("ComplexLayouter::MyLayoutInfo(): %ld elements:\n", fCount); 89 for (int32 i = 0; i < fCount + 1; i++) 90 printf(" %2ld: location: %4ld\n", i, fLocations[i]); 91 } 92 93 public: 94 int32 fCount; 95 int32 fSpacing; 96 int32* fLocations; 97 }; 98 99 100 // Constraint 101 struct ComplexLayouter::Constraint { 102 Constraint(int32 start, int32 end, int32 min, int32 max) 103 : start(start), 104 end(end), 105 min(min), 106 max(max), 107 next(NULL) 108 { 109 if (min > max) 110 max = min; 111 effectiveMax = max; 112 } 113 114 void Restrict(int32 newMin, int32 newMax) 115 { 116 if (newMin > min) 117 min = newMin; 118 if (newMax < max) 119 max = newMax; 120 if (min > max) 121 max = min; 122 effectiveMax = max; 123 } 124 125 bool IsSatisfied(int32* sumValues) const 126 { 127 int32 value = sumValues[end] - sumValues[start - 1]; 128 return (value >= min && value <= max); 129 } 130 131 int32 start; 132 int32 end; 133 int32 min; 134 int32 max; 135 int32 effectiveMax; 136 Constraint* next; 137 }; 138 139 140 // SumItem 141 struct ComplexLayouter::SumItem { 142 int32 min; 143 int32 max; 144 bool minDirty; 145 bool maxDirty; 146 }; 147 148 149 // SumItemBackup 150 struct ComplexLayouter::SumItemBackup { 151 int32 min; 152 int32 max; 153 }; 154 155 156 // #pragma mark - ComplexLayouter 157 158 159 // constructor 160 ComplexLayouter::ComplexLayouter(int32 elementCount, float spacing) 161 : fElementCount(elementCount), 162 fSpacing((int32)spacing), 163 fConstraints(new(nothrow) Constraint*[elementCount]), 164 fWeights(new(nothrow) float[elementCount]), 165 fSums(new(nothrow) SumItem[elementCount + 1]), 166 fSumBackups(new(nothrow) SumItemBackup[elementCount + 1]), 167 fOptimizer(new(nothrow) LayoutOptimizer(elementCount)), 168 fUnlimited(B_SIZE_UNLIMITED / (elementCount == 0 ? 1 : elementCount)), 169 fMinMaxValid(false), 170 fOptimizerConstraintsAdded(false) 171 { 172 if (fConstraints) 173 memset(fConstraints, 0, sizeof(Constraint*) * fElementCount); 174 175 if (fWeights) { 176 for (int32 i = 0; i < fElementCount; i++) 177 fWeights[i] = 1.0f; 178 } 179 } 180 181 182 // destructor 183 ComplexLayouter::~ComplexLayouter() 184 { 185 for (int32 i = 0; i < fElementCount; i++) { 186 Constraint* constraint = fConstraints[i]; 187 fConstraints[i] = NULL; 188 while (constraint != NULL) { 189 Constraint* next = constraint->next; 190 delete constraint; 191 constraint = next; 192 } 193 } 194 195 delete[] fConstraints; 196 delete[] fWeights; 197 delete[] fSums; 198 delete[] fSumBackups; 199 delete fOptimizer; 200 } 201 202 203 // InitCheck 204 status_t 205 ComplexLayouter::InitCheck() const 206 { 207 if (!fConstraints || !fWeights || !fSums || !fSumBackups || !fOptimizer) 208 return B_NO_MEMORY; 209 return fOptimizer->InitCheck(); 210 } 211 212 213 // AddConstraints 214 void 215 ComplexLayouter::AddConstraints(int32 element, int32 length, 216 float _min, float _max, float _preferred) 217 { 218 if (element < 0 || length <= 0 || element + length > fElementCount) 219 return; 220 221 TRACE("%p->ComplexLayouter::AddConstraints(%ld, %ld, %ld, %ld, %ld)\n", 222 this, element, length, (int32)_min, (int32)_max, (int32)_preferred); 223 224 int32 spacing = fSpacing * (length - 1); 225 int32 min = (int32)_min + 1 - spacing; 226 int32 max = (int32)_max + 1 - spacing; 227 228 if (min < 0) 229 min = 0; 230 if (max > fUnlimited) 231 max = fUnlimited; 232 233 int32 end = element + length - 1; 234 Constraint** slot = fConstraints + end; 235 while (*slot != NULL && (*slot)->start > element) 236 slot = &(*slot)->next; 237 238 if (*slot != NULL && (*slot)->start == element) { 239 // previous constraint exists -- use stricter values 240 (*slot)->Restrict(min, max); 241 } else { 242 // no previous constraint -- create new one 243 Constraint* constraint = new(nothrow) Constraint(element, end, min, 244 max); 245 if (!constraint) 246 return; 247 constraint->next = *slot; 248 *slot = constraint; 249 } 250 251 fMinMaxValid = false; 252 } 253 254 255 // SetWeight 256 void 257 ComplexLayouter::SetWeight(int32 element, float weight) 258 { 259 if (element < 0 || element >= fElementCount) 260 return; 261 262 fWeights[element] = max_c(weight, 0); 263 } 264 265 266 // MinSize 267 float 268 ComplexLayouter::MinSize() 269 { 270 _ValidateLayout(); 271 return fMin; 272 } 273 274 275 // MaxSize 276 float 277 ComplexLayouter::MaxSize() 278 { 279 _ValidateLayout(); 280 return fMax; 281 } 282 283 284 // PreferredSize 285 float 286 ComplexLayouter::PreferredSize() 287 { 288 return fMin; 289 } 290 291 292 // CreateLayoutInfo 293 LayoutInfo* 294 ComplexLayouter::CreateLayoutInfo() 295 { 296 MyLayoutInfo* layoutInfo = new(nothrow) MyLayoutInfo(fElementCount, 297 fSpacing); 298 if (layoutInfo && !layoutInfo->fLocations) { 299 delete layoutInfo; 300 return NULL; 301 } 302 303 return layoutInfo; 304 } 305 306 307 // Layout 308 void 309 ComplexLayouter::Layout(LayoutInfo* _layoutInfo, float _size) 310 { 311 TRACE("%p->ComplexLayouter::Layout(%ld)\n", this, (int32)_size); 312 313 if (fElementCount == 0) 314 return; 315 316 _ValidateLayout(); 317 318 MyLayoutInfo* layoutInfo = (MyLayoutInfo*)_layoutInfo; 319 320 int32 min = fSums[fElementCount].min; 321 int32 max = fSums[fElementCount].max; 322 323 int32 size = (int32)_size + 1 - (fElementCount - 1) * fSpacing; 324 if (size < min) 325 size = min; 326 if (size > max) 327 size = max; 328 329 SumItem sums[fElementCount + 1]; 330 memcpy(sums, fSums, (fElementCount + 1) * sizeof(SumItem)); 331 332 sums[fElementCount].min = size; 333 sums[fElementCount].max = size; 334 sums[fElementCount].minDirty = (size != min); 335 sums[fElementCount].maxDirty = (size != max); 336 337 // propagate the size 338 _PropagateChangesBack(sums, fElementCount - 1, NULL); 339 _PropagateChanges(sums, fElementCount - 1, NULL); 340 341 #if TRACE_COMPLEX_LAYOUTER 342 TRACE("Layout(%ld)\n", size); 343 for (int32 i = 0; i < fElementCount; i++) { 344 SumItem& sum = sums[i + 1]; 345 TRACE("[%ld] minc = %4ld, maxc = %4ld\n", i + 1, sum.min, sum.max); 346 } 347 #endif 348 349 int32 sizes[fElementCount]; 350 if (!_Layout(size, sums, sizes)) { 351 } 352 353 layoutInfo->InitFromSizes(sizes); 354 } 355 356 357 // CloneLayouter 358 Layouter* 359 ComplexLayouter::CloneLayouter() 360 { 361 ComplexLayouter* layouter 362 = new(nothrow) ComplexLayouter(fElementCount, fSpacing); 363 ObjectDeleter<ComplexLayouter> layouterDeleter(layouter); 364 if (!layouter || layouter->InitCheck() != B_OK 365 || !layouter->fOptimizer->AddConstraintsFrom(fOptimizer)) { 366 return NULL; 367 } 368 369 // clone the constraints 370 for (int32 i = 0; i < fElementCount; i++) { 371 Constraint* constraint = fConstraints[i]; 372 Constraint** end = layouter->fConstraints + i; 373 while (constraint) { 374 *end = new(nothrow) Constraint(constraint->start, constraint->end, 375 constraint->min, constraint->max); 376 if (!*end) 377 return NULL; 378 379 end = &(*end)->next; 380 constraint = constraint->next; 381 } 382 } 383 384 // copy the other stuff 385 memcpy(layouter->fWeights, fWeights, fElementCount * sizeof(float)); 386 memcpy(layouter->fSums, fSums, (fElementCount + 1) * sizeof(SumItem)); 387 memcpy(layouter->fSumBackups, fSumBackups, 388 (fElementCount + 1) * sizeof(SumItemBackup)); 389 layouter->fMin = fMin; 390 layouter->fMax = fMax; 391 layouter->fMinMaxValid = fMinMaxValid; 392 393 return layouterDeleter.Detach(); 394 } 395 396 397 // _Layout 398 bool 399 ComplexLayouter::_Layout(int32 size, SumItem* sums, int32* sizes) 400 { 401 // prepare the desired solution 402 SimpleLayouter::DistributeSize(size, fWeights, sizes, fElementCount); 403 if (_SatisfiesConstraints(sizes)) { 404 // The desired solution already satisfies all constraints. 405 return true; 406 } 407 408 double realSizes[fElementCount]; 409 for (int32 i = 0; i < fElementCount; i++) 410 realSizes[i] = sizes[i]; 411 412 if (!_AddOptimizerConstraints()) 413 return false; 414 415 416 // prepare a feasible solution (the minimum) 417 double values[fElementCount]; 418 for (int32 i = 0; i < fElementCount; i++) 419 values[i] = sums[i + 1].min - sums[i].min; 420 421 #if TRACE_COMPLEX_LAYOUTER 422 TRACE("feasible solution vs. desired solution:\n"); 423 for (int32 i = 0; i < fElementCount; i++) 424 TRACE("%8.4f %8.4f\n", values[i], realSizes[i]); 425 #endif 426 427 // solve 428 TRACE_ONLY(bigtime_t time = system_time();) 429 if (!fOptimizer->Solve(realSizes, size, values)) 430 return false; 431 TRACE_ONLY(time = system_time() - time;) 432 433 // compute integer solution 434 // The basic strategy is to floor() the sums. This guarantees that the 435 // difference between two rounded sums remains in the range of floor() 436 // and ceil() of their real value difference. Since the constraints have 437 // integer values, the integer solution will therefore satisfy all 438 // constraints the real solution satisfied. 439 TRACE("computed solution in %lld us:\n", time); 440 441 double realSum = 0; 442 double previousSum = 0; 443 for (int32 i = 0; i < fElementCount; i++) { 444 realSum += values[i]; 445 double roundedRealSum = floor(realSum); 446 if (fuzzy_equals(realSum, roundedRealSum + 1)) 447 realSum = roundedRealSum + 1; 448 sizes[i] = int32(roundedRealSum - previousSum); 449 previousSum = roundedRealSum; 450 451 TRACE("x[%ld] = %8.4f %4ld\n", i, values[i], sizes[i]); 452 } 453 454 return _SatisfiesConstraints(sizes); 455 } 456 457 458 // _AddOptimizerConstraints 459 bool 460 ComplexLayouter::_AddOptimizerConstraints() 461 { 462 if (fOptimizerConstraintsAdded) 463 return true; 464 465 fOptimizer->RemoveAllConstraints(); 466 467 // add constraints 468 for (int32 i = 0; i < fElementCount; i++) { 469 SumItem& sum = fSums[i + 1]; 470 471 Constraint* constraint = fConstraints[i]; 472 while (constraint != NULL) { 473 SumItem& base = fSums[constraint->start]; 474 int32 sumMin = base.min + constraint->min; 475 int32 baseMax = sum.max - constraint->min; 476 bool minRedundant = (sumMin < sum.min && baseMax > base.max); 477 478 int32 sumMax = base.max + constraint->effectiveMax; 479 int32 baseMin = sum.min - constraint->effectiveMax; 480 bool maxRedundant = (sumMax > sum.max && baseMin < base.min); 481 482 if (!minRedundant || !maxRedundant) { 483 bool success = true; 484 if (constraint->min == constraint->effectiveMax) { 485 // min and max equal -- add an equality constraint 486 success = fOptimizer->AddConstraint(constraint->start - 1, 487 constraint->end, constraint->min, true); 488 } else { 489 // min and max not equal -- add them individually, 490 // unless redundant 491 if (!minRedundant) { 492 success |= fOptimizer->AddConstraint( 493 constraint->start - 1, constraint->end, 494 constraint->min, false); 495 } 496 if (!maxRedundant) { 497 success |= fOptimizer->AddConstraint(constraint->end, 498 constraint->start - 1, 499 -constraint->effectiveMax, false); 500 } 501 } 502 503 if (!success) 504 return false; 505 } 506 507 constraint = constraint->next; 508 } 509 } 510 511 fOptimizerConstraintsAdded = true; 512 return true; 513 } 514 515 516 // _SatisfiesConstraints 517 bool 518 ComplexLayouter::_SatisfiesConstraints(int32* sizes) const 519 { 520 int32 sumValues[fElementCount + 1]; 521 sumValues[0] = 0; 522 for (int32 i = 0; i < fElementCount; i++) 523 sumValues[i + 1] = sumValues[i] + sizes[i]; 524 525 return _SatisfiesConstraintsSums(sumValues); 526 } 527 528 529 // _SatisfiesConstraintsSums 530 bool 531 ComplexLayouter::_SatisfiesConstraintsSums(int32* sumValues) const 532 { 533 for (int32 i = 0; i < fElementCount; i++) { 534 Constraint* constraint = fConstraints[i]; 535 while (constraint) { 536 if (!constraint->IsSatisfied(sumValues)) 537 return false; 538 539 constraint = constraint->next; 540 } 541 } 542 543 return true; 544 } 545 546 547 // _ValidateLayout 548 void 549 ComplexLayouter::_ValidateLayout() 550 { 551 // The general idea for computing the min and max for the given constraints 552 // is that we rewrite the problem a little. Instead of considering the 553 // x_1, ... x_n (n = fElementCount) and the constraints of the form 554 // x_i + ... + x_{i+j} >= min[i,j] and 555 // x_i + ... + x_{i+j} >= max[i,j], with i >= 1, j >= 0, i + j <= n 556 // and min[i,j], max[i,j] >= 0 557 // we define 558 // c[0] = 0 559 // c[i] = \sum_{k=1}^i x_k, for all i, 1 <= i <= n 560 // and thus the constraints read: 561 // c[i+j] - c[i-1] >= min[i,j] 562 // c[i+j] - c[i-1] <= max[i,j] 563 // 564 // Let minc[i] and maxc[i] the limits imposed by the given constraints, i.e. 565 // minc[i] <= c[i] <= maxc[i] for any tuple of (c[i])_i satisfying the 566 // constraints (minc[i] and maxc[i] are unique), then we gain: 567 // minc[i+j] >= c[i-1] + min[i,j] 568 // maxc[i+j] <= c[i-1] + min[i,j] 569 // minc[i-1] >= minc[i+j] - max[i,j] 570 // maxc[i-1] >= maxc[i+j] - min[i,j] 571 // We can compute the minc[i] and maxc[i] in an iterative process, 572 // propagating the first to kinds of constraints forward and the other two 573 // backwards. First we start considering all min constraints only. They 574 // can't contradict each other and are usually to be enforced over max 575 // constraints. Afterwards we add the max constraints one by one. For each 576 // one of them we propagate resulting changes back and forth. In case of 577 // a conflict, we relax the max constraint as much as necessary to yield 578 // a consistent set of constraints. After all constraints have been 579 // incorporated, the resulting minc[n] and maxc[n] are the min and max 580 // limits we wanted to compute. 581 582 if (fMinMaxValid) 583 return; 584 585 fSums[0].min = 0; 586 fSums[0].max = 0; 587 588 int32 maxSum = 0; 589 for (int32 i = 0; i < fElementCount; i++) { 590 SumItem& sum = fSums[i + 1]; 591 sum.min = 0; 592 sum.max = maxSum += fUnlimited; 593 sum.minDirty = false; 594 sum.maxDirty = false; 595 } 596 597 // apply min constraints forward: 598 // minc[i+j] >= minc[i-1] + min[i,j] 599 for (int32 i = 0; i < fElementCount; i++) { 600 SumItem& sum = fSums[i + 1]; 601 602 Constraint* constraint = fConstraints[i]; 603 while (constraint != NULL) { 604 int32 minSum = fSums[constraint->start].min + constraint->min; 605 if (minSum > sum.min) { 606 sum.min = minSum; 607 } else { 608 TRACE("min constraint is redundant: x%ld + ... + x%ld >= %ld\n", 609 constraint->start, constraint->end, constraint->min); 610 } 611 612 constraint = constraint->next; 613 } 614 } 615 616 // apply min constraints backwards: 617 // maxc[i-1] <= maxc[i+j] - min[i,j] 618 for (int32 i = fElementCount - 1; i >= 0; i--) { 619 SumItem& sum = fSums[i + 1]; 620 621 Constraint* constraint = fConstraints[i]; 622 while (constraint != NULL) { 623 SumItem& base = fSums[constraint->start]; 624 int32 baseMax = sum.max - constraint->min; 625 if (baseMax < base.max) 626 base.max = baseMax; 627 628 constraint = constraint->next; 629 } 630 } 631 632 // apply max constraints 633 for (int32 i = 0; i < fElementCount; i++) { 634 Constraint* constraint = fConstraints[i]; 635 while (constraint != NULL) { 636 _ApplyMaxConstraint(constraint, i); 637 638 constraint = constraint->next; 639 } 640 } 641 642 #if TRACE_COMPLEX_LAYOUTER 643 for (int32 i = 0; i < fElementCount; i++) { 644 SumItem& sum = fSums[i + 1]; 645 TRACE("[%ld] minc = %4ld, maxc = %4ld\n", i + 1, sum.min, sum.max); 646 } 647 #endif 648 649 if (fElementCount == 0) { 650 fMin = -1; 651 fMax = B_SIZE_UNLIMITED; 652 } else { 653 int32 spacing = (fElementCount - 1) * fSpacing; 654 fMin = fSums[fElementCount].min + spacing - 1; 655 fMax = fSums[fElementCount].max + spacing - 1; 656 if (fMax >= fUnlimited) 657 fMax = B_SIZE_UNLIMITED; 658 } 659 660 fOptimizerConstraintsAdded = false; 661 fMinMaxValid = true; 662 } 663 664 665 // _ApplyMaxConstraint 666 void 667 ComplexLayouter::_ApplyMaxConstraint(Constraint* currentConstraint, int32 index) 668 { 669 SumItem& sum = fSums[index + 1]; 670 SumItem& base = fSums[currentConstraint->start]; 671 672 // We want to apply: 673 // c[i+j] <= c[i-1] + max[i,j] 674 // 675 // This has the following direct consequences (let k = i + j): 676 // (1) maxc[k] <= maxc[i-1] + max[i,j] 677 // (2) minc[i-1] >= minc[k] - max[i,j] 678 // 679 // If maxc[k] or minc[i-i] changed, those changes have to be propagated 680 // back. 681 682 // apply (1) maxc[k] <= maxc[i-1] + max[i,j] 683 int32 max = currentConstraint->effectiveMax; 684 int32 sumMax = base.max + max; 685 686 // enforce maxc[i+j] >= minc[i+j] 687 if (sumMax < sum.min) { 688 sumMax = sum.min; 689 max = sumMax - base.max; 690 } 691 692 // apply (2) minc[i-1] >= minc[k] - max[i,j] 693 // and check minc[i-1] <= maxc[i-1] 694 int32 baseMin = sum.min - max; 695 if (baseMin > base.max) { 696 baseMin = base.max; 697 max = sum.min - baseMin; 698 sumMax = base.max + max; 699 } 700 701 if (currentConstraint->effectiveMax != max) { 702 TRACE("relaxing conflicting max constraint (1): " 703 "x%ld + ... + x%ld <= %ld -> %ld\n", currentConstraint->start, 704 currentConstraint->end, currentConstraint->effectiveMax, max); 705 } 706 currentConstraint->effectiveMax = max; 707 708 if (baseMin <= base.min && sumMax >= sum.max) { 709 TRACE("max constraint is redundant: x%ld + ... + x%ld <= %ld\n", 710 currentConstraint->start, currentConstraint->end, 711 currentConstraint->effectiveMax); 712 return; 713 } 714 715 // backup old values, in case we detect a conflict later 716 _BackupValues(index); 717 718 int32 diff; 719 do { 720 // apply the changes 721 int32 changedIndex = currentConstraint->start; 722 723 if (baseMin > base.min) { 724 base.min = baseMin; 725 base.minDirty = true; 726 } 727 728 if (sumMax < sum.max) { 729 changedIndex = index; 730 sum.max = sumMax; 731 sum.maxDirty = true; 732 } 733 734 // propagate the changes 735 _PropagateChangesBack(fSums, changedIndex, currentConstraint); 736 _PropagateChanges(fSums, index, currentConstraint); 737 738 // check the new constraint again -- if it doesn't hold, it 739 // conflicts with the other constraints 740 diff = 0; 741 742 // check (1) maxc[k] <= maxc[i-1] + max[i,j] 743 max = currentConstraint->effectiveMax; 744 sumMax = base.max + max; 745 if (sumMax < sum.max) 746 diff = sum.max - sumMax; 747 748 // check (2) minc[i-1] >= minc[k] - max[i,j] 749 baseMin = sum.min - max; 750 if (baseMin > base.min) 751 diff = max_c(diff, baseMin - base.min); 752 753 // clear the dirty flags 754 for (int32 i = 0; i <= changedIndex; i++) { 755 SumItem& sum = fSums[i + 1]; 756 sum.minDirty = false; 757 sum.maxDirty = false; 758 } 759 760 // if we've got a conflict, we relax the constraint and try again 761 if (diff > 0) { 762 max += diff; 763 TRACE("relaxing conflicting max constraint (2): " 764 "x%ld + ... + x%ld <= %ld -> %ld\n", currentConstraint->start, 765 currentConstraint->end, currentConstraint->effectiveMax, max); 766 currentConstraint->effectiveMax = max; 767 768 _RestoreValues(index); 769 770 sumMax = base.max + max; 771 baseMin = sum.min - max; 772 773 if (baseMin <= base.min && sumMax >= sum.max) 774 return; 775 } 776 } while (diff > 0); 777 } 778 779 780 // _PropagateChanges 781 /*! Propagate changes forward using min and max constraints. Max constraints 782 Beyond \a toIndex or at \a to toIndex after (and including) 783 \a lastMaxConstraint will be ignored. To have all constraints be 784 considered pass \c fElementCount and \c NULL. 785 */ 786 void 787 ComplexLayouter::_PropagateChanges(SumItem* sums, int32 toIndex, 788 Constraint* lastMaxConstraint) 789 { 790 for (int32 i = 0; i < fElementCount; i++) { 791 SumItem& sum = sums[i + 1]; 792 793 bool ignoreMaxConstraints = (i > toIndex); 794 795 Constraint* constraint = fConstraints[i]; 796 while (constraint != NULL) { 797 SumItem& base = sums[constraint->start]; 798 799 if (constraint == lastMaxConstraint) 800 ignoreMaxConstraints = true; 801 802 // minc[k] >= minc[i-1] + min[i,j] 803 if (base.minDirty) { 804 int32 sumMin = base.min + constraint->min; 805 if (sumMin > sum.min) { 806 sum.min = sumMin; 807 sum.minDirty = true; 808 } 809 } 810 811 // maxc[k] <= maxc[i-1] + max[i,j] 812 if (base.maxDirty && !ignoreMaxConstraints) { 813 int32 sumMax = base.max + constraint->effectiveMax; 814 if (sumMax < sum.max) { 815 sum.max = sumMax; 816 sum.maxDirty = true; 817 } 818 } 819 820 constraint = constraint->next; 821 } 822 823 if (sum.minDirty || sum.maxDirty) { 824 if (sum.min > sum.max) { 825 // TODO: Can this actually happen? 826 TRACE("adjusted max in propagation phase: index: " 827 "%ld: %ld -> %ld\n", i, sum.max, sum.min); 828 sum.max = sum.min; 829 sum.maxDirty = true; 830 } 831 } 832 } 833 } 834 835 836 // _PropagateChangesBack 837 void 838 ComplexLayouter::_PropagateChangesBack(SumItem* sums, int32 changedIndex, 839 Constraint* lastMaxConstraint) 840 { 841 for (int32 i = changedIndex; i >= 0; i--) { 842 SumItem& sum = sums[i + 1]; 843 844 bool ignoreMaxConstraints = false; 845 846 Constraint* constraint = fConstraints[i]; 847 while (constraint != NULL) { 848 SumItem& base = sums[constraint->start]; 849 850 if (constraint == lastMaxConstraint) 851 ignoreMaxConstraints = true; 852 853 // minc[i-1] >= minc[k] - max[i,j] 854 if (sum.minDirty && !ignoreMaxConstraints) { 855 int32 baseMin = sum.min - constraint->effectiveMax; 856 if (baseMin > base.min) { 857 if (baseMin > base.max) { 858 TRACE("min above max in back propagation phase: index: " 859 "(%ld -> %ld), min: %ld, max: %ld\n", i, 860 constraint->start, baseMin, base.max); 861 } 862 base.min = baseMin; 863 base.minDirty = true; 864 } 865 } 866 867 // maxc[i-1] <= maxc[k] - min[i,j] 868 if (sum.maxDirty) { 869 int32 baseMax = sum.max - constraint->min; 870 if (baseMax < base.max) { 871 if (baseMax < base.min) { 872 TRACE("max below min in back propagation phase: index: " 873 "(%ld -> %ld), max: %ld, min: %ld\n", i, 874 constraint->start, baseMax, base.min); 875 } 876 base.max = baseMax; 877 base.maxDirty = true; 878 } 879 } 880 881 constraint = constraint->next; 882 } 883 } 884 } 885 886 887 // _BackupValues 888 void 889 ComplexLayouter::_BackupValues(int32 maxIndex) 890 { 891 for (int32 i = 0; i <= maxIndex; i++) { 892 SumItem& sum = fSums[i + 1]; 893 fSumBackups[i + 1].min = sum.min; 894 fSumBackups[i + 1].max = sum.max; 895 } 896 } 897 898 899 // _RestoreValues 900 void 901 ComplexLayouter::_RestoreValues(int32 maxIndex) 902 { 903 for (int32 i = 0; i <= maxIndex; i++) { 904 SumItem& sum = fSums[i + 1]; 905 sum.min = fSumBackups[i + 1].min; 906 sum.max = fSumBackups[i + 1].max; 907 } 908 } 909 910