All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy