GithubHelp home page GithubHelp logo

rhea's People

Contributors

arximboldi avatar hfossli avatar nocte- avatar simonjamain avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rhea's Issues

A couple questions

This looks so clean, great job. I am going to try it. Any updates on whether you think it could be used in production? Also, is it thread safe? I am going to try in a Mac app and when I detect that multiple layout problems could be solved in parallel I was going to spin off a few jobs to do so.

Is the warning on the README accurate?

Hi!

Just read this in the README:

This software is alpha. It does pass all unit tests, but it hasn't been used in an actual application yet. Use at your own peril.

I wonder if this is still accurate, knowing that it is being used in Hexahedra. I want to use the Cassowary in some real software in production code, and I was tempted to use this library instead, which looks so much better and in agreement with my modern C++ moral values (hehe). This warning just left me wondering if it would be just a waste of time or not...

Tag

Can you add a tag e.g. '0.1' to this repo? It would be great for my build environment.

`Suggest` with auto_solve=false does not work as expected

To get an understanding of the behavior I'm seeing I think it is best to see this video.

The constraint is very simple – I can't understand why it behaves so jaggy. Also notice how the content gets totally offset from its content while I'm holding the mouse down.

Code

static rhea::simplex_solver solver;

static rhea::variable blueLeft (10);
static rhea::variable blueTop (10);
static rhea::variable blueWidth (100);
static rhea::variable blueHeight (100);

static rhea::variable greenLeft (220);
static rhea::variable greenTop (10);
static rhea::variable greenHeight (100);
static rhea::variable greenWidth (100);

static rhea::variable containerWidth (320);
static rhea::variable containerHeight (480);

@interface ViewController ()

@property (nonatomic, strong) UIView *blueView;
@property (nonatomic, strong) UIView *greenView;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self setupViews];

    solver.set_autosolve(false);

    solver.add_stay(blueLeft).add_stay(blueTop).add_stay(blueWidth).add_stay(blueHeight);
    solver.add_stay(greenLeft).add_stay(greenTop).add_stay(greenWidth).add_stay(greenHeight);
    solver.add_stay(containerWidth).add_stay(containerHeight);

    solver.add_constraints({
        greenTop == blueTop,
        blueLeft >= 10,
    });

    solver.on_resolve = [self](rhea::simplex_solver &solver) {
        [self updateFrames];
    };
}

- (void)setupViews
{
    self.blueView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
    self.blueView.backgroundColor = [UIColor blueColor];
    [self.view addSubview:self.blueView];

    UIPanGestureRecognizer *blueRecognizer = [UIPanGestureRecognizer new];
    [blueRecognizer addTarget:self action:@selector(blueRecognizerUpdated:)];
    [self.blueView addGestureRecognizer:blueRecognizer];

    self.greenView = [[UIView alloc] initWithFrame:CGRectMake(220, 0, 200, 200)];
    self.greenView.backgroundColor = [UIColor greenColor];
    [self.view addSubview:self.greenView];

    UIPanGestureRecognizer *greenRecognizer = [UIPanGestureRecognizer new];
    [greenRecognizer addTarget:self action:@selector(greeRecognizerUpdated:)];
    [self.greenView addGestureRecognizer:greenRecognizer];
}

- (void)blueRecognizerUpdated:(UIPanGestureRecognizer *)recognizer
{
    CGPoint translation = [recognizer translationInView:recognizer.view.superview];
    [recognizer setTranslation:CGPointZero inView:recognizer.view.superview];

    solver.suggest({
        { blueLeft, blueLeft.value() + translation.x },
        { blueTop, blueTop.value() + translation.y }
    });

    solver.solve();
}

- (void)greeRecognizerUpdated:(UIPanGestureRecognizer *)recognizer
{
    CGPoint translation = [recognizer translationInView:recognizer.view.superview];
    [recognizer setTranslation:CGPointZero inView:recognizer.view.superview];

    solver.suggest({
        { greenLeft, greenLeft.value() + translation.x },
        { greenTop, greenTop.value() + translation.y }
    });

    solver.solve();
}

- (void)updateFrames
{
    CGRect blueFrame;
    blueFrame.origin.x = blueLeft.value();
    blueFrame.origin.y = blueTop.value();
    blueFrame.size.width = blueWidth.value();
    blueFrame.size.height = blueHeight.value();
    self.blueView.frame = blueFrame;

    CGRect greenFrame;
    greenFrame.origin.x = greenLeft.value();
    greenFrame.origin.y = greenTop.value();
    greenFrame.size.width = greenWidth.value();
    greenFrame.size.height = greenHeight.value();
    self.greenView.frame = greenFrame;
}

@end

Unexpected implementation of `set_autosolve`

    solver& set_autosolve(bool is_auto = true)
    {
        auto_solve_ = true;
        if (auto_solve_)
            solve();

        return *this;
    }

I would expect it to be something like this

    solver& set_autosolve(bool is_auto = true)
    {
        auto_solve_ = is_auto;
        if (auto_solve_)
            solve();

        return *this;
    }

Named variables

In python they have named their variables.
http://cassowary.readthedocs.org/en/latest/topics/theory.html#variables

In my framework I have a map of variables with names as key. Is this something that we should have in Rhea as well? I guess it is useful when logging and when exceptions is thrown. For me at least the map is sufficient.

Edit: I'm not saying we should have a map in Rhea. Just asking wether we should have a name property in the rhea variable.

Adding stay with required strength

I'm not sure if this is a bug or not. I wrote some unit tests derived from the test required_strength which I expect to pass. Am I missing something?

BOOST_AUTO_TEST_CASE(required_strength_stay)
{
    variable v(0);
    simplex_solver solver;

    solver.add_stay(v, strength::required(), 1);

    BOOST_CHECK_EQUAL(v.value(), 0);

    solver.add_edit_var(v, strength::required(), 2);
    solver.begin_edit();
    solver.suggest_value(v, 2);
    solver.end_edit();

    BOOST_CHECK_EQUAL(v.value(), 2);
}

BOOST_AUTO_TEST_CASE(required_strength_stay_and_constraint)
{
    variable v(0), y(0);
    simplex_solver solver;

    solver.add_stay(v, strength::required(), 2);

    BOOST_CHECK_EQUAL(v.value(), 0);
    BOOST_CHECK_EQUAL(y.value(), 2);

    solver.add_edit_var(v, strength::required(), 3);
    solver.begin_edit();
    solver.suggest_value(v, 2);
    solver.end_edit();

    solver.add_constraint(y == v + 2);

    BOOST_CHECK_EQUAL(v.value(), 2);
    BOOST_CHECK_EQUAL(y.value(), 4);
}

Better callback system

It would be absolutely lovely to be able to write

rhea::variable containerHeight (10);
containerHeight.on_change = [self](const rhea::variable &v) {
    std::cout << "containerHeight changed to: " << v.value() << std::endl;
};

Instead of

solver.on_variable_change = [self](const rhea::variable& v, rhea::simplex_solver& s) {
    if(v.is(containerHeight)) {
        std::cout << "containerHeight changed to: " << v.value() << std::endl;
    }
}

The disadvantage with the current API is that a growing number of variables will also lead the a growing number of if's.

What do you think?

Question: Hashmap string => variable

How do I create a hashmap of variables where string is key?

I rather ask you than have something work by chance.

Pseudo code

std::unordered_map<std::string, rhea::variable> _variableMap;

- (rhea::variable)variableByName:(NSString *)name
{
    std::string key([name UTF8String]);
    rhea::variable variable = _variableMap[key];
    if(variable.is_nil())
    {
        variable = rhea::variable(0);
        _variableMap[key] = variable;
    }
    return variable;
}

Understanding the edit protocol

Hi!

I have a couple of questions regarding the edit protocol. The first one is a exception I stumbled upon today when adding a edit constraint on a variable that has no constraints. I got an edit_misuse exception thrown in add_constraint_ because this condition is not met !is_basic_var(v) && !columns_has_key(v). I wonder if this is really necessary (it seems conceptually feasible to edit an unconstrained variable).

Also, I have problems in my code because edits are not exactly stacked. Let's think for example of a multi-touch environment and a program where two dots can be moved with the fingers. The edits of the first and second dot might interleave, so the trivial association of touch/release to begin/end-edit does not work for situations like touch(p1) -> touch(p2) -> release(p1) -> release(p2). I wonder if a valid alternative is to have one single big edit around the whole program that guards all editable variables. Otherwise, could you hint on a better alternative within rhea or, alternatively, how could I change rhea to work this way?

In the lines of the later, I considered returning the identifier the edit_info_list_size_ in begin_edit and optionally taking this as an argument in end_edit to identify which variables exactly to remove.

What do you think?

Thanks!

Remove names from variables

Users that don't use variable names shouldn't have to pay for it. If an application needs named variables, it trivial to implement this with a (bi)map.

Maybe glitch in simplex solver when choosing subject?

Hi,

I'm studying with this library by rewriting a new Lua version of Cassowary algorithm. It's real good example of how to use C++11 to write neat code. but I think maybe I found a little glitch in simplex_solver.cpp#L532:

               // v is restricted.  If we have already found a suitable
                // restricted variable just stick with that.  Otherwise, if v
                // is new to the solver and has a negative coefficient pick
                // it.  Regarding being new to the solver -- if the variable
                // occurs only in the objective function we regard it as being
                // new to the solver, since error variables are added to the
                // objective function when we make the Expression.  We also
                // never pick a dummy variable here.
                if (!found_new_restricted && !v.is_dummy() && c < 0.0) {
                    auto i(columns_.find(v));
                    if (i == columns_.end()
                        || (columns_.size() == 1
                            && columns_has_key(objective_))) {
                        subj = v;
                        found_new_restricted = true;
                    }
                }

If I'm not misunderstanding, columns_ is a map with the parameter variable and the set of basic variable which expression contain this parameter variable. so to see the v is only occurs in objective function, maybe we should found objective_ in *i, which is the basic variable set, but not in columns_ mapping.

Excessive callbacks

When running with this code

    static rhea::simplex_solver solver;
    static rhea::variable testValue (10);

    solver.add_stay(testValue);

    solver.add_constraints({
        testValue >= 200,
    });

    solver.on_variable_change = [self](const rhea::variable& v, rhea::simplex_solver& s) {
        if(v.is(testValue)) {
            std::cout << "on_variable_change " << v.value() << std::endl;
        }
    };

    solver.on_resolve = [self](rhea::simplex_solver &solver) {
        std::cout << "on_resolve" << std::endl;
    };

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        solver.suggest({ { testValue, 500 } });
    });

I get this output

on_variable_change 200
on_resolve
on_variable_change 500
on_resolve
on_variable_change 500
on_resolve

From a user point of view it would be beneficial if it was one callback less like this

on_variable_change 200
on_resolve
on_variable_change 500
on_resolve

The stack trace for the on_resolve callback is

First time
screen shot 2014-09-23 at 11 45 17

Second time
screen shot 2014-09-23 at 11 45 25

Third time
screen shot 2014-09-23 at 11 45 32

Expression parser

The library really needs an expression parser module. Look into boost.spirit.

Is there any way to round variables?

Hi

When dealing with constriants like

textLeftPosition == containerWidth * 0.1,
textWidth == containerWidth * 0.8

The text quickly gets blury due to subpixel positioning? Is there any way in Rhea / Cassowary to say that I'd rather have the values rounded? Or should this logic reside outside of Rhea / Cassowary?

nil is reserved

ObjC has nil defined as

#ifndef nil
# if __has_feature(cxx_nullptr)
#   define nil nullptr
# else
#   define nil __DARWIN_NULL
# endif
#endif

and you have

class variable {
    static variable nil() { return variable(); }
}

it breaks if you try to include rhea

Nothing happens on setting constant through set_constant method

Here is the sample project which is reproducing the problem: https://github.com/har-gup/rhea-constant-issue.

The same issue doesn't happen if we use edit variable in a constraint instead of constant.
I tried debugging the issue, and saw that set_constant_ method is returning from https://github.com/Nocte-/rhea/blob/0.3/rhea/simplex_solver.cpp#L184 without adding any row to infeasible_rows_, and therefore dual_optimize function is not called.

@Nocte- If we change following lines https://github.com/Nocte-/rhea/blob/0.3/rhea/simplex_solver.cpp#L164 and https://github.com/Nocte-/rhea/blob/0.3/rhea/simplex_solver.cpp#L202 to
auto delta = -(constant - evs.prev_constant);, and expr.add(expr.coefficient(evs.marker) * delta); respectively, the way it is in suggest_value_ function https://github.com/Nocte-/rhea/blob/0.3/rhea/simplex_solver.cpp#L265 then it works fine.

Subclassing variables

We discussed this briefly on slack. "it can only be done as part of the library, since rhea::variable needs to know about them."

The reason I want to subclass variables is to deny users from using the same variable in multiple solvers.

My setup is like this:

A container can have many nodes. Each node has its own set of variables (left, right, bottom, top, etc). Each container has its own solver. A node can be a container. And here's the catch: the user should only use container-specific variables like contentHeight and contentWidth between the container and its nodes, not between siblings and parent container.

Any ideas?

Unexpected behavior of constraints

Expectations: I am expecting greenView to be positioned wherever blueView is positioned with an offset of 210 on the x-axis.

    solver.add_constraints({
        greenLeft == blueLeft + 210,
    });

Result: blueView is not moving when suggesting new values.

Code

#import "ViewController.h"
#include "rhea/simplex_solver.hpp"
#include "rhea/iostream.hpp"

static rhea::simplex_solver solver;
static rhea::variable blueLeft (10), blueTop (10), blueWidth (200), blueHeight (200);
static rhea::variable greenLeft (10), greenTop (10), greenWidth (200), greenHeight (200);
static rhea::variable boundsWidth (1000), boundsHeight (768);

@interface ViewController ()

@property (nonatomic, strong) UIView *blueView;
@property (nonatomic, strong) UIView *greenView;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self setupViews];
    [self setupConstraints];
    [self updateFrames];
}

- (void)setupViews
{
    self.blueView = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 200, 200)];
    self.blueView.backgroundColor = [UIColor blueColor];
    [self.view addSubview:self.blueView];

    UIPanGestureRecognizer *blueRecognizer = [UIPanGestureRecognizer new];
    [blueRecognizer addTarget:self action:@selector(blueRecognizerUpdated:)];
    [self.blueView addGestureRecognizer:blueRecognizer];

    self.greenView = [[UIView alloc] initWithFrame:CGRectMake(220, 10, 200, 200)];
    self.greenView.backgroundColor = [UIColor greenColor];
    [self.view addSubview:self.greenView];

    UIPanGestureRecognizer *greenRecognizer = [UIPanGestureRecognizer new];
    [greenRecognizer addTarget:self action:@selector(greeRecognizerUpdated:)];
    [self.greenView addGestureRecognizer:greenRecognizer];
}

- (void)setupConstraints
{
    solver.set_autosolve(false);

    solver.add_constraints({
        greenLeft == blueLeft + 210,
    });

    solver.add_constraints({
        greenTop == blueTop,
    });

    solver.solve();
}

- (void)updateFrames
{
    CGRect blueFrame;
    blueFrame.origin.x = blueLeft.value();
    blueFrame.origin.y = blueTop.value();
    blueFrame.size.width = blueWidth.value();
    blueFrame.size.height = blueHeight.value();
    self.blueView.frame = blueFrame;

    CGRect greenFrame;
    greenFrame.origin.x = greenLeft.value();
    greenFrame.origin.y = greenTop.value();
    greenFrame.size.width = greenWidth.value();
    greenFrame.size.height = greenHeight.value();
    self.greenView.frame = greenFrame;
}

- (void)blueRecognizerUpdated:(UIPanGestureRecognizer *)recognizer
{
    CGPoint translation = [recognizer translationInView:recognizer.view.superview];
    [recognizer setTranslation:CGPointZero inView:recognizer.view.superview];

    solver.suggest({
        { blueLeft, self.blueView.frame.origin.x + translation.x },
        { blueTop, self.blueView.frame.origin.y + translation.y }
    });

    solver.solve();
    [self updateFrames];
}

- (void)greeRecognizerUpdated:(UIPanGestureRecognizer *)recognizer
{
    CGPoint translation = [recognizer translationInView:recognizer.view.superview];
    [recognizer setTranslation:CGPointZero inView:recognizer.view.superview];

    solver.suggest({
        { greenLeft, self.greenView.frame.origin.x + translation.x },
        { greenTop, self.greenView.frame.origin.y + translation.y }
    });

    solver.solve();
    [self updateFrames];
}

@end

Crash when adding required edit variable

I'm doing some research into the various implementations of the Cassowary algorithm to spot their differences. Part of this process is comparing tests for these implementations. Upon porting one of the unit tests from the javascript port at https://github.com/slightlyoff/cassowary.js/ I noticed that Rhea crashes when dealing with stay constraints having zero weight.

The failing test case is:

BOOST_AUTO_TEST_CASE (required_strength)
{
    variable variable(0);
    simplex_solver solver;

    constraint constraint(std::make_shared<stay_constraint>(variable, strength::strong(), 0));
    solver.add_constraint(constraint);
    solver.resolve();

    BOOST_CHECK_EQUAL(variable.value(), 0);

    solver.add_edit_var(variable, strength::required());
    solver.begin_edit();
    solver.suggest_value(variable, 2);
    solver.resolve();

    BOOST_CHECK_EQUAL(variable.value(), 2);
}

The backtrace I got:

* thread #1: tid = 0x19edf7, 0x00000001000469f2 librhea.1.dylib`rhea::linear_expression::substitute_out(rhea::variable const&, rhea::linear_expression const&, rhea::variable const&, rhea::tableau&) + 162, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
  * frame #0: 0x00000001000469f2 librhea.1.dylib`rhea::linear_expression::substitute_out(rhea::variable const&, rhea::linear_expression const&, rhea::variable const&, rhea::tableau&) + 162
    frame #1: 0x0000000100055379 librhea.1.dylib`rhea::tableau::substitute_out(rhea::variable const&, rhea::linear_expression const&) + 313
    frame #2: 0x000000010004b13a librhea.1.dylib`rhea::simplex_solver::pivot(rhea::variable const&, rhea::variable const&) + 74
    frame #3: 0x000000010004c943 librhea.1.dylib`rhea::simplex_solver::optimize(rhea::variable const&) + 659
    frame #4: 0x0000000100049661 librhea.1.dylib`rhea::simplex_solver::add_with_artificial_variable(rhea::linear_expression&) + 241
    frame #5: 0x0000000100048ea7 librhea.1.dylib`rhea::simplex_solver::add_constraint_(rhea::constraint const&) + 647
    frame #6: 0x000000010001cd4e unit_tests`rhea::simplex_solver::add_edit_var(rhea::variable const&, rhea::strength const&, double) + 254
    frame #7: 0x000000010001a6c1 unit_tests`required_strength::test_method() + 785
    frame #8: 0x0000000100080781 libboost_unit_test_framework-mt.dylib`boost::unit_test::ut_detail::callback0_impl_t<int, boost::unit_test::(anonymous namespace)::zero_return_wrapper_t<boost::unit_test::callback0<boost::unit_test::ut_detail::unused> > >::invoke() + 17
    frame #9: 0x0000000100065850 libboost_unit_test_framework-mt.dylib`boost::execution_monitor::catch_signals(boost::unit_test::callback0<int> const&) + 160
    frame #10: 0x00000001000658f7 libboost_unit_test_framework-mt.dylib`boost::execution_monitor::execute(boost::unit_test::callback0<int> const&) + 39
    frame #11: 0x000000010008055f libboost_unit_test_framework-mt.dylib`boost::unit_test::unit_test_monitor_t::execute_and_translate(boost::unit_test::test_case const&) + 159
    frame #12: 0x0000000100072097 libboost_unit_test_framework-mt.dylib`boost::unit_test::framework_impl::visit(boost::unit_test::test_case const&) + 151
    frame #13: 0x00000001000a478e libboost_unit_test_framework-mt.dylib`boost::unit_test::traverse_test_tree(boost::unit_test::test_suite const&, boost::unit_test::test_tree_visitor&) + 350
    frame #14: 0x000000010006ee25 libboost_unit_test_framework-mt.dylib`boost::unit_test::framework::run(unsigned long, bool) + 4181
    frame #15: 0x000000010007ec03 libboost_unit_test_framework-mt.dylib`boost::unit_test::unit_test_main(bool (*)(), int, char**) + 211
    frame #16: 0x00007fff89c5e5fd libdyld.dylib`start + 1

Stays do not always stay

Hi!

So... as discussed on Slack, I have been having trouble with stays not really having the desired effect all of the time. Indeed, quite often, when I add or remove constraints, variables jump around even though they should not. This have driven me crazy for some time and led to resort to all kinds of hacks. Because I use Rhea via aqt-cassowary [1], this has been very hard to reproduce -- this library usually manipulate the solver in non-deterministic, but in theory correct, orders.

So, finally I have been able to write a test that fails sistematically in my machine here. I have two variations of the test, that execute the sames calls in different order. In one it fails, in the other it passes. Funny thing is, the failing test requires only to change the order in which variables are declared just to make it pass. Crazy shit.

So, here are the tests:
arximboldi@adbfdf5

[1] https://github.com/AbletonAG/aqt-cassowary

Is there any way to create read-only external variables?

I am most certainly missing something :-)

Much like constants there are some things that should never be updated by the solver. For instance the dimension of the screen on the device. The only time the dimension of the screen changes is when the user is rotating thus flipping the width and height.

Any thoughts? Should I play around with the strength of variable and constraints?

Many constraints are being solved to 0

Expectations: I am expecting the blueTop and greenTop to initially be resolved to 10 and then later resolved to whatever I suggest.

Result: The constraint is resolved 0 all the time

#import "ViewController.h"
#include "rhea/simplex_solver.hpp"
#include "rhea/iostream.hpp"

static rhea::simplex_solver solver;
static rhea::variable blueLeft (10), blueTop (10), blueWidth (200), blueHeight (200);
static rhea::variable greenLeft (10), greenTop (10), greenWidth (200), greenHeight (200);
static rhea::variable boundsWidth (1000), boundsHeight (768);

@interface ViewController ()

@property (nonatomic, strong) UIView *blueView;
@property (nonatomic, strong) UIView *greenView;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self setupViews];
    [self setupConstraints];
    [self updateFrames];
}

- (void)setupViews
{
    self.blueView = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 200, 200)];
    self.blueView.backgroundColor = [UIColor blueColor];
    [self.view addSubview:self.blueView];

    UIPanGestureRecognizer *blueRecognizer = [UIPanGestureRecognizer new];
    [blueRecognizer addTarget:self action:@selector(blueRecognizerUpdated:)];
    [self.blueView addGestureRecognizer:blueRecognizer];

    self.greenView = [[UIView alloc] initWithFrame:CGRectMake(220, 10, 200, 200)];
    self.greenView.backgroundColor = [UIColor greenColor];
    [self.view addSubview:self.greenView];

    UIPanGestureRecognizer *greenRecognizer = [UIPanGestureRecognizer new];
    [greenRecognizer addTarget:self action:@selector(greeRecognizerUpdated:)];
    [self.greenView addGestureRecognizer:greenRecognizer];
}

- (void)setupConstraints
{
    solver.set_autosolve(false);

    solver.add_constraints({
        blueLeft >= 20,
    });

    solver.add_constraints({
        greenLeft <= 400,
    });

    solver.add_constraints({
        greenTop == blueTop,
    });

    solver.solve();
}

- (void)updateFrames
{
    CGRect blueFrame;
    blueFrame.origin.x = blueLeft.value();
    blueFrame.origin.y = blueTop.value();
    blueFrame.size.width = blueWidth.value();
    blueFrame.size.height = blueHeight.value();
    self.blueView.frame = blueFrame;

    CGRect greenFrame;
    greenFrame.origin.x = greenLeft.value();
    greenFrame.origin.y = greenTop.value();
    greenFrame.size.width = greenWidth.value();
    greenFrame.size.height = greenHeight.value();
    self.greenView.frame = greenFrame;
}

- (void)blueRecognizerUpdated:(UIPanGestureRecognizer *)recognizer
{
    CGPoint translation = [recognizer translationInView:recognizer.view.superview];
    [recognizer setTranslation:CGPointZero inView:recognizer.view.superview];

    solver.suggest({
        { blueLeft, self.blueView.frame.origin.x + translation.x },
        { blueTop, self.blueView.frame.origin.y + translation.y }
    });

    solver.solve();
    [self updateFrames];
}

- (void)greeRecognizerUpdated:(UIPanGestureRecognizer *)recognizer
{
    CGPoint translation = [recognizer translationInView:recognizer.view.superview];
    [recognizer setTranslation:CGPointZero inView:recognizer.view.superview];

    solver.suggest({
        { greenLeft, self.greenView.frame.origin.x + translation.x },
        { greenTop, self.greenView.frame.origin.y + translation.y }
    });

    solver.solve();
    [self updateFrames];
}

@end

Solving constraints does not work in a dynamic application

I have tried your library, but it just does not work. This is my code:

//
//  AFRheaTestView.m
//  FlexSchematics
//
//  Created by Phillip Schuster on 18.11.15.
//  Copyright © 2015 Appfruits. All rights reserved.
//

#import "AFRheaTestView.h"

#include "rhea/simplex_solver.hpp"
#include "rhea/iostream.hpp"
#include "rhea/variable.hpp"

@interface AFRheaTestView ()
@property (nonatomic, strong) NSTrackingArea* trackingArea;
@property (nonatomic, assign) BOOL dragged;
@property (nonatomic, assign) rhea::variable l1X;
@property (nonatomic, assign) rhea::variable l1Y;
@property (nonatomic, assign) rhea::variable r1X;
@property (nonatomic, assign) rhea::variable r1Y;
@property (nonatomic, assign) rhea::simplex_solver solver;

@end

@implementation AFRheaTestView

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    // Drawing code here.
    [[NSColor magentaColor] setStroke];
    NSBezierPath* bezierPath = [NSBezierPath bezierPath];
    [bezierPath moveToPoint:NSMakePoint(self.l1X.value(), self.l1Y.value())];
    [bezierPath lineToPoint:NSMakePoint(self.r1X.value(), self.r1Y.value())];
    [bezierPath stroke];
}

-(void)awakeFromNib
{
//  self.l1X.set_value(100);
//  self.l1Y.set_value(100);

    self.solver.set_autosolve(false);

    self.solver.on_resolve = [self](rhea::simplex_solver &solver) {
        std::cout << "on_resolve" << std::endl;
    };

    self.solver.on_variable_change = [self](const rhea::variable& v, rhea::simplex_solver& s) {
        std::cout << "on_variable_change " << v.value() << std::endl;
    };

    self.solver.add_stay(_l1X);
    self.solver.add_stay(_l1Y);

    self.solver.set_explaining(true);

    self.solver.add_constraint(_r1X == _l1X + 100);
    self.solver.add_constraint(_r1Y == _l1Y + 100);
    self.solver.solve();

    NSTrackingAreaOptions options = (NSTrackingActiveAlways | NSTrackingInVisibleRect |
                                     NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved);
    self.trackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds options:options owner:self userInfo:nil];
    [self addTrackingArea:self.trackingArea];
}

-(void)updateTrackingAreas
{
    [self removeTrackingArea:self.trackingArea];
    NSTrackingAreaOptions options = (NSTrackingActiveAlways | NSTrackingInVisibleRect |
                                     NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved);
    self.trackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds options:options owner:self userInfo:nil];
    [self addTrackingArea:self.trackingArea];
}

-(void)mouseMoved:(NSEvent *)theEvent
{
    CGPoint curPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil];

    self.solver.suggest(_l1X, curPoint.x);
    self.solver.suggest(_l1Y, curPoint.y);
    self.solver.solve();

    [self setNeedsDisplay:YES];
}

@end

I have attached a screenshot that shows what is the result. As you can see, it seems to work for the first time (l1X and l1Y is 0 at the beginning which is the lower left corner in Mac OS X), but r1X and r1Y never change when moving the mouse.

What's wrong with my code?

bildschirmfoto 2015-11-18 um 15 11 50

Values get changed back to original

This code

    static rhea::simplex_solver solver;
    static rhea::variable testValue (10);

    solver.add_constraints({
        testValue >= 200
    });

    solver.on_variable_change = [self](const rhea::variable& v, rhea::simplex_solver& s) {
        if(v.is(testValue)) {
            NSLog(@"Blue top %.0f", v.value());
        }
    };

    solver.on_resolve = [self](rhea::simplex_solver &solver) {
        NSLog(@"Solved. Blue top: %.0f", testValue.value());
    };

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        solver.suggest({ { testValue, 500 } });
    });

Produces

2014-09-22 17:16:38.879 IVYLayoutEngine[51431:4937714] Blue top 200
2014-09-22 17:16:38.879 IVYLayoutEngine[51431:4937714] Solved. Blue top: 200
2014-09-22 17:16:38.880 IVYLayoutEngine[51431:4937714] Blue top 500
2014-09-22 17:16:38.880 IVYLayoutEngine[51431:4937714] Solved. Blue top: 500
2014-09-22 17:16:38.880 IVYLayoutEngine[51431:4937714] Blue top 200
2014-09-22 17:16:38.880 IVYLayoutEngine[51431:4937714] Solved. Blue top: 200

I would expect it to produce

2014-09-22 17:16:38.879 IVYLayoutEngine[51431:4937714] Blue top 200
2014-09-22 17:16:38.879 IVYLayoutEngine[51431:4937714] Solved. Blue top: 200
2014-09-22 17:16:38.880 IVYLayoutEngine[51431:4937714] Blue top 500
2014-09-22 17:16:38.880 IVYLayoutEngine[51431:4937714] Solved. Blue top: 500

Breaking changes between 0.2.x and 0.3

I am trying out the 0.3 branch. When it is ready I recommend publishing it as version 1.0.0 as there are several breaking changes. Following Semantic Versioning is always a good thing. It suggests bumping the major number (major.minor.patch) whenever making a breaking change.

I would like to publish a podspec named "1.0.0-beta" pointing to commit cfd33f3.

Nonlinear expressions

It seems like nonlinear expressions is handled automagically. I don't know if this is by design since the java version throws a "nonlinear expression exception".

// This is a nonlinear expression and should not be supported
BOOST_AUTO_TEST_CASE(variable_is_denominator)
{
    variable x(0), y(0);
    simplex_solver solver;

    BOOST_CHECK_EQUAL(x.value(), 0);
    BOOST_CHECK_EQUAL(y.value(), 0);

    solver.add_constraint(y == 10);

    BOOST_CHECK_EQUAL(x.value(), 0);
    BOOST_CHECK_EQUAL(y.value(), 10);

    solver.add_constraint(x == 5 / y);

    BOOST_CHECK_EQUAL(x.value(), 0.5);
    BOOST_CHECK_EQUAL(y.value(), 10);
}

// This is a linear expression and should be supported
BOOST_AUTO_TEST_CASE(variable_is_numerator)
{
    variable x(0), y(0);
    simplex_solver solver;

    BOOST_CHECK_EQUAL(x.value(), 0);
    BOOST_CHECK_EQUAL(y.value(), 0);

    solver.add_constraint(y == 10);

    BOOST_CHECK_EQUAL(x.value(), 0);
    BOOST_CHECK_EQUAL(y.value(), 10);

    solver.add_constraint(x == y / 5);

    BOOST_CHECK_EQUAL(x.value(), 2);
    BOOST_CHECK_EQUAL(y.value(), 10);
}

Result

Running 41 test cases...
/unit_tests.cpp:777: error in "variable_is_denominator": check x.value() == 0.5 failed [2 != 0.5]

*** 1 failure detected in test suite "rhea"

This is the Rhea implementation

linear_expression& linear_expression::operator/=(const linear_expression& x)
{
    if (is_constant())
        return *this = x / constant();

    if (!x.is_constant())
        throw nonlinear_expression();

    return operator/=(x.constant());
}

This is the java implementation

public final Expression divide(double x)
           throws NonlinearExpression
   {
       if (Util.approx(x, 0.0))
       {
           throw new NonlinearExpression();
       }
       return times(1.0 / x);
   }

Building branch 0.3

Screenshot:
skjermbilde 2016-06-08 kl 09 55 44

I'm not sure what's wrong. I'm not that familiar with std and c++. In the podspec I'm stating

    s.ios.deployment_target = '6.0'
    s.osx.deployment_target = '10.8'
    s.library = 'c++'
    s.pod_target_xcconfig = {
       'CLANG_CXX_LANGUAGE_STANDARD' => 'c++11',
       'CLANG_CXX_LIBRARY' => 'libc++'
    }

Does those settings seem right to you?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.