![JAR search and dependency download from the Maven repository](/logo.png)
org.graphstream.ui.j2dviewer.Camera.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gs-ui Show documentation
Show all versions of gs-ui Show documentation
The GraphStream library. With GraphStream you deal with
graphs. Static and Dynamic. You create them from scratch, from a file
or any source. You display and render them.
The newest version!
/*
* Copyright 2006 - 2015
* Stefan Balev
* Julien Baudry
* Antoine Dutot
* Yoann Pigné
* Guilhelm Savin
*
* 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.
*/
package org.graphstream.ui.j2dviewer
import org.graphstream.ui.geom.Point3
import java.util.ArrayList
import scala.collection.mutable.HashSet
import scala.collection.JavaConversions._
import scala.math._
import org.graphstream.graph.Node
import org.graphstream.ui.graphicGraph.stylesheet.Selector.Type._
import org.graphstream.ui.graphicGraph.{GraphicEdge, GraphicElement, GraphicGraph, GraphicNode, GraphicSprite}
import org.graphstream.ui.graphicGraph.stylesheet.{Style, Values}
import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants._
import org.graphstream.ui.util.CubicCurve
import org.graphstream.ui.swingViewer.util.GraphMetrics
import org.graphstream.ui.geom.Point2
import org.graphstream.ui.geom.Vector2
import org.graphstream.ui.j2dviewer.renderer.{Skeleton, AreaSkeleton, ConnectorSkeleton}
import scala.math._
import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants
/**
* Define a view of the graph or a part of the graph.
*
* The camera can be seen as an element in charge of projecting the graph elements in graph units
* (GU) into rendering space units, often in pixels. It defines the transformation, an affine
* matrix, to pass from the first to the second (in fact its the back-end that does it).
*
* It also contains the graph metrics. This is 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 any rendering units). The two mains methods
* for this operation are [[Camera.pushView(Graphics2D,GraphicGraph)]] and [[Camera.popView()]].
*
* The user of the camera must set both the view port and the graph bounds in order for the
* camera to correctly project the graph view (the Renderer does that before using the Camera,
* at each frame). The camera model is as follows: the camera defines a center at which it
* always points. It can zoom on the graph (as if the camera angle of view was changing), pan in any
* direction by moving its center of view and rotate along the axe going from the center to the
* camera position (camera can rotate around two axes in 3D, but this is a 2D camera).
*
* There are two modes:
* - an "auto-fit" mode where the camera always show the whole graph even if it changes in size, by
* automatically changing the center and zoom values,
* - and a "user" mode where the camera center (looked-at point), zoom and panning are specified and
* will not be modified in the bounds of the graph change.
*
* The camera is also able to answer questions like: "what element is visible actually?", or "on
* what element is the mouse cursor actually?".
*
* The camera is also able to compute sprite positions according to their attachment, as well as
* maintaining a list of all elements out of the view, so that it is not needed to render them.
*/
class Camera(protected val graph:GraphicGraph) extends org.graphstream.ui.view.Camera {
// Attribute
/** Information on the graph overall dimension and position. */
val metrics = new org.graphstream.ui.swingViewer.util.GraphMetrics
/** Automatic centering of the view. */
protected var autoFit = true
/** The camera center of view. */
protected val center = new Point3
/** The camera zoom. */
protected var zoom:Double = 1
/** The rotation angle (along an axis perpendicular to the view). */
protected var rotation:Double = 0
/** Padding around the graph. */
protected var padding = new Values(Units.GU, 0, 0, 0);
/** The rendering back-end. */
protected var bck:Backend = null
/** Which node is visible. This allows to mark invisible nodes to fasten visibility tests for
* nodes, attached sprites and edges. The visibility test is heavy, and we often need to test
* for nodes visibility. This allows to do it only once per rendering step. Hence the storage
* of the invisible nodes here. */
protected val nodeInvisible = new HashSet[String]
/** 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 var gviewport:Array[Double] = null
// Access
def getMetrics() = metrics
/** The view center (a point in graph units). */
def viewCenter:Point3 = center
def getViewCenter:Point3 = viewCenter
/** The visible portion of the graph.
* @return A real for which value 1 means the graph is fully visible and uses the whole
* view port. */
def viewPercent:Double = zoom
def getViewPercent:Double = viewPercent
/** The rotation angle in degrees.
* @return The rotation angle in degrees. */
def viewRotation:Double = rotation
def getViewRotation:Double = viewRotation
def getGraphDimension():Double = metrics.getDiagonal
override def toString():String = {
var builder = new StringBuilder( "Camera :%n".format() )
builder.append( " autoFit = %b%n".format( autoFit ) )
builder.append( " center = %s%n".format( center ) )
builder.append( " rotation = %f%n".format( rotation ) )
builder.append( " zoom = %f%n".format( zoom ) )
builder.append( " padding = %s%n".format( padding ) )
builder.append( " metrics = %s%n".format( metrics ) )
builder.toString
}
/** True if the element is be visible by the camera view (not out of view). 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. If auto fitting is on, all elements are necessarily visible, this
* method takes this in consideration.
* @param element The element to test.
* @return True if the element is visible and therefore must be rendered. */
def isVisible(element:GraphicElement):Boolean = {
if(autoFit) {
((! element.hidden) && (element.style.getVisibilityMode() != StyleConstants.VisibilityMode.HIDDEN))
} else {
if(styleVisible(element)) element.getSelectorType match {
case NODE => ! nodeInvisible.contains(element.getId)
case EDGE => isEdgeVisible(element.asInstanceOf[GraphicEdge])
case SPRITE => isSpriteVisible(element.asInstanceOf[GraphicSprite])
case _ => false
} else false
}
}
/** Return the given point in pixels converted in graph units (GU) using the inverse
* transformation of the current projection matrix. The inverse matrix is computed only
* once each time a new projection matrix is created.
* @param x The source point abscissa in pixels.
* @param y The source point ordinate in pixels.
* @return The resulting points in graph units. */
def transformPxToGu(x:Double, y:Double):Point3 = bck.inverseTransform(x, y, 0)
/** Transform a point in graph units into pixels.
* @return The transformed point. */
def transformGuToPx(x:Double, y:Double, z:Double):Point3 = bck.transform(x, y, 0)
/** Search for the first node or 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. */
def findNodeOrSpriteAt(graph:GraphicGraph, x:Double, y:Double):GraphicElement = {
var ge:GraphicElement = null
graph.getEachNode.foreach { n =>
val node = n.asInstanceOf[GraphicNode]
if( nodeContains( node, x, y ) )
ge = node
}
graph.spriteSet.foreach { sprite =>
if( spriteContains( sprite, x, y ) )
ge = sprite
}
ge
}
/** Search for all the nodes and sprites contained inside the rectangle (x1,y1)-(x2,y2).
* @param graph The graph to search for.
* @param x1 The rectangle lowest point abscissa.
* @param y1 The rectangle lowest point ordinate.
* @param x2 The rectangle highest point abscissa.
* @param y2 The rectangle highest point ordinate.
* @return The set of sprites and nodes in the given rectangle. */
def allNodesOrSpritesIn(graph:GraphicGraph, x1:Double, y1:Double, x2:Double, y2:Double):ArrayList[GraphicElement] = {
val elts = new ArrayList[GraphicElement]
graph.getEachNode.foreach { node:Node =>
if(isNodeIn(node.asInstanceOf[GraphicNode], x1, y1, x2, y2))
elts.add( node.asInstanceOf[GraphicNode])
}
graph.spriteSet.foreach { sprite:GraphicSprite =>
if(isSpriteIn(sprite, x1, y1, x2, y2))
elts.add(sprite)
}
elts
}
/** 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. */
def getSpritePosition(sprite:GraphicSprite, pos:Point3, units:Units):Point3 = {
if( sprite.isAttachedToNode() ) getSpritePositionNode(sprite, pos, units)
else if( sprite.isAttachedToEdge() ) getSpritePositionEdge(sprite, pos, units)
else getSpritePositionFree(sprite, pos, units)
}
def graphViewport = gviewport
// Command
def setBackend(backend:Backend) { bck = backend }
def setGraphViewport(minx:Double, miny:Double, maxx:Double, maxy:Double) {
gviewport = Array( minx, miny, maxx, maxy )
}
def removeGraphViewport() { gviewport = null }
def resetView() {
setAutoFitView(true)
setViewRotation(0)
}
/** Set the camera view in the given graphics and backup the previous transform of the graphics.
* Call {@link #popView(Graphics2D)} to restore the saved transform. You can only push one time
* the view.
* @param graph The graphic graph (used to check element visibility). */
def pushView(graph:GraphicGraph) {
bck.pushTransform
setPadding(graph)
if(autoFit)
autoFitView
else userView
checkVisibility(graph)
}
/** Restore the transform that was used before {@link #pushView(Graphics2D)} is used. */
def popView() { bck.popTransform }
/** Compute a transformation matrix that pass from graph units (user space) to pixel units
* (device space) so that the whole graph is visible.
* @return The transformation modified. */
protected def autoFitView() {
var sx = 0.0; var sy = 0.0
var tx = 0.0; var ty = 0.0
val padXgu = paddingXgu * 2
val padYgu = paddingYgu * 2
var padXpx = paddingXpx * 2
var padYpx = paddingYpx * 2
if(padXpx > metrics.viewport(2)) padXpx = metrics.viewport(2) / 10.0
if(padYpx > metrics.viewport(3)) padYpx = metrics.viewport(3) / 10.0
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) // Center of graph in X
ty = metrics.lo.y + (metrics.size.data(1) / 2) // Center of graph in Y
if(sx > sy) // The least ratio.
sx = sy
else sy = sx
bck.beginTransform
bck.setIdentity
bck.translate(metrics.viewport(2)/2,
metrics.viewport(3)/2, 0) // 4. Place the whole result at the center of the view port.
if(rotation != 0)
bck.rotate(rotation/(180.0/Pi), 0, 0, 1) // 3. Eventually apply a Z axis rotation.
bck.scale(sx, -sy, 0) // 2. Scale the graph to pixels. Scale -y since we reverse the view (top-left to bottom-left).
bck.translate(-tx, -ty, 0) // 1. Move the graph so that its real center is at (0,0).
bck.endTransform
zoom = 1
center.set(tx, ty, 0)
metrics.ratioPx2Gu = 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 center) requested by the user is produced.
* @return The transformation modified. */
protected def userView() {
var sx = 0.0; var sy = 0.0
var tx = 0.0; var ty = 0.0
val padXgu = paddingXgu * 2
val padYgu = paddingYgu * 2
var padXpx = paddingXpx * 2
var padYpx = paddingYpx * 2
val gw = if(gviewport ne null) gviewport(2)-gviewport(0) else metrics.size.data(0)
val gh = if(gviewport ne null) gviewport(3)-gviewport(1) else metrics.size.data(1)
if(padXpx > metrics.viewport(2)) padXpx = metrics.viewport(2) / 10.0
if(padYpx > metrics.viewport(3)) padYpx = metrics.viewport(3) / 10.0
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;
bck.beginTransform
bck.setIdentity
bck.translate(metrics.viewport(2)/2,
metrics.viewport(3)/2, 0) // 4. Place the whole result at the center of the view port.
if(rotation != 0)
bck.rotate(rotation/(180.0/Pi), 0, 0, 1) // 3. Eventually apply a rotation.
bck.scale(sx, -sy, 0) // 2. Scale the graph to pixels. Scale -y since we reverse the view (top-left to bottom-left).
bck.translate(-tx, -ty, 0) // 1. Move the graph so that the give center is at (0,0).
bck.endTransform
metrics.ratioPx2Gu = sx
val w2 = (metrics.viewport(2) / sx) / 2f
val h2 = (metrics.viewport(3) / sx) / 2f
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. */
def setAutoFitView(on:Boolean) {
if(autoFit && (! on)) {
// We go from autoFit to user view, ensure the current center 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 center of the view (the looked at point). As the viewer is only 2D, the z value is
* not required.
* @param x The new position abscissa.
* @param y The new position ordinate. */
def setViewCenter(x:Double, y:Double, z:Double) {
setAutoFitView(false)
center.set(x, y, z)
graph.graphChanged = true
}
/** Set the zoom (or percent of the graph visible), 1 means the graph is fully visible.
* @param z The zoom. */
def viewPercent_=(z:Double) { zoom = z; graph.graphChanged = true }
def setViewPercent(z:Double) {
setAutoFitView(false)
zoom = z
graph.graphChanged = true
}
/** Set the rotation angle around the center.
* @param angle The rotation angle in degrees. */
def viewRotation_=(angle:Double) { rotation = angle; graph.graphChanged = true }
def setViewRotation(angle:Double) { rotation = angle; 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. */
def setViewport(x:Double, y:Double, viewportWidth:Double, viewportHeight:Double) { metrics.setViewport(x, y, viewportWidth, viewportHeight) }
/** Set the graphic graph bounds (the lowest and highest points).
* @param minx Lowest abscissa.
* @param miny Lowest ordinate.
* @param minz Lowest depth.
* @param maxx Highest abscissa.
* @param maxy Highest ordinate.
* @param maxz Highest depth. */
def setBounds(minx:Double, miny:Double, minz:Double, maxx:Double, maxy:Double, maxz:Double) = metrics.setBounds(minx, miny, minz, maxx, maxy, maxz)
/** Set the graphic graph bounds from the graphic graph. */
def setBounds(graph:GraphicGraph) {
setBounds(graph.getMinPos.x, graph.getMinPos.y, 0, graph.getMaxPos.x, graph.getMaxPos.y, 0)
}
// Utility
/** Set the graph padding. Called in pushView.
* @param graph The graphic graph. */
protected def setPadding(graph:GraphicGraph) { padding.copy(graph.getStyle.getPadding) }
/** 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). Called in pushView.
* A node is not visible if it is out of the view port, if it is deliberately hidden (its hidden
* flag is set) or if it has not yet been positioned (has not yet received a (x,y,z) position).
* If the auto fitting feature is activate the whole graph is always visible. */
protected def checkVisibility(graph:GraphicGraph) {
nodeInvisible.clear
if(! autoFit) {
// If autoFit is on, we know the whole graph is visible anyway.
val X:Double = metrics.viewport(0)
val Y:Double = metrics.viewport(1)
val W:Double = metrics.viewport(2)
val H:Double = metrics.viewport(3)
graph.getEachNode.foreach { node:Node =>
val n:GraphicNode = node.asInstanceOf[GraphicNode]
val visible = isNodeIn(n, X, Y, X+W, Y+H) && (!n.hidden) && n.positionned;
// val visible = isNodeIn(n, 0, 0, W, H) && (!n.hidden) && n.positionned;
if(! visible) {
nodeInvisible += node.getId
}
}
}
}
protected def paddingXgu:Double = if(padding.units == Units.GU && padding.size > 0) padding.get( 0 ) else 0
protected def paddingYgu:Double = if(padding.units == Units.GU && padding.size > 1) padding.get( 1 ) else paddingXgu
protected def paddingXpx:Double = if(padding.units == Units.PX && padding.size > 0) padding.get( 0 ) else 0
protected def paddingYpx:Double = if(padding.units == Units.PX && padding.size > 1) padding.get( 1 ) else paddingXpx
/** Check if a sprite is visible in the current view port.
* @param sprite The sprite to check.
* @return True if visible. */
protected def isSpriteVisible(sprite:GraphicSprite):Boolean = isSpriteIn(sprite, 0, 0, metrics.viewport(2), 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 def isEdgeVisible(edge:GraphicEdge):Boolean = {
if((!edge.getNode0[GraphicNode].positionned)
|| (!edge.getNode1[GraphicNode].positionned)) {
false
} else if(edge.hidden) {
false
} else {
val node0Invis = nodeInvisible.contains(edge.getNode0[Node].getId)
val node1Invis = nodeInvisible.contains(edge.getNode1[Node].getId)
! (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 def isNodeIn(node:GraphicNode, X1:Double, Y1:Double, X2:Double, Y2:Double):Boolean = {
val size = getNodeOrSpriteSize(node)//node.getStyle.getSize
val w2 = metrics.lengthToPx(size, 0) / 2
val h2 = if(size.size > 1) metrics.lengthToPx(size, 1)/2 else w2
val src = new Point3(node.getX, node.getY, 0)
bck.transform(src)
// Tx.transform( src, src )
val x1 = src.x - w2
val x2 = src.x + w2
val y1 = src.y - h2
val y2 = src.y + h2
if( x2 < X1) false
else if(y2 < Y1) false
else if(x1 > X2) false
else if(y1 > Y2) false
else true
}
/** 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 def isSpriteIn( sprite:GraphicSprite, X1:Double, Y1:Double, X2:Double, Y2:Double ):Boolean = {
if( sprite.isAttachedToNode && ( nodeInvisible.contains( sprite.getNodeAttachment.getId ) ) ) {
false
} else if( sprite.isAttachedToEdge && ! isEdgeVisible( sprite.getEdgeAttachment ) ) {
false
} else {
val size = sprite.getStyle.getSize
val w2 = metrics.lengthToPx( size, 0 ) / 2
val h2 = if( size.size > 1 ) metrics.lengthToPx( size, 1 )/2 else w2
val src = spritePositionPx( sprite )
val x1 = src.x - w2
val x2 = src.x + w2
val y1 = src.y - h2
val y2 = src.y + h2
if( x2 < X1 ) false
else if( y2 < Y1 ) false
else if( x1 > X2 ) false
else if( y1 > Y2 ) false
else true
}
}
protected def spritePositionPx(sprite:GraphicSprite):Point3 = getSpritePosition(sprite, new Point3, Units.PX)
/** 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 def nodeContains(elt:GraphicElement, x:Double, y:Double):Boolean = {
val size = getNodeOrSpriteSize(elt) // elt.getStyle.getSize // TODO use nodeinfo
val w2 = metrics.lengthToPx(size, 0) / 2
val h2 = if(size.size() > 1) metrics.lengthToPx(size, 1)/2 else w2
val dst = bck.transform(elt.getX, elt.getY, 0)
//println(s"nodeContains(${elt.getId}, ${x}, ${y}) -> (${dst.x}, ${dst.y})")
val x1 = (dst.x) - w2
val x2 = (dst.x) + w2
val y1 = (dst.y) - h2
val y2 = (dst.y) + h2
if( x < x1) false
else if(y < y1) false
else if(x > x2) false
else if(y > y2) false
else 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 def spriteContains(elt:GraphicElement, x:Double, y:Double):Boolean = {
val sprite = elt.asInstanceOf[GraphicSprite]
val size = getNodeOrSpriteSize(elt) //sprite.getStyle.getSize // TODO use nodeinfo
val w2 = metrics.lengthToPx(size, 0) / 2
val h2 = if(size.size() > 1) metrics.lengthToPx(size, 1)/2 else w2
val dst = spritePositionPx(sprite) // new Point2D.Double( sprite.getX(), sprite.getY() )
// val dst = new Point2D.Double
//
// Tx.transform( src, dst )
val x1 = dst.x - w2
val x2 = dst.x + w2
val y1 = dst.y - h2
val y2 = dst.y + h2
if( x < x1) false
else if(y < y1) false
else if(x > x2) false
else if(y > y2) false
else true
}
protected def getNodeOrSpriteSize(elt:GraphicElement):Values = {
val info = elt.getAttribute(Skeleton.attributeName).asInstanceOf[AreaSkeleton]
if(info ne null) {
new Values(Units.GU, info.theSize.x, info.theSize.y)
} else {
elt.getStyle.getSize
}
}
protected def styleVisible(element:GraphicElement):Boolean = {
val visibility = element.getStyle.getVisibility
element.getStyle.getVisibilityMode match {
case VisibilityMode.HIDDEN => false
case VisibilityMode.AT_ZOOM => (zoom == visibility.get(0))
case VisibilityMode.UNDER_ZOOM => (zoom <= visibility.get(0))
case VisibilityMode.OVER_ZOOM => (zoom >= visibility.get(0))
case VisibilityMode.ZOOM_RANGE => if(visibility.size > 1) (zoom >= visibility.get(0) && zoom <= visibility.get(1)) else true
case VisibilityMode.ZOOMS => values.contains(visibility.get(0))
case _ => true
}
}
def isTextVisible(element:GraphicElement):Boolean = {
val visibility = element.getStyle.getTextVisibility
element.getStyle.getTextVisibilityMode match {
case TextVisibilityMode.HIDDEN => false
case TextVisibilityMode.AT_ZOOM => (zoom == visibility.get(0))
case TextVisibilityMode.UNDER_ZOOM => (zoom <= visibility.get(0))
case TextVisibilityMode.OVER_ZOOM => (zoom >= visibility.get(0))
case TextVisibilityMode.ZOOM_RANGE => if(visibility.size > 1) (zoom >= visibility.get(0) && zoom <= visibility.get(1)) else true
case TextVisibilityMode.ZOOMS => values.contains(visibility.get(0))
case _ => true
}
}
def getNodeOrSpritePositionGU(elt:GraphicElement, pos:Point3):Point3 = {
var p = pos
if(p eq null) p = new Point3
elt match {
case node:GraphicNode => { p.x = node.getX; p.y = node.getY; p }
case sprite:GraphicSprite => { getSpritePosition(sprite, p, Units.GU) }
}
}
/** Compute the position of a sprite if it is not attached.
* @param sprite The sprite.
* @param position 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 def getSpritePositionFree(sprite:GraphicSprite, position:Point3, units:Units):Point3 = {
var pos = position
if(pos eq null)
pos = new Point3
if(sprite.getUnits == units) {
pos.x = sprite.getX
pos.y = sprite.getY
} else if(units == Units.GU && sprite.getUnits == Units.PX) {
pos.x = sprite.getX
pos.y = sprite.getY
bck.inverseTransform(pos)
} else if(units == Units.PX && sprite.getUnits == Units.GU) {
pos.x = sprite.getX
pos.y = sprite.getY
bck.transform(pos)
} else if(units == Units.GU && sprite.getUnits == Units.PERCENTS) {
pos.x = metrics.lo.x + (sprite.getX/100f) * metrics.graphWidthGU
pos.y = metrics.lo.y + (sprite.getY/100f) * metrics.graphHeightGU
} else if(units == Units.PX && sprite.getUnits == Units.PERCENTS) {
pos.x = (sprite.getX/100f) * metrics.viewport(2)
pos.y = (sprite.getY/100f) * metrics.viewport(3)
} else {
throw new RuntimeException("Unhandled yet sprite positioning convertion %s to %s.".format(sprite.getUnits, units))
}
pos
}
/** Compute the position of a sprite if attached to a node.
* @param sprite The sprite.
* @param position 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 def getSpritePositionNode(sprite:GraphicSprite, position:Point3, units:Units):Point3 = {
var pos = position
if(pos eq null)
pos = new Point3
val node = sprite.getNodeAttachment
val radius = metrics.lengthToGu( sprite.getX, sprite.getUnits )
val z = sprite.getZ * (Pi / 180.0)
pos.x = node.x + (cos(z) * radius)
pos.y = node.y + (sin(z) * radius)
if(units == Units.PX)
bck.transform(pos)
pos
}
/** Compute the position of a sprite if attached to an edge.
* @param sprite The sprite.
* @param position Where to store 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 def getSpritePositionEdge(sprite:GraphicSprite, position:Point3, units:Units):Point3 = {
var pos = position
if(pos eq null)
pos = new Point3
val edge = sprite.getEdgeAttachment.asInstanceOf[GraphicEdge]
val info = edge.getAttribute(Skeleton.attributeName).asInstanceOf[ConnectorSkeleton]
if(info ne null) {
val o = metrics.lengthToGu(sprite.getY, sprite.getUnits)
if(o==0) {
val p = info.pointOnShape(sprite.getX)
pos.x = p.x
pos.y = p.y
} else {
val p = info.pointOnShapeAndPerpendicular(sprite.getX, o)
pos.x = p.x
pos.y = p.y
}
} else {
var x = 0.0
var y = 0.0
var dx = 0.0
var dy = 0.0
var d = sprite.getX // Percent on the edge.
val o = metrics.lengthToGu(sprite.getY, sprite.getUnits)
// Offset from the position given by percent, perpendicular to the edge.
x = edge.from.x
y = edge.from.y
dx = edge.to.x - x
dy = edge.to.y - y
d = if( d > 1 ) 1 else d
d = if( d < 0 ) 0 else d
x += dx * d
y += dy * d
if(o != 0) {
d = sqrt( dx*dx + dy*dy )
dx /= d
dy /= d
x += -dy * o
y += dx * o
}
pos.x = x
pos.y = y
}
if(units == Units.PX)
bck.transform(pos)
pos
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy