remove Kiwi constraint-based packing code from canvas library and users (library version)

This commit is contained in:
Paul Davis 2021-11-04 11:25:30 -06:00
parent a9adc3c5c6
commit 0f0c5c7039
28 changed files with 0 additions and 5235 deletions

View file

@ -1,143 +0,0 @@
/*
* Copyright (C) 2020 Paul Davis <paul@linuxaudiosystems.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __CANVAS_CONSTRAINED_ITEM_H__
#define __CANVAS_CONSTRAINED_ITEM_H__
#include "kiwi/kiwi.h"
#include "canvas/types.h"
#include "canvas/visibility.h"
namespace ArdourCanvas
{
class Item;
class ConstraintPacker;
class /* LIBCANVAS_API */ ConstrainedItem
{
public:
ConstrainedItem (Item& i);
virtual ~ConstrainedItem ();
Item& item() { return _item; }
kiwi::Variable& left () { return _left; }
kiwi::Variable& right () { return _right; }
kiwi::Variable& top () { return _top; }
kiwi::Variable& bottom () { return _bottom; }
kiwi::Variable& width () { return _width; }
kiwi::Variable& height () { return _height; }
kiwi::Variable& center_x () { return _center_x; }
kiwi::Variable& center_y () { return _center_y; }
kiwi::Variable const & left () const { return _left; }
kiwi::Variable const & right () const { return _right; }
kiwi::Variable const & top () const { return _top; }
kiwi::Variable const & bottom () const { return _bottom; }
kiwi::Variable const & width () const { return _width; }
kiwi::Variable const & height () const { return _height; }
kiwi::Variable const & center_x () const { return _center_x; }
kiwi::Variable const & center_y () const { return _center_y; }
kiwi::Variable& left_padding () { return _left_padding; }
kiwi::Variable& right_padding () { return _right_padding; }
kiwi::Variable& top_padding () { return _top_padding; }
kiwi::Variable& bottom_padding () { return _bottom_padding; }
void constrained (ConstraintPacker const & parent);
virtual bool involved (kiwi::Constraint const &) const;
std::vector<kiwi::Constraint> const & constraints() const { return _constraints; }
void add_constraint (kiwi::Constraint const & c) { _constraints.push_back (c); }
ConstrainedItem& at (Duple const &);
ConstrainedItem& size (Duple const &);
ConstrainedItem& box (Rect const &);
ConstrainedItem& left_of (ConstrainedItem const &, Distance pad = 0);
ConstrainedItem& right_of (ConstrainedItem const &, Distance pad = 0);
ConstrainedItem& above (ConstrainedItem const &, Distance pad = 0);
ConstrainedItem& below (ConstrainedItem const &, Distance pad = 0);
ConstrainedItem& x_centered (ConstrainedItem const &, Distance offset = 0);
ConstrainedItem& y_centered (ConstrainedItem const &, Distance offset = 0);
ConstrainedItem& centered_on (ConstrainedItem const &, Distance xoffset = 0, Distance yoffset = 0);
ConstrainedItem& top_aligned_with (ConstrainedItem const &, Distance offset = 0);
ConstrainedItem& bottom_aligned_with (ConstrainedItem const &, Distance offset = 0);
ConstrainedItem& left_aligned_with (ConstrainedItem const &, Distance offset = 0);
ConstrainedItem& right_aligned_with (ConstrainedItem const &, Distance offset = 0);
ConstrainedItem& same_size_as (ConstrainedItem const &, Distance wdelta = 0, Distance hdelta = 0);
ConstrainedItem& same_width_as (ConstrainedItem const &, Distance delta = 0);
ConstrainedItem& same_height_as (ConstrainedItem const &, Distance delta = 0);
virtual void dump (std::ostream&);
protected:
Item& _item;
std::vector<kiwi::Constraint> _constraints;
kiwi::Variable _left;
kiwi::Variable _right;
kiwi::Variable _top;
kiwi::Variable _bottom;
kiwi::Variable _width;
kiwi::Variable _height;
kiwi::Variable _left_padding;
kiwi::Variable _right_padding;
kiwi::Variable _top_padding;
kiwi::Variable _bottom_padding;
/* derived */
kiwi::Variable _center_x;
kiwi::Variable _center_y;
};
class /* LIBCANVAS_API */ BoxConstrainedItem : public ConstrainedItem
{
public:
BoxConstrainedItem (Item& i, PackOptions primary_axis_opts, PackOptions secondary_axis_opts);
~BoxConstrainedItem ();
virtual bool involved (kiwi::Constraint const &) const;
kiwi::Variable& left_margin () { return _left_margin; }
kiwi::Variable& right_margin () { return _right_margin; }
kiwi::Variable& top_margin () { return _top_margin; }
kiwi::Variable& bottom_margin () { return _bottom_margin; }
PackOptions primary_axis_pack_options() const { return _primary_axis_pack_options; }
PackOptions secondary_axis_pack_options() const { return _secondary_axis_pack_options; }
void dump (std::ostream&);
private:
kiwi::Variable _left_margin;
kiwi::Variable _right_margin;
kiwi::Variable _top_margin;
kiwi::Variable _bottom_margin;
PackOptions _primary_axis_pack_options;
PackOptions _secondary_axis_pack_options;
};
}
#endif

View file

@ -1,117 +0,0 @@
/*
* Copyright (C) 2020 Paul Davis <paul@linuxaudiosystems.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __CANVAS_CONSTRAINT_PACKER_H__
#define __CANVAS_CONSTRAINT_PACKER_H__
#include <list>
#include <map>
#include "canvas/container.h"
#include "kiwi/kiwi.h"
namespace ArdourCanvas
{
class Rectangle;
class ConstrainedItem;
class BoxConstrainedItem;
class LIBCANVAS_API ConstraintPacker : public Container
{
public:
ConstraintPacker (Canvas *, Orientation o = Horizontal);
ConstraintPacker (Item *, Orientation o = Horizontal);
void set_spacing (double s);
void set_padding (double top, double right = -1.0, double bottom = -1.0, double left = -1.0);
void set_margin (double top, double right = -1.0, double bottom = -1.0, double left = -1.0);
/* aliases so that CSS box model terms work */
void set_border_width (double w) { set_outline_width (w); }
void set_border_color (Gtkmm2ext::Color c) { set_outline_color (c); }
void add (Item *);
void add_front (Item *);
void remove (Item *);
void constrain (kiwi::Constraint const &);
BoxConstrainedItem* pack_start (Item*, PackOptions primary_axis_packing = PackOptions (0), PackOptions secondary_axis_packing = PackOptions (PackExpand|PackFill));
BoxConstrainedItem* pack_end (Item*, PackOptions primary_axis_packing = PackOptions (0), PackOptions secondary_axis_packing = PackOptions (PackExpand|PackFill));
virtual ConstrainedItem* add_constrained (Item* item);
void solve ();
void apply (kiwi::Solver*);
void compute_bounding_box () const;
void _size_allocate (Rect const &);
void size_request (Distance& w, Distance& h) const;
void render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const;
kiwi::Variable width;
kiwi::Variable height;
protected:
void child_changed (bool bbox_changed);
Orientation _orientation;
double _spacing;
double _top_padding;
double _bottom_padding;
double _left_padding;
double _right_padding;
double _top_margin;
double _bottom_margin;
double _left_margin;
double _right_margin;
kiwi::Variable expanded_item_size;
typedef std::map<Item*,ConstrainedItem*> ConstrainedItemMap;
ConstrainedItemMap constrained_map;
typedef std::list<kiwi::Constraint> ConstraintList;
ConstraintList constraint_list;
kiwi::Solver _solver;
bool in_alloc;
bool _need_constraint_update;
void add_constrained_internal (Item*, ConstrainedItem*);
void add_constraints (kiwi::Solver&, ConstrainedItem*) const;
void non_const_size_request (Distance& w, Distance& h);
virtual void update_constraints ();
void add_vertical_box_constraints (kiwi::Solver& solver, BoxConstrainedItem* ci, BoxConstrainedItem* prev, double main_dimenion, double second_dimension, kiwi::Variable& alloc_var);
void add_horizontal_box_constraints (kiwi::Solver& solver, BoxConstrainedItem* ci, BoxConstrainedItem* prev, double main_dimenion, double second_dimension, kiwi::Variable& alloc_var);
private:
typedef std::list<BoxConstrainedItem*> BoxPackedItems;
BoxPackedItems packed;
BoxConstrainedItem* pack (Item*, PackOptions primary_axis_packing, PackOptions secondary_axis_packing);
void box_size_request (Distance& w, Distance& h) const;
};
}
#endif

View file

@ -1,273 +0,0 @@
/*
* Copyright (C) 2020 Paul Davis <paul@linuxaudiosystems.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <iostream>
#include "canvas/item.h"
#include "canvas/types.h"
#include "canvas/constrained_item.h"
using namespace ArdourCanvas;
using std::cerr;
using std::endl;
using kiwi::Constraint;
using kiwi::Variable;
ConstrainedItem::ConstrainedItem (Item& i)
: _item (i)
, _left (_item.name + " left")
, _right (_item.name + " right")
, _top (_item.name + " top")
, _bottom (_item.name + " bottom")
, _width (_item.name + " width")
, _height (_item.name + " height")
, _left_padding (_item.name + " left_padding")
, _right_padding (_item.name + " right_padding")
, _top_padding (_item.name + " top_padding")
, _bottom_padding (_item.name + " bottom_padding")
, _center_x (_item.name + " center_x")
, _center_y (_item.name + " center_y")
{
/* setup center_{x,y} variables in case calling/using
* code wants to use them for additional constraints
*/
_constraints.push_back (center_x() == left() + (width() / 2.));
_constraints.push_back (center_y() == top() + (height() / 2.));
_constraints.push_back (_right == _left + _width);
_constraints.push_back (_bottom == _top + _height);
}
ConstrainedItem::~ConstrainedItem ()
{
}
void
ConstrainedItem::constrained (ConstraintPacker const & parent)
{
/* our variables should be set. Deliver computed size to item */
Rect r (_left.value(), _top.value(), _right.value(), _bottom.value());
//dump (cerr);
// cerr << _item.whoami() << " constrained-alloc " << r << endl;
_item.size_allocate (r);
}
void
ConstrainedItem::dump (std::ostream& out)
{
out << _item.name << " value dump:\n"
<< '\t' << "left: " << _left.value() << '\n'
<< '\t' << "right: " << _right.value() << '\n'
<< '\t' << "top: " << _top.value() << '\n'
<< '\t' << "bottom: " << _bottom.value() << '\n'
<< '\t' << "width: " << _width.value() << '\n'
<< '\t' << "height: " << _height.value() << '\n'
<< '\t' << "right_padding: " << _right_padding.value() << '\n'
<< '\t' << "left_padding: " << _left_padding.value() << '\n'
<< '\t' << "top_padding: " << _top_padding.value() << '\n'
<< '\t' << "bottom_padding: " << _bottom_padding.value() << '\n'
<< '\t' << "center_x: " << _center_x.value() << '\n'
<< '\t' << "center_y: " << _center_y.value() << '\n';
}
bool
ConstrainedItem::involved (Constraint const & c) const
{
if (c.involves (_left) ||
c.involves (_right) ||
c.involves (_top) ||
c.involves (_bottom) ||
c.involves (_width) ||
c.involves (_height) ||
c.involves (_center_x) ||
c.involves (_center_y)) {
return true;
}
return false;
}
/*** BoxConstrainedItem */
BoxConstrainedItem::BoxConstrainedItem (Item& parent, PackOptions primary_axis_opts, PackOptions secondary_axis_opts)
: ConstrainedItem (parent)
, _left_margin (_item.name + " left_margin")
, _right_margin (_item.name + " right_margin")
, _top_margin (_item.name + " top_margin")
, _bottom_margin (_item.name + " bottom_margin")
, _primary_axis_pack_options (primary_axis_opts)
, _secondary_axis_pack_options (secondary_axis_opts)
{
}
BoxConstrainedItem::~BoxConstrainedItem ()
{
}
bool
BoxConstrainedItem::involved (Constraint const & c) const
{
if (ConstrainedItem::involved (c)) {
return true;
}
if (c.involves (_left_margin) ||
c.involves (_right_margin) ||
c.involves (_top_margin) ||
c.involves (_bottom_margin)) {
return true;
}
return false;
}
void
BoxConstrainedItem::dump (std::ostream& out)
{
ConstrainedItem::dump (out);
out << '\t' << "left_margin: " << _left_margin.value() << '\n'
<< '\t' << "right_margin: " << _right_margin.value() << '\n'
<< '\t' << "top_margin: " << _top_margin.value() << '\n'
<< '\t' << "bottom_margin: " << _bottom_margin.value() << '\n';
}
ConstrainedItem&
ConstrainedItem::at (Duple const & d)
{
_constraints.push_back (_left == d.x);
_constraints.push_back (_top == d.y);
return *this;
}
ConstrainedItem&
ConstrainedItem::size (Duple const & d)
{
_constraints.push_back (_width == d.x);
_constraints.push_back (_height == d.y);
return *this;
}
ConstrainedItem&
ConstrainedItem::box (Rect const & r)
{
_constraints.push_back (_left == r.x0);
_constraints.push_back (_top == r.y0);
_constraints.push_back (_width == r.width());
_constraints.push_back (_height == r.height());
return *this;
}
ConstrainedItem&
ConstrainedItem::left_of (ConstrainedItem const & other, Distance by)
{
_constraints.push_back (_right_padding == by);
_constraints.push_back (_right == other.left() + _right_padding);
return *this;
}
ConstrainedItem&
ConstrainedItem::right_of (ConstrainedItem const & other, Distance by)
{
_constraints.push_back (_left_padding == by);
_constraints.push_back (_left == other.right() + _left_padding);
return *this;
}
ConstrainedItem&
ConstrainedItem::above (ConstrainedItem const & other, Distance by)
{
_constraints.push_back (_bottom_padding == by);
_constraints.push_back (_bottom == other.top() + _bottom_padding);
return *this;
}
ConstrainedItem&
ConstrainedItem::below (ConstrainedItem const & other, Distance by)
{
_constraints.push_back (_top_padding == by);
_constraints.push_back (_top == other.bottom() + _top_padding);
return *this;
}
ConstrainedItem&
ConstrainedItem::centered_on (ConstrainedItem const & other, Distance xoffset, Distance yoffset)
{
_constraints.push_back (_center_x == other.center_x() + xoffset);
_constraints.push_back (_center_y == other.center_y() + yoffset);
return *this;
}
ConstrainedItem&
ConstrainedItem::top_aligned_with (ConstrainedItem const & other, Distance offset)
{
_constraints.push_back (_top == other.top() + offset);
return *this;
}
ConstrainedItem&
ConstrainedItem::bottom_aligned_with (ConstrainedItem const & other, Distance offset)
{
_constraints.push_back (_bottom == other.bottom() + offset);
return *this;
}
ConstrainedItem&
ConstrainedItem::left_aligned_with (ConstrainedItem const & other, Distance offset)
{
_constraints.push_back (_left == other.left() + offset);
return *this;
}
ConstrainedItem&
ConstrainedItem::right_aligned_with (ConstrainedItem const & other, Distance offset)
{
_constraints.push_back (_right == other.right() + offset);
return *this;
}
ConstrainedItem&
ConstrainedItem::same_size_as (ConstrainedItem const & other, Distance wdelta, Distance hdelta)
{
_constraints.push_back (_width == other.width() + wdelta);
_constraints.push_back (_height == other.height() + hdelta);
return *this;
}
ConstrainedItem&
ConstrainedItem::same_width_as (ConstrainedItem const & other, Distance delta)
{
_constraints.push_back (_width == other.width() + delta);
return *this;
}
ConstrainedItem&
ConstrainedItem::same_height_as (ConstrainedItem const & other, Distance delta)
{
_constraints.push_back (_height == other.height() + delta);
return *this;
}

View file

@ -1,769 +0,0 @@
/*
* Copyright (C) 2020 Paul Davis <paul@linuxaudiosystems.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <iostream>
#include "pbd/i18n.h"
#include "pbd/unwind.h"
#include "pbd/stacktrace.h"
#include "kiwi/kiwi.h"
#include "canvas/canvas.h"
#include "canvas/constraint_packer.h"
#include "canvas/constrained_item.h"
#include "canvas/item.h"
#include "canvas/rectangle.h"
using namespace ArdourCanvas;
using std::cerr;
using std::endl;
using std::vector;
using kiwi::Constraint;
using namespace kiwi;
ConstraintPacker::ConstraintPacker (Canvas* canvas, Orientation o)
: Container (canvas)
, width (X_("packer width"))
, height (X_("packer height"))
, _orientation (o)
, _spacing (0)
, _top_padding (0)
, _bottom_padding (0)
, _left_padding (0)
, _right_padding (0)
, _top_margin (0)
, _bottom_margin (0)
, _left_margin (0)
, _right_margin (0)
, in_alloc (false)
, _need_constraint_update (false)
{
set_fill (false);
set_outline (false);
set_layout_sensitive (true);
_solver.addEditVariable (width, kiwi::strength::strong);
_solver.addEditVariable (height, kiwi::strength::strong);
}
ConstraintPacker::ConstraintPacker (Item* parent, Orientation o)
: Container (parent)
, width (X_("packer width"))
, height (X_("packer height"))
, _orientation (o)
, _spacing (0)
, _top_padding (0)
, _bottom_padding (0)
, _left_padding (0)
, _right_padding (0)
, _top_margin (0)
, _bottom_margin (0)
, _left_margin (0)
, _right_margin (0)
, in_alloc (false)
, _need_constraint_update (false)
{
set_fill (false);
set_outline (false);
set_layout_sensitive (true);
_solver.addEditVariable (width, kiwi::strength::strong);
_solver.addEditVariable (height, kiwi::strength::strong);
}
void
ConstraintPacker::compute_bounding_box () const
{
_bounding_box = _allocation;
bb_clean ();
}
void
ConstraintPacker::child_changed (bool bbox_changed)
{
Item::child_changed (bbox_changed);
if (in_alloc || !bbox_changed) {
return;
}
#if 0
cerr << "CP, child bbox changed\n";
for (ConstrainedItemMap::iterator x = constrained_map.begin(); x != constrained_map.end(); ++x) {
Duple i;
x->first->size_request (i.x, i.y);
if (r) {
// cerr << x->first->whatami() << '/' << x->first->name << " has instrinsic size " << r << endl;
kiwi::Variable& w (x->second->intrinsic_width());
if (!r.width()) {
if (_solver.hasEditVariable (w)) {
_solver.removeEditVariable (w);
cerr << "\tremoved inttrinsic-width edit var\n";
}
} else {
if (!_solver.hasEditVariable (w)) {
cerr << "\tadding intrinsic width constraints\n";
_solver.addEditVariable (w, kiwi::strength::strong);
_solver.addConstraint (Constraint {x->second->width() >= w } | kiwi::strength::strong);
_solver.addConstraint (Constraint (x->second->width() <= w) | kiwi::strength::weak);
}
}
kiwi::Variable& h (x->second->intrinsic_height());
if (!r.height()) {
if (_solver.hasEditVariable (h)) {
_solver.removeEditVariable (h);
cerr << "\tremoved inttrinsic-height edit var\n";
}
} else {
if (!_solver.hasEditVariable (h)) {
cerr << "\tadding intrinsic height constraints\n";
_solver.addEditVariable (h, kiwi::strength::strong);
_solver.addConstraint (Constraint {x->second->height() >= h } | kiwi::strength::strong);
_solver.addConstraint (Constraint (x->second->height() <= h) | kiwi::strength::weak);
}
}
}
}
#endif
}
void
ConstraintPacker::constrain (kiwi::Constraint const &c)
{
constraint_list.push_back (c);
_need_constraint_update = true;
}
void
ConstraintPacker::box_size_request (Distance& w, Distance& h) const
{
BoxPackedItems::size_type n_expanding = 0;
BoxPackedItems::size_type n_nonexpanding = 0;
BoxPackedItems::size_type total = 0;
Distance non_expanding_used = 0;
Distance largest = 0;
Distance largest_opposite = 0;
Distance width;
Distance height;
for (BoxPackedItems::const_iterator o = packed.begin(); o != packed.end(); ++o) {
(*o)->item().size_request (width, height);
// cerr << '\t' << (*o)->item().whoami() << " min " << i_min << " nat " << i_natural << endl;
if ((*o)->primary_axis_pack_options() & PackExpand) {
n_expanding++;
if (_orientation == Vertical) {
if (height > largest) {
largest = height;
}
} else {
if (width > largest) {
largest = width;;
}
if (height > largest) {
largest_opposite = height;
}
}
} else {
n_nonexpanding++;
if (_orientation == Vertical) {
non_expanding_used += height;
} else {
non_expanding_used += width;
}
}
/* determine the maximum size for the opposite axis. All items
* will be this size or less on this axis
*/
if (_orientation == Vertical) {
if (width > largest_opposite) {
largest_opposite = width;
}
} else {
if (height > largest_opposite) {
largest_opposite = height;
}
}
total++;
}
Duple r;
if (_orientation == Vertical) {
// cerr << "+++ vertical box, neu = " << non_expanding_used << " neuo " << non_expanding_used_opposite << " largest = " << largest << " opp " << largest_opposite << " total " << total << endl;
height = non_expanding_used + (n_expanding * largest) + _top_margin + _bottom_margin + ((total - 1) * _spacing);
width= largest_opposite + _left_margin + _right_margin;
} else {
// cerr << "+++ horiz box, neu = " << non_expanding_used << " neuo " << non_expanding_used_opposite << " largest = " << largest << " opp " << largest_opposite << " total " << total << endl;
width = non_expanding_used + (n_expanding * largest) + _left_margin + _right_margin + ((total - 1) * _spacing);
height = largest_opposite + _top_margin + _bottom_margin;
}
}
void
ConstraintPacker::size_request (Distance& w, Distance& h) const
{
const_cast<ConstraintPacker*>(this)->non_const_size_request (w, h);
}
void
ConstraintPacker::non_const_size_request (Distance& w, Distance& h)
{
/* our parent wants to know how big we are.
We may have some intrinsic size (i.e. "everything in this constraint
layout should fit into WxH". Just add two constraints on our width
and height, and solve.
We may have one intrinsic dimension (i.e. "everything in this
constraint layout should fit into this (width|height). Ask all of
our children for the size-given-(W|H). Add constraints to represent
those values, and solve.
We may have no intrinsic dimensions at all. This is the tricky one.
*/
if (packed.size() == constrained_map.size()) {
/* All child items were packed using ::pack() */
box_size_request (w, h);
return;
}
if (_requested_width < 0 && _requested_height < 0) {
w = 100;
h = 100;
return;
}
if (_need_constraint_update) {
const_cast<ConstraintPacker*>(this)->update_constraints ();
}
if (_requested_width > 0) {
_solver.suggestValue (width, _requested_width);
} else if (_requested_height > 0) {
_solver.suggestValue (height, _requested_height);
}
_solver.updateVariables ();
apply (0);
Rect bb (bounding_box());
w = std::max (bb.width(), _requested_width);
h = std::max (bb.height(), _requested_width);
/* put solver back to default state */
_solver.reset ();
_need_constraint_update = true;
}
void
ConstraintPacker::_size_allocate (Rect const & r)
{
PBD::Unwinder<bool> uw (in_alloc, true);
double expanded_size;
if (_layout_sensitive) {
_position = Duple (r.x0, r.y0);
_allocation = r;
}
if (!packed.empty()) {
BoxPackedItems::size_type n_expanding = 0;
BoxPackedItems::size_type n_nonexpanding = 0;
BoxPackedItems::size_type total = 0;
Distance non_expanding_used = 0;
for (BoxPackedItems::iterator o = packed.begin(); o != packed.end(); ++o) {
if ((*o)->primary_axis_pack_options() & PackExpand) {
n_expanding++;
} else {
n_nonexpanding++;
Distance w, h;
(*o)->item().size_request (w, h);
if (_orientation == Vertical) {
non_expanding_used += h;
} else {
non_expanding_used += w;
}
}
total++;
}
if (_orientation == Vertical) {
expanded_size = (r.height() - _top_margin - _bottom_margin - ((total - 1) * _spacing) - non_expanding_used) / n_expanding;
} else {
expanded_size = (r.width() - _left_margin - _right_margin - ((total - 1) * _spacing) - non_expanding_used) / n_expanding;
}
}
if (_need_constraint_update) {
update_constraints ();
}
_solver.suggestValue (width, r.width());
_solver.suggestValue (height, r.height());
if (!packed.empty()) {
_solver.suggestValue (expanded_item_size, expanded_size);
}
_solver.updateVariables ();
#if 0
// PBD::stacktrace (cerr, 100);
_canvas->dump (cerr);
_solver.dump (cerr);
for (ConstrainedItemMap::const_iterator o = constrained_map.begin(); o != constrained_map.end(); ++o) {
o->second->dump (cerr);
}
#endif
apply (0);
_bounding_box_dirty = true;
}
void
ConstraintPacker::add (Item* item)
{
(void) add_constrained (item);
}
void
ConstraintPacker::add_front (Item* item)
{
(void) add_constrained (item);
}
void
ConstraintPacker::add_constraints (Solver& s, ConstrainedItem* ci) const
{
/* add any constraints inherent to this item */
vector<Constraint> const & vc (ci->constraints());
for (vector<Constraint>::const_iterator x = vc.begin(); x != vc.end(); ++x) {
s.addConstraint (*x);
}
}
ConstrainedItem*
ConstraintPacker::add_constrained (Item* item)
{
ConstrainedItem* ci = new ConstrainedItem (*item);
add_constrained_internal (item, ci);
return ci;
}
void
ConstraintPacker::add_constrained_internal (Item* item, ConstrainedItem* ci)
{
Item::add (item);
item->set_layout_sensitive (true);
constrained_map.insert (std::make_pair (item, ci));
_need_constraint_update = true;
child_changed (true);
}
void
ConstraintPacker::remove (Item* item)
{
Item::remove (item);
for (ConstrainedItemMap::iterator x = constrained_map.begin(); x != constrained_map.end(); ++x) {
if (x->first == item) {
/* remove any non-builtin constraints for this item */
for (ConstraintList::iterator c = constraint_list.begin(); c != constraint_list.end(); ++c) {
if (x->second->involved (*c)) {
constraint_list.erase (c);
}
}
item->set_layout_sensitive (false);
/* clean up */
delete x->second;
constrained_map.erase (x);
break;
}
}
for (BoxPackedItems::iterator t = packed.begin(); t != packed.end(); ++t) {
if (&(*t)->item() == item) {
packed.erase (t);
break;
}
}
_need_constraint_update = true;
}
void
ConstraintPacker::apply (Solver* s)
{
for (ConstrainedItemMap::iterator x = constrained_map.begin(); x != constrained_map.end(); ++x) {
x->second->constrained (*this);
}
}
void
ConstraintPacker::update_constraints ()
{
_solver.reset ();
_solver.addEditVariable (width, kiwi::strength::strong);
_solver.addEditVariable (height, kiwi::strength::strong);
if (!packed.empty()) {
_solver.addEditVariable (expanded_item_size, kiwi::strength::strong);
}
try {
/* First handle box-packed items */
BoxPackedItems::iterator prev = packed.end();
for (BoxPackedItems::iterator o = packed.begin(); o != packed.end(); ++o) {
Distance w, h;
(*o)->item().size_request (w,h);
if (_orientation == Vertical) {
add_vertical_box_constraints (_solver, *o, prev == packed.end() ? 0 : *prev, h, w, width);
} else {
add_horizontal_box_constraints (_solver, *o, prev == packed.end() ? 0 : *prev, w, h, height);
}
prev = o;
}
/* Now handle all other items (exclude those already dealt with */
for (ConstrainedItemMap::iterator x = constrained_map.begin(); x != constrained_map.end(); ++x) {
if (std::find (packed.begin(), packed.end(), x->second) != packed.end()) {
add_constraints (_solver, x->second);
continue;
}
Distance w, h;
ConstrainedItem* ci = x->second;
x->first->size_request (w, h);
_solver.addConstraint ((ci->width() == w) | kiwi::strength::medium);
_solver.addConstraint ((ci->height() == h) | kiwi::strength::medium);
add_constraints (_solver, ci);
}
/* Now add packer-level constraints */
for (ConstraintList::const_iterator c = constraint_list.begin(); c != constraint_list.end(); ++c) {
_solver.addConstraint (*c);
}
_need_constraint_update = false;
} catch (std::exception& e) {
cerr << "Setting up sovler failed: " << e.what() << endl;
}
}
BoxConstrainedItem*
ConstraintPacker::pack_start (Item* item, PackOptions primary_axis_opts, PackOptions secondary_axis_opts)
{
return pack (item, PackOptions (primary_axis_opts|PackFromStart), secondary_axis_opts);
}
BoxConstrainedItem*
ConstraintPacker::pack_end (Item* item, PackOptions primary_axis_opts, PackOptions secondary_axis_opts)
{
return pack (item, PackOptions (primary_axis_opts|PackFromEnd), secondary_axis_opts);
}
BoxConstrainedItem*
ConstraintPacker::pack (Item* item, PackOptions primary_axis_opts, PackOptions secondary_axis_opts)
{
BoxConstrainedItem* ci = new BoxConstrainedItem (*item, primary_axis_opts, secondary_axis_opts);
add_constrained_internal (item, ci);
packed.push_back (ci);
return ci;
}
/* It would be nice to do this with templates or even by passing ptr-to-method,
* but both of them interfere with the similarly meta-programming-ish nature of
* the way that kiwi builds Constraint objects from expressions. So a macro it
* is ...
*/
#define add_box_constraints(\
solver, \
bci, \
prev, \
natural_main_dimension, \
natural_second_dimension, \
alloc_var, \
m_main_dimension, \
m_second_dimension, \
m_trailing, \
m_leading, \
m_trailing_padding, \
m_leading_padding, \
m_second_trailing, \
m_second_leading, \
m_second_trailing_padding, \
m_second_leading_padding, \
m_trailing_margin, \
m_leading_margin, \
m_second_trailing_margin, \
m_second_leading_margin) \
\
/* Add constraints that will size the item within this box */ \
\
/* set up constraints for expand/fill options, done by \
* adjusting height and margins of each item \
*/ \
\
if (bci->primary_axis_pack_options() & PackExpand) { \
\
/* item will take up more than it's natural \
* size, if space is available \
*/ \
\
if (bci->primary_axis_pack_options() & PackFill) { \
\
/* item is expanding to fill all \
* available space and wants that space \
* for itself. \
*/ \
\
solver.addConstraint ({(bci->m_main_dimension() == expanded_item_size) | kiwi::strength::strong}); \
solver.addConstraint ({(bci->m_trailing_padding() == 0. ) | kiwi::strength::strong}); \
solver.addConstraint ({(bci->m_leading_padding() == 0. ) | kiwi::strength::strong}); \
\
} else { \
\
/* item is expanding to fill all \
* available space and wants that space \
* as padding \
*/ \
\
solver.addConstraint ({bci->m_main_dimension() == natural_main_dimension}); \
solver.addConstraint ({(bci->m_trailing_padding() + bci->m_leading_padding() + bci->m_main_dimension() == expanded_item_size) | kiwi::strength::strong}); \
solver.addConstraint ({(bci->m_leading_padding() == bci->m_trailing_padding()) | kiwi::strength::strong}); \
} \
\
} else { \
\
/* item is not going to expand to fill \
* available space. just give it's preferred \
* height. \
*/ \
\
/* cerr << bci->item().whoami() << " will usenatural height of " << natural.height() << endl; */ \
\
solver.addConstraint ({bci->m_main_dimension() == natural_main_dimension}); \
solver.addConstraint ({bci->m_trailing_padding() == 0.}); \
solver.addConstraint ({bci->m_leading_padding() == 0.}); \
} \
\
/* now set upper upper edge of the item */ \
\
if (prev == 0) { \
\
/* first item */ \
\
solver.addConstraint ({(bci->m_trailing() == m_trailing_margin + bci->m_trailing_padding()) | kiwi::strength::strong}); \
\
} else { \
/* subsequent items */ \
\
solver.addConstraint ({(bci->m_trailing() == prev->m_leading() + prev->m_leading_padding() + bci->m_trailing_padding() + _spacing) | kiwi::strength::strong}); \
} \
\
solver.addConstraint ({bci->m_leading() == bci->m_trailing() + bci->m_main_dimension()}); \
\
/* set the side-effect variables and/or constants */ \
\
solver.addConstraint ({(bci->m_second_trailing_padding() == 0) | kiwi::strength::weak}); \
solver.addConstraint ({(bci->m_second_leading_padding() == 0) | kiwi::strength::weak}); \
\
solver.addConstraint ({bci->m_second_trailing() + bci->m_second_dimension() == bci->m_second_leading()}); \
solver.addConstraint ({(bci->m_second_trailing() == m_second_trailing_margin + bci->m_second_trailing_padding()) | kiwi::strength::strong}); \
\
if (!(bci->secondary_axis_pack_options() & PackExpand) && natural_second_dimension > 0) { \
solver.addConstraint ({bci->m_second_dimension() == natural_second_dimension}); \
} else { \
solver.addConstraint ({(bci->m_second_dimension() == alloc_var - (m_second_trailing_margin + m_second_leading_margin + bci->m_second_leading_padding())) | kiwi::strength::strong}); \
}
void
ConstraintPacker::add_vertical_box_constraints (kiwi::Solver& solver, BoxConstrainedItem* ci, BoxConstrainedItem* prev, double main_dimension, double second_dimension, kiwi::Variable & alloc_var)
{
add_box_constraints (solver, ci, prev, main_dimension, second_dimension, alloc_var,
height, width,
top, bottom, top_padding, bottom_padding,
left, right, left_padding, right_padding,
_top_margin, _bottom_margin, _left_margin, _right_margin);
}
void
ConstraintPacker::add_horizontal_box_constraints (kiwi::Solver& solver, BoxConstrainedItem* ci, BoxConstrainedItem* prev, double main_dimension, double second_dimension, kiwi::Variable& alloc_var)
{
add_box_constraints (solver, ci, prev, main_dimension, second_dimension, alloc_var,
width, height,
left, right, left_padding, right_padding,
top, bottom, top_padding, bottom_padding,
_left_margin, _right_margin, _top_margin, _bottom_margin);
}
void
ConstraintPacker::set_spacing (double s)
{
_spacing = s;
}
void
ConstraintPacker::set_padding (double top, double right, double bottom, double left)
{
double last = top;
_top_padding = last;
if (right >= 0) {
last = right;
}
_right_padding = last;
if (bottom >= 0) {
last = bottom;
}
_bottom_padding = last;
if (left >= 0) {
last = left;
}
_left_padding = last;
}
void
ConstraintPacker::set_margin (double top, double right, double bottom, double left)
{
double last = top;
_top_margin = last;
if (right >= 0) {
last = right;
}
_right_margin = last;
if (bottom >= 0) {
last = bottom;
}
_bottom_margin = last;
if (left >= 0) {
last = left;
}
_left_margin = last;
}
void
ConstraintPacker::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
{
if ((fill() || outline()) && _allocation) {
Rect contents = _allocation;
/* allocation will have been left with (x0,y0) as given by the
* parent, but _position is set to the same value and will
* be taken into account by item_to_window()
*/
double width = contents.width() - (_left_margin + _top_margin);
double height = contents.height() - (_top_margin + _bottom_margin);
contents.x0 = _left_margin;
contents.y0 = _top_margin;
contents.x1 = contents.x0 + width;
contents.y1 = contents.y0 + height;
Rect self (item_to_window (contents, false));
const Rect draw = self.intersection (area);
if (fill()) {
setup_fill_context (context);
context->rectangle (draw.x0, draw.y0, draw.width(), draw.height());
if (outline()) {
context->fill_preserve ();
} else {
context->fill ();
}
}
if (outline()) {
if (!fill()) {
context->rectangle (draw.x0, draw.y0, draw.width(), draw.height());
}
setup_outline_context (context);
context->stroke ();
}
}
Item::render_children (area, context);
}

View file

@ -24,7 +24,6 @@
#include "pbd/convert.h"
#include "canvas/canvas.h"
#include "canvas/constraint_packer.h"
#include "canvas/debug.h"
#include "canvas/item.h"
#include "canvas/scroll_group.h"

View file

@ -1,356 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// The Loki Library
// Copyright (c) 2001 by Andrei Alexandrescu
// This code accompanies the book:
// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design
// Patterns Applied". Copyright (c) 2001. Addison-Wesley.
// Permission to use, copy, modify, distribute and sell this software for any
// purpose is hereby granted without fee, provided that the above copyright
// notice appear in all copies and that both that copyright notice and this
// permission notice appear in supporting documentation.
// The author or Addison-Wesley Longman make no representations about the
// suitability of this software for any purpose. It is provided "as is"
// without express or implied warranty.
////////////////////////////////////////////////////////////////////////////////
// Updated 2019 by Matthieu Dartiailh for C++11 compliancy
////////////////////////////////////////////////////////////////////////////////
#pragma once
// $Id: AssocVector.h 765 2006-10-18 13:55:32Z syntheticpp $
#include <algorithm>
#include <functional>
#include <vector>
#include <utility>
namespace Loki
{
////////////////////////////////////////////////////////////////////////////////
// class template AssocVectorCompare
// Used by AssocVector
////////////////////////////////////////////////////////////////////////////////
namespace Private
{
template <class Value, class C>
class AssocVectorCompare : public C
{
typedef std::pair<typename C::first_argument_type, Value>
Data;
typedef typename C::first_argument_type first_argument_type;
public:
AssocVectorCompare()
{}
AssocVectorCompare(const C& src) : C(src)
{}
bool operator()(const first_argument_type& lhs,
const first_argument_type& rhs) const
{ return C::operator()(lhs, rhs); }
bool operator()(const Data& lhs, const Data& rhs) const
{ return operator()(lhs.first, rhs.first); }
bool operator()(const Data& lhs,
const first_argument_type& rhs) const
{ return operator()(lhs.first, rhs); }
bool operator()(const first_argument_type& lhs,
const Data& rhs) const
{ return operator()(lhs, rhs.first); }
};
}
////////////////////////////////////////////////////////////////////////////////
// class template AssocVector
// An associative vector built as a syntactic drop-in replacement for std::map
// BEWARE: AssocVector doesn't respect all map's guarantees, the most important
// being:
// * iterators are invalidated by insert and erase operations
// * the complexity of insert/erase is O(N) not O(log N)
// * value_type is std::pair<K, V> not std::pair<const K, V>
// * iterators are random
////////////////////////////////////////////////////////////////////////////////
template
<
class K,
class V,
class C = std::less<K>,
class A = std::allocator< std::pair<K, V> >
>
class AssocVector
: private std::vector< std::pair<K, V>, A >
, private Private::AssocVectorCompare<V, C>
{
typedef std::vector<std::pair<K, V>, A> Base;
typedef Private::AssocVectorCompare<V, C> MyCompare;
public:
typedef K key_type;
typedef V mapped_type;
typedef typename Base::value_type value_type;
typedef C key_compare;
typedef A allocator_type;
typedef typename A::reference reference;
typedef typename A::const_reference const_reference;
typedef typename Base::iterator iterator;
typedef typename Base::const_iterator const_iterator;
typedef typename Base::size_type size_type;
typedef typename Base::difference_type difference_type;
typedef typename A::pointer pointer;
typedef typename A::const_pointer const_pointer;
typedef typename Base::reverse_iterator reverse_iterator;
typedef typename Base::const_reverse_iterator const_reverse_iterator;
class value_compare
: public std::function<bool(value_type, value_type)>
, private key_compare
{
friend class AssocVector;
protected:
value_compare(key_compare pred) : key_compare(pred)
{}
public:
bool operator()(const value_type& lhs, const value_type& rhs) const
{ return key_compare::operator()(lhs.first, rhs.first); }
};
// 23.3.1.1 construct/copy/destroy
explicit AssocVector(const key_compare& comp = key_compare(),
const A& alloc = A())
: Base(alloc), MyCompare(comp)
{}
template <class InputIterator>
AssocVector(InputIterator first, InputIterator last,
const key_compare& comp = key_compare(),
const A& alloc = A())
: Base(first, last, alloc), MyCompare(comp)
{
MyCompare& me = *this;
std::sort(begin(), end(), me);
}
AssocVector& operator=(const AssocVector& rhs)
{
AssocVector(rhs).swap(*this);
return *this;
}
// iterators:
// The following are here because MWCW gets 'using' wrong
iterator begin() { return Base::begin(); }
const_iterator begin() const { return Base::begin(); }
iterator end() { return Base::end(); }
const_iterator end() const { return Base::end(); }
reverse_iterator rbegin() { return Base::rbegin(); }
const_reverse_iterator rbegin() const { return Base::rbegin(); }
reverse_iterator rend() { return Base::rend(); }
const_reverse_iterator rend() const { return Base::rend(); }
// capacity:
bool empty() const { return Base::empty(); }
size_type size() const { return Base::size(); }
size_type max_size() { return Base::max_size(); }
// 23.3.1.2 element access:
mapped_type& operator[](const key_type& key)
{ return insert(value_type(key, mapped_type())).first->second; }
// modifiers:
std::pair<iterator, bool> insert(const value_type& val)
{
bool found(true);
iterator i(lower_bound(val.first));
if (i == end() || this->operator()(val.first, i->first))
{
i = Base::insert(i, val);
found = false;
}
return std::make_pair(i, !found);
}
//Section [23.1.2], Table 69
//http://developer.apple.com/documentation/DeveloperTools/gcc-3.3/libstdc++/23_containers/howto.html#4
iterator insert(iterator pos, const value_type& val)
{
if( (pos == begin() || this->operator()(*(pos-1),val)) &&
(pos == end() || this->operator()(val, *pos)) )
{
return Base::insert(pos, val);
}
return insert(val).first;
}
template <class InputIterator>
void insert(InputIterator first, InputIterator last)
{ for (; first != last; ++first) insert(*first); }
void erase(iterator pos)
{ Base::erase(pos); }
size_type erase(const key_type& k)
{
iterator i(find(k));
if (i == end()) return 0;
erase(i);
return 1;
}
void erase(iterator first, iterator last)
{ Base::erase(first, last); }
void swap(AssocVector& other)
{
Base::swap(other);
MyCompare& me = *this;
MyCompare& rhs = other;
std::swap(me, rhs);
}
void clear()
{ Base::clear(); }
// observers:
key_compare key_comp() const
{ return *this; }
value_compare value_comp() const
{
const key_compare& comp = *this;
return value_compare(comp);
}
// 23.3.1.3 map operations:
iterator find(const key_type& k)
{
iterator i(lower_bound(k));
if (i != end() && this->operator()(k, i->first))
{
i = end();
}
return i;
}
const_iterator find(const key_type& k) const
{
const_iterator i(lower_bound(k));
if (i != end() && this->operator()(k, i->first))
{
i = end();
}
return i;
}
size_type count(const key_type& k) const
{ return find(k) != end(); }
iterator lower_bound(const key_type& k)
{
MyCompare& me = *this;
return std::lower_bound(begin(), end(), k, me);
}
const_iterator lower_bound(const key_type& k) const
{
const MyCompare& me = *this;
return std::lower_bound(begin(), end(), k, me);
}
iterator upper_bound(const key_type& k)
{
MyCompare& me = *this;
return std::upper_bound(begin(), end(), k, me);
}
const_iterator upper_bound(const key_type& k) const
{
const MyCompare& me = *this;
return std::upper_bound(begin(), end(), k, me);
}
std::pair<iterator, iterator> equal_range(const key_type& k)
{
MyCompare& me = *this;
return std::equal_range(begin(), end(), k, me);
}
std::pair<const_iterator, const_iterator> equal_range(
const key_type& k) const
{
const MyCompare& me = *this;
return std::equal_range(begin(), end(), k, me);
}
template <class K1, class V1, class C1, class A1>
friend bool operator==(const AssocVector<K1, V1, C1, A1>& lhs,
const AssocVector<K1, V1, C1, A1>& rhs);
bool operator<(const AssocVector& rhs) const
{
const Base& me = *this;
const Base& yo = rhs;
return me < yo;
}
template <class K1, class V1, class C1, class A1>
friend bool operator!=(const AssocVector<K1, V1, C1, A1>& lhs,
const AssocVector<K1, V1, C1, A1>& rhs);
template <class K1, class V1, class C1, class A1>
friend bool operator>(const AssocVector<K1, V1, C1, A1>& lhs,
const AssocVector<K1, V1, C1, A1>& rhs);
template <class K1, class V1, class C1, class A1>
friend bool operator>=(const AssocVector<K1, V1, C1, A1>& lhs,
const AssocVector<K1, V1, C1, A1>& rhs);
template <class K1, class V1, class C1, class A1>
friend bool operator<=(const AssocVector<K1, V1, C1, A1>& lhs,
const AssocVector<K1, V1, C1, A1>& rhs);
};
template <class K, class V, class C, class A>
inline bool operator==(const AssocVector<K, V, C, A>& lhs,
const AssocVector<K, V, C, A>& rhs)
{
const std::vector<std::pair<K, V>, A>& me = lhs;
return me == rhs;
}
template <class K, class V, class C, class A>
inline bool operator!=(const AssocVector<K, V, C, A>& lhs,
const AssocVector<K, V, C, A>& rhs)
{ return !(lhs == rhs); }
template <class K, class V, class C, class A>
inline bool operator>(const AssocVector<K, V, C, A>& lhs,
const AssocVector<K, V, C, A>& rhs)
{ return rhs < lhs; }
template <class K, class V, class C, class A>
inline bool operator>=(const AssocVector<K, V, C, A>& lhs,
const AssocVector<K, V, C, A>& rhs)
{ return !(lhs < rhs); }
template <class K, class V, class C, class A>
inline bool operator<=(const AssocVector<K, V, C, A>& lhs,
const AssocVector<K, V, C, A>& rhs)
{ return !(rhs < lhs); }
// specialized algorithms:
template <class K, class V, class C, class A>
void swap(AssocVector<K, V, C, A>& lhs, AssocVector<K, V, C, A>& rhs)
{ lhs.swap(rhs); }
} // namespace Loki

View file

@ -1,148 +0,0 @@
/*-----------------------------------------------------------------------------
| Copyright (c) 2013-2017, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#pragma once
#include <ostream>
#include <map>
#include <vector>
#include "expression.h"
#include "shareddata.h"
#include "strength.h"
#include "term.h"
#include "variable.h"
namespace kiwi
{
enum RelationalOperator
{
OP_LE,
OP_GE,
OP_EQ
};
static std::ostream& operator<< (std::ostream& o, RelationalOperator op)
{
switch (op) {
case OP_LE:
o << "<=";
break;
case OP_GE:
o << ">=";
break;
case OP_EQ:
o << "==";
break;
}
return o;
}
class Constraint
{
public:
Constraint() : m_data(0) {}
Constraint(const Expression &expr,
RelationalOperator op,
double strength = strength::required) : m_data(new ConstraintData(expr, op, strength)) {}
Constraint(const Constraint &other, double strength) : m_data(new ConstraintData(other, strength)) {}
~Constraint() {}
const Expression &expression() const
{
return m_data->m_expression;
}
RelationalOperator op() const
{
return m_data->m_op;
}
double strength() const
{
return m_data->m_strength;
}
bool operator!() const
{
return !m_data;
}
bool involves (Variable const & v) const {
if (expression().involves (v)) {
return true;
}
return false;
}
private:
static Expression reduce(const Expression &expr)
{
std::map<Variable, double> vars;
typedef std::vector<Term>::const_iterator iter_t;
iter_t end = expr.terms().end();
for (iter_t it = expr.terms().begin(); it != end; ++it)
vars[it->variable()] += it->coefficient();
std::vector<Term> terms(vars.begin(), vars.end());
return Expression(terms, expr.constant());
}
class ConstraintData : public SharedData
{
public:
ConstraintData(const Expression &expr,
RelationalOperator op,
double strength) : SharedData(),
m_expression(reduce(expr)),
m_strength(strength::clip(strength)),
m_op(op) {}
ConstraintData(const Constraint &other, double strength) : SharedData(),
m_expression(other.expression()),
m_strength(strength::clip(strength)),
m_op(other.op()) {}
~ConstraintData() {}
Expression m_expression;
double m_strength;
RelationalOperator m_op;
private:
ConstraintData(const ConstraintData &other);
ConstraintData &operator=(const ConstraintData &other);
};
SharedDataPtr<ConstraintData> m_data;
friend bool operator<(const Constraint &lhs, const Constraint &rhs)
{
return lhs.m_data < rhs.m_data;
}
friend bool operator==(const Constraint &lhs, const Constraint &rhs)
{
return lhs.m_data == rhs.m_data;
}
friend bool operator!=(const Constraint &lhs, const Constraint &rhs)
{
return lhs.m_data != rhs.m_data;
}
};
static std::ostream& operator<< (std::ostream& o, kiwi::Constraint const & c)
{
return o << c.expression() << " OP " << c.op();
}
} // namespace kiwi

View file

@ -1,200 +0,0 @@
/*-----------------------------------------------------------------------------
| Copyright (c) 2013-2017, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#pragma once
#include <iostream>
#include <sstream>
#include <vector>
#include "constraint.h"
#include "solverimpl.h"
#include "term.h"
namespace kiwi
{
namespace impl
{
class DebugHelper
{
public:
static void dump(const SolverImpl &solver, std::ostream &out)
{
out << "Objective" << std::endl;
out << "---------" << std::endl;
dump(*solver.m_objective, out);
out << std::endl;
out << "Tableau" << std::endl;
out << "-------" << std::endl;
dump(solver.m_rows, out);
out << std::endl;
out << "Infeasible" << std::endl;
out << "----------" << std::endl;
dump(solver.m_infeasible_rows, out);
out << std::endl;
out << "Variables" << std::endl;
out << "---------" << std::endl;
dump(solver.m_vars, out);
out << std::endl;
out << "Edit Variables" << std::endl;
out << "--------------" << std::endl;
dump(solver.m_edits, out);
out << std::endl;
out << "Constraints" << std::endl;
out << "-----------" << std::endl;
dump(solver.m_cns, out);
out << std::endl;
out << std::endl;
}
static void dump(const SolverImpl::RowMap &rows, std::ostream &out)
{
typedef SolverImpl::RowMap::const_iterator iter_t;
iter_t end = rows.end();
for (iter_t it = rows.begin(); it != end; ++it)
{
dump(it->first, out);
out << " | ";
dump(*it->second, out);
}
}
static void dump(const std::vector<Symbol> &symbols, std::ostream &out)
{
typedef std::vector<Symbol>::const_iterator iter_t;
iter_t end = symbols.end();
for (iter_t it = symbols.begin(); it != end; ++it)
{
dump(*it, out);
out << std::endl;
}
}
static void dump(const SolverImpl::VarMap &vars, std::ostream &out)
{
typedef SolverImpl::VarMap::const_iterator iter_t;
iter_t end = vars.end();
for (iter_t it = vars.begin(); it != end; ++it)
{
out << it->first.name() << " = ";
dump(it->second, out);
out << std::endl;
}
}
static void dump(const SolverImpl::CnMap &cns, std::ostream &out)
{
typedef SolverImpl::CnMap::const_iterator iter_t;
iter_t end = cns.end();
for (iter_t it = cns.begin(); it != end; ++it)
dump(it->first, out);
}
static void dump(const SolverImpl::EditMap &edits, std::ostream &out)
{
typedef SolverImpl::EditMap::const_iterator iter_t;
iter_t end = edits.end();
for (iter_t it = edits.begin(); it != end; ++it)
out << it->first.name() << std::endl;
}
static void dump(const Row &row, std::ostream &out)
{
typedef Row::CellMap::const_iterator iter_t;
out << row.constant();
iter_t end = row.cells().end();
for (iter_t it = row.cells().begin(); it != end; ++it)
{
out << " + " << it->second << " * ";
dump(it->first, out);
}
out << std::endl;
}
static void dump(const Symbol &symbol, std::ostream &out)
{
switch (symbol.type())
{
case Symbol::Invalid:
out << "i";
break;
case Symbol::External:
out << "v";
break;
case Symbol::Slack:
out << "s";
break;
case Symbol::Error:
out << "e";
break;
case Symbol::Dummy:
out << "d";
break;
default:
break;
}
out << symbol.id();
}
static void dump(const Constraint &cn, std::ostream &out)
{
typedef std::vector<Term>::const_iterator iter_t;
iter_t begin = cn.expression().terms().begin();
iter_t end = cn.expression().terms().end();
for (iter_t it = begin; it != end; ++it)
{
out << it->coefficient() << " * ";
out << it->variable().name() << " + ";
}
out << cn.expression().constant();
switch (cn.op())
{
case OP_LE:
out << " <= 0 ";
break;
case OP_GE:
out << " >= 0 ";
break;
case OP_EQ:
out << " == 0 ";
break;
default:
break;
}
out << " | strength = " << cn.strength() << std::endl;
}
};
} // namespace impl
namespace debug
{
template <typename T>
void dump(const T &value)
{
impl::DebugHelper::dump(value, std::cout);
}
template <typename T>
void dump(const T &value, std::ostream &out)
{
impl::DebugHelper::dump(value, out);
}
template <typename T>
std::string dumps(const T &value)
{
std::stringstream stream;
impl::DebugHelper::dump(value, stream);
return stream.str();
}
} // namespace debug
} // namespace kiwi

View file

@ -1,162 +0,0 @@
/*-----------------------------------------------------------------------------
| Copyright (c) 2013-2017, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#pragma once
#include <exception>
#include <string>
#include "constraint.h"
#include "variable.h"
namespace kiwi
{
class UnsatisfiableConstraint : public std::exception
{
public:
UnsatisfiableConstraint(const Constraint &constraint) : m_constraint(constraint) {}
~UnsatisfiableConstraint() throw() {}
const char *what() const throw()
{
return "The constraint can not be satisfied.";
}
const Constraint &constraint() const
{
return m_constraint;
}
private:
Constraint m_constraint;
};
class UnknownConstraint : public std::exception
{
public:
UnknownConstraint(const Constraint &constraint) : m_constraint(constraint) {}
~UnknownConstraint() throw() {}
const char *what() const throw()
{
return "The constraint has not been added to the solver.";
}
const Constraint &constraint() const
{
return m_constraint;
}
private:
Constraint m_constraint;
};
class DuplicateConstraint : public std::exception
{
public:
DuplicateConstraint(const Constraint &constraint) : m_constraint(constraint) {}
~DuplicateConstraint() throw() {}
const char *what() const throw()
{
return "The constraint has already been added to the solver.";
}
const Constraint &constraint() const
{
return m_constraint;
}
private:
Constraint m_constraint;
};
class UnknownEditVariable : public std::exception
{
public:
UnknownEditVariable(const Variable &variable) : m_variable(variable) {}
~UnknownEditVariable() throw() {}
const char *what() const throw()
{
return "The edit variable has not been added to the solver.";
}
const Variable &variable() const
{
return m_variable;
}
private:
Variable m_variable;
};
class DuplicateEditVariable : public std::exception
{
public:
DuplicateEditVariable(const Variable &variable) : m_variable(variable) {}
~DuplicateEditVariable() throw() {}
const char *what() const throw()
{
return "The edit variable has already been added to the solver.";
}
const Variable &variable() const
{
return m_variable;
}
private:
Variable m_variable;
};
class BadRequiredStrength : public std::exception
{
public:
BadRequiredStrength() {}
~BadRequiredStrength() throw() {}
const char *what() const throw()
{
return "A required strength cannot be used in this context.";
}
};
class InternalSolverError : public std::exception
{
public:
InternalSolverError() : m_msg("An internal solver error ocurred.") {}
InternalSolverError(const char *msg) : m_msg(msg) {}
InternalSolverError(const std::string &msg) : m_msg(msg) {}
~InternalSolverError() throw() {}
const char *what() const throw()
{
return m_msg.c_str();
}
private:
std::string m_msg;
};
} // namespace kiwi

View file

@ -1,72 +0,0 @@
/*-----------------------------------------------------------------------------
| Copyright (c) 2013-2017, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#pragma once
#include <ostream>
#include <vector>
#include "term.h"
namespace kiwi
{
class Expression
{
public:
Expression(double constant = 0.0) : m_constant(constant) {}
Expression(const Term &term, double constant = 0.0) : m_terms(1, term), m_constant(constant) {}
Expression(const std::vector<Term> &terms, double constant = 0.0) : m_terms(terms), m_constant(constant) {}
~Expression() {}
const std::vector<Term> &terms() const
{
return m_terms;
}
double constant() const
{
return m_constant;
}
double value() const
{
typedef std::vector<Term>::const_iterator iter_t;
double result = m_constant;
iter_t end = m_terms.end();
for (iter_t it = m_terms.begin(); it != end; ++it)
result += it->value();
return result;
}
bool involves (Variable const & v) const {
for (std::vector<Term>::const_iterator it = m_terms.begin(); it != m_terms.end(); ++it) {
if (it->variable().equals (v)) {
return true;
}
}
return false;
}
private:
std::vector<Term> m_terms;
double m_constant;
};
static std::ostream& operator<<(std::ostream& o, kiwi::Expression const &e)
{
o << e.constant() << " + ";
for (std::vector<kiwi::Term>::const_iterator it = e.terms().begin(); it != e.terms().end(); ++it) {
o << (*it) << ' ';
}
return o;
}
} // namespace kiwi

View file

@ -1,19 +0,0 @@
/*-----------------------------------------------------------------------------
| Copyright (c) 2013-2017, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#pragma once
#include "constraint.h"
#include "debug.h"
#include "errors.h"
#include "expression.h"
#include "shareddata.h"
#include "solver.h"
#include "strength.h"
#include "symbolics.h"
#include "term.h"
#include "variable.h"
#include "version.h"

View file

@ -1,37 +0,0 @@
/*-----------------------------------------------------------------------------
| Copyright (c) 2013-2019, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#pragma once
#include <functional>
#include <map>
#include <memory>
#include <utility>
#include "AssocVector.h"
namespace kiwi
{
namespace impl
{
template <
typename K,
typename V,
typename C = std::less<K>,
typename A = std::allocator<std::pair<K, V>>>
using MapType = Loki::AssocVector<K, V, C, A>;
// template<
// typename K,
// typename V,
// typename C = std::less<K>,
// typename A = std::allocator< std::pair<const K, V> > >
// using MapType = std::map<K, V, C, A>;
} // namespace impl
} // namespace kiwi

View file

@ -1,188 +0,0 @@
/*-----------------------------------------------------------------------------
| Copyright (c) 2013-2017, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#pragma once
#include "maptype.h"
#include "symbol.h"
#include "util.h"
namespace kiwi
{
namespace impl
{
class Row
{
public:
typedef MapType<Symbol, double> CellMap;
Row() : m_constant(0.0) {}
Row(double constant) : m_constant(constant) {}
Row(const Row &other) : m_cells(other.m_cells), m_constant(other.m_constant) {}
~Row() {}
const CellMap &cells() const
{
return m_cells;
}
double constant() const
{
return m_constant;
}
/* Add a constant value to the row constant.
The new value of the constant is returned.
*/
double add(double value)
{
return m_constant += value;
}
/* Insert a symbol into the row with a given coefficient.
If the symbol already exists in the row, the coefficient will be
added to the existing coefficient. If the resulting coefficient
is zero, the symbol will be removed from the row.
*/
void insert(const Symbol &symbol, double coefficient = 1.0)
{
if (nearZero(m_cells[symbol] += coefficient))
m_cells.erase(symbol);
}
/* Insert a row into this row with a given coefficient.
The constant and the cells of the other row will be multiplied by
the coefficient and added to this row. Any cell with a resulting
coefficient of zero will be removed from the row.
*/
void insert(const Row &other, double coefficient = 1.0)
{
typedef CellMap::const_iterator iter_t;
m_constant += other.m_constant * coefficient;
iter_t end = other.m_cells.end();
for (iter_t it = other.m_cells.begin(); it != end; ++it)
{
double coeff = it->second * coefficient;
if (nearZero(m_cells[it->first] += coeff))
m_cells.erase(it->first);
}
}
/* Remove the given symbol from the row.
*/
void remove(const Symbol &symbol)
{
CellMap::iterator it = m_cells.find(symbol);
if (it != m_cells.end())
m_cells.erase(it);
}
/* Reverse the sign of the constant and all cells in the row.
*/
void reverseSign()
{
typedef CellMap::iterator iter_t;
m_constant = -m_constant;
iter_t end = m_cells.end();
for (iter_t it = m_cells.begin(); it != end; ++it)
it->second = -it->second;
}
/* Solve the row for the given symbol.
This method assumes the row is of the form a * x + b * y + c = 0
and (assuming solve for x) will modify the row to represent the
right hand side of x = -b/a * y - c / a. The target symbol will
be removed from the row, and the constant and other cells will
be multiplied by the negative inverse of the target coefficient.
The given symbol *must* exist in the row.
*/
void solveFor(const Symbol &symbol)
{
typedef CellMap::iterator iter_t;
double coeff = -1.0 / m_cells[symbol];
m_cells.erase(symbol);
m_constant *= coeff;
iter_t end = m_cells.end();
for (iter_t it = m_cells.begin(); it != end; ++it)
it->second *= coeff;
}
/* Solve the row for the given symbols.
This method assumes the row is of the form x = b * y + c and will
solve the row such that y = x / b - c / b. The rhs symbol will be
removed from the row, the lhs added, and the result divided by the
negative inverse of the rhs coefficient.
The lhs symbol *must not* exist in the row, and the rhs symbol
*must* exist in the row.
*/
void solveFor(const Symbol &lhs, const Symbol &rhs)
{
insert(lhs, -1.0);
solveFor(rhs);
}
/* Get the coefficient for the given symbol.
If the symbol does not exist in the row, zero will be returned.
*/
double coefficientFor(const Symbol &symbol) const
{
CellMap::const_iterator it = m_cells.find(symbol);
if (it == m_cells.end())
return 0.0;
return it->second;
}
/* Substitute a symbol with the data from another row.
Given a row of the form a * x + b and a substitution of the
form x = 3 * y + c the row will be updated to reflect the
expression 3 * a * y + a * c + b.
If the symbol does not exist in the row, this is a no-op.
*/
void substitute(const Symbol &symbol, const Row &row)
{
typedef CellMap::iterator iter_t;
iter_t it = m_cells.find(symbol);
if (it != m_cells.end())
{
double coefficient = it->second;
m_cells.erase(it);
insert(row, coefficient);
}
}
private:
CellMap m_cells;
double m_constant;
};
} // namespace impl
} // namespace kiwi

View file

@ -1,151 +0,0 @@
/*-----------------------------------------------------------------------------
| Copyright (c) 2013-2017, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#pragma once
namespace kiwi
{
class SharedData
{
public:
SharedData() : m_refcount(0) {}
SharedData(const SharedData &other) : m_refcount(0) {}
int m_refcount;
private:
SharedData &operator=(const SharedData &other);
};
template <typename T>
class SharedDataPtr
{
public:
typedef T Type;
SharedDataPtr() : m_data(0) {}
explicit SharedDataPtr(T *data) : m_data(data)
{
incref(m_data);
}
~SharedDataPtr()
{
decref(m_data);
}
T *data()
{
return m_data;
}
const T *data() const
{
return m_data;
}
operator T *()
{
return m_data;
}
operator const T *() const
{
return m_data;
}
T *operator->()
{
return m_data;
}
const T *operator->() const
{
return m_data;
}
T &operator*()
{
return *m_data;
}
const T &operator*() const
{
return *m_data;
}
bool operator!() const
{
return !m_data;
}
bool operator<(const SharedDataPtr<T> &other) const
{
return m_data < other.m_data;
}
bool operator==(const SharedDataPtr<T> &other) const
{
return m_data == other.m_data;
}
bool operator!=(const SharedDataPtr<T> &other) const
{
return m_data != other.m_data;
}
SharedDataPtr(const SharedDataPtr<T> &other) : m_data(other.m_data)
{
incref(m_data);
}
SharedDataPtr<T> &operator=(const SharedDataPtr<T> &other)
{
if (m_data != other.m_data)
{
T *temp = m_data;
m_data = other.m_data;
incref(m_data);
decref(temp);
}
return *this;
}
SharedDataPtr<T> &operator=(T *other)
{
if (m_data != other)
{
T *temp = m_data;
m_data = other;
incref(m_data);
decref(temp);
}
return *this;
}
private:
static void incref(T *data)
{
if (data)
++data->m_refcount;
}
static void decref(T *data)
{
if (data && --data->m_refcount == 0)
delete data;
}
T *m_data;
};
} // namespace kiwi

View file

@ -1,178 +0,0 @@
/*-----------------------------------------------------------------------------
| Copyright (c) 2013-2017, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#pragma once
#include "constraint.h"
#include "debug.h"
#include "solverimpl.h"
#include "strength.h"
#include "variable.h"
namespace kiwi
{
class Solver
{
public:
Solver() {}
~Solver() {}
/* Add a constraint to the solver.
Throws
------
DuplicateConstraint
The given constraint has already been added to the solver.
UnsatisfiableConstraint
The given constraint is required and cannot be satisfied.
*/
void addConstraint( const Constraint& constraint )
{
m_impl.addConstraint( constraint );
}
/* Remove a constraint from the solver.
Throws
------
UnknownConstraint
The given constraint has not been added to the solver.
*/
void removeConstraint( const Constraint& constraint )
{
m_impl.removeConstraint( constraint );
}
/* Test whether a constraint has been added to the solver.
*/
bool hasConstraint( const Constraint& constraint ) const
{
return m_impl.hasConstraint( constraint );
}
/* Add an edit variable to the solver.
This method should be called before the `suggestValue` method is
used to supply a suggested value for the given edit variable.
Throws
------
DuplicateEditVariable
The given edit variable has already been added to the solver.
BadRequiredStrength
The given strength is >= required.
*/
void addEditVariable( const Variable& variable, double strength )
{
m_impl.addEditVariable( variable, strength );
}
/* Remove an edit variable from the solver.
Throws
------
UnknownEditVariable
The given edit variable has not been added to the solver.
*/
void removeEditVariable( const Variable& variable )
{
m_impl.removeEditVariable( variable );
}
/* Test whether an edit variable has been added to the solver.
*/
bool hasEditVariable( const Variable& variable ) const
{
return m_impl.hasEditVariable( variable );
}
/* Suggest a value for the given edit variable.
This method should be used after an edit variable as been added to
the solver in order to suggest the value for that variable. After
all suggestions have been made, the `solve` method can be used to
update the values of all variables.
Throws
------
UnknownEditVariable
The given edit variable has not been added to the solver.
*/
void suggestValue( const Variable& variable, double value )
{
m_impl.suggestValue( variable, value );
}
/* Update the values of the external solver variables.
*/
void updateVariables()
{
m_impl.updateVariables();
}
/* Reset the solver to the empty starting condition.
This method resets the internal solver state to the empty starting
condition, as if no constraints or edit variables have been added.
This can be faster than deleting the solver and creating a new one
when the entire system must change, since it can avoid unecessary
heap (de)allocations.
*/
void reset()
{
m_impl.reset();
}
/* Dump a representation of the solver internals to stdout.
*/
void dump()
{
debug::dump( m_impl );
}
/* Dump a representation of the solver internals to a stream.
*/
void dump( std::ostream& out )
{
debug::dump( m_impl, out );
}
/* Dump a representation of the solver internals to a string.
*/
std::string dumps()
{
return debug::dumps( m_impl );
}
private:
Solver( const Solver& );
Solver& operator=( const Solver& );
impl::SolverImpl m_impl;
};
} // namespace kiwi

View file

@ -1,840 +0,0 @@
/*-----------------------------------------------------------------------------
| Copyright (c) 2013-2017, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#pragma once
#include <algorithm>
#include <limits>
#include <memory>
#include <vector>
#include "constraint.h"
#include "errors.h"
#include "expression.h"
#include "maptype.h"
#include "row.h"
#include "symbol.h"
#include "term.h"
#include "util.h"
#include "variable.h"
namespace kiwi
{
namespace impl
{
class SolverImpl
{
friend class DebugHelper;
struct Tag
{
Symbol marker;
Symbol other;
};
struct EditInfo
{
Tag tag;
Constraint constraint;
double constant;
};
typedef MapType<Variable, Symbol> VarMap;
typedef MapType<Symbol, Row*> RowMap;
typedef MapType<Constraint, Tag> CnMap;
typedef MapType<Variable, EditInfo> EditMap;
struct DualOptimizeGuard
{
DualOptimizeGuard( SolverImpl& impl ) : m_impl( impl ) {}
~DualOptimizeGuard() { m_impl.dualOptimize(); }
SolverImpl& m_impl;
};
public:
SolverImpl() : m_objective( new Row() ), m_id_tick( 1 ) {}
~SolverImpl() { clearRows(); }
/* Add a constraint to the solver.
Throws
------
DuplicateConstraint
The given constraint has already been added to the solver.
UnsatisfiableConstraint
The given constraint is required and cannot be satisfied.
*/
void addConstraint( const Constraint& constraint )
{
if( m_cns.find( constraint ) != m_cns.end() )
throw DuplicateConstraint( constraint );
// Creating a row causes symbols to be reserved for the variables
// in the constraint. If this method exits with an exception,
// then its possible those variables will linger in the var map.
// Since its likely that those variables will be used in other
// constraints and since exceptional conditions are uncommon,
// i'm not too worried about aggressive cleanup of the var map.
Tag tag;
std::unique_ptr<Row> rowptr( createRow( constraint, tag ) );
Symbol subject( chooseSubject( *rowptr, tag ) );
// If chooseSubject could not find a valid entering symbol, one
// last option is available if the entire row is composed of
// dummy variables. If the constant of the row is zero, then
// this represents redundant constraints and the new dummy
// marker can enter the basis. If the constant is non-zero,
// then it represents an unsatisfiable constraint.
if( subject.type() == Symbol::Invalid && allDummies( *rowptr ) )
{
if( !nearZero( rowptr->constant() ) )
throw UnsatisfiableConstraint( constraint );
else
subject = tag.marker;
}
// If an entering symbol still isn't found, then the row must
// be added using an artificial variable. If that fails, then
// the row represents an unsatisfiable constraint.
if( subject.type() == Symbol::Invalid )
{
if( !addWithArtificialVariable( *rowptr ) )
throw UnsatisfiableConstraint( constraint );
}
else
{
rowptr->solveFor( subject );
substitute( subject, *rowptr );
m_rows[ subject ] = rowptr.release();
}
m_cns[ constraint ] = tag;
// Optimizing after each constraint is added performs less
// aggregate work due to a smaller average system size. It
// also ensures the solver remains in a consistent state.
optimize( *m_objective );
}
/* Remove a constraint from the solver.
Throws
------
UnknownConstraint
The given constraint has not been added to the solver.
*/
void removeConstraint( const Constraint& constraint )
{
CnMap::iterator cn_it = m_cns.find( constraint );
if( cn_it == m_cns.end() )
throw UnknownConstraint( constraint );
Tag tag( cn_it->second );
m_cns.erase( cn_it );
// Remove the error effects from the objective function
// *before* pivoting, or substitutions into the objective
// will lead to incorrect solver results.
removeConstraintEffects( constraint, tag );
// If the marker is basic, simply drop the row. Otherwise,
// pivot the marker into the basis and then drop the row.
RowMap::iterator row_it = m_rows.find( tag.marker );
if( row_it != m_rows.end() )
{
std::unique_ptr<Row> rowptr( row_it->second );
m_rows.erase( row_it );
}
else
{
row_it = getMarkerLeavingRow( tag.marker );
if( row_it == m_rows.end() )
throw InternalSolverError( "failed to find leaving row" );
Symbol leaving( row_it->first );
std::unique_ptr<Row> rowptr( row_it->second );
m_rows.erase( row_it );
rowptr->solveFor( leaving, tag.marker );
substitute( tag.marker, *rowptr );
}
// Optimizing after each constraint is removed ensures that the
// solver remains consistent. It makes the solver api easier to
// use at a small tradeoff for speed.
optimize( *m_objective );
}
/* Test whether a constraint has been added to the solver.
*/
bool hasConstraint( const Constraint& constraint ) const
{
return m_cns.find( constraint ) != m_cns.end();
}
/* Add an edit variable to the solver.
This method should be called before the `suggestValue` method is
used to supply a suggested value for the given edit variable.
Throws
------
DuplicateEditVariable
The given edit variable has already been added to the solver.
BadRequiredStrength
The given strength is >= required.
*/
void addEditVariable( const Variable& variable, double strength )
{
if( m_edits.find( variable ) != m_edits.end() )
throw DuplicateEditVariable( variable );
strength = strength::clip( strength );
if( strength == strength::required )
throw BadRequiredStrength();
Constraint cn( Expression( variable ), OP_EQ, strength );
addConstraint( cn );
EditInfo info;
info.tag = m_cns[ cn ];
info.constraint = cn;
info.constant = 0.0;
m_edits[ variable ] = info;
}
/* Remove an edit variable from the solver.
Throws
------
UnknownEditVariable
The given edit variable has not been added to the solver.
*/
void removeEditVariable( const Variable& variable )
{
EditMap::iterator it = m_edits.find( variable );
if( it == m_edits.end() )
throw UnknownEditVariable( variable );
removeConstraint( it->second.constraint );
m_edits.erase( it );
}
/* Test whether an edit variable has been added to the solver.
*/
bool hasEditVariable( const Variable& variable ) const
{
return m_edits.find( variable ) != m_edits.end();
}
/* Suggest a value for the given edit variable.
This method should be used after an edit variable as been added to
the solver in order to suggest the value for that variable.
Throws
------
UnknownEditVariable
The given edit variable has not been added to the solver.
*/
void suggestValue( const Variable& variable, double value )
{
EditMap::iterator it = m_edits.find( variable );
if( it == m_edits.end() )
throw UnknownEditVariable( variable );
DualOptimizeGuard guard( *this );
EditInfo& info = it->second;
double delta = value - info.constant;
info.constant = value;
// Check first if the positive error variable is basic.
RowMap::iterator row_it = m_rows.find( info.tag.marker );
if( row_it != m_rows.end() )
{
if( row_it->second->add( -delta ) < 0.0 )
m_infeasible_rows.push_back( row_it->first );
return;
}
// Check next if the negative error variable is basic.
row_it = m_rows.find( info.tag.other );
if( row_it != m_rows.end() )
{
if( row_it->second->add( delta ) < 0.0 )
m_infeasible_rows.push_back( row_it->first );
return;
}
// Otherwise update each row where the error variables exist.
RowMap::iterator end = m_rows.end();
for( row_it = m_rows.begin(); row_it != end; ++row_it )
{
double coeff = row_it->second->coefficientFor( info.tag.marker );
if( coeff != 0.0 &&
row_it->second->add( delta * coeff ) < 0.0 &&
row_it->first.type() != Symbol::External )
m_infeasible_rows.push_back( row_it->first );
}
}
/* Update the values of the external solver variables.
*/
void updateVariables()
{
typedef RowMap::iterator row_iter_t;
typedef VarMap::iterator var_iter_t;
row_iter_t row_end = m_rows.end();
var_iter_t var_end = m_vars.end();
for( var_iter_t var_it = m_vars.begin(); var_it != var_end; ++var_it )
{
Variable& var( const_cast<Variable&>( var_it->first ) );
row_iter_t row_it = m_rows.find( var_it->second );
if( row_it == row_end )
var.setValue( 0.0 );
else
var.setValue( row_it->second->constant() );
}
}
/* Reset the solver to the empty starting condition.
This method resets the internal solver state to the empty starting
condition, as if no constraints or edit variables have been added.
This can be faster than deleting the solver and creating a new one
when the entire system must change, since it can avoid unecessary
heap (de)allocations.
*/
void reset()
{
clearRows();
m_cns.clear();
m_vars.clear();
m_edits.clear();
m_infeasible_rows.clear();
m_objective.reset( new Row() );
m_artificial.reset();
m_id_tick = 1;
}
private:
SolverImpl( const SolverImpl& );
SolverImpl& operator=( const SolverImpl& );
struct RowDeleter
{
template<typename T>
void operator()( T& pair ) { delete pair.second; }
};
void clearRows()
{
std::for_each( m_rows.begin(), m_rows.end(), RowDeleter() );
m_rows.clear();
}
/* Get the symbol for the given variable.
If a symbol does not exist for the variable, one will be created.
*/
Symbol getVarSymbol( const Variable& variable )
{
VarMap::iterator it = m_vars.find( variable );
if( it != m_vars.end() )
return it->second;
Symbol symbol( Symbol::External, m_id_tick++ );
m_vars[ variable ] = symbol;
return symbol;
}
/* Create a new Row object for the given constraint.
The terms in the constraint will be converted to cells in the row.
Any term in the constraint with a coefficient of zero is ignored.
This method uses the `getVarSymbol` method to get the symbol for
the variables added to the row. If the symbol for a given cell
variable is basic, the cell variable will be substituted with the
basic row.
The necessary slack and error variables will be added to the row.
If the constant for the row is negative, the sign for the row
will be inverted so the constant becomes positive.
The tag will be updated with the marker and error symbols to use
for tracking the movement of the constraint in the tableau.
*/
Row* createRow( const Constraint& constraint, Tag& tag )
{
typedef std::vector<Term>::const_iterator iter_t;
const Expression& expr( constraint.expression() );
Row* row = new Row( expr.constant() );
// Substitute the current basic variables into the row.
iter_t end = expr.terms().end();
for( iter_t it = expr.terms().begin(); it != end; ++it )
{
if( !nearZero( it->coefficient() ) )
{
Symbol symbol( getVarSymbol( it->variable() ) );
RowMap::const_iterator row_it = m_rows.find( symbol );
if( row_it != m_rows.end() )
row->insert( *row_it->second, it->coefficient() );
else
row->insert( symbol, it->coefficient() );
}
}
// Add the necessary slack, error, and dummy variables.
switch( constraint.op() )
{
case OP_LE:
case OP_GE:
{
double coeff = constraint.op() == OP_LE ? 1.0 : -1.0;
Symbol slack( Symbol::Slack, m_id_tick++ );
tag.marker = slack;
row->insert( slack, coeff );
if( constraint.strength() < strength::required )
{
Symbol error( Symbol::Error, m_id_tick++ );
tag.other = error;
row->insert( error, -coeff );
m_objective->insert( error, constraint.strength() );
}
break;
}
case OP_EQ:
{
if( constraint.strength() < strength::required )
{
Symbol errplus( Symbol::Error, m_id_tick++ );
Symbol errminus( Symbol::Error, m_id_tick++ );
tag.marker = errplus;
tag.other = errminus;
row->insert( errplus, -1.0 ); // v = eplus - eminus
row->insert( errminus, 1.0 ); // v - eplus + eminus = 0
m_objective->insert( errplus, constraint.strength() );
m_objective->insert( errminus, constraint.strength() );
}
else
{
Symbol dummy( Symbol::Dummy, m_id_tick++ );
tag.marker = dummy;
row->insert( dummy );
}
break;
}
}
// Ensure the row as a positive constant.
if( row->constant() < 0.0 )
row->reverseSign();
return row;
}
/* Choose the subject for solving for the row.
This method will choose the best subject for using as the solve
target for the row. An invalid symbol will be returned if there
is no valid target.
The symbols are chosen according to the following precedence:
1) The first symbol representing an external variable.
2) A negative slack or error tag variable.
If a subject cannot be found, an invalid symbol will be returned.
*/
Symbol chooseSubject( const Row& row, const Tag& tag )
{
typedef Row::CellMap::const_iterator iter_t;
iter_t end = row.cells().end();
for( iter_t it = row.cells().begin(); it != end; ++it )
{
if( it->first.type() == Symbol::External )
return it->first;
}
if( tag.marker.type() == Symbol::Slack || tag.marker.type() == Symbol::Error )
{
if( row.coefficientFor( tag.marker ) < 0.0 )
return tag.marker;
}
if( tag.other.type() == Symbol::Slack || tag.other.type() == Symbol::Error )
{
if( row.coefficientFor( tag.other ) < 0.0 )
return tag.other;
}
return Symbol();
}
/* Add the row to the tableau using an artificial variable.
This will return false if the constraint cannot be satisfied.
*/
bool addWithArtificialVariable( const Row& row )
{
// Create and add the artificial variable to the tableau
Symbol art( Symbol::Slack, m_id_tick++ );
m_rows[ art ] = new Row( row );
m_artificial.reset( new Row( row ) );
// Optimize the artificial objective. This is successful
// only if the artificial objective is optimized to zero.
optimize( *m_artificial );
bool success = nearZero( m_artificial->constant() );
m_artificial.reset();
// If the artificial variable is not basic, pivot the row so that
// it becomes basic. If the row is constant, exit early.
RowMap::iterator it = m_rows.find( art );
if( it != m_rows.end() )
{
std::unique_ptr<Row> rowptr( it->second );
m_rows.erase( it );
if( rowptr->cells().empty() )
return success;
Symbol entering( anyPivotableSymbol( *rowptr ) );
if( entering.type() == Symbol::Invalid )
return false; // unsatisfiable (will this ever happen?)
rowptr->solveFor( art, entering );
substitute( entering, *rowptr );
m_rows[ entering ] = rowptr.release();
}
// Remove the artificial variable from the tableau.
RowMap::iterator end = m_rows.end();
for( it = m_rows.begin(); it != end; ++it )
it->second->remove( art );
m_objective->remove( art );
return success;
}
/* Substitute the parametric symbol with the given row.
This method will substitute all instances of the parametric symbol
in the tableau and the objective function with the given row.
*/
void substitute( const Symbol& symbol, const Row& row )
{
typedef RowMap::iterator iter_t;
iter_t end = m_rows.end();
for( iter_t it = m_rows.begin(); it != end; ++it )
{
it->second->substitute( symbol, row );
if( it->first.type() != Symbol::External &&
it->second->constant() < 0.0 )
m_infeasible_rows.push_back( it->first );
}
m_objective->substitute( symbol, row );
if( m_artificial.get() )
m_artificial->substitute( symbol, row );
}
/* Optimize the system for the given objective function.
This method performs iterations of Phase 2 of the simplex method
until the objective function reaches a minimum.
Throws
------
InternalSolverError
The value of the objective function is unbounded.
*/
void optimize( const Row& objective )
{
while( true )
{
Symbol entering( getEnteringSymbol( objective ) );
if( entering.type() == Symbol::Invalid )
return;
RowMap::iterator it = getLeavingRow( entering );
if( it == m_rows.end() )
throw InternalSolverError( "The objective is unbounded." );
// pivot the entering symbol into the basis
Symbol leaving( it->first );
Row* row = it->second;
m_rows.erase( it );
row->solveFor( leaving, entering );
substitute( entering, *row );
m_rows[ entering ] = row;
}
}
/* Optimize the system using the dual of the simplex method.
The current state of the system should be such that the objective
function is optimal, but not feasible. This method will perform
an iteration of the dual simplex method to make the solution both
optimal and feasible.
Throws
------
InternalSolverError
The system cannot be dual optimized.
*/
void dualOptimize()
{
while( !m_infeasible_rows.empty() )
{
Symbol leaving( m_infeasible_rows.back() );
m_infeasible_rows.pop_back();
RowMap::iterator it = m_rows.find( leaving );
if( it != m_rows.end() && !nearZero( it->second->constant() ) &&
it->second->constant() < 0.0 )
{
Symbol entering( getDualEnteringSymbol( *it->second ) );
if( entering.type() == Symbol::Invalid )
throw InternalSolverError( "Dual optimize failed." );
// pivot the entering symbol into the basis
Row* row = it->second;
m_rows.erase( it );
row->solveFor( leaving, entering );
substitute( entering, *row );
m_rows[ entering ] = row;
}
}
}
/* Compute the entering variable for a pivot operation.
This method will return first symbol in the objective function which
is non-dummy and has a coefficient less than zero. If no symbol meets
the criteria, it means the objective function is at a minimum, and an
invalid symbol is returned.
*/
Symbol getEnteringSymbol( const Row& objective )
{
typedef Row::CellMap::const_iterator iter_t;
iter_t end = objective.cells().end();
for( iter_t it = objective.cells().begin(); it != end; ++it )
{
if( it->first.type() != Symbol::Dummy && it->second < 0.0 )
return it->first;
}
return Symbol();
}
/* Compute the entering symbol for the dual optimize operation.
This method will return the symbol in the row which has a positive
coefficient and yields the minimum ratio for its respective symbol
in the objective function. The provided row *must* be infeasible.
If no symbol is found which meats the criteria, an invalid symbol
is returned.
*/
Symbol getDualEnteringSymbol( const Row& row )
{
typedef Row::CellMap::const_iterator iter_t;
Symbol entering;
double ratio = std::numeric_limits<double>::max();
iter_t end = row.cells().end();
for( iter_t it = row.cells().begin(); it != end; ++it )
{
if( it->second > 0.0 && it->first.type() != Symbol::Dummy )
{
double coeff = m_objective->coefficientFor( it->first );
double r = coeff / it->second;
if( r < ratio )
{
ratio = r;
entering = it->first;
}
}
}
return entering;
}
/* Get the first Slack or Error symbol in the row.
If no such symbol is present, and Invalid symbol will be returned.
*/
Symbol anyPivotableSymbol( const Row& row )
{
typedef Row::CellMap::const_iterator iter_t;
iter_t end = row.cells().end();
for( iter_t it = row.cells().begin(); it != end; ++it )
{
const Symbol& sym( it->first );
if( sym.type() == Symbol::Slack || sym.type() == Symbol::Error )
return sym;
}
return Symbol();
}
/* Compute the row which holds the exit symbol for a pivot.
This method will return an iterator to the row in the row map
which holds the exit symbol. If no appropriate exit symbol is
found, the end() iterator will be returned. This indicates that
the objective function is unbounded.
*/
RowMap::iterator getLeavingRow( const Symbol& entering )
{
typedef RowMap::iterator iter_t;
double ratio = std::numeric_limits<double>::max();
iter_t end = m_rows.end();
iter_t found = m_rows.end();
for( iter_t it = m_rows.begin(); it != end; ++it )
{
if( it->first.type() != Symbol::External )
{
double temp = it->second->coefficientFor( entering );
if( temp < 0.0 )
{
double temp_ratio = -it->second->constant() / temp;
if( temp_ratio < ratio )
{
ratio = temp_ratio;
found = it;
}
}
}
}
return found;
}
/* Compute the leaving row for a marker variable.
This method will return an iterator to the row in the row map
which holds the given marker variable. The row will be chosen
according to the following precedence:
1) The row with a restricted basic varible and a negative coefficient
for the marker with the smallest ratio of -constant / coefficient.
2) The row with a restricted basic variable and the smallest ratio
of constant / coefficient.
3) The last unrestricted row which contains the marker.
If the marker does not exist in any row, the row map end() iterator
will be returned. This indicates an internal solver error since
the marker *should* exist somewhere in the tableau.
*/
RowMap::iterator getMarkerLeavingRow( const Symbol& marker )
{
const double dmax = std::numeric_limits<double>::max();
typedef RowMap::iterator iter_t;
double r1 = dmax;
double r2 = dmax;
iter_t end = m_rows.end();
iter_t first = end;
iter_t second = end;
iter_t third = end;
for( iter_t it = m_rows.begin(); it != end; ++it )
{
double c = it->second->coefficientFor( marker );
if( c == 0.0 )
continue;
if( it->first.type() == Symbol::External )
{
third = it;
}
else if( c < 0.0 )
{
double r = -it->second->constant() / c;
if( r < r1 )
{
r1 = r;
first = it;
}
}
else
{
double r = it->second->constant() / c;
if( r < r2 )
{
r2 = r;
second = it;
}
}
}
if( first != end )
return first;
if( second != end )
return second;
return third;
}
/* Remove the effects of a constraint on the objective function.
*/
void removeConstraintEffects( const Constraint& cn, const Tag& tag )
{
if( tag.marker.type() == Symbol::Error )
removeMarkerEffects( tag.marker, cn.strength() );
if( tag.other.type() == Symbol::Error )
removeMarkerEffects( tag.other, cn.strength() );
}
/* Remove the effects of an error marker on the objective function.
*/
void removeMarkerEffects( const Symbol& marker, double strength )
{
RowMap::iterator row_it = m_rows.find( marker );
if( row_it != m_rows.end() )
m_objective->insert( *row_it->second, -strength );
else
m_objective->insert( marker, -strength );
}
/* Test whether a row is composed of all dummy variables.
*/
bool allDummies( const Row& row )
{
typedef Row::CellMap::const_iterator iter_t;
iter_t end = row.cells().end();
for( iter_t it = row.cells().begin(); it != end; ++it )
{
if( it->first.type() != Symbol::Dummy )
return false;
}
return true;
}
CnMap m_cns;
RowMap m_rows;
VarMap m_vars;
EditMap m_edits;
std::vector<Symbol> m_infeasible_rows;
std::unique_ptr<Row> m_objective;
std::unique_ptr<Row> m_artificial;
Symbol::Id m_id_tick;
};
} // namespace impl
} // namespace kiwi

View file

@ -1,44 +0,0 @@
/*-----------------------------------------------------------------------------
| Copyright (c) 2013-2017, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#pragma once
#include <algorithm>
namespace kiwi
{
namespace strength
{
inline double create( double a, double b, double c, double w = 1.0 )
{
double result = 0.0;
result += std::max( 0.0, std::min( 1000.0, a * w ) ) * 1000000.0;
result += std::max( 0.0, std::min( 1000.0, b * w ) ) * 1000.0;
result += std::max( 0.0, std::min( 1000.0, c * w ) );
return result;
}
const double required = create( 1000.0, 1000.0, 1000.0 );
const double strong = create( 1.0, 0.0, 0.0 );
const double medium = create( 0.0, 1.0, 0.0 );
const double weak = create( 0.0, 0.0, 1.0 );
inline double clip( double value )
{
return std::max( 0.0, std::min( required, value ) );
}
} // namespace strength
} // namespace kiwi

View file

@ -1,68 +0,0 @@
/*-----------------------------------------------------------------------------
| Copyright (c) 2013-2017, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#pragma once
namespace kiwi
{
namespace impl
{
class Symbol
{
public:
typedef unsigned long long Id;
enum Type
{
Invalid,
External,
Slack,
Error,
Dummy
};
Symbol() : m_id( 0 ), m_type( Invalid ) {}
Symbol( Type type, Id id ) : m_id( id ), m_type( type ) {}
~Symbol() {}
Id id() const
{
return m_id;
}
Type type() const
{
return m_type;
}
private:
Id m_id;
Type m_type;
friend bool operator<( const Symbol& lhs, const Symbol& rhs )
{
return lhs.m_id < rhs.m_id;
}
friend bool operator==( const Symbol& lhs, const Symbol& rhs )
{
return lhs.m_id == rhs.m_id;
}
};
} // namespace impl
} // namespace kiwi

View file

@ -1,685 +0,0 @@
/*-----------------------------------------------------------------------------
| Copyright (c) 2013-2017, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#pragma once
#include <vector>
#include "constraint.h"
#include "expression.h"
#include "term.h"
#include "variable.h"
namespace kiwi
{
// Variable multiply, divide, and unary invert
inline
Term operator*( const Variable& variable, double coefficient )
{
return Term( variable, coefficient );
}
inline
Term operator/( const Variable& variable, double denominator )
{
return variable * ( 1.0 / denominator );
}
inline
Term operator-( const Variable& variable )
{
return variable * -1.0;
}
// Term multiply, divide, and unary invert
inline
Term operator*( const Term& term, double coefficient )
{
return Term( term.variable(), term.coefficient() * coefficient );
}
inline
Term operator/( const Term& term, double denominator )
{
return term * ( 1.0 / denominator );
}
inline
Term operator-( const Term& term )
{
return term * -1.0;
}
// Expression multiply, divide, and unary invert
inline
Expression operator*( const Expression& expression, double coefficient )
{
std::vector<Term> terms;
terms.reserve( expression.terms().size() );
typedef std::vector<Term>::const_iterator iter_t;
iter_t begin = expression.terms().begin();
iter_t end = expression.terms().end();
for( iter_t it = begin; it != end; ++it )
terms.push_back( ( *it ) * coefficient );
return Expression( terms, expression.constant() * coefficient );
}
inline
Expression operator/( const Expression& expression, double denominator )
{
return expression * ( 1.0 / denominator );
}
inline
Expression operator-( const Expression& expression )
{
return expression * -1.0;
}
// Double multiply
inline
Expression operator*( double coefficient, const Expression& expression )
{
return expression * coefficient;
}
inline
Term operator*( double coefficient, const Term& term )
{
return term * coefficient;
}
inline
Term operator*( double coefficient, const Variable& variable )
{
return variable * coefficient;
}
// Expression add and subtract
inline
Expression operator+( const Expression& first, const Expression& second )
{
std::vector<Term> terms;
terms.reserve( first.terms().size() + second.terms().size() );
terms.insert( terms.begin(), first.terms().begin(), first.terms().end() );
terms.insert( terms.end(), second.terms().begin(), second.terms().end() );
return Expression( terms, first.constant() + second.constant() );
}
inline
Expression operator+( const Expression& first, const Term& second )
{
std::vector<Term> terms;
terms.reserve( first.terms().size() + 1 );
terms.insert( terms.begin(), first.terms().begin(), first.terms().end() );
terms.push_back( second );
return Expression( terms, first.constant() );
}
inline
Expression operator+( const Expression& expression, const Variable& variable )
{
return expression + Term( variable );
}
inline
Expression operator+( const Expression& expression, double constant )
{
return Expression( expression.terms(), expression.constant() + constant );
}
inline
Expression operator-( const Expression& first, const Expression& second )
{
return first + -second;
}
inline
Expression operator-( const Expression& expression, const Term& term )
{
return expression + -term;
}
inline
Expression operator-( const Expression& expression, const Variable& variable )
{
return expression + -variable;
}
inline
Expression operator-( const Expression& expression, double constant )
{
return expression + -constant;
}
// Term add and subtract
inline
Expression operator+( const Term& term, const Expression& expression )
{
return expression + term;
}
inline
Expression operator+( const Term& first, const Term& second )
{
std::vector<Term> terms;
terms.reserve( 2 );
terms.push_back( first );
terms.push_back( second );
return Expression( terms );
}
inline
Expression operator+( const Term& term, const Variable& variable )
{
return term + Term( variable );
}
inline
Expression operator+( const Term& term, double constant )
{
return Expression( term, constant );
}
inline
Expression operator-( const Term& term, const Expression& expression )
{
return -expression + term;
}
inline
Expression operator-( const Term& first, const Term& second )
{
return first + -second;
}
inline
Expression operator-( const Term& term, const Variable& variable )
{
return term + -variable;
}
inline
Expression operator-( const Term& term, double constant )
{
return term + -constant;
}
// Variable add and subtract
inline
Expression operator+( const Variable& variable, const Expression& expression )
{
return expression + variable;
}
inline
Expression operator+( const Variable& variable, const Term& term )
{
return term + variable;
}
inline
Expression operator+( const Variable& first, const Variable& second )
{
return Term( first ) + second;
}
inline
Expression operator+( const Variable& variable, double constant )
{
return Term( variable ) + constant;
}
inline
Expression operator-( const Variable& variable, const Expression& expression )
{
return variable + -expression;
}
inline
Expression operator-( const Variable& variable, const Term& term )
{
return variable + -term;
}
inline
Expression operator-( const Variable& first, const Variable& second )
{
return first + -second;
}
inline
Expression operator-( const Variable& variable, double constant )
{
return variable + -constant;
}
// Double add and subtract
inline
Expression operator+( double constant, const Expression& expression )
{
return expression + constant;
}
inline
Expression operator+( double constant, const Term& term )
{
return term + constant;
}
inline
Expression operator+( double constant, const Variable& variable )
{
return variable + constant;
}
inline
Expression operator-( double constant, const Expression& expression )
{
return -expression + constant;
}
inline
Expression operator-( double constant, const Term& term )
{
return -term + constant;
}
inline
Expression operator-( double constant, const Variable& variable )
{
return -variable + constant;
}
// Expression relations
inline
Constraint operator==( const Expression& first, const Expression& second )
{
return Constraint( first - second, OP_EQ );
}
inline
Constraint operator==( const Expression& expression, const Term& term )
{
return expression == Expression( term );
}
inline
Constraint operator==( const Expression& expression, const Variable& variable )
{
return expression == Term( variable );
}
inline
Constraint operator==( const Expression& expression, double constant )
{
return expression == Expression( constant );
}
inline
Constraint operator<=( const Expression& first, const Expression& second )
{
return Constraint( first - second, OP_LE );
}
inline
Constraint operator<=( const Expression& expression, const Term& term )
{
return expression <= Expression( term );
}
inline
Constraint operator<=( const Expression& expression, const Variable& variable )
{
return expression <= Term( variable );
}
inline
Constraint operator<=( const Expression& expression, double constant )
{
return expression <= Expression( constant );
}
inline
Constraint operator>=( const Expression& first, const Expression& second )
{
return Constraint( first - second, OP_GE );
}
inline
Constraint operator>=( const Expression& expression, const Term& term )
{
return expression >= Expression( term );
}
inline
Constraint operator>=( const Expression& expression, const Variable& variable )
{
return expression >= Term( variable );
}
inline
Constraint operator>=( const Expression& expression, double constant )
{
return expression >= Expression( constant );
}
// Term relations
inline
Constraint operator==( const Term& term, const Expression& expression )
{
return expression == term;
}
inline
Constraint operator==( const Term& first, const Term& second )
{
return Expression( first ) == second;
}
inline
Constraint operator==( const Term& term, const Variable& variable )
{
return Expression( term ) == variable;
}
inline
Constraint operator==( const Term& term, double constant )
{
return Expression( term ) == constant;
}
inline
Constraint operator<=( const Term& term, const Expression& expression )
{
return expression >= term;
}
inline
Constraint operator<=( const Term& first, const Term& second )
{
return Expression( first ) <= second;
}
inline
Constraint operator<=( const Term& term, const Variable& variable )
{
return Expression( term ) <= variable;
}
inline
Constraint operator<=( const Term& term, double constant )
{
return Expression( term ) <= constant;
}
inline
Constraint operator>=( const Term& term, const Expression& expression )
{
return expression <= term;
}
inline
Constraint operator>=( const Term& first, const Term& second )
{
return Expression( first ) >= second;
}
inline
Constraint operator>=( const Term& term, const Variable& variable )
{
return Expression( term ) >= variable;
}
inline
Constraint operator>=( const Term& term, double constant )
{
return Expression( term ) >= constant;
}
// Variable relations
inline
Constraint operator==( const Variable& variable, const Expression& expression )
{
return expression == variable;
}
inline
Constraint operator==( const Variable& variable, const Term& term )
{
return term == variable;
}
inline
Constraint operator==( const Variable& first, const Variable& second )
{
return Term( first ) == second;
}
inline
Constraint operator==( const Variable& variable, double constant )
{
return Term( variable ) == constant;
}
inline
Constraint operator<=( const Variable& variable, const Expression& expression )
{
return expression >= variable;
}
inline
Constraint operator<=( const Variable& variable, const Term& term )
{
return term >= variable;
}
inline
Constraint operator<=( const Variable& first, const Variable& second )
{
return Term( first ) <= second;
}
inline
Constraint operator<=( const Variable& variable, double constant )
{
return Term( variable ) <= constant;
}
inline
Constraint operator>=( const Variable& variable, const Expression& expression )
{
return expression <= variable;
}
inline
Constraint operator>=( const Variable& variable, const Term& term )
{
return term <= variable;
}
inline
Constraint operator>=( const Variable& first, const Variable& second )
{
return Term( first ) >= second;
}
inline
Constraint operator>=( const Variable& variable, double constant )
{
return Term( variable ) >= constant;
}
// Double relations
inline
Constraint operator==( double constant, const Expression& expression )
{
return expression == constant;
}
inline
Constraint operator==( double constant, const Term& term )
{
return term == constant;
}
inline
Constraint operator==( double constant, const Variable& variable )
{
return variable == constant;
}
inline
Constraint operator<=( double constant, const Expression& expression )
{
return expression >= constant;
}
inline
Constraint operator<=( double constant, const Term& term )
{
return term >= constant;
}
inline
Constraint operator<=( double constant, const Variable& variable )
{
return variable >= constant;
}
inline
Constraint operator>=( double constant, const Expression& expression )
{
return expression <= constant;
}
inline
Constraint operator>=( double constant, const Term& term )
{
return term <= constant;
}
inline
Constraint operator>=( double constant, const Variable& variable )
{
return variable <= constant;
}
// Constraint strength modifier
inline
Constraint operator|( const Constraint& constraint, double strength )
{
return Constraint( constraint, strength );
}
inline
Constraint operator|( double strength, const Constraint& constraint )
{
return constraint | strength;
}
} // namespace kiwi

View file

@ -1,58 +0,0 @@
/*-----------------------------------------------------------------------------
| Copyright (c) 2013-2017, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#pragma once
#include <ostream>
#include <utility>
#include "variable.h"
namespace kiwi
{
class Term
{
public:
Term( const Variable& variable, double coefficient = 1.0 ) :
m_variable( variable ), m_coefficient( coefficient ) {}
// to facilitate efficient map -> vector copies
Term( const std::pair<const Variable, double>& pair ) :
m_variable( pair.first ), m_coefficient( pair.second ) {}
~Term() {}
const Variable& variable() const
{
return m_variable;
}
double coefficient() const
{
return m_coefficient;
}
double value() const
{
return m_coefficient * m_variable.value();
}
private:
Variable m_variable;
double m_coefficient;
};
static std::ostream& operator<< (std::ostream& o, kiwi::Term const & t)
{
return o << t.variable().name() << " * " << t.coefficient();
}
} // namespace kiwi

View file

@ -1,24 +0,0 @@
/*-----------------------------------------------------------------------------
| Copyright (c) 2013-2017, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#pragma once
namespace kiwi
{
namespace impl
{
inline bool nearZero(double value)
{
const double eps = 1.0e-8;
return value < 0.0 ? -value < eps : value < eps;
}
} // namespace impl
} // namespace kiwi

View file

@ -1,111 +0,0 @@
/*-----------------------------------------------------------------------------
| Copyright (c) 2013-2017, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#pragma once
#include <memory>
#include <string>
#include "shareddata.h"
namespace kiwi
{
class Variable
{
public:
class Context
{
public:
Context() {}
virtual ~Context() {} // LCOV_EXCL_LINE
};
Variable(Context *context = 0) : m_data(new VariableData("", context)) {}
Variable(const std::string &name, Context *context = 0) : m_data(new VariableData(name, context)) {}
Variable(const char *name, Context *context = 0) : m_data(new VariableData(name, context)) {}
~Variable() {}
const std::string &name() const
{
return m_data->m_name;
}
void setName(const char *name)
{
m_data->m_name = name;
}
void setName(const std::string &name)
{
m_data->m_name = name;
}
Context *context() const
{
return m_data->m_context.get();
}
void setContext(Context *context)
{
m_data->m_context.reset(context);
}
double value() const
{
return m_data->m_value;
}
void setValue(double value)
{
m_data->m_value = value;
}
// operator== is used for symbolics
bool equals(const Variable &other) const
{
return m_data == other.m_data;
}
private:
class VariableData : public SharedData
{
public:
VariableData(const std::string &name, Context *context) : SharedData(),
m_name(name),
m_context(context),
m_value(0.0) {}
VariableData(const char *name, Context *context) : SharedData(),
m_name(name),
m_context(context),
m_value(0.0) {}
~VariableData() {}
std::string m_name;
std::unique_ptr<Context> m_context;
double m_value;
private:
VariableData(const VariableData &other);
VariableData &operator=(const VariableData &other);
};
SharedDataPtr<VariableData> m_data;
friend bool operator<(const Variable &lhs, const Variable &rhs)
{
return lhs.m_data < rhs.m_data;
}
};
} // namespace kiwi

View file

@ -1,14 +0,0 @@
/*-----------------------------------------------------------------------------
| Copyright (c) 2013-2020, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#pragma once
#define KIWI_MAJOR_VERSION 1
#define KIWI_MINOR_VERSION 2
#define KIWI_MICRO_VERSION 0
#define KIWI_VERSION_HEX 0x010200
#define KIWI_VERSION "1.2.0"

View file

@ -1,131 +0,0 @@
#include <iostream>
#include <gtkmm/adjustment.h>
#include <gtkmm/main.h>
#include <gtkmm/window.h>
#include "gtkmm2ext/colors.h"
#include "canvas/box.h"
#include "canvas/canvas.h"
#include "canvas/circle.h"
#include "canvas/constrained_item.h"
#include "canvas/constraint_packer.h"
#include "canvas/rectangle.h"
#include "canvas/text.h"
using namespace ArdourCanvas;
using namespace Gtk;
using std::cerr;
using std::endl;
int
main (int argc, char* argv[])
{
Gtk::Main app (&argc, &argv);
Gtk::Window win;
Gtk::Adjustment hadj (0, 0, 1000, 1, 10);
Gtk::Adjustment vadj (0, 0, 1000, 1, 10);
GtkCanvasViewport cview (hadj, vadj);
Canvas* c = cview.canvas ();
c->set_background_color (0xffffffff);
// cview.set_size_request (100, 100);
win.add (cview);
Rectangle* r1 = new Rectangle (c);
Rectangle* r2 = new Rectangle (c);
Rectangle* r3 = new Rectangle (c);
r1->set_fill_color (Gtkmm2ext::random_color());
r2->set_fill_color (Gtkmm2ext::random_color());
r3->set_fill_color (Gtkmm2ext::random_color());
r1->name = "r1";
r2->name = "r2";
r3->name = "r3";
//r1->set_size_request (20, 20);
//r2->set_size_request (30, 30);
//r3->set_size_request (40, 40);
ConstraintPacker* vbox = new ConstraintPacker (c->root(), Vertical);
vbox->name = "vbox";
vbox->set_fill (true);
vbox->set_fill_color (0xff0000ff);
vbox->set_margin (20);
vbox->pack_start (r1, PackOptions(PackExpand|PackFill));
vbox->pack_start (r2, PackOptions(PackExpand|PackFill));
vbox->pack_start (r3, PackOptions(PackExpand|PackFill));
ConstraintPacker* hbox1 = new ConstraintPacker (c, Horizontal);
hbox1->name = "hbox1";
hbox1->set_fill (true);
hbox1->set_fill_color (0x00ff00ff);
hbox1->set_margin (10);
Rectangle* r4 = new Rectangle (c);
Rectangle* r5 = new Rectangle (c);
Rectangle* r6 = new Rectangle (c);
r4->set_fill_color (Gtkmm2ext::random_color());
r5->set_fill_color (Gtkmm2ext::random_color());
r6->set_fill_color (Gtkmm2ext::random_color());
r4->name = "r4";
r5->name = "r5";
r6->name = "r6";
ConstrainedItem* ci4 = hbox1->pack_start (r4, PackOptions(PackExpand|PackFill));
hbox1->pack_start (r5, PackOptions(PackExpand|PackFill));
hbox1->pack_start (r6, PackOptions(PackExpand|PackFill));
BoxConstrainedItem* hb1;
BoxConstrainedItem* ci;
hb1 = vbox->pack_start (hbox1, PackOptions (PackExpand|PackFill));
ci4->add_constraint (ci4->width() == hb1->width() / 2.);
Circle* circle = new Circle (c);
circle->name = "circle";
//circle->set_radius (30);
circle->set_fill_color (Gtkmm2ext::random_color());
circle->set_outline_color (Gtkmm2ext::random_color());
ci = vbox->pack_start (circle, PackOptions (PackExpand|PackFill));
ci->add_constraint (ci->height() == 0.5 * hb1->height());
ci->add_constraint (ci->center_x() == ci4->center_x());
ci->add_constraint (ci->top_padding() == 10);
ci->add_constraint (ci->bottom_padding() == 10);
ConstraintPacker* hbox2 = new ConstraintPacker (c, Horizontal);
hbox2->name = "hbox2";
hbox2->set_fill (true);
hbox2->set_fill_color (Gtkmm2ext::random_color());
hbox2->set_outline (true);
Text* txt = new Text (c);
txt->name = "text";
Pango::FontDescription font ("Sans");
txt->set_font_description (font);
txt->set ("hello world");
ConstrainedItem* hb2 = vbox->pack_start (hbox2, PackOptions (PackExpand|PackFill));
ConstrainedItem* ti = hbox2->pack_start (txt, PackOptions (PackExpand), PackOptions (0));
ti->add_constraint (ti->center_x() == hb2->center_x());
ti->add_constraint (ti->center_y() == hb2->center_y());
win.show_all ();
app.run ();
return 0;
}

View file

@ -1,184 +0,0 @@
#include <iostream>
#include <gtkmm/adjustment.h>
#include <gtkmm/main.h>
#include <gtkmm/window.h>
#include "pbd/compose.h"
#include "gtkmm2ext/colors.h"
#include "canvas/box.h"
#include "canvas/canvas.h"
#include "canvas/circle.h"
#include "canvas/constrained_item.h"
#include "canvas/constraint_packer.h"
#include "canvas/rectangle.h"
#include "canvas/text.h"
using namespace ArdourCanvas;
using namespace Gtk;
using std::cerr;
using std::endl;
#define SQUARED 16
struct Column {
Column (Canvas* c, uint32_t num) : number (num) {
box = new ConstraintPacker (c, Vertical);
box->name = string_compose ("col%1", num);
box->set_spacing (12);
Pango::FontDescription font ("Sans");
for (int i = 0; i < SQUARED; ++i) {
rects[i] = new Rectangle (c);
rects[i]->name = string_compose ("r%1-%2", number, i);
rects[i]->set_size_request (8, 12);
rects[i]->set_outline_color (0xff0000ff);
rects[i]->set_fill_color (Gtkmm2ext::random_color());
BoxConstrainedItem* b = box->pack_start (rects[i], PackOptions (PackExpand|PackFill));
labels[i] = new Text (c);
labels[i]->name = string_compose ("t%1-%2", number, i);
labels[i]->set_font_description (font);
labels[i]->set (labels[i]->name);
labels[i]->set_fill_color (0x000000ff);
ConstrainedItem* l = box->add_constrained (labels[i]);
/* Note: the use of labels[i].width() here is
* equivalent to using a constant. This is not the same
* as l->width(), which is a constraint-solved
* variable. labels[i].width() is the pixel width of
* the current text contents of labels[i].
*/
l->centered_on (*b);
l->add_constraint (l->width() == labels[i]->width());
l->add_constraint (l->height() == labels[i]->height());
#if 0
l->add_constraint (l->left() == b->center_x() - (labels[i]->width() / 2.));
l->add_constraint (l->width() == labels[i]->width());
l->add_constraint (l->right() == l->left() + labels[i]->width());
l->add_constraint (l->top() == b->center_y() - (labels[i]->height() / 2));
l->add_constraint (l->height() == labels[i]->height());
l->add_constraint (l->bottom() == l->top() + l->height());
#endif
}
}
ConstraintPacker* box;
Rectangle* rects[SQUARED];
Text* labels[SQUARED];
uint32_t number;
};
int
main (int argc, char* argv[])
{
Gtk::Main app (&argc, &argv);
Gtk::Window win;
Gtk::Adjustment hadj (0, 0, 1000, 1, 10);
Gtk::Adjustment vadj (0, 0, 1000, 1, 10);
GtkCanvasViewport cview (hadj, vadj);
Canvas* c = cview.canvas ();
c->set_background_color (0xffffffff);
// cview.set_size_request (100, 100);
win.add (cview);
ConstraintPacker* main_hbox = new ConstraintPacker (c->root(), Horizontal);
main_hbox->name = "main";
main_hbox->set_spacing (12);
main_hbox->set_margin (24);
Column* cols[SQUARED];
for (size_t i = 0; i < SQUARED; ++i) {
cols[i] = new Column (c, i);
main_hbox->pack_start (cols[i]->box, PackOptions (PackExpand|PackFill));
}
#if 0
Circle* circle = new Circle (c);
circle->name = "circle";
//circle->set_radius (30);
circle->set_fill_color (Gtkmm2ext::random_color());
circle->set_outline_color (Gtkmm2ext::random_color());
ci = vbox->pack_start (circle, PackOptions (PackExpand|PackFill));
ci->add_constraint (ci->height() == 0.5 * hb1->height());
ConstraintPacker* hbox2 = new ConstraintPacker (c, Horizontal);
hbox2->name = "hbox2";
hbox2->set_fill (true);
hbox2->set_fill_color (Gtkmm2ext::random_color());
Text* txt = new Text (c);
txt->name = "text";
Pango::FontDescription font ("Sans");
txt->set_font_description (font);
txt->set ("hello, world");
ConstrainedItem* ti = hbox2->pack_start (txt, PackExpand);
ti->add_constraint (ti->left() == 25);
vbox->pack_start (hbox2, PackOptions (PackExpand|PackFill));
#endif
win.show_all ();
app.run ();
return 0;
}
#if 0
/* code test arbitrary constraint layout */
ConstraintPacker* packer = new ConstraintPacker (c->root());
ConstrainedItem* left = packer->add_constrained (r1);
ConstrainedItem* right = packer->add_constrained (r2);
ConstrainedItem* center = packer->add_constrained (r3);
/* x-axis */
packer->constrain (left->left() == 0);
packer->constrain (center->left() == left->right());
packer->constrain (right->left() == center->right());
packer->constrain (left->width() == packer->width * 0.4);
packer->constrain (center->width() == packer->width * 0.1);
packer->constrain (left->width() + right->width() + center->width() == packer->width);
packer->constrain (left->right() == left->left() + left->width());
packer->constrain (right->right() == right->left() + right->width());
packer->constrain (center->right() == center->left() + center->width());
/* y-axis */
packer->constrain (left->top() == 0);
packer->constrain (right->top() == left->top());
packer->constrain (center->top() == left->top());
packer->constrain (left->height() == packer->height);
packer->constrain (right->height() == left->height());
packer->constrain (center->height() == left->height());
packer->constrain (left->bottom() == left->top() + left->height());
packer->constrain (center->bottom() == center->top() + center->height());
packer->constrain (right->bottom() == right->top() + right->height());
#endif

View file

@ -1,93 +0,0 @@
#include <iostream>
#include <gtkmm/adjustment.h>
#include <gtkmm/main.h>
#include <gtkmm/window.h>
#include "gtkmm2ext/colors.h"
#include "canvas/box.h"
#include "canvas/canvas.h"
#include "canvas/circle.h"
#include "canvas/constrained_item.h"
#include "canvas/constraint_packer.h"
#include "canvas/rectangle.h"
#include "canvas/text.h"
using namespace ArdourCanvas;
using namespace Gtk;
using std::cerr;
using std::endl;
int
main (int argc, char* argv[])
{
Gtk::Main app (&argc, &argv);
Gtk::Window win;
Gtk::Adjustment hadj (0, 0, 1000, 1, 10);
Gtk::Adjustment vadj (0, 0, 1000, 1, 10);
GtkCanvasViewport cview (hadj, vadj);
Canvas* c = cview.canvas ();
c->set_background_color (0xffffffff);
cview.set_size_request (100, 100);
win.add (cview);
Rectangle* r1 = new Rectangle (c);
Rectangle* r2 = new Rectangle (c);
Rectangle* r3 = new Rectangle (c);
r1->set_fill_color (Gtkmm2ext::random_color());
r2->set_fill_color (Gtkmm2ext::random_color());
r3->set_fill_color (Gtkmm2ext::random_color());
r1->name = "r1";
r2->name = "r2";
r3->name = "r3";
r1->set_size_request (20, 20);
r2->set_size_request (30, 30);
r3->set_size_request (40, 40);
ConstraintPacker* packer = new ConstraintPacker (c->root());
ConstrainedItem* left = packer->add_constrained (r1);
ConstrainedItem* right = packer->add_constrained (r2);
ConstrainedItem* center = packer->add_constrained (r3);
/* x-axis */
packer->constrain (left->left() == 0);
packer->constrain (center->left() == left->right());
packer->constrain (right->left() == center->right());
packer->constrain (left->width() == packer->width * 0.4);
packer->constrain (center->width() == packer->width * 0.1);
packer->constrain (left->width() + right->width() + center->width() == packer->width);
packer->constrain (left->right() == left->left() + left->width());
packer->constrain (right->right() == right->left() + right->width());
packer->constrain (center->right() == center->left() + center->width());
/* y-axis */
packer->constrain (left->top() == 0);
packer->constrain (right->top() == left->top());
packer->constrain (center->top() == left->top());
packer->constrain (left->height() == packer->height);
packer->constrain (right->height() == left->height());
packer->constrain (center->height() == left->height());
packer->constrain (left->bottom() == left->top() + left->height());
packer->constrain (center->bottom() == center->top() + center->height());
packer->constrain (right->bottom() == right->top() + right->height());
win.show_all ();
app.run ();
return 0;
}

View file

@ -1,127 +0,0 @@
#include <iostream>
#include <gtkmm/adjustment.h>
#include <gtkmm/main.h>
#include <gtkmm/window.h>
#include "gtkmm2ext/colors.h"
#include "canvas/box.h"
#include "canvas/canvas.h"
#include "canvas/circle.h"
#include "canvas/constrained_item.h"
#include "canvas/constraint_packer.h"
#include "canvas/rectangle.h"
#include "canvas/text.h"
using namespace ArdourCanvas;
using namespace Gtk;
using std::cerr;
using std::endl;
int
main (int argc, char* argv[])
{
Gtk::Main app (&argc, &argv);
Gtk::Window win;
Gtk::Adjustment hadj (0, 0, 1000, 1, 10);
Gtk::Adjustment vadj (0, 0, 1000, 1, 10);
GtkCanvasViewport cview (hadj, vadj);
Canvas* c = cview.canvas ();
c->set_background_color (0xffffffff);
win.add (cview);
/* Make some items */
Rectangle* r1 = new Rectangle (c);
Rectangle* r2 = new Rectangle (c);
Rectangle* r3 = new Rectangle (c);
r1->set_fill_color (Gtkmm2ext::random_color());
r2->set_fill_color (Gtkmm2ext::random_color());
r3->set_fill_color (Gtkmm2ext::random_color());
r1->name = "L";
r2->name = "R";
r3->name = "C";
r1->set_size_request (20, 20);
r2->set_size_request (30, 30);
r3->set_size_request (40, 40);
Text* txt = new Text (c);
txt->name = "text";
Pango::FontDescription font ("Sans");
txt->set_font_description (font);
txt->set ("hello world");
Rectangle* bb = new Rectangle (c);
bb->set_fill_color (Gtkmm2ext::random_color());
Circle* circ = new Circle (c);
circ->name = "circle";
circ->set_fill_color (Gtkmm2ext::random_color());
circ->set_outline_color (Gtkmm2ext::random_color());
/* create a container */
ConstraintPacker* packer = new ConstraintPacker (c->root());
/* give it a minimum size */
packer->set_size_request (100, 100);
/* add stuff */
ConstrainedItem* left = packer->add_constrained (r1);
ConstrainedItem* right = packer->add_constrained (r2);
ConstrainedItem* center = packer->add_constrained (r3);
ConstrainedItem* text = packer->add_constrained (txt);
ConstrainedItem* bens_box = packer->add_constrained (bb);
ConstrainedItem* circle = packer->add_constrained (circ);
/* first, constraints that connect an item dimension to the container dimensions or a constant */
packer->constrain (left->left() == 0);
packer->constrain (left->height() == packer->height);
packer->constrain (left->top() == 0);
packer->constrain (left->width() == 0.5 * packer->width);
packer->constrain (right->right() == packer->width);
packer->constrain (center->height() == 0.5 * packer->height);
/* second, constraints that connect an item dimension to other items */
center->right_of (*left, 50);
right->right_of (*center);
center->same_width_as (*right);
right->same_width_as (*center);
right->same_height_as (*left);
center->top_aligned_with (*left);
right->top_aligned_with (*center);
/* XXX this needs to somehow move into ConstraintPacker but I currently
* see no way to build a constraint from a container of
* ConstrainedItems
*/
packer->constrain (left->width() + right->width() + center->width() +
left->left_padding() + left->right_padding() +
center->left_padding() + center->right_padding() +
right->left_padding() + right->right_padding()
== packer->width);
/* Text at a fixed position */
text->at (Duple (150, 50));
/* Rectangle of fixed position and size */
bens_box->box (Rect (40, 40, 80, 80));
/* a circle sized and centered */
circle->size (Duple (30, 30));
circle->centered_on (*center);
win.show_all ();
app.run ();
return 0;
}

View file

@ -34,8 +34,6 @@ canvas_sources = [
'canvas.cc',
'circle.cc',
'container.cc',
'constrained_item.cc',
'constraint_packer.cc',
'curve.cc',
'debug.cc',
'item.cc',
@ -98,46 +96,6 @@ def build(bld):
obj.install_path = bld.env['LIBDIR']
obj.defines += [ 'PACKAGE="' + I18N_PACKAGE + '"' ]
# interactive (non-automated, non-cppunit) canvas tests
# fails to link when cross-compiling (lacks libpbd dependencies
# uselib = 'GLIBMM SIGCPP XML UUID SNDFILE GIOMM ARCHIVE CURL'
if False : # bld.env['BUILD_TESTS']:
constraint_test_src = [ 'test/interactive/constraint_test.cc' ]
constraint_test = bld (features = 'cxx cxxprogram')
constraint_test.source = constraint_test_src
constraint_test.includes = obj.includes + ['../pbd', '../gtkmm2ext']
constraint_test.use = [ 'GTKMM', 'libcanvas', 'libgtkmm2ext' ]
constraint_test.name = 'constraint_test'
constraint_test.target = 'constraint_test'
constraint_test.install_path = ''
constraint_test2_src = [ 'test/interactive/constraint_test2.cc' ]
constraint_test2 = bld (features = 'cxx cxxprogram')
constraint_test2.source = constraint_test2_src
constraint_test2.includes = obj.includes + ['../pbd', '../gtkmm2ext']
constraint_test2.use = [ 'GTKMM', 'libcanvas', 'libgtkmm2ext' ]
constraint_test2.name = 'constraint_test2'
constraint_test2.target = 'constraint_test2'
constraint_test2.install_path = ''
constraint_test3_src = [ 'test/interactive/constraint_test3.cc' ]
constraint_test3 = bld (features = 'cxx cxxprogram')
constraint_test3.source = constraint_test3_src
constraint_test3.includes = obj.includes + ['../pbd', '../gtkmm2ext']
constraint_test3.use = [ 'GTKMM', 'libcanvas', 'libgtkmm2ext' ]
constraint_test3.name = 'constraint_test3'
constraint_test3.target = 'constraint_test3'
constraint_test3.install_path = ''
constraint_test4_src = [ 'test/interactive/constraint_test4.cc' ]
constraint_test4 = bld (features = 'cxx cxxprogram')
constraint_test4.source = constraint_test4_src
constraint_test4.includes = obj.includes + ['../pbd', '../gtkmm2ext']
constraint_test4.use = [ 'GTKMM', 'libcanvas', 'libgtkmm2ext' ]
constraint_test4.name = 'constraint_test4'
constraint_test4.target = 'constraint_test4'
constraint_test4.install_path = ''
# canvas unit-tests are outdated
if False and bld.env['BUILD_TESTS'] and bld.is_defined('HAVE_CPPUNIT'):
unit_testobj = bld(features = 'cxx cxxprogram')