
org.jfree.chart3d.internal.ChartBox3D Maven / Gradle / Ivy
/* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2022, by David Gilbert. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 org.jfree.chart3d.internal;
import java.awt.Color;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.jfree.chart3d.axis.TickData;
import org.jfree.chart3d.graphics2d.Anchor2D;
import org.jfree.chart3d.graphics3d.Face;
import org.jfree.chart3d.graphics3d.Object3D;
import org.jfree.chart3d.graphics3d.Point3D;
import org.jfree.chart3d.marker.MarkerData;
import org.jfree.chart3d.marker.MarkerDataType;
/**
* A chart box is the container within which the chart elements are drawn.
* The six faces of the box created so that they are visible only when they
* do not obscure the content of the chart (generally the back three faces
* will be visible and the front three faces will not be visible, although from
* some angles four faces will be visible at one time). There is also support
* provided for specifying gridlines on the visible faces, as well as markers
* to label specific values and value ranges.
*/
public class ChartBox3D {
private double xLength, yLength, zLength;
private double xOffset, yOffset, zOffset;
/**
* Tick info for the x-axis (or column axis).
*/
private List xTicks;
/**
* Tick info for the y-axis (or value axis).
*/
private List yTicks;
/**
* Tick info for the z-axis (or row axis).
*/
private List zTicks;
/**
* Incoming data for x-axis markers. New instances are created and
* assigned to chart box faces to track vertices.
*/
private List xMarkers;
/** Required data for y-axis markers. */
private List yMarkers;
/** Required data for z-axis markers. */
private List zMarkers;
private final Color color;
private ChartBoxFace faceA;
private ChartBoxFace faceB;
private ChartBoxFace faceC;
private ChartBoxFace faceD;
private ChartBoxFace faceE;
private ChartBoxFace faceF;
/**
* Creates a new chart box with the specified attributes. When drawn, only
* the faces at the rear of the box will be rendered, allowing the user to
* see the content of the box (typically the plot items).
*
* @param xLength the x-dimension.
* @param yLength the y-dimension.
* @param zLength the z-dimension.
* @param xOffset the x-offset.
* @param yOffset the y-offset.
* @param zOffset the z-offset.
* @param color the color for the sides of the box ({@code null} not
* permitted).
*/
public ChartBox3D(double xLength, double yLength, double zLength,
double xOffset, double yOffset, double zOffset, Color color) {
Args.nullNotPermitted(color, "color");
this.xLength = xLength;
this.yLength = yLength;
this.zLength = zLength;
this.xOffset = xOffset;
this.yOffset = yOffset;
this.zOffset = zOffset;
this.color = color;
this.xTicks = new ArrayList<>(0);
this.yTicks = new ArrayList<>(0);
this.zTicks = new ArrayList<>(0);
this.xMarkers = new ArrayList<>(0);
this.yMarkers = new ArrayList<>(0);
this.zMarkers = new ArrayList<>(0);
}
/**
* Returns the list of tick data items for the x-axis.
*
* @return The list of tick data items for the x-axis (possibly empty, but
* never {@code null}).
*
* @since 1.2
*/
public List getXTicks() {
return this.xTicks;
}
/**
* Sets the list of tick data items for the x-axis.
*
* @param ticks the tick data ({@code null} not permitted).
*
* @since 1.2
*/
public void setXTicks(List ticks) {
Args.nullNotPermitted(ticks, "ticks");
this.xTicks = ticks;
}
/**
* Returns the list of tick data items for the y-axis.
*
* @return The list of tick data items for the y-axis (possibly empty, but
* never {@code null}).
*
* @since 1.2
*/
public List getYTicks() {
return this.yTicks;
}
/**
* Sets the list of tick data items for the y-axis.
*
* @param ticks the tick data ({@code null} not permitted).
*
* @since 1.2
*/
public void setYTicks(List ticks) {
Args.nullNotPermitted(ticks, "ticks");
this.yTicks = ticks;
}
/**
* Returns the list of tick data items for the z-axis.
*
* @return The list of tick data items for the z-axis (possibly empty, but
* never {@code null}).
*
* @since 1.2
*/
public List getZTicks() {
return this.zTicks;
}
/**
* Sets the list of tick data items for the z-axis.
*
* @param ticks the tick data ({@code null} not permitted).
*
* @since 1.2
*/
public void setZTicks(List ticks) {
Args.nullNotPermitted(ticks, "ticks");
this.zTicks = ticks;
}
/**
* Returns the marker data for the x-axis markers, if any.
*
* @return The marker data for the x-axis markers (possibly empty but
* never {@code null}).
*
* @since 1.2
*/
public List getXMarkers() {
return this.xMarkers;
}
/**
* Sets the list of marker data items for the x-axis.
*
* @param markers the list of marker data items ({@code null} not
* permitted).
*
* @since 1.2
*/
public void setXMarkers(List markers) {
Args.nullNotPermitted(markers, "markers");
this.xMarkers = markers;
}
/**
* Returns the marker data for the y-axis markers, if any.
*
* @return The marker data for the y-axis markers (possibly empty but
* never {@code null}).
*
* @since 1.2
*/
public List getYMarkers() {
return this.yMarkers;
}
/**
* Sets the list of marker data items for the y-axis.
*
* @param markers the list of marker data items ({@code null} not
* permitted).
*
* @since 1.2
*/
public void setYMarkers(List markers) {
Args.nullNotPermitted(markers, "markers");
this.yMarkers = markers;
}
/**
* Returns the marker data for the z-axis markers, if any.
*
* @return The marker data for the z-axis markers (possibly empty but
* never {@code null}).
*
* @since 1.2
*/
public List getZMarkers() {
return this.zMarkers;
}
/**
* Sets the list of marker data items for the x-axis.
*
* @param markers the list of marker data items ({@code null} not
* permitted).
*
* @since 1.2
*/
public void setZMarkers(List markers) {
Args.nullNotPermitted(markers, "markers");
this.zMarkers = markers;
}
/**
* Returns face A (the bottom face, in the XZ axis plane).
*
* @return Face A.
*/
public ChartBoxFace faceA() {
return this.faceA;
}
/**
* Returns face B (the front face, in the XY axis plane).
*
* @return Face B.
*/
public ChartBoxFace faceB() {
return this.faceB;
}
/**
* Returns face C (the top face, in the XZ axis plane).
*
* @return Face C.
*/
public ChartBoxFace faceC() {
return this.faceC;
}
/**
* Returns face D (the rear face, in the XY axis plane).
*
* @return Face D.
*/
public ChartBoxFace faceD() {
return this.faceD;
}
/**
* Returns face E (the left face, in the YZ axis plane).
*
* @return Face E.
*/
public ChartBoxFace faceE() {
return this.faceE;
}
/**
* Returns face F (the right face, in the YZ axis plane).
*
* @return Face F.
*/
public ChartBoxFace faceF() {
return this.faceF;
}
/**
* Creates an {@link Object3D} that contains the six faces for the
* chart box, plus the vertices for the tick marks along the edges of
* each face.
*
* @return A 3D object.
*/
public Object3D createObject3D() {
Object3D box = new Object3D(this.color);
Point3D v0 = new Point3D(xOffset, yOffset, zOffset);
Point3D v1 = new Point3D(xLength + xOffset, yOffset, zOffset);
Point3D v2 = new Point3D(xLength + xOffset, yLength + yOffset, zOffset);
Point3D v3 = new Point3D(xOffset, yLength + yOffset, zOffset);
Point3D v4 = new Point3D(xOffset, yLength + yOffset, zLength + zOffset);
Point3D v5 = new Point3D(xOffset, yOffset, zLength + zOffset);
Point3D v6 = new Point3D(xLength + xOffset, yOffset, zLength + zOffset);
Point3D v7 = new Point3D(xLength + xOffset, yLength + yOffset, zLength
+ zOffset);
box.addVertex(v0); // 0, 0, 0
box.addVertex(v1); // 1, 0, 0
box.addVertex(v2); // 1, 1, 0
box.addVertex(v3); // 0, 1, 0
box.addVertex(v4); // 0, 1, 1
box.addVertex(v5); // 0, 0, 1
box.addVertex(v6); // 1, 0, 1
box.addVertex(v7); // 1, 1, 1
this.faceA = new ChartBoxFace(box, new int[] {0, 5, 6, 1}); // XZ
this.faceB = new ChartBoxFace(box, new int[] {0, 1, 2, 3}); // XY
this.faceC = new ChartBoxFace(box, new int[] {7, 4, 3, 2}); // XZ
this.faceD = new ChartBoxFace(box, new int[] {5, 4, 7, 6}); // XY
this.faceE = new ChartBoxFace(box, new int[] {0, 3, 4, 5}); // YZ
this.faceF = new ChartBoxFace(box, new int[] {6, 7, 2, 1}); // YZ
box.addFace(faceA);
box.addFace(faceB);
box.addFace(faceC);
box.addFace(faceD);
box.addFace(faceE);
box.addFace(faceF);
// add vertices for the x-grid lines (ABCD)
int base = 8;
for (TickData t : this.xTicks) {
double xx = this.xOffset + this.xLength * t.getPos();
box.addVertex(xx, yOffset, zOffset);
box.addVertex(xx, yOffset, zOffset + zLength);
box.addVertex(xx, yOffset + yLength, zOffset + zLength);
box.addVertex(xx, yOffset + yLength, zOffset);
TickData td0 = new TickData(t, base);
TickData td1 = new TickData(t, base + 1);
TickData td2 = new TickData(t, base + 2);
TickData td3 = new TickData(t, base + 3);
this.faceA.addXTicks(td0, td1);
this.faceB.addXTicks(td0, td3);
this.faceC.addXTicks(td3, td2);
this.faceD.addXTicks(td2, td1);
base += 4;
}
// add vertices for the y-grid lines (BDEF)
for (TickData t : this.yTicks) {
double yy = this.yOffset + this.yLength * t.getPos();
box.addVertex(xOffset, yy, zOffset);
box.addVertex(xOffset + xLength, yy, zOffset);
box.addVertex(xOffset + xLength, yy, zOffset + zLength);
box.addVertex(xOffset, yy, zOffset + zLength);
TickData td0 = new TickData(t, base);
TickData td1 = new TickData(t, base + 1);
TickData td2 = new TickData(t, base + 2);
TickData td3 = new TickData(t, base + 3);
this.faceB.addYTicks(td0, td1);
this.faceD.addYTicks(td2, td3);
this.faceE.addYTicks(td0, td3);
this.faceF.addYTicks(td1, td2);
base += 4;
}
// add vertices for the z-grid lines (ACEF)
for (TickData t : this.zTicks) {
double zz = this.zOffset + this.zLength * t.getPos();
box.addVertex(xOffset, yOffset, zz);
box.addVertex(xOffset + xLength, yOffset, zz);
box.addVertex(xOffset + xLength, yOffset + yLength, zz);
box.addVertex(xOffset, yOffset + yLength, zz);
TickData td0 = new TickData(t, base);
TickData td1 = new TickData(t, base + 1);
TickData td2 = new TickData(t, base + 2);
TickData td3 = new TickData(t, base + 3);
this.faceA.addZTicks(td0, td1);
this.faceC.addZTicks(td3, td2);
this.faceE.addZTicks(td0, td3);
this.faceF.addZTicks(td1, td2);
base += 4;
}
// add vertices for the x-markers
for (MarkerData m : this.xMarkers) {
if (m.getType().equals(MarkerDataType.VALUE)) {
double xpos = this.xOffset + xLength
* m.getValueLine().getPos();
base += addXMarker(box, m, xpos, base);
} else if (m.getType().equals(MarkerDataType.RANGE)) {
double startX = this.xOffset + xLength
* m.getStartLine().getPos();
double endX = this.xOffset + xLength * m.getEndLine().getPos();
base += addXRangeMarker(box, m, startX, endX, base);
}
}
// add vertices for the y-markers
for (MarkerData m : this.yMarkers) {
if (m.getType().equals(MarkerDataType.VALUE)) {
double ypos = this.yOffset + yLength
* m.getValueLine().getPos();
base += addYMarker(box, m, ypos, base);
} else if (m.getType().equals(MarkerDataType.RANGE)) {
double startY = this.yOffset + yLength
* m.getStartLine().getPos();
double endY = this.yOffset + yLength * m.getEndLine().getPos();
base += addYRangeMarker(box, m, startY, endY, base);
}
}
// add vertices for the z-markers
for (MarkerData m : this.zMarkers) {
if (m.getType().equals(MarkerDataType.VALUE)) {
double zpos = this.zOffset + zLength
* m.getValueLine().getPos();
base += addZMarker(box, m, zpos, base);
} else if (m.getType().equals(MarkerDataType.RANGE)) {
double startZ = this.zOffset + zLength
* m.getStartLine().getPos();
double endZ = this.zOffset + zLength * m.getEndLine().getPos();
base += addZRangeMarker(box, m, startZ, endZ, base);
}
}
return box;
}
/**
* Adds the vertices required for an x-marker (VALUE type), creates the
* marker data records and adds them to the four faces that will be
* required to draw the marker. If there is a label for the marker, this
* method will also add vertices to track the label anchor points.
*
* @param box the chart box.
* @param m the marker data record.
* @param x the x position for the marker.
* @param base the base vertex index.
*/
private int addXMarker(Object3D box, MarkerData m, double x, int base) {
int result = 4;
Point3D v0 = new Point3D(x, yOffset, zOffset);
Point3D v1 = new Point3D(x, yOffset, zOffset + zLength);
Point3D v2 = new Point3D(x, yOffset + yLength, zOffset + zLength);
Point3D v3 = new Point3D(x, yOffset + yLength, zOffset);
box.addVertex(v0);
box.addVertex(v1);
box.addVertex(v2);
box.addVertex(v3);
MarkerData md0 = new MarkerData(m, base, base + 1); // A
MarkerData md1 = new MarkerData(m, base + 1, base + 2); // D
MarkerData md2 = new MarkerData(m, base + 2, base + 3); // C
MarkerData md3 = new MarkerData(m, base + 3, base); // B
if (m.getLabelAnchor() != null) {
// add vertices for the label anchor
Point3D v4 = calcAnchorXY(m.getLabelAnchor(), v0, v3, xLength); // B
Point3D v5 = calcAnchorXY(m.getLabelAnchor(), v2, v1, xLength); // D
Point3D v6 = calcAnchorXZ(m.getLabelAnchor(), v1, v0, xLength); // A
Point3D v7 = calcAnchorXZ(m.getLabelAnchor(), v3, v2, xLength);
box.addVertex(v4);
box.addVertex(v5);
box.addVertex(v6);
box.addVertex(v7);
// now write back the label vertex indices to the marker data
md3.setLabelVertexIndex(base + 4);
md1.setLabelVertexIndex(base + 5);
md0.setLabelVertexIndex(base + 6);
md2.setLabelVertexIndex(base + 7);
result += 4;
}
this.faceA.addXMarker(md0);
this.faceD.addXMarker(md1);
this.faceC.addXMarker(md2);
this.faceB.addXMarker(md3);
return result;
}
/**
* Adds the vertices required for an x-marker (RANGE type), creates the
* marker data records and adds them to the four faces that will be
* required to draw the marker. If there is a label for the marker, this
* method will also add vertices to track the label anchor points.
*
* @param box the chart box.
* @param m the marker data record.
* @param startX the starting x position for the marker.
* @param endX the ending x position for the marker.
* @param base the base vertex index.
*/
private int addXRangeMarker(Object3D box, MarkerData m, double startX,
double endX, int base) {
int result = 8; // number of vertices added
Point3D v0 = new Point3D(startX, yOffset, zOffset);
Point3D v1 = new Point3D(startX, yOffset, zOffset + zLength);
Point3D v2 = new Point3D(startX, yOffset + yLength, zOffset + zLength);
Point3D v3 = new Point3D(startX, yOffset + yLength, zOffset);
Point3D v4 = new Point3D(endX, yOffset, zOffset);
Point3D v5 = new Point3D(endX, yOffset, zOffset + zLength);
Point3D v6 = new Point3D(endX, yOffset + yLength, zOffset + zLength);
Point3D v7 = new Point3D(endX, yOffset + yLength, zOffset);
box.addVertex(v0);
box.addVertex(v1);
box.addVertex(v2);
box.addVertex(v3);
box.addVertex(v4);
box.addVertex(v5);
box.addVertex(v6);
box.addVertex(v7);
MarkerData md0 = new MarkerData(m, base, base + 1, base + 4,
base + 5); // A
MarkerData md1 = new MarkerData(m, base + 1, base + 2, base + 5,
base + 6); // D
MarkerData md2 = new MarkerData(m, base + 2, base + 3, base + 6,
base + 7); // C
MarkerData md3 = new MarkerData(m, base + 3, base, base + 7,
base + 4); // B
if (m.getLabelAnchor() != null) {
// add vertices for the label anchor
Point3D v8 = calcRangeAnchorXY(m.getLabelAnchor(), v2, v1, v6, v5);
Point3D v9 = calcRangeAnchorXY(m.getLabelAnchor(), v0, v3, v4, v7);
Point3D v10 = calcRangeAnchorXZ(m.getLabelAnchor(), v3, v2, v7, v6);
Point3D v11 = calcRangeAnchorXZ(m.getLabelAnchor(), v1, v0, v5, v4);
box.addVertex(v8);
box.addVertex(v9);
box.addVertex(v10);
box.addVertex(v11);
// now write back the label vertex indices to the marker data
md1.setLabelVertexIndex(base + 8);
md3.setLabelVertexIndex(base + 9);
md2.setLabelVertexIndex(base + 10);
md0.setLabelVertexIndex(base + 11);
result += 4;
}
this.faceA.addXMarker(md0);
this.faceD.addXMarker(md1);
this.faceC.addXMarker(md2);
this.faceB.addXMarker(md3);
return result;
}
/**
* Adds the vertices required for an y-marker (VALUE type), creates the
* marker data records and adds them to the four faces that will be
* required to draw the marker. If there is a label for the marker, this
* method will also add vertices to track the label anchor points.
*
* @param box the chart box.
* @param m the marker data record.
* @param y the y position for the marker.
* @param base the base vertex index.
*/
private int addYMarker(Object3D box, MarkerData m, double y, int base) {
int result = 4; // number of vertices added
Point3D v0 = new Point3D(xOffset, y, zOffset);
Point3D v1 = new Point3D(xOffset, y, zOffset + zLength);
Point3D v2 = new Point3D(xOffset + xLength, y, zOffset + zLength);
Point3D v3 = new Point3D(xOffset + xLength, y, zOffset);
box.addVertex(v0);
box.addVertex(v1);
box.addVertex(v2);
box.addVertex(v3);
MarkerData md0 = new MarkerData(m, base, base + 1); // E
MarkerData md1 = new MarkerData(m, base + 1, base + 2); // D
MarkerData md2 = new MarkerData(m, base + 2, base + 3); // F
MarkerData md3 = new MarkerData(m, base + 3, base); // B
if (m.getLabelAnchor() != null) {
// add vertices for the label anchor
Point3D v4 = calcAnchorYX(m.getLabelAnchor(), v1, v2, yLength); // D
Point3D v5 = calcAnchorYX(m.getLabelAnchor(), v3, v0, yLength); // B
Point3D v6 = calcAnchorYZ(m.getLabelAnchor(), v0, v1, yLength); // E
Point3D v7 = calcAnchorYZ(m.getLabelAnchor(), v2, v3, yLength); // F
box.addVertex(v4);
box.addVertex(v5);
box.addVertex(v6);
box.addVertex(v7);
// now write back the label vertex indices to the marker data
md1.setLabelVertexIndex(base + 4);
md3.setLabelVertexIndex(base + 5);
md0.setLabelVertexIndex(base + 6);
md2.setLabelVertexIndex(base + 7);
result += 4;
}
this.faceE.addYMarker(md0);
this.faceD.addYMarker(md1);
this.faceF.addYMarker(md2);
this.faceB.addYMarker(md3);
return result;
}
/**
* Adds the vertices required for an y-marker (RANGE type), creates the
* marker data records and adds them to the four faces that will be
* required to draw the marker. If there is a label for the marker, this
* method will also add vertices to track the label anchor points.
*
* @param box the chart box.
* @param m the marker data record.
* @param startY the starting y position for the marker.
* @param endY the ending y position for the marker.
* @param base the base vertex index.
*/
private int addYRangeMarker(Object3D box, MarkerData m, double startY,
double endY, int base) {
int result = 8; // number of vertices added
Point3D v0 = new Point3D(xOffset, startY, zOffset);
Point3D v1 = new Point3D(xOffset, startY, zOffset + zLength);
Point3D v2 = new Point3D(xOffset + xLength, startY, zOffset + zLength);
Point3D v3 = new Point3D(xOffset + xLength, startY, zOffset);
Point3D v4 = new Point3D(xOffset, endY, zOffset);
Point3D v5 = new Point3D(xOffset, endY, zOffset + zLength);
Point3D v6 = new Point3D(xOffset + xLength, endY, zOffset + zLength);
Point3D v7 = new Point3D(xOffset + xLength, endY, zOffset);
box.addVertex(v0);
box.addVertex(v1);
box.addVertex(v2);
box.addVertex(v3);
box.addVertex(v4);
box.addVertex(v5);
box.addVertex(v6);
box.addVertex(v7);
MarkerData md0 = new MarkerData(m, base, base + 1, base + 4,
base + 5); // E
MarkerData md1 = new MarkerData(m, base + 1, base + 2, base + 5,
base + 6); // D
MarkerData md2 = new MarkerData(m, base + 2, base + 3, base + 6,
base + 7); // F
MarkerData md3 = new MarkerData(m, base + 3, base, base + 7,
base + 4); // B
if (m.getLabelAnchor() != null) {
// add vertices for the label anchor
Point3D v8 = calcRangeAnchorYX(m.getLabelAnchor(), v1, v2, v5, v6);
Point3D v9 = calcRangeAnchorYX(m.getLabelAnchor(), v3, v0, v7, v4);
Point3D v10 = calcRangeAnchorYZ(m.getLabelAnchor(), v2, v3, v6, v7);
Point3D v11 = calcRangeAnchorYZ(m.getLabelAnchor(), v0, v1, v4, v5);
box.addVertex(v8);
box.addVertex(v9);
box.addVertex(v10);
box.addVertex(v11);
// now write back the label vertex indices to the marker data
md1.setLabelVertexIndex(base + 8);
md3.setLabelVertexIndex(base + 9);
md2.setLabelVertexIndex(base + 10);
md0.setLabelVertexIndex(base + 11);
result += 4;
}
this.faceE.addYMarker(md0);
this.faceD.addYMarker(md1);
this.faceF.addYMarker(md2);
this.faceB.addYMarker(md3);
return result;
}
/**
* Adds the vertices required for an z-marker (VALUE type), creates the
* marker data records and adds them to the four faces that will be
* required to draw the marker. If there is a label for the marker, this
* method will also add vertices to track the label anchor points.
*
* @param box the chart box.
* @param m the marker data record.
* @param z the z position for the marker.
* @param base the base vertex index.
*/
private int addZMarker(Object3D box, MarkerData m, double z, int base) {
int result = 4; // number of vertices added
Point3D v0 = new Point3D(xOffset, yOffset, z);
Point3D v1 = new Point3D(xOffset + xLength, yOffset, z);
Point3D v2 = new Point3D(xOffset + xLength, yOffset + yLength, z);
Point3D v3 = new Point3D(xOffset, yOffset + yLength, z);
box.addVertex(v0); // A
box.addVertex(v1);
box.addVertex(v2);
box.addVertex(v3);
MarkerData md0 = new MarkerData(m, base, base + 1); // A
MarkerData md1 = new MarkerData(m, base + 1, base + 2); // F
MarkerData md2 = new MarkerData(m, base + 2, base + 3); // C
MarkerData md3 = new MarkerData(m, base + 3, base); // E
if (m.getLabelAnchor() != null) {
// add vertices for the label anchor
Point3D v4 = calcAnchorZX(m.getLabelAnchor(), v0, v1, zLength); // A
Point3D v5 = calcAnchorZX(m.getLabelAnchor(), v2, v3, zLength); // C
Point3D v6 = calcAnchorZY(m.getLabelAnchor(), v1, v2, zLength); // F
Point3D v7 = calcAnchorZY(m.getLabelAnchor(), v3, v0, zLength); // E
box.addVertex(v4);
box.addVertex(v5);
box.addVertex(v6);
box.addVertex(v7);
// now write back the label vertex indices to the marker data
md0.setLabelVertexIndex(base + 4);
md2.setLabelVertexIndex(base + 5);
md1.setLabelVertexIndex(base + 6);
md3.setLabelVertexIndex(base + 7);
result += 4;
}
this.faceA.addZMarker(md0);
this.faceF.addZMarker(md1);
this.faceC.addZMarker(md2);
this.faceE.addZMarker(md3);
return result;
}
/**
* Adds the vertices required for an x-marker (RANGE type), creates the
* marker data records and adds them to the four faces that will be
* required to draw the marker. If there is a label for the marker, this
* method will also add vertices to track the label anchor points.
*
* @param box the chart box.
* @param m the marker data record.
* @param startZ the starting z position for the marker.
* @param endZ the ending z position for the marker.
* @param base the base vertex index.
*/
private int addZRangeMarker(Object3D box, MarkerData m, double startZ,
double endZ, int base) {
int result = 8;
Point3D v0 = new Point3D(xOffset, yOffset, startZ);
Point3D v1 = new Point3D(xOffset + xLength, yOffset, startZ);
Point3D v2 = new Point3D(xOffset + xLength, yOffset + yLength, startZ);
Point3D v3 = new Point3D(xOffset, yOffset + yLength, startZ);
Point3D v4 = new Point3D(xOffset, yOffset, endZ);
Point3D v5 = new Point3D(xOffset + xLength, yOffset, endZ);
Point3D v6 = new Point3D(xOffset + xLength, yOffset + yLength, endZ);
Point3D v7 = new Point3D(xOffset, yOffset + yLength, endZ);
box.addVertex(v0); // A
box.addVertex(v1);
box.addVertex(v2);
box.addVertex(v3);
box.addVertex(v4); // A
box.addVertex(v5);
box.addVertex(v6);
box.addVertex(v7);
MarkerData md0 = new MarkerData(m, base, base + 1, base + 4,
base + 5); // A
MarkerData md1 = new MarkerData(m, base + 1, base + 2, base + 5,
base + 6); // F
MarkerData md2 = new MarkerData(m, base + 2, base + 3, base + 6,
base + 7); // C
MarkerData md3 = new MarkerData(m, base + 3, base, base + 7,
base + 4); // E
if (m.getLabelAnchor() != null) {
// add vertices for the label anchor
Point3D v8 = calcRangeAnchorZX(m.getLabelAnchor(), v0, v1, v4, v5);
Point3D v9 = calcRangeAnchorZX(m.getLabelAnchor(), v2, v3, v6, v7);
Point3D v10 = calcRangeAnchorZY(m.getLabelAnchor(), v3, v0, v7, v4);
Point3D v11 = calcRangeAnchorZY(m.getLabelAnchor(), v1, v2, v5, v6);
box.addVertex(v8);
box.addVertex(v9);
box.addVertex(v10);
box.addVertex(v11);
// now write back the label vertex indices to the marker data
md0.setLabelVertexIndex(base + 8);
md2.setLabelVertexIndex(base + 9);
md3.setLabelVertexIndex(base + 10);
md1.setLabelVertexIndex(base + 11);
result += 4;
}
this.faceA.addZMarker(md0);
this.faceF.addZMarker(md1);
this.faceC.addZMarker(md2);
this.faceE.addZMarker(md3);
return result;
}
/**
* Returns the horizontal offset for an anchor assuming that (a) the delta
* is expressed as a percentage, and (b) the length in the offset direction
* is {@code length}.
*
* @param anchor the anchor.
* @param length the length.
*
* @return The offset.
*/
private double hoffset(Anchor2D anchor, double length) {
double offset = 0.0;
if (anchor.getRefPt().isLeft()) {
offset = length * anchor.getOffset().getDX();
} else if (anchor.getRefPt().isRight()) {
offset = -length * anchor.getOffset().getDX();
}
return offset;
}
/**
* Returns the vertical offset for an anchor assuming that (a) the delta is
* expressed as a percentage, and (b) the length in the offset direction
* is {@code length}.
*
* @param anchor the anchor.
* @param length the length.
*
* @return The offset.
*/
private double voffset(Anchor2D anchor, double length) {
double offset = 0.0;
if (anchor.getRefPt().isTop()) {
offset = length * anchor.getOffset().getDY();
} else if (anchor.getRefPt().isBottom()) {
offset = -length * anchor.getOffset().getDY();
}
return offset;
}
/**
* Returns the horizontal position along the line based on the anchor
* point.
*
* @param anchor the anchor ({@code null} not permitted).
* @param start the start value.
* @param end the end value.
*
* @return The position.
*/
private double hpos(Anchor2D anchor, double start, double end) {
if (anchor.getRefPt().isLeft()) {
return start;
} else if (anchor.getRefPt().isRight()) {
return end;
}
return (start + end) / 2.0;
}
// anchor for x-marker label where line runs parallel to y-axis
private Point3D calcAnchorXY(Anchor2D anchor, Point3D start, Point3D end,
double xLength) {
double dx = hoffset(anchor, end.getY() - start.getY());
double dy = voffset(anchor, xLength);
double y = hpos(anchor, start.getY(), end.getY());
return new Point3D(start.getX() + dy, y + dx, start.getZ());
}
// anchor for x-marker label where line runs parallel to z-axis
private Point3D calcAnchorXZ(Anchor2D anchor, Point3D start, Point3D end,
double xLength) {
double dx = hoffset(anchor, end.getZ() - start.getZ());
double dy = voffset(anchor, xLength);
double z = hpos(anchor, start.getZ(), end.getZ());
return new Point3D(start.getX() + dy, start.getY(), z + dx);
}
// anchor for y-marker label where line runs parallel to x-axis
private Point3D calcAnchorYX(Anchor2D anchor, Point3D start, Point3D end,
double yLength) {
double dx = hoffset(anchor, end.getX() - start.getX());
double dy = voffset(anchor, yLength);
double x = hpos(anchor, start.getX(), end.getX());
return new Point3D(x + dx, start.getY() + dy, start.getZ());
}
// anchor for y-marker label where line runs parallel to z-axis
private Point3D calcAnchorYZ(Anchor2D anchor, Point3D start, Point3D end,
double yLength) {
double dx = hoffset(anchor, end.getZ() - start.getZ());
double dy = voffset(anchor, yLength);
double z = hpos(anchor, start.getZ(), end.getZ());
return new Point3D(start.getX(), start.getY() + dy, z + dx);
}
// anchor for z-marker label where line runs parallel to x-axis
private Point3D calcAnchorZX(Anchor2D anchor, Point3D start, Point3D end,
double zLength) {
double dx = hoffset(anchor, end.getX() - start.getX());
double dy = voffset(anchor, zLength);
double x = hpos(anchor, start.getX(), end.getX());
return new Point3D(x + dx, start.getY(), start.getZ() + dy);
}
// anchor for z-marker label where line runs parallel to y-axis
private Point3D calcAnchorZY(Anchor2D anchor, Point3D start, Point3D end,
double zLength) {
double dx = hoffset(anchor, end.getY() - start.getY());
double dy = voffset(anchor, zLength);
double y = hpos(anchor, start.getY(), end.getY());
return new Point3D(start.getX(), y + dx, start.getZ() + dy);
}
// anchor for x-marker label where band runs parallel to y-axis
private Point3D calcRangeAnchorXY(Anchor2D anchor, Point3D start1,
Point3D end1, Point3D start2, Point3D end2) {
Point2D p = anchor.resolveAnchorWithPercentOffset(start1.getY(),
start1.getX(), end2.getY(), end2.getX());
return new Point3D(p.getY(), p.getX(), end1.getZ());
}
// anchor for x-marker label where line runs parallel to z-axis
private Point3D calcRangeAnchorXZ(Anchor2D anchor, Point3D start1,
Point3D end1, Point3D start2, Point3D end2) {
Point2D p = anchor.resolveAnchorWithPercentOffset(start1.getZ(),
start1.getX(), end2.getZ(), end2.getX());
return new Point3D(p.getY(), end1.getY(), p.getX());
}
// // anchor for y-marker label where line runs parallel to x-axis
private Point3D calcRangeAnchorYX(Anchor2D anchor, Point3D start1,
Point3D end1, Point3D start2, Point3D end2) {
Point2D p = anchor.resolveAnchorWithPercentOffset(start1.getX(),
start1.getY(), end2.getX(), end2.getY());
return new Point3D(p.getX(), p.getY(), end1.getZ());
}
// // anchor for y-marker label where line runs parallel to z-axis
private Point3D calcRangeAnchorYZ(Anchor2D anchor, Point3D start1,
Point3D end1, Point3D start2, Point3D end2) {
Point2D p = anchor.resolveAnchorWithPercentOffset(start1.getZ(),
start1.getY(), end2.getZ(), end2.getY());
return new Point3D(start1.getX(), p.getY(), p.getX());
}
// anchor for z-marker label where line runs parallel to x-axis
private Point3D calcRangeAnchorZX(Anchor2D anchor, Point3D start1,
Point3D end1, Point3D start2, Point3D end2) {
Point2D p = anchor.resolveAnchorWithPercentOffset(start1.getX(),
start1.getZ(), end2.getX(), end2.getZ());
return new Point3D(p.getX(), end1.getY(), p.getY());
}
// anchor for z-marker label where line runs parallel to y-axis
private Point3D calcRangeAnchorZY(Anchor2D anchor, Point3D start1,
Point3D end1, Point3D start2, Point3D end2) {
Point2D p = anchor.resolveAnchorWithPercentOffset(start1.getY(),
start1.getZ(), end2.getY(), end2.getZ());
return new Point3D(end1.getX(), p.getX(), p.getY());
}
/**
* A special subclass of {@link Face} that is used by the {@link ChartBox3D}
* so that when faces are sorted by z-order, the chart box sides are always
* drawn first (furthest in the background). Also, these faces track
* tick marks, values and anchor points.
*/
public static final class ChartBoxFace extends Face {
/** Info about the x-axis ticks on edge A. */
private final List xTicksA;
/** Info about the x-axis ticks on edge B. */
private final List xTicksB;
/** Info about the y-axis ticks on edge A. */
private final List yTicksA;
/** Info about the y-axis ticks on edge B. */
private final List yTicksB;
/** Info about the z-axis ticks on edge A. */
private final List zTicksA;
/** Info about the z-axis ticks on edge B. */
private final List zTicksB;
private final List xMarkers;
private final List yMarkers;
private final List zMarkers;
/**
* Creates a new face for a {@link ChartBox3D}.
*
* @param owner the object that the new face belongs to ({@code null}
* not permitted).
* @param vertices the indices of the vertices.
*
* @since 1.3
*/
public ChartBoxFace(Object3D owner, int[] vertices) {
super(owner, vertices);
this.xTicksA = new ArrayList<>();
this.xTicksB = new ArrayList<>();
this.yTicksA = new ArrayList<>();
this.yTicksB = new ArrayList<>();
this.zTicksA = new ArrayList<>();
this.zTicksB = new ArrayList<>();
this.xMarkers = new ArrayList<>();
this.yMarkers = new ArrayList<>();
this.zMarkers = new ArrayList<>();
}
/**
* Clears the ticks for the x-axis.
*/
public void clearXTicks() {
this.xTicksA.clear();
this.xTicksB.clear();
}
/**
* Returns the x-axis tick data for edge A.
*
* @return The x-axis tick data for edge A.
*/
public List getXTicksA() {
return this.xTicksA;
}
/**
* Adds tick data for edges A and B.
*
* @param a data for a tick on edge A.
* @param b data for a tick on edge B.
*/
public void addXTicks(TickData a, TickData b) {
this.xTicksA.add(a);
this.xTicksB.add(b);
}
/**
* Returns the x-axis tick data for the edge B.
*
* @return The x-axis tick data for the edge B.
*/
public List getXTicksB() {
return this.xTicksB;
}
/**
* Adds tick data items for the y-axis.
*
* @param a data for a tick on edge A.
* @param b data for a tick on edge B.
*/
public void addYTicks(TickData a, TickData b) {
this.yTicksA.add(a);
this.yTicksB.add(b);
}
/**
* Returns the y-axis tick data for the edge A.
*
* @return The y-axis tick data for the edge A.
*/
public List getYTicksA() {
return this.yTicksA;
}
/**
* Returns the y-axis tick data for the edge B.
*
* @return The y-axis tick data for the edge B.
*/
public List getYTicksB() {
return this.yTicksB;
}
/**
* Adds tick data items for the z-axis.
*
* @param a data for a tick on edge A.
* @param b data for a tick on edge B.
*/
public void addZTicks(TickData a, TickData b) {
this.zTicksA.add(a);
this.zTicksB.add(b);
}
/**
* Returns the z-axis tick data for the edge A.
*
* @return The z-axis tick data for the edge A.
*/
public List getZTicksA() {
return this.zTicksA;
}
/**
* Returns the z-axis tick data for the edge B.
*
* @return The z-axis tick data for the edge B.
*/
public List getZTicksB() {
return this.zTicksB;
}
/**
* Adds marker data for the x-dimension.
*
* @param marker the marker data ({@code null} not permitted).
*/
public void addXMarker(MarkerData marker) {
Args.nullNotPermitted(marker, "marker");
this.xMarkers.add(marker);
}
/**
* Returns a list of marker data for the x-dimension (the list is not
* modifiable).
*
* @return The markers (never {@code null}).
*/
public List getXMarkers() {
return Collections.unmodifiableList(this.xMarkers);
}
/**
* Adds marker data for the y-dimension.
*
* @param marker the marker data ({@code null} not permitted).
*/
public void addYMarker(MarkerData marker) {
Args.nullNotPermitted(marker, "marker");
this.yMarkers.add(marker);
}
/**
* Returns a list of marker data for the y-dimension (the list is not
* modifiable).
*
* @return The markers (never {@code null}).
*/
public List getYMarkers() {
return Collections.unmodifiableList(this.yMarkers);
}
/**
* Adds marker data for the z-dimension.
*
* @param marker the marker data ({@code null} not permitted).
*/
public void addZMarker(MarkerData marker) {
Args.nullNotPermitted(marker, "marker");
this.zMarkers.add(marker);
}
/**
* Returns a list of marker data for the z-dimension (the list is not
* modifiable).
*
* @return The markers (never {@code null}).
*/
public List getZMarkers() {
return Collections.unmodifiableList(this.zMarkers);
}
/**
* Returns {@code -123456f} which ensures that the chart box face
* is always drawn first (before any data items).
*
* @param points the points (ignored here).
*
* @return {@code -123456f}.
*/
@Override
public float calculateAverageZValue(Point3D[] points) {
return -123456f;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy