com.orsoncharts.graphics3d.swing.Panel3D 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.
The newest version!
/* ===========================================================
* 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.swing;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.Dimension2D;
import java.io.File;
import javax.swing.JPanel;
import javax.swing.ToolTipManager;
import com.orsoncharts.util.ArgChecks;
import com.orsoncharts.graphics3d.Drawable3D;
import com.orsoncharts.graphics3d.Offset2D;
import com.orsoncharts.graphics3d.ViewPoint3D;
import com.orsoncharts.graphics3d.Dimension3D;
import com.orsoncharts.graphics3d.ExportUtils;
import com.orsoncharts.graphics3d.RenderingInfo;
/**
* A panel that displays a set of 3D objects from a particular viewing point.
* The view point is maintained by the {@link Drawable3D} but the panel
* provides convenience methods to get/set it.
*
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*/
@SuppressWarnings("serial")
public class Panel3D extends JPanel implements MouseListener,
MouseMotionListener, MouseWheelListener {
/**
* The object that is displayed in the panel.
*/
private Drawable3D drawable;
/**
* The minimum viewing distance (zooming in will not go closer than this).
*/
private double minViewingDistance;
private double maxViewingDistanceMultiplier;
/**
* The margin to leave around the edges of the chart when zooming to fit.
* This is expressed as a percentage (0.25 = 25 percent) of the width
* and height.
*/
private double margin;
/** The angle increment for panning left and right (in radians). */
private double panIncrement;
/** The angle increment for rotating up and down (in radians). */
private double rotateIncrement;
/** The roll increment (in radians). */
private double rollIncrement;
/**
* The (screen) point of the last mouse click (will be {@code null}
* initially). Used to calculate the mouse drag distance and direction.
*/
private Point lastClickPoint;
/**
* The (screen) point of the last mouse move point that was handled.
*/
private Point lastMovePoint;
/**
* Temporary state to track the 2D offset during an ALT-mouse-drag
* operation.
*/
private Offset2D offsetAtMousePressed;
private RenderingInfo renderingInfo;
/**
* Creates a new panel with the specified {@link Drawable3D} to
* display.
*
* @param drawable the content to display ({@code null} not
* permitted).
*/
public Panel3D(Drawable3D drawable) {
super(new BorderLayout());
ArgChecks.nullNotPermitted(drawable, "drawable");
this.drawable = drawable;
this.margin = 0.25;
this.minViewingDistance
= drawable.getDimensions().getDiagonalLength();
this.maxViewingDistanceMultiplier = 8.0;
this.panIncrement = Math.PI / 60;
this.rotateIncrement = Math.PI / 60;
this.rollIncrement = Math.PI / 60;
addMouseListener(this);
addMouseMotionListener(this);
addMouseWheelListener(this);
}
/**
* Returns the {@code Drawable3D} object that is displayed in this
* panel. This is specified via the panel constructor and there is no
* setter method to change it.
*
* @return The {@code Drawable3D} object (never {@code null}).
*/
public Drawable3D getDrawable() {
return this.drawable;
}
/**
* Returns the margin, expressed as a percentage, that controls the amount
* of space to leave around the edges of the 3D content when the
* {@code zoomToFit()} method is called. The default value is
* {@code 0.25} (25 percent).
*
* @return The margin.
*/
public double getMargin() {
return this.margin;
}
/**
* Sets the margin that controls the amount of space to leave around the
* edges of the 3D content when the {@code zoomToFit()} method is
* called.
*
* @param margin the margin (as a percentage, where 0.25 = 25 percent).
*/
public void setMargin(double margin) {
this.margin = margin;
}
/**
* Returns the minimum viewing distance. Zooming by mouse wheel or other
* means will not move the viewing point closer than this. The value
* is computed in the constructor from the dimensions of the drawable
* object.
*
* @return The minimum viewing distance.
*/
public double getMinViewingDistance() {
return this.minViewingDistance;
}
/**
* Returns the multiplier for the maximum viewing distance (a multiple of
* the minimum viewing distance). The default value is {@code 8.0}.
*
* @return The multiplier.
*
* @since 1.3
*/
public double getMaxViewingDistanceMultiplier() {
return this.maxViewingDistanceMultiplier;
}
/**
* Sets the multiplier used to calculate the maximum viewing distance.
*
* @param multiplier the new multiplier.
*
* @since 1.3
*/
public void setMaxViewingDistanceMultiplier(double multiplier) {
this.maxViewingDistanceMultiplier = multiplier;
}
/**
* Returns the angle delta for each pan left or right. The default
* value is {@code Math.PI / 60}.
*
* @return The angle delta (in radians).
*/
public double getPanIncrement() {
return panIncrement;
}
/**
* Sets the standard increment for panning left and right (a rotation
* specified in radians).
*
* @param panIncrement the increment (in radians).
*/
public void setPanIncrement(double panIncrement) {
this.panIncrement = panIncrement;
}
/**
* Returns the angle delta for each rotate up or down. The default
* value is {@code Math.PI / 60}.
*
* @return The angle delta (in radians).
*/
public double getRotateIncrement() {
return rotateIncrement;
}
/**
* Sets the vertical (up and down) rotation increment (in radians).
*
* @param rotateIncrement the increment (in radians).
*/
public void setRotateIncrement(double rotateIncrement) {
this.rotateIncrement = rotateIncrement;
}
/**
* Returns the angle delta for each roll operation. The default
* value is {@code Math.PI / 60}.
*
* @return The angle delta (in radians).
*/
public double getRollIncrement() {
return rollIncrement;
}
/**
* Sets the roll increment in radians.
*
* @param rollIncrement the increment (in radians).
*/
public void setRollIncrement(double rollIncrement) {
this.rollIncrement = rollIncrement;
}
/**
* Returns the view point that is maintained by the {@link Drawable3D}
* instance on display.
*
* @return The view point (never {@code null}).
*/
public ViewPoint3D getViewPoint() {
return this.drawable.getViewPoint();
}
/**
* Sets a new view point and repaints the panel.
*
* @param vp the view point ({@code null} not permitted).
*/
public void setViewPoint(ViewPoint3D vp) {
ArgChecks.nullNotPermitted(vp, "vp");
this.drawable.setViewPoint(vp); //
repaint();
}
/**
* Returns the last click point (possibly {@code null}).
*
* @return The last click point (possibly {@code null}).
*/
protected Point getLastClickPoint() {
return this.lastClickPoint;
}
/**
* Returns the rendering info from the previous call to
* draw().
*
* @return The rendering info (possibly {@code null}).
*/
protected RenderingInfo getRenderingInfo() {
return this.renderingInfo;
}
/**
* Rotates the view point around from left to right by the specified
* angle and repaints the 3D scene. The direction relative to the
* world coordinates depends on the orientation of the view point.
*
* @param angle the angle of rotation (in radians).
*/
public void panLeftRight(double angle) {
this.drawable.getViewPoint().panLeftRight(angle);
repaint();
}
/**
* Adjusts the viewing distance so that the chart fits the current panel
* size. A margin is left (see {@link #getMargin()} around the edges to
* leave room for labels etc.
*/
public void zoomToFit() {
zoomToFit(getSize());
}
/**
* Adjusts the viewing distance so that the chart fits the specified
* size. A margin is left (see {@link #getMargin()} around the edges to
* leave room for labels etc.
*
* @param size the target size ({@code null} not permitted).
*/
public void zoomToFit(Dimension2D size) {
int w = (int) (size.getWidth() * (1.0 - this.margin));
int h = (int) (size.getHeight() * (1.0 - this.margin));
Dimension2D target = new Dimension(w, h);
Dimension3D d3d = this.drawable.getDimensions();
float distance = this.drawable.getViewPoint().optimalDistance(target,
d3d, this.drawable.getProjDistance());
this.drawable.getViewPoint().setRho(distance);
repaint();
}
/**
* Paints the panel by asking the drawable to render a 2D projection of the
* objects it is managing.
*
* @param g the graphics target ({@code null} not permitted, assumed to be
* an instance of {@code Graphics2D}).
*/
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
AffineTransform saved = g2.getTransform();
Dimension size = getSize();
Insets insets = getInsets();
Rectangle drawArea = new Rectangle(insets.left, insets.top,
size.width - insets.left - insets.right,
size.height - insets.top - insets.bottom);
this.renderingInfo = this.drawable.draw(g2, drawArea);
g2.setTransform(saved);
}
/**
* Registers this component with the tool tip manager.
*
* @since 1.3
*/
public void registerForTooltips() {
ToolTipManager.sharedInstance().registerComponent(this);
}
/**
* Unregisters this component with the tool tip manager.
*
* @since 1.3
*/
public void unregisterForTooltips() {
ToolTipManager.sharedInstance().unregisterComponent(this);
}
/* (non-Javadoc)
* @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
*/
@Override
public void mouseClicked(MouseEvent e) {
// nothing to do
}
/* (non-Javadoc)
* @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
*/
@Override
public void mouseEntered(MouseEvent e) {
// nothing to do
}
/* (non-Javadoc)
* @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
*/
@Override
public void mouseExited(MouseEvent e) {
// nothing to do
}
/* (non-Javadoc)
* @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
*/
@Override
public void mousePressed(MouseEvent e) {
this.lastClickPoint = e.getPoint();
this.lastMovePoint = this.lastClickPoint;
this.offsetAtMousePressed = this.drawable.getTranslate2D();
}
/* (non-Javadoc)
* @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
*/
@Override
public void mouseReleased(MouseEvent e) {
// nothing to do
}
/* (non-Javadoc)
* @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
*/
@Override
public void mouseDragged(MouseEvent e) {
if (e.isAltDown()) {
Point currPt = e.getPoint();
Offset2D offset = this.offsetAtMousePressed;
Point lastPt = getLastClickPoint();
double dx = offset.getDX() + (currPt.x - lastPt.x);
double dy = offset.getDY() + (currPt.y - lastPt.y);
this.drawable.setTranslate2D(new Offset2D(dx, dy));
} else {
Point currPt = e.getPoint();
int dx = currPt.x - this.lastMovePoint.x;
int dy = currPt.y - this.lastMovePoint.y;
this.lastMovePoint = currPt;
this.drawable.getViewPoint().panLeftRight(-dx * Math.PI / 120);
this.drawable.getViewPoint().moveUpDown(-dy * Math.PI / 120);
repaint();
}
}
/* (non-Javadoc)
* @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
*/
@Override
public void mouseMoved(MouseEvent e) {
// nothing to do
}
/**
* Receives notification of a mouse wheel movement and responds by moving
* the viewpoint in or out (zooming).
*
* @param mwe the mouse wheel event.
*/
@Override
public void mouseWheelMoved(MouseWheelEvent mwe) {
float units = mwe.getUnitsToScroll();
double maxViewingDistance = this.maxViewingDistanceMultiplier
* this.minViewingDistance;
double valRho = Math.max(this.minViewingDistance,
Math.min(maxViewingDistance,
this.drawable.getViewPoint().getRho() + units));
this.drawable.getViewPoint().setRho(valRho);
repaint();
}
/**
* Writes the current content to the specified file in PDF format. This
* will only work when the OrsonPDF library is found on the classpath.
* Reflection is used to ensure there is no compile-time dependency on
* OrsonPDF (which is non-free software).
*
* @param file the output file ({@code null} not permitted).
* @param w the chart width.
* @param h the chart height.
*
* @deprecated Use ExportUtils.writeAsPDF() directly.
*/
void writeAsPDF(File file, int w, int h) {
ExportUtils.writeAsPDF(drawable, w, h, file);
}
/**
* Writes the current content to the specified file in SVG format. This
* will only work when the JFreeSVG library is found on the classpath.
* Reflection is used to ensure there is no compile-time dependency on
* JFreeSVG.
*
* @param file the output file ({@code null} not permitted).
* @param w the chart width.
* @param h the chart height.
*
* @deprecated Use ExportUtils.writeAsPDF() directly.
*/
void writeAsSVG(File file, int w, int h) {
ExportUtils.writeAsSVG(this.drawable, w, h, file);
}
}