org.graphstream.ui.fx_viewer.util.DefaultCamera Maven / Gradle / Ivy
/*
* This file is part of GraphStream .
*
* GraphStream is a library whose purpose is to handle static or dynamic
* graph, create them from scratch, file or any source and display them.
*
* This program is free software distributed under the terms of two licenses, the
* CeCILL-C license that fits European law, and the GNU Lesser General Public
* License. You can use, modify and/ or redistribute the software under the terms
* of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
* URL or under the terms of the GNU LGPL 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C and LGPL licenses and that you accept their terms.
*/
/**
* @author Antoine Dutot
* @author Guilhelm Savin
* @author Hicham Brahimi
*/
package org.graphstream.ui.fx_viewer.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.graphstream.graph.Edge;
import org.graphstream.graph.Node;
import org.graphstream.ui.geom.Point2;
import org.graphstream.ui.geom.Point3;
import org.graphstream.ui.geom.Vector2;
import org.graphstream.ui.graphicGraph.GraphicEdge;
import org.graphstream.ui.graphicGraph.GraphicElement;
import org.graphstream.ui.graphicGraph.GraphicGraph;
import org.graphstream.ui.graphicGraph.GraphicNode;
import org.graphstream.ui.graphicGraph.GraphicSprite;
import org.graphstream.ui.graphicGraph.stylesheet.Selector;
import org.graphstream.ui.graphicGraph.stylesheet.Style;
import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants;
import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants.Units;
import org.graphstream.ui.graphicGraph.stylesheet.Values;
import org.graphstream.ui.view.camera.Camera;
import org.graphstream.ui.view.util.GraphMetrics;
import org.graphstream.ui.view.util.InteractiveElement;
import javafx.geometry.Point2D ;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.transform.Affine;
import javafx.scene.transform.NonInvertibleTransformException;
/**
* Define how the graph is viewed.
*
*
* The camera is in charge of projecting the graph spaces in graph units (GU)
* into user spaces (often in pixels). It defines the transformation (an affine
* matrix) to passe from the first to the second. It also contains the graph
* metrics, a set of values that give the overall dimensions of the graph in
* graph units, as well as the view port, the area on the screen (or any
* rendering surface) that will receive the results in pixels (or rendering
* units).
*
*
*
* The camera defines a centre at which it always points. It can zoom on the
* graph, pan in any direction and rotate along two axes.
*
*
*
* Knowing the transformation also allows to provide services like "what element
* is not invisible ?" (not in the camera view) or "on what element is the mouse
* cursor actually ?".
*
*/
public class DefaultCamera implements Camera {
/**
* class level logger
*/
private static final Logger logger = Logger.getLogger(DefaultCamera.class.getSimpleName());
// Attribute
/**
* The graph.
*/
protected GraphicGraph graph = null;
/**
* Information on the graph overall dimension and position.
*/
protected GraphMetrics metrics = new GraphMetrics();
/**
* Automatic centring of the view.
*/
protected boolean autoFit = true;
/**
* The camera centre of view.
*/
protected Point3 center = new Point3();
/**
* The camera zoom.
*/
protected double zoom;
/**
* The graph-space -> pixel-space transformation.
*/
protected Affine Tx = new Affine();
/**
* The inverse transform of Tx.
*/
protected Affine xT;
/**
* The previous affine transform.
*/
protected Affine oldTx;
/**
* The rotation angle.
*/
protected double rotation;
/**
* Padding around the graph.
*/
protected Values padding = new Values(Style.Units.GU, 0, 0, 0);
/**
* Which node is visible. This allows to mark invisible nodes to fasten
* visibility tests for nodes, attached sprites and edges.
*/
protected HashSet nodeInvisible = new HashSet();
/**
* The graph view port, if any. The graph view port is a view inside the
* graph space. It allows to compute the view according to a specified area
* of the graph space instead of the graph dimensions.
*/
protected double gviewport[] = null;
protected double gviewportDiagonal = 0;
// Construction
/**
* New camera.
*/
public DefaultCamera(GraphicGraph graph) {
this.graph = graph;
}
// Access
/*
* (non-Javadoc)
*
* @see org.graphstream.ui.swingViewer.util.Camera#getViewCenter()
*/
public Point3 getViewCenter() {
return center;
}
/*
* (non-Javadoc)
*
* @see org.graphstream.ui.swingViewer.util.Camera#setViewCenter(double,
* double, double)
*/
public void setViewCenter(double x, double y, double z) {
setAutoFitView(false);
center.set(x, y, z);
graph.graphChanged = true;
}
public void setViewCenter(double x, double y) {
setViewCenter(x, y, 0);
}
/*
* (non-Javadoc)
*
* @see org.graphstream.ui.swingViewer.util.Camera#getViewPercent()
*/
public double getViewPercent() {
return zoom;
}
/*
* (non-Javadoc)
*
* @see org.graphstream.ui.swingViewer.util.Camera#setViewPercent(double)
*/
public void setViewPercent(double percent) {
setAutoFitView(false);
setZoom(percent);
graph.graphChanged = true;
}
/*
* (non-Javadoc)
*
* @see org.graphstream.ui.swingViewer.util.Camera#getViewRotation()
*/
public double getViewRotation() {
return rotation;
}
/*
* (non-Javadoc)
*
* @see org.graphstream.ui.swingViewer.util.Camera#getMetrics()
*/
public GraphMetrics getMetrics() {
return metrics;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(String.format("Camera :%n"));
builder.append(String.format(" autoFit = %b%n", autoFit));
builder.append(String.format(" center = %s%n", center));
builder.append(String.format(" rotation = %f%n", rotation));
builder.append(String.format(" zoom = %f%n", zoom));
builder.append(String.format(" padding = %s%n", padding));
builder.append(String.format(" metrics = %s%n", metrics));
return builder.toString();
}
/*
* (non-Javadoc)
*
* @see org.graphstream.ui.swingViewer.util.Camera#resetView()
*/
public void resetView() {
setAutoFitView(true);
setViewRotation(0);
}
/*
* (non-Javadoc)
*
* @see org.graphstream.ui.swingViewer.util.Camera#setBounds(double, double,
* double, double, double, double)
*/
public void setBounds(double minx, double miny, double minz, double maxx, double maxy, double maxz) {
metrics.setBounds(minx, miny, minz, maxx, maxy, maxz);
}
/*
* (non-Javadoc)
*
* @see org.graphstream.ui.swingViewer.util.Camera#getGraphDimension()
*/
public double getGraphDimension() {
if (gviewport != null)
return gviewportDiagonal;
return metrics.diagonal;
}
/**
* True if the element should be visible on screen. The method used is to
* transform the center of the element (which is always in graph units)
* using the camera actual transformation to put it in pixel units. Then to
* look in the style sheet the size of the element and to test if its
* enclosing rectangle intersects the view port. For edges, its two nodes
* are used. As a speed-up by default if the camera is in automatic fitting
* mode, all element should be visible, and the test always returns true.
*
* @param element
* The element to test.
* @return True if the element is visible and therefore must be rendered.
*/
public boolean isVisible(GraphicElement element) {
if (autoFit) {
return ((!element.hidden) && (element.style.getVisibilityMode() != StyleConstants.VisibilityMode.HIDDEN));
} else {
switch (element.getSelectorType()) {
case NODE:
return !nodeInvisible.contains(element.getId());
case EDGE:
return isEdgeVisible((GraphicEdge) element);
case SPRITE:
return isSpriteVisible((GraphicSprite) element);
default:
return false;
}
}
}
public boolean isTextVisible(GraphicElement element) {
Values visibility = element.getStyle().getTextVisibility();
switch (element.getStyle().getTextVisibilityMode()) {
case HIDDEN: return false ;
case AT_ZOOM: return (zoom == visibility.get(0)) ;
case UNDER_ZOOM: return (zoom <= visibility.get(0)) ;
case OVER_ZOOM: return (zoom >= visibility.get(0)) ;
case ZOOM_RANGE:
if(visibility.size() > 1)
return (zoom >= visibility.get(0) && zoom <= visibility.get(1)) ;
else return true ;
case ZOOMS: return Arrays.asList(Selector.Type.values()).contains(visibility.get(0)) ;
default: return true ;
}
}
/*
* (non-Javadoc)
*
* @see org.graphstream.ui.swingViewer.util.Camera#inverseTransform(double,
* double)
*/
public Point3 transformPxToGu(double x, double y) {
Point2D p = xT.transform(x, y);
return new Point3(p.getX(), p.getY(), 0);
}
/*
* (non-Javadoc)
*
* @see org.graphstream.ui.swingViewer.util.Camera#transform(double, double)
*/
public Point3 transformGuToPx(double x, double y, double z) {
Point2D p = Tx.transform(x, y);
return new Point3(p.getX(), p.getY(), 0);
}
/**
* Process each node to check if it is in the actual view port, and mark
* invisible nodes. This method allows for fast node, sprite and edge
* visibility checking when drawing. This must be called before each
* rendering (if the view port changed).
*/
public void checkVisibility(GraphicGraph graph) {
double X = metrics.viewport[0];
double Y = metrics.viewport[1];
double W = metrics.viewport[2];
double H = metrics.viewport[3];
nodeInvisible.clear();
for (Node node : graph) {
boolean visible = isNodeIn((GraphicNode) node, X, Y, X + W, Y + H) && (!((GraphicNode) node).hidden)
&& ((GraphicNode) node).positionned;
if (!visible)
nodeInvisible.add(node.getId());
}
}
/**
* Search for the first GraphicElement among those specified. Multiple elements are resolved by priority- {@link InteractiveElement.NODE} > {@link InteractiveElement.EDGE} > {@link InteractiveElement.SPRITE}, (in that order) that contains the
* point at coordinates (x, y).
*
* @param graph
* The graph to search for.
* @param x
* The point abscissa.
* @param y
* The point ordinate.
* @return The first node or sprite at the given coordinates or null if
* nothing found.
*/
@Override
public GraphicElement findGraphicElementAt(GraphicGraph graph, EnumSet types, double x, double y) {
if (types.contains(InteractiveElement.NODE)) {
Optional node = graph
.nodes()
.filter(n ->nodeContains((GraphicElement) n, x, y))
.findFirst();
if(node.isPresent()) {
return (GraphicElement) node.get();
}
}
if (types.contains(InteractiveElement.EDGE)) {
Optional edge = graph
.edges()
.filter(e ->edgeContains((GraphicElement) e, x, y))
.findFirst();
if(edge.isPresent()) {
return (GraphicElement) edge.get();
}
}
if (types.contains(InteractiveElement.SPRITE)) {
Optional sprite = graph
.sprites()
.filter(s ->spriteContains(s, x, y))
.findFirst();
if(sprite.isPresent()) {
return (GraphicElement) sprite.get();
}
}
return null;
}
@Override
public Collection allGraphicElementsIn(GraphicGraph graph, EnumSet types, double x1, double y1, double x2, double y2) {
List elts = new ArrayList();
Stream nodeStream = null;
Stream edgeStream = null;
Stream spriteStream = null;
if (types.contains(InteractiveElement.NODE)) {
nodeStream = graph.nodes()
.filter(n -> isNodeIn((GraphicNode) n, x1, y1, x2, y2));
}
else {
nodeStream = Stream.empty();
}
if (types.contains(InteractiveElement.EDGE)) {
edgeStream = graph.edges()
.filter(e -> isEdgeIn((GraphicEdge) e, x1, y1, x2, y2));
}
else {
edgeStream = Stream.empty();
}
if (types.contains(InteractiveElement.SPRITE)) {
spriteStream = graph.sprites()
.filter(e -> isSpriteIn((GraphicSprite) e, x1, y1, x2, y2));
}
else {
spriteStream = Stream.empty();
}
Stream s = Stream.concat(nodeStream, Stream.concat(edgeStream, spriteStream));
return s.collect(Collectors.toList());
}
/**
* Compute the real position of a sprite according to its eventual
* attachment in graph units.
*
* @param sprite
* The sprite.
* @param pos
* Receiver for the sprite 2D position, can be null.
* @param units
* The units in which the position must be computed (the sprite
* already contains units).
* @return The same instance as the one given by parameter pos or a new one
* if pos was null, containing the computed position in the given
* units.
*/
public Point2D getSpritePosition(GraphicSprite sprite, Point2D pos, Units units) {
if (sprite.isAttachedToNode())
return getSpritePositionNode(sprite, pos, units);
else if (sprite.isAttachedToEdge())
return getSpritePositionEdge(sprite, pos, units);
else
return getSpritePositionFree(sprite, pos, units);
}
public double[] getGraphViewport() {
return gviewport;
}
// Command
public void setGraphViewport(double minx, double miny, double maxx, double maxy) {
setAutoFitView(false);
setViewCenter(minx + (maxx - minx) / 2.0, miny + (maxy - miny) / 2.0);
gviewport = new double[4];
gviewport[0] = minx;
gviewport[1] = miny;
gviewport[2] = maxx;
gviewport[3] = maxy;
gviewportDiagonal = Math.sqrt((maxx - minx) * (maxx - minx) + (maxy - miny) * (maxy - miny));
setZoom(1);
}
public void removeGraphViewport() {
logger.fine("Graph viewport removed for [" + this + "].");
gviewport = null;
resetView();
}
/**
* Set the camera view in the given graphics and backup the previous
* transform of the graphics. Call {@link #popView(GraphicsContext)} to restore
* the saved transform. You can only push one time the view.
*
* @param g2
* The Swing graphics to change.
*/
public void pushView(GraphicGraph graph, GraphicsContext g2) {
if (oldTx == null) {
oldTx = g2.getTransform(); // Backup the Swing transform.
if (autoFit)
autoFitView(g2);
else
userView(g2);
// g2.setTransform(Tx); // Set the final transform, a composition of
// the old Swing transform and our new coordinate system.
} else {
throw new RuntimeException("DefaultCamera.pushView() / popView() wrongly nested");
}
checkVisibility(graph);
}
/**
* Restore the transform that was used before
* {@link #pushView(GraphicGraph, GraphicsContext)} is used.
*
* @param g2
* The Swing graphics to restore.
*/
public void popView(GraphicsContext g2) {
if (oldTx != null) {
g2.setTransform(oldTx); // Set back the old Swing Transform.
oldTx = null;
}
}
/**
* Compute a transformation matrix that pass from graph units (user space)
* to pixel units (device space) so that the whole graph is visible.
*
* @param g2
* The Swing graphics.
*/
protected void autoFitView(GraphicsContext g2) {
double sx, sy;
double tx, ty;
double padXgu = getPaddingXgu() * 2;
double padYgu = getPaddingYgu() * 2;
double padXpx = getPaddingXpx() * 2;
double padYpx = getPaddingYpx() * 2;
sx = (metrics.viewport[2] - padXpx) / (metrics.size.data[0] + padXgu); // Ratio
// along
// X
sy = (metrics.viewport[3] - padYpx) / (metrics.size.data[1] + padYgu); // Ratio
// along
// Y
tx = metrics.lo.x + (metrics.size.data[0] / 2); // Centre of graph in X
ty = metrics.lo.y + (metrics.size.data[1] / 2); // Centre of graph in Y
if (sx <= 0) {
sx = (metrics.viewport[2] - Math.min(metrics.viewport[2] - 1, padXpx)) / (metrics.size.data[0] + padXgu);
}
if (sy <= 0) {
sy = (metrics.viewport[3] - Math.min(metrics.viewport[3] - 1, padYpx)) / (metrics.size.data[1] + padYgu);
}
if (sx > sy) // The least ratio.
sx = sy;
else
sy = sx;
g2.translate(metrics.viewport[2] / 2, metrics.viewport[3] / 2);
if (rotation != 0)
g2.rotate(rotation / (180 / Math.PI));
g2.scale(sx, -sy);
g2.translate(-tx, -ty);
Tx = g2.getTransform();
xT = new Affine(Tx);
try {
xT.invert();
} catch (NonInvertibleTransformException e) {
logger.warning("Cannot inverse gu2px matrix.");
}
zoom = 1;
center.set(tx, ty, 0);
metrics.setRatioPx2Gu(sx);
metrics.loVisible.copy(metrics.lo);
metrics.hiVisible.copy(metrics.hi);
}
/**
* Compute a transformation that pass from graph units (user space) to a
* pixel units (device space) so that the view (zoom and centre) requested
* by the user is produced.
*
* @param g2
* The Swing graphics.
*/
protected void userView(GraphicsContext g2) {
double sx, sy;
double tx, ty;
double padXgu = getPaddingXgu() * 2;
double padYgu = getPaddingYgu() * 2;
double padXpx = getPaddingXpx() * 2;
double padYpx = getPaddingYpx() * 2;
double gw = gviewport != null ? gviewport[2] - gviewport[0] : metrics.size.data[0];
double gh = gviewport != null ? gviewport[3] - gviewport[1] : metrics.size.data[1];
sx = (metrics.viewport[2] - padXpx) / ((gw + padXgu) * zoom);
sy = (metrics.viewport[3] - padYpx) / ((gh + padYgu) * zoom);
tx = center.x;
ty = center.y;
if (sx > sy) // The least ratio.
sx = sy;
else
sy = sx;
g2.translate((metrics.viewport[2] / 2), (metrics.viewport[3] / 2));
if (rotation != 0)
g2.rotate(rotation / (180 / Math.PI));
g2.scale(sx, -sy);
g2.translate(-tx, -ty);
Tx = g2.getTransform();
xT = new Affine(Tx);
try {
xT.invert();
} catch (NonInvertibleTransformException e) {
logger.log(Level.WARNING, "Cannot inverse gu2px matrix.", e);
}
metrics.setRatioPx2Gu(sx);
double w2 = (metrics.viewport[2] / sx) / 2;
double h2 = (metrics.viewport[3] / sx) / 2;
metrics.loVisible.set(center.x - w2, center.y - h2);
metrics.hiVisible.set(center.x + w2, center.y + h2);
}
/**
* Enable or disable automatic adjustment of the view to see the entire
* graph.
*
* @param on
* If true, automatic adjustment is enabled.
*/
public void setAutoFitView(boolean on) {
if (autoFit && (!on)) {
// We go from autoFit to user view, ensure the current centre is at
// the
// middle of the graph, and the zoom is at one.
zoom = 1;
center.set(metrics.lo.x + (metrics.size.data[0] / 2), metrics.lo.y + (metrics.size.data[1] / 2), 0);
}
autoFit = on;
}
/**
* Set the zoom (or percent of the graph visible), 1 means the graph is
* fully visible.
*
* @param z
* The zoom.
*/
public void setZoom(double z) {
zoom = z;
graph.graphChanged = true;
}
/**
* Set the rotation angle around the centre.
*
* @param theta
* The rotation angle in degrees.
*/
public void setViewRotation(double theta) {
rotation = theta;
graph.graphChanged = true;
}
/**
* Set the output view port size in pixels.
*
* @param viewportWidth
* The width in pixels of the view port.
* @param viewportHeight
* The width in pixels of the view port.
*/
public void setViewport(double viewportX, double viewportY, double viewportWidth, double viewportHeight) {
metrics.setViewport(viewportX, viewportY, viewportWidth, viewportHeight);
}
/**
* Set the graph padding.
*
* @param graph
* The graphic graph.
*/
public void setPadding(GraphicGraph graph) {
padding.copy(graph.getStyle().getPadding());
}
// Utility
protected double getPaddingXgu() {
if (padding.units == Style.Units.GU && padding.size() > 0)
return padding.get(0);
return 0;
}
protected double getPaddingYgu() {
if (padding.units == Style.Units.GU && padding.size() > 1)
return padding.get(1);
return getPaddingXgu();
}
protected double getPaddingXpx() {
if (padding.units == Style.Units.PX && padding.size() > 0)
return padding.get(0);
return 0;
}
protected double getPaddingYpx() {
if (padding.units == Style.Units.PX && padding.size() > 1)
return padding.get(1);
return getPaddingXpx();
}
/**
* Check if a sprite is visible in the current view port.
*
* @param sprite
* The sprite to check.
* @return True if visible.
*/
protected boolean isSpriteVisible(GraphicSprite sprite) {
return isSpriteIn(sprite, metrics.viewport[0], metrics.viewport[1], metrics.viewport[0] + metrics.viewport[2],
metrics.viewport[1] + metrics.viewport[3]);
}
/**
* Check if an edge is visible in the current view port.
*
* @param edge
* The edge to check.
* @return True if visible.
*/
protected boolean isEdgeVisible(GraphicEdge edge) {
GraphicNode node0 = (GraphicNode) edge.getNode0();
GraphicNode node1 = (GraphicNode) edge.getNode1();
if (edge.hidden)
return false;
if ((!node1.positionned) || (!node0.positionned))
return false;
boolean node0Invis = nodeInvisible.contains(node0.getId());
boolean node1Invis = nodeInvisible.contains(node1.getId());
return !(node0Invis && node1Invis);
}
/**
* Is the given node visible in the given area.
*
* @param node
* The node to check.
* @param X1
* The min abscissa of the area.
* @param Y1
* The min ordinate of the area.
* @param X2
* The max abscissa of the area.
* @param Y2
* The max ordinate of the area.
* @return True if the node lies in the given area.
*/
protected boolean isNodeIn(GraphicNode node, double X1, double Y1, double X2, double Y2) {
Values size = node.getStyle().getSize();
double w2 = metrics.lengthToPx(size, 0) / 2;
double h2 = size.size() > 1 ? metrics.lengthToPx(size, 1) / 2 : w2;
Point2D src = new Point2D(node.getX(), node.getY());
boolean vis = true;
Point2D dst = Tx.transform(src.getX(), src.getY());
double x1 = dst.getX() - w2;
double x2 = dst.getX() + w2;
double y1 = dst.getY() - h2;
double y2 = dst.getY() + h2;
if (x2 < X1)
vis = false;
else if (y2 < Y1)
vis = false;
else if (x1 > X2)
vis = false;
else if (y1 > Y2)
vis = false;
return vis;
}
/**
* Is the given sprite visible in the given area.
*
* @param edge
* The edge to check.
* @param X1
* The min abscissa of the area.
* @param Y1
* The min ordinate of the area.
* @param X2
* The max abscissa of the area.
* @param Y2
* The max ordinate of the area.
* @return True if the edge lies in the given area.
*/
protected boolean isEdgeIn(GraphicEdge edge, double X1, double Y1, double X2, double Y2) {
Values size = edge.getStyle().getSize();
double w2 = metrics.lengthToPx(size, 0) / 2;
double h2 = size.size() > 1 ? metrics.lengthToPx(size, 1) / 2 : w2;
Point2D src = new Point2D(edge.getX(), edge.getY());
boolean vis = true;
Point2D dst = Tx.transform(src.getX(), src.getY());
double x1 = dst.getX() - w2;
double x2 = dst.getX() + w2;
double y1 = dst.getY() - h2;
double y2 = dst.getY() + h2;
if (x2 < X1)
vis = false;
else if (y2 < Y1)
vis = false;
else if (x1 > X2)
vis = false;
else if (y1 > Y2)
vis = false;
return vis;
}
/**
* Is the given sprite visible in the given area.
*
* @param sprite
* The sprite to check.
* @param X1
* The min abscissa of the area.
* @param Y1
* The min ordinate of the area.
* @param X2
* The max abscissa of the area.
* @param Y2
* The max ordinate of the area.
* @return True if the node lies in the given area.
*/
protected boolean isSpriteIn(GraphicSprite sprite, double X1, double Y1, double X2, double Y2) {
if (sprite.isAttachedToNode() && nodeInvisible.contains(sprite.getNodeAttachment().getId())) {
return false;
} else if (sprite.isAttachedToEdge() && !isEdgeVisible(sprite.getEdgeAttachment())) {
return false;
} else {
Values size = sprite.getStyle().getSize();
double w2 = metrics.lengthToPx(size, 0) / 2;
double h2 = size.size() > 1 ? metrics.lengthToPx(size, 1) / 2 : w2;
Point2D src = spritePositionPx(sprite);// new Point2D.Double(
// sprite.getX(),
// sprite.getY() );
// Tx.transform( src, src );
double x1 = src.getX() - w2;
double x2 = src.getX() + w2;
double y1 = src.getY() - h2;
double y2 = src.getY() + h2;
if (x2 < X1)
return false;
if (y2 < Y1)
return false;
if (x1 > X2)
return false;
if (y1 > Y2)
return false;
return true;
}
}
protected Point2D spritePositionPx(GraphicSprite sprite) {
return getSpritePosition(sprite, null, Units.PX);
// if( sprite.getUnits() == Units.PX )
// {
// return new Point2D.Double( sprite.getX(), sprite.getY() );
// }
// else if( sprite.getUnits() == Units.GU )
// {
// Point2D.Double pos = new Point2D.Double( sprite.getX(), sprite.getY()
// );
// return (Point2D.Double) Tx.transform( pos, pos );
// }
// else// if( sprite.getUnits() == Units.PERCENTS )
// {
// return new Point2D.Double(
// (sprite.getX()/100f)*metrics.viewport.data[0],
// (sprite.getY()/100f)*metrics.viewport.data[1] );
// }
}
/**
* Check if a node contains the given point (x,y).
*
* @param elt
* The node.
* @param x
* The point abscissa.
* @param y
* The point ordinate.
* @return True if (x,y) is in the given element.
*/
protected boolean nodeContains(GraphicElement elt, double x, double y) {
Values size = elt.getStyle().getSize();
double w2 = metrics.lengthToPx(size, 0) / 2;
double h2 = size.size() > 1 ? metrics.lengthToPx(size, 1) / 2 : w2;
Point2D src = new Point2D(elt.getX(), elt.getY());
Point2D dst = Tx.transform(src.getX(), src.getY());
double dstX = dst.getX() - metrics.viewport[0];
double dstY = dst.getY() - metrics.viewport[1];
dst = new Point2D(dstX, dstY);
double x1 = dst.getX() - w2;
double x2 = dst.getX() + w2;
double y1 = dst.getY() - h2;
double y2 = dst.getY() + h2;
// System.out.println(elt+" x="+x1+" "+x2+" y="+y1+" "+y2);
if (x < x1)
return false;
if (y < y1)
return false;
if (x > x2)
return false;
if (y > y2)
return false;
return true;
}
/**
* Check if an edge contains the given point (x,y).
*
* @param elt
* The edge.
* @param x
* The point abscissa.
* @param y
* The point ordinate.
* @return True if (x,y) is in the given element.
*/
protected boolean edgeContains(GraphicElement elt, double x, double y) {
Values size = elt.getStyle().getSize();
double w2 = metrics.lengthToPx(size, 0) / 2;
double h2 = size.size() > 1 ? metrics.lengthToPx(size, 1) / 2 : w2;
Point2D src = new Point2D(elt.getX(), elt.getY());
Point2D dst = Tx.transform(src.getX(), src.getY());
double dstX = dst.getX() - metrics.viewport[0];
double dstY = dst.getY() - metrics.viewport[1];
dst = new Point2D(dstX, dstY);
double x1 = dst.getX() - w2;
double x2 = dst.getX() + w2;
double y1 = dst.getY() - h2;
double y2 = dst.getY() + h2;
if (x < x1)
return false;
if (y < y1)
return false;
if (x > x2)
return false;
if (y > y2)
return false;
return true;
}
/**
* Check if a sprite contains the given point (x,y).
*
* @param elt
* The sprite.
* @param x
* The point abscissa.
* @param y
* The point ordinate.
* @return True if (x,y) is in the given element.
*/
protected boolean spriteContains(GraphicElement elt, double x, double y) {
Values size = elt.getStyle().getSize();
double w2 = metrics.lengthToPx(size, 0) / 2;
double h2 = size.size() > 1 ? metrics.lengthToPx(size, 1) / 2 : w2;
Point2D dst = spritePositionPx((GraphicSprite) elt); // new
// Point2D.Double(
// elt.getX(),
// elt.getY()
// );
// Point2D.Double dst = new Point2D.Double();
// Tx.transform( src, dst );
double dstX = dst.getX() - metrics.viewport[0];
double dstY = dst.getY() - metrics.viewport[1];
dst = new Point2D(dstX, dstY);
double x1 = dst.getX() - w2;
double x2 = dst.getX() + w2;
double y1 = dst.getY() - h2;
double y2 = dst.getY() + h2;
if (x < x1)
return false;
if (y < y1)
return false;
if (x > x2)
return false;
if (y > y2)
return false;
return true;
}
/**
* Compute the position of a sprite if it is not attached.
*
* @param sprite
* The sprite.
* @param pos
* Where to stored the computed position, if null, the position
* is created.
* @param units
* The units the computed position must be given into.
* @return The same instance as pos, or a new one if pos was null.
*/
protected Point2D getSpritePositionFree(GraphicSprite sprite, Point2D pos, Units units) {
if (sprite.getUnits() == units) {
pos = new Point2D(sprite.getX(), sprite.getY());
} else if (units == Units.GU && sprite.getUnits() == Units.PX) {
pos = new Point2D(sprite.getX(), sprite.getY());
pos = xT.transform(pos.getX(), pos.getY());
} else if (units == Units.PX && sprite.getUnits() == Units.GU) {
pos = new Point2D(sprite.getX(), sprite.getY());
pos = Tx.transform(pos.getX(), pos.getY());
} else if (units == Units.GU && sprite.getUnits() == Units.PERCENTS) {
double x = metrics.lo.x + (sprite.getX() / 100f) * metrics.graphWidthGU();
double y = metrics.lo.y + (sprite.getY() / 100f) * metrics.graphHeightGU();
pos = new Point2D(x, y);
} else if (units == Units.PX && sprite.getUnits() == Units.PERCENTS) {
double x = (sprite.getX() / 100f) * metrics.viewport[2];
double y = (sprite.getY() / 100f) * metrics.viewport[3];
pos = new Point2D(x, y);
} else {
throw new RuntimeException("Unhandled yet sprite positioning.");
}
return pos;
}
/**
* Compute the position of a sprite if attached to a node.
*
* @param sprite
* The sprite.
* @param pos
* Where to stored the computed position, if null, the position
* is created.
* @param units
* The units the computed position must be given into.
* @return The same instance as pos, or a new one if pos was null.
*/
protected Point2D getSpritePositionNode(GraphicSprite sprite, Point2D pos, Units units) {
GraphicNode node = sprite.getNodeAttachment();
double x = node.x + sprite.getX();
double y = node.y + sprite.getY();
pos = new Point2D(x, y);
if (units == Units.PX)
pos = Tx.transform(pos.getX(), pos.getY());
return pos;
}
/**
* Compute the position of a sprite if attached to an edge.
*
* @param sprite
* The sprite.
* @param pos
* Where to stored the computed position, if null, the position
* is created.
* @param units
* The units the computed position must be given into.
* @return The same instance as pos, or a new one if pos was null.
*/
protected Point2D getSpritePositionEdge(GraphicSprite sprite, Point2D pos, Units units) {
GraphicEdge edge = sprite.getEdgeAttachment();
if (edge.isCurve()) {
double ctrl[] = edge.getControlPoints();
Point2 p0 = new Point2(edge.from.getX(), edge.from.getY());
Point2 p1 = new Point2(ctrl[0], ctrl[1]);
Point2 p2 = new Point2(ctrl[1], ctrl[2]);
Point2 p3 = new Point2(edge.to.getX(), edge.to.getY());
Vector2 perp = CubicCurve.perpendicular(p0, p1, p2, p3, sprite.getX());
double y = metrics.lengthToGu(sprite.getY(), sprite.getUnits());
perp.normalize();
perp.scalarMult(y);
double X = CubicCurve.eval(p0.x, p1.x, p2.x, p3.x, sprite.getX()) - perp.data[0];
double Y = CubicCurve.eval(p0.y, p1.y, p2.y, p3.y, sprite.getX()) - perp.data[1];
pos = new Point2D(X, Y);
} else {
double x = ((GraphicNode) edge.getSourceNode()).x;
double y = ((GraphicNode) edge.getSourceNode()).y;
double dx = ((GraphicNode) edge.getTargetNode()).x - x;
double dy = ((GraphicNode) edge.getTargetNode()).y - y;
double d = sprite.getX(); // Percent on the edge.
double o = metrics.lengthToGu(sprite.getY(), sprite.getUnits());
// Offset from the position given by percent, perpendicular to the
// edge.
d = d > 1 ? 1 : d;
d = d < 0 ? 0 : d;
x += dx * d;
y += dy * d;
d = (double) Math.sqrt(dx * dx + dy * dy);
dx /= d;
dy /= d;
x += -dy * o;
y += dx * o;
pos = new Point2D(x, y);
if (units == Units.PX) {
pos = Tx.transform(pos.getX(), pos.getY());
}
}
return pos;
}
}