Source code: com/trapezium/chisel/cleaners/IFS_DupCoordDetector.java
1 /*
2 * @(#)IFS_DupCoordDetector.java
3 *
4 * Copyright (c) 1998 by Trapezium Development LLC. All Rights Reserved.
5 *
6 * The information in this file is the property of Trapezium Development LLC
7 * and may be used only in accordance with the terms of the license granted
8 * by Trapezium.
9 *
10 */
11 package com.trapezium.chisel.cleaners;
12
13 import com.trapezium.parse.TokenEnumerator;
14 import com.trapezium.vrml.VrmlElement;
15 import com.trapezium.vrml.Value;
16 import com.trapezium.vrml.node.Node;
17 import com.trapezium.vrml.node.DEFUSENode;
18 import com.trapezium.vrml.fields.Field;
19 import com.trapezium.vrml.fields.FieldValue;
20 import com.trapezium.vrml.fields.MFFieldValue;
21 import com.trapezium.chisel.*;
22
23 import java.util.Vector;
24 import java.io.PrintStream;
25
26 /**
27 * Detects and removes unnecessarily duplicated values in a coord, texCoord, color,
28 * or normal node. These are not removed if the Coordinate/TextureCoordinate/Color/
29 * Normal node are DEFfed and USEd by a ROUTE. This is based on the assumption that
30 * the ROUTE changes the values, therefore editting these affects the corresponding
31 * "index" field, which exists for ROUTEd values and not for the pre-existing values.
32 *
33 * @author Johannes N. Johannsen
34 * @version 1.0, 16 Oct 1998 (really older, just added header)
35 *
36 * @since 1.0
37 */
38
39 public class IFS_DupCoordDetector extends Optimizer {
40 int mergedPointCount = 0;
41 int modifiedFaceCount = 0;
42
43 class CoordMergeData {
44 int[] offsets;
45 Vector[] vlistKey;
46 Vector[] vlistValue;
47
48 public CoordMergeData( int[] offsets ) {
49 this.offsets = offsets;
50 vlistKey = new Vector[ 512 ];
51 vlistValue = new Vector[ 512 ];
52 }
53
54 public int[] getOffsets() {
55 return( offsets );
56 }
57
58 public void makeUnique( int offset ) {
59 offsets[ offset ] = offset;
60 }
61
62 public void wipeout() {
63 vlistKey = null;
64 vlistValue = null;
65 }
66
67 boolean arraysEqual( byte[] s1, byte[] s2 ) {
68 int len = s1.length;
69 for ( int i = 0; i < len; i++ ) {
70 if ( s1[i] != s2[i] ) {
71 return( false );
72 }
73 }
74 return( true );
75 }
76
77 public Integer get( byte[] key ) {
78 int offset = key[0];
79 if ( key.length > 3 ) {
80 offset += (key[1] + key[2] + key[3]);
81 }
82 offset += key[ key.length - 1 ];
83 offset = offset & 0x1ff;
84 Vector keyVector = vlistKey[ offset ];
85 if ( keyVector != null ) {
86 int keyVecSize = keyVector.size();
87 for ( int i = 0; i < keyVecSize; i++ ) {
88 byte[] entry = (byte[])keyVector.elementAt( i );
89 if ( entry.length == key.length ) {
90 if ( arraysEqual( entry, key )) {
91 Vector valueVector = vlistValue[ offset ];
92 return( (Integer)valueVector.elementAt( i ));
93 }
94 }
95 }
96 }
97 return( null );
98 }
99
100 public void put( byte[] key, Integer keyValue ) {
101 int offset = key[0];
102 if ( key.length > 3 ) {
103 offset += (key[1] + key[2] + key[3]);
104 }
105 offset += key[ key.length - 1 ];
106 offset = offset & 0x1ff;
107 if ( vlistKey[ offset ] == null ) {
108 vlistKey[ offset ] = new Vector();
109 vlistValue[ offset ] = new Vector();
110 }
111 vlistKey[ offset ].addElement( key );
112 vlistValue[ offset ].addElement( keyValue );
113 }
114
115 }
116
117 /** Class constructor */
118 public IFS_DupCoordDetector() {
119 super( "CoordinateOwner", "Removing duplicate values..." );
120 }
121
122 class CharArrayContainer {
123 char[] charArray;
124 CharArrayContainer( char[] a ) {
125 charArray = a;
126 }
127 public char[] getCharArray() {
128 return( charArray );
129 }
130 }
131
132 int totalMadeUnique = 0;
133 public void attemptOptimization( Node n ) {
134 CoordMergeData coord_cmd = attemptOptimization( n, "coord", "coordIndex", "point" );
135 CoordMergeData tex_cmd = attemptOptimization( n, "texCoord", "texCoordIndex", "point" );
136 CoordMergeData clr_cmd = attemptOptimization( n, "color", "colorIndex", "color" );
137 CoordMergeData normal_cmd = attemptOptimization( n, "normal", "normalIndex", "vector" );
138
139 // adjust coord_cmd based on texCoord (see comment below)
140 if (( coord_cmd != null ) && ( tex_cmd != null ) && ( n.getField( "texCoordIndex" ) == null )) {
141 // this means there is no texCoordIndex, and texCoord maps one-to-one with coords.
142 // So if there is a coord merge, and the texCoords are not the same, then we
143 // have to undo the coord merge
144 int[] coordOffsets = coord_cmd.getOffsets();
145 int[] texCoordOffsets = tex_cmd.getOffsets();
146 int numberCoords = coordOffsets.length;
147 int numberTexCoords = texCoordOffsets.length;
148 int count = numberCoords;
149 if ( numberTexCoords < count ) {
150 count = numberTexCoords;
151 }
152 int discrepancyCount = 0;
153 int mergeCount = 0;
154 for ( int i = 0; i < count; i++ ) {
155 if ( coordOffsets[i] != i ) {
156 mergeCount++;
157 if ( texCoordOffsets[i] != texCoordOffsets[coordOffsets[i]] ) {
158 discrepancyCount++;
159 coord_cmd.makeUnique( i );
160 }
161 }
162 }
163 totalMadeUnique += discrepancyCount;
164 // System.out.println( "mergeCount " + mergeCount + ", discrepancyCount " + discrepancyCount + ", totalMadeUnique " + totalMadeUnique );
165 }
166 }
167
168 /**
169 * This needs some work, when the CoordMergeData gets made, the
170 * offsets need to be readjusted, depending on how the related sets
171 * get merged. For example, "texCoord" alone map one-to-one with
172 * coordinates if there is no "texCoordIndex". In this case, the
173 * offsets of the two arrays must match exactly. Any place they don't
174 * match must be un-optimized.
175 *
176 * @param n the Node to optimize
177 * @param nodeFieldName the name of the IFS node field to attempt optimization for,
178 * possible values are "coord", "normal", "color", "texCoord"
179 * @param indexName the name of the index field that corresponds to the node field,
180 * possible values are "coordIndex", "normalIndex", "colorIndex", "texCoordIndex"
181 * @param valueName the name of the field within the value node containing values
182 *
183 * @return a CoordMergeData object used during the actual optimization. If the
184 * Node is DEFfed and USEd by a ROUTE, it is not optimized, and null is returned.
185 */
186 CoordMergeData attemptOptimization( Node n, String nodeFieldName, String indexName, String valueName ) {
187 if ( n.isFieldUsedByROUTE( nodeFieldName )) {
188 return( null );
189 }
190 Field coord = n.getField( nodeFieldName );
191 Field coordIndex = n.getField( indexName );
192 int factor = 3;
193 if ( nodeFieldName.compareTo( "texCoord" ) == 0 ) {
194 factor = 2;
195 }
196 if ( coord != null ) {
197 FieldValue fv = coord.getFieldValue();
198 Node coordNode = (Node)fv.getChildAt( 0 );
199 if ( coordNode instanceof DEFUSENode ) {
200 DEFUSENode dun = (DEFUSENode)coordNode;
201 coordNode = dun.getNode();
202 }
203 if ( coordNode != null ) {
204 MFFieldValue coordValues = null;
205 Field coords = coordNode.getField( valueName );
206 if ( coords == null ) {
207 return( null );
208 }
209 if ( coords.isISfield() ) {
210 return( null );
211 }
212 FieldValue cfv = coords.getFieldValue();
213 if ( cfv instanceof MFFieldValue ) {
214 coordValues = (MFFieldValue)cfv;
215 // System.out.println( "raw value count " + coordValues.getRawValueCount() + ", factor " + factor );
216 }
217 if ( coordValues == null ) {
218 return( null );
219 }
220 int numberCoords = coordValues.getRawValueCount()/factor;
221 if ( numberCoords < 2 ) {
222 return( null );
223 }
224 int[] offsets = new int[ numberCoords ];
225 for ( int i = 0; i < numberCoords; i++ ) {
226 offsets[i] = i;
227 }
228 int scannerOffset = coords.getFirstTokenOffset();
229 int endOffset = coords.getLastTokenOffset();
230 CoordMergeData cmd = new CoordMergeData( offsets );
231 int coordNumber = 0;
232 while ( true ) {
233 if ( dataSource.isNumber( scannerOffset )) {
234 int n1 = scannerOffset;
235 int n2 = dataSource.getNextToken( n1 );
236 if ( factor == 3 ) {
237 int n3 = dataSource.getNextToken( n2 );
238 byte[] result = dataSource.getCharArray( n1, n2, n3 );
239 Integer iresult = cmd.get( result );
240 if ( iresult == null ) {
241 cmd.put( result, new Integer( coordNumber ));
242 } else {
243 offsets[ coordNumber ] = iresult.intValue();
244 mergedPointCount++;
245 }
246 coordNumber++;
247 scannerOffset = n3;
248 } else {
249 byte[] result = dataSource.getCharArray( n1, n2 );
250 Integer iresult = cmd.get( result );
251 if ( iresult == null ) {
252 cmd.put( result, new Integer( coordNumber ));
253 } else {
254 offsets[ coordNumber ] = iresult.intValue();
255 mergedPointCount++;
256 }
257 coordNumber++;
258 scannerOffset = n2;
259 }
260 }
261 if ( scannerOffset == endOffset ) {
262 break;
263 }
264 if ( coordNumber >= numberCoords ) {
265 break;
266 }
267 scannerOffset = dataSource.getNextToken( scannerOffset );
268 if ( scannerOffset == -1 ) {
269 return( null );
270 }
271 }
272 cmd.wipeout();
273 if ( coordIndex != null ) {
274 replaceRange( coordIndex.getFirstTokenOffset(), coordIndex.getLastTokenOffset(), cmd );
275 }
276 return( cmd );
277 }
278 }
279 return( null );
280 }
281
282 /** Replace index values so that they only refer to a coordinate with a given
283 * value one way. Duplicate coordinates then end up not being referenced, which
284 * allows them to be clean with the IFS_CoordRemover chisel.
285 *
286 * @param tp print destination
287 * @param param coordinate merge info
288 * @param startTokenOffset first offset in the range of tokens to replace
289 * @param endTokenOffset last offset in the range of tokens to replace
290 */
291 public void optimize( TokenPrinter tp, Object param, int startTokenOffset, int endTokenOffset ) {
292 int scannerOffset = startTokenOffset;
293 CoordMergeData cmd = (CoordMergeData)param;
294 int[] offsets = cmd.getOffsets();
295 int offsetSize = offsets.length;
296 boolean modifiedFace = false;
297 dataSource.setState( scannerOffset );
298 while ( scannerOffset != -1 ) {
299 if ( dataSource.isNumber( scannerOffset )) {
300 int ival = dataSource.getIntValue( scannerOffset );
301 if ( ival == -1 ) {
302 tp.print( dataSource, scannerOffset );
303 modifiedFace = false;
304 } else if (( ival >= 0 ) && ( ival < offsetSize )) {
305 tp.print( offsets[ ival ] );
306 if ( offsets[ ival ] != ival ) {
307 if ( !modifiedFace ) {
308 modifiedFaceCount++;
309 modifiedFace = true;
310 }
311 }
312 }
313 } else {
314 tp.print( dataSource, scannerOffset );
315 }
316 if ( scannerOffset == endTokenOffset ) {
317 break;
318 }
319 scannerOffset = dataSource.getNextToken( scannerOffset );
320 }
321 }
322
323 public void summarize( PrintStream ps ) {
324 ps.println( "mergedPointCount is " + mergedPointCount );
325 ps.println( "modifiedFaceCount is " + modifiedFaceCount );
326 }
327 }
328
329