mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-15 19:16:40 +01:00
remove Kiwi constraint-based packing code from canvas library and users (library version)
This commit is contained in:
parent
a9adc3c5c6
commit
0f0c5c7039
28 changed files with 0 additions and 5235 deletions
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue