/*
Copyright 2006 Rene Grothmann, modified by Eric Hakenholz
This file is part of C.a.R. software.
C.a.R. is a 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, version 3 of the License.
C.a.R. 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, see .
*/
package rene.zirkel.tools;
// file: ObjectTracker.java
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.util.Enumeration;
import java.util.Vector;
import rene.util.xml.XmlTag;
import rene.util.xml.XmlTree;
import rene.util.xml.XmlWriter;
import rene.gui.Global;
import rene.zirkel.ZirkelCanvas;
import rene.zirkel.construction.Construction;
import rene.zirkel.construction.ConstructionException;
import rene.zirkel.construction.Selector;
import rene.zirkel.constructors.ObjectConstructor;
import rene.zirkel.graphics.MyGraphics;
import rene.zirkel.graphics.PolygonDrawer;
import rene.zirkel.graphics.TrackPainter;
import rene.zirkel.objects.AreaObject;
import rene.zirkel.objects.CircleObject;
import rene.zirkel.objects.ConstructionObject;
import rene.zirkel.objects.ExpressionObject;
import rene.zirkel.objects.FunctionObject;
import rene.zirkel.objects.MoveableObject;
import rene.zirkel.objects.PointObject;
import rene.zirkel.objects.PrimitiveCircleObject;
import rene.zirkel.objects.PrimitiveLineObject;
import rene.zirkel.objects.QuadricObject;
import rene.zirkel.objects.RayObject;
import rene.zirkel.objects.SegmentObject;
import rene.zirkel.objects.TrackObject;
import rene.zirkel.structures.Coordinates;
import rene.zirkel.objects.JLocusTrackObject;
public class ObjectTracker extends ObjectConstructor implements TrackPainter,
Runnable, Selector {
public PointObject PM;
public ConstructionObject O, P;
public int PMax=16, PN;
public ConstructionObject PO[]=new ConstructionObject[PMax];
Vector V=new Vector(), VPM=new Vector();
Vector VO[]=new Vector[PMax];
boolean Animate, Paint;
public boolean Interactive=true;
boolean Other=false;
public ZirkelCanvas ZC;
public ObjectTracker() {
}
public ObjectTracker(final ConstructionObject p, final PointObject pm,
final ConstructionObject o, final ZirkelCanvas zc,
final boolean animate, final boolean paint,
final ConstructionObject po[]) {
P=p;
PM=pm;
O=o;
if (P!=null&&O!=null&&(PM!=null||(O instanceof ExpressionObject))) {
Animate=animate;
Paint=paint;
if (PM!=null) {
PM.project(O);
}
zc.validate();
zc.repaint();
showStatus(zc);
PN=0;
for (int i=0; i=PMax||po[i]==null) {
break;
}
PO[PN]=po[i];
VO[i]=new Vector();
PN++;
}
}
}
public boolean isAdmissible(final ZirkelCanvas zc,
final ConstructionObject o) {
if (O==null) {
if (o instanceof ExpressionObject&&((ExpressionObject) o).isSlider()) {
return true;
}
if (o instanceof PrimitiveLineObject) {
return true;
}
if (o instanceof PrimitiveCircleObject) {
return true;
}
if (o instanceof PointObject&&((PointObject) o).isPointOn()) {
return true;
}
return false;
} else {
if (!(o instanceof PointObject)) {
return false;
}
if (!((MoveableObject) o).moveable()) {
return false;
}
if ((O instanceof CircleObject)&&((CircleObject) O).getP2()==o) {
return true;
}
if (zc.depends(O, o)) {
return false;
}
final ConstructionObject bound=((PointObject) o).getBound();
if (bound!=null&&bound!=O) {
return false;
}
return true;
}
}
@Override
public void mousePressed(final MouseEvent e, final ZirkelCanvas zc) {
zc.y(e.getY());
if (P==null) // no point selected yet
{
P=zc.selectPoint(e.getX(), e.getY());
if (P==null) {
P=zc.selectLine(e.getX(), e.getY());
}
if (P!=null) {
P.setSelected(true);
zc.repaint();
}
if (e.isShiftDown()) {
Other=true;
PN=0;
} else {
Other=false;
}
showStatus(zc);
} else if (Other) // want more points to track
{
ConstructionObject P=zc.selectPoint(e.getX(), e.getY());
if (P==null) {
P=zc.selectLine(e.getX(), e.getY());
}
if (P!=null) {
P.setSelected(true);
zc.repaint();
PO[PN]=P;
VO[PN]=new Vector();
PN++;
}
if (!e.isShiftDown()||PN>=PMax) {
Other=false;
}
showStatus(zc);
} else if (O==null) // no object to track on yet
{
O=zc.selectWithSelector(e.getX(), e.getY(), this);
if (O instanceof ExpressionObject) {
zc.clearSelected();
zc.clearIndicated();
Animate=!e.isShiftDown();
Paint=true;
compute(zc);
ZC=zc;
if (Animate) {
zc.validate();
}
zc.repaint();
} else if (O instanceof PointObject&&((PointObject) O).isPointOn()) {
PM=(PointObject) O;
O=PM.getBound();
zc.clearSelected();
zc.clearIndicated();
Animate=!e.isShiftDown();
Paint=true;
compute(zc);
ZC=zc;
if (Animate) {
zc.validate();
}
zc.repaint();
} else {
if (O!=null) {
O.setSelected(true);
zc.repaint();
}
showStatus(zc);
}
} else if (PM==null&&!(O instanceof ExpressionObject)) {
final ConstructionObject pm=zc.selectWithSelector(e.getX(), e.getY(), this);
if (pm==null) {
return;
}
PM=(PointObject) pm;
zc.clearSelected();
zc.clearIndicated();
Animate=!e.isShiftDown();
Paint=true;
compute(zc);
ZC=zc;
if (Animate) {
zc.validate();
}
zc.repaint();
} else if (!e.isControlDown()&&!e.isShiftDown()&&!e.isAltDown()) {
if (!Running&&Interactive&&PM!=null&&PM.nearto(e.getX(), e.getY(), zc)) {
Dragging=true;
zc.getConstruction().shouldSwitch(true);
} else if (Animate) {
if (Paint) {
Paint=false;
} else {
Animate=false;
Paint=true;
if (Running) {
stop();
}
showStatus(zc);
}
} else {
if (Running) {
return;
}
Paint=true;
Animate=true;
compute(zc);
zc.validate();
zc.repaint();
showStatus(zc);
}
}
}
@Override
public void mouseMoved(final MouseEvent e, final ZirkelCanvas zc,
final boolean simple) {
if (PM!=null) {
return;
}
if (P==null||Other) {
zc.indicatePointsOrLines(e.getX(), e.getY());
} else if (O==null) {
zc.indicateWithSelector(e.getX(), e.getY(), this);
} else {
zc.indicateWithSelector(e.getX(), e.getY(), this);
}
}
@Override
public void mouseDragged(final MouseEvent e, final ZirkelCanvas zc) {
if (!Dragging||PM==null) {
return;
}
PM.getY();
PM.move(zc.x(e.getX()), zc.y(e.getY()));
zc.dovalidate();
if (P.valid()) {
zc.repaint();
}
}
@Override
public void mouseReleased(final MouseEvent e, final ZirkelCanvas zc) {
if (!Dragging||PM==null) {
return;
}
zc.getConstruction().shouldSwitch(false);
Dragging=false;
zc.validate();
}
@Override
public void reset(final ZirkelCanvas zc) {
zc.clearSelected();
if (Running) {
stop();
}
PM=null;
P=O=null;
V=new Vector();
PN=0;
for (int i=0; i50) {
h=50;
}
Thread.sleep(50-h);
time=System.currentTimeMillis();
} catch (final Exception ex) {
}
}
if (Stopped) {
break;
}
}
ZCG.dispose();
zc.getConstruction().shouldSwitch(false);
zc.getConstruction().clearSwitches();
}
public ConstructionObject getObject() {
return O;
}
double mod(final double x) {
if (x>=Math.PI) {
return x-2*Math.PI;
}
if (x<-Math.PI) {
return x+2*Math.PI;
}
return x;
}
public void run() {
if (O instanceof ExpressionObject&&!((ExpressionObject) O).isSlider()) {
return;
}
if (V.size()==0) {
return;
}
if (!Animate) {
return;
}
Running=true;
Stopped=false;
showStatus(ZC);
while (true) {
try {
Thread.sleep(100);
} catch (final Exception ex) {
}
if (ZC.I!=null) {
break;
}
}
while (true) {
if (PM!=null) {
PM.move(oldx, oldy);
} else if (O instanceof ExpressionObject) {
((ExpressionObject) O).setSliderPosition(oldx);
}
animate(ZC);
if (Stopped) {
break;
}
}
if (PM!=null&&!(O instanceof ExpressionObject)) {
if (PM!=null) {
PM.move(oldx, oldy);
} else if (O instanceof ExpressionObject) {
((ExpressionObject) O).setSliderPosition(oldx);
}
ZC.getConstruction().switchBack();
ZC.dovalidate();
ZC.repaint();
}
synchronized (this) {
notify();
}
Running=false;
showStatus(ZC);
}
@Override
public void invalidate(final ZirkelCanvas zc) {
stop();
}
public void stop() {
if (!Running) {
return;
}
Stopped=true;
try {
wait();
} catch (final Exception e) {
}
ZC.repaint();
showStatus(ZC);
}
public void save(final XmlWriter xml) {
if (P==null||O==null||(PM==null&&!(O instanceof ExpressionObject))) {
return;
}
xml.startTagStart("Track");
xml.printArg("track", P.getName());
for (int i=0; i1) {
xml.printArg("omit", ""+(Omit-1));
}
xml.finishTagNewLine();
}
@Override
public void pause(final boolean flag) {
if (flag) {
if (Running) {
stop();
}
}
}
/**
* This is the update function, calling docompute.
*
* @param zc
*/
public synchronized void compute(final ZirkelCanvas zc) {
if (!isComplete()) {
return;
}
double x=0, y=0;
if (PM!=null) {
x=PM.getX();
y=PM.getY();
} else if (O instanceof ExpressionObject) {
x=((ExpressionObject) O).getSliderPosition();
}
zc.getConstruction().clearSwitches();
zc.getConstruction().shouldSwitch(true);
docompute(zc);
zc.getConstruction().shouldSwitch(false);
zc.getConstruction().clearSwitches();
if (PM!=null) {
PM.move(x, y);
} else if (O instanceof ExpressionObject) {
((ExpressionObject) O).setSliderPosition(x);
}
zc.dovalidate();
}
/**
* Complicated procedure to recompute the automatic track. In principle, a
* moving point moves on some object, the coordinates of the tracked points
* or the intersections of the tracked lines are stored, as well as the
* positions of the moved point. But if the tracked point gets invalid, the
* movement reverses and the interesections change. Moreover, there is a
* list of secondary tracked objects.
*
* @param zc
*/
public synchronized void docompute(final ZirkelCanvas zc) {
V=new Vector();
for (int i=0; i=2*Math.PI) {
d-=2*Math.PI;
}
amin=astart=-d/2;
amax=d/2;
anull=(a1+a2)/2;
} else {
amin=astart=-Math.PI*0.9999;
amax=Math.PI*0.9999;
}
double a=astart;
PM.move(x+r*Math.cos(anull+a), y+r*Math.sin(anull+a));
PM.project(c);
zc.getConstruction().validate(P, PM);
zc.resetSum();
double x1=0, y1=0;
boolean started=false;
if (P.valid()) {
zc.getConstruction().shouldSwitch(true);
if (P instanceof PointObject) {
final PointObject p=(PointObject) P;
x1=p.getX();
y1=p.getY();
V.addElement(new Coordinates(x1, y1));
VPM.addElement(new Coordinates(PM.getX(), PM.getY()));
started=true;
} else if (P instanceof PrimitiveLineObject) {
final PrimitiveLineObject L=(PrimitiveLineObject) P;
X=L.getX();
Y=L.getY();
DX=L.getDX();
DY=L.getDY();
started=true;
}
}
final boolean startedO[]=new boolean[PMax];
for (int i=0; izc.dx(1)) {
da=zc.dx(1)/10;
}
double aold=a;
double x2=0, y2=0;
while (true) {
a=a+da;
boolean Break=false;
if ((!started||range)&&a>=amax) {
a=amax;
Break=true;
} else if ((!started||range)&&a<=amin) {
a=amin;
Break=true;
} else if (started&&da>0) {
if ((mod(aold-astart)<0&&mod(a-astart)>=0)&&!zc.getConstruction().haveSwitched()) {
Break=true;
a=astart;
}
}
aold=a;
PM.move(x+r*Math.cos(anull+a), y+r*Math.sin(anull+a));
PM.project(c);
zc.getConstruction().validate(P, PM);
if (P.valid()) {
if (!started) {
zc.getConstruction().shouldSwitch(true);
astart=a;
}
boolean valid=false;
if (P instanceof PointObject) {
final PointObject p=(PointObject) P;
x2=p.getX();
y2=p.getY();
valid=true;
} else if (P instanceof PrimitiveLineObject) {
final PrimitiveLineObject L=(PrimitiveLineObject) P;
if (!started) {
X=L.getX();
Y=L.getY();
DX=L.getDX();
DY=L.getDY();
} else {
double xx, yy, dx, dy;
xx=L.getX();
yy=L.getY();
dx=L.getDX();
dy=L.getDY();
final double det=dx*DY-dy*DX;
if (Math.sqrt(Math.abs(det))>1e-9) {
final double h=(-(X-xx)*DY+DX*(Y-yy))/(-det);
x2=xx+h*dx;
y2=yy+h*dy;
valid=true;
}
X=xx;
Y=yy;
DX=dx;
DY=dy;
}
}
final double dist=zc.dCenter(x2, y2);
final boolean different=((int) zc.col(x1)!=(int) zc.col(x2)||(int) zc.row(y1)!=(int) zc.row(y2));
if (valid&&different) {
V.addElement(new Coordinates(x2, y2));
VPM.addElement(new Coordinates(PM.getX(), PM.getY()));
}
final double dp=Math.abs(x2-x1)+Math.abs(y2-y1);
da=updateDA(da, valid, dist, dp, dmin, dmax, zc);
x1=x2;
y1=y2;
started=true;
} else if (started) {
V.addElement(new Coordinates(x2, y2));
VPM.addElement(new Coordinates(PM.getX(), PM.getY()));
da=-da;
}
addSecondary(startedO, zc);
if (Break||System.currentTimeMillis()-time>5000) {
break;
}
}
} // Running on a line:
else if (O instanceof PrimitiveLineObject) {
zc.getConstruction().shouldSwitch(false);
final PrimitiveLineObject l=(PrimitiveLineObject) O;
PM.project(l);
final double lx=l.getX(), ly=l.getY(), ldx=l.getDX(), ldy=l.getDY();
double amin=0, amax=0, astart=0;
double dmax=0.5;
boolean range=false;
if (l instanceof RayObject) {
amin=astart=0;
amax=Math.PI*0.9999;
range=true;
} else if (l instanceof SegmentObject) {
amin=astart=0;
final double r=((SegmentObject) l).getLength();
System.out.println(r);
dmax=r/20;
amax=Math.atan(r)*2;
range=true;
} else {
amin=astart=-Math.PI*0.99999;
amax=Math.PI*0.9999;
}
double a=astart;
double hd=Math.tan(mod(a)/2);
PM.move(lx+hd*ldx, ly+hd*ldy);
PM.project(l);
zc.getConstruction().validate(P, PM);
zc.resetSum();
double x1=0, y1=0;
boolean started=false;
if (P.valid()) {
zc.getConstruction().shouldSwitch(true);
if (P instanceof PointObject) {
final PointObject p=(PointObject) P;
x1=p.getX();
y1=p.getY();
V.addElement(new Coordinates(x1, y1));
VPM.addElement(new Coordinates(PM.getX(), PM.getY()));
started=true;
} else if (P instanceof PrimitiveLineObject) {
final PrimitiveLineObject L=(PrimitiveLineObject) P;
X=L.getX();
Y=L.getY();
DX=L.getDX();
DY=L.getDY();
started=true;
}
}
final boolean startedO[]=new boolean[PMax];
for (int i=0; izc.dx(1)) {
da=zc.dx(1)/10;
}
double aold=a;
double x2=0, y2=0;
while (true) {
a=a+da;
boolean Break=false;
if ((!started||range)&&a>=amax) {
a=amax;
Break=true;
} else if ((!started||range)&&a<=amin) {
a=amin;
Break=true;
} else if (started&&da>0) {
if ((mod(aold-astart)<0&&mod(a-astart)>=0)&&!zc.getConstruction().haveSwitched()) {
Break=true;
a=astart;
}
}
aold=a;
hd=Math.tan(mod(a)/2);
PM.move(lx+hd*ldx, ly+hd*ldy);
PM.project(l);
zc.getConstruction().validate(P, PM);
if (P.valid()) {
if (!started) {
zc.getConstruction().shouldSwitch(true);
astart=a;
}
boolean valid=false;
if (P instanceof PointObject) {
final PointObject p=(PointObject) P;
x2=p.getX();
y2=p.getY();
valid=true;
} else if (P instanceof PrimitiveLineObject) {
final PrimitiveLineObject L=(PrimitiveLineObject) P;
if (!started) {
X=L.getX();
Y=L.getY();
DX=L.getDX();
DY=L.getDY();
} else {
double xx, yy, dx, dy;
xx=L.getX();
yy=L.getY();
dx=L.getDX();
dy=L.getDY();
final double det=dx*DY-dy*DX;
if (Math.sqrt(Math.abs(det))>1e-9) {
final double h=(-(X-xx)*DY+DX*(Y-yy))/(-det);
x2=xx+h*dx;
y2=yy+h*dy;
valid=true;
}
X=xx;
Y=yy;
DX=dx;
DY=dy;
}
}
final double dist=zc.dCenter(x2, y2);
final boolean different=((int) zc.col(x1)!=(int) zc.col(x2)||(int) zc.row(y1)!=(int) zc.row(y2));
if (valid&&different) {
V.addElement(new Coordinates(x2, y2));
VPM.addElement(new Coordinates(PM.getX(), PM.getY()));
}
final double dp=Math.abs(x2-x1)+Math.abs(y2-y1);
da=updateDA(da, valid, dist, dp, dmin, dmax, zc);
x1=x2;
y1=y2;
started=true;
} else if (started) {
V.addElement(new Coordinates(x2, y2));
VPM.addElement(new Coordinates(PM.getX(), PM.getY()));
da=-da;
}
addSecondary(startedO, zc);
if (Break||System.currentTimeMillis()-time>5000) {
break;
}
}
} // Running an expression slider:
else if (O instanceof ExpressionObject) {
zc.getConstruction().shouldSwitch(false);
final ExpressionObject eo=(ExpressionObject) O;
if (!eo.isSlider()) {
return;
}
double amin=0, amax=0, astart=0;
double dmax=0.5;
boolean range=false;
amin=astart=0;
final double r=1;
dmax=r/20;
amax=Math.atan(r)*2;
range=true;
double a=astart;
double hd=Math.tan(mod(a)/2);
eo.setSliderPosition(0);
zc.getConstruction().validate(P, null);
zc.resetSum();
double x1=0, y1=0;
boolean started=false;
if (P.valid()) {
zc.getConstruction().shouldSwitch(true);
if (P instanceof PointObject) {
final PointObject p=(PointObject) P;
x1=p.getX();
y1=p.getY();
V.addElement(new Coordinates(x1, y1));
VPM.addElement(new Coordinates(eo.getSliderPosition(), 0));
started=true;
} else if (P instanceof PrimitiveLineObject) {
final PrimitiveLineObject L=(PrimitiveLineObject) P;
X=L.getX();
Y=L.getY();
DX=L.getDX();
DY=L.getDY();
started=true;
}
}
final boolean startedO[]=new boolean[PMax];
for (int i=0; izc.dx(1)) {
da=zc.dx(1)/10;
}
double aold=a;
double x2=0, y2=0;
while (true) {
a=a+da;
boolean Break=false;
if ((!started||range)&&a>=amax) {
a=amax;
Break=true;
} else if ((!started||range)&&a<=amin) {
a=amin;
Break=true;
} else if (started&&da>0) {
if ((mod(aold-astart)<0&&mod(a-astart)>=0)&&!zc.getConstruction().haveSwitched()) {
Break=true;
a=astart;
}
}
aold=a;
hd=Math.tan(mod(a)/2);
eo.setSliderPosition(hd);
zc.getConstruction().validate(P, null);
if (P.valid()) {
if (!started) {
zc.getConstruction().shouldSwitch(true);
astart=a;
}
boolean valid=false;
if (P instanceof PointObject) {
final PointObject p=(PointObject) P;
x2=p.getX();
y2=p.getY();
valid=true;
} else if (P instanceof PrimitiveLineObject) {
final PrimitiveLineObject L=(PrimitiveLineObject) P;
if (!started) {
X=L.getX();
Y=L.getY();
DX=L.getDX();
DY=L.getDY();
} else {
double xx, yy, dx, dy;
xx=L.getX();
yy=L.getY();
dx=L.getDX();
dy=L.getDY();
final double det=dx*DY-dy*DX;
if (Math.sqrt(Math.abs(det))>1e-9) {
final double h=(-(X-xx)*DY+DX*(Y-yy))/(-det);
x2=xx+h*dx;
y2=yy+h*dy;
valid=true;
}
X=xx;
Y=yy;
DX=dx;
DY=dy;
}
}
final double dist=zc.dCenter(x2, y2);
final boolean different=((int) zc.col(x1)!=(int) zc.col(x2)||(int) zc.row(y1)!=(int) zc.row(y2));
if (valid&&different) {
V.addElement(new Coordinates(x2, y2));
VPM.addElement(new Coordinates(eo.getSliderPosition(),
0));
}
final double dp=Math.abs(x2-x1)+Math.abs(y2-y1);
da=updateDA(da, valid, dist, dp, dmin, dmax, zc);
x1=x2;
y1=y2;
started=true;
} else if (started) {
V.addElement(new Coordinates(x2, y2));
VPM.addElement(new Coordinates(eo.getSliderPosition(), 0));
da=-da;
}
addSecondary(startedO, zc);
if (Break||System.currentTimeMillis()-time>5000) {
break;
}
}
}
}
public void addSecondary(final boolean startedO[], final ZirkelCanvas zc) {
for (int i=0; i1e-9) {
final double h=(-(XO[i]-xx)*DYO[i]+DXO[i]*(YO[i]-yy))/(-det);
XO[i]=xx;
YO[i]=yy;
DXO[i]=dx;
DYO[i]=dy;
VO[i].addElement(new Coordinates(xx+h*dx, yy+h*dy));
}
}
}
}
}
public double StepSize=5;
public double updateDA(double da, final boolean valid, final double dist,
final double dp, final double dmin, final double dmax,
final ZirkelCanvas zc) {
if (V.size()>0&&valid) {
if (dist<1.2) {
if (dp>zc.dx(StepSize)) {
da/=2;
} else if (dp0&&da-dmin) {
da=-dmin;
}
if (da>dmax) {
da=dmax;
} else if (da<-dmax) {
da=-dmax;
}
} else {
if (dp>zc.dx(StepSize*10)) {
da/=2;
} else if (dp0&&da-dmin) {
da=-dmin;
}
if (da>dmax) {
da=dmax;
} else if (da<-dmax) {
da=-dmax;
}
}
}
return da;
}
public void increaseOmit() {
Omit++;
}
public void decreaseOmit() {
if (Omit>1) {
Omit--;
}
}
public void setOmit(final int f) {
Omit=f+1;
}
public boolean isComplete() {
return !(P==null||O==null||(PM==null&&!(O instanceof ExpressionObject&&((ExpressionObject) O).isSlider())));
}
public void keep(final ZirkelCanvas zc) {
if (!isComplete()) {
return;
}
final TrackObject t=new TrackObject(zc.getConstruction(), P, PO, PN,
O, PM);
zc.addObject(t);
t.setDefaults();
reset(zc);
t.compute(zc);
zc.validate();
}
@Override
public String getTag() {
return "Track";
}
@Override
public boolean construct(final XmlTree tree, final Construction c)
throws ConstructionException {
if (!testTree(tree, "Track")) {
return false;
}
final XmlTag tag=tree.getTag();
if (!tag.hasParam("on")||!tag.hasParam("track")) {
throw new ConstructionException("Track parameters missing!");
} else {
try {
PointObject pm=null;
try {
pm=(PointObject) c.find(tag.getValue("point"));
} catch (final Exception ex) {
}
final ConstructionObject o=c.find(tag.getValue("on"));
if (pm==null&&!(o instanceof ExpressionObject)) {
throw new ConstructionException("");
}
final ConstructionObject p=c.find(tag.getValue("track"));
final ConstructionObject po[]=new ConstructionObject[PMax];
int pn=0;
for (pn=0; pn