FileDocCategorySizeDatePackage
Flattener.javaAPI DocphoneME MR2 API (J2ME)9412Wed May 02 18:00:36 BST 2007com.sun.pisces

Flattener

public class Flattener extends PathSink
The Flattener class rewrites a general path, which may include curved segments, into one containing only linear segments suitable for sending to a LineSink.

Curved segments specified by quadTo and curveTo commands will be subdivided into two pieces. When the control points of a segment lie sufficiently close togther, such that max(x_i) - min(x_i) < flatness and max(y_i) - min(y_i) < flatness for a user-supplied flatness parameter, a lineTo command is emitted between the first and last points of the curve.

Fields Summary
public static final long
MAX_CHORD_LENGTH_SQ
public static final long
MIN_CHORD_LENGTH_SQ
public static final int
LG_FLATNESS
public static final int
FLATNESS_SQ_SHIFT
LineSink
output
int
flatness
int
flatnessSq
int
x0
int
y0
Constructors Summary
public Flattener()
Empty constructor. setOutput and setFlatness must be called prior to calling any other methods.


                        
      
public Flattener(LineSink output, int flatness)
Constructs a Flattener that will rewrite any incoming quadTo and curveTo commands into a series of lineTo commands with maximum X and Y extents no larger than the supplied flatness value. The flat segments will be sent as commands to the given LineSink.

param
output a LineSink to which commands should be sent.
param
flatness the maximum extent of a subdivided output line segment, in S15.16 format.

        setOutput(output);
        setFlatness(flatness);
    
Methods Summary
public voidclose()

        output.lineJoin();
	output.close();
    
public voidcubicTo(int x1, int y1, int x2, int y2, int x3, int y3)

        output.lineJoin();
        cubicToHelper(x1, y1, x2, y2, x3, y3);
    
private voidcubicToHelper(int x1, int y1, int x2, int y2, int x3, int y3)

        if (flatEnough(x0, y0, x1, y1, x2, y2, x3, y3)) {
            output.lineTo(x1, y1);
            output.lineTo(x2, y2);
            output.lineTo(x3, y3);
        } else {
	    long lx1 = (long)x1;
	    long ly1 = (long)y1;
	    long lx2 = (long)x2;
	    long ly2 = (long)y2;

	    long x01 = x0 + lx1; // >> 1
            long y01 = y0 + ly1; // >> 1
            long x12 = lx1 + lx2; // >> 1
            long y12 = ly1 + ly2; // >> 1
            long x23 = lx2 + x3; // >> 1
            long y23 = ly2 + y3; // >> 1

            long x012 = x01 + x12; // >> 2
            long y012 = y01 + y12; // >> 2
            long x123 = x12 + x23; // >> 2
            long y123 = y12 + y23; // >> 2
            long x0123 = x012 + x123; // >> 3
            long y0123 = y012 + y123; // >> 3
            
            cubicToHelper((int)(x01 >> 1), (int)(y01 >> 1),
                          (int)(x012 >> 2), (int)(y012 >> 2),
                          (int)(x0123 >> 3), (int)(y0123 >> 3));
            cubicToHelper((int)(x123 >> 2), (int)(y123 >> 2),
                          (int)(x23 >> 1), (int)(y23 >> 1),
                          x3, y3);
        }
            
        this.x0 = x3;
        this.y0 = y3;
    
public voidend()

	output.end();
    
private booleanflatEnough(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3)

        long dx = (long)x3 - (long)x0;          // S47.16
        long dy = (long)y3 - (long)y0;          // S47.16
        long denom2 = dx*dx + dy*dy; // S31.32
        // Always subdivide curves with a large chord length
        if (denom2 > MAX_CHORD_LENGTH_SQ) {
            return false;
        }

	// Stop dividing if all control points are close together
        if (denom2 < MIN_CHORD_LENGTH_SQ) {
	    int minx = Math.min(Math.min(Math.min(x0, x1), x2), x3);
	    int miny = Math.min(Math.min(Math.min(y0, y1), y2), y3);
	    int maxx = Math.max(Math.max(Math.max(x0, x1), x2), x3);
	    int maxy = Math.max(Math.max(Math.max(y0, y1), y2), y3);

	    long dx1 = (long)maxx - (long)minx;
	    long dy1 = (long)maxy - (long)miny;
	    long l2 = dx1*dx1 + dy1*dy1;
	    if (l2 < MIN_CHORD_LENGTH_SQ) {
		return true;
	    }
        }

        // Want to know if num/denom < flatness, so compare
        // numerator^2 against (denominator*flatness)^2 to avoid a square root
        long df2 = denom2*flatnessSq >> 16; // S31.32

        long cross = (long)x0*y3 - (long)x3*y0; // S31.32
        long num1 = dx*y1 - dy*x1 + cross;      // S31.32
        num1 >>= 16;                            // S47.16
        long num1sq = num1*num1;                // S31.32
        if (num1sq > df2) {
            return false;
        }

        long num2 = dx*y2 - dy*x2 + cross;      // S31.32
        num2 >>= 16;                            // S47.16
        long num2sq = num2*num2;                // S31.32

        return num2sq < df2;
    
private booleanflatEnough(int x0, int y0, int x1, int y1, int x2, int y2)

        long dx = (long)x2 - (long)x0;
        long dy = (long)y2 - (long)y0;
        long denom2 = dx*dx + dy*dy;
        if (denom2 > MAX_CHORD_LENGTH_SQ) {
            return false;
        }

	// Stop dividing if all control points are close together
        if (denom2 < MIN_CHORD_LENGTH_SQ) {
	    int minx = Math.min(Math.min(x0, x1), x2);
	    int miny = Math.min(Math.min(y0, y1), y2);
	    int maxx = Math.max(Math.max(x0, x1), x2);
	    int maxy = Math.max(Math.max(y0, y1), y2);

	    long dx1 = (long)maxx - (long)minx;
	    long dy1 = (long)maxy - (long)miny;
	    long l2 = dx1*dx1 + dy1*dy1;
	    if (l2 < MIN_CHORD_LENGTH_SQ) {
		return true;
	    }
        }

        long num = -dy*x1 + dx*y1 + ((long)x0*y2 - (long)x2*y0);
        num >>= 16;
        long numsq = num*num;
        long df2 = denom2*flatnessSq >> 16;
        return numsq < df2;
    
public voidlineJoin()

        output.lineJoin();
    
public voidlineTo(int x1, int y1)

        output.lineJoin();
	output.lineTo(x1, y1);
        this.x0 = x1;
        this.y0 = y1;
    
public voidmoveTo(int x0, int y0)

	output.moveTo(x0, y0);
        this.x0 = x0;
        this.y0 = y0;
    
public voidquadTo(int x1, int y1, int x2, int y2)

        output.lineJoin();
        quadToHelper(x1, y1, x2, y2);
    
private voidquadToHelper(int x1, int y1, int x2, int y2)

	if (flatEnough(x0, y0, x1, y1, x2, y2)) {
	    output.lineTo(x1, y1);
	    output.lineTo(x2, y2);
	} else {
	    long lx1 = (long)x1;
	    long ly1 = (long)y1;
	    long x01 = x0 + lx1; // >> 1
	    long y01 = y0 + ly1; // >> 1
	    long x12 = lx1 + x2; // >> 1
	    long y12 = ly1 + y2; // >> 1

	    long x012 = x01 + x12; // >> 2
	    long y012 = y01 + y12; // >> 2
	    
	    quadToHelper((int)(x01 >> 1), (int)(y01 >> 1),
                         (int)(x012 >> 2), (int)(y012 >> 2));
	    quadToHelper((int)(x12 >> 1), (int)(y12 >> 1),
                         x2, y2); 
	}
	
	this.x0 = x2;
	this.y0 = y2;
    
public voidsetFlatness(int flatness)
Sets the desired output flatness for this Flattener.

param
flatness the maximum extent of a subdivided output line segment, in S15.16 format.

        this.flatness = flatness;
        this.flatnessSq = (int)((long)flatness*flatness >> 16);
    
public voidsetOutput(LineSink output)
Sets the output LineSink of this Flattener.

param
output an output LineSink.

        this.output = output;