com.orsoncharts.graphics3d.Line3D Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of orsoncharts Show documentation
Show all versions of orsoncharts Show documentation
Orson Charts is a 3D chart library for the Java platform.
/* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2016, by Object Refinery Limited. All rights reserved.
*
* http://www.object-refinery.com/orsoncharts/index.html
*
* 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 3 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, see .
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package com.orsoncharts.graphics3d;
import com.orsoncharts.util.ArgChecks;
/**
* A line segment in 3D space.
*
* @since 1.5
*/
public class Line3D {
private Point3D start;
private Point3D end;
/**
* Creates a new line in 3D space.
*
* @param start the starting point ({@code null} not permitted).
* @param end the ending point ({@code null} not permitted).
*/
public Line3D(Point3D start, Point3D end) {
ArgChecks.nullNotPermitted(start, "start");
ArgChecks.nullNotPermitted(end, "end");
this.start = start;
this.end = end;
}
/**
* Creates a new line in 3D space between the points {@code (x0, y0, z0)}
* and {@code (x1, y1, z1)}.
*
* @param x0 the x-coordinate for the line's start point.
* @param y0 the y-coordinate for the line's start point.
* @param z0 the z-coordinate for the line's start point.
* @param x1 the x-coordinate for the line's end point.
* @param y1 the y-coordinate for the line's end point.
* @param z1 the z-coordinate for the line's end point.
*/
public Line3D(double x0, double y0, double z0, double x1, double y1,
double z1) {
this.start = new Point3D(x0, y0, z0);
this.end = new Point3D(x1, y1, z1);
}
/**
* Returns the starting point for the line.
*
* @return The starting point (never {@code null}).
*/
public Point3D getStart() {
return this.start;
}
/**
* Returns the ending point for the line.
*
* @return The ending point (never {@code null}).
*/
public Point3D getEnd() {
return this.end;
}
/**
* Calculates and returns the line segment that is the result of cropping
* the specified line segment to fit within an axis aligned bounding box.
*
* @param line the original line segment ({@code null} not permitted).
* @param x0 the lower x-bound.
* @param x1 the upper x-bound.
* @param y0 the lower y-bound.
* @param y1 the upper y-bound.
* @param z0 the lower z-bound.
* @param z1 the upper z-bound.
*
* @return The cropped line segment (or {@code null} if the original line
* segment falls outside the bounding box).
*/
public static Line3D cropLineToAxisAlignedBoundingBox(Line3D line,
double x0, double x1, double y0, double y1, double z0, double z1) {
// we need this method to be fast, since it will get called a lot of
// times during line chart rendering...there may be a faster method
// than what is implemented below, in which case we should use it
double xx0 = line.getStart().getX();
double xx1 = line.getEnd().getX();
int kx0 = 2;
if (xx0 < x0) {
kx0 = 1;
} else if (xx0 > x1) {
kx0 = 4;
}
int kx1 = 2;
if (xx1 < x0) {
kx1 = 1;
} else if (xx1 > x1) {
kx1 = 4;
}
if ((kx0 | kx1) == 1 || (kx0 | kx1) == 4) {
return null;
}
double yy0 = line.getStart().getY();
double yy1 = line.getEnd().getY();
int ky0 = 2;
if (yy0 < y0) {
ky0 = 1;
} else if (yy0 > y1) {
ky0 = 4;
}
int ky1 = 2;
if (yy1 < y0) {
ky1 = 1;
} else if (yy1 > y1) {
ky1 = 4;
}
if ((ky0 | ky1) == 1 || (ky0 | ky1) == 4) {
return null;
}
double zz0 = line.getStart().getZ();
double zz1 = line.getEnd().getZ();
int kz0 = 2;
if (zz0 < z0) {
kz0 = 1;
} else if (zz0 > z1) {
kz0 = 4;
}
int kz1 = 2;
if (zz1 < z0) {
kz1 = 1;
} else if (zz1 > z1) {
kz1 = 4;
}
if ((kz0 | kz1) == 1 || (kz0 | kz1) == 4) {
return null;
}
// if the X's, Y's and Z's are all inside, just return the line
if ((kx0 | kx1 | ky0 | ky1 | kz0 | kz1) == 2) {
return line;
}
// now we know we have to do some clipping
Point3D firstValidPoint = null;
if (isPoint3DInBoundingBox(line.getStart(), x0, x1, y0, y1, z0, z1)) {
firstValidPoint = line.getStart();
}
if (isPoint3DInBoundingBox(line.getEnd(), x0, x1, y0, y1, z0, z1)) {
firstValidPoint = line.getEnd();
}
if (((kx0 | kx1) & 1) != 0) { // X's span the lower bound
Point3D p = projectLineOnX(line, x0);
if (isPoint3DInBoundingBox(p, x0, x1, y0, y1, z0, z1)) {
if (firstValidPoint == null) {
firstValidPoint = p;
} else {
return new Line3D(firstValidPoint, p);
}
}
}
if (((kx0 | kx1) & 4) != 0) { // X's span the upper bound
Point3D p = projectLineOnX(line, x1);
if (isPoint3DInBoundingBox(p, x0, x1, y0, y1, z0, z1)) {
if (firstValidPoint == null) {
firstValidPoint = p;
} else {
return new Line3D(firstValidPoint, p);
}
}
}
if (((ky0 | ky1) & 1) != 0) { // Y's span the lower bound
Point3D p = projectLineOnY(line, y0);
if (isPoint3DInBoundingBox(p, x0, x1, y0, y1, z0, z1)) {
if (firstValidPoint == null) {
firstValidPoint = p;
} else {
return new Line3D(firstValidPoint, p);
}
}
}
if (((ky0 | ky1) & 4) != 0) { // Y's span the upper bound
Point3D p = projectLineOnY(line, y1);
if (isPoint3DInBoundingBox(p, x0, x1, y0, y1, z0, z1)) {
if (firstValidPoint == null) {
firstValidPoint = p;
} else {
return new Line3D(firstValidPoint, p);
}
}
}
if (((kz0 | kz1) & 1) != 0) { // X's span the lower bound
Point3D p = projectLineOnZ(line, z0);
if (isPoint3DInBoundingBox(p, x0, x1, y0, y1, z0, z1)) {
if (firstValidPoint == null) {
firstValidPoint = p;
} else {
return new Line3D(firstValidPoint, p);
}
}
}
if (((kz0 | kz1) & 4) != 0) { // X's span the upper bound
Point3D p = projectLineOnZ(line, z1);
if (isPoint3DInBoundingBox(p, x0, x1, y0, y1, z0, z1)) {
if (firstValidPoint == null) {
firstValidPoint = p;
} else {
return new Line3D(firstValidPoint, p);
}
}
}
return null;
}
private static Point3D projectLineOnX(Line3D line, double x) {
double x0 = line.getStart().getX();
double x1 = line.getEnd().getX();
double factor = 0.0;
if (Math.abs(x1 - x0) > 0) {
factor = (x - x0) / (x1 - x0);
}
if (factor >= 1.0) {
return line.getEnd();
} else if (factor <= 0.0) {
return line.getStart();
} else {
double y0 = line.getStart().getY();
double y1 = line.getEnd().getY();
double z0 = line.getStart().getZ();
double z1 = line.getEnd().getZ();
return new Point3D(x0 + factor * (x1 - x0), y0 + factor * (y1 - y0),
z0 + factor * (z1 - z0));
}
}
private static Point3D projectLineOnY(Line3D line, double y) {
double y0 = line.getStart().getY();
double y1 = line.getEnd().getY();
double factor = 0.0;
if (Math.abs(y1 - y0) > 0) {
factor = (y - y0) / (y1 - y0);
}
if (factor >= 1.0) {
return line.getEnd();
} else if (factor <= 0.0) {
return line.getStart();
} else {
double x0 = line.getStart().getX();
double x1 = line.getEnd().getX();
double z0 = line.getStart().getZ();
double z1 = line.getEnd().getZ();
return new Point3D(x0 + factor * (x1 - x0), y0 + factor * (y1 - y0),
z0 + factor * (z1 - z0));
}
}
private static Point3D projectLineOnZ(Line3D line, double z) {
double z0 = line.getStart().getZ();
double z1 = line.getEnd().getZ();
double factor = 0.0;
if (Math.abs(z1 - z0) > 0) {
factor = (z - z0) / (z1 - z0);
}
if (factor >= 1.0) {
return line.getEnd();
} else if (factor <= 0.0) {
return line.getStart();
} else {
double x0 = line.getStart().getX();
double x1 = line.getEnd().getX();
double y0 = line.getStart().getY();
double y1 = line.getEnd().getY();
return new Point3D(x0 + factor * (x1 - x0), y0 + factor * (y1 - y0),
z0 + factor * (z1 - z0));
}
}
private static boolean isPoint3DInBoundingBox(Point3D p, double x0,
double x1, double y0, double y1, double z0, double z1) {
double x = p.getX();
if (x < x0) {
return false;
}
if (x > x1) {
return false;
}
double y = p.getY();
if (y < y0) {
return false;
}
if (y > y1) {
return false;
}
double z = p.getZ();
if (z < z0) {
return false;
}
if (z > z1) {
return false;
}
return true;
}
}