Files
secondo/Javagui/viewer/v3d/Face.java
2026-01-23 17:03:45 +08:00

566 lines
18 KiB
Java

package viewer.v3d;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import javax.vecmath.Point2d;
import javax.vecmath.Point3d;
import java.awt.Color;
import javax.vecmath.Color3b;
import sj.lang.ListExpr;
import gui.SecondoObject;
import javax.media.j3d.TriangleArray;
/**
* This class represents a secondo-object, which should be displayed in 3D.
*
* @author Florian Heinz <fh@sysv.de>
*/
class Face {
// Sizes of the bounding box the object is to be scaled in
static public final double RANGEX = 5,
RANGEY = 5,
RANGEZ = 5;
// The colors of the 3d-triangles, add more colors here if needed
private static final Color3b[] colors = {
new Color3b(Color.RED),
new Color3b(Color.BLUE),
new Color3b(Color.YELLOW),
new Color3b(Color.GREEN),
new Color3b(Color.MAGENTA),
new Color3b(Color.CYAN),
new Color3b(Color.ORANGE),
new Color3b(Color.PINK),
new Color3b(Color.GRAY)
};
private SecondoObject o; // The object to be displayed
private static boolean compensateTranslation = false;
private static boolean lightBackground = false;
/**
* Construct a new object to be displayed in 3d.
*
* @param o the object to be displayed
* @param compensateTranslation compensate translation of interval borders
* @param lightBackground displayed on light background
*/
Face(SecondoObject o, boolean compensateTranslation, boolean lightBackground) {
this.o = o;
this.compensateTranslation = compensateTranslation;
this.lightBackground = lightBackground;
}
/**
* Construct a new object to be displayed in 3d.
*
* @param o the object to be displayed
*/
Face(SecondoObject o) {
this.o = o;
}
private static double getPrecise (ListExpr le) {
String s[] = le.textValue().split("/");
if (s.length > 1) {
return new Double(s[0])/new Double(s[1]);
} else {
return new Double(s[0]);
}
}
/**
* Calculates and builds a Triangle-Array which represents a secondo-object
* to be displayed by the java3d-framework
*
* @return the array of triangles making up the secondo object
*/
public TriangleArray GetTriangleArray() {
List<Point3d> points = MRegionList2Triangles();
FixCoordinates(points);
points.addAll(arrow());
TriangleArray ret = new TriangleArray(points.size(),
TriangleArray.COORDINATES | TriangleArray.COLOR_3);
Color3b c = nextColor();
Color3b white = new Color3b(Color.WHITE);
Color3b black = new Color3b(Color.BLACK);
for (int i = 0; i < points.size(); i++) {
// Each triangle is uniformy colored, so change the color every
// third iteration only
if (i % 3 == 0) {
c = nextColor();
}
ret.setCoordinate(i, points.get(i));
if (i < points.size() - 6) {
ret.setColor(i, c);
} else {
// The last six values are the arrow, which we want to be black
if (lightBackground) {
ret.setColor(i, black);
} else { // or white in case the background is black
ret.setColor(i, white);
}
}
}
return ret;
}
public static TriangleArray GetTriangleArray(List<Face> fcs) {
List<Point3d> points = new LinkedList();
for (Face f : fcs) {
points.addAll(f.MRegionList2Triangles());
}
FixCoordinates(points);
points.addAll(arrow());
TriangleArray ret = new TriangleArray(points.size(),
TriangleArray.COORDINATES | TriangleArray.COLOR_3);
Color3b c = nextColor();
Color3b white = new Color3b(Color.WHITE);
Color3b black = new Color3b(Color.BLACK);
for (int i = 0; i < points.size(); i++) {
// Each triangle is uniformy colored, so change the color every
// third iteration only
if (i % 3 == 0) {
c = nextColor();
}
ret.setCoordinate(i, points.get(i));
if (i < points.size() - 6) {
ret.setColor(i, c);
} else {
// The last six values are the arrow, which we want to be black
if (lightBackground) {
ret.setColor(i, black);
} else { // or white in case the background is black
ret.setColor(i, white);
}
}
}
return ret;
}
// Get the color for the next triangle
private static int cnr = 0; // the index of the next color to be displayed
private static Color3b nextColor() {
return colors[cnr++ % colors.length];
}
/**
* Get a list of corner points for the triangles of the object to display.
* Three points in a row define one triangle.
*
* @return list of triangle corner points
*/
private List<Point3d> MRegionList2Triangles() {
List<Point3d> ret = new LinkedList();
ListExpr le = o.toListExpr();
String type = le.first().textValue();
le = le.second();
if (type.equals("mregion")) {
// We have an mregion-object which may consist of several intervals
ListExpr p = le.first();
while (p != null) {
le = le.rest();
// z1 and z2 receive the unix-timestamp of the intervals borders
ListExpr interval = p.first();
double z1 = parseInstant(interval.first().textValue());
double z2 = parseInstant(interval.second().textValue());
ListExpr uregion = p.second();
ret.addAll(FacesList2Triangles(uregion, z1, z2));
p = le.first();
}
} else if (type.equals("mregion2")) {
// We have an mregion2-object which may consist of several intervals
le = le.second();
ListExpr p = le.first();
while (p != null) {
le = le.rest();
// z1 and z2 receive the unix-timestamp of the intervals borders
ListExpr interval = p.first();
double z1 = parseInstant(interval.first().textValue());
double z2 = parseInstant(interval.second().textValue());
ListExpr preciseInterval = interval.fifth();
if (preciseInterval.listLength() == 2) {
double z1p = getPrecise(preciseInterval.first());
double z2p = getPrecise(preciseInterval.second());
z1 += (z1p*86400);
z2 += (z2p*86400);
}
ListExpr uregion = p.second();
ret.addAll(FacesList2Triangles(uregion, z1, z2));
p = le.first();
}
} else if (type.equals("uregion")) {
// a uregion consists of only one interval
ListExpr p = le;
// z1 and z2 receive the unix-timestamp of the intervals borders
ListExpr interval = p.first();
double z1 = parseInstant(interval.first().textValue());
double z2 = parseInstant(interval.second().textValue());
ListExpr uregion = p.second();
ret.addAll(FacesList2Triangles(uregion, z1, z2));
p = le.first();
} else if (type.equals("uregion2")) {
le = le.second();
// a uregion consists of only one interval
ListExpr p = le;
// z1 and z2 receive the unix-timestamp of the intervals borders
ListExpr interval = p.first();
double z1 = parseInstant(interval.first().textValue());
double z2 = parseInstant(interval.second().textValue());
ListExpr preciseInterval = interval.fifth();
if (preciseInterval.listLength() == 2) {
double z1p = getPrecise(preciseInterval.first());
double z2p = getPrecise(preciseInterval.second());
z1 += (z1p*86400);
z2 += (z2p*86400);
}
ListExpr uregion = p.second();
ret.addAll(FacesList2Triangles(uregion, z1, z2));
p = le.first();
} else if (type.equals("pmregion")) {
ListExpr points = le.first();
ListExpr triangles = le.second();
Point3d pl[] = new Point3d[points.listLength()];
int i = 0;
while (points.first() != null) {
pl[i] = new Point3d(
points.first().first().realValue(),
points.first().second().realValue(),
points.first().third().realValue()
);
i++;
points = points.rest();
}
while (triangles.first() != null) {
int idx1 = (int)triangles.first().first().realValue();
int idx3 = (int)triangles.first().second().realValue();
int idx2 = (int)triangles.first().third().realValue();
ret.add(new Point3d(pl[idx1]));
ret.add(new Point3d(pl[idx2]));
ret.add(new Point3d(pl[idx3]));
triangles = triangles.rest();
}
}
return ret;
}
public static List<Point3d> arrow() {
List<Point3d> ret = new LinkedList();
// Draw an arrow for orientation
ret.add(new Point3d(-RANGEX / 2 * 1.1, -RANGEY / 2 * 1.1, -RANGEZ / 2));
ret.add(new Point3d(-RANGEX / 2 * 1.1, -RANGEY / 2 * 1.1 + 0.1, -RANGEZ / 2));
ret.add(new Point3d(-RANGEX / 2 * 1.1, -RANGEY / 2 * 1.1 + 0.05, RANGEZ / 2));
ret.add(new Point3d(-RANGEX / 2 * 1.1, -RANGEY / 2 * 1.1, RANGEZ / 2 * 0.9));
ret.add(new Point3d(-RANGEX / 2 * 1.1, -RANGEY / 2 * 1.1+0.1, RANGEZ/2*0.95));
ret.add(new Point3d(-RANGEX / 2 * 1.1, -RANGEY / 2 * 1.1 + 0.05, RANGEZ / 2));
return ret;
}
/**
* Move and scale the points to fill exactly the bounding box RANGE(X/Y/Z)
*
* @param l A list of points
*/
private static void FixCoordinates(List<Point3d> l) {
if (l.isEmpty())
return;
double minx, maxx, miny, maxy, minz, maxz;
List<Point3d> ret = new LinkedList();
Point3d fp = l.get(0);
minx = maxx = fp.x;
miny = maxy = fp.y;
minz = maxz = fp.z;
// Determine the minimum and maximum values of the coordinates for
// each axis
for (int i = 1; i < l.size(); i++) {
Point3d p = l.get(i);
if (p.x < minx) {
minx = p.x;
}
if (p.x > maxx) {
maxx = p.x;
}
if (p.y < miny) {
miny = p.y;
}
if (p.y > maxy) {
maxy = p.y;
}
if (p.z < minz) {
minz = p.z;
}
if (p.z > maxz) {
maxz = p.z;
}
}
// Calculate offset and scale-factors from the result
double offx = -minx;
double offy = -miny;
double offz = -minz;
double scalex = RANGEX / (maxx - minx);
double scaley = RANGEY / (maxy - miny);
double scalez = RANGEZ / (maxz - minz);
// and transform all points with that parameters
for (int i = 0; i < l.size(); i++) {
Point3d p = l.get(i);
p.x = (p.x + offx) * scalex - RANGEX / 2;
p.y = (p.y + offy) * scaley - RANGEY / 2;
p.z = (p.z + offz) * scalez - RANGEZ / 2;
}
}
/**
* Transforms the points for each z-value to have the left corner of the
* bounding box on (0/0).
*
* @param l list of points to be transformed
*/
private static void CompensateTranslation(List<Point3d> l) {
Point2d[] off = new Point2d[2];
double z1 = l.size() > 0 ? l.get(0).z : 0; // Only two z-values are
// possible, z1 and the other
// Calculate the offset value for each z-value seperatly
for (Point3d p : l) {
int i = (p.z == z1) ? 0 : 1;
if (off[i] == null) {
off[i] = new Point2d(p.x, p.y);
} else {
if (p.x < off[i].x) {
off[i].x = p.x;
}
if (p.y < off[i].y) {
off[i].y = p.y;
}
}
}
// Transform the points accordingly
for (Point3d p : l) {
int i = (p.z == z1) ? 0 : 1;
p.x -= off[i].x;
p.y -= off[i].y;
}
}
/**
* Transforms several faces in a Nested List to triangles
*
* @param le the nested list to be transformed to triangles
* @param z1 the z-coordinate of the initial segments
* @param z2 the z-coordinate of the final segments
* @return List of triangle corner points
*/
private List<Point3d> FacesList2Triangles(ListExpr le,
double z1, double z2) {
List<Point3d> ret = new LinkedList();
ListExpr p = le.first();
while (p != null) {
le = le.rest();
ret.addAll(FaceList2Triangles(p, z1, z2));
p = le.first();
}
if (compensateTranslation) {
CompensateTranslation(ret);
}
return ret;
}
/**
* Transforms one face in a Nested List to triangles
*
* @param le the nested list to be transformed to triangles
* @param z1 the z-coordinate of the initial segments
* @param z2 the z-coordinate of the final segments
* @return List of triangle corner points
*/
private static List<Point3d> FaceList2Triangles(ListExpr le,
double z1, double z2) {
List<Point3d> ret = new LinkedList();
ListExpr p = le.first();
while (p != null) {
le = le.rest();
ret.addAll(List2Triangles(p, z1, z2));
p = le.first();
}
return ret;
}
/**
* Transform a list of Moving Segments to triangles
*
* @param le the nested list to be transformed to triangles
* @param z1 the z-coordinate of the initial segments
* @param z2 the z-coordinate of the final segments
* @return List of triangle corner points
*/
private static List<Point3d> List2Triangles(ListExpr le,
double z1, double z2) {
List<Point3d> ret = new LinkedList();
List<HalfSeg> lhs = List2HS(le);
for (int i = 0; i < lhs.size() - 1; i++) {
ret.addAll(HS2Triangles(lhs.get(i), lhs.get(i + 1), z1, z2));
}
return ret;
}
/**
* Transforms a Nested List of Half-Segments to triangles
*
* @param le the nested list to be transformed to triangles
* @return a list of Half-Segments
*/
private static List<HalfSeg> List2HS(ListExpr le) {
List<HalfSeg> ret = new LinkedList();
ListExpr first = le.first();
ListExpr p = le.first();
while (p != null) {
le = le.rest();
ret.add(new HalfSeg(p));
p = le.first();
}
ret.add(new HalfSeg(first));
return ret;
}
/**
*
* @param h1 first Half-Segment
* @param h2 second Half-Segment
* @param z1 z-coordinate of initial Segment
* @param z2 z-coordinate of final Segment
* @return List of points representing one or two triangles
*/
private static List<Point3d> HS2Triangles(HalfSeg h1, HalfSeg h2,
double z1, double z2) {
List<Point3d> ret = new LinkedList();
Point2d i1 = h1.Initial();
Point2d f1 = h1.Final();
Point2d i2 = h2.Initial();
Point2d f2 = h2.Final();
if (!i1.equals(i2)) { // Initial segment is not degenerated
ret.add(new Point3d(i1.x, i1.y, z1));
ret.add(new Point3d(i2.x, i2.y, z1));
ret.add(new Point3d(f2.x, f2.y, z2));
}
if (!f1.equals(f2)) { // Final segment is not degenerated
ret.add(new Point3d(i1.x, i1.y, z1));
ret.add(new Point3d(f1.x, f1.y, z2));
ret.add(new Point3d(f2.x, f2.y, z2));
}
return ret;
}
/**
* Helper-class representing one Half-Segment consisting of an initial and
* a final point.
*
*/
private static class HalfSeg {
double x1, y1, x2, y2;
private static double getValue(ListExpr le) {
if (le.atomType() == ListExpr.INT_ATOM) {
return (double) le.intValue();
}
return le.realValue();
}
HalfSeg(ListExpr le) {
x1 = getValue(le.first());
y1 = getValue(le.second());
x2 = getValue(le.third());
y2 = getValue(le.fourth());
if (le.listLength() > 4) {
ListExpr precise = le.fifth();
if (precise.listLength() == 4) {
x1 += getPrecise(precise.first());
y1 += getPrecise(precise.second());
x2 += getPrecise(precise.third());
y2 += getPrecise(precise.fourth());
}
}
}
Point2d Initial() {
return new Point2d(x1, y1);
}
Point2d Final() {
return new Point2d(x2, y2);
}
}
/**
* Converts an instant-string to a unix-timestamp
*
* @param instant a string representing an instant in time
* @return the corresponding unix-timestamp
*/
private static double parseInstant(String instant) {
String format;
int len = instant.length();
// Only these formats can occur
switch (len) {
case 10:
format = "yyyy-MM-dd";
break;
case 16:
format = "yyyy-MM-dd-HH:mm";
break;
case 19:
default:
format = "yyyy-MM-dd-HH:mm:ss";
break;
}
SimpleDateFormat sdf = new SimpleDateFormat(format);
try {
Date s = sdf.parse(instant);
return (s.getTime() / 1000);
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}