Implemented a ComplexLayouter class which is going to replace the Layouter
implementation which used the qoca constraint solver. It does the min/max computation itself -- thanks to Peter Moulder for hinting that we're actually dealing with separation constraints and proposing an algorithm. The actual layout is done with the help of an active set method based optimizer. The test results look very good so far. The code needs some cleanup (debug output, math comments, special handling for some cases) and is therefore not yet enabled by default. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22288 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
a0710babd7
commit
9c7408528e
846
src/kits/interface/layouter/ComplexLayouter.cpp
Normal file
846
src/kits/interface/layouter/ComplexLayouter.cpp
Normal file
@ -0,0 +1,846 @@
|
||||
/*
|
||||
* Copyright 2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "ComplexLayouter.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <OS.h>
|
||||
#include <Size.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
|
||||
#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<ComplexLayouter> 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;
|
||||
}
|
||||
}
|
||||
|
78
src/kits/interface/layouter/ComplexLayouter.h
Normal file
78
src/kits/interface/layouter/ComplexLayouter.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef COMPLEX_LAYOUTER_H
|
||||
#define COMPLEX_LAYOUTER_H
|
||||
|
||||
#include <List.h>
|
||||
|
||||
#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
|
982
src/kits/interface/layouter/LayoutOptimizer.cpp
Normal file
982
src/kits/interface/layouter/LayoutOptimizer.cpp
Normal file
@ -0,0 +1,982 @@
|
||||
/*
|
||||
* Copyright 2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "LayoutOptimizer.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
|
||||
|
||||
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<typename Type>
|
||||
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<double*, MatrixDelete> 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<LayoutOptimizer> 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];
|
||||
}
|
68
src/kits/interface/layouter/LayoutOptimizer.h
Normal file
68
src/kits/interface/layouter/LayoutOptimizer.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef LAYOUT_OPTIMIZER_H
|
||||
#define LAYOUT_OPTIMIZER_H
|
||||
|
||||
#include <List.h>
|
||||
|
||||
|
||||
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
|
Loading…
Reference in New Issue
Block a user