uk.org.retep.util.graphics.Join Maven / Gradle / Ivy
/*
* Copyright (c) 1998-2010, Peter T Mount
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the retep.org.uk nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package uk.org.retep.util.graphics;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import uk.org.retep.swing.model.EnumModel;
import uk.org.retep.util.string.StringUtils;
/**
* An enum defining how two entities are joined.
*
* @author peter
*/
public enum Join
implements EnumModel.Renderable
{
CUBIC( 0 ),
QUAD( 0 ),
STRAIGHT( 0 ),
DIRECT( 0 ),
CUBIC_DASHED( 1 ),
QUAD_DASHED( 1 ),
STRAIGHT_DASHED( 1 ),
DIRECT_DASHED( 1 ),
CUBIC_DOTTED( 2 ),
QUAD_DOTTED( 2 ),
STRAIGHT_DOTTED( 2 ),
DIRECT_DOTTED( 2 );
private int mode;
private boolean fillable;
Join( int mode )
{
this.mode = mode;
fillable = mode == 0;
}
/**
* Draw the join between two Shapes
*
* If bottom = false, [From] --> [To]
* If bottom = true,
* [From]
* |
* V
* [To]
*
* @param g Graphics
* @param orientation Orientation of the graph
* @param bottom true for NORMAL to draw from bottom of from, false for right of from
* @param fromShape
* @param toShape
* @param fromJoin JoinEnd on start
* @param toJoin JoinEnd on end
*/
public final void draw( Graphics g,
Orientation orientation, boolean bottom,
Shape fromShape,
JoinEnd fromJoin, Shape toShape,
JoinEnd toJoin )
{
// Bounds
Rectangle from = fromShape.getBounds();
Rectangle to = toShape.getBounds();
// Centres
Point2D fc = GeomUtils.getCentre( fromShape );
Point2D tc = GeomUtils.getCentre( toShape );
double x1;
double y1;
double x2;
double y2;
double x3;
double y3;
double x4;
double y4;
if( bottom )
{
// Join bottom to top
switch( orientation )
{
case FLIP_VERTICAL:
case INVERTED:
y1 = from.getY();
y4 = to.getY() + to.getHeight();
y2 = y3 = y4 + ((y1 - y4) / 2.0);
break;
default:
y1 = from.getY() + from.getHeight();
y4 = to.getY();
y2 = y3 = y1 + ((y4 - y1) / 2.0);
break;
}
x1 = x2 = fc.getX();
x3 = x4 = tc.getX();
}
else
{
// Join right to left edges
switch( orientation )
{
case FLIP_HORIZONTAL:
case INVERTED:
x1 = from.getX();
x4 = to.getX() + to.getWidth();
x2 = x3 = x4 + ((x1 - x4) / 2.0);
break;
default:
x1 = from.getX() + from.getWidth();
x4 = to.getX();
x2 = x3 = x1 + ((x4 - x1) / 2.0);
break;
}
y1 = y2 = fc.getY();
y3 = y4 = tc.getY();
}
// Now correct the start/end points so they intersect the shape
Point2D pt = new Point2D.Double( x1, y1 );
Line2D line = new Line2D.Double( pt, fc );
Point2D np =
GeomUtils.findIntersection( line, fromShape );
if( np != null )
{
x1 = (int) np.getX();
y1 = (int) np.getY();
}
pt.setLocation( x4, y4 );
line = new Line2D.Double( pt, tc );
np = GeomUtils.findIntersection( line, toShape );
if( np != null )
{
x4 = (int) np.getX();
y4 = (int) np.getY();
}
draw( g, x1, y1, x2, y2, x3, y3, x4, y4, fromJoin, toJoin );
}
/**
* Draw the join between two points with an arrow at the end
*
* @param g Graphics
* @param x1 Start x
* @param y1 Start y
* @param x2 first intermediary x
* @param y2 first intermediary x
* @param x3 second intermediary x
* @param y3 second intermediary x
* @param x4 end x
* @param y4 end y
* @param fromJoin JoinEnd on start
* @param toJoin JoinEnd on end
*/
public final void draw( Graphics g, double x1, double y1, double x2,
double y2, double x3, double y3, double x4,
double y4,
JoinEnd fromJoin,
JoinEnd toJoin )
{
draw( (Graphics2D) g, x1, y1, x2, y2, x3, y3, x4, y4, fromJoin, toJoin );
}
/**
* Draw the join between two points with an arrow at the end
*
* @param g Graphics2D
* @param x1 Start x
* @param y1 Start y
* @param x2 first intermediary x
* @param y2 first intermediary x
* @param x3 second intermediary x
* @param y3 second intermediary x
* @param x4 end x
* @param y4 end y
* @param fromJoin JoinEnd on start
* @param toJoin JoinEnd on end
*/
public final void draw( Graphics2D g, double x1, double y1, double x2,
double y2, double x3, double y3, double x4,
double y4,
JoinEnd fromJoin,
JoinEnd toJoin )
{
draw( g, x1, y1, x2, y2, x3, y3, x4, y4, fromJoin, toJoin, null, null );
}
/**
* Draw the join between two points with an arrow at the end
*
* @param g Graphics2D
* @param x1 Start x
* @param y1 Start y
* @param x2 first intermediary x
* @param y2 first intermediary x
* @param x3 second intermediary x
* @param y3 second intermediary x
* @param x4 end x
* @param y4 end y
* @param fromJoin JoinEnd on start
* @param toJoin JoinEnd on end
* @param color colour of the Join
*/
public final void draw( Graphics2D g, double x1, double y1, double x2,
double y2, double x3, double y3, double x4,
double y4,
JoinEnd fromJoin,
JoinEnd toJoin, Color color )
{
draw( g, x1, y1, x2, y2, x3, y3, x4, y4, fromJoin, toJoin, color, color );
}
/**
* Draw the join between two points with an arrow at the end
*
* @param g Graphics2D
* @param x1 Start x
* @param y1 Start y
* @param x2 first intermediary x
* @param y2 first intermediary x
* @param x3 second intermediary x
* @param y3 second intermediary x
* @param x4 end x
* @param y4 end y
* @param fromJoin JoinEnd on start
* @param toJoin JoinEnd on end
* @param borderColor colour of the Join border
* @param fillColor colour of the Join fill
*/
public final void draw( Graphics2D g, double x1, double y1, double x2,
double y2, double x3, double y3, double x4,
double y4,
JoinEnd fromJoin,
JoinEnd toJoin, Color borderColor,
Color fillColor )
{
Graphics2D g2 = (Graphics2D) g.create();
try
{
GeneralPath p = new GeneralPath();
if( fillable )
{
appendPath( p, (float) x1, (float) y1, (float) x2, (float) y2,
(float) x3, (float) y3, (float) x4, (float) y4,
fromJoin, toJoin, true );
if( fillColor != null )
{
g2.setColor( fillColor );
}
else if( borderColor != null )
{
g2.setColor( borderColor );
}
g2.fill( p );
p.reset();
}
appendPath( p, (float) x1, (float) y1, (float) x2, (float) y2,
(float) x3, (float) y3, (float) x4, (float) y4, fromJoin,
toJoin, false );
if( borderColor != null )
{
g2.setColor( borderColor );
}
switch( mode )
{
case 0:
break;
case 1:
GeomUtils.setDashStroke( g2, 8.0f, 4.0f );
break;
case 2:
GeomUtils.setDashStroke( g2, 4.0f, 4.0f );
break;
}
g2.draw( p );
}
finally
{
g2.dispose();
}
}
/**
* Append the join to an existing GeneralPath
*
* @param p GeneralPath
* @param x1 Start x
* @param y1 Start y
* @param x2 first intermediary x
* @param y2 first intermediary x
* @param x3 second intermediary x
* @param y3 second intermediary x
* @param x4 end x
* @param y4 end y
* @param fromJoin JoinEnd on start
* @param toJoin JoinEnd on end
* @param forFill
* @return GeneralPath p
*/
public final GeneralPath appendPath( GeneralPath p, float x1, float y1,
float x2, float y2, float x3, float y3,
float x4, float y4,
JoinEnd fromJoin,
JoinEnd toJoin, boolean forFill )
{
if( p == null )
{
p = new GeneralPath();
}
// x1a, y1a is the start control point
Point2D pt = fromJoin.getJoinPoint( x1, y1, x2, y2 );
float x1a = (float) pt.getX();
float y1a = (float) pt.getY();
// x4a, y4a is the end control point
pt = toJoin.getJoinPoint( x4, y4, x3, y3 );
float x4a = (float) pt.getX();
float y4a = (float) pt.getY();
// Now append the fromJoin JoinEnd
p = fromJoin.appendPath( p, x1, y1, x1a, y1a, false, forFill );
switch( this )
{
case STRAIGHT:
case STRAIGHT_DASHED:
case STRAIGHT_DOTTED:
default:
p.lineTo( x2, y2 );
p.lineTo( x3, y3 );
p.lineTo( x4a, y4a );
break;
case DIRECT:
case DIRECT_DASHED:
case DIRECT_DOTTED:
p.lineTo( x4a, y4a );
break;
case CUBIC:
case CUBIC_DASHED:
case CUBIC_DOTTED:
p.curveTo( x2, y2, x3, y3, x4a, y4a );
break;
case QUAD:
case QUAD_DASHED:
case QUAD_DOTTED:
p.quadTo( x2, y2, x2 + ((x3 - x2) / 2.0f),
y2 + ((y3 - y2) / 2.0f) );
p.quadTo( x3, y3, x4a, y4a );
break;
}
p = toJoin.appendPath( p, x4, y4, x4a, y4a, true, forFill );
if( forFill )
{
// Back over the join (for filling)
switch( this )
{
case STRAIGHT:
case STRAIGHT_DASHED:
case STRAIGHT_DOTTED:
default:
p.lineTo( x3, y3 );
p.lineTo( x2, y2 );
p.lineTo( x1a, y1a );
break;
case DIRECT:
case DIRECT_DASHED:
case DIRECT_DOTTED:
p.lineTo( x1a, y1a );
break;
case CUBIC:
case CUBIC_DASHED:
case CUBIC_DOTTED:
p.curveTo( x3, y3, x2, y2, x1a, y1a );
break;
case QUAD:
case QUAD_DASHED:
case QUAD_DOTTED:
p.quadTo( x3, y3, x2 + ((x3 - x2) / 2.0f),
y2 + ((y3 - y2) / 2.0f) );
p.quadTo( x2, y2, x1a, y1a );
break;
}
// Move to the end (contract that the path will end at the end of
// the Join
//p.moveTo( x4, y4 );
}
return p;
}
public String getDescription()
{
return StringUtils.capitaliseEnum( toString() );
}
public void render( Graphics2D g, int w, int h )
{
double x1 = ((double) w - 8.0) / 3.0;
double x2 = x1 + x1;
double x3 = x2 + x1;
double y1 = (double) h / 3.0;
double y2 = y1 + y1;
draw( g, x1, y1, x2, y1, x2, y2, x3, y2,
JoinEnd.NONE, JoinEnd.NONE );
}
}