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

org.graphstream.ui.j2dviewer.renderer.shape.AdvancedShapes.scala Maven / Gradle / Ivy

/*
 * Copyright 2006 - 2011 
 *     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.renderer.shape

import java.awt._
import java.awt.geom._

import org.graphstream.ui.j2dviewer.renderer._
import org.graphstream.ui.geom.Point2
import org.graphstream.ui.graphicGraph._
import org.graphstream.ui.graphicGraph.stylesheet._
import org.graphstream.ui.j2dviewer._
import org.graphstream.ui.util._
import org.graphstream.ui.sgeom._

import scala.math._

/** Utility trait to display cubics Bézier curves control polygons. */
abstract trait ShowCubics {
	protected var showControlPolygon = false
	
	/** Show the control polygons. */
	protected def showCtrlPoints(g:Graphics2D, camera:Camera, info:EdgeInfo) {
		if(showControlPolygon && info.isCurve) {
			val from   = info.from
			val ctrl1  = info(1)
			val ctrl2  = info(2)
			val to     = info.to
		   	val oval   = new Ellipse2D.Double
	 		val color  = g.getColor
		 	val stroke = g.getStroke
		 	val px6    = camera.metrics.px1*6;
		   	val px3    = camera.metrics.px1*3
	   
	 		g.setColor(Color.RED)
	 		oval.setFrame(from.x-px3, from.y-px3, px6, px6)
	 		g.fill(oval)
	
	 		if(ctrl1 != null) {
	 			oval.setFrame(ctrl1.x-px3, ctrl1.y-px3, px6, px6)
		 		g.fill(oval)
		 		oval.setFrame(ctrl2.x-px3, ctrl2.y-px3, px6, px6)
		 		g.fill(oval)
		 		val line = new Line2D.Double
		 		line.setLine(ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y)
		 		g.setStroke(new java.awt.BasicStroke(camera.metrics.px1.toFloat))
		 		g.draw(line)
		 		line.setLine(from.x, from.y, ctrl1.x, ctrl1.y)
		 		g.draw(line)
		 		line.setLine(ctrl2.x, ctrl2.y, to.x, to.y)
		 		g.draw(line)
	 		}
	
	 		oval.setFrame(to.x-px3, to.y-px3, px6, px6)
	 		g.fill(oval)
	 		g.setColor(color)
		 	g.setStroke(stroke)
		}
	}
}

/**
 * A blob-like shape.
 */
class BlobShape extends AreaConnectorShape with ShowCubics {
	protected var theShape = new Path2D.Double
 
// Command
 
	protected def make(bck:Backend, camera:Camera) {
		make(camera, 0, 0, 0, 0)
	}
	
	protected def make(camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double) {
		if(info.isCurve)
		     makeOnCurve(camera, sox, soy, swx, swy)
		else if(info.isPoly)
		     makeOnPolyline(camera, sox, soy, swx, swy)
		else makeOnLine(camera, sox, soy, swx, swy)
	}
 
	protected def makeOnLine( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		val fromx = info.from.x + sox
		val fromy = info.from.y + soy
		val tox   = info.to.x + sox
		val toy   = info.to.y + soy
		val dir   = new Vector2(tox - fromx, toy - fromy)
		val perp1 = new Vector2(dir.y, -dir.x); perp1.normalize	// 1/2 perp vector to the from point.
		val perp2 = new Vector2(perp1.x, perp1.y)				// 1/2 perp vector to the to point.
		val perpm = new Vector2(perp1.x, perp1.y)				// 1/2 perp vector to the middle point on the edge.
		val srcsz = min( theSourceSizeX, theSourceSizeY )
		val trgsz = min( theTargetSizeX, theTargetSizeY )
		
		perp1.scalarMult( (srcsz+swx)/2f )
		perpm.scalarMult( (theSize+swx)/2f )
   
		if( isDirected )
		     perp2.scalarMult( (theSize+swx)/2f )
		else perp2.scalarMult( (trgsz+swx)/2f )
		
		val t1 = 5f
		val t2 = 2.3f
		val m  = 1f
		theShape.reset
		theShape.moveTo( fromx + perp1.x, fromy + perp1.y )
		theShape.quadTo( fromx + dir.x/t1 + perpm.x*m, fromy + dir.y/t1 + perpm.y*m,
		                 fromx + dir.x/t2 + perpm.x,   fromy + dir.y/t2 + perpm.y )
		theShape.lineTo( tox - dir.x/t2 + perpm.x, toy - dir.y/t2 + perpm.y )
		theShape.quadTo( tox - dir.x/t1 + perpm.x*m, toy - dir.y/t1 + perpm.y*m,
		                 tox + perp2.x, toy + perp2.y )
		theShape.lineTo( tox - perp2.x, toy - perp2.y )
		theShape.quadTo( tox - dir.x/t1 - perpm.x*m, toy - dir.y/t1 - perpm.y*m,
		                 tox - dir.x/t2 - perpm.x,   toy - dir.y/t2 - perpm.y )
		theShape.lineTo( fromx + dir.x/t2 - perpm.x, fromy + dir.y/t2 - perpm.y )
		theShape.quadTo( fromx + dir.x/t1 - perpm.x*m, fromy + dir.y/t1 - perpm.y*m,
		                 fromx - perp1.x, fromy - perp1.y )
		theShape.closePath
	}
	
	protected def makeOnPolyline(camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double) {
	    // TODO
	    makeOnLine(camera, sox, soy, swx, swy)
	}
 
	protected def makeOnCurve( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		if( info.isLoop )
		     makeLoop( camera, sox, soy, swx, swy )
		else makeMulti( camera, sox, soy, swx, swy )
	}
	
	protected def makeMulti( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		val fromx = info(0).x + sox
		val fromy = info(0).y + soy
		val tox   = info(3).x + sox
		val toy   = info(3).y + soy
		val c1x   = info(1).x + sox
		val c1y   = info(1).y + soy
		val c2x   = info(2).x + sox
		val c2y   = info(2).y + soy
		val srcsz = min( theSourceSizeX, theSourceSizeY )
		val trgsz = min( theTargetSizeX, theTargetSizeY )

		val maindir = new Vector2( c2x - c1x, c2y - c1y )
		val perp1   = new Vector2( maindir.y, -maindir.x ); perp1.normalize	// 1/2 perp vector to the from point.
		val perp2   = new Vector2( perp1.x, perp1.y )						// 1/2 perp vector to the to point.
		val perpm   = new Vector2( perp1.x, perp1.y )						// 1/2 perp vector to the middle point on the edge.
		  
        val t = 5f
                                                       
        perp1.scalarMult( (srcsz+swx)/2f )
        perpm.scalarMult( (theSize+swx)/2f )
                 
        //   ctrl1           ctrl2
        //     x---t-------t---x
        //    /                 \
        //   /                   \
        //  X                     X
        // from                  to
            
		if( isDirected )
		     perp2.scalarMult( (theSize+swx)/2f )	
		else perp2.scalarMult( (trgsz+swx)/2f )
		  
        theShape.reset
        theShape.moveTo( fromx + perp1.x, fromy + perp1.y )
        
        theShape.quadTo( c1x + perpm.x, c1y + perpm.y,
                         c1x + maindir.x/t + perpm.x, c1y + maindir.y/t + perpm.y )
        theShape.lineTo( c2x - maindir.x/t + perpm.x, c2y - maindir.y/t + perpm.y )
        theShape.quadTo( c2x + perpm.x, c2y + perpm.y, tox + perp2.x, toy + perp2.y )
        
        theShape.lineTo( tox - perp2.x, toy - perp2.y )
        
        theShape.quadTo( c2x - perpm.x, c2y - perpm.y,
                         c2x - maindir.x/t - perpm.x, c2y - maindir.y/t - perpm.y )
        theShape.lineTo( c1x + maindir.x/t - perpm.x, c1y + maindir.y/t - perpm.y )
        theShape.quadTo( c1x - perpm.x, c1y - perpm.y, fromx - perp1.x, fromy - perp1.y )
        
        theShape.closePath
	}

	protected def makeLoop( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		val fromx = info(0).x + sox
		val fromy = info(0).y + soy
		val tox   = info(3).x + sox
		val toy   = info(3).y + soy
		val c1x   = info(1).x + sox
		val c1y   = info(1).y + soy
		val c2x   = info(2).x + sox
		val c2y   = info(2).y + soy
		val srcsz = min( theSourceSizeX, theSourceSizeY )
//		val trgsz = min( theTargetSizeX, theTargetSizeY )
		
		val dirFrom  = new Vector2( c1x - fromx, c1y - fromy );
		val dirTo    = new Vector2( tox - c2x, toy - c2y );
	  	val mainDir  = new Vector2( c2x - c1x, c2y - c1y )
	  	
	  	val perpFrom = new Vector2( dirFrom.y, -dirFrom.x ); perpFrom.normalize
		val mid1     = new Vector2( dirFrom ); mid1.sub( mainDir ); mid1.normalize
		val mid2     = new Vector2( mainDir ); mid2.sub( dirTo );   mid2.normalize
			
		perpFrom.scalarMult( (srcsz+swx)*0.3f )
		
		if( isDirected ) {
			mid1.scalarMult( (theSize+swx)*4f )
			mid2.scalarMult( (theSize+swx)*2f )
		} else {
			mid1.scalarMult( (theSize+swx)*4f )
			mid2.scalarMult( (theSize+swx)*4f )
		}
			
		theShape.reset
		theShape.moveTo( fromx + perpFrom.x, fromy + perpFrom.y )
		if( isDirected ) {
			theShape.curveTo( c1x + mid1.x, c1y + mid1.y, c2x + mid2.x, c2y + mid2.y, tox, toy )
			theShape.curveTo( c2x - mid2.x, c2y - mid2.y, c1x - mid1.x, c1y - mid1.y, fromx - perpFrom.x, fromy - perpFrom.y )
		} else {
			var perpTo = new Vector2( dirTo.y, -dirTo.x ); perpTo.normalize; perpTo.scalarMult( (srcsz+swx)*0.3f )
			theShape.curveTo( c1x + mid1.x, c1y + mid1.y, c2x + mid2.x, c2y + mid2.y, tox + perpTo.x, toy + perpTo.y )
			theShape.lineTo( tox - perpTo.x, toy - perpTo.y )
			theShape.curveTo( c2x - mid2.x, c2y - mid2.y, c1x - mid1.x, c1y - mid1.y, fromx - perpFrom.x, fromy - perpFrom.y )
		}
		theShape.closePath
	}

	protected def makeShadow(bck:Backend, camera:Camera ) {
		make( camera, theShadowOff.x, theShadowOff.y, theShadowWidth.x, theShadowWidth.y )
	}
	
	def renderShadow(bck:Backend, camera:Camera, element:GraphicElement, info:ElementInfo ) {
 		makeShadow(bck, camera )
 		cast(bck.graphics2D, theShape )
	}
 
	def render(bck:Backend, camera:Camera, element:GraphicElement, info:ElementInfo ) {
	    val g = bck.graphics2D
 		make(bck, camera )
 		stroke( g, theShape )
 		fill( g, theSize, theShape, camera )
 		decorConnector( g, camera, info.iconAndText, element, theShape )

 		if( showControlPolygon ) {
	 		val c = g.getColor();
	 		val s = g.getStroke();
	 		g.setStroke( new java.awt.BasicStroke( camera.metrics.px1.toFloat ) )
	 		g.setColor( Color.red );
	 		g.draw( theShape );
	 		g.setStroke( s );
	 		g.setColor( c );
	 		showCtrlPoints( g, camera, info.asInstanceOf[EdgeInfo] )
 		}
	}
}

/**
 * An angular shape.
 */
class AngleShape extends AreaConnectorShape {
	protected var theShape = new Path2D.Double
 
// Command
 
	protected def make(bck:Backend, camera:Camera) {
		make(camera, 0, 0, 0, 0)
	}
  
	protected def make( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		if(info.isCurve)
		     makeOnCurve(camera, sox, soy, swx, swy)
		else if(info.isPoly)
		     makeOnPolyline(camera, sox, soy, swx, swy)
		else makeOnLine(camera, sox, soy, swx, swy)
	}
 
	protected def makeOnLine(camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double) {
		val fromx = info.from.x + sox
		val fromy = info.from.y + soy
		val tox   = info.to.x + sox
		val toy   = info.to.y + soy
		val dir   = new Vector2(tox - fromx, toy - fromy)
		val perp  = new Vector2(dir.y, -dir.x); perp.normalize	// 1/2 perp vector to the from point.
   
		perp.scalarMult((theSize+swx)/2f)
   
		theShape.reset
		theShape.moveTo(fromx + perp.x, fromy + perp.y)
		if( isDirected ) {
		     theShape.lineTo( tox, toy )
		} else {
			theShape.lineTo( tox + perp.x, toy + perp.y )
			theShape.lineTo( tox - perp.x, toy - perp.y )
		}
		theShape.lineTo( fromx - perp.x, fromy - perp.y )
		theShape.closePath
	}
	
	protected def makeOnPolyline(camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double) {
	    // TODO
	    makeOnLine(camera, sox, soy, swx, swy)
	}
 
	protected def makeOnCurve( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		if( info.isLoop )
		     makeLoop( camera, sox, soy, swx, swy )
		else makeMulti( camera, sox, soy, swx, swy )
	}
	
	protected def makeMulti( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		val fromx   = info(0).x + sox
		val fromy   = info(0).y + soy
		val tox     = info(3).x + sox
		val toy     = info(3).y + soy
		val c1x     = info(1).x + sox
		val c1y     = info(1).y + soy
		val c2x     = info(2).x + sox
		val c2y     = info(2).y + soy
		val maindir = new Vector2( c2x - c1x, c2y - c1y )
		val perp    = new Vector2( maindir.y, -maindir.x ); perp.normalize	// 1/2 perp vector to the from point.
		val perp1   = new Vector2( perp.x, perp.y )							// 1/2 perp vector to the first control point.
		val perp2   = new Vector2( perp.x, perp.y )							// 1/2 perp vector to the second control point.

        perp.scalarMult( (theSize+swx) * 0.5f )
        
        if( isDirected ) {
        	perp1.scalarMult( (theSize+swx) * 0.4f )
        	perp2.scalarMult( (theSize+swx) * 0.2f )
        } else {
        	perp1.scalarMult( (theSize+swx) * 0.5f )
        	perp2.scalarMult( (theSize+swx) * 0.5f )
        }
                 
        //   ctrl1           ctrl2
        //     x---t-------t---x
        //    /                 \
        //   /                   \
        //  X                     X
        // from                  to
            
        theShape.reset
        theShape.moveTo( fromx + perp.x, fromy + perp.y )
        if( isDirected ) {
        	theShape.curveTo( c1x + perp1.x, c1y + perp1.y,
                              c2x + perp2.x, c2y + perp2.y,
                              tox, toy )
            theShape.curveTo( c2x - perp2.x, c2y - perp2.y,
                              c1x - perp1.x, c1y - perp1.y,
                              fromx - perp.x,  fromy - perp.y )
        } else {
        	theShape.curveTo( c1x + perp.x, c1y + perp.y,
                              c2x + perp.x, c2y + perp.y,
                              tox + perp.x, toy + perp.y )
            theShape.lineTo(  tox - perp.x, toy - perp.y )
            theShape.curveTo( c2x - perp.x, c2y - perp.y,
                              c1x - perp.x, c1y - perp.y,
                              fromx - perp.x, fromy - perp.y )
        }
        theShape.closePath
	}

	protected def makeLoop( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
	  	val fromx = info(0).x + sox
		val fromy = info(0).y + soy
		val tox   = info(3).x + sox
		val toy   = info(3).y + soy
		val c1x   = info(1).x + sox
		val c1y   = info(1).y + soy
		val c2x   = info(2).x + sox
		val c2y   = info(2).y + soy

		val dirFrom  = new Vector2( c1x - fromx, c1y - fromy );
		val dirTo    = new Vector2( tox - c2x, toy - c2y );
	  	val mainDir  = new Vector2( c2x - c1x, c2y - c1y )
	  	
	  	val perpFrom = new Vector2( dirFrom.y, -dirFrom.x ); perpFrom.normalize
		val mid1     = new Vector2( dirFrom ); mid1.sub( mainDir ); mid1.normalize
		val mid2     = new Vector2( mainDir ); mid2.sub( dirTo );   mid2.normalize
			
		perpFrom.scalarMult( theSize*0.5f )
		
		if( isDirected ) {
			mid1.scalarMult( theSize*0.8f )
			mid2.scalarMult( theSize*0.6f )
		} else {
			mid1.scalarMult( theSize*0.99f )
			mid2.scalarMult( theSize*0.99f )
		}
			
		theShape.reset
		theShape.moveTo( fromx + perpFrom.x, fromy + perpFrom.y )
		if( isDirected ) {
			theShape.curveTo( c1x + mid1.x, c1y + mid1.y, c2x + mid2.x, c2y + mid2.y, tox, toy )
			theShape.curveTo( c2x - mid2.x, c2y - mid2.y, c1x - mid1.x, c1y - mid1.y, fromx - perpFrom.x, fromy - perpFrom.y )
		} else {
			var perpTo = new Vector2( dirTo.y, -dirTo.x ); perpTo.normalize; perpTo.scalarMult( theSize*0.5f )
			theShape.curveTo( c1x + mid1.x, c1y + mid1.y, c2x + mid2.x, c2y + mid2.y, tox + perpTo.x, toy + perpTo.y )
			theShape.lineTo( tox - perpTo.x, toy - perpTo.y )
			theShape.curveTo( c2x - mid2.x, c2y - mid2.y, c1x - mid1.x, c1y - mid1.y, fromx - perpFrom.x, fromy - perpFrom.y )
		}
		theShape.closePath
	}

	protected def makeShadow(bck:Backend, camera:Camera ) {
		if( info.isCurve )
		     makeOnCurve( camera, theShadowOff.x, theShadowOff.y, theShadowWidth.x, theShadowWidth.y )
		else makeOnLine( camera, theShadowOff.x, theShadowOff.y, theShadowWidth.x, theShadowWidth.y )
	}
 
	def renderShadow(bck:Backend, camera:Camera, element:GraphicElement, info:ElementInfo ) {
 		makeShadow(bck, camera )
 		cast(bck.graphics2D, theShape )
	}
 
	def render(bck:Backend, camera:Camera, element:GraphicElement, info:ElementInfo ) {
	    val g = bck.graphics2D
 		make(bck, camera )
 		stroke( g, theShape )
 		fill( g, theSize, theShape, camera )
 		decorConnector( g, camera, info.iconAndText, element, theShape )
	}
}

/**
 * A cubic curve shape.
 */
class CubicCurveShape extends LineConnectorShape with ShowCubics {
	protected var theShape = new Path2D.Double

// Command
 
	protected def make(bck:Backend, camera:Camera ) {
		make( camera, 0, 0, 0, 0 )
	}
  
	protected def make( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		if( info.multi > 1 || info.isLoop )	// is a loop or a multi edge
		     makeMultiOrLoop( camera, sox, soy, swx, swy )
		else makeSingle( camera, sox, soy, swx, swy )	// is a single edge.
	}
 
	protected def makeSingle( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		val fromx   = info.from.x + sox
		val fromy   = info.from.y + soy
		val tox     = info.to.x + sox
		val toy     = info.to.y + soy
		val mainDir = new Vector2( info.from, info.to )
		val length  = mainDir.length
		val angle   = mainDir.y / length
		var c1x     = 0.0
		var c1y     = 0.0
		var c2x     = 0.0
		var c2y     = 0.0
		
		if( angle > 0.707107f || angle < -0.707107f ) {
			// North or south.
			c1x = fromx + mainDir.x / 2
			c2x = c1x
			c1y = fromy
			c2y = toy
		} else {
			// East or west.
			c1x = fromx
			c2x = tox
			c1y = fromy + mainDir.y / 2
			c2y = c1y
		}

		theShape.reset
		theShape.moveTo( fromx, fromy )
		theShape.curveTo( c1x, c1y, c2x, c2y, tox, toy )

		// Set the connector as a curve.
		
		if( sox == 0 && soy == 0 ) {
			info.setCurve(
				fromx, fromy, 0,
				c1x, c1y, 0,
				c2x, c2y, 0,
				tox, toy, 0 )
		}
	}
 
	protected def makeMultiOrLoop( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		if( info.isLoop )
			 makeLoop( camera, sox, soy, swx, swy )
		else makeMulti( camera, sox, soy, swx, swy )
	}
	
	protected def makeMulti( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		val fromx   = info(0).x + sox
		val fromy   = info(0).y + soy
		val tox     = info(3).x + sox
		val toy     = info(3).y + soy
		val c1x     = info(1).x + sox
		val c1y     = info(1).y + soy
		val c2x     = info(2).x + sox
		val c2y     = info(2).y + soy

		theShape.reset
		theShape.moveTo( fromx, fromy )
		theShape.curveTo( c1x, c1y, c2x, c2y, tox, toy )
	}

	protected def makeLoop( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
	  	val fromx = info(0).x + sox
		val fromy = info(0).y + soy
		val tox   = info(3).x + sox
		val toy   = info(3).y + soy
		val c1x   = info(1).x + sox
		val c1y   = info(1).y + soy
		val c2x   = info(2).x + sox
		val c2y   = info(2).y + soy

		theShape.reset
		theShape.moveTo( fromx, fromy )
		theShape.curveTo( c1x, c1y, c2x, c2y, tox, toy )
	}

	protected def makeShadow(bck:Backend, camera:Camera ) {
		if( info.isCurve )
		     makeMultiOrLoop( camera, theShadowOff.x, theShadowOff.y, theShadowWidth, theShadowWidth )
		else makeSingle( camera, theShadowOff.x, theShadowOff.y, theShadowWidth, theShadowWidth )
	}
	
	def renderShadow(bck:Backend, camera:Camera, element:GraphicElement, info:ElementInfo ) {
 		makeShadow(bck, camera )
 		cast(bck.graphics2D, theShape )
	}
 
	def render(bck:Backend, camera:Camera, element:GraphicElement, info:ElementInfo ) {
	    val g = bck.graphics2D
 		make(bck, camera )
 		stroke( g, theShape )
 		fill( g, theSize, theShape )
 		decorConnector( g, camera, info.iconAndText, element, theShape )
// 		showControlPolygon = true
// 		if( showControlPolygon ) {
//	 		val c = g.getColor();
//	 		val s = g.getStroke();
//	 		g.setStroke( new java.awt.BasicStroke( camera.metrics.px1 ) )
//	 		g.setColor( Color.red );
//	 		g.draw( theShape );
//	 		g.setStroke( s );
//	 		g.setColor( c );
//	 		showCtrlPoints( g, camera )
// 		}
	}
}

/**
 * A cubic curve shape that mimics freeplane edges.
 */
class FreePlaneEdgeShape extends LineConnectorShape {
	protected var theShape = new Path2D.Double

// Command
 
	protected def make(bck:Backend, camera:Camera ) {
		make( camera, 0, 0, 0, 0 )
	}
  
	protected def make( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		if( info.multi > 1 || info.isLoop )	// is a loop or a multi edge
		     makeMultiOrLoop( camera, sox, soy, swx, swy )
		else makeSingle( camera, sox, soy, swx, swy )	// is a single edge.
	}
 
	protected def makeSingle( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		var fromx   = info.from.x + sox
		val fromy   = info.from.y + soy - theSourceSizeY/2
		var tox     = info.to.x + sox
		val toy     = info.to.y + soy - theTargetSizeY/2
		val length  = abs( info.to.x - info.from.x )
		var c1x     = 0.0
		var c1y     = 0.0
		var c2x     = 0.0
		var c2y     = 0.0
		
		if( info.from.x < info.to.x ) {
			// At right.
			fromx += theSourceSizeX/2
			tox   -= theTargetSizeX/2
			c1x    = fromx + length/3
			c2x    = tox - length/3
			c1y    = fromy
			c2y    = toy
		} else {
			// At left.
			fromx -= theSourceSizeX/2
			tox   += theTargetSizeX/2
			c1x    = fromx - length/3
			c2x    = tox + length/3
			c1y    = fromy
			c2y    = toy
		}

		theShape.reset
		theShape.moveTo( fromx, fromy )
		theShape.curveTo( c1x, c1y, c2x, c2y, tox, toy )

		// Set the connector as a curve.
		
		if( sox == 0 && soy == 0 ) {
			info.setCurve(
					fromx, fromy, 0,
					c1x, c1y, 0,
					c2x, c2y, 0,
					tox, toy, 0 )
		}
	}
 
	protected def makeMultiOrLoop( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		if( info.isLoop )
			 makeLoop( camera, sox, soy, swx, swy )
		else makeMulti( camera, sox, soy, swx, swy )
	}
	
	protected def makeMulti( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		val fromx   = info(0).x + sox
		val fromy   = info(0).y + soy
		val tox     = info(3).x + sox
		val toy     = info(3).y + soy
		val c1x     = info(1).x + sox
		val c1y     = info(1).y + soy
		val c2x     = info(2).x + sox
		val c2y     = info(2).y + soy

		theShape.reset
		theShape.moveTo( fromx, fromy )
		theShape.curveTo( c1x, c1y, c2x, c2y, tox, toy )
	}

	protected def makeLoop( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
	  	val fromx = info(0).x + sox
		val fromy = info(0).y + soy
		val tox   = info(3).x + sox
		val toy   = info(3).y + soy
		val c1x   = info(1).x + sox
		val c1y   = info(1).y + soy
		val c2x   = info(2).x + sox
		val c2y   = info(2).y + soy

		theShape.reset
		theShape.moveTo( fromx, fromy )
		theShape.curveTo( c1x, c1y, c2x, c2y, tox, toy )
	}

	protected def makeShadow(bck:Backend, camera:Camera ) {
		if( info.isCurve )
		     makeMultiOrLoop( camera, theShadowOff.x, theShadowOff.y, theShadowWidth, theShadowWidth )
		else makeSingle( camera, theShadowOff.x, theShadowOff.y, theShadowWidth, theShadowWidth )
	}
	
	def renderShadow(bck:Backend, camera:Camera, element:GraphicElement, info:ElementInfo ) {
 		makeShadow(bck, camera )
 		cast(bck.graphics2D, theShape )
	}
 
	def render(bck:Backend, camera:Camera, element:GraphicElement, info:ElementInfo ) {
	    val g = bck.graphics2D
 		make(bck, camera )
 		stroke( g, theShape )
 		fill( g, theSize, theShape )
 		decorConnector( g, camera, info.iconAndText, element, theShape )
	}
}

class HorizontalSquareEdgeShape extends LineConnectorShape {
	protected var theShape = new Path2D.Double

// Command
 
	protected def make(bck:Backend, camera:Camera ) {
		make( camera, 0, 0, 0, 0 )
	}
  
	protected def make( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		if( info.multi > 1 || info.isLoop )	// is a loop or a multi edge
		     makeMultiOrLoop( camera, sox, soy, swx, swy )
		else makeSingle( camera, sox, soy, swx, swy )	// is a single edge.
	}
 
	protected def makeSingle( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
	    val from   = new Point3(info.from.x+sox, info.from.y+soy, 0)
	    val to     = new Point3(info.to.x+sox, info.to.y+soy, 0)
	    val size   = (theSourceSizeX+theTargetSizeX)
	    var inter1:Point3 = null 
	    var inter2:Point3 = null 
	    var inter3:Point3 = null 
	    var inter4:Point3 = null 
	    
	    if(to.x > from.x) {
		    val len = to.x - from.x
		    
		    if(len < size) {
		        inter1 = new Point3(from.x+theSourceSizeX, from.y, 0)
		        inter2 = new Point3(to.x-theTargetSizeX, to.y, 0)
		        
		        inter3 = new Point3(inter1.x, inter1.y+(to.y-from.y)/2, 0)
		        inter4 = new Point3(inter2.x, inter3.y, 0)
		        
		        if(sox==0 && soy==0)
		        	info.setPoly(from, inter1, inter3, inter4, inter2, to)
		
		    } else {
		        val middle = (to.x - from.x) / 2
		        inter1 = new Point3(from.x + middle, from.y, 0)
		        inter2 = new Point3(to.x - middle, to.y, 0)
		        
		        if(sox==0 && soy==0)
		        	info.setPoly(from, inter1, inter2, to)
		    }
		} else {
		    val len = from.x - to.x
		    
		    if(len < size) {
		        inter1 = new Point3(from.x-theSourceSizeX, from.y, 0)
		        inter2 = new Point3(to.x+theTargetSizeX, to.y, 0)

		        inter3 = new Point3(inter1.x, inter1.y+(to.y-from.y)/2, 0)
		        inter4 = new Point3(inter2.x, inter3.y, 0)
		        
		        if(sox==0 && soy==0)
		        	info.setPoly(from, inter1, inter3, inter4, inter2, to)
		
		    } else {
		        val middle = (to.x - from.x) / 2
		        inter1 = new Point3(from.x + middle, from.y, 0)
		        inter2 = new Point3(to.x - middle, to.y, 0)
		        
		        if(sox==0 && soy==0)
		        	info.setPoly(from, inter1, inter2, to)
		    }
		}
		
		theShape.reset
		theShape.moveTo(from.x, from.y)
		theShape.lineTo(inter1.x, inter1.y)
		if((inter3 ne null) && (inter4 ne null)) {
			theShape.lineTo(inter3.x, inter3.y)
		    theShape.lineTo(inter4.x, inter4.y)
		}
		theShape.lineTo(inter2.x, inter2.y)
		theShape.lineTo(to.x, to.y)
	}
 
	protected def makeMultiOrLoop( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		if( info.isLoop )
			 makeLoop( camera, sox, soy, swx, swy )
		else makeMulti( camera, sox, soy, swx, swy )
	}
	
	protected def makeMulti( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		val fromx   = info(0).x + sox
		val fromy   = info(0).y + soy
		val tox     = info(3).x + sox
		val toy     = info(3).y + soy
		val c1x     = info(1).x + sox
		val c1y     = info(1).y + soy
		val c2x     = info(2).x + sox
		val c2y     = info(2).y + soy

		theShape.reset
		theShape.moveTo( fromx, fromy )
		theShape.curveTo( c1x, c1y, c2x, c2y, tox, toy )
	}

	protected def makeLoop( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
	  	val fromx = info(0).x + sox
		val fromy = info(0).y + soy
		val tox   = info(3).x + sox
		val toy   = info(3).y + soy
		val c1x   = info(1).x + sox
		val c1y   = info(1).y + soy
		val c2x   = info(2).x + sox
		val c2y   = info(2).y + soy

		theShape.reset
		theShape.moveTo( fromx, fromy )
		theShape.curveTo( c1x, c1y, c2x, c2y, tox, toy )
	}

	protected def makeShadow(bck:Backend, camera:Camera ) {
		if( info.isCurve )
		     makeMultiOrLoop( camera, theShadowOff.x, theShadowOff.y, theShadowWidth, theShadowWidth )
		else makeSingle( camera, theShadowOff.x, theShadowOff.y, theShadowWidth, theShadowWidth )
	}
	
	def renderShadow(bck:Backend, camera:Camera, element:GraphicElement, info:ElementInfo ) {
 		makeShadow(bck, camera )
 		cast(bck.graphics2D, theShape )
	}
 
	def render(bck:Backend, camera:Camera, element:GraphicElement, info:ElementInfo ) {
	    val g = bck.graphics2D
 		make(bck, camera )
 		stroke( g, theShape )
 		fill( g, theSize, theShape )
 		decorConnector( g, camera, info.iconAndText, element, theShape )
	}
}

class LSquareEdgeShape extends LineConnectorShape {
	protected var theShape = new Path2D.Double

// Command
 
	protected def make(bck:Backend, camera:Camera ) {
		make( camera, 0, 0, 0, 0 )
	}
  
	protected def make( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		if( info.multi > 1 || info.isLoop )	// is a loop or a multi edge
		     makeMultiOrLoop( camera, sox, soy, swx, swy )
		else makeSingle( camera, sox, soy, swx, swy )	// is a single edge.
	}
 
	protected def makeSingle( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
	    val from    = new Point3(info.from.x+sox, info.from.y+soy, 0)
	    val to      = new Point3(info.to.x+sox, info.to.y+soy, 0)
		val mainDir = new Vector2( from, to )
		val length  = mainDir.length
		val angle   = mainDir.y / length
		var inter:Point3 = null

		if( angle > 0.707107f || angle < -0.707107f ) {
			// North or south.
		    inter = new Point3(from.x, to.y, 0)
		} else {
			// East or west.
		    inter = new Point3(to.x, from.y, 0)
		}
	    
	    if(sox == 0 && soy == 0)
	    	info.setPoly(from, inter, to)
	    
		theShape.reset
		theShape.moveTo(from.x, from.y)
		theShape.lineTo(inter.x, inter.y)
		theShape.lineTo(to.x, to.y)
	}
 
	protected def makeMultiOrLoop( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		if( info.isLoop )
			 makeLoop( camera, sox, soy, swx, swy )
		else makeMulti( camera, sox, soy, swx, swy )
	}
	
	protected def makeMulti( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
		val fromx   = info(0).x + sox
		val fromy   = info(0).y + soy
		val tox     = info(3).x + sox
		val toy     = info(3).y + soy
		val c1x     = info(1).x + sox
		val c1y     = info(1).y + soy
		val c2x     = info(2).x + sox
		val c2y     = info(2).y + soy

		theShape.reset
		theShape.moveTo( fromx, fromy )
		theShape.curveTo( c1x, c1y, c2x, c2y, tox, toy )
	}

	protected def makeLoop( camera:Camera, sox:Double, soy:Double, swx:Double, swy:Double ) {
	  	val fromx = info(0).x + sox
		val fromy = info(0).y + soy
		val tox   = info(3).x + sox
		val toy   = info(3).y + soy
		val c1x   = info(1).x + sox
		val c1y   = info(1).y + soy
		val c2x   = info(2).x + sox
		val c2y   = info(2).y + soy

		theShape.reset
		theShape.moveTo( fromx, fromy )
		theShape.curveTo( c1x, c1y, c2x, c2y, tox, toy )
	}

	protected def makeShadow(bck:Backend, camera:Camera ) {
		if( info.isCurve )
		     makeMultiOrLoop( camera, theShadowOff.x, theShadowOff.y, theShadowWidth, theShadowWidth )
		else makeSingle( camera, theShadowOff.x, theShadowOff.y, theShadowWidth, theShadowWidth )
	}
	
	def renderShadow(bck:Backend, camera:Camera, element:GraphicElement, info:ElementInfo ) {
 		makeShadow(bck, camera )
 		cast(bck.graphics2D, theShape )
	}
 
	def render(bck:Backend, camera:Camera, element:GraphicElement, info:ElementInfo ) {
	    val g = bck.graphics2D
 		make(bck, camera )
 		stroke( g, theShape )
 		fill( g, theSize, theShape )
 		decorConnector( g, camera, info.iconAndText, element, theShape )
	}
}
object PieChartShape {
	/** Some predefined colors. */
	val colors = Array[Color]( Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW, Color.MAGENTA,
			Color.CYAN, Color.ORANGE, Color.PINK )
}

class PieChartShape
	extends Shape
	with Area
	with FillableMulticolored
	with Strokable 
	with Shadowable 
	with Decorable
	with AttributeUtils {
	
	val theShape = new Ellipse2D.Double
	
	var theValues:Array[Double] = null
	var valuesRef:AnyRef = null

	def configureForGroup(bck:Backend, style:Style, camera:Camera ) {
		configureAreaForGroup( style, camera )
		configureFillableMultiColoredForGroup( style, camera )
		configureStrokableForGroup( style, camera )
		configureShadowableForGroup( style, camera )
		configureDecorableForGroup( style, camera )
	}
	
	def configureForElement(bck:Backend, element:GraphicElement, info:ElementInfo, camera:Camera ) {
	    val g = bck.graphics2D
		configureDecorableForElement( g, camera, element, info )
		configureAreaForElement( g, camera, info.asInstanceOf[NodeInfo], element, theDecor )

		if( element.hasAttribute( "ui.pie-values" ) ) {
			val oldRef = valuesRef
			valuesRef = element.getAttribute( "ui.pie-values" )
			// We use valueRef to avoid
			// recreating the values array for nothing.
			if( ( theValues == null ) || ( oldRef ne valuesRef ) ) {
				theValues = getDoubles( valuesRef )
			}
		}
	}
	
	override def make(bck:Backend, camera:Camera ) {
		theShape.setFrameFromCenter( theCenter.x, theCenter.y, theCenter.x+theSize.x/2, theCenter.y+theSize.y/2 )
	}
	
	override def makeShadow(bck:Backend, camera:Camera ) {
		theShape.setFrameFromCenter( theCenter.x+theShadowOff.x, theCenter.y+theShadowOff.y,
				theCenter.x+(theSize.x+theShadowWidth.x)/2, theCenter.y+(theSize.y+theShadowWidth.y)/2 )
	}
	
	override def renderShadow(bck:Backend, camera:Camera, element:GraphicElement, info:ElementInfo ) {
		makeShadow(bck, camera )
		cast(bck.graphics2D, theShape )
 	}
  
 	override def render(bck:Backend, camera:Camera, element:GraphicElement, info:ElementInfo ) {
 	    val g = bck.graphics2D
 		make(bck, camera )
 		fillPies( g, element )
 		//fill( g, theSize, theShape )
 		stroke( g, theShape )
 		decorArea( g, camera, info.iconAndText, element, theShape )
 	}
 	
 	protected def fillPies( g:Graphics2D, element:GraphicElement ) {
 		if( theValues != null ) {
	 		// we assume the pies values sum up to one. And we wont check it, its a mater of speed ;-).
	 		val arc = new Arc2D.Double
	 		var beg = 0.0
	 		var end = 0.0
	 		var col = 0
	 		var sum = 0.0
	 		
	 		theValues.foreach { value =>
	 			end = beg + value
	 			arc.setArcByCenter( theCenter.x, theCenter.y, theSize.x/2, beg*360, value*360, Arc2D.PIE )
	 			g.setColor( fillColors( col % fillColors.length ) )
	 			g.fill( arc )
	 			beg = end
	 			sum += value
	 			col += 1
	 		}
	 		
	 		if( sum > 1.01f )
	 			Console.err.print( "[Sprite %s] The sum of values for ui.pie-value should eval to 1 at max (actually %f)%n".format( element.getId, sum ) )
 		}
 	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy