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