📄 psg.java
字号:
// Psg - a PostScript-like alternative to the Graphics class
//
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. 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.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
//
// Visit the ACME Labs Java page for up-to-date versions of this and other
// fine Java utilities: http://www.acme.com/java/
package Acme;
import java.util.*;
import java.awt.*;
import java.awt.image.*;
import java.applet.*;
/// <A HREF="http://java.developer.com/"><IMG WIDTH=162 HEIGHT=35 ALIGN="right" SRC="/resources/gamelan_best.jpg"></A>
// <A HREF="http://www.developer.com/whatscool/"><IMG WIDTH=173 HEIGHT=36 ALIGN="right" SRC="/resources/gamelan_cool.png"></A>
// A PostScript-like alternative to the Graphics class.
// <P>
// This class provides a functional equivalent of java.awt.Graphics,
// but with a very PostScript-like interface. It implements the following
// PostScript operators:
// <BLOCKQUOTE>
// gsave grestore grestoreall initgraphics setlinewidth setcolor setgray
// sethsbcolor setrgbcolor translate scale rotate transform dtransform
// itransform idtransform
// newpath moveto rmoveto lineto rlineto arc arcn curveto rcurveto closepath
// flattenpath clippath pathbbox
// erasepage fill stroke
// rectfill rectstroke
// findfont scalefont setfont
// show stringwidth
// </BLOCKQUOTE>
// Not only is this a more powerful rendering idiom than the standard
// Graphics class, but it also makes it hellof easy to translate
// PostScript graphics hacks into Java. Here's a
// <A HREF="/resources/classes/Yoyo.java">sample</A>:
// <BLOCKQUOTE>
// <APPLET WIDTH=300 HEIGHT=200 CODEBASE="/resources/classes" CODE="Yoyo">
// </APPLET>
// </BLOCKQUOTE>
// <P>
// JavaSoft and Adobe are said to be working on a 2-D rendering API
// similar to PostScript, and therefore similar to this. When that
// comes out this class will probably be obsolete, so I don't plan
// on doing any major improvements.
// <P>
// <A HREF="/resources/classes/Acme/Psg.java">Fetch the software.</A><BR>
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
public class Psg
{
private Component component;
private Graphics graphics;
private PsgState current;
private Stack gStack;
private Vector path; // holds PsgPathItems
private PsgPathItem subpathStart; // pointer to most recent move
/// Constructor from Component.
// This is the preferred way to make a Psg.
public Psg( Component component )
{
this.component = component;
this.graphics = component.getGraphics();
initgraphics();
}
/// Constructor from Graphics.
// If you don't have a Component, you can create a Psg from a Graphics
// instead, but this is not as good.
public Psg( Graphics graphics )
{
this.component = null;
this.graphics = graphics;
initgraphics();
}
/// Save the current graphics state onto a stack.
public void gsave()
{
PsgState next = (PsgState) current.clone();
gStack.push( current );
current = next;
}
/// Restore the last Save()ed graphics state.
public void grestore()
{
if ( gStack.empty() )
initgraphics();
else
{
current = (PsgState) gStack.pop();
if ( current.font == null )
graphics.setFont( current.font );
}
}
/// Pop to bottom-most graphics state.
public void grestoreall()
{
// This is equivalent to initgraphics(), isn't it?
while ( ! gStack.empty() )
grestore();
}
/// Reset graphics state.
public void initgraphics()
{
current = new PsgState();
gStack = new Stack();
path = new Vector();
subpathStart = null;
// Set the current transformation to fit screen space.
Dimension d = size();
this.translate( 0.0, d.height );
this.scale( 1.0, -1.0 );
erasepage();
}
/// Set the current line width.
public void setlinewidth( double linewidth )
{
current.linewidth = wtransform( linewidth );
}
/// Set the current color.
public void setcolor( Color color )
{
current.color = color;
}
/// Set the color to the specified gray value (0=black, 1=white).
public void setgray( float grayVal )
{
setrgbcolor( grayVal, grayVal, grayVal );
}
/// Set the color from HSB coordinates.
public void sethsbcolor( float hue, float saturation, float brightness )
{
setcolor( new Color( Color.HSBtoRGB( hue, saturation, brightness ) ) );
}
/// Set the color from RGB coordinates.
public void setrgbcolor( float r, float g, float b )
{
setcolor( new Color( r, g, b ) );
}
/// Translate graphics space.
public void translate( double tx, double ty )
{
double nx =
tx * current.matrix.xx +
ty * current.matrix.yx +
current.matrix.tx;
double ny =
ty * current.matrix.yy +
tx * current.matrix.xy +
current.matrix.ty;
current.matrix.tx = nx;
current.matrix.ty = ny;
}
/// Scale graphics space.
public void scale( double sx, double sy )
{
current.matrix.xx *= sx;
current.matrix.xy *= sx;
current.matrix.yx *= sy;
current.matrix.yy *= sy;
}
/// Rotate graphics space.
public void rotate( double degrees )
{
double radians = toRadians( degrees );
double cos = Math.cos( radians );
double sin = Math.sin( radians );
double sxx = current.matrix.xx;
double sxy = current.matrix.xy;
current.matrix.xx = cos * sxx + sin * current.matrix.yx;
current.matrix.xy = cos * sxy + sin * current.matrix.yy;
current.matrix.yx = cos * current.matrix.yx - sin * sxx;
current.matrix.yy = cos * current.matrix.yy - sin * sxy;
}
/// Transform (x, y) into a device-space x-coordinate.
public int transform_x( double x, double y )
{
return (int) (
x * current.matrix.xx + y * current.matrix.yx + current.matrix.tx );
}
/// Transform (x, y) into a device-space y-coordinate.
public int transform_y( double x, double y )
{
return (int) (
y * current.matrix.yy + x * current.matrix.xy + current.matrix.ty );
}
/// Transform (dx, dy) into a device-space x-distance.
public int dtransform_x( double dx, double dy )
{
return (int) ( dx * current.matrix.xx + dy * current.matrix.yx );
}
/// Transform (dx, dy) into a device-space y-distance.
public int dtransform_y( double dx, double dy )
{
return (int) ( dy * current.matrix.yy + dx * current.matrix.xy );
}
/// Inverse transform (x, y) into a user-space x-coordinate.
// @exception Acme.PsgException "undefined result"
public double itransform_x( int x, int y ) throws PsgException
{
PsgMatrix inv = current.matrix.invert();
return x * inv.xx + y * inv.yx + inv.tx;
}
/// Inverse transform (x, y) into a user-space y-coordinate.
// @exception Acme.PsgException "undefined result"
public double itransform_y( int x, int y ) throws PsgException
{
PsgMatrix inv = current.matrix.invert();
return y * inv.yy + x * inv.xy + inv.ty;
}
/// Inverse transform (dx, dy) into a user-space x-distance.
// @exception Acme.PsgException "undefined result"
public double idtransform_x( int dx, int dy ) throws PsgException
{
PsgMatrix inv = current.matrix.invert();
return dx * inv.xx + dy * inv.yx;
}
/// Inverse transform (dx, dy) into a user-space y-distance.
// @exception Acme.PsgException "undefined result"
public double idtransform_y( int dx, int dy ) throws PsgException
{
PsgMatrix inv = current.matrix.invert();
return dy * inv.yy + dx * inv.xy;
}
// Transform a width into device space.
private int wtransform( double width )
{
int dx = dtransform_x( width, width );
int dy = dtransform_y( width, width );
return ( Math.abs( dx ) + Math.abs( dy ) ) / 2;
}
/// Start a new, empty path.
public void newpath()
{
current.point = null;
path.setSize( 0 );
subpathStart = null;
}
/// Set the current point.
public void moveto( double x, double y )
{
if ( current.point == null )
current.point = new DoublePoint();
current.point.x = x;
current.point.y = y;
PsgPathItem n =
new PsgPathItem(
transform_x( x, y ), transform_y( x, y ), 0, false );
path.addElement( n );
subpathStart = n;
}
/// Relative moveto().
// @exception Acme.PsgException "no current point"
public void rmoveto( double dx, double dy ) throws PsgException
{
if ( current.point == null )
throw new PsgException( "no current point" );
moveto( current.point.x + dx, current.point.y + dy );
}
/// Add a line to the path.
// @exception Acme.PsgException "no current point"
public void lineto( double x, double y ) throws PsgException
{
if ( current.point == null )
throw new PsgException( "no current point" );
current.point.x = x;
current.point.y = y;
PsgPathItem n =
new PsgPathItem(
transform_x( x, y ), transform_y( x, y ),
current.linewidth, true );
path.addElement( n );
}
/// Relative lineto().
// @exception Acme.PsgException "no current point"
public void rlineto( double dx, double dy ) throws PsgException
{
if ( current.point == null )
throw new PsgException( "no current point" );
lineto( current.point.x + dx, current.point.y + dy );
}
private final double segAng = 5.0;
/// Append counterclockwise arc.
public void arc( double cx, double cy, double r, double ang1, double ang2 )
{
ang1 = normAngle( ang1 );
ang2 = normAngle( ang2 );
if ( ang2 <= ang1 )
ang2 += 360.0;
for ( double a = ang1 ; a < ang2; a += segAng )
arcSeg( cx, cy, r, a );
arcSeg( cx, cy, r, ang2 );
}
/// Append clockwise arc.
public void arcn( double cx, double cy, double r, double ang1, double ang2 )
{
ang1 = normAngle( ang1 );
ang2 = normAngle( ang2 );
if ( ang2 >= ang1 )
ang2 -= 360.0;
for ( double a = ang1 ; a > ang2; a -= segAng )
arcSeg( cx, cy, r, a );
arcSeg( cx, cy, r, ang2 );
}
private void arcSeg( double cx, double cy, double r, double a )
{
double x = cx + r * Math.cos( toRadians( a ) );
double y = cy + r * Math.sin( toRadians( a ) );
if ( current.point == null )
moveto( x, y );
else
try
{
lineto( x, y );
}
catch ( Acme.PsgException e )
{
// Shouldn't happen.
throw new InternalError();
}
}
private final double tStep = 0.025;
/// Append a Bezier cubic section.
// @exception Acme.PsgException "no current point"
public void curveto( double x1, double y1, double x2, double y2, double x3, double y3 ) throws PsgException
{
if ( current.point == null )
throw new PsgException( "no current point" );
double x0 = current.point.x;
double y0 = current.point.y;
double cx = ( x1 - x0 ) * 3.0;
double cy = ( y1 - y0 ) * 3.0;
double bx = ( x2 - x1 ) * 3.0 - cx;
double by = ( y2 - y1 ) * 3.0 - cy;
double ax = x3 - x0 - cx - bx;
double ay = y3 - y0 - cy - by;
for ( double t = 0.0; t < 1.0; t += tStep )
curveSeg( ax, ay, bx, by, cx, cy, x0, y0, t );
curveSeg( ax, ay, bx, by, cx, cy, x0, y0, 1.0 );
}
private void curveSeg( double ax, double ay, double bx, double by, double cx, double cy, double x0, double y0, double t )
{
double t2 = t * t;
double t3 = t2 * t;
double x = ax * t3 + bx * t2 + cx * t + x0;
double y = ay * t3 + by * t2 + cy * t + y0;
try
{
lineto( x, y );
}
catch ( Acme.PsgException e )
{
// Shouldn't happen.
throw new InternalError();
}
}
/// Relative curveto.
// @exception Acme.PsgException "no current point"
public void rcurveto( double dx1, double dy1, double dx2, double dy2, double dx3, double dy3 ) throws PsgException
{
if ( current.point == null )
throw new PsgException( "no current point" );
double x0 = current.point.x;
double y0 = current.point.y;
curveto( x0 + dx1, y0 + dy1, x0 + dx2, y0 + dy2, x0 + dx3, y0 + dy3 );
}
/// Connect current path back to its starting point.
public void closepath()
{
if ( subpathStart != null )
path.addElement(
new PsgPathItem(
subpathStart.p.x, subpathStart.p.y,
current.linewidth, true ) );
current.point = null;
subpathStart = null;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -