
org.graphstream.ui.j2dviewer.renderer.shape.ShapeParts.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._
import org.graphstream.ui.j2dviewer.renderer._
import org.graphstream.ui.util._
import org.graphstream.ui.sgeom._
import org.graphstream.ui.graphicGraph._
import org.graphstream.ui.graphicGraph.stylesheet._
import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants._
/**
* Trait for shapes that can be filled.
*/
trait Fillable {
/** The fill paint. */
var fillPaint:ShapePaint = null
/** Value in [0..1] for dyn-colors. */
var theFillPercent = 0.0;
/**
* Fill the shape.
* @param g The Java2D graphics.
* @param dynColor The value between 0 and 1 allowing to know the dynamic plain color, if any.
* @param shape The awt shape to fill.
*/
def fill( g:Graphics2D, dynColor:Double, shape:java.awt.Shape, camera:Camera ) {
fillPaint match {
case p:ShapeAreaPaint => g.setPaint( p.paint( shape, camera.metrics.ratioPx2Gu ) ); g.fill( shape )
case p:ShapeColorPaint => g.setPaint( p.paint( dynColor ) ); g.fill( shape )
case _ => null; // No fill. // printf( "no fill !!!%n" )
}
}
/**
* Fill the shape.
* @param g The Java2D graphics.
* @param shape The awt shape to fill.
*/
def fill( g:Graphics2D, shape:java.awt.Shape, camera:Camera ) { fill( g, theFillPercent, shape, camera ) }
/**
* Configure all static parts needed to fill the shape.
*/
protected def configureFillableForGroup( style:Style, camera:Camera ) {
fillPaint = ShapePaint( style )
}
/**
* Configure the dynamic parts needed to fill the shape.
*/
protected def configureFillableForElement( style:Style, camera:Camera, element:GraphicElement ) {
if( style.getFillMode == StyleConstants.FillMode.DYN_PLAIN && element != null ) {
element.getAttribute[AnyRef]( "ui.color" ) match {
case x:Number => theFillPercent = x.floatValue
case _ => theFillPercent = 0f
}
} else {
theFillPercent = 0
}
}
}
trait FillableMulticolored {
var fillColors:Array[Color] = null
protected def configureFillableMultiColoredForGroup( style:Style, camera:Camera ) {
val count = style.getFillColorCount
if( fillColors == null || fillColors.length != count ) {
fillColors = new Array[Color]( count )
for( i <- 0 until count )
fillColors( i ) = style.getFillColor( i )
}
}
// protected def configureFillableMulticoloredForElement( g:Graphics2D, element:GraphicElement, info:ElementInfo, camera:Camera ) {
// }
}
/**
* Shape that cannot be filled, but must be stroked.
*/
trait FillableLine {
var fillColors:Array[Color] = null
var fillStroke:ShapeStroke = null
var theFillPercent = 0.0;
def fill( g:Graphics2D, width:Double, dynColor:Double, shape:java.awt.Shape ) {
if( fillStroke != null ) {
val stroke = fillStroke.stroke( width )
g.setColor( fillColors(0) )
g.setStroke( stroke )
g.draw( shape )
}
}
def fill( g:Graphics2D, width:Double, shape:java.awt.Shape ) { fill( g, width, theFillPercent, shape ) }
protected def configureFillableLineForGroup( style:Style, camera:Camera ) {
fillStroke = ShapeStroke.strokeForConnectorFill( style )
}
protected def configureFillableLineForElement( style:Style, camera:Camera, element:GraphicElement ) {
// TODO look at this and try to create the fillColors at the in ForGroup configuration !!!
theFillPercent = 0
if( style.getFillMode == StyleConstants.FillMode.DYN_PLAIN && element != null ) {
element.getAttribute[AnyRef]( "ui.color" ) match {
case x:Number => theFillPercent = x.floatValue
case _ => theFillPercent = 0f
}
fillColors = ShapePaint.createColors( style, style.getFillColorCount, style.getFillColors )
fillColors(0) = ShapePaint.interpolateColor( fillColors, theFillPercent )
}
else
{
if( fillColors == null || fillColors.length < 1 )
fillColors = new Array[Color]( 1 )
fillColors(0) = style.getFillColor( 0 )
}
}
}
/**
* Trait for shapes that can be stroked.
*/
trait Strokable {
/** The stroke color. */
var strokeColor:Color = null
/** The stroke. */
var theStroke:ShapeStroke = null
/** The stroke width. */
var theStrokeWidth = 0.0
/** Paint the stroke of the shape. */
def stroke( g:Graphics2D, shape:java.awt.Shape ) {
if( theStroke != null ) {
g.setStroke( theStroke.stroke( theStrokeWidth ) )
g.setColor( strokeColor )
g.draw( shape )
}
}
/** Configure all the static parts needed to stroke the shape. */
protected def configureStrokableForGroup( style:Style, camera:Camera ) {
theStrokeWidth = camera.metrics.lengthToGu( style.getStrokeWidth )
/*if( strokeColor == null )*/ strokeColor = ShapeStroke.strokeColor( style )
/*if( theStroke == null )*/ theStroke = ShapeStroke.strokeForArea( style )
}
}
/** Trait for strokable lines. */
trait StrokableLine extends Strokable {
protected override def configureStrokableForGroup( style:Style, camera:Camera ) {
theStrokeWidth = camera.metrics.lengthToGu( style.getStrokeWidth ) + camera.metrics.lengthToGu( style.getSize, 0 )
strokeColor = ShapeStroke.strokeColor( style )
theStroke = ShapeStroke.strokeForArea( style )
}
protected def configureStrokableLineForGroup( style:Style, camera:Camera ) { configureStrokableForGroup( style, camera ) }
}
/**
* Trait for shapes that can cast a shadow.
*/
trait Shadowable {
/** The shadow paint. */
var shadowPaint:ShapePaint = null
/** Additional width of a shadow (added to the shape size). */
protected val theShadowWidth = new Point2
/** Offset of the shadow according to the shape center. */
protected val theShadowOff = new Point2
/** Sety the shadow width added to the shape width. */
def shadowWidth( width:Double, height:Double ) { theShadowWidth.set( width, height ) }
/** Set the shadow offset according to the shape. */
def shadowOffset( xoff:Double, yoff:Double ) { theShadowOff.set( xoff, yoff ) }
/**
* Render the shadow.
* @param g The Java2D graphics.
*/
def cast( g:Graphics2D, shape:java.awt.Shape ) {
shadowPaint match {
case p:ShapeAreaPaint => g.setPaint( p.paint( shape, 1 ) ); g.fill( shape )
case p:ShapeColorPaint => g.setPaint( p.paint( 0 ) ); g.fill( shape )
case _ => null; printf( "no shadow !!!%n" )
}
}
/** Configure all the static parts needed to cast the shadow of the shape. */
protected def configureShadowableForGroup( style:Style, camera:Camera ) {
theShadowWidth.x = camera.metrics.lengthToGu( style.getShadowWidth )
theShadowWidth.y = theShadowWidth.x
theShadowOff.x = camera.metrics.lengthToGu( style.getShadowOffset, 0 )
theShadowOff.y = if( style.getShadowOffset.size > 1 ) camera.metrics.lengthToGu( style.getShadowOffset, 1 ) else theShadowOff.x
/*if( shadowPaint == null )*/ shadowPaint = ShapePaint( style, true )
}
}
trait ShadowableLine {
/** The shadow paint. */
var shadowStroke:ShapeStroke = null
/** Additional width of a shadow (added to the shape size). */
protected var theShadowWidth = 0.0
/** Offset of the shadow according to the shape center. */
protected val theShadowOff = new Point2
protected var theShadowColor:Color = null
/** Sety the shadow width added to the shape width. */
def shadowWidth( width:Double ) { theShadowWidth = width }
/** Set the shadow offset according to the shape. */
def shadowOffset( xoff:Double, yoff:Double ) { theShadowOff.set( xoff, yoff ) }
/**
* Render the shadow.
* @param g The Java2D graphics.
*/
def cast( g:Graphics2D, shape:java.awt.Shape ) {
g.setColor( theShadowColor )
g.setStroke( shadowStroke.stroke( theShadowWidth ) )
g.draw( shape )
}
/** Configure all the static parts needed to cast the shadow of the shape. */
protected def configureShadowableLineForGroup( style:Style, camera:Camera ) {
theShadowWidth = camera.metrics.lengthToGu( style.getSize, 0 ) +
camera.metrics.lengthToGu( style.getShadowWidth ) +
camera.metrics.lengthToGu( style.getStrokeWidth )
theShadowOff.x = camera.metrics.lengthToGu( style.getShadowOffset, 0 )
theShadowOff.y = if( style.getShadowOffset.size > 1 ) camera.metrics.lengthToGu( style.getShadowOffset, 1 ) else theShadowOff.x
theShadowColor = style.getShadowColor( 0 )
shadowStroke = ShapeStroke.strokeForConnectorFill( style )
}
}
/**
* Trait for shapes that can be decorated by an icon and/or a text.
*/
trait Decorable {
var text:String = null
/** The text and icon. */
var theDecor:ShapeDecor = null
/** Paint the decorations (text and icon). */
def decorArea( g:Graphics2D, camera:Camera, iconAndText:IconAndText, element:GraphicElement, shape:java.awt.Shape ) {
var visible = true
if( element != null ) visible = camera.isTextVisible( element )
if( theDecor != null && visible ) {
val bounds = shape.getBounds2D
theDecor.renderInside( g, camera, iconAndText, bounds.getMinX, bounds.getMinY, bounds.getMaxX, bounds.getMaxY )
}
}
def decorConnector( g:Graphics2D, camera:Camera, iconAndText:IconAndText, element:GraphicElement, shape:java.awt.Shape ) {
var visible = true
if( element != null ) visible = camera.isTextVisible( element )
if( theDecor != null && visible ) {
element match {
case edge:GraphicEdge => {
theDecor.renderAlong( g, camera, iconAndText, edge.from.x, edge.from.y, edge.to.x, edge.to.y )
}
case _ => {
val bounds = shape.getBounds2D
theDecor.renderAlong( g, camera, iconAndText, bounds.getMinX, bounds.getMinY, bounds.getMaxX, bounds.getMaxY )
}
}
}
}
/** Configure all the static parts needed to decor the shape. */
protected def configureDecorableForGroup( style:Style, camera:Camera ) {
/*if( theDecor == null )*/ theDecor = ShapeDecor( style )
}
/** Setup the parts of the decor specific to each element. */
protected def configureDecorableForElement( g:Graphics2D, camera:Camera, element:GraphicElement, info:ElementInfo ) {
text = element.label
if( info != null ) {
val style = element.getStyle
if( info.iconAndText == null )
info.iconAndText = ShapeDecor.iconAndText( style, camera, element )
if( style.getIcon != null && style.getIcon.equals( "dynamic" ) && element.hasAttribute( "ui.icon" ) ) {
val url = element.getLabel("ui.icon").toString
info.iconAndText.setIcon( g, url )
// Console.err.printf( "changing icon %s%n", url )
}
// else Console.err.print( "NOT changing icon... %b %s %b%n".format( style.getIcon != null, style.getIcon, element.hasAttribute( "ui.icon" ) ) )
info.iconAndText.setText( g, element.label )
}
}
}
trait Orientable {
var orientation:StyleConstants.SpriteOrientation = null
var target = new Point3
protected def configureOrientableForGroup( style:Style, camera:Camera ) {
orientation = style.getSpriteOrientation
}
protected def configureOrientableForElement( camera:Camera, sprite:GraphicSprite ) {
sprite.getAttachment match {
case gn:GraphicNode => {
sprite.getStyle.getSpriteOrientation match {
case SpriteOrientation.NONE => { target.set( 0, 0 ) }
case SpriteOrientation.FROM => { target.set( gn.getX, gn.getY ) }
case SpriteOrientation.TO => { target.set( gn.getX, gn.getY ) }
case SpriteOrientation.PROJECTION => { target.set( gn.getX, gn.getY ) }
}
}
case ge:GraphicEdge => {
sprite.getStyle.getSpriteOrientation match {
case SpriteOrientation.NONE => { target.set( 0, 0 ) }
case SpriteOrientation.FROM => { target.set( ge.from.getX, ge.from.getY ) }
case SpriteOrientation.TO => { target.set( ge.to.getX, ge.to.getY ) }
case SpriteOrientation.PROJECTION => {
val ei = ge.getAttribute[EdgeInfo]( ElementInfo.attributeName )
if( ei != null )
ei.pointOnShape(sprite.getX, target)//setTargetOnEdgeInfo( ei, camera, sprite, ge )
else setTargetOnLineEdge( camera, sprite, ge )
}
}
}
case _ => { orientation = SpriteOrientation.NONE }
}
}
// private def setTargetOnEdgeInfo( ei:EdgeInfo, camera:Camera, sprite:GraphicSprite, ge:GraphicEdge ) {
// ei.pointOnShape(sprite.getX, target)
// if( ei.isCurve ) {
// CubicCurve.eval( ei(0), ei(1), ei(2), ei(3), sprite.getX, target )
// } else if( ei.isPoly ) {
//
// } else {
// setTargetOnLineEdge( camera, sprite, ge )
// }
// }
private def setTargetOnLineEdge( camera:Camera, sprite:GraphicSprite, ge:GraphicEdge ) {
val dir = Vector2( ge.to.getX-ge.from.getX, ge.to.getY-ge.from.getY )
dir.scalarMult( sprite.getX )
target.set( ge.from.getX + dir.x, ge.from.getY + dir.y )
}
}
/**
* Trait for elements painted inside an area.
*/
trait Area {
protected val theCenter = new Point2
protected val theSize = new Point2
protected var fit = false
protected def configureAreaForGroup( style:Style, camera:Camera ) {
size( style, camera )
}
protected def configureAreaForElement( g:Graphics2D, camera:Camera, info:NodeInfo, element:GraphicElement, x:Double, y:Double ) {
dynSize( element.getStyle, camera, element )
positionAndFit( g, camera, info, element, x, y, 0, 0 )
}
protected def configureAreaForElement( g:Graphics2D, camera:Camera, info:NodeInfo, element:GraphicElement, x:Double, y:Double, contentOverallWidth:Double, contentOverallHeight:Double ) {
dynSize( element.getStyle, camera, element )
positionAndFit( g, camera, info, element, x, y, contentOverallWidth, contentOverallHeight )
}
protected def configureAreaForElement( g:Graphics2D, camera:Camera, info:NodeInfo, element:GraphicElement, decor:ShapeDecor ) {
var pos = camera.getNodeOrSpritePositionGU( element, null )
if( fit ) {
val decorSize = decor.size( g, camera, info.iconAndText )
configureAreaForElement( g, camera, info.asInstanceOf[NodeInfo], element, pos.x, pos.y, decorSize._1, decorSize._2 )
} else {
configureAreaForElement( g, camera, info.asInstanceOf[NodeInfo], element, pos.x, pos.y )
}
}
private def size( width:Double, height:Double ) { theSize.set( width, height ) }
private def size( style:Style, camera:Camera ) {
val w = camera.metrics.lengthToGu( style.getSize, 0 )
val h = if( style.getSize.size > 1 ) camera.metrics.lengthToGu( style.getSize, 1 ) else w
theSize.set( w, h )
fit = ( style.getSizeMode == StyleConstants.SizeMode.FIT )
}
private def dynSize( style:Style, camera:Camera, element:GraphicElement ) {
var w = camera.metrics.lengthToGu( style.getSize, 0 )
var h = if( style.getSize.size > 1 ) camera.metrics.lengthToGu( style.getSize, 1 ) else w
if( element.hasAttribute( "ui.size" ) ) {
w = camera.metrics.lengthToGu( StyleConstants.convertValue( element.getAttribute( "ui.size" ) ) )
h = w;
}
theSize.set( w, h )
}
protected def positionAndFit( g:Graphics2D, camera:Camera, info:NodeInfo, element:GraphicElement, x:Double, y:Double, contentOverallWidth:Double, contentOverallHeight:Double ) {
if( info != null ) {
if( contentOverallWidth > 0 && contentOverallHeight > 0 )
theSize.set( contentOverallWidth, contentOverallHeight )
info.theSize.copy( theSize )
}
theCenter.set( x, y )
}
}
/**
* Trait for elements painted between two points.
*
* The purpose of this class is to store the lines coordinates of an edge. This connector can
* be made of only two points, 4 points when this is a bezier curve or more if this is a polyline.
* The coordinates of these points are stored in a EdgeInfo attribute directly on the edge element
* since several parts of the rendering need to access it (for example, sprites retrieve it
* to follow the correct path when attached to this edge).
*/
trait Connector {
// Attribute
var info:EdgeInfo = null
/** Width of the connector. */
protected var theSize:Double = 0
protected var theTargetSizeX = 0.0
protected var theTargetSizeY = 0.0
protected var theSourceSizeX = 0.0
protected var theSourceSizeY = 0.0
/** Is the connector directed ? */
var isDirected = false
// Command
/** Origin point of the connector. */
def fromPos:Point3 = info.from
/** First control point. Works only for curves. */
def byPos1:Point3 = if(info.isCurve) info(1) else null
/** Second control point. Works only for curves. */
def byPos2:Point3 = if(info.isCurve) info(2) else null
/** Destination of the connector. */
def toPos:Point3 = info.to
def configureConnectorForGroup( style:Style, camera:Camera ) {
size( style, camera )
}
def configureConnectorForElement( g2:Graphics2D, camera:Camera, element:GraphicEdge, info:EdgeInfo ) {
this.info = info
dynSize( element.getStyle, camera, element )
endPoints( element.from, element.to, element.isDirected, camera )
if(element.getGroup != null) {
info.setMulti(element.getGroup.getCount)
}
if(element.hasAttribute("ui.points")) {
info.setPoly(element.getAttribute("ui.points").asInstanceOf[AnyRef])
} else {
positionForLinesAndCurves( info, element.from.getStyle, element.from.getX, element.from.getY,
element.to.getX, element.to.getY, element.multi, element.getGroup )
}
}
/** Set the size (`width`) of the connector. */
private def size( width:Double ) { theSize = width }
/** Set the size of the connector using a predefined style. */
private def size( style:Style, camera:Camera ) { size( camera.metrics.lengthToGu( style.getSize, 0 ) ) }
private def dynSize( style:Style, camera:Camera, element:GraphicElement ) {
var w = theSize // already set by the configureForGroup() //camera.metrics.lengthToGu( style.getSize, 0 )
if( element.hasAttribute( "ui.size" ) ) {
w = camera.metrics.lengthToGu( StyleConstants.convertValue( element.getAttribute( "ui.size" ) ) )
}
size( w )
}
/** Define the two end points sizes using the fit size stored in the nodes. */
private def endPoints( from:GraphicNode, to:GraphicNode, directed:Boolean, camera:Camera ) {
val fromInfo = from.getAttribute( ElementInfo.attributeName ).asInstanceOf[NodeInfo]
val toInfo = to.getAttribute( ElementInfo.attributeName ).asInstanceOf[NodeInfo]
if( fromInfo != null && toInfo != null ) {
//Console.err.printf( "Using the dynamic size%n" )
isDirected = directed
theSourceSizeX = fromInfo.theSize.x
theSourceSizeY = fromInfo.theSize.y
theTargetSizeX = toInfo.theSize.x
theTargetSizeY = toInfo.theSize.y
} else {
//Console.err.printf( "NOT using the dynamic size :-(%n" )
endPoints( from.getStyle, to.getStyle, directed, camera )
}
}
/** Define the two end points sizes (does not use the style nor the fit size). */
private def endPoints( sourceWidth:Double, targetWidth:Double, directed:Boolean ) {
theSourceSizeX = sourceWidth
theSourceSizeY = sourceWidth
theTargetSizeX = targetWidth
theTargetSizeY = targetWidth
isDirected = directed
}
/** Define the two end points sizes (does not use the style nor the fit size). */
private def endPoints( sourceWidth:Double, sourceHeight:Double, targetWidth:Double, targetHeight:Double, directed:Boolean ) {
theSourceSizeX = sourceWidth
theSourceSizeY = sourceHeight
theTargetSizeX = targetWidth
theTargetSizeY = targetHeight
isDirected = directed
}
/** Compute the two end points sizes using the style (may not use the fit size). */
private def endPoints( sourceStyle:Style, targetStyle:Style, directed:Boolean, camera:Camera ) {
theSourceSizeX = camera.metrics.lengthToGu( sourceStyle.getSize, 0 )
if( sourceStyle.getSize.size > 1 )
theSourceSizeY = camera.metrics.lengthToGu( sourceStyle.getSize, 1 )
else theSourceSizeY = theSourceSizeX
theTargetSizeX = camera.metrics.lengthToGu( targetStyle.getSize, 0 )
if( targetStyle.getSize.size > 1 )
theTargetSizeY = camera.metrics.lengthToGu( targetStyle.getSize, 1 )
else theTargetSizeY = theTargetSizeX
isDirected = directed
}
/** Give the position of the origin and destination points. */
private def positionForLinesAndCurves( info:EdgeInfo, style:Style, xFrom:Double, yFrom:Double, xTo:Double, yTo:Double ) {
positionForLinesAndCurves( info, style, xFrom, yFrom, xTo, yTo, 0, null ) }
/**
* Give the position of the origin and destination points, for multi edges.
*
* This only sets the isCurve/isLoop and ctrl1/ctrl2 for multi-edges/edge-loops, if the shape of the
* edge given by the style is also a curve, the make() methods must set these fields (we cannot do it here
* since we do not know the curves). This is important since arrows and sprites can be attached to edges.
*
*/
private def positionForLinesAndCurves( info:EdgeInfo, style:Style, xFrom:Double, yFrom:Double, xTo:Double, yTo:Double, multi:Int, group:GraphicEdge#EdgeGroup ) {
//info.points(0).set( xFrom, yFrom )
//info.points(3).set( xTo, yTo )
if( group != null ) {
if( xFrom == xTo && yFrom == yTo ) {
positionEdgeLoop(info, xFrom, yFrom, multi)
} else {
positionMultiEdge(info, xFrom, yFrom, xTo, yTo, multi, group)
}
} else {
if( xFrom == xTo && yFrom == yTo ) {
positionEdgeLoop(info, xFrom, yFrom, 0)
} else {
// This does not mean the edge is not a curve, this means
// that with what we know actually it is not a curve.
// The style mays indicate a curve.
info.setLine(xFrom, yFrom, 0, xTo, yTo, 0)
// XXX we will have to mutate the info into a curve later.
}
}
}
/** Define the control points to make the edge a loop. */
private def positionEdgeLoop(info:EdgeInfo, x:Double, y:Double, multi:Int) {
var m = 1f + multi * 0.2f
val s = ( theTargetSizeX + theTargetSizeY ) / 2
var d = s / 2 * m + 4 * s * m
info.setLoop(
x, y, 0,
x+d, y, 0,
x, y+d, 0 )
}
/** Define the control points to make this edge a part of a multi-edge. */
private def positionMultiEdge(info:EdgeInfo, x1:Double, y1:Double, x2:Double, y2:Double, multi:Int, group:GraphicEdge#EdgeGroup) {
var vx = ( x2 - x1 )
var vy = ( y2 - y1 )
var vx2 = ( vy ) * 0.6
var vy2 = ( -vx ) * 0.6
val gap = 0.2
var ox = 0.0
var oy = 0.0
val f = ( ( 1 + multi ) / 2 ) * gap // (1+multi)/2 must be done on integers.
vx *= 0.2
vy *= 0.2
val main = group.getEdge( 0 )
val edge = group.getEdge( multi )
if( group.getCount %2 == 0 ) {
ox = vx2 * (gap/2)
oy = vy2 * (gap/2)
if( edge.from ne main.from ) { // Edges are in the same direction.
ox = - ox
oy = - oy
}
}
vx2 *= f
vy2 *= f
var xx1 = x1 + vx
var yy1 = y1 + vy
var xx2 = x2 - vx
var yy2 = y2 - vy
val m = multi + ( if( edge.from eq main.from ) 0 else 1 )
if( m % 2 == 0 ) {
xx1 += ( vx2 + ox )
yy1 += ( vy2 + oy )
xx2 += ( vx2 + ox )
yy2 += ( vy2 + oy )
} else {
xx1 -= ( vx2 - ox )
yy1 -= ( vy2 - oy )
xx2 -= ( vx2 - ox )
yy2 -= ( vy2 - oy )
}
info.setCurve(
x1, y1, 0,
xx1, yy1, 0,
xx2, yy2, 0,
x2, y2, 0 )
}
}
trait AreaOnConnector extends Area {
protected var theConnector:Connector = null
protected var theEdge:GraphicEdge = null
/** XXX must call this method explicitly in the renderer !!! bad !!! XXX */
def theConnectorYoureAttachedTo( connector:Connector ) { theConnector = connector }
protected def configureAreaOnConnectorForGroup( style:Style, camera:Camera ) {
sizeForEdgeArrow( style, camera )
}
protected def configureAreaOnConnectorForElement( edge:GraphicEdge, style:Style, camera:Camera ) {
connector( edge )
theCenter.set( edge.to.getX, edge.to.getY )
}
private def connector( edge:GraphicEdge ) { theEdge = edge }
private def sizeForEdgeArrow( style:Style, camera:Camera ) {
val w = camera.metrics.lengthToGu( style.getArrowSize, 0 )
val h = if( style.getArrowSize.size > 1 ) camera.metrics.lengthToGu( style.getArrowSize, 1 ) else w
theSize.set( w, h )
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy