diff --git a/src/kits/interface/layouter/ComplexLayouter.cpp b/src/kits/interface/layouter/ComplexLayouter.cpp new file mode 100644 index 0000000000..5a9b6569d5 --- /dev/null +++ b/src/kits/interface/layouter/ComplexLayouter.cpp @@ -0,0 +1,846 @@ +/* + * Copyright 2007, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "ComplexLayouter.h" + +#include +#include +#include + +#include + +#include +#include + +#include + +#include "LayoutOptimizer.h" +#include "SimpleLayouter.h" + + +using std::nothrow; + + +// MyLayoutInfo +class ComplexLayouter::MyLayoutInfo : public LayoutInfo { +public: + MyLayoutInfo(int32 elementCount, int32 spacing) + : fCount(elementCount), + fSpacing(spacing) + { + // We also store the location of the virtual elementCountth element. + // Thus fLocation[i + 1] - fLocation[i] is the size of the ith element + // (not considering spacing). + fLocations = new(nothrow) int32[elementCount + 1]; + } + + ~MyLayoutInfo() + { + delete[] fLocations; + } + + virtual float ElementLocation(int32 element) + { + if (element < 0 || element >= fCount) + return 0; + + return fLocations[element]; + } + + virtual float ElementSize(int32 element) + { + if (element < 0 || element >= fCount) + return -1; + + return fLocations[element + 1] - fLocations[element] - 1 + - fSpacing; + } + + virtual float ElementRangeSize(int32 position, int32 length) + { + if (position < 0 || length < 0 || position + length > fCount) + return -1; + + return fLocations[position + length] - fLocations[position] - 1 + - fSpacing; + } + + void Dump() + { + printf("ComplexLayouter::MyLayoutInfo(): %ld elements:\n", fCount); + for (int32 i = 0; i < fCount + 1; i++) + printf(" %2ld: location: %4ld\n", i, fLocations[i]); + } + +public: + int32 fCount; + int32 fSpacing; + int32* fLocations; +}; + + +// Constraint +struct ComplexLayouter::Constraint { + Constraint(int32 start, int32 end, int32 min, int32 max) + : start(start), + end(end), + min(min), + max(max), + next(NULL) + { + if (min > max) + max = min; + effectiveMax = max; + } + + void Restrict(int32 newMin, int32 newMax) + { + if (newMin > min) + min = newMin; + if (newMax < max) + max = newMax; + if (min > max) + max = min; + effectiveMax = max; + } + + int32 start; + int32 end; + int32 min; + int32 max; + int32 effectiveMax; + Constraint* next; +}; + + +// SumItem +struct ComplexLayouter::SumItem { + int32 min; + int32 max; + bool minDirty; + bool maxDirty; +}; + + +// SumItemBackup +struct ComplexLayouter::SumItemBackup { + int32 min; + int32 max; +}; + + +// #pragma mark - ComplexLayouter + + +// constructor +ComplexLayouter::ComplexLayouter(int32 elementCount, int32 spacing) + : fElementCount(elementCount), + fSpacing(spacing), + fConstraints(new(nothrow) Constraint*[elementCount]), + fWeights(new(nothrow) float[elementCount]), + fSums(new(nothrow) SumItem[elementCount + 1]), + fSumBackups(new(nothrow) SumItemBackup[elementCount + 1]), + fOptimizer(new(nothrow) LayoutOptimizer(elementCount)), + fLayoutValid(false) +{ +// TODO: Check initialization! + if (fConstraints) + memset(fConstraints, 0, sizeof(Constraint*) * fElementCount); + + if (fWeights) { + for (int32 i = 0; i < fElementCount; i++) + fWeights[i] = 1.0f; + } +} + + +// destructor +ComplexLayouter::~ComplexLayouter() +{ + for (int32 i = 0; i < fElementCount; i++) { + Constraint* constraint = fConstraints[i]; + fConstraints[i] = NULL; + while (constraint != NULL) { + Constraint* next = constraint->next; + delete constraint; + constraint = next; + } + } + + delete[] fConstraints; + delete[] fWeights; + delete[] fSums; + delete[] fSumBackups; + delete fOptimizer; +} + + +// InitCheck +status_t +ComplexLayouter::InitCheck() const +{ + if (!fConstraints || !fWeights || !fSums || !fSumBackups || !fOptimizer) + return B_NO_MEMORY; + return fOptimizer->InitCheck(); +} + + +// AddConstraints +void +ComplexLayouter::AddConstraints(int32 element, int32 length, + float _min, float _max, float _preferred) +{ + if (element < 0 || length <= 0 || element + length > fElementCount) + return; + +//printf("%p->ComplexLayouter::AddConstraints(%ld, %ld, %ld, %ld, %ld)\n", +//this, element, length, (int32)_min, (int32)_max, (int32)_preferred); + + int32 spacing = fSpacing * (length - 1); + int32 min = (int32)_min + 1 - spacing; + int32 max = (int32)_max + 1 - spacing; + + if (min < 0) + min = 0; + if (max > B_SIZE_UNLIMITED) + max = B_SIZE_UNLIMITED; + + int32 end = element + length - 1; + Constraint** slot = fConstraints + end; + while (*slot != NULL && (*slot)->start > element) + slot = &(*slot)->next; + + if (*slot != NULL && (*slot)->start == element) { + // previous constraint exists -- use stricter values + (*slot)->Restrict(min, max); + } else { + // no previous constraint -- create new one + Constraint* constraint = new(nothrow) Constraint(element, end, min, + max); + if (!constraint) + return; + constraint->next = *slot; + *slot = constraint; + } +} + + +// SetWeight +void +ComplexLayouter::SetWeight(int32 element, float weight) +{ + if (element < 0 || element >= fElementCount) + return; + + fWeights[element] = max_c(weight, 0); +} + + +// MinSize +float +ComplexLayouter::MinSize() +{ + _ValidateLayout(); + return fMin; +} + + +// MaxSize +float +ComplexLayouter::MaxSize() +{ + _ValidateLayout(); + return fMax; +} + + +// PreferredSize +float +ComplexLayouter::PreferredSize() +{ + return fMin; +} + + +// CreateLayoutInfo +LayoutInfo* +ComplexLayouter::CreateLayoutInfo() +{ + MyLayoutInfo* layoutInfo = new(nothrow) MyLayoutInfo(fElementCount, + fSpacing); + if (layoutInfo && !layoutInfo->fLocations) { + delete layoutInfo; + return NULL; + } + + return layoutInfo; +} + + +// Layout +void +ComplexLayouter::Layout(LayoutInfo* _layoutInfo, float _size) +{ +//printf("%p->ComplexLayouter::Layout(%ld)\n", this, (int32)_size); + if (fElementCount == 0) + return; + + _ValidateLayout(); + + MyLayoutInfo* layoutInfo = (MyLayoutInfo*)_layoutInfo; + + int32 min = fSums[fElementCount].min; + int32 max = fSums[fElementCount].max; + + int32 size = (int32)_size + 1 - (fElementCount - 1) * fSpacing; + if (size < min) + size = min; + if (size > max) + size = max; + + SumItem sums[fElementCount + 1]; + memcpy(sums, fSums, (fElementCount + 1) * sizeof(SumItem)); + + sums[fElementCount].min = size; + sums[fElementCount].max = size; + sums[fElementCount].minDirty = (size != min); + sums[fElementCount].maxDirty = (size != max); + + // propagate the size + _PropagateChangesBack(sums, fElementCount - 1, NULL); + _PropagateChanges(sums, fElementCount - 1, NULL); + +printf("Layout(%ld)\n", size); +for (int32 i = 0; i < fElementCount; i++) { +SumItem& sum = sums[i + 1]; +printf("[%ld] minc = %4ld, maxc = %4ld\n", i + 1, sum.min, sum.max); +} + +// TODO: Test whether the desired solution already satisfies all constraints. +// If so, we can skip the constraint solving part. + + +// TODO: We should probably already setup the constraints in _ValidateLayout(). +// This way we might not be able to skip as many redundant constraints, but +// supposedly it doesn't matter all that much, since those constraints +// wouldn't make it into the active set anyway. + fOptimizer->RemoveAllConstraints(); + + // add constraints + for (int32 i = 0; i < fElementCount; i++) { + SumItem& sum = fSums[i + 1]; + + Constraint* constraint = fConstraints[i]; + while (constraint != NULL) { + SumItem& base = fSums[constraint->start]; + int32 sumMin = base.min + constraint->min; + int32 baseMax = sum.max - constraint->min; + bool minRedundant = (sumMin < sum.min && baseMax > base.max); + + int32 sumMax = base.max + constraint->effectiveMax; + int32 baseMin = sum.min - constraint->effectiveMax; + bool maxRedundant = (sumMax > sum.max && baseMin < base.min); + + if (!minRedundant || !maxRedundant) { + bool success = true; + if (constraint->min == constraint->effectiveMax) { + // min and max equal -- add an equality constraint + success = fOptimizer->AddConstraint(constraint->start - 1, + constraint->end, constraint->min, true); + } else { + // min and max not equal -- add them individually, + // unless redundant + if (!minRedundant) { + success |= fOptimizer->AddConstraint( + constraint->start - 1, constraint->end, + constraint->min, false); + } + if (!maxRedundant) { + success |= fOptimizer->AddConstraint(constraint->end, + constraint->start - 1, + -constraint->effectiveMax, false); + } + } + } + + constraint = constraint->next; + } + } + + // prepare a feasible solution (the minimum) + double values[fElementCount]; + for (int32 i = 0; i < fElementCount; i++) + values[i] = sums[i + 1].min - sums[i].min; + + // prepare the desired solution + int32 sizes[fElementCount]; + SimpleLayouter::DistributeSize(size, fWeights, sizes, fElementCount); + double realSizes[fElementCount]; + for (int32 i = 0; i < fElementCount; i++) + realSizes[i] = sizes[i]; + +printf("feasible solution vs. desired solution:\n"); +for (int32 i = 0; i < fElementCount; i++) +printf("%8.4f %8.4f\n", values[i], realSizes[i]); + + // solve +bigtime_t time = system_time(); + if (!fOptimizer->Solve(realSizes, size, values)) + return; +time = system_time() - time; + + // compute integer solution + // The basic strategy is to floor() the sums. This guarantees that the + // difference between two rounded sums remains in the range of floor() + // and ceil() of their real value difference. Since the constraints have + // integer values, the integer solution will satisfy all constraints the + // real solution satisfied. +printf("computed solution in %lld us:\n", time); + double realSum = 0; + int32 spacing = 0; + layoutInfo->fLocations[0] = 0; + for (int32 i = 0; i < fElementCount; i++) { + realSum += values[i]; + double roundedRealSum = floor(realSum); + if (fuzzy_equals(realSum, roundedRealSum + 1)) + realSum = roundedRealSum + 1; + layoutInfo->fLocations[i + 1] = (int32)roundedRealSum + spacing; + spacing += fSpacing; + +printf("x[%ld] = %8.4f %4ld\n", i, values[i], layoutInfo->fLocations[i + 1] - layoutInfo->fLocations[i]); + } +} + + +// CloneLayouter +Layouter* +ComplexLayouter::CloneLayouter() +{ + ComplexLayouter* layouter + = new(nothrow) ComplexLayouter(fElementCount, fSpacing); + ObjectDeleter layouterDeleter(layouter); + if (!layouter || layouter->InitCheck() != B_OK + || !layouter->fOptimizer->AddConstraintsFrom(fOptimizer)) { + return NULL; + } + + // clone the constraints + for (int32 i = 0; i < fElementCount; i++) { + Constraint* constraint = fConstraints[i]; + Constraint** end = layouter->fConstraints + i; + while (constraint) { + *end = new(nothrow) Constraint(constraint->start, constraint->end, + constraint->min, constraint->max); + if (!*end) + return NULL; + + end = &(*end)->next; + constraint = constraint->next; + } + } + + // copy the other stuff + memcpy(layouter->fWeights, fWeights, fElementCount * sizeof(float)); + memcpy(layouter->fSums, fSums, (fElementCount + 1) * sizeof(SumItem)); + memcpy(layouter->fSumBackups, fSumBackups, + (fElementCount + 1) * sizeof(SumItemBackup)); + layouter->fMin = fMin; + layouter->fMax = fMax; + layouter->fLayoutValid = fLayoutValid; + + return layouterDeleter.Detach(); +} + + +// _ValidateLayout +void +ComplexLayouter::_ValidateLayout() +{ + if (fLayoutValid) + return; + + fSums[0].min = 0; + fSums[0].max = 0; + + for (int32 i = 0; i < fElementCount; i++) { + SumItem& sum = fSums[i + 1]; + sum.min = 0; + sum.max = B_SIZE_UNLIMITED; + sum.minDirty = false; + sum.maxDirty = false; + } + + // apply min constraints forward: + // minc[k] >= minc[i-1] + min[i,j] + for (int32 i = 0; i < fElementCount; i++) { + SumItem& sum = fSums[i + 1]; + + Constraint* constraint = fConstraints[i]; + while (constraint != NULL) { + int32 minSum = fSums[constraint->start].min + constraint->min; + if (minSum > sum.min) + sum.min = minSum; +else { +printf("min constraint is redundant: x%ld + ... + x%ld >= %ld\n", +constraint->start, constraint->end, constraint->min); +} + + constraint = constraint->next; + } + } + + // apply min constraints backwards: + // maxc[i-1] <= maxc[k] - min[i,j] + for (int32 i = fElementCount - 1; i >= 0; i--) { + SumItem& sum = fSums[i + 1]; + + Constraint* constraint = fConstraints[i]; + while (constraint != NULL) { + SumItem& base = fSums[constraint->start]; + int32 baseMax = sum.max - constraint->min; + if (baseMax < base.max) + base.max = baseMax; + + constraint = constraint->next; + } + } + + // apply max constraints + for (int32 i = 0; i < fElementCount; i++) { + Constraint* constraint = fConstraints[i]; + while (constraint != NULL) { + _ApplyMaxConstraint(constraint, i); + + constraint = constraint->next; + } +//printf("fSums[%ld] = {%ld, %ld}\n", i + 1, sum.min, sum.max); + } + +for (int32 i = 0; i < fElementCount; i++) { +SumItem& sum = fSums[i + 1]; +printf("[%ld] minc = %4ld, maxc = %4ld\n", i + 1, sum.min, sum.max); +} + + if (fElementCount == 0) { + fMin = -1; + fMax = B_SIZE_UNLIMITED; + } else { + int32 spacing = (fElementCount - 1) * fSpacing; + fMin = fSums[fElementCount].min + spacing - 1; + fMax = fSums[fElementCount].max + spacing - 1; + } + + fLayoutValid = true; +} + +/* + x[i] + ... + x[i+j] >= min[i,j] + x[i] + ... + x[i+j] <= max[i,j] + with + 1 <= i <= n + 0 <= j <= n - i + 0 <= min[i,j] <= max[i,j] + + Let + + c[0] = 0 + c[k] = x[1] + ... + x[k] for 1 <= k <= n + + it follows + + x[i] + ... + x[i+j] = c[i+j] - c[i-1] + + and thus the constraints can be rewritten as + + c[i+j] - c[i-1] >= min[i,j] + c[i+j] - c[i-1] <= max[i,j] + + or + + c[i+j] >= c[i-1] + min[i,j] + c[i+j] <= c[i-1] + max[i,j] + + We're looking for minimal minc[] and maximal maxc[] such that + + minc[i+j] >= minc[i-1] + min[i,j] + maxc[i+j] <= maxc[i-1] + max[i,j] + + minc[i+j] <= minc[i-1] + max[i,j] + maxc[i+j] >= maxc[i-1] + min[i,j] + + holds for all i and j. The latter two kinds of constraints have to be + enforced backwards: + + minc[i-1] >= minc[i+j] - max[i,j] + maxc[i-1] <= maxc[i+j] - min[i,j] + + + +----------------- + + // (1) maxc[k] <= maxc[i-1] + max[i,j] + // (2) minc[i-1] >= minc[k] - max[i,j] + + Modifying maxc[k] according to (1) potentially invalidates constraints of + these forms: + + (i) maxc[i'-1] <= maxc[k] - min[i',j'] + (ii) maxc[k+1+j'] <= maxc[k] + max[k+1,j'] + + After propagating (i) constraints backwards, all of them will be hold, + though more (ii) constraints might have been invalidated, though. + Propagating (ii) constraints forward afterwards, will make them all hold. + Since the min[i,j] and max[i,j] constraints are separation constraints and + the CSP not including the newly added constraint was conflict-free, + propagating the (i) and (ii) constraints won't change the maxc[i], i < k by + more than what maxc[k] changed. If afterwards the constraint (1) doesn't + hold, it apparently conflicts with the other constraints. +*/ + + +// _ApplyMaxConstraint +void +ComplexLayouter::_ApplyMaxConstraint(Constraint* currentConstraint, int32 index) +{ + SumItem& sum = fSums[index + 1]; + SumItem& base = fSums[currentConstraint->start]; + + // We want to apply: + // c[i+j] <= c[i-1] + max[i,j] + // + // This has the following direct consequences (let k = i + j): + // (1) maxc[k] <= maxc[i-1] + max[i,j] + // (2) minc[i-1] >= minc[k] - max[i,j] + // + // If maxc[k] or minc[i-i] changed, those changes have to be propagated + // back. + + // apply (1) maxc[k] <= maxc[i-1] + max[i,j] + int32 max = currentConstraint->effectiveMax; + int32 sumMax = base.max + max; + + // enforce maxc[i+j] >= minc[i+j] + if (sumMax < sum.min) { + sumMax = sum.min; + max = sumMax - base.max; + } + + // apply (2) minc[i-1] >= minc[k] - max[i,j] + // and check minc[i-1] <= maxc[i-1] + int32 baseMin = sum.min - max; + if (baseMin > base.max) { + baseMin = base.max; + max = sum.min - baseMin; + sumMax = base.max + max; + } + + // apply changes + +if (currentConstraint->effectiveMax != max) { +printf("relaxing conflicting max constraint (1): x%ld + ... + x%ld <= %ld -> %ld\n", +currentConstraint->start, currentConstraint->end, +currentConstraint->effectiveMax, max); +} + currentConstraint->effectiveMax = max; + + if (baseMin <= base.min && sumMax >= sum.max) +{ +printf("max constraint is redundant: x%ld + ... + x%ld <= %ld\n", +currentConstraint->start, currentConstraint->end, currentConstraint->effectiveMax); + return; +} + + // backup old values, in case we detect a conflict later + _BackupValues(index); + + int32 diff; + do { + // apply the changes + int32 changedIndex = currentConstraint->start; + + if (baseMin > base.min) { + base.min = baseMin; + base.minDirty = true; + } + + if (sumMax < sum.max) { + changedIndex = index; + sum.max = sumMax; + sum.maxDirty = true; + } + + // propagate the changes + _PropagateChangesBack(fSums, changedIndex, currentConstraint); + _PropagateChanges(fSums, index, currentConstraint); + + // check the new constraint again -- if it doesn't hold, it + // conflicts with the other constraints + diff = 0; + + // check (1) maxc[k] <= maxc[i-1] + max[i,j] + max = currentConstraint->effectiveMax; + sumMax = base.max + max; + if (sumMax < sum.max) + diff = sum.max - sumMax; + + // check (2) minc[i-1] >= minc[k] - max[i,j] + baseMin = sum.min - max; + if (baseMin > base.min) + diff = max_c(diff, baseMin - base.min); + + // clear the dirty flags + for (int32 i = 0; i <= changedIndex; i++) { + SumItem& sum = fSums[i + 1]; + sum.minDirty = false; + sum.maxDirty = false; + } + + // if we've got a conflict, we relax the constraint and try again + if (diff > 0) { + max += diff; +printf("relaxing conflicting max constraint (2): x%ld + ... + x%ld <= %ld -> %ld\n", +currentConstraint->start, currentConstraint->end, +currentConstraint->effectiveMax, max); + currentConstraint->effectiveMax = max; + + _RestoreValues(index); + + sumMax = base.max + max; + baseMin = sum.min - max; + + if (baseMin <= base.min && sumMax >= sum.max) + return; + } + } while (diff > 0); +} + + +// _PropagateChanges +/*! Propagate changes forward using min and max constraints. Max constraints + Beyond \a toIndex or at \a to toIndex after (and including) + \a lastMaxConstraint will be ignored. To have all constraints be + considered pass \c fElementCount and \c NULL. +*/ +void +ComplexLayouter::_PropagateChanges(SumItem* sums, int32 toIndex, + Constraint* lastMaxConstraint) +{ + for (int32 i = 0; i < fElementCount; i++) { + SumItem& sum = sums[i + 1]; + + bool ignoreMaxConstraints = (i > toIndex); + + Constraint* constraint = fConstraints[i]; + while (constraint != NULL) { + SumItem& base = sums[constraint->start]; + + if (constraint == lastMaxConstraint) + ignoreMaxConstraints = true; + + // minc[k] >= minc[i-1] + min[i,j] + if (base.minDirty) { + int32 sumMin = base.min + constraint->min; + if (sumMin > sum.min) { + sum.min = sumMin; + sum.minDirty = true; + } + } + + // maxc[k] <= maxc[i-1] + max[i,j] + if (base.maxDirty && !ignoreMaxConstraints) { + int32 sumMax = base.max + constraint->effectiveMax; + if (sumMax < sum.max) { + sum.max = sumMax; + sum.maxDirty = true; + } + } + + constraint = constraint->next; + } + + if (sum.minDirty || sum.maxDirty) { + if (sum.min > sum.max) { +// TODO: Can this actually happen? +printf("adjusted max in propagation phase: index: %ld: %ld -> %ld\n", i, sum.max, sum.min); + sum.max = sum.min; + sum.maxDirty = true; + } + } + } +} + + +// _PropagateChangesBack +void +ComplexLayouter::_PropagateChangesBack(SumItem* sums, int32 changedIndex, + Constraint* lastMaxConstraint) +{ + for (int32 i = changedIndex; i >= 0; i--) { + SumItem& sum = sums[i + 1]; + + bool ignoreMaxConstraints = false; + + Constraint* constraint = fConstraints[i]; + while (constraint != NULL) { + SumItem& base = sums[constraint->start]; + + if (constraint == lastMaxConstraint) + ignoreMaxConstraints = true; + + // minc[i-1] >= minc[k] - max[i,j] + if (sum.minDirty && !ignoreMaxConstraints) { + int32 baseMin = sum.min - constraint->effectiveMax; + if (baseMin > base.min) { +if (baseMin > base.max) { +printf("min above max in back propagation phase: index: (%ld -> %ld), " +"min: %ld, max: %ld\n", i, constraint->start, baseMin, base.max); +} + base.min = baseMin; + base.minDirty = true; + } + } + + // maxc[i-1] <= maxc[k] - min[i,j] + if (sum.maxDirty) { + int32 baseMax = sum.max - constraint->min; + if (baseMax < base.max) { +if (baseMax < base.min) { +printf("max below min in back propagation phase: index: (%ld -> %ld), " +"max: %ld, min: %ld\n", i, constraint->start, baseMax, base.min); +} + base.max = baseMax; + base.maxDirty = true; + } + } + + constraint = constraint->next; + } + } +} + + +// _BackupValues +void +ComplexLayouter::_BackupValues(int32 maxIndex) +{ + for (int32 i = 0; i <= maxIndex; i++) { + SumItem& sum = fSums[i + 1]; + fSumBackups[i + 1].min = sum.min; + fSumBackups[i + 1].max = sum.max; + } +} + + +// _RestoreValues +void +ComplexLayouter::_RestoreValues(int32 maxIndex) +{ + for (int32 i = 0; i <= maxIndex; i++) { + SumItem& sum = fSums[i + 1]; + sum.min = fSumBackups[i + 1].min; + sum.max = fSumBackups[i + 1].max; + } +} + diff --git a/src/kits/interface/layouter/ComplexLayouter.h b/src/kits/interface/layouter/ComplexLayouter.h new file mode 100644 index 0000000000..348f67bd5f --- /dev/null +++ b/src/kits/interface/layouter/ComplexLayouter.h @@ -0,0 +1,78 @@ +/* + * Copyright 2007, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef COMPLEX_LAYOUTER_H +#define COMPLEX_LAYOUTER_H + +#include + +#include "Layouter.h" + + +namespace BPrivate { +namespace Layout { + +class LayoutOptimizer; + +class ComplexLayouter : public Layouter { +public: + ComplexLayouter(int32 elementCount, + int32 spacing); + virtual ~ComplexLayouter(); + + virtual status_t InitCheck() const; + + virtual void AddConstraints(int32 element, int32 length, + float min, float max, float preferred); + virtual void SetWeight(int32 element, float weight); + + virtual float MinSize(); + virtual float MaxSize(); + virtual float PreferredSize(); + + virtual LayoutInfo* CreateLayoutInfo(); + + virtual void Layout(LayoutInfo* layoutInfo, float size); + + virtual Layouter* CloneLayouter(); + +private: + class MyLayoutInfo; + struct Constraint; + struct SumItem; + struct SumItemBackup; + + + void _ValidateLayout(); + void _ApplyMaxConstraint( + Constraint* currentConstraint, int32 index); + void _PropagateChanges(SumItem* sums, int32 toIndex, + Constraint* lastMaxConstraint); + void _PropagateChangesBack(SumItem* sums, + int32 changedIndex, + Constraint* lastMaxConstraint); + + void _BackupValues(int32 maxIndex); + void _RestoreValues(int32 maxIndex); + +private: + int32 fElementCount; + int32 fSpacing; + Constraint** fConstraints; + float* fWeights; + SumItem* fSums; + SumItemBackup* fSumBackups; + LayoutOptimizer* fOptimizer; + float fMin; + float fMax; + bool fLayoutValid; +}; + +} // namespace Layout +} // namespace BPrivate + +using BPrivate::Layout::ComplexLayouter; + + +#endif // COMPLEX_LAYOUTER_H diff --git a/src/kits/interface/layouter/LayoutOptimizer.cpp b/src/kits/interface/layouter/LayoutOptimizer.cpp new file mode 100644 index 0000000000..6394ef8338 --- /dev/null +++ b/src/kits/interface/layouter/LayoutOptimizer.cpp @@ -0,0 +1,982 @@ +/* + * Copyright 2007, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "LayoutOptimizer.h" + +#include +#include + +#include + +#include + + +using std::nothrow; + + +// #pragma mark - vector and matrix operations + + +// is_zero +static inline bool +is_zero(double* x, int n) +{ + for (int i = 0; i < n; i++) { + if (!fuzzy_equals(x[i], 0)) + return false; + } + + return true; +} + + +// add_vectors +static inline void +add_vectors(double* x, const double* y, int n) +{ + for (int i = 0; i < n; i++) + x[i] += y[i]; +} + + +// add_vectors_scaled +static inline void +add_vectors_scaled(double* x, const double* y, double scalar, int n) +{ + for (int i = 0; i < n; i++) + x[i] += y[i] * scalar; +} + + +// negate_vector +static inline void +negate_vector(double* x, int n) +{ + for (int i = 0; i < n; i++) + x[i] = -x[i]; +} + + +// allocate_matrix +static double** +allocate_matrix(int m, int n) +{ + double** matrix = new(nothrow) double*[m]; + if (!matrix) + return NULL; + + double* values = new(nothrow) double[m * n]; + if (!values) { + delete[] matrix; + return NULL; + } + + double* row = values; + for (int i = 0; i < m; i++, row += n) + matrix[i] = row; + + return matrix; +} + + +// free_matrix +static void +free_matrix(double** matrix) +{ + if (matrix) { + delete[] *matrix; + delete[] matrix; + } +} + + +// multiply_matrix_vector +/*! y = Ax + A: m x n matrix +*/ +static inline void +multiply_matrix_vector(const double* const* A, const double* x, int m, int n, + double* y) +{ + for (int i = 0; i < m; i++) { + double sum = 0; + for (int k = 0; k < n; k++) + sum += A[i][k] * x[k]; + y[i] = sum; + } +} + + +// multiply_matrices +/*! c = a*b +*/ +static void +multiply_matrices(const double* const* a, const double* const* b, double** c, + int m, int n, int l) +{ + for (int i = 0; i < m; i++) { + for (int j = 0; j < l; j++) { + double sum = 0; + for (int k = 0; k < n; k++) + sum += a[i][k] * b[k][j]; + c[i][j] = sum; + } + } +} + + +// transpose_matrix +static inline void +transpose_matrix(const double* const* A, double** Atrans, int m, int n) +{ + for (int i = 0; i < m; i++) { + for (int k = 0; k < n; k++) + Atrans[k][i] = A[i][k]; + } +} + + +// zero_matrix +static inline void +zero_matrix(double** A, int m, int n) +{ + for (int i = 0; i < m; i++) { + for (int k = 0; k < n; k++) + A[i][k] = 0; + } +} + + +// copy_matrix +static inline void +copy_matrix(const double* const* A, double** B, int m, int n) +{ + for (int i = 0; i < m; i++) { + for (int k = 0; k < n; k++) + B[i][k] = A[i][k]; + } +} + + +static inline void +multiply_optimization_matrix_vector(const double* x, int n, double* y) +{ + // The matrix has the form: + // 2 -1 0 ... 0 0 + // -1 2 -1 0 ... . . + // 0 -1 2 . . + // . 0 . . . + // . . 0 0 + // . . -1 0 + // 0 ... 0 -1 2 -1 + // 0 ... -1 1 + if (n == 1) { + y[0] = x[0]; + return; + } + + y[0] = 2 * x[0] - x[1]; + for (int i = 1; i < n - 1; i++) + y[i] = 2 * x[i] - x[i - 1] - x[i + 1]; + y[n - 1] = x[n - 1] - x[n - 2]; +} + + +static inline void +multiply_optimization_matrix_matrix(const double* const* A, int m, int n, + double** B) +{ + if (m == 1) { + memcpy(B[0], A[0], n * sizeof(double)); + return; + } + + for (int k = 0; k < n; k++) { + B[0][k] = 2 * A[0][k] - A[1][k]; + for (int i = 1; i < m - 1; i++) + B[i][k] = 2 * A[i][k] - A[i - 1][k] - A[i + 1][k]; + B[m - 1][k] = A[m - 1][k] - A[m - 2][k]; + } +} + + +template +static inline void +swap(Type& a, Type& b) +{ + Type c = a; + a = b; + b = c; +} + + +// #pragma mark - algorithms + + +bool +solve(double** a, int n, double* b) +{ + // index array for row permutation + // Note: We could eliminate it, if we would permutate the row pointers of a. + int indices[n]; + for (int i = 0; i < n; i++) + indices[i] = i; + + // forward elimination + for (int i = 0; i < n - 1; i++) { + // find pivot + int pivot = i; + double pivotValue = fabs(a[indices[i]][i]); + for (int j = i + 1; j < n; j++) { + int index = indices[j]; + double value = fabs(a[index][i]); + if (value > pivotValue) { + pivot = j; + pivotValue = value; + } + } + + if (fuzzy_equals(pivotValue, 0)) { + printf("solve(): matrix is not regular\n"); + return false; + } + + if (pivot != i) { + swap(indices[i], indices[pivot]); + swap(b[i], b[pivot]); + } + pivot = indices[i]; + + // eliminate + for (int j = i + 1; j < n; j++) { + int index = indices[j]; + double q = -a[index][i] / a[pivot][i]; + a[index][i] = 0; + for (int k = i + 1; k < n; k++) + a[index][k] += a[pivot][k] * q; + b[j] += b[i] * q; + } + } + + // backwards substitution + for (int i = n - 1; i >= 0; i--) { + int index = indices[i]; + double sum = b[i]; + for (int j = i + 1; j < n; j++) + sum -= a[index][j] * b[j]; + + b[i] = sum / a[index][i]; + } + + return true; +} + + +int +compute_dependencies(double** a, int m, int n, bool* independent) +{ + // index array for row permutation + // Note: We could eliminate it, if we would permutate the row pointers of a. + int indices[m]; + for (int i = 0; i < m; i++) + indices[i] = i; + + // forward elimination + int iterations = (m > n ? n : m); + int i = 0; + int column = 0; + for (; i < iterations && column < n; i++) { + // find next pivot + int pivot = i; + do { + double pivotValue = fabs(a[indices[i]][column]); + for (int j = i + 1; j < m; j++) { + int index = indices[j]; + double value = fabs(a[index][column]); + if (value > pivotValue) { + pivot = j; + pivotValue = value; + } + } + + if (!fuzzy_equals(pivotValue, 0)) + break; + + column++; + } while (column < n); + + if (column == n) + break; + + if (pivot != i) + swap(indices[i], indices[pivot]); + pivot = indices[i]; + + independent[pivot] = true; + + // eliminate + for (int j = i + 1; j < m; j++) { + int index = indices[j]; + double q = -a[index][column] / a[pivot][column]; + a[index][column] = 0; + for (int k = column + 1; k < n; k++) + a[index][k] += a[pivot][k] * q; + } + + column++; + } + + for (int j = i; j < m; j++) + independent[indices[j]] = false; + + return i; +} + + +// remove_linearly_dependent_rows +static int +remove_linearly_dependent_rows(double** A, double** temp, bool* independentRows, + int m, int n) +{ + // copy to temp + copy_matrix(A, temp, m, n); + + int count = compute_dependencies(temp, m, n, independentRows); + if (count == m) + return count; + + // remove the rows + int index = 0; + for (int i = 0; i < m; i++) { + if (independentRows[i]) { + if (index < i) { + for (int k = 0; k < n; k++) + A[index][k] = A[i][k]; + } + index++; + } + } + + return count; +} + + +/*! QR decomposition using Householder transformations. +*/ +bool +qr_decomposition(double** a, int m, int n, double* d, double** q) +{ + if (m < n) + return false; + + for (int j = 0; j < n; j++) { + // inner product of the first vector x of the (j,j) minor + double innerProductU = 0; + for (int i = j + 1; i < m; i++) + innerProductU = innerProductU + a[i][j] * a[i][j]; + double innerProduct = innerProductU + a[j][j] * a[j][j]; + if (fuzzy_equals(innerProduct, 0)) { + printf("qr_decomposition(): 0 column %d\n", j); + return false; + } + + // alpha (norm of x with opposite signedness of x_1) and thus r_{j,j} + double alpha = (a[j][j] < 0 ? sqrt(innerProduct) : -sqrt(innerProduct)); + d[j] = alpha; + + double beta = 1 / (alpha * a[j][j] - innerProduct); + + // u = x - alpha * e_1 + // (u is a[j..n][j]) + a[j][j] -= alpha; + + // left-multiply A_k with Q_k, thus obtaining a row of R and the A_{k+1} + // for the next iteration + for (int k = j + 1; k < n; k++) { + double sum = 0; + for (int i = j; i < m; i++) + sum += a[i][j] * a[i][k]; + sum *= beta; + + for (int i = j; i < m; i++) + a[i][k] += a[i][j] * sum; + } + + // v = u/|u| + innerProductU += a[j][j] * a[j][j]; + double beta2 = -2 / innerProductU; + + // right-multiply Q with Q_k + // Q_k = I - 2vv^T + // Q * Q_k = Q - 2 * Q * vv^T + if (j == 0) { + for (int k = 0; k < m; k++) { + for (int i = 0; i < m; i++) + q[k][i] = beta2 * a[k][0] * a[i][0]; + + q[k][k] += 1; + } + } else { + for (int k = 0; k < m; k++) { + double sum = 0; + for (int i = j; i < m; i++) + sum += q[k][i] * a[i][j]; + sum *= beta2; + + for (int i = j; i < m; i++) + q[k][i] += sum * a[i][j]; + } + } + } + + return true; +} + + +// MatrixDeleter +struct MatrixDelete { + inline void operator()(double** matrix) + { + free_matrix(matrix); + } +}; +typedef BPrivate::AutoDeleter MatrixDeleter; + + +// Constraint +struct LayoutOptimizer::Constraint { + Constraint(int32 left, int32 right, double value, bool equality, + int32 index) + : left(left), + right(right), + value(value), + index(index), + equality(equality) + { + } + + double ActualValue(double* values) const + { + double result = 0; + if (right >= 0) + result = values[right]; + if (left >= 0) + result -= values[left]; + return result; + } + + void Print() const + { + printf("c[%2ld] - c[%2ld] %2s %4d\n", right, left, + (equality ? "=" : ">="), (int)value); + } + + int32 left; + int32 right; + double value; + int32 index; + bool equality; +}; + + +// #pragma mark - LayoutOptimizer + + +// constructor +LayoutOptimizer::LayoutOptimizer(int32 variableCount) + : fVariableCount(variableCount), + fConstraints(), + fVariables(new (nothrow) double[variableCount]) +{ + fTemp1 = allocate_matrix(fVariableCount, fVariableCount); + fTemp2 = allocate_matrix(fVariableCount, fVariableCount); + fZtrans = allocate_matrix(fVariableCount, fVariableCount); + fQ = allocate_matrix(fVariableCount, fVariableCount); +} + + +// destructor +LayoutOptimizer::~LayoutOptimizer() +{ + free_matrix(fTemp1); + free_matrix(fTemp2); + free_matrix(fZtrans); + free_matrix(fQ); + + delete[] fVariables; + + for (int32 i = 0; + Constraint* constraint = (Constraint*)fConstraints.ItemAt(i); + i++) { + delete constraint; + } +} + + +// InitCheck +status_t +LayoutOptimizer::InitCheck() const +{ + if (!fVariables || !fTemp1 || !fTemp2 || !fZtrans || !fQ) + return B_NO_MEMORY; + return B_OK; +} + + +// Clone +LayoutOptimizer* +LayoutOptimizer::Clone() const +{ + LayoutOptimizer* clone = new(nothrow) LayoutOptimizer(fVariableCount); + ObjectDeleter cloneDeleter(clone); + if (!clone || clone->InitCheck() != B_OK + || !clone->AddConstraintsFrom(this)) { + return NULL; + } + + return cloneDeleter.Detach(); +} + + +// AddConstraint +/*! Adds a constraint of the form + \sum_{i=left+1}^{right} x_i >=/= value, if left < right + -\sum_{i=right+1}^{left} x_i >=/= value, if left > right + If \a equality is \c true, the constraint is an equality constraint. +*/ +bool +LayoutOptimizer::AddConstraint(int32 left, int32 right, double value, + bool equality) +{ + Constraint* constraint = new(nothrow) Constraint(left, right, value, + equality, fConstraints.CountItems()); + if (constraint == NULL) + return false; + + if (!fConstraints.AddItem(constraint)) { + delete constraint; + return false; + } + + return true; +} + + +// AddConstraintsFrom +bool +LayoutOptimizer::AddConstraintsFrom(const LayoutOptimizer* other) +{ + if (!other || other->fVariableCount != fVariableCount) + return false; + + int32 count = fConstraints.CountItems(); + for (int32 i = 0; i < count; i++) { + Constraint* constraint = (Constraint*)other->fConstraints.ItemAt(i); + if (!AddConstraint(constraint->left, constraint->right, + constraint->value, constraint->equality)) { + return false; + } + } + + return true; +} + + +// RemoveAllConstraints +void +LayoutOptimizer::RemoveAllConstraints() +{ + int32 count = fConstraints.CountItems(); + for (int32 i = 0; i < count; i++) { + Constraint* constraint = (Constraint*)fConstraints.ItemAt(i); + delete constraint; + } + fConstraints.MakeEmpty(); +} + + +// Solve +/*! Solves the quadratic program (QP) given by the constraints added via + AddConstraint(), the additional constraint \sum_{i=0}^{n-1} x_i = size, + and the optimization criterion to minimize + \sum_{i=0}^{n-1} (x_i - desired[i])^2. + The \a values array must contain a feasible solution when called and will + be overwritten with the optimial solution the method computes. +*/ +bool +LayoutOptimizer::Solve(const double* desired, double size, double* values) +{ + if (fVariables == NULL || desired == NULL|| values == NULL) + return false; + + int32 constraintCount = fConstraints.CountItems() + 1; + + // allocate the active constraint matrix and its transposed matrix + fActiveMatrix = allocate_matrix(constraintCount, fVariableCount); + fActiveMatrixTemp = allocate_matrix(constraintCount, fVariableCount); + MatrixDeleter _(fActiveMatrix); + MatrixDeleter _2(fActiveMatrixTemp); + if (!fActiveMatrix || !fActiveMatrixTemp) + return false; + + // add sum constraint + if (!AddConstraint(-1, fVariableCount - 1, size, true)) + return false; + + bool success = _Solve(desired, values); + + // remove sum constraint + Constraint* constraint = (Constraint*)fConstraints.RemoveItem( + constraintCount - 1); + delete constraint; + + return success; +} + + +// _Solve +bool +LayoutOptimizer::_Solve(const double* desired, double* values) +{ + int32 constraintCount = fConstraints.CountItems(); + +//printf("constraints:\n"); +//for (int32 i = 0; i < constraintCount; i++) { +// printf(" %-2ld: ", i); +// ((Constraint*)fConstraints.ItemAt(i))->Print(); +//} + + // our QP is suppose to be in this form: + // min_x 1/2x^TGx + x^Td + // s.t. a_i^Tx = b_i, i \in E + // a_i^Tx >= b_i, i \in I + + // init G and d + // + // The optimal solution minimizes the square of the distance to the desired + // solution: + // \sum_i=1^n (x_i - desired_i)^2 + // Since we consider the sums c_i = \sum_k=1^i x_k, no the x_i directly, + // we get + // \sum_{i=1}^n (c_i - c_{i-1} - desired_i)^2 + // Expanding and ignoring the constant part, we get + // \sum_{i=1}^n(c_i^2 - 2c_{i-1}c_i + c_{i-1}^2 + // + 2desired_i(c_{i-1} - c_i)) + // This results in a G of the form: + // 2 -1 0 ... 0 0 + // -1 2 -1 0 ... . . + // 0 -1 2 . . + // . 0 . . . + // . . 0 0 + // . . -1 0 + // 0 ... 0 -1 2 -1 + // 0 ... -1 1 + // and d: + // d_i = 2(desired_{i+1} - desired_i) + // where desired_{n+1} = 0 + // + // Note, that it is 1/2x^TGx, which is why we would have to multiply the + // G entries with 2. Instead we divide d by 2 though, which results in the + // equivalent optimization problem. + // + double x[fVariableCount]; + x[0] = values[0]; + for (int i = 1; i < fVariableCount; i++) + x[i] = values[i] + x[i - 1]; + + double d[fVariableCount]; + for (int i = 0; i < fVariableCount - 1; i++) + d[i] = desired[i + 1] - desired[i]; + d[fVariableCount - 1] = -desired[fVariableCount - 1]; + +//printf("d:\n"); +//Matrix(fVariableCount, 1, d).Print(); + + // init active set + BList activeConstraints(constraintCount); + + for (int32 i = 0; i < constraintCount; i++) { + Constraint* constraint = (Constraint*)fConstraints.ItemAt(i); + double actualValue = constraint->ActualValue(x); +//printf("constraint %ld: actual: %f constraint: %f\n", i, actualValue, constraint->value); + if (fuzzy_equals(actualValue, constraint->value)) + activeConstraints.AddItem(constraint); + } + + // The main loop: Each iteration we try to get closer to the optimum + // solution. We compute a vector p that brings our x closer to the optimum. + // We do that by computing the QP resulting from our active constraint set, + // W^k. Afterward each iteration we adjust the active set. +//int iteration = 0; + while (true) { +//printf("\n[iteration %d]\n", iteration++); +//printf("x:\n"); +//Matrix(fVariableCount, 1, x).Print(); +//printf("active set:\n"); +//for (int32 i = 0; i < activeConstraints.CountItems(); i++) { +// printf(" "); +// ((Constraint*)activeConstraints.ItemAt(i))->Print(); +//} + + // solve the QP: + // min_p 1/2p^TGp + g_k^Tp + // s.t. a_i^Tp = 0 + // with a_i \in activeConstraints + // g_k = Gx_k + d + // p = x - x_k + + int32 activeCount = activeConstraints.CountItems(); + if (activeCount == 0) { + printf("Solve(): Error: No more active constraints!\n"); + return false; + } + + // construct a matrix from the active constraints + int am = activeCount; + const int an = fVariableCount; + bool independentRows[activeCount]; + zero_matrix(fActiveMatrix, am, an); + + for (int32 i = 0; i < activeCount; i++) { + Constraint* constraint = (Constraint*)activeConstraints.ItemAt(i); + if (constraint->right >= 0) + fActiveMatrix[i][constraint->right] = 1; + if (constraint->left >= 0) + fActiveMatrix[i][constraint->left] = -1; + } + +// TODO: The fActiveMatrix is sparse (max 2 entries per row). There should be +// some room for optimization. + am = remove_linearly_dependent_rows(fActiveMatrix, fActiveMatrixTemp, + independentRows, am, an); + + // gxd = G * x + d + double gxd[fVariableCount]; + multiply_optimization_matrix_vector(x, fVariableCount, gxd); + add_vectors(gxd, d, fVariableCount); + + double p[fVariableCount]; + if (!_SolveSubProblem(gxd, am, p)) + return false; + +//printf("p:\n"); +//Matrix(fVariableCount, 1, p).Print(); + + if (is_zero(p, fVariableCount)) { + // compute Lagrange multipliers lambda_i + // if lambda_i >= 0 for all i \in W^k \union inequality constraints, + // then we're done. + // Otherwise remove the constraint with the smallest lambda_i + // from the active set. + // The derivation of the Lagrangian yields: + // \sum_{i \in W^k}(lambda_ia_i) = Gx_k + d + // Which is an system we can solve: + // A^Tlambda = Gx_k + d + + // A^T is over-determined, hence we need to reduce the number of + // rows before we can solve it. + bool independentColumns[an]; + double** aa = fTemp1; + transpose_matrix(fActiveMatrix, aa, am, an); + const int aam = remove_linearly_dependent_rows(aa, fTemp2, + independentColumns, an, am); + const int aan = am; + if (aam != aan) { + // This should not happen, since A has full row rank. + printf("Solve(): Transposed A has less linear independent rows " + "than it has columns!\n"); + return false; + } + + // also reduce the number of rows on the right hand side + double lambda[aam]; + int index = 0; + for (int i = 0; i < an; i++) { + if (independentColumns[i]) + lambda[index++] = gxd[i]; + } + + bool success = solve(aa, aam, lambda); + if (!success) { + // Impossible, since we've removed all linearly dependent rows. + printf("Solve(): Failed to compute lambda!\n"); + return false; + } +//printf("lambda:\n"); +//Matrix(aam, 1, lambda).Print(); + + // find min lambda_i (only, if it's < 0, though) + double minLambda = 0; + int minIndex = -1; + index = 0; + for (int i = 0; i < activeCount; i++) { + if (independentRows[i]) { + Constraint* constraint + = (Constraint*)activeConstraints.ItemAt(i); + if (!constraint->equality) { + if (lambda[index] < minLambda) { + minLambda = lambda[index]; + minIndex = i; + } + } + + index++; + } + } + + // if the min lambda is >= 0, we're done + if (minIndex < 0 || fuzzy_equals(minLambda, 0)) { + _SetResult(x, values); +//printf("all lambda_i >= 0\n"); + return true; + } + + // remove i from the active set + activeConstraints.RemoveItem(minIndex); + + } else { + // compute alpha_k + double alpha = 1; + int barrier = -1; + // if alpha_k < 1, add a barrier constraint to W^k + for (int32 i = 0; i < constraintCount; i++) { + Constraint* constraint = (Constraint*)fConstraints.ItemAt(i); + if (activeConstraints.HasItem(constraint)) + continue; + + double divider = constraint->ActualValue(p); + if (divider > 0 || fuzzy_equals(divider, 0)) + continue; + + // (b_i - a_i^Tx_k) / a_i^Tp_k + double alphaI = constraint->value + - constraint->ActualValue(x); + alphaI /= divider; + if (alphaI < alpha) { + alpha = alphaI; + barrier = i; + } + } +//printf("alpha: %f, barrier: %d\n", alpha, barrier); + + if (alpha < 1) + activeConstraints.AddItem(fConstraints.ItemAt(barrier)); + + // x += p * alpha; + add_vectors_scaled(x, p, alpha, fVariableCount); + } + } + + +/* +min_x 1/2x^TGx + x^Td +s.t. Ax = b + +x^* = x + p +c = Ax - b +g = d + Gx + +-Gp - A^T lambda^* = g +-Ap = c + +p = Yp_Y + Zp_Z + +Y und Z aus QR-Faktorisierung von A^T + +(AY)p_Y = -c -> p_Y +Z^TGZp_Z = -(Z^TGYp_Y + Z^Tg) -> p_Z +----------- + +min_x x^TGx + x^Td +s.t. a_i^Tx = b_i i \in E + a_i^Tx >= b_i i \in I + +p_k berechnen durch Loesen von + +min_p 1/2p^TGp + g_k^Tp +s.t. a_i^Tp = 0, i \in W^k + + + +*/ +} + + +bool +LayoutOptimizer::_SolveSubProblem(const double* d, int am, double* p) +{ + // x = p + // d = g_k + // b = 0 + // + // x^* = x + p + // c = Ax - b + // g = d + Gx + // + // with x = 0 we get + // c = -b = 0 + // g = d = g_k + // + // p = Yp_Y + Zp_Z + // + // (AY)p_Y = -c = 0 + // => p_Y = 0 + // + // (Z^TGZ)p_Z = -(Z^TYp_Y + Z^Tg) = -Z^Tg_k + // -> we have to solve (Z^TGZ)p_Z = -Z^Tg_k + + const int an = fVariableCount; + + // we get Y and Z by QR decomposition of A^T + double tempD[am]; + double** const Q = fQ; + transpose_matrix(fActiveMatrix, fTemp1, am, an); + bool success = qr_decomposition(fTemp1, an, am, tempD, Q); + if (!success) { + printf("Solve(): QR decomposition failed!\n"); + return false; + } + + // Z is the (1, m + 1) minor of Q + const int zm = an; + const int zn = an - am; + double* Z[zm]; + for (int i = 0; i < zm; i++) + Z[i] = Q[i] + am; + + // solve (Z^TGZ)p_Z = -Z^Tg_k + + // Z^T + transpose_matrix(Z, fZtrans, zm, zn); + // rhs: -Z^T * d; + double pz[zm]; + multiply_matrix_vector(fZtrans, d, zn, zm, pz); + negate_vector(pz, zn); + + // fTemp2 = Ztrans * G * Z + multiply_optimization_matrix_matrix(Z, an, zn, fTemp1); + multiply_matrices(fZtrans, fTemp1, fTemp2, zn, zm, zn); + + success = solve(fTemp2, zn, pz); + if (!success) { + printf("Solve(): Failed to solve() system for p_Z\n"); + return false; + } + + // p = Z * pz; + multiply_matrix_vector(Z, pz, zm, zn, p); + + return true; +} + + +// _SetResult +void +LayoutOptimizer::_SetResult(const double* x, double* values) +{ + values[0] = x[0]; + for (int i = 1; i < fVariableCount; i++) + values[i] = x[i] - x[i - 1]; +} diff --git a/src/kits/interface/layouter/LayoutOptimizer.h b/src/kits/interface/layouter/LayoutOptimizer.h new file mode 100644 index 0000000000..84bdec625d --- /dev/null +++ b/src/kits/interface/layouter/LayoutOptimizer.h @@ -0,0 +1,68 @@ +/* + * Copyright 2007, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef LAYOUT_OPTIMIZER_H +#define LAYOUT_OPTIMIZER_H + +#include + + +static const double kEqualsEpsilon = 0.000001; + + +namespace BPrivate { +namespace Layout { + +class LayoutOptimizer { +public: + LayoutOptimizer(int32 variableCount); + ~LayoutOptimizer(); + + status_t InitCheck() const; + + LayoutOptimizer* Clone() const; + + bool AddConstraint(int32 left, int32 right, + double value, bool equality); + bool AddConstraintsFrom( + const LayoutOptimizer* solver); + void RemoveAllConstraints(); + + bool Solve(const double* desired, double size, + double* values); + +private: + bool _Solve(const double* desired, double* values); + bool _SolveSubProblem(const double* d, int am, + double* p); + void _SetResult(const double* x, double* values); + + + struct Constraint; + + int32 fVariableCount; + BList fConstraints; + double* fVariables; + double** fTemp1; + double** fTemp2; + double** fZtrans; + double** fQ; + double** fActiveMatrix; + double** fActiveMatrixTemp; +}; + +} // namespace Layout +} // namespace BPrivate + +using BPrivate::Layout::LayoutOptimizer; + + +inline bool +fuzzy_equals(double a, double b) +{ + return fabs(a - b) < kEqualsEpsilon; +} + + +#endif // LAYOUT_OPTIMIZER_H