Closed hfossli closed 4 days ago
use am_hasvariable()
but notice that if you add a variable to solver and remove it, am_hasvariable()
still return true. because I don't know whether other constraint in solver use this variable.
a >= 5 In this can hasvariable will be true, right, but the value is still underconstrained.
Another example a + b == 100 Both a and b are underconstrained
yes, in first case hasvariable(a)
will return true, and in second case a and b all return true. if you create a variable, hasvariable()
to it will return false, but after you add the constraint containing it to solver, hasvariable() will return true. but after you remove all constraints containing this variable from solver, hasvariable() will still return true.
So it sounds like am_hasvariable()
is not a viable solution...
but I don't have other idea to do that. this API is designed to do this, but to track variable need more cost. do you really need this function? I will think to implement if you really want it.
Think about it :)
The designers in my workplace would love such a feature. Our designers write json like this
"title.left == width / 2 - title.width / 2 - 10",
"title.height == title.intrinsicHeight",
"title.width == title.intrinsicWidth",
"title.top == category.bottom",
Sometimes they forget to constraint all four variables (top, left, width, height) either explicitly or implicitly (using e.g. right, bottom). In a case like this the designer can get confused if he forgets one constraint
"title.left == width / 2 - title.width / 2 - 10",
"title.height == title.intrinsicHeight",
"title.width == title.intrinsicWidth",
Having a warning telling him 'title' is not constrained for variable 'top'
could be very useful.
am_isunderconstrained(a)
should return TRUE
a >= 1
should return TRUE
a >= 1
a <= 10
should return FALSE
a >= 1
a <= 10
a == 5 !strong
should return FALSE
a == 5
should return TRUE
(nothing)
If you are going to look at this I can write tests for you. :)
why a >= 1
return TRUE but a >= 1 && a <= 10
return FALSE?
I think I am not communicating clearly. Statement is above code (not under)
So, why (nothing)
returns TRUE and a == 5
returns FALSE?
Sorry, I'll try to communicate more clearly.
I'm thinking of underconstrained variables as variables that have a multiple correct answers.
a >= 1
In this case a
can be any value above 1 and is thus underconstrained. We don't know if it really should be 1, 2, 3, 4, 5, 6... or any other value above 1, right?
a == 1
In this case a
can only be 1 and is thus not underconstrained.
a >= 1
a <= 10
In this case a
can be any value above 1 and below 10 and is thus underconstrained. We don't know if it really should be 1, 2, 3, 4, 5, 6, 7, 8 or 9 or any other value in between like 5.12315.
a == b
b >= 5
In this case a
can be any value above 5 and is thus underconstrained.
b == c
c == d
d == 5
In this case a
can be any value and is thus underconstrained because there's no constraints for a
. b
, c
and d
on the other is not underconstrained.
Let me know if I can explain better.
So, if a
can be any value, then it's underconstrainted, but if it can only be the exactly one value, so it's not underconstrainted, right?
and, what if: a == 30 weak a >= 50 strong
and then a = 50, is a
underconstrainted?
So, if a can be any value, then it's underconstrainted, but if it can only be the exactly one value, so it's not underconstrainted, right?
Yep, that's what I'm thinking
and then a = 50, is a underconstrainted?
No, it is not underconstrained. It is sufficiently constrained to be 50 because a == 30 weak
pulls it to the lowest possible value while satisfying constraint a >= 50 strong
You posed a really good question. I thought maybe I could write something like this
public func isUnderconstrained(_ variable: Variable) -> Bool {
do {
let currentValue: Double = try value(variable)
let dummyConstraint: Constraint = variable == currentValue
try add(dummyConstraint)
try remove(dummyConstraint)
return true
} catch let error {
return false
}
}
(😬 this will probably be a little bit expensive?)
Which makes this test pass
func testUnderconstrained() {
let solver = Solver(debug: true)
do {
let v = Variable()
XCTAssertTrue(solver.isUnderconstrained(v))
let constraint = v == 5
try solver.add(constraint)
XCTAssertFalse(solver.isUnderconstrained(v))
try solver.remove(constraint)
XCTAssertTrue(solver.isUnderconstrained(v))
} catch let error as AmoebaError {
switch error {
case .unbound:
break
default:
XCTFail("Didn't expect \(error)")
break
}
} catch let error {
XCTFail("Didn't expect \(error)")
}
}
But that doesn't work when using constraints which isn't using required
strength.
func testUnderconstrained2() {
let solver = Solver(debug: true)
do {
let v = Variable()
XCTAssertTrue(solver.isUnderconstrained(v))
let constraint = (v == 5).strong()
try solver.add(constraint)
XCTAssertFalse(solver.isUnderconstrained(v)) // <<< fails
try solver.remove(constraint)
XCTAssertTrue(solver.isUnderconstrained(v))
} catch let error as AmoebaError {
switch error {
case .unbound:
break
default:
XCTFail("Didn't expect \(error)")
break
}
} catch let error {
XCTFail("Didn't expect \(error)")
}
}
I find the question about underconstraint (its meaning and its influence on the workflow of designers) quite important. Any more thoughts on this?
I need rethought this thing, because now I changed the API it export. Can you give me some insight about how you will use this function?
It is only useful at the time of writing constraints. Give the designers feedback on which variables that is not being constrained sufficiently to have a deterministic behavior.
A designer places a textbox on screen. The textbox is placed given the constraints
left == 0
top == 0
height == automatic_height
But then nothing shows up. The designer scratches his head because he is not aware that it is missing a constraint for the width.
He should be given a cue (on demand) about how to sufficiently constrain this text box.
He could write
right == 200
Or
width == 200
in order to be sufficiently constrained. Now the textbox is visible.
Having some API to tell our designers of his mistake is what we are asking for – ~as opposed to fixing/adding constraints automatically~
But if you don't add width to amoeba, it can't know whether it need under constraint. It's somewhat "business related" in this case.
For me it's for the same reason as @hfossli described. I understand, that it's not inherently needed for Amoeba, but we might treat this as an optional extension to the Amoeba functionality or even API. So what I'm asking is a built-in support for the possibility to create such an extension.
The extension might be also in a form of a flag indicating how the rule evaluation should proceed - we might call it somehow like "a stronger requirement on rules".
I think I have an idea. It won't be efficient computation wise, but it will probably do the job. I'll try to post next week
@dumblob this is acceptable I think. can you give some examples or definitions for the plugin interface?
I've thought about it, but everything I came up with was not good enough. I think we should wait the few days for @hfossli and his new idea and proceed with the discussion afterwards. Should something worth mentioning come to my mind in the mean time, I'll post it here.
I think my new idea might be flawed after all... :( maybe @kongtomorrow has some ideas?
My suggestion is to improve one of my earlier suggestions in this thread. Introduce a new function AM_API bool am_isunderconstrained (am_Variable *var);
It could do the following:
I'm sure there are edge cases that won't be covered. It might be quite expensive, but it should only be done at the time of writing constraints (oppsoed to consuming).
@hfossli did you succeed to implement your last proposal? Anything I could take a look at and try it to see what are the caveats of this logic?
Any comments @kongtomorrow?
I ran out of time and that feature was put in backlog unfortunately. I think I made it work the way I described it. I guess it messes up stays and edit variables though.
I would like to ask the solver wether a variable is underconstrained or not. Maybe I can try to add a constraint for a variable and see if I get an error? Is there better ways?