001: /*
002: * $RCSfile: CompressionStreamNormal.java,v $
003: *
004: * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * - Redistribution of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * - Redistribution in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * Neither the name of Sun Microsystems, Inc. or the names of
019: * contributors may be used to endorse or promote products derived
020: * from this software without specific prior written permission.
021: *
022: * This software is provided "AS IS," without a warranty of any
023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
034: * POSSIBILITY OF SUCH DAMAGES.
035: *
036: * You acknowledge that this software is not designed, licensed or
037: * intended for use in the design, construction, operation or
038: * maintenance of any nuclear facility.
039: *
040: * $Revision: 1.5 $
041: * $Date: 2007/02/09 17:20:16 $
042: * $State: Exp $
043: */
044:
045: package com.sun.j3d.utils.compression;
046:
047: import javax.vecmath.Vector3f;
048:
049: /**
050: * This class represents a normal in a compression stream. It maintains both
051: * floating-point and quantized representations. This normal may be bundled
052: * with a vertex or exist separately as a global normal.
053: */
054: class CompressionStreamNormal extends CompressionStreamElement {
055: private int u, v;
056: private int specialOctant, specialSextant;
057: private float normalX, normalY, normalZ;
058:
059: int octant, sextant;
060: boolean specialNormal;
061: int uAbsolute, vAbsolute;
062:
063: /**
064: * Create a CompressionStreamNormal.
065: *
066: * @param stream CompressionStream associated with this element
067: * @param normal floating-point representation to be encoded
068: */
069: CompressionStreamNormal(CompressionStream stream, Vector3f normal) {
070: this .normalX = normal.x;
071: this .normalY = normal.y;
072: this .normalZ = normal.z;
073: stream.byteCount += 12;
074: }
075:
076: //
077: // Normal Encoding Parameterization
078: //
079: // A floating point normal is quantized to a desired number of bits by
080: // comparing it to candidate entries in a table of every possible normal
081: // at that quantization and finding the closest match. This table of
082: // normals is indexed by the following encoding:
083: //
084: // First, points on a unit radius sphere are parameterized by two angles,
085: // th and psi, using usual spherical coordinates. th is the angle about
086: // the y axis, psi is the inclination to the plane containing the point.
087: // The mapping between rectangular and spherical coordinates is:
088: //
089: // x = cos(th)*cos(psi)
090: // y = sin(psi)
091: // z = sin(th)*cos(psi)
092: //
093: // Points on sphere are folded first by octant, and then by sort order
094: // of xyz into one of six sextants. All the table encoding takes place in
095: // the positive octant, in the region bounded by the half spaces:
096: //
097: // x >= z
098: // z >= y
099: // y >= 0
100: //
101: // This triangular shaped patch runs from 0 to 45 degrees in th, and
102: // from 0 to as much as 0.615479709 (MAX_Y_ANG) in psi. The xyz bounds
103: // of the patch is:
104: //
105: // (1, 0, 0) (1/sqrt(2), 0, 1/sqrt(2)) (1/sqrt(3), 1/sqrt(3), 1/sqrt(3))
106: //
107: // When dicing this space up into discrete points, the choice for y is
108: // linear quantization in psi. This means that if the y range is to be
109: // divided up into n segments, the angle of segment j is:
110: //
111: // psi(j) = MAX_Y_ANG*(j/n)
112: //
113: // The y height of the patch (in arc length) is *not* the same as the xz
114: // dimension. However, the subdivision quantization needs to treat xz and
115: // y equally. To achieve this, the th angles are re-parameterized as
116: // reflected psi angles. That is, the i-th point's th is:
117: //
118: // th(i) = asin(tan(psi(i))) = asin(tan(MAX_Y_ANG*(i/n)))
119: //
120: // To go the other direction, the angle th corresponds to the real index r
121: // (in the same 0-n range as i):
122: //
123: // r(th) = n*atan(sin(th))/MAX_Y_ANG
124: //
125: // Rounded to the nearest integer, this gives the closest integer index i
126: // to the xz angle th. Because the triangle has a straight edge on the
127: // line x=z, it is more intuitive to index the xz angles in reverse
128: // order. Thus the two equations above are replaced by:
129: //
130: // th(i) = asin(tan(psi(i))) = asin(tan(MAX_Y_ANG*((n-i)/n)))
131: //
132: // r(th) = n*(1 - atan(sin(th))/MAX_Y_ANG)
133: //
134: // Each level of quantization subdivides the triangular patch twice as
135: // densely. The case in which only the three vertices of the triangle are
136: // present is the first logical stage of representation, but because of
137: // how the table is encoded the first usable case starts one level of
138: // sub-division later. This three point level has an n of 2 by the above
139: // conventions.
140: //
141: private static final int MAX_UV_BITS = 6;
142: private static final int MAX_UV_ENTRIES = 64;
143:
144: private static final double cgNormals[][][][] = new double[MAX_UV_BITS + 1][MAX_UV_ENTRIES + 1][MAX_UV_ENTRIES + 1][3];
145:
146: private static final double MAX_Y_ANG = 0.615479709;
147: private static final double UNITY_14 = 16384.0;
148:
149: private static void computeNormals() {
150: int inx, iny, inz, n;
151: double th, psi, qnx, qny, qnz;
152:
153: for (int quant = 0; quant <= MAX_UV_BITS; quant++) {
154: n = 1 << quant;
155:
156: for (int j = 0; j <= n; j++) {
157: for (int i = 0; i <= n; i++) {
158: if (i + j > n)
159: continue;
160:
161: psi = MAX_Y_ANG * (j / ((double) n));
162: th = Math.asin(Math.tan(MAX_Y_ANG
163: * ((n - i) / ((double) n))));
164:
165: qnx = Math.cos(th) * Math.cos(psi);
166: qny = Math.sin(psi);
167: qnz = Math.sin(th) * Math.cos(psi);
168:
169: // The normal table uses 16-bit components and must be
170: // able to represent both +1.0 and -1.0, so convert the
171: // floating point normal components to fixed point with 14
172: // fractional bits, a unity bit, and a sign bit (s1.14).
173: // Set them back to get the float equivalent.
174: qnx = qnx * UNITY_14;
175: inx = (int) qnx;
176: qnx = inx;
177: qnx = qnx / UNITY_14;
178:
179: qny = qny * UNITY_14;
180: iny = (int) qny;
181: qny = iny;
182: qny = qny / UNITY_14;
183:
184: qnz = qnz * UNITY_14;
185: inz = (int) qnz;
186: qnz = inz;
187: qnz = qnz / UNITY_14;
188:
189: cgNormals[quant][j][i][0] = qnx;
190: cgNormals[quant][j][i][1] = qny;
191: cgNormals[quant][j][i][2] = qnz;
192: }
193: }
194: }
195: }
196:
197: //
198: // An inverse sine table is used for each quantization level to take the Y
199: // component of a normal (which is the sine of the inclination angle) and
200: // obtain the closest quantized Y angle.
201: //
202: // At any level of compression, there are a fixed number of different Y
203: // angles (between 0 and MAX_Y_ANG). The inverse table is built to have
204: // slightly more than twice as many entries as y angles at any particular
205: // level; this ensures that the inverse look-up will get within one angle
206: // of the right one. The size of the table should be as small as
207: // possible, but with its delta sine still smaller than the delta sine
208: // between the last two angles to be encoded.
209: //
210: // Example: the inverse sine table has a maximum angle of 0.615479709. At
211: // the maximum resolution of 6 bits there are 65 discrete angles used,
212: // but twice as many are needed for thresholding between angles, so the
213: // delta angle is 0.615479709/128. The difference then between the last
214: // two angles to be encoded is:
215: // sin(0.615479709*128.0/128.0) - sin(0.615479709*127.0/128.0) = 0.003932730
216: //
217: // Using 8 significent bits below the binary point, fixed point can
218: // represent sines in increments of 0.003906250, just slightly smaller.
219: // However, because the maximum Y angle sine is 0.577350269, only 148
220: // instead of 256 table entries are needed.
221: //
222: private static final short inverseSine[][] = new short[MAX_UV_BITS + 1][];
223:
224: // UNITY_14 * sin(MAX_Y_ANGLE)
225: private static final short MAX_SIN_14BIT = 9459;
226:
227: private static void computeInverseSineTables() {
228: int intSin, deltaSin, intAngle;
229: double floatSin, floatAngle;
230: short sin14[] = new short[MAX_UV_ENTRIES + 1];
231:
232: // Build table of sines in s1.14 fixed point for each of the
233: // discrete angles used at maximum resolution.
234: for (int i = 0; i <= MAX_UV_ENTRIES; i++) {
235: sin14[i] = (short) (UNITY_14 * Math.sin(i * MAX_Y_ANG
236: / MAX_UV_ENTRIES));
237: }
238:
239: for (int quant = 0; quant <= MAX_UV_BITS; quant++) {
240: switch (quant) {
241: default:
242: case 6:
243: // Delta angle: MAX_Y_ANGLE/128.0
244: // Bits below binary point for fixed point delta sine: 8
245: // Integer delta sine: 64
246: // Inverse sine table size: 148 entries
247: deltaSin = 1 << (14 - 8);
248: break;
249: case 5:
250: // Delta angle: MAX_Y_ANGLE/64.0
251: // Bits below binary point for fixed point delta sine: 7
252: // Integer delta sine: 128
253: // Inverse sine table size: 74 entries
254: deltaSin = 1 << (14 - 7);
255: break;
256: case 4:
257: // Delta angle: MAX_Y_ANGLE/32.0
258: // Bits below binary point for fixed point delta sine: 6
259: // Integer delta sine: 256
260: // Inverse sine table size: 37 entries
261: deltaSin = 1 << (14 - 6);
262: break;
263: case 3:
264: // Delta angle: MAX_Y_ANGLE/16.0
265: // Bits below binary point for fixed point delta sine: 5
266: // Integer delta sine: 512
267: // Inverse sine table size: 19 entries
268: deltaSin = 1 << (14 - 5);
269: break;
270: case 2:
271: // Delta angle: MAX_Y_ANGLE/8.0
272: // Bits below binary point for fixed point delta sine: 4
273: // Integer delta sine: 1024
274: // Inverse sine table size: 10 entries
275: deltaSin = 1 << (14 - 4);
276: break;
277: case 1:
278: // Delta angle: MAX_Y_ANGLE/4.0
279: // Bits below binary point for fixed point delta sine: 3
280: // Integer delta sine: 2048
281: // Inverse sine table size: 5 entries
282: deltaSin = 1 << (14 - 3);
283: break;
284: case 0:
285: // Delta angle: MAX_Y_ANGLE/2.0
286: // Bits below binary point for fixed point delta sine: 2
287: // Integer delta sine: 4096
288: // Inverse sine table size: 3 entries
289: deltaSin = 1 << (14 - 2);
290: break;
291: }
292:
293: inverseSine[quant] = new short[(MAX_SIN_14BIT / deltaSin) + 1];
294:
295: intSin = 0;
296: for (int i = 0; i < inverseSine[quant].length; i++) {
297: // Compute float representation of integer sine with desired
298: // number of fractional bits by effectively right shifting 14.
299: floatSin = intSin / UNITY_14;
300:
301: // Compute the angle with this sine value and quantize it.
302: floatAngle = Math.asin(floatSin);
303: intAngle = (int) ((floatAngle / MAX_Y_ANG) * (1 << quant));
304:
305: // Choose the closest of the three nearest quantized values
306: // intAngle-1, intAngle, and intAngle+1.
307: if (intAngle > 0) {
308: if (Math.abs(sin14[intAngle << (6 - quant)]
309: - intSin) > Math
310: .abs(sin14[(intAngle - 1) << (6 - quant)]
311: - intSin))
312: intAngle = intAngle - 1;
313: }
314:
315: if (intAngle < (1 << quant)) {
316: if (Math.abs(sin14[intAngle << (6 - quant)]
317: - intSin) > Math
318: .abs(sin14[(intAngle + 1) << (6 - quant)]
319: - intSin))
320: intAngle = intAngle + 1;
321: }
322:
323: inverseSine[quant][i] = (short) intAngle;
324: intSin += deltaSin;
325: }
326: }
327: }
328:
329: /**
330: * Compute static tables needed for normal quantization.
331: */
332: static {
333: computeNormals();
334: computeInverseSineTables();
335: }
336:
337: /**
338: * Quantize the floating point normal to a 6-bit octant/sextant plus u,v
339: * components of [0..6] bits. Full resolution is 18 bits and the minimum
340: * is 6 bits.
341: *
342: * @param stream CompressionStream associated with this element
343: * @param table HuffmanTable for collecting data about the quantized
344: * representation of this element
345: */
346: void quantize(CompressionStream stream, HuffmanTable huffmanTable) {
347: double nx, ny, nz, t;
348:
349: // Clamp UV quantization.
350: int quant = (stream.normalQuant < 0 ? 0
351: : (stream.normalQuant > 6 ? 6 : stream.normalQuant));
352:
353: nx = normalX;
354: ny = normalY;
355: nz = normalZ;
356:
357: octant = 0;
358: sextant = 0;
359: u = 0;
360: v = 0;
361:
362: // Normalize the fixed point normal to the positive signed octant.
363: if (nx < 0.0) {
364: octant |= 4;
365: nx = -nx;
366: }
367: if (ny < 0.0) {
368: octant |= 2;
369: ny = -ny;
370: }
371: if (nz < 0.0) {
372: octant |= 1;
373: nz = -nz;
374: }
375:
376: // Normalize the fixed point normal to the proper sextant of the octant.
377: if (nx < ny) {
378: sextant |= 1;
379: t = nx;
380: nx = ny;
381: ny = t;
382: }
383: if (nz < ny) {
384: sextant |= 2;
385: t = ny;
386: ny = nz;
387: nz = t;
388: }
389: if (nx < nz) {
390: sextant |= 4;
391: t = nx;
392: nx = nz;
393: nz = t;
394: }
395:
396: // Convert the floating point y component to s1.14 fixed point.
397: int yInt = (int) (ny * UNITY_14);
398:
399: // The y component of the normal is the sine of the y angle. Quantize
400: // the y angle by using the fixed point y component as an index into
401: // the inverse sine table of the correct size for the quantization
402: // level. (12 - quant) bits of the s1.14 y normal component are
403: // rolled off with a right shift; the remaining bits then match the
404: // number of bits used to represent the delta sine of the table.
405: int yIndex = inverseSine[quant][yInt >> (12 - quant)];
406:
407: // Search the two xz rows near y for the best match.
408: int ii = 0;
409: int jj = 0;
410: int n = 1 << quant;
411: double dot, bestDot = -1;
412:
413: for (int j = yIndex - 1; j < yIndex + 1 && j <= n; j++) {
414: if (j < 0)
415: continue;
416:
417: for (int i = 0; i <= n; i++) {
418: if (i + j > n)
419: continue;
420:
421: dot = nx * cgNormals[quant][j][i][0] + ny
422: * cgNormals[quant][j][i][1] + nz
423: * cgNormals[quant][j][i][2];
424:
425: if (dot > bestDot) {
426: bestDot = dot;
427: ii = i;
428: jj = j;
429: }
430: }
431: }
432:
433: // Convert u and v to standard grid form.
434: u = ii << (6 - quant);
435: v = jj << (6 - quant);
436:
437: // Check for special normals and specially encode them.
438: specialNormal = false;
439: if (u == 64 && v == 0) {
440: // six coordinate axes case
441: if (sextant == 0 || sextant == 2) {
442: // +/- x-axis
443: specialSextant = 0x6;
444: specialOctant = ((octant & 4) != 0) ? 0x2 : 0;
445:
446: } else if (sextant == 3 || sextant == 1) {
447: // +/- y-axis
448: specialSextant = 0x6;
449: specialOctant = 4 | (((octant & 2) != 0) ? 0x2 : 0);
450:
451: } else if (sextant == 5 || sextant == 4) {
452: // +/- z-axis
453: specialSextant = 0x7;
454: specialOctant = ((octant & 1) != 0) ? 0x2 : 0;
455: }
456: specialNormal = true;
457: u = v = 0;
458:
459: } else if (u == 0 && v == 64) {
460: // eight mid point case
461: specialSextant = 6 | (octant >> 2);
462: specialOctant = ((octant & 0x3) << 1) | 1;
463: specialNormal = true;
464: u = v = 0;
465: }
466:
467: // Compute deltas if possible.
468: // Use the non-normalized ii and jj indices.
469: int du = 0;
470: int dv = 0;
471: int uv64 = 64 >> (6 - quant);
472:
473: absolute = false;
474: if (stream.firstNormal || stream.normalQuantChanged
475: || stream.lastSpecialNormal || specialNormal) {
476: // The first normal by definition is absolute, and normals cannot
477: // be represented as deltas to or from special normals, nor from
478: // normals with a different quantization.
479: absolute = true;
480: stream.firstNormal = false;
481: stream.normalQuantChanged = false;
482:
483: } else if (stream.lastOctant == octant
484: && stream.lastSextant == sextant) {
485: // Deltas are always allowed within the same sextant/octant.
486: du = ii - stream.lastU;
487: dv = jj - stream.lastV;
488:
489: } else if (stream.lastOctant != octant
490: && stream.lastSextant == sextant
491: && (((sextant == 1 || sextant == 5) && (stream.lastOctant & 3) == (octant & 3))
492: || ((sextant == 0 || sextant == 4) && (stream.lastOctant & 5) == (octant & 5)) || ((sextant == 2 || sextant == 3) && (stream.lastOctant & 6) == (octant & 6)))) {
493: // If the sextants are the same, the octants can differ only when
494: // they are bordering each other on the same edge that the
495: // sextant has.
496: du = ii - stream.lastU;
497: dv = -jj - stream.lastV;
498:
499: // Can't delta by less than -64.
500: if (dv < -uv64)
501: absolute = true;
502:
503: // Can't delta doubly defined points.
504: if (jj == 0)
505: absolute = true;
506:
507: } else if (stream.lastOctant == octant
508: && stream.lastSextant != sextant
509: && ((sextant == 0 && stream.lastSextant == 4)
510: || (sextant == 4 && stream.lastSextant == 0)
511: || (sextant == 1 && stream.lastSextant == 5)
512: || (sextant == 5 && stream.lastSextant == 1)
513: || (sextant == 2 && stream.lastSextant == 3) || (sextant == 3 && stream.lastSextant == 2))) {
514: // If the octants are the same, the sextants must border on
515: // the i side (this case) or the j side (next case).
516: du = -ii - stream.lastU;
517: dv = jj - stream.lastV;
518:
519: // Can't delta by less than -64.
520: if (du < -uv64)
521: absolute = true;
522:
523: // Can't delta doubly defined points.
524: if (ii == 0)
525: absolute = true;
526:
527: } else if (stream.lastOctant == octant
528: && stream.lastSextant != sextant
529: && ((sextant == 0 && stream.lastSextant == 2)
530: || (sextant == 2 && stream.lastSextant == 0)
531: || (sextant == 1 && stream.lastSextant == 3)
532: || (sextant == 3 && stream.lastSextant == 1)
533: || (sextant == 4 && stream.lastSextant == 5) || (sextant == 5 && stream.lastSextant == 4))) {
534: // If the octants are the same, the sextants must border on
535: // the j side (this case) or the i side (previous case).
536: if (((ii + jj) != uv64) && (ii != 0) && (jj != 0)) {
537: du = uv64 - ii - stream.lastU;
538: dv = uv64 - jj - stream.lastV;
539:
540: // Can't delta by greater than +63.
541: if ((du >= uv64) || (dv >= uv64))
542: absolute = true;
543: } else
544: // Can't delta doubly defined points.
545: absolute = true;
546:
547: } else
548: // Can't delta this normal.
549: absolute = true;
550:
551: if (absolute == false) {
552: // Convert du and dv to standard grid form.
553: u = du << (6 - quant);
554: v = dv << (6 - quant);
555: }
556:
557: // Compute length and shift common to all components.
558: computeLengthShift(u, v);
559:
560: if (absolute && length > 6) {
561: // Absolute normal u, v components are unsigned 6-bit integers, so
562: // truncate the 0 sign bit for values > 0x001f.
563: length = 6;
564: }
565:
566: // Add this element to the Huffman table associated with this stream.
567: huffmanTable.addNormalEntry(length, shift, absolute);
568:
569: // Save current normal as last.
570: stream.lastSextant = sextant;
571: stream.lastOctant = octant;
572: stream.lastU = ii;
573: stream.lastV = jj;
574: stream.lastSpecialNormal = specialNormal;
575:
576: // Copy and retain absolute normal for mesh buffer lookup.
577: uAbsolute = ii;
578: vAbsolute = jj;
579: }
580:
581: /**
582: * Output a setNormal command.
583: *
584: * @param table HuffmanTable mapping quantized representations to
585: * compressed encodings
586: * @param output CommandStream for collecting compressed output
587: */
588: void outputCommand(HuffmanTable table, CommandStream output) {
589: outputNormal(table, output, CommandStream.SET_NORM, 8);
590: }
591:
592: /**
593: * Output a normal subcommand.
594: *
595: * @param table HuffmanTable mapping quantized representations to
596: * compressed encodings
597: * @param output CommandStream for collecting compressed output
598: */
599: void outputSubcommand(HuffmanTable table, CommandStream output) {
600: outputNormal(table, output, 0, 6);
601: }
602:
603: //
604: // Output the final compressed bits to the output command stream.
605: //
606: private void outputNormal(HuffmanTable table, CommandStream output,
607: int header, int headerLength) {
608:
609: HuffmanNode t;
610:
611: // Look up the Huffman token for this compression stream element.
612: t = table.getNormalEntry(length, shift, absolute);
613:
614: // Construct the normal subcommand.
615: int componentLength = t.dataLength - t.shift;
616: int subcommandLength = 0;
617: long normalSubcommand = 0;
618:
619: if (absolute) {
620: // A 3-bit sextant and a 3-bit octant are always present.
621: subcommandLength = t.tagLength + 6;
622:
623: if (specialNormal)
624: // Use the specially-encoded sextant and octant.
625: normalSubcommand = (t.tag << 6) | (specialSextant << 3)
626: | specialOctant;
627: else
628: // Use the general encoding rule.
629: normalSubcommand = (t.tag << 6) | (sextant << 3)
630: | octant;
631: } else {
632: // The tag is immediately followed by the u and v delta components.
633: subcommandLength = t.tagLength;
634: normalSubcommand = t.tag;
635: }
636:
637: // Add the u and v values to the subcommand.
638: subcommandLength += (2 * componentLength);
639:
640: u = (u >> t.shift) & (int) lengthMask[componentLength];
641: v = (v >> t.shift) & (int) lengthMask[componentLength];
642:
643: normalSubcommand = (normalSubcommand << (2 * componentLength))
644: | (u << (1 * componentLength))
645: | (v << (0 * componentLength));
646:
647: if (subcommandLength < 6) {
648: // The header will have some empty bits. The Huffman tag
649: // computation will prevent this if necessary.
650: header |= (int) (normalSubcommand << (6 - subcommandLength));
651: subcommandLength = 0;
652: } else {
653: // Move the 1st 6 bits of the subcommand into the header.
654: header |= (int) (normalSubcommand >>> (subcommandLength - 6));
655: subcommandLength -= 6;
656: }
657:
658: // Add the header and body to the output buffer.
659: output.addCommand(header, headerLength, normalSubcommand,
660: subcommandLength);
661: }
662:
663: public String toString() {
664: String fixed;
665:
666: if (specialNormal)
667: fixed = " special normal, sextant " + specialSextant
668: + " octant " + specialOctant;
669:
670: else if (absolute)
671: fixed = " sextant " + sextant + " octant " + octant + " u "
672: + u + " v " + v;
673: else
674: fixed = " du " + u + " dv " + v;
675:
676: return "normal: " + normalX + " " + normalY + " " + normalZ
677: + "\n" + fixed + "\n" + " length " + length + " shift "
678: + shift + (absolute ? " absolute" : " relative");
679: }
680: }
|