/*******************************************************************************
* Breakout Cave Survey Visualizer
*
* Copyright (C) 2014 James Edwards
*
* jedwards8 at fastmail dot fm
*
* This program is 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; either version 2 of the License, or (at your option) any later
* version.
*
* This program 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, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*******************************************************************************/
package org.breakout.model;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import org.andork.collect.CollectionUtils;
import org.andork.math.misc.AngleUtils;
import org.andork.math3d.LineLineIntersection2d;
import org.andork.math3d.Vecmath;
public class Station {
public static boolean isVertical(Shot o1, Station station) {
boolean o1vertical = Math.abs(o1.inc) == Math.toRadians(90.0) ||
o1.crossSectionAt(station).type == CrossSectionType.NSEW;
return o1vertical;
}
public Station calcedFrom;
public String name;
public final List<Shot> shots = new ArrayList<>();
public final double[] position = { Double.NaN, Double.NaN, Double.NaN };
public void calcSplayPoints(LineLineIntersection2d llx) {
shots.sort(new Comparator<Shot>() {
@Override
public int compare(Shot o1, Shot o2) {
boolean o1vertical = isVertical(o1, Station.this);
boolean o2vertical = isVertical(o2, Station.this);
if (o1vertical && o2vertical) {
return 0;
}
if (o1vertical != o2vertical) {
return o1vertical ? 1 : -1;
}
return Double.compare(o1.azimuthAt(Station.this), o2.azimuthAt(Station.this));
}
});
int verticalIndex = CollectionUtils.indexOf(shots, shot -> Station.isVertical(shot, this));
if (verticalIndex < 0) {
verticalIndex = shots.size();
}
double maxUp = 0.0;
double maxDown = 0.0;
double maxNorth = 0.0;
double maxSouth = 0.0;
double maxEast = 0.0;
double maxWest = 0.0;
boolean anyLrud = false;
boolean anyNsew = false;
for (Shot shot : shots) {
shot.setSplayPointsAt(this, new float[shot.crossSectionAt(this).dist.length][]);
shot.setSplayNormalsAt(this, new float[shot.crossSectionAt(this).dist.length][]);
CrossSection sect = shot.crossSectionAt(this);
if (sect.type == CrossSectionType.LRUD) {
anyLrud = true;
maxUp = Math.max(maxUp, sect.dist[2]);
maxDown = Math.max(maxDown, sect.dist[3]);
} else if (sect.type == CrossSectionType.NSEW) {
anyNsew = true;
maxNorth = Math.max(maxNorth, sect.dist[0]);
maxSouth = Math.max(maxSouth, sect.dist[1]);
maxEast = Math.max(maxEast, sect.dist[2]);
maxWest = Math.max(maxWest, sect.dist[3]);
}
}
float[] up = null;
float[] down = null;
float[] north = null;
float[] south = null;
float[] east = null;
float[] west = null;
float[] upNorm = null;
float[] downNorm = null;
float[] northNorm = null;
float[] southNorm = null;
float[] eastNorm = null;
float[] westNorm = null;
if (anyLrud) {
up = new float[] {
(float) position[0],
(float) (position[1] + maxUp),
(float) position[2]
};
upNorm = new float[] { 0, 1, 0 };
down = new float[] {
(float) position[0],
(float) (position[1] - maxDown),
(float) position[2]
};
downNorm = new float[] { 0, -1, 0 };
}
if (anyNsew) {
north = new float[] {
(float) position[0],
(float) position[1],
(float) (position[2] - maxNorth)
};
northNorm = new float[] { 0, 0, -1 };
south = new float[] {
(float) position[0],
(float) position[1],
(float) (position[2] + maxSouth)
};
southNorm = new float[] { 0, 0, 1 };
east = new float[] {
(float) (position[0] + maxEast),
(float) position[1],
(float) position[2]
};
eastNorm = new float[] { 1, 0, 0 };
west = new float[] {
(float) (position[0] - maxWest),
(float) position[1],
(float) position[2]
};
westNorm = new float[] { -1, 0, 0 };
}
for (int i1 = 0; i1 < verticalIndex; i1++) {
int i2 = (i1 + 1) % verticalIndex;
Shot shot1 = shots.get(i1);
Shot shot2 = shots.get(i2);
CrossSection sect1 = shot1.crossSectionAt(this);
maxUp = Math.max(maxUp, sect1.dist[2]);
maxDown = Math.max(maxDown, sect1.dist[3]);
double left1 = shot1.leftAt(this);
double right2 = shot2.rightAt(this);
float[][] splayPoints1 = shot1.splayPointsAt(this);
splayPoints1[2] = up;
splayPoints1[3] = down;
float[][] splayNorms1 = shot1.splayNormalsAt(this);
splayNorms1[2] = upNorm;
splayNorms1[3] = downNorm;
float[] leftSplayPoint1 = new float[3];
float[] rightSplayPoint2 = leftSplayPoint1;
float[] leftSplayNorm1 = new float[3];
float[] rightSplayNorm2 = leftSplayNorm1;
double shot1azm = shot1.azimuthAt(this);
double shot2azm = shot2.azimuthAt(this);
double angle = AngleUtils.clockwiseRotation(shot1azm, shot2azm);
if (verticalIndex == 1) {
double xv1 = Math.sin(shot1azm);
double zv1 = -Math.cos(shot1azm);
leftSplayPoint1[0] = (float) (position[0] - zv1 * left1);
leftSplayPoint1[1] = (float) position[1];
leftSplayPoint1[2] = (float) (position[2] + xv1 * left1);
leftSplayNorm1[0] = (float) -zv1;
leftSplayNorm1[1] = 0;
leftSplayNorm1[2] = (float) xv1;
rightSplayPoint2 = new float[3];
rightSplayNorm2 = new float[3];
rightSplayPoint2[0] = (float) (position[0] + zv1 * right2);
rightSplayPoint2[1] = (float) position[1];
rightSplayPoint2[2] = (float) (position[2] - xv1 * right2);
rightSplayNorm2[0] = (float) zv1;
rightSplayNorm2[1] = 0;
rightSplayNorm2[2] = (float) -xv1;
} else if (verticalIndex == 2) {
double bisectorAzm = shot1azm + angle * 0.5;
double xv = Math.sin(bisectorAzm);
double zv = -Math.cos(bisectorAzm);
leftSplayPoint1[0] = (float) (position[0] + xv * left1);
leftSplayPoint1[1] = (float) position[1];
leftSplayPoint1[2] = (float) (position[2] + zv * left1);
leftSplayNorm1[0] = (float) xv;
leftSplayNorm1[1] = 0;
leftSplayNorm1[2] = (float) zv;
} else {
double xv1 = Math.sin(shot1azm);
double zv1 = -Math.cos(shot1azm);
double xv2 = Math.sin(shot2azm);
double zv2 = -Math.cos(shot2azm);
double offset1;
double offset2;
if (angle < Math.PI * 0.5) {
offset2 = (left1 + right2 * Math.cos(angle)) / Math.sin(angle);
offset1 = right2 * Math.sin(angle) + offset2 * Math.cos(angle);
} else if (angle < Math.PI) {
offset2 = (left1 - right2 * Math.cos(Math.PI - angle)) / Math.sin(Math.PI - angle);
offset1 = right2 * Math.sin(Math.PI - angle) + offset2 * Math.cos(Math.PI - angle);
} else {
offset2 = -(left1 + right2 * Math.cos(2 * Math.PI - angle)) / Math.sin(2 * Math.PI - angle);
offset1 = -right2 * Math.sin(2 * Math.PI - angle) - offset2 * Math.cos(2 * Math.PI - angle);
}
if (Math.abs(offset2) > shot2.dist || Math.abs(offset1) > shot1.dist) {
leftSplayPoint1[0] = (float) (position[0] - zv1 * left1);
leftSplayPoint1[1] = (float) position[1];
leftSplayPoint1[2] = (float) (position[2] + xv1 * left1);
leftSplayNorm1[0] = (float) -zv1;
leftSplayNorm1[1] = 0;
leftSplayNorm1[2] = (float) xv1;
rightSplayPoint2 = new float[3];
rightSplayNorm2 = new float[3];
rightSplayPoint2[0] = (float) (position[0] + zv2 * right2);
rightSplayPoint2[1] = (float) position[1];
rightSplayPoint2[2] = (float) (position[2] - xv2 * right2);
rightSplayNorm2[0] = (float) zv2;
rightSplayNorm2[1] = 0;
rightSplayNorm2[2] = (float) -xv2;
} else {
leftSplayPoint1[0] = (float) (position[0] + offset2 * xv2 + right2 * zv2);
leftSplayPoint1[1] = (float) position[1];
leftSplayPoint1[2] = (float) (position[2] + offset2 * zv2 - right2 * xv2);
leftSplayNorm1[0] = (float) (leftSplayPoint1[0] - position[0]);
leftSplayNorm1[1] = 0;
leftSplayNorm1[2] = (float) (leftSplayPoint1[2] - position[2]);
Vecmath.normalize3(leftSplayNorm1);
}
}
shot1.setLeftSplayPointAt(this, leftSplayPoint1);
shot1.setLeftSplayNormalAt(this, leftSplayNorm1);
shot2.setRightSplayPointAt(this, rightSplayPoint2);
shot2.setRightSplayNormalAt(this, rightSplayNorm2);
}
for (int i = verticalIndex; i < shots.size(); i++) {
Shot shot = shots.get(i);
CrossSection sect = shot.crossSectionAt(this);
float[][] splays = shot.splayPointsAt(this);
float[][] splayNorms = shot.splayNormalsAt(this);
if (sect.type == CrossSectionType.NSEW) {
splays[0] = north;
splays[1] = south;
splays[2] = east;
splays[3] = west;
splayNorms[0] = northNorm;
splayNorms[1] = southNorm;
splayNorms[2] = eastNorm;
splayNorms[3] = westNorm;
} else {
double xv = Math.sin(shot.azm);
double zv = -Math.cos(shot.azm);
splays[0] = new float[] {
(float) (position[0] + sect.dist[0] * zv),
(float) position[1],
(float) (position[2] - sect.dist[0] * xv)
};
splayNorms[0] = new float[] { (float) zv, 0, (float) -xv };
splays[1] = new float[] {
(float) (position[0] - sect.dist[1] * zv),
(float) position[1],
(float) (position[2] + sect.dist[1] * xv)
};
splayNorms[1] = new float[] { (float) -zv, 0, (float) xv };
splays[2] = up;
splayNorms[2] = upNorm;
splays[3] = down;
splayNorms[3] = downNorm;
}
}
}
@Override
public String toString() {
return name;
}
}