private boolean operate() throws ScriptException {
Token op = oStack[oPt--];
Point3f pt;
Point4f pt4;
Matrix3f m;
String s;
float f;
if (logMessages) {
dumpStacks("operate: " + op);
}
// check for a[3][2]
if (isArrayItem && squareCount == 0 && equalCount == 1 && oPt < 0
&& (op.tok == Token.opEQ)) {
return true;
}
ScriptVariable x2 = getX();
if (x2 == Token.tokenArraySelector)
return false;
// unary:
if (x2.tok == Token.varray || x2.tok == Token.matrix3f
|| x2.tok == Token.matrix4f)
x2 = ScriptVariable.selectItem(x2);
if (op.tok == Token.minusMinus || op.tok == Token.plusPlus) {
if (!isSyntaxCheck && !x2.increment(incrementX))
return false;
wasX = true;
putX(x2); // reverse getX()
return true;
}
if (op.tok == Token.opNot) {
if (isSyntaxCheck)
return addX(true);
switch (x2.tok) {
case Token.point4f: // quaternion
return addX((new Quaternion((Point4f) x2.value)).inv().toPoint4f());
case Token.matrix3f:
m = new Matrix3f((Matrix3f) x2.value);
m.invert();
return addX(m);
case Token.matrix4f:
Matrix4f m4 = new Matrix4f((Matrix4f) x2.value);
m4.invert();
return addX(m4);
case Token.bitset:
return addX(BitSetUtil.copyInvert(ScriptVariable.bsSelect(x2),
(x2.value instanceof BondSet ? viewer.getBondCount() : viewer
.getAtomCount())));
default:
return addX(!ScriptVariable.bValue(x2));
}
}
int iv = op.intValue & ~Token.minmaxmask;
if (op.tok == Token.propselector) {
switch (iv) {
case Token.length:
if (!(x2.value instanceof BondSet))
return addX(ScriptVariable.sizeOf(x2));
break;
case Token.size:
return addX(ScriptVariable.sizeOf(x2));
case Token.type:
return addX(ScriptVariable.typeOf(x2));
case Token.keys:
if (x2.tok != Token.hash)
return addX("");
Object[] keys = ((Map) x2.value).keySet().toArray();
Arrays.sort(keys);
String[] ret = new String[keys.length];
for (int i = 0; i < keys.length; i++)
ret[i] = (String) keys[i];
return addX(ret);
case Token.lines:
switch (x2.tok) {
case Token.matrix3f:
case Token.matrix4f:
s = ScriptVariable.sValue(x2);
s = TextFormat.simpleReplace(s.substring(1, s.length() - 1), "],[",
"]\n[");
break;
case Token.string:
s = (String) x2.value;
break;
default:
s = ScriptVariable.sValue(x2);
}
s = TextFormat.simpleReplace(s, "\n\r", "\n").replace('\r', '\n');
return addX(TextFormat.split(s, '\n'));
case Token.color:
switch (x2.tok) {
case Token.string:
case Token.varray:
s = ScriptVariable.sValue(x2);
pt = new Point3f();
return addX(Graphics3D.colorPointFromString(s, pt));
case Token.integer:
case Token.decimal:
return addX(viewer.getColorPointForPropertyValue(ScriptVariable
.fValue(x2)));
case Token.point3f:
return addX(Escape.escapeColor(Graphics3D
.colorPtToInt((Point3f) x2.value)));
default:
// handle bitset later
}
break;
case Token.boundbox:
return (isSyntaxCheck ? addX("x") : getBoundBox(x2));
}
if (isSyntaxCheck)
return addX(ScriptVariable.sValue(x2));
if (x2.tok == Token.string) {
Object v = ScriptVariable
.unescapePointOrBitsetAsVariable(ScriptVariable.sValue(x2));
if (!(v instanceof ScriptVariable))
return false;
x2 = (ScriptVariable) v;
}
if (op.tok == x2.tok)
x2 = getX();
return getPointOrBitsetOperation(op, x2);
}
// binary:
ScriptVariable x1 = getX();
if (isSyntaxCheck) {
if (op == Token.tokenAndFALSE || op == Token.tokenOrTRUE)
isSyntaxCheck = false;
return addX(new ScriptVariable(x1));
}
switch (op.tok) {
case Token.opAND:
case Token.opAnd:
switch (x1.tok) {
case Token.bitset:
BitSet bs = ScriptVariable.bsSelect(x1);
switch (x2.tok) {
case Token.bitset:
bs = BitSetUtil.copy(bs);
bs.and(ScriptVariable.bsSelect(x2));
return addX(bs);
case Token.integer:
int x = ScriptVariable.iValue(x2);
return (addX(x < 0 ? false : bs.get(x)));
}
break;
}
return addX(ScriptVariable.bValue(x1) && ScriptVariable.bValue(x2));
case Token.opOr:
switch (x1.tok) {
case Token.bitset:
BitSet bs = BitSetUtil.copy(ScriptVariable.bsSelect(x1));
switch (x2.tok) {
case Token.bitset:
bs.or(ScriptVariable.bsSelect(x2));
return addX(bs);
case Token.integer:
int x = ScriptVariable.iValue(x2);
if (x < 0)
break;
bs.set(x);
return addX(bs);
case Token.varray:
List sv = (ArrayList) x2.value;
for (int i = sv.size(); --i >= 0;)
bs.set(ScriptVariable.iValue((ScriptVariable) sv.get(i)));
return addX(bs);
}
break;
case Token.varray:
return addX(ScriptVariable.concatList(x1, x2, false));
}
return addX(ScriptVariable.bValue(x1) || ScriptVariable.bValue(x2));
case Token.opXor:
if (x1.tok == Token.bitset && x2.tok == Token.bitset) {
BitSet bs = BitSetUtil.copy(ScriptVariable.bsSelect(x1));
bs.xor(ScriptVariable.bsSelect(x2));
return addX(bs);
}
boolean a = ScriptVariable.bValue(x1);
boolean b = ScriptVariable.bValue(x2);
return addX(a && !b || b && !a);
case Token.opToggle:
if (x1.tok != Token.bitset || x2.tok != Token.bitset)
return false;
return addX(BitSetUtil.toggleInPlace(BitSetUtil.copy(ScriptVariable
.bsSelect(x1)), ScriptVariable.bsSelect(x2)));
case Token.opLE:
return addX(ScriptVariable.fValue(x1) <= ScriptVariable.fValue(x2));
case Token.opGE:
return addX(ScriptVariable.fValue(x1) >= ScriptVariable.fValue(x2));
case Token.opGT:
return addX(ScriptVariable.fValue(x1) > ScriptVariable.fValue(x2));
case Token.opLT:
return addX(ScriptVariable.fValue(x1) < ScriptVariable.fValue(x2));
case Token.opEQ:
return addX(ScriptVariable.areEqual(x1, x2));
case Token.opNE:
if (x1.tok == Token.string && x2.tok == Token.string)
return addX(!(ScriptVariable.sValue(x1).equalsIgnoreCase(ScriptVariable
.sValue(x2))));
if (x1.tok == Token.point3f && x2.tok == Token.point3f)
return addX(((Point3f) x1.value).distance((Point3f) x2.value) >= 0.000001);
if (x1.tok == Token.point4f && x2.tok == Token.point4f)
return addX(((Point4f) x1.value).distance((Point4f) x2.value) >= 0.000001);
{
float f1 = ScriptVariable.fValue(x1);
float f2 = ScriptVariable.fValue(x2);
return addX(Float.isNaN(f1) || Float.isNaN(f2)
|| Math.abs(f1 - f2) >= 0.000001);
}
case Token.plus:
switch (x1.tok) {
default:
return addX(ScriptVariable.fValue(x1) + ScriptVariable.fValue(x2));
case Token.varray:
return addX(ScriptVariable.concatList(x1, x2, true));
case Token.integer:
switch (x2.tok) {
case Token.string:
if ((s = (ScriptVariable.sValue(x2)).trim()).indexOf(".") < 0
&& s.indexOf("+") <= 0 && s.lastIndexOf("-") <= 0)
return addX(x1.intValue + ScriptVariable.iValue(x2));
break;
case Token.decimal:
return addX(x1.intValue + ScriptVariable.fValue(x2));
default:
return addX(x1.intValue + ScriptVariable.iValue(x2));
}
case Token.string:
return addX(new ScriptVariable(Token.string, ScriptVariable.sValue(x1)
+ ScriptVariable.sValue(x2)));
case Token.point4f:
Quaternion q1 = new Quaternion((Point4f) x1.value);
switch (x2.tok) {
default:
return addX(q1.add(ScriptVariable.fValue(x2)).toPoint4f());
case Token.point4f:
return addX(q1.mul(new Quaternion((Point4f) x2.value)).toPoint4f());
}
case Token.point3f:
pt = new Point3f((Point3f) x1.value);
switch (x2.tok) {
case Token.point3f:
pt.add((Point3f) x2.value);
return addX(pt);
case Token.point4f:
// extract {xyz}
pt4 = (Point4f) x2.value;
pt.add(new Point3f(pt4.x, pt4.y, pt4.z));
return addX(pt);
default:
f = ScriptVariable.fValue(x2);
return addX(new Point3f(pt.x + f, pt.y + f, pt.z + f));
}
case Token.matrix3f:
switch (x2.tok) {
default:
return addX(ScriptVariable.fValue(x1) + ScriptVariable.fValue(x2));
case Token.matrix3f:
m = new Matrix3f((Matrix3f) x1.value);
m.add((Matrix3f) x2.value);
return addX(m);
case Token.point3f:
return addX(getMatrix4f((Matrix3f) x1.value, (Point3f) x2.value));
}
}
case Token.minus:
if (x1.tok == Token.integer) {
if (x2.tok == Token.string) {
if ((s = (ScriptVariable.sValue(x2)).trim()).indexOf(".") < 0
&& s.indexOf("+") <= 0 && s.lastIndexOf("-") <= 0)
return addX(x1.intValue - ScriptVariable.iValue(x2));
} else if (x2.tok != Token.decimal)
return addX(x1.intValue - ScriptVariable.iValue(x2));
}
if (x1.tok == Token.string && x2.tok == Token.integer) {
if ((s = (ScriptVariable.sValue(x1)).trim()).indexOf(".") < 0
&& s.indexOf("+") <= 0 && s.lastIndexOf("-") <= 0)
return addX(ScriptVariable.iValue(x1) - x2.intValue);
}
switch (x1.tok) {
default:
return addX(ScriptVariable.fValue(x1) - ScriptVariable.fValue(x2));
case Token.hash:
Map ht = new Hashtable((Map) x1.value);
ht.remove(ScriptVariable.sValue(x2));
return addX(ScriptVariable.getVariable(ht));
case Token.matrix3f:
switch (x2.tok) {
default:
return addX(ScriptVariable.fValue(x1) - ScriptVariable.fValue(x2));
case Token.matrix3f:
m = new Matrix3f((Matrix3f) x1.value);
m.sub((Matrix3f) x2.value);
return addX(m);
}
case Token.matrix4f:
switch (x2.tok) {
default:
return addX(ScriptVariable.fValue(x1) - ScriptVariable.fValue(x2));
case Token.matrix4f:
Matrix4f m4 = new Matrix4f((Matrix4f) x1.value);
m4.sub((Matrix4f) x2.value);
return addX(m4);
}
case Token.point3f:
pt = new Point3f((Point3f) x1.value);
switch (x2.tok) {
default:
f = ScriptVariable.fValue(x2);
return addX(new Point3f(pt.x - f, pt.y - f, pt.z - f));
case Token.point3f:
pt.sub((Point3f) x2.value);
return addX(pt);
case Token.point4f:
// extract {xyz}
pt4 = (Point4f) x2.value;
pt.sub(new Point3f(pt4.x, pt4.y, pt4.z));
return addX(pt);
}
case Token.point4f:
Quaternion q1 = new Quaternion((Point4f) x1.value);
switch (x2.tok) {
default:
return addX(q1.add(-ScriptVariable.fValue(x2)).toPoint4f());
case Token.point4f:
Quaternion q2 = new Quaternion((Point4f) x2.value);
return addX(q2.mul(q1.inv()).toPoint4f());
}
}
case Token.unaryMinus:
switch (x2.tok) {
default:
return addX(-ScriptVariable.fValue(x2));
case Token.integer:
return addX(-ScriptVariable.iValue(x2));
case Token.point3f:
pt = new Point3f((Point3f) x2.value);
pt.scale(-1f);
return addX(pt);
case Token.point4f:
pt4 = new Point4f((Point4f) x2.value);
pt4.scale(-1f);
return addX(pt4);
case Token.matrix3f:
m = new Matrix3f((Matrix3f) x2.value);
m.transpose();
return addX(m);
case Token.matrix4f:
Matrix4f m4 = new Matrix4f((Matrix4f) x2.value);
m4.transpose();
return addX(m4);
case Token.bitset:
return addX(BitSetUtil.copyInvert(ScriptVariable.bsSelect(x2),
(x2.value instanceof BondSet ? viewer.getBondCount() : viewer
.getAtomCount())));
}
case Token.times:
if (x1.tok == Token.integer && x2.tok != Token.decimal)
return addX(x1.intValue * ScriptVariable.iValue(x2));
pt = (x1.tok == Token.matrix3f ? ptValue(x2, false)
: x2.tok == Token.matrix3f ? ptValue(x1, false) : null);
pt4 = (x1.tok == Token.matrix4f ? planeValue(x2)
: x2.tok == Token.matrix4f ? planeValue(x1) : null);
// checking here to make sure arrays remain arrays and
// points remain points with matrix operations.
// we check x2, because x1 could be many things.
switch (x2.tok) {
case Token.matrix3f:
if (pt != null) {
// pt * m
Matrix3f m3b = new Matrix3f((Matrix3f) x2.value);
m3b.transpose();
m3b.transform(pt);
if (x1.tok == Token.varray)
return addX(ScriptVariable.getVariable(new float[] { pt.x, pt.y,
pt.z }));
return addX(pt);
}
if (pt4 != null) {
// q * m --> q
return addX(((new Quaternion(pt4)).mul(new Quaternion(
(Matrix3f) x2.value))));
}
break;
case Token.matrix4f:
// pt4 * m4
// [a b c d] * m4
if (pt4 != null) {
Matrix4f m4b = new Matrix4f((Matrix4f) x2.value);
m4b.transpose();
m4b.transform(pt4);
if (x1.tok == Token.varray)
return addX(ScriptVariable.getVariable(new float[] { pt4.x, pt4.y,
pt4.z, pt4.w }));
return addX(pt4);
}
break;
}
switch (x1.tok) {
default:
return addX(ScriptVariable.fValue(x1) * ScriptVariable.fValue(x2));
case Token.matrix3f:
Matrix3f m3 = (Matrix3f) x1.value;
if (pt != null) {
m3.transform(pt);
if (x2.tok == Token.varray)
return addX(ScriptVariable.getVariable(new float[] { pt.x, pt.y,
pt.z }));
return addX(pt);
}
switch (x2.tok) {
case Token.matrix3f:
m = new Matrix3f((Matrix3f) x2.value);
m.mul(m3, m);
return addX(m);
case Token.point4f:
// m * q
return addX((new Quaternion(m3)).mul(
new Quaternion((Point4f) x2.value)).getMatrix());
default:
f = ScriptVariable.fValue(x2);
AxisAngle4f aa = new AxisAngle4f();
aa.set(m3);
aa.angle *= f;
Matrix3f m2 = new Matrix3f();
m2.set(aa);
return addX(m2);
}
case Token.matrix4f:
Matrix4f m4 = (Matrix4f) x1.value;
if (pt != null) {
m4.transform(pt);
if (x2.tok == Token.varray)
return addX(ScriptVariable.getVariable(new float[] { pt.x, pt.y,
pt.z }));
return addX(pt);
}
if (pt4 != null) {
m4.transform(pt4);
if (x2.tok == Token.varray)
return addX(ScriptVariable.getVariable(new float[] { pt4.x, pt4.y,
pt4.z, pt4.w }));
return addX(pt4);
}
switch (x2.tok) {
case Token.matrix4f:
Matrix4f m4b = new Matrix4f((Matrix4f) x2.value);
m4b.mul(m4, m4b);
return addX(m4b);
default:
return addX("NaN");
}
case Token.point3f:
pt = new Point3f((Point3f) x1.value);
switch (x2.tok) {
case Token.point3f:
Point3f pt2 = ((Point3f) x2.value);
return addX(pt.x * pt2.x + pt.y * pt2.y + pt.z * pt2.z);
default:
f = ScriptVariable.fValue(x2);
return addX(new Point3f(pt.x * f, pt.y * f, pt.z * f));
}
case Token.point4f:
switch (x2.tok) {
case Token.point4f:
// quaternion multiplication
// note that Point4f is {x,y,z,w} so we use that for
// quaternion notation as well here.
return addX((new Quaternion((Point4f) x1.value)).mul(new Quaternion(
(Point4f) x2.value)));
}
return addX(new Quaternion((Point4f) x1.value).mul(
ScriptVariable.fValue(x2)).toPoint4f());
}
case Token.percent:
// more than just modulus
// float % n round to n digits; n = 0 does "nice" rounding
// String % -n trim to width n; left justify
// String % n trim to width n; right justify
// Point3f % n ah... sets to multiple of unit cell!
// bitset % n
// Point3f * Point3f does dot product
// Point3f / Point3f divides by magnitude
// float * Point3f gets m agnitude
// Point4f % n returns q0, q1, q2, q3, or theta
// Point4f % Point4f
s = null;
int n = ScriptVariable.iValue(x2);
switch (x1.tok) {
case Token.on:
case Token.off:
case Token.integer:
default:
if (n == 0)
return addX(0);
return addX(ScriptVariable.iValue(x1) % n);
case Token.decimal:
f = ScriptVariable.fValue(x1);
// neg is scientific notation
if (n == 0)
return addX((int) (f + 0.5f * (f < 0 ? -1 : 1)));
s = TextFormat.formatDecimal(f, n);
return addX(s);
case Token.string:
s = (String) x1.value;
if (n == 0)
return addX(TextFormat.trim(s, "\n\t "));
if (n == 9999)
return addX(s.toUpperCase());
if (n == -9999)
return addX(s.toLowerCase());
if (n > 0)
return addX(TextFormat.format(s, n, n, false, false));
return addX(TextFormat.format(s, -n, n - 1, true, false));
case Token.varray:
String[] list = ScriptVariable.listValue(x1);
for (int i = 0; i < list.length; i++) {
if (n == 0)
list[i] = list[i].trim();
else if (n > 0)
list[i] = TextFormat.format(list[i], n, n, true, false);
else
list[i] = TextFormat.format(s, -n, n, false, false);
}
return addX(list);
case Token.point3f:
pt = new Point3f((Point3f) x1.value);
viewer.toUnitCell(pt, new Point3f(n, n, n));
return addX(pt);
case Token.point4f:
pt4 = (Point4f) x1.value;
if (x2.tok == Token.point3f)
return addX((new Quaternion(pt4)).transform((Point3f) x2.value));
if (x2.tok == Token.point4f) {
Point4f v4 = new Point4f((Point4f) x2.value);
(new Quaternion(pt4)).getThetaDirected(v4);
return addX(v4);
}
switch (n) {
// q%0 w
// q%1 x
// q%2 y
// q%3 z
// q%4 normal
// q%-1 vector(1)
// q%-2 theta
// q%-3 Matrix column 0
// q%-4 Matrix column 1
// q%-5 Matrix column 2
// q%-6 AxisAngle format
// q%-9 Matrix format
case 0:
return addX(pt4.w);
case 1:
return addX(pt4.x);
case 2:
return addX(pt4.y);
case 3:
return addX(pt4.z);
case 4:
return addX((new Quaternion(pt4)).getNormal());
case -1:
return addX(new Quaternion(pt4).getVector(-1));
case -2:
return addX((new Quaternion(pt4)).getTheta());
case -3:
return addX((new Quaternion(pt4)).getVector(0));
case -4:
return addX((new Quaternion(pt4)).getVector(1));
case -5:
return addX((new Quaternion(pt4)).getVector(2));
case -6:
AxisAngle4f ax = (new Quaternion(pt4)).toAxisAngle4f();
return addX(new Point4f(ax.x, ax.y, ax.z,
(float) (ax.angle * 180 / Math.PI)));
case -9:
return addX((new Quaternion(pt4)).getMatrix());
default:
return addX(pt4);
}
case Token.matrix4f:
Matrix4f m4 = (Matrix4f) x1.value;
switch (n) {
case 1:
Matrix3f m3 = new Matrix3f();
m4.get(m3);
return addX(m3);
case 2:
Vector3f v3 = new Vector3f();
m4.get(v3);
return addX(v3);
default:
return false;
}
case Token.bitset:
return addX(ScriptVariable.bsSelectRange(x1, n));
}
case Token.divide:
if (x1.tok == Token.integer && x2.tok == Token.integer
&& x2.intValue != 0)
return addX(x1.intValue / x2.intValue);
float f2 = ScriptVariable.fValue(x2);
switch (x1.tok) {
default:
float f1 = ScriptVariable.fValue(x1);
if (f2 == 0)
return addX(f1 == 0 ? 0f : f1 < 0 ? Float.POSITIVE_INFINITY
: Float.POSITIVE_INFINITY);
return addX(f1 / f2);
case Token.point3f:
pt = new Point3f((Point3f) x1.value);
if (f2 == 0)
return addX(new Point3f(Float.NaN, Float.NaN, Float.NaN));
return addX(new Point3f(pt.x / f2, pt.y / f2, pt.z / f2));
case Token.point4f:
if (x2.tok == Token.point4f)
return addX(new Quaternion((Point4f) x1.value).div(
new Quaternion((Point4f) x2.value)).toPoint4f());
if (f2 == 0)
return addX(new Point4f(Float.NaN, Float.NaN, Float.NaN, Float.NaN));
return addX(new Quaternion((Point4f) x1.value).mul(1 / f2).toPoint4f());
}
case Token.leftdivide:
f = ScriptVariable.fValue(x2);
switch (x1.tok) {
default:
return addX(f == 0 ? 0
: (int) (ScriptVariable.fValue(x1) / ScriptVariable.fValue(x2)));
case Token.point4f:
if (f == 0)
return addX(new Point4f(Float.NaN, Float.NaN, Float.NaN, Float.NaN));
if (x2.tok == Token.point4f)
return addX(new Quaternion((Point4f) x1.value).divLeft(
new Quaternion((Point4f) x2.value)).toPoint4f());
return addX(new Quaternion((Point4f) x1.value).mul(1 / f).toPoint4f());
}