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

org.graphstream.ui.util.ShapeUtil.scala Maven / Gradle / Ivy

Go to download

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.util

import org.graphstream.ui.graphicGraph._
import org.graphstream.ui.graphicGraph.stylesheet._
import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants._
import org.graphstream.ui.geom._
import org.graphstream.ui.j2dviewer._
import org.graphstream.ui.j2dviewer.renderer._

import scala.math._

object ShapeUtil {
  	/** Try to evaluate the "radius" of the edge target node shape along the edge. In other words
	  * this method computes the intersection point between the edge and the node shape contour.
	  * The returned length is the length of a line going from the center of the shape toward
	  * the point of intersection between the target node shape contour and the edge.
	  * @param edge The edge (it contains its target node).
	  * @param camera the camera.
	  * @return The radius. */
 	def evalTargetRadius2D(edge:GraphicEdge, camera:Camera):Double = {
 	    val eskel = edge.getAttribute(Skeleton.attributeName).asInstanceOf[ConnectorSkeleton]
 	    if(eskel ne null) {
 	    	evalTargetRadius2D(
 	    		edge.to.getStyle,
 	    		edge.to.getAttribute(Skeleton.attributeName).asInstanceOf[AreaSkeleton],
 	    		new Point3(eskel.from.x, eskel.from.y, eskel.from.z),
 	    		new Point3(eskel.to.x, eskel.to.y, eskel.to.z),
 	   	 		camera)
 		} else {
 			throw new RuntimeException("no skeleton on edge ??")
 		}
 	}
 	
 	/** Try to evaluate the "radius" of the given node considering an edge between points `from` and `to`.
 	  * In other words, this method computes the intersection point between the edge and the node
 	  * shape contour. The returned length is the length of a line going from the center of the shape
 	  * toward the point of intersection between the target node shape contour and the edge.
 	  * @param from The origin point of the edge.
 	  * @param to The target point of the edge.
 	  * @param node The target node shape.
 	  * @param the camera.
 	  * @return The radius. */
 	def evalTargetRadius2D(from:Point3, to:Point3, node:GraphicNode, camera:Camera):Double = {
 	    evalTargetRadius2D(node.getStyle, node.getAttribute(Skeleton.attributeName).asInstanceOf[AreaSkeleton],
 	            from, null, null, to, camera)
 	}
 
 	/** Try to evaluate the "radius" of the given area skeleton considering an edge between points `p0` and
 	  * point `p3` (the edge is considered a straight line). In other words, this method computes the intersection
 	  * point between the edge and the area geometry. The returned length of the line going from the center of
 	  * the skeleton geometry toward the point of intersection between the skeleton geometry and the edge.
 	  * @param style The style of the area skeleton.
 	  * @param skeleton The skeleton.
 	  * @param p0 The origin point of the edge.
 	  * @param p3 the target point of the edge.
 	  * @param camera the camera.
 	  * @return the radius. */
   	def evalTargetRadius2D(style:Style, skeleton:AreaSkeleton, p0:Point3, p3:Point3, camera:Camera):Double = evalTargetRadius2D(style, skeleton, p0, null, null, p3, camera)
  
 	/** Try to evaluate the "radius" of the given area skeleton considering a cubic curve edge between points `p0` and
 	  * point `p3` and curving to control points `p1`  and `p2`. In other words, this method computes the intersection
 	  * point between the edge and the area geometry. The returned length of the line going from the center of
 	  * the skeleton geometry toward the point of intersection between the skeleton geometry and the edge.
 	  * @param edge The edge.
 	  * @param p0 The origin point of the edge.
 	  * @param p3 the target point of the edge.
 	  * @param camera the camera.
 	  * @return the radius. */
  	def evalTargetRadius2D(edge:GraphicEdge, p0:Point3, p1:Point3, p2:Point3, p3:Point3, camera:Camera):Double = 
  		evalTargetRadius2D(edge.to.getStyle,
  			edge.to.getAttribute(Skeleton.attributeName).asInstanceOf[AreaSkeleton],
  			p0, p1, p2, p3, camera)
  		
 	/** Try to evaluate the "radius" of the given area skeleton considering a cubic curve edge between points `p0` and
 	  * point `p3` and curving to control points `p1`  and `p2`. In other words, this method computes the intersection
 	  * point between the edge and the area geometry. The returned length of the line going from the center of
 	  * the skeleton geometry toward the point of intersection between the skeleton geometry and the edge.
 	  * @param style The style of the area skeleton.
 	  * @param skeleton The skeleton.
 	  * @param p0 The origin point of the edge.
 	  * @param p3 the target point of the edge.
 	  * @param camera the camera.
 	  * @return the radius. */
  	def evalTargetRadius2D(style:Style, skeleton:AreaSkeleton, p0:Point3, p1:Point3, p2:Point3, p3:Point3, camera:Camera):Double = { 
		import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants.StrokeMode
		import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants.Shape._

  	  	var w = 0.0
  	  	var h = 0.0
  	  	val s = if(style.getStrokeMode != StrokeMode.NONE) camera.metrics.lengthToGu(style.getStrokeWidth) else 0f

  	  	if(skeleton != null) {
  	  		w = skeleton.theSize.x
  	  		h = skeleton.theSize.y
  	  	} else {
  	  		w = camera.metrics.lengthToGu(style.getSize, 0)
  	  		h = if(style.getSize.size > 1) camera.metrics.lengthToGu(style.getSize, 1) else w
  	  	}
  	  	
		style.getShape match {
			case CIRCLE       => evalEllipseRadius2D(p0, p1, p2, p3, w, h, s)
			case DIAMOND      => evalEllipseRadius2D(p0, p1, p2, p3, w, h, s)
			case CROSS        => evalEllipseRadius2D(p0, p1, p2, p3, w, h, s)
			case TRIANGLE     => evalEllipseRadius2D(p0, p1, p2, p3, w, h, s)
			case TEXT_CIRCLE  => evalEllipseRadius2D(p0, p1, p2, p3, w, h, s)
			case TEXT_DIAMOND => evalEllipseRadius2D(p0, p1, p2, p3, w, h, s)
			case BOX          => evalBoxRadius2D(p0, p1, p2, p3, w/2+s, h/2+s)
			case ROUNDED_BOX  => evalBoxRadius2D(p0, p1, p2, p3, w/2+s, h/2+s)
			case TEXT_BOX     => evalBoxRadius2D(p0, p1, p2, p3, w/2+s, h/2+s)
			case JCOMPONENT   => evalBoxRadius2D(p0, p1, p2, p3, w/2+s, h/2+s)
			case _            => evalBoxRadius2D(p0, p1, p2, p3, w/2+s, h/2+s)
		}
	}
  
   	/** Compute the length of a (eventually cubic curve) vector along the edge from the ellipse center toward the intersection
   	  * point with the ellipse that match the ellipse radius. If `p1` and `p2` are null, the edge is considered
   	  * a straight line, else a cubic curve.
   	  * @param p0 the origin point of the edge
   	  * @param p1 the first cubic-curve control point or null for straight edge.
   	  * @param p2 the second cubic-curve control point or null for straight edge.
   	  * @param p3 the target point of the edge.
   	  * @param w the width of the ellipse.
   	  * @param h the height of the ellipse.
   	  * @param s the width of the stroke of the ellipse shape.
   	  */
  	protected def evalEllipseRadius2D(p0:Point3, p1:Point3, p2:Point3, p3:Point3, w:Double, h:Double, s:Double):Double = {
  	  	if(w == h)
  	  	     w / 2 + s	// Welcome simplification for circles ...
  	  	else evalEllipseRadius2D(p0, p1, p2, p3, w/2 + s, h/2 + s)
  	}

	/** Compute the length of a vector along the edge from the ellipse center that match the
	  * ellipse radius.
	  * @param edge The edge representing the vector.
	  * @param w The ellipse first radius (width/2).
	  * @param h The ellipse second radius (height/2).
	  * @return The length of the radius along the edge vector. */
	protected def evalEllipseRadius2D(p0:Point3, p1:Point3, p2:Point3, p3:Point3, w:Double, h:Double):Double = {
  	  	if(w == h) {
  	  	     w / 2	// Welcome simplification for circles ...
  	  	} else {
			// Vector of the entering edge.
	
			var dx = 0.0
			var dy = 0.0
	
			if(p1 != null && p2 != null) {
				dx = p3.x - p2.x //( p2.x + ((p1.x-p2.x)/4) )	// Use the line going from the last control-point to target
				dy = p3.y - p2.y //( p2.y + ((p1.y-p2.y)/4) )	// center as the entering edge.
			} else {
				dx = p3.x - p0.x
				dy = p3.y - p0.y
			}
			
			// The entering edge must be deformed by the ellipse ratio to find the correct angle.
	
			dy *= w / h
	
			// Find the angle of the entering vector with (1,0).
	
			val d  = sqrt(dx*dx + dy*dy)
			var a  = dx / d
	
			// Compute the coordinates at which the entering vector and the ellipse cross.
	
			a  = acos(a)
			dx = cos(a) * w
			dy = sin(a) * h
	
			// The distance from the ellipse center to the crossing point of the ellipse and
			// vector. Yo !
	
			sqrt(dx*dx + dy*dy)
  	  	}
	}

 	/** Compute the length of a vector along the edge from the box center that match the box
	  * "radius".
	  * @param edge The edge representing the vector.
	  * @param w The box first radius (width/2).
	  * @param h The box second radius (height/2).
	  * @return The length of the radius along the edge vector. */
	def evalBoxRadius2D(p0:Point3, p1:Point3, p2:Point3, p3:Point3, w:Double, h:Double):Double = {
		// Pythagora : Angle at which we compute the intersection with the height or the width.
	
		var da = w / sqrt(w*w + h*h).toDouble
		
		da = if(da < 0) -da else da
		
		// Angle of the incident vector.
		var dx = 0.0
		var dy = 0.0

		if(p1 != null && p2 != null) {
			dx = p3.x - p2.x // ( p2.x + ((p1.x-p2.x)/4) )	// Use the line going from the last control-point to target
			dy = p3.y - p2.y //( p2.y + ((p1.y-p2.y)/4) )	// center as the entering edge.
		} else {
			dx = p3.x - p0.x
			dy = p3.y - p0.y
		}
  
		val d = sqrt(dx*dx + dy*dy).toDouble
		var a = dx/d
		
		a = if(a < 0) -a else a
	
		// Choose the side of the rectangle the incident edge vector crosses.
		
		if(da < a) {
			w / a
		} else {
			a = dy/d
			a = if(a < 0) -a else a
            h / a
		}
	}
	
	/** Compute if point `p` is inside of the shape of `elt` whose overall size is `w` x `h`. */
	def isPointIn(elt:GraphicElement, p:Point3, w:Double, h:Double):Boolean = {
		import ShapeKind._
		elt.getStyle.getShape.kind match {
			case RECTANGULAR => isPointIn2DBox( p, elt.getX, elt.getY, w, h )
			case ELLIPSOID   => isPointIn2DEllipse( p, elt.getX, elt.getY, w, h )
			case _ => false
		}
	}
	
	/** Compute if point `p` is inside of a rectangular shape of overall size `w` x `h`. */
	def isPointIn2DBox(p:Point3, x:Double, y:Double, w:Double, h:Double):Boolean = {
		val w2 = w/2
		val h2 = h/2
		( p.x > (x-w2) && p.x < (x+w2) && p.y > (y-h2) && p.y < (y+h2) )
	}
	
	/** Compute if point `p` is inside of a ellipsoid shape of overall size `w` x `h`. */
	def isPointIn2DEllipse(p:Point3, x:Double, y:Double, w:Double, h:Double):Boolean = {
		val xx = p.x - x
		val yy = p.y - y
		val w2 = w/2
		val h2 = h/2
		
		(((xx*xx)/(w2*w2)) + ((yy*yy)/(h2*h2)) < 1)
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy