Rigid body constraints rewritten
Ok - I botched it. Not really that obviously, but still, the code for the rigid body constraints was not thought out enough. The issue there is the lifetime of things: When do things start to appear in the animation?
My implicit assumption was that the first time a thing (i.e., a thing's anchor) is mentioned, it will be rendered into the animation. But then, I cannot hook all rigid body constraints to the first step - because then, all things will try to appear in the first step. However, at this point the constraints explicitly written in the script are still missing! - so the constraint solver will not be able to compute a location for the things, and hence it will complain.
I therefore rewrote the rigid body constraint code so that it now creates these constraints at the first step where a thing's anchor appears on the left side of a constraint. In the course of this, I also added code to create the 2d constraints (and I found out that the MovimentumParser.ConstAdd() method did not behave well with 2d anchors - it needed another if. The code looks now a little "over-if-ed", but for this helper method, so be it.)
Altogether, the code for these constraints is about 110 lines.
Debugging Output = ToString()
Moreover, I now added (hopefully) sensible code for ToString() of constraints and expressions so that they are more or less easily readable in the debugger. I will not explain that code too deeply - it is only for debugging -, but only give a short overview. If you are interested, please look into the github repository.
The main parts of the ToString() code are:
- Each expression gets a ToString method with an additional parameter for controlling the parentheses. The code of this methods is one of the following if the expresion is a binary or a unary expression:
protected internal override string ToString(AbstractOperator parentOp) {
return parentOp.Wrap(_lhs, _operator, _rhs);
}
return parentOp.Wrap(_lhs, _operator, _rhs);
}
protected internal override string ToString(AbstractOperator parentOp) {
return parentOp.Wrap(_operator, _inner);
}
return parentOp.Wrap(_operator, _inner);
}
- This code is used in a ToString() method for all expressions: I wasted the precious resource "base class" for this feature - both ScalarExpr and VectorExpr now derive from a class Expr which has the ToString() method in it:
public abstract class Expr {
public override string ToString() {
return ToString(AbstractOperator.IGNORE_OP);
}
protected internal abstract string ToString(AbstractOperator parentOp);
}
public override string ToString() {
return ToString(AbstractOperator.IGNORE_OP);
}
protected internal abstract string ToString(AbstractOperator parentOp);
}
- Finally, the following code inside AbstractOperator handles the parenthesizing by a mutually recursive call to the ToString(AbstractOperator parentOp) method from the first item above:
public string Wrap(Expr lhs, AbstractOperator op, Expr rhs) {
string s = lhs.ToString(op) + op + rhs.ToString(op);
return op.Precedence < Precedence ? "(" + s + ")" : s;
}
public string Wrap(AbstractOperator op, Expr e) {
string s = e.ToString(op) + op;
return op.Precedence < Precedence ? "(" + s + ")" : s;
}
string s = lhs.ToString(op) + op + rhs.ToString(op);
return op.Precedence < Precedence ? "(" + s + ")" : s;
}
public string Wrap(AbstractOperator op, Expr e) {
string s = e.ToString(op) + op;
return op.Precedence < Precedence ? "(" + s + ")" : s;
}
So that the precedence check and output works, operators need to be defined with the correct precedence and output string. Here is an example for the binay scalar operators - the precedences are copied over from the grammar (if the operator occurs in simpleexpr2, precedence is 2; if it occurs in simpleexpr3, then 3, etc.):
public class BinaryScalarOperator : AbstractOperator {
private BinaryScalarOperator(int precedence, string asString) : base(precedence, asString) { }
public static BinaryScalarOperator PLUS = new BinaryScalarOperator(1, " + ");
public static BinaryScalarOperator MINUS = new BinaryScalarOperator(1, " - ");
public static BinaryScalarOperator TIMES = new BinaryScalarOperator(2, " * ");
public static BinaryScalarOperator DIVIDE = new BinaryScalarOperator(2, " / ");
}
private BinaryScalarOperator(int precedence, string asString) : base(precedence, asString) { }
public static BinaryScalarOperator PLUS = new BinaryScalarOperator(1, " + ");
public static BinaryScalarOperator MINUS = new BinaryScalarOperator(1, " - ");
public static BinaryScalarOperator TIMES = new BinaryScalarOperator(2, " * ");
public static BinaryScalarOperator DIVIDE = new BinaryScalarOperator(2, " / ");
}
For the moment, this works as expected. And as it is only debugging output, I confess that I skipped the unit tests for this.
No comments:
Post a Comment