📄 psg.java
字号:
/// Convert curves in the path to sequences of straight lines.
public void flattenpath()
{
// This is actually a no-op, since we always represent curves
// as sequences of straight lines.
}
/// Set the current path to the clipping path.
// @exception Acme.PsgException "undefined result"
public void clippath() throws PsgException
{
Dimension d = size();
rectpath(
itransform_x( 0, 0 ), itransform_y( 0, 0 ),
idtransform_x( d.width, d.height ),
idtransform_y( d.width, d.height ) );
}
/// Return the bounding box of the current path.
// The return is in the form of a four-element array - element 0 is
// llx, 1 is lly, 2 is urx, and 3 is ury.
// @exception Acme.PsgException "no current point"
public double[] pathbbox() throws PsgException
{
if ( path.isEmpty() )
throw new PsgException( "no current point" );
int llx, lly, urx, ury;
llx = ury = Integer.MAX_VALUE;
urx = lly = Integer.MIN_VALUE;
Enumeration enum = path.elements();
while ( enum.hasMoreElements() )
{
PsgPathItem pi = (PsgPathItem) enum.nextElement();
llx = Math.min( llx, pi.p.x );
lly = Math.max( lly, pi.p.y );
urx = Math.max( urx, pi.p.x );
ury = Math.min( ury, pi.p.y );
}
double[] r = new double[4];
r[0] = itransform_x( llx, lly );
r[1] = itransform_y( llx, lly );
r[2] = itransform_x( urx, ury );
r[3] = itransform_y( urx, ury );
return r;
}
/// Paint the whole graphics area with the background color.
public void erasepage()
{
Dimension d = size();
graphics.clearRect( 0, 0, d.width, d.height );
}
/// Fill current path with current color.
public void fill()
{
int x, y;
Polygon poly = null;
graphics.setColor( current.color );
Enumeration enum = path.elements();
while ( enum.hasMoreElements() )
{
PsgPathItem pi = (PsgPathItem) enum.nextElement();
if ( ! pi.draw )
{
if ( poly != null )
graphics.fillPolygon( poly );
poly = new Polygon();
}
poly.addPoint( pi.p.x, pi.p.y );
x = pi.p.x;
y = pi.p.y;
}
if ( poly != null )
graphics.fillPolygon( poly );
newpath();
}
/// Draw lines along the current path.
public void stroke()
{
int x = 0, y = 0; // initial values don't ever get used
graphics.setColor( current.color );
Enumeration enum = path.elements();
while ( enum.hasMoreElements() )
{
PsgPathItem pi = (PsgPathItem) enum.nextElement();
if ( pi.draw )
if ( pi.linewidth < 1 )
graphics.drawLine( x, y, pi.p.x, pi.p.y );
else
Acme.GuiUtils.drawThickLine(
graphics, x, y, pi.p.x, pi.p.y, pi.linewidth );
x = pi.p.x;
y = pi.p.y;
}
newpath();
}
/// Fill a rectangular path.
public void rectfill( double x, double y, double width, double height )
{
gsave();
rectpath( x, y, width, height );
fill();
grestore();
}
/// Stroke a rectangular path.
public void rectstroke( double x, double y, double width, double height )
{
gsave();
rectpath( x, y, width, height );
stroke();
grestore();
}
// Make a rectangular path.
private void rectpath( double x, double y, double width, double height )
{
newpath();
moveto( x, y );
try
{
rlineto( width, 0 );
rlineto( 0, height );
rlineto( -width, 0 );
}
catch ( Acme.PsgException e )
{
// Shouldn't happen.
throw new InternalError();
}
closepath();
}
/// Set the current font to the specified name, style, and size.
// Similar to a PostScript findfont scalefont setfont sequence.
// @param name the name of the font - Dialog, Helvetica, TimesRoman, Courier
// @param style some combination of Font.PLAIN, Font.BOLD, and Font.ITALIC
// @param size how tall the font should be
public void setfont( String name, int style, double size )
{
setfontName( name );
setfontStyle( style );
setfontSize( size );
}
/// Set the current font to the specified name.
// @param name the name of the font - Dialog, Helvetica, TimesRoman, Courier
public void setfontName( String name )
{
current.fontName = new String( name );
current.font = null;
}
/// Set the current font to the specified style.
// @param style some combination of Font.PLAIN, Font.BOLD, and Font.ITALIC
public void setfontStyle( int style )
{
current.fontStyle = style;
current.font = null;
}
/// Set the current font to the specified size.
// @param size how tall the font should be
public void setfontSize( double size )
{
current.fontSize = dtransform_y( 0.0, size );
current.font = null;
}
/// Paint a string starting at the current point.
// Doesn't do rotation yet.
// @exception Acme.PsgException "no current point"
public void show( String str ) throws PsgException
{
if ( current.point == null )
throw new PsgException( "no current point" );
if ( current.font == null )
{
current.font = new Font(
current.fontName, current.fontStyle, current.fontSize );
graphics.setFont( current.font );
}
graphics.drawString( str,
transform_x( current.point.x, current.point.y ),
transform_y( current.point.x, current.point.y ) );
}
/// Return the x-width of a string.
// Doesn't do rotation yet.
// @exception Acme.PsgException "undefined result"
public double stringwidth_x( String str ) throws PsgException
{
FontMetrics fm = graphics.getFontMetrics();
return idtransform_x( fm.stringWidth( str ), 0 );
}
private Dimension size()
{
if ( component != null )
return component.size();
// return component.getSize();
// Second choice - use the clipping area.
Rectangle r = graphics.getClipBounds();
return new Dimension( r.x + r.width, r.y + r.height );
}
private static double normAngle( double a )
{
int circles = (int) ( a / 360.0 );
return a - circles * 360.0;
}
private static double toRadians( double a )
{
return a * Math.PI / 180.0;
}
/// Test program.
public static void main( String[] args )
{
new MainFrame( new PsgTest(), args, 350, 350 );
}
}
class PsgState implements Cloneable
{
public int linewidth;
public Color color;
public DoublePoint point;
public PsgMatrix matrix;
public String fontName;
public int fontStyle;
public int fontSize;
public Font font;
// Create a new PsgState.
public PsgState()
{
linewidth = 0;
color = Color.black;
point = null;
matrix = new PsgMatrix();
fontName = "Dialog";
fontStyle = Font.PLAIN;
fontSize = 12;
font = null;
}
public Object clone()
{
try
{
PsgState n = (PsgState) super.clone();
n.color = new Color( color.getRGB() );
if ( point != null )
n.point = (DoublePoint) point.clone();
n.matrix = (PsgMatrix) matrix.clone();
return n;
}
catch ( CloneNotSupportedException e )
{
// Shouldn't happen.
throw new InternalError();
}
}
}
class PsgPathItem extends Acme.GenericCloneable
{
public Point p;
public int linewidth;
public boolean draw;
public PsgPathItem( int x, int y, int linewidth, boolean draw )
{
this.p = new Point( x, y );
this.linewidth = linewidth;
this.draw = draw;
}
// You might think that since we contain a Point object that we need
// to clone it. However, we treat Points as immutable, never
// modifying them once they are created, so it's perfectly safe
// to just copy the reference and share the contents.
}
class PsgMatrix extends Acme.GenericCloneable
{
public double xx, xy, yx, yy, tx, ty;
public PsgMatrix()
{
// Initialize with the identity matrix.
xx = 1.0;
xy = 0.0;
yx = 0.0;
yy = 1.0;
tx = 0.0;
ty = 0.0;
}
public PsgMatrix multiply( PsgMatrix b )
{
PsgMatrix r = new PsgMatrix();
r.xx = this.xx * b.xx + this.xy * b.yx;
r.xy = this.xx * b.xy + this.xy * b.yy;
r.yy = this.yx * b.xy + this.yy * b.yy;
r.yx = this.yx * b.xx + this.yy * b.yx;
r.tx = this.tx * b.xx + this.ty * b.yx + b.tx;
r.ty = this.tx * b.xy + this.ty * b.yy + b.ty;
return r;
}
public PsgMatrix invert() throws PsgException
{
PsgMatrix r = new PsgMatrix();
double det = this.xx * this.yy - this.xy * this.yx;
if ( det == 0.0 )
throw new PsgException( "undefined result" );
r.xx = this.yy / det;
r.xy = - this.xy / det;
r.yx = - this.yx / det;
r.yy = this.xx / det;
r.tx = - ( this.tx * r.xx + this.ty * r.yx );
r.ty = - ( this.tx * r.xy + this.ty * r.yy );
return r;
}
}
// Test class.
class PsgTest extends Applet
{
// Called when the applet is first created.
public void init()
{
setBackground( Color.white );
}
// Called when the applet should paint itself.
public void paint( Graphics graphics )
{
try
{
Psg psg = new Psg( this );
psg.gsave();
psg.translate( 50, 50 );
psg.newpath();
squarePath( psg, 50 );
psg.stroke();
psg.grestore();
psg.gsave();
psg.translate( 150, 50 );
psg.rotate( 30 );
psg.newpath();
squarePath( psg, 50 );
psg.stroke();
psg.grestore();
psg.gsave();
psg.translate( 250, 50 );
psg.rotate( 30 );
psg.scale( 1.5, 1.5 );
psg.newpath();
squarePath( psg, 50 );
psg.stroke();
psg.grestore();
psg.gsave();
psg.translate( 50, 150 );
psg.newpath();
squarePath( psg, 50 );
psg.fill();
psg.grestore();
psg.gsave();
psg.translate( 150, 150 );
psg.rotate( 30 );
psg.newpath();
squarePath( psg, 50 );
psg.fill();
psg.grestore();
psg.gsave();
psg.translate( 250, 150 );
psg.rotate( 30 );
psg.scale( 1.5, 1.5 );
psg.newpath();
squarePath( psg, 50 );
psg.fill();
psg.grestore();
psg.gsave();
psg.translate( 50, 250 );
psg.newpath();
psg.arc( 25, 25, 25, 0, 360 );
psg.stroke();
psg.grestore();
psg.gsave();
psg.translate( 150, 250 );
psg.newpath();
psg.arc( 25, 25, 25, 0, 360 );
psg.fill();
psg.grestore();
psg.gsave();
psg.translate( 250, 250 );
psg.moveto( 0, 0 );
psg.curveto( 0, 50, 50, 50, 50, 0 );
psg.stroke();
psg.grestore();
}
catch ( PsgException e )
{
System.err.println( e );
System.exit( 1 );
}
}
private void squarePath( Psg psg, int size ) throws PsgException
{
psg.moveto( 0, 0 );
psg.rlineto( size, 0 );
psg.rlineto( 0, size );
psg.rlineto( -size, 0 );
psg.closepath();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -