/* ------------------------------------------------------------- DGeom2D.java: Some classes for 2D, floating-point geometry. Classes: DPoint2D, DRectangle, DPolygon2D mirror java.awt.point, rectangle and polygon, although they're incomplete. Some of their methods (especially initializers) use the similar java.awt classes (for instance you can initialize a DPoint2D with a Point), so watch out for the similar names. DTransform2D, DView2D. There is no Transform in java.awt. A DTransform2D converts points in one coordinate system into another coordinate system with translation (sliding in x and y), rotation, scaling, reflection & distortion. DView2D is sort of equivalent to java.awt.Graphics-- it's the thing you draw into. Except you can set or reset the coordinate system (or not: you can let it default to pixel coordinates). Drawing: Instead of, say (assuming "g" is a Graphics), g.drawPolygon( polygon ); you do: dPolygon.drawInView( dView ); Setting colors: Instead of (assuming "g" is a Graphics): g.setColor( Color.red ); or: g.setColor( new Color( (float) 1.0, 0, 0 ) ); // Note: 1.0 is a double that needs casting to float! you do: dView.setColor( Color.red ); or: dView.setRGB( 1.0, 0, 0 ); // No casting needed. Steve Witham --------------------------- history --------------------------- 1999.08.16: Started. Combined "DVector2D" into DPoint2D. 1999.08.18: Did DRectangle2D.scaleTo( DRect2D dest, boolean sameXY ) and started DView2D. 215 lines. 1999.09.04: Working with colorchart.java drawing one swatch! And it rescales and stuff! 306 lines. --------------------------------------------------------------- 1999.09.04.2: Added DRectangle toDPolygon(), drawInView() and fillInView() for colorchart. 326 lines. Added DView2D setColor() and setRGB(), plus some more comments. 357 lines. Added DTransform2D.newScaleRotateXY( ), DPolygon through(), made npoints optional in new DPolygon(). Changed from DTransform2D( origin, xmult, ymult ) to DTransform2D( xmult, ymult, translation ) because the translation happens logically last. 391 lines. 1999.09.05: Added patch for bug in Mac Netscape 4.05 drawPolygon(). 400 lines. 1999.09.11: Made DView2D save the fromDRect and sameXY. Added DView2D::adjust() w/special case if fromDRect is null. Added DView2D::dispose(). 416 lines. ------------------------------------------------------------- */ import java.applet.Applet; import java.util.*; import java.awt.*; import java.lang.Math; public class DPoint2D { public double x; public double y; public DPoint2D ( double x, double y ) { this.x = x; this.y = y; } public DPoint2D ( Point p ) { this( p.x, p.y ); } public Point toPoint () { return new Point( (int) this.x, (int) this.y ); } public void move( double x, double y ) { this.x = x; this.y = y; } public void translate( double x, double y ) { this.x += x; this.y += y; } public boolean equals( Object obj ) { return false; } public DPoint2D through( DTransform2D t ) { return t.transform( this ); } public Point throughView( DView2D view ) { return this.through( view.trans ).toPoint(); } /* The following methods treat the point as a vector: */ public DPoint2D times( double m ) { return new DPoint2D( this.x * m, this.y * m ); } public DPoint2D plus( DPoint2D v ) { return new DPoint2D( this.x + v.x, this.y + v.y ); } public DPoint2D minus( DPoint2D v ) { return new DPoint2D( this.x - v.x, this.y - v.y ); } public double length( ) { return Math.sqrt( this.x * this.x + this.y * this.y ); } } public class DRectangle { /* CLASS METHODS */ public static DRectangle newXXYY( double x0, double x1, double y0, double y1 ) { return new DRectangle( x0, y0, x1 - x0, y1 - y0 ); } /* INSTANCE STUFF */ public double x; /* left side */ public double y; /* Top (least y) */ public double width; public double height; public DRectangle( double x, double y, double width, double height ) { this.x = x; this.y = y; this.height = height; this.width = width; } public DRectangle( ) { this( 0, 0, 0, 0 ); } public DRectangle( Rectangle r ) { this( r.x, r.y, r.width, r.height ); } public Rectangle toRectangle( ) { return new Rectangle( (int) this.x, (int) this.y, (int) this.width, (int) this.height ); } public DPolygon toDPolygon( ) { return( new DPolygon( new DPoint2D [] { new DPoint2D( this.x, this.y ), new DPoint2D( this.x + this.width, this.y ), new DPoint2D( this.x + this.width, this.y + this.height ), new DPoint2D( this.x, this.y + this.height ) } ) ); } public DPoint2D center( ) { return new DPoint2D( this.x + this.width / 2, this.y + this.height / 2 ); } public DTransform2D scaleTo( DRectangle dest, boolean sameXY ) { double xScale = dest.width / this.width; double yScale = dest.height / this.height; if( sameXY ) { if( Math.abs( xScale ) > Math.abs( yScale ) ) { if( xScale > 0 ) { xScale = Math.abs( yScale ); } else { xScale = -Math.abs( yScale ); } } else { if( yScale > 0 ) { yScale = Math.abs( xScale ); } else { yScale = -Math.abs( xScale ); } } } /* Make this.center() map to dest.center(): */ DTransform2D trans = new DTransform2D( new DPoint2D( xScale, 0 ), new DPoint2D( 0, yScale ), new DPoint2D( 0, 0 ) ); trans.setTranslation( dest.center() .minus( this.center().through( trans ) ) ); return( trans ); } public DTransform2D scaleTo( Rectangle dest, boolean sameXY ) { return this.scaleTo( new DRectangle( dest ), sameXY ); } public void drawInView( DView2D view ) { this.toDPolygon().drawInView( view ); } public void fillInView( DView2D view ) { this.toDPolygon().fillInView( view ); } } public class DPolygon { DPoint2D [] dpoints; int npoints; public DPolygon () { /* empty */ this.dpoints = new DPoint2D[ 1 ]; this.npoints = 0; } public DPolygon ( double [] xpoints, double [] ypoints, int npoints ) { this.dpoints = new DPoint2D[ npoints ]; for( int i = 0; i < npoints; i++ ) { dpoints[ i ] = new DPoint2D( xpoints[i], ypoints[i] ); } this.npoints = npoints; } public DPolygon ( DPoint2D [] dpoints, int npoints ) { this.dpoints = dpoints; this.npoints = npoints; } public DPolygon ( DPoint2D [] dpoints ) { this( dpoints, dpoints.length ); } public DPoint2D[] getPoints ( ) { return this.dpoints; } public DPoint2D getPoint ( int i ) { return this.dpoints[ i ]; } public int getNPoints ( ) { return this.npoints; } void makeRoomFor( int n ) { int size = this.dpoints.length; if( size <= n ) { DPoint2D newDPoints [] = new DPoint2D [ (int) Math.max( n, size * 2 ) ]; for( int i = 0; i < size; i++ ) { newDPoints[ i ] = this.dpoints[ i ]; } dpoints = newDPoints; } } public void addPoint ( DPoint2D dpoint ) { this.makeRoomFor( this.npoints + 1 ); this.dpoints[ this.npoints++ ] = dpoint; } public void addPoint ( double x, double y ) { this.addPoint( new DPoint2D( x, y ) ); } public DPolygon through( DTransform2D trans ) { DPoint2D [] transDPoints = new DPoint2D [ this.npoints ]; for( int i = 0; i < this.npoints; i++ ) { transDPoints[ i ] = this.dpoints[ i ].through( trans ); } return( new DPolygon( transDPoints ) ); } public Polygon throughView( DView2D view ) { Polygon poly = new Polygon(); for( int i = 0; i < this.npoints; i++ ) { Point p = this.dpoints[ i ].throughView( view ); poly.addPoint( p.x, p.y ); } return( poly ); } public void drawInView( DView2D view ) { Polygon poly = this.throughView( view ); view.graphics.drawPolygon( poly ); // Hack: draw the last segment; Netscape 4.05 on the Mac doesn't: view.graphics.drawLine( poly.xpoints[ npoints - 1 ], poly.ypoints[ npoints - 1 ], poly.xpoints[ 0 ], poly.ypoints[ 0 ] ); } public void fillInView( DView2D view ) { view.graphics.fillPolygon( this.throughView( view ) ); } } public class DTransform2D { // Constant -- identity transform: public static final DTransform2D IDENTITY = new DTransform2D( new DPoint2D( 1, 0 ), new DPoint2D( 0, 1 ), new DPoint2D( 0, 0 ) ); // Class method: public static DTransform2D newScaleRotateXY( double scale, double angle, double x, double y ) { // The scaling and rotation happens before the translation. double s = scale * Math.sin( angle ); double c = scale * Math.cos( angle ); return new DTransform2D( new DPoint2D( c, s ), new DPoint2D( -s, c ), new DPoint2D( x, y ) ); } // Instance stuff: DPoint2D xmult; DPoint2D ymult; DPoint2D translation; public DTransform2D( DPoint2D xmult, DPoint2D ymult, DPoint2D translation ) { this.xmult = xmult; this.ymult = ymult; this.translation = translation; } public DPoint2D transform( DPoint2D p ) { return( this.xmult.times( p.x ) .plus( this.ymult.times( p.y ) ) .plus( this.translation ) ); } public void setTranslation( DPoint2D translation ) { this.translation = translation; } } public class DView2D { Component drawInto; public Graphics graphics; DRectangle fromDRect; boolean sameXY; DTransform2D trans; public DView2D( Component drawInto ) { // Use the component's own coordinate system. this( drawInto, null, false ); } public DView2D( Component drawInto, DRectangle fromDRect, boolean sameXY ) { this.drawInto = drawInto; this.reScaleFrom( fromDRect, sameXY ); } public void reScaleFrom( DRectangle fromDRect, boolean sameXY ) { this.fromDRect = fromDRect; this.sameXY = sameXY; this.adjust( ); } public void adjust( ) { // to changes in bounds or graphics. this.dispose( ); this.graphics = drawInto.getGraphics(); Rectangle r = drawInto.bounds(); if( fromDRect != null ) { this.trans = fromDRect.scaleTo( drawInto.bounds(), sameXY ); } else { // Use the component's own coordinate system. this.trans = DTransform2D.IDENTITY; } } public void setColor( Color c ) { this.graphics.setColor( c ); } public Color setRGB( double r, double g, double b ) { Color c = new Color( (float) r, (float) g, (float) b ); this.graphics.setColor( c ); return( c ); } public void dispose() { if( this.graphics != null ) { this.graphics.dispose( ); } this.graphics = null; } }