package DDrawing;
import java.awt.*;
import java.awt.geom.AffineTransform;
import javax.swing.BorderFactory;
import javax.swing.border.*;
/*---------------------- class declaration -----------------------------------*/
/**
* ScaledBorder puts an border around Components ( especially around DrawingAreas ) with scaled and labeled x- and y-axes
*/
public class ScaledBorder implements Border{
private boolean under_construction = false;
/**
* length of the distance markers on the axes in pixels
*/
int marker_length = 2;
/**
* length in pixels of the arrows at the ends of the axes
*/
int arrow_length = 10;
/**
* a flag if the arrows should be visible
*/
public boolean show_arrows = true;
/**
* distance between the x-values in digits
*/
int x_value2value = 2;
/**
* distance between y-label and y-values in digit width
*/
int y_label2values = 1;
/**
* distance between y-values and y-axis markers in parts of the digit width
*/
int y_values2marker = 2;
/**
* distance between values and arrows in pixels
*/
int x_values2arrow = 10 ;
/**
* distance between values and arrows in pixels
*/
int y_values2arrow = 10 ;
/**
* distance between arrows and outer border
*/
int axis2border = 4;
/**
* distance between labels and the border in pixels
*/
public int x_label2border = 6 ;
/**
* distance between labels and the border in pixels
*/
public int y_label2border = 6 ;
/**
* the size of the shown rectangle
*/
double src_minx = 0 ;
/**
* the size of the shown rectangle
*/
double src_maxx = 1 ;
/**
* the size of the shown rectangle
*/
double src_miny = 0 ;
/**
* the size of the shown rectangle
*/
double src_maxy = 1 ;
/**
* the minimal increment of the scales
*/
public double minimal_increment = 0;
/**
* the displayed labels
*/
public String x_label;
/**
* the displayed labels
*/
public String y_label;
/**
* foreground and background colors
*/
public Color foreground;
/**
* foreground and background colors
*/
public Color background;
/**
* the border which is shown around the scaled border
*/
Border outer_border;
/**
* flag if the outer border should be displayed
*/
boolean show_outer_border = true;
public DFunction x_scale = null ;
public DFunction y_scale = null ;
private double src_dX = - 1 ;
private double src_dY = - 1 ;
private boolean do_refresh;
private Insets old_insets;
private DMeasures m;
/**
* constructor creates a default ScaledBorder inside of a lowered BevelBorder
*/
public ScaledBorder(){
this(
BorderFactory.createBevelBorder(
BevelBorder.LOWERED,
Color.white,
Color.lightGray,
Color.black,
Color.lightGray
)
);
}
/**
* constructor creates a new <code>ScaledBorder<code/>
* surrounded by the specified <code>Border<code/>
*/
public ScaledBorder( Border outer ){
outer_border = outer;
m = new DMeasures( this );
}
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height){
if( under_construction ) System.out.println("ScaledBorder.paintBorder()");
if( foreground == null ) foreground = c.getForeground();
if( background == null ) background = c.getBackground();
Color old_color = g.getColor();
g.setColor( background );
g.fillRect( x, y, width, height );
g.setColor( old_color );
Insets outer_insets = new Insets( 0, 0, 0, 0);// insets of the outer border
if( show_outer_border ) {
outer_border.paintBorder( c, g, x, y, width, height );
outer_insets = outer_border.getBorderInsets( c );
}
do_refresh = true;
Insets inner_insets = getBorderInsets(c);
Dimension d = c.getSize(),
cd = new Dimension( d.width - inner_insets.left - inner_insets.right,
d.height - inner_insets.top - inner_insets.bottom );
FontMetrics fm = g.getFontMetrics();
int fontAsc = fm.getAscent();
do_refresh = false;
m.update(c, inner_insets);
// axes
g.setColor( foreground );
g.drawLine( inner_insets.left, inner_insets.top,
inner_insets.left, inner_insets.top + cd.height );
g.drawLine( inner_insets.left, inner_insets.top + cd.height,
inner_insets.left + cd.width, inner_insets.top + cd.height );
if( show_arrows ){
g.drawLine( inner_insets.left, inner_insets.top,
inner_insets.left, inner_insets.top - y_values2arrow );
g.drawLine( inner_insets.left - marker_length, inner_insets.top - y_values2arrow,
inner_insets.left, inner_insets.top - y_values2arrow - arrow_length );
g.drawLine( inner_insets.left + marker_length, inner_insets.top - y_values2arrow,
inner_insets.left, inner_insets.top - y_values2arrow - arrow_length);
g.drawLine( inner_insets.left - marker_length, inner_insets.top - y_values2arrow,
inner_insets.left + marker_length, inner_insets.top - y_values2arrow );
g.drawLine( inner_insets.left + cd.width , inner_insets.top + cd.height,
inner_insets.left + cd.width + x_values2arrow, inner_insets.top + cd.height );
g.drawLine( inner_insets.left + cd.width + x_values2arrow,
inner_insets.top + cd.height - marker_length,
inner_insets.left + cd.width + x_values2arrow + arrow_length,
inner_insets.top + cd.height );
g.drawLine( inner_insets.left + cd.width + x_values2arrow,
inner_insets.top + cd.height + marker_length,
inner_insets.left + cd.width + x_values2arrow + arrow_length,
inner_insets.top + cd.height );
g.drawLine( inner_insets.left + cd.width + x_values2arrow,
inner_insets.top + cd.height - marker_length,
inner_insets.left + cd.width + x_values2arrow,
inner_insets.top + cd.height + marker_length );
}
if( y_label != null ) {
Dimension yld = new Dimension(fm.getAscent()+fm.getDescent(), fm.stringWidth(y_label));
AffineTransform T = new AffineTransform(0, -1, 1, 0, 0, 0);
Font old = g.getFont(), f = old.deriveFont( T );
g.setFont( f );
g.drawString( y_label, y_label2border + fm.getAscent(), inner_insets.top + ( cd.height + yld.height )/ 2 );
g.setFont( old );
}
if( x_label != null )
g.drawString(
x_label, inner_insets.left + ( cd.width - fm.stringWidth( x_label ) ) / 2,
d.height - outer_insets.bottom - x_label2border - fm.getDescent() );
if( src_minx == 0 && src_miny == 0 ){
int v2m = fm.stringWidth("0") / y_values2marker;
g.drawString( "0", inner_insets.left - fm.stringWidth( "0" ) - v2m - marker_length,
inner_insets.top + cd.height + fontAsc );
g.drawLine( inner_insets.left, inner_insets.top + cd.height + fm.getAscent(),
inner_insets.left, inner_insets.top + cd.height);
g.drawLine( inner_insets.left, inner_insets.top + cd.height,
inner_insets.left - fm.stringWidth( "0" ) - v2m - marker_length,
inner_insets.top + cd.height );
}
drawYValues( g, inner_insets, src_miny, src_maxy, cd );
drawXValues( g, inner_insets, src_minx, src_maxx, cd );
g.setColor( old_color );
}
private void drawYValues( Graphics g, Insets insets, double src_miny, double src_maxy, Dimension cd ){
if( under_construction ) System.out.println("ScaledBorder.drawYValues()");
FontMetrics fm = g.getFontMetrics();
int n, fontAsc = fm.getAscent(), v2m = fm.stringWidth("0") / y_values2marker;
n = (int)( src_miny / src_dY );
if( n * src_dY < src_miny || ( src_minx == 0 && src_miny == 0 ) ) n++;
int decs;
if( y_scale != null ) decs = getDecs( y_scale.getImageOf( src_dY ) );
else decs = getDecs( src_dY );
double v;
while( (v = n * src_dY) <= src_maxy ){
if( y_scale != null ) v = y_scale.getImageOf( v );
v = cutDec(v, decs);
Point p = m.getPoint( 0, v );
String text = stringOf(v);
g.drawString( text,
insets.left - fm.stringWidth( text ) - v2m - marker_length,
p.y + fontAsc / 2 );
g.drawLine( insets.left - marker_length, p.y, insets.left, p.y );
n++;
}
}
public double getSrcdY( FontMetrics fm, Dimension cd, double src_miny, double src_maxy ){
if( under_construction ) System.out.println("ScaledBorder.getSrcdY()");
if( !do_refresh && src_dY != -1 ) return src_dY;
int max = cd.height / fm.getHeight();
double minsrc_dY = 2 * ( src_maxy - src_miny ) / (double)max; // die 2 einfach mal so eingesetzt <--------------------------
src_dY = aBitBigger( minsrc_dY );
if( src_dY < minimal_increment ) src_dY = minimal_increment;
return src_dY;
}
private void drawXValues( Graphics g, Insets insets, double src_minx, double src_maxx, Dimension cd ){
if( under_construction ) System.out.println("ScaledBorder.drawXValues()");
FontMetrics fm = g.getFontMetrics();
double mx = cd.width / ( src_maxx - src_minx );
int n, labelX, decs,
xnull = insets.left + (int)( - src_minx * mx );
if( x_scale != null ) decs = getDecs( x_scale.getImageOf( src_dX ) );
else decs = getDecs( src_dX );
n = (int)( src_minx / src_dX );
if( n * src_dX < src_minx || ( src_minx == 0 && src_miny == 0 ) ) n++;
int fontAsc = fm.getAscent(), xLineY = insets.top + cd.height;
labelX = xnull + (int)(n * src_dX * mx);
while( n * src_dX <= src_maxx ){
double v = n * src_dX;
if( x_scale != null ) v = x_scale.getImageOf(v);
v = cutDec(v, decs);
String text = stringOf(v);
int strW = fm.stringWidth( text );
g.drawString( text, labelX - strW / 2, xLineY + fontAsc );
g.drawLine( labelX, xLineY, labelX, xLineY + marker_length );
n++;
labelX = xnull + (int)( n * src_dX * mx);
}
}
public double getSrcdX( FontMetrics fm, Dimension cd, double src_minx, double src_maxx ){
if( under_construction ) System.out.println("ScaledBorder.getSrcdX()");
if( !do_refresh && src_dX != - 1 ) return src_dX;
int digit_width = fm.stringWidth("0"),
max = cd.width / ( digit_width * ( x_value2value + 1 ) );
src_dX = ( src_maxx - src_minx ) / (double)max;
int n, labelX, olsrc_dX;
boolean ok = false;
while( !ok ){
src_dX = aBitBigger( src_dX );
n = (int)( src_minx / src_dX );
if( n * src_dX < src_minx ) n++;
int decs;
if( x_scale != null ) decs = getDecs( x_scale.getImageOf( src_dX ) );
else decs = getDecs( src_dX );
olsrc_dX = 0;
boolean suits = true, first = true;
while( suits && n * src_dX <= src_maxx ){
double v = n * src_dX;
if( x_scale != null ) v = x_scale.getImageOf( v );
String text = stringOf( cutDec( v, decs ) );
int strW = fm.stringWidth( text );
labelX = (int)((( n * src_dX - src_minx ) / ( src_maxx - src_minx ) ) * cd.width ) - strW / 2;
if( !first && labelX <= olsrc_dX + digit_width * x_value2value ) suits = false;
else{
olsrc_dX = labelX + strW;
n++;
}
first = false;
}
if( !suits ) ok = false;
else ok = true;
}
if( src_dX < minimal_increment ) src_dX = minimal_increment;
return src_dX;
}
/**
* method returns to a certain minimal value the next higher value which can be
* displayed, which looks a bit nicer
* it returns values like ... 0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10, ...
*
* @param the double value next to which the displayable value should be found
* @return the displayable value
*/
public static double aBitBigger( double min ){
if( min <= 0 ) return 1;
double d = 1;
if( min < d ){
while( d * .5 > min ) {
d *= .5;
if( d * .4 > min ) d *= .4;
if( d * .5 > min ) d *= .5;
}
}
else{
while( d <= min ) {
d *= 2;
if( d <= min ) d *= 2.5;
if( d <= min ) d *= 2;
}
}
return d;
}
public boolean isBorderOpaque(){
return outer_border.isBorderOpaque();
}
private String stringOf( double v ){
if( (int)v == v ) return String.valueOf( (int)v );
return String.valueOf( v );
}
private double cutDec( double v, int dec ){
if( dec < 1 ){
String s = String.valueOf( v );
int index = s.indexOf(".");
if( index == -1 ) return Double.parseDouble(s);
return Double.parseDouble(s.substring(0, index));
}
double pot = Math.pow( 10, dec );
v = (int)( v * pot );
v /= pot;
if( v == (int)v ) return (int)v;
return v;
}
public Insets getBorderInsets(Component c){
if( under_construction ) System.out.println("ScaledBorder.getBorderInsets()");
if( !do_refresh && old_insets != null ) return old_insets;
Graphics g = c.getGraphics();
Insets insets = new Insets(0, 0, 0, 0);
if( show_outer_border ) insets = outer_border.getBorderInsets( c );
if( g == null ) return insets;
FontMetrics fm = g.getFontMetrics();
int fontAsc = fm.getAscent(),
fontHeight = fm.getHeight(),
digit_width = fm.stringWidth("0");
if( c instanceof DArea ){
DArea area = (DArea)c;
DMeasures m = area.getDMeasures();
DRectangle rect = m.getSourceOf( area.getDRectangle() );
src_minx = rect.x;
src_maxx = rect.x + rect.width;
src_miny = rect.y;
src_maxy = rect.y + rect.height;
x_scale = area.getDMeasures().x_scale;
y_scale = area.getDMeasures().y_scale;
}
// left:
if( y_label != null ) insets.left += fm.getAscent() + fm.getDescent();
insets.left += y_label2values * digit_width;
getSrcdY( fm, c.getSize(), src_miny, src_maxy );
int n, maxWidth = 0, decs;
if( y_scale != null ) decs = getDecs( y_scale.getImageOf( src_dY ) );
else decs = getDecs( src_dY );
n = (int)( src_miny / src_dY );
if( n * src_dY < src_miny ) n++;
while( n * src_dY <= src_maxy ){
double v = n * src_dY;
if( y_scale != null ) v = y_scale.getImageOf( v );
int w = fm.stringWidth( String.valueOf(cutDec( v, decs )) );
if( w > maxWidth ) maxWidth = w;
n++;
}
insets.left += 1 + y_label2border + maxWidth + digit_width / y_values2marker + marker_length;
// bottom:
insets.bottom += 1 + fontHeight + x_label2border;
if( x_label != null ) insets.bottom += fontHeight;
// top:
if( show_arrows ) insets.top += y_values2arrow + arrow_length;
insets.top += axis2border;
// right:
if( show_arrows ) insets.right += x_values2arrow + arrow_length;
insets.right += axis2border;
getSrcdX( fm, c.getSize(), src_minx, src_maxx );
decs = getDecs( src_dX );
n = (int)( src_maxx / src_dX );
if( n < 0 ) n ++;
int w = fm.stringWidth( String.valueOf(cutDec( n * src_dX, decs )) );
if( w / 2 > insets.right ) insets.right = w / 2;
old_insets = insets;
return insets;
}
private int getDecs( double v ){
if( under_construction ) System.out.println("ScaledBorder.getDecs()");
int decs = 0;
while( !( Math.pow( v / (int)v, 2 ) < 1.001 ) ) {
decs++;
v *= 10;
}
return decs;
}
}