001: /*
002: *
003: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License version
008: * 2 only, as published by the Free Software Foundation.
009: *
010: * This program is distributed in the hope that it will be useful, but
011: * WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * General Public License version 2 for more details (a copy is
014: * included at /legal/license.txt).
015: *
016: * You should have received a copy of the GNU General Public License
017: * version 2 along with this work; if not, write to the Free Software
018: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
019: * 02110-1301 USA
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
022: * Clara, CA 95054 or visit www.sun.com if you need additional
023: * information or have any questions.
024: */
025:
026: package com.sun.pisces;
027:
028: /**
029: * The <code>Dasher</code> class takes a series of linear commands
030: * (<code>moveTo</code>, <code>lineTo</code>, <code>close</code> and
031: * <code>end</code>) and breaks them into smaller segments according to a
032: * dash pattern array and a starting dash phase.
033: *
034: * <p> Issues: in J2Se, a zero length dash segment as drawn as a very
035: * short dash, whereas Pisces does not draw anything. The PostScript
036: * semantics are unclear.
037: *
038: */
039: public class Dasher extends LineSink {
040:
041: LineSink output;
042: int[] dash;
043: int startPhase;
044: int startIdx;
045:
046: int idx;
047: int phase;
048:
049: int sx, sy;
050: int x0, y0;
051:
052: int m00, m01;
053: int m10, m11;
054:
055: Transform4 transform;
056:
057: boolean symmetric;
058: long ldet;
059:
060: boolean firstDashOn;
061: boolean starting;
062: int sx1, sy1;
063:
064: /**
065: * Empty constructor. <code>setOutput</code> and
066: * <code>setParameters</code> must be called prior to calling any
067: * other methods.
068: */
069: public Dasher() {
070: }
071:
072: /**
073: * Constructs a <code>Dasher</code>.
074: *
075: * @param output an output <code>LineSink</code>.
076: * @param dash an array of <code>int</code>s containing the dash
077: * pattern in S15.16 format.
078: * @param phase an <code>int</code> containing the dash phase in
079: * S15.16 format.
080: * @param transform a <code>Transform4</code> object indicating
081: * the transform that has been previously applied to all incoming
082: * coordinates. This is required in order to compute dash lengths
083: * properly.
084: */
085: public Dasher(LineSink output, int[] dash, int phase,
086: Transform4 transform) {
087: setOutput(output);
088: setParameters(dash, phase, transform);
089: }
090:
091: /**
092: * Sets the output <code>LineSink</code> of this
093: * <code>Dasher</code>.
094: *
095: * @param output an output <code>LineSink</code>.
096: */
097: public void setOutput(LineSink output) {
098: this .output = output;
099: }
100:
101: /**
102: * Sets the parameters of this <code>Dasher</code>.
103: *
104: * @param dash an array of <code>int</code>s containing the dash
105: * pattern in S15.16 format.
106: * @param phase an <code>int</code> containing the dash phase in
107: * S15.16 format.
108: * @param transform a <code>Transform4</code> object indicating
109: * the transform that has been previously applied to all incoming
110: * coordinates. This is required in order to compute dash lengths
111: * properly.
112: */
113: public void setParameters(int[] dash, int phase,
114: Transform4 transform) {
115: if (phase < 0) {
116: throw new IllegalArgumentException("phase < 0 !");
117: }
118:
119: // Normalize so 0 <= phase < dash[0]
120: int idx = 0;
121: int d;
122: while (phase >= (d = dash[idx])) {
123: phase -= d;
124: idx = (idx + 1) % dash.length;
125: }
126:
127: this .dash = new int[dash.length];
128: for (int i = 0; i < dash.length; i++) {
129: this .dash[i] = dash[i];
130: }
131: this .startPhase = this .phase = phase;
132: this .startIdx = idx;
133:
134: this .transform = transform;
135:
136: this .m00 = transform.m00;
137: this .m01 = transform.m01;
138: this .m10 = transform.m10;
139: this .m11 = transform.m11;
140: this .ldet = ((long) m00 * m11 - (long) m01 * m10) >> 16;
141: this .symmetric = (m00 == m11 && m10 == -m01);
142: }
143:
144: public void moveTo(int x0, int y0) {
145: output.moveTo(x0, y0);
146: this .idx = startIdx;
147: this .phase = this .startPhase;
148: this .sx = this .x0 = x0;
149: this .sy = this .y0 = y0;
150: this .starting = true;
151: }
152:
153: public void lineJoin() {
154: output.lineJoin();
155: }
156:
157: private void goTo(int x1, int y1) {
158: if ((idx % 2) == 0) {
159: if (starting) {
160: this .sx1 = x1;
161: this .sy1 = y1;
162: firstDashOn = true;
163: starting = false;
164: }
165: output.lineTo(x1, y1);
166: } else {
167: if (starting) {
168: firstDashOn = false;
169: starting = false;
170: }
171: output.moveTo(x1, y1);
172: }
173: this .x0 = x1;
174: this .y0 = y1;
175: }
176:
177: public void lineTo(int x1, int y1) {
178: while (true) {
179: int d = dash[idx] - phase;
180: int lx = x1 - x0;
181: int ly = y1 - y0;
182:
183: // Compute segment length in the untransformed
184: // coordinate system
185: // IMPL NOTE - use fixed point
186:
187: int l;
188: if (symmetric) {
189: l = (int) ((PiscesMath.hypot(lx, ly) * 65536L) / ldet);
190: } else {
191: long la = ((long) ly * m00 - (long) lx * m10) / ldet;
192: long lb = ((long) ly * m01 - (long) lx * m11) / ldet;
193: l = (int) PiscesMath.hypot(la, lb);
194: }
195:
196: if (l < d) {
197: goTo(x1, y1);
198: // Advance phase within current dash segment
199: phase += l;
200: return;
201: }
202:
203: long t;
204: int xsplit, ysplit;
205: // // For zero length dashses, SE appears to move 1/8 unit
206: // // in device space
207: // if (d == 0) {
208: // double dlx = lx/65536.0;
209: // double dly = ly/65536.0;
210: // len = PiscesMath.hypot(dlx, dly);
211: // double dt = 1.0/(8*len);
212: // double dxsplit = (x0/65536.0) + dt*dlx;
213: // double dysplit = (y0/65536.0) + dt*dly;
214: // xsplit = (int)(dxsplit*65536.0);
215: // ysplit = (int)(dysplit*65536.0);
216: // } else {
217: t = ((long) d << 16) / l;
218: xsplit = x0 + (int) (t * (x1 - x0) >> 16);
219: ysplit = y0 + (int) (t * (y1 - y0) >> 16);
220: // }
221: goTo(xsplit, ysplit);
222:
223: // Advance to next dash segment
224: idx = (idx + 1) % dash.length;
225: phase = 0;
226: }
227: }
228:
229: public void close() {
230: lineTo(sx, sy);
231: if (firstDashOn) {
232: output.lineTo(sx1, sy1);
233: }
234: }
235:
236: public void end() {
237: output.end();
238: }
239: }
|