Source code: com/trapezium/bean/BehaviorBean.java
1 /*
2 * @(#)BehaviorBean.java
3 *
4 * Copyright (c) 1999 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.bean;
12
13 import com.trapezium.parse.TokenEnumerator;
14 import com.trapezium.humanoid.Humanoid_1_0;
15 import com.trapezium.vrml.ROUTE;
16 import com.trapezium.vrml.VrmlElement;
17 import com.trapezium.vrml.Scene;
18 import com.trapezium.vrml.node.Node;
19 import com.trapezium.chisel.TokenPrinter;
20 import com.trapezium.vrml.node.DEFUSENode;
21 import com.trapezium.vrml.node.PROTOInstance;
22 import com.trapezium.vrml.node.PROTObase;
23
24 import java.io.*;
25 import java.util.Hashtable;
26 import java.util.Enumeration;
27 import java.util.Vector;
28
29 /** The BehaviorBean contains the set of DEFs and associated ROUTEs for a particular
30 * behavior. At the moment, it uses an "ultimate" root, which is the first ROUTE
31 * in a chain of ROUTEs. This might not be appropriate, since this might be some
32 * sort of sensor with associated geometry.
33 *
34 * An alternate approach, which might be better in other cases is to just use the
35 * first TimeSensor encountered as the "ultimate" root.
36 *
37 */
38 public class BehaviorBean implements java.io.Serializable, Humanoid_1_0 {
39
40 // the DEFs used by this behavior
41 Vector defs;
42
43 // the ROUTEs used by this behavior
44 Vector routes;
45
46 // no longer used, but needed if we include touch sensor stuff
47 Vector sensorContainingNodes;
48 Hashtable defToJoint;
49 Hashtable jointToDef;
50 ROUTE ultimate;
51
52 // modification points are where token offsets have shifted
53 int[] modificationPoints;
54 int[] modificationValue;
55 int mpidx;
56 void addModificationPoint( int offset, int value ) {
57 modificationPoints[mpidx] = offset;
58 modificationValue[mpidx] = value;
59 mpidx++;
60 }
61 int getModifiedStartValue( int offset ) {
62 for ( int i = 0; i < mpidx; i++ ) {
63 if ( offset > modificationPoints[i] ) {
64 offset += modificationValue[i];
65 }
66 }
67 return( offset );
68 }
69 int getModifiedEndValue( int offset ) {
70 for ( int i = 0; i < mpidx; i++ ) {
71 if ( offset > (modificationPoints[i] + modificationValue[i])) {
72 offset += modificationValue[i];
73 }
74 }
75 return( offset );
76 }
77
78 public BehaviorBean( ROUTE ultimate, Hashtable defToJoint, Hashtable jointToDef ) {
79 modificationPoints = new int[100];
80 modificationValue = new int[100];
81 mpidx = 0;
82 this.ultimate = ultimate;
83 routes = new Vector();
84 defs = new Vector();
85 sensorContainingNodes = new Vector();
86 this.defToJoint = defToJoint;
87 this.jointToDef = jointToDef;
88 }
89
90 private void writeObject( ObjectOutputStream stream ) throws java.io.IOException {
91 System.out.println( "BehaviorBean.writeObject" );
92 stream.defaultWriteObject();
93 }
94
95 /** Save ROUTE info. */
96 public void saveROUTE( ROUTE route ) {
97 if ( routes.indexOf( route ) == -1 ) {
98 routes.addElement( route );
99 }
100 }
101
102 /** Save DEF info, but only for DEFs that aren't joints. */
103 public void saveDEF( String defName ) {
104 if ( defs.indexOf( defName ) == -1 ) {
105 if ( defToJoint.get( defName ) == null ) {
106 defs.addElement( defName );
107 }
108 }
109 }
110
111 /** Generate VRML for this, using DEF names from the humanoid
112 * if the ROUTE is referring to a Joint.
113 *
114 * @param humanBean used to resolve Joint DEF names to one used by
115 * HumanBean in the case where the behavior was originally applied
116 * to a different humanoid
117 * @param ps where to print the data
118 * @param behaviorNumber used to rename behaviors
119 * @param defsGenerated list of DEFs that text has been generated for,
120 * to prevent repeatedly generating text for a DEF that was used
121 * in more than one behavior.
122 */
123 public void generateVRML( HumanBean humanBean, PrintStream ps, int behaviorNumber, Hashtable defsGenerated, Vector generatedRoutes, Hashtable generatedPROTOs ) {
124 String behaviorName = "hb_behavior_" + behaviorNumber;
125 Scene baseScene = (Scene)ultimate.getRoot();
126 replaceDEF( ultimate.getSourceDEFname(), behaviorName, baseScene );
127 ps.println( "#" );
128 ps.println( "# Generating ROUTEs for ultimate " + ultimate.getSourceDEFname() + " TO " + ultimate.getDestDEFname() );
129 ps.println( "#" );
130 generateDEFs( ps, defsGenerated, generatedPROTOs, baseScene );
131 int numberRoutes = routes.size();
132 Hashtable beanJointToDef = humanBean.getJointToDef();
133 for ( int i = 0; i < numberRoutes; i++ ) {
134 ROUTE r = (ROUTE)routes.elementAt( i );
135 if ( generatedRoutes.indexOf( r ) == -1 ) {
136 generatedRoutes.addElement( r );
137 generateRoute( ps, beanJointToDef, r );
138 }
139 }
140 }
141
142 void replaceDEF( String originalName, String newName, Scene scene ) {
143 if ( originalName.compareTo( newName ) == 0 ) {
144 return;
145 }
146 TokenEnumerator te = scene.getTokenEnumerator();
147 DEFUSENode dun = scene.getDEF( originalName );
148 if ( dun != null ) {
149 int offset = getModifiedStartValue( dun.getFirstTokenOffset() + 1 );
150 te.replace( offset, newName );
151 Node n = dun.getNode();
152
153 // possibly def is PROTO for TimeSensor
154 if ( n instanceof PROTOInstance ) {
155 PROTOInstance pi = (PROTOInstance)n;
156 PROTObase pb = pi.getPROTObase();
157 n = pb.getPROTONodeType();
158 if ( n == null ) {
159 return;
160 }
161 }
162 // make TimeSensor fields uniform
163 int oldSize = n.getLastTokenOffset() - n.getFirstTokenOffset();
164 System.out.println( "old range " + n.getFirstTokenOffset() + " to " + n.getLastTokenOffset() );
165 // System.out.println( "3678 is '" + te.toString( 3678 ) + "'" );
166 // System.out.println( "3681 is '" + te.toString( 3681 ) + "'" );
167 int originalLast = n.getLastTokenOffset();
168 if ( n.getBaseName().compareTo( "TimeSensor" ) == 0 ) {
169 try {
170 n.setField( "startTime 0" );
171 n.setField( "stopTime 0" );
172 n.setField( "enabled FALSE" );
173 } catch ( Exception e ) {
174 e.printStackTrace();
175 System.out.println( "error setting TimeSensor fields" );
176 }
177 }
178 int newSize = n.getLastTokenOffset() - n.getFirstTokenOffset();
179 if (( newSize - oldSize ) != 0 ) {
180 addModificationPoint( originalLast, newSize - oldSize );
181 }
182 System.out.println( "new range " + n.getFirstTokenOffset() + " to " + n.getLastTokenOffset() );
183 System.out.println( "added modification point " + originalLast + ", size " + (newSize-oldSize));
184 // System.out.println( "3678 is '" + te.toString( 3678 ) + "'" );
185 // System.out.println( "3681 is '" + te.toString( 3681 ) + "'" );
186 }
187 int numberRoutes = routes.size();
188 for ( int i = 0; i < numberRoutes; i++ ) {
189 ROUTE r = (ROUTE)routes.elementAt( i );
190 if ( r.getSourceDEFname().compareTo( originalName ) == 0 ) {
191 int offset = getModifiedStartValue( r.getFirstTokenOffset() + 1 );
192 te.replace( offset, newName + "." + r.getSourceFieldName() );
193 r.setSourceDEFname( newName );
194 }
195 }
196 }
197
198 /** Generate text for all the DEFs used by this behavior. The DEFs listed
199 * in this behavior are only the ones that are used by the behavior, but
200 * are not Joint DEFs.
201 */
202 void generateDEFs( PrintStream ps, Hashtable defsGenerated, Hashtable generatedPROTOs, Scene scene ) {
203 int numberDEFs = defs.size();
204 TokenEnumerator te = scene.getTokenEnumerator();
205 for ( int i = 1; i < numberDEFs; i++ ) {
206 String defName = (String)defs.elementAt( i );
207 generateDEF( ps, defName, scene, te, defsGenerated, generatedPROTOs );
208 }
209 String ultimateDEF = (String)defs.elementAt( 0 );
210 generateDEF( ps, ultimateDEF, scene, te, defsGenerated, generatedPROTOs );
211 }
212
213 /** Generate text for a single DEF. If that DEF is totally contained in another DEF,
214 * then generate text for that other DEF (unless it was done already).
215 *
216 * @param ps PrintStream destination of generated text
217 * @param defName the name of the DEF to generate
218 * @param scene the Scene containing the DEF
219 * @param te source of text
220 * @param defsGenerated table of DEFs already generated to prevent duplicate DEFS
221 * (NOTE: still one DEF containing another will result in duplicates)
222 */
223 void generateDEF( PrintStream ps, String defName, Scene scene, TokenEnumerator te, Hashtable defsGenerated, Hashtable generatedPROTOs ) {
224 if ( defsGenerated.get( defName ) != null ) {
225 return;
226 }
227 defsGenerated.put( defName, defName );
228 VrmlElement defNode = scene.getDEF( defName );
229 if ( defNode == null ) {
230 return;
231 }
232 if ( defNode instanceof DEFUSENode ) {
233 DEFUSENode dun = (DEFUSENode)defNode;
234 Node n = dun.getNode();
235 if ( n instanceof PROTOInstance ) {
236 PROTOInstance pi = (PROTOInstance)n;
237 PROTObase pb = pi.getPROTObase();
238 String pbName = pb.getId();
239 if ( pbName != null ) {
240 if ( pbName.compareTo( "Joint" ) != 0 ) {
241 if ( generatedPROTOs.get( pb ) == null ) {
242 generatedPROTOs.put( pb, pb );
243 TokenPrinter tp = new TokenPrinter( te, new TokenEnumerator() );
244 tp.printRange( getModifiedStartValue( pb.getFirstTokenOffset() ),
245 getModifiedStartValue( pb.getLastTokenOffset() ), true );
246 tp.flush();
247 tp.saveDataSink( ps );
248 ps.println( "#" );
249 ps.println( "# end of PROTO from " + pb.getFirstTokenOffset() + " to " + pb.getLastTokenOffset() );
250 }
251 }
252 }
253 }
254 }
255 DEFUSENode containingDEF = getContainingDEF( scene, defNode.getFirstTokenOffset() );
256 if ( containingDEF != null ) {
257 while ( true ) {
258 defNode = containingDEF;
259 String name = containingDEF.getId();
260 System.out.println( "DEF contained in '" + name + "'" );
261 if ( defsGenerated.get( name ) != null ) {
262 return;
263 }
264 defsGenerated.put( name, name );
265 DEFUSENode ctest = getContainingDEF( scene, containingDEF.getFirstTokenOffset() );
266 if ( ctest == null ) {
267 break;
268 } else {
269 containingDEF = ctest;
270 }
271 }
272 }
273 Node test = getSensorContainingNode( defNode.getFirstTokenOffset() );
274 if ( test != null ) {
275 defNode = test;
276 }
277 TokenPrinter tp = new TokenPrinter( te, new TokenEnumerator() );
278 System.out.println( "def node goes from " + defNode.getFirstTokenOffset() + " to " + defNode.getLastTokenOffset() );
279 System.out.println( "tokens are: " + te.toString( defNode.getFirstTokenOffset() ) + ", then " + te.toString( defNode.getFirstTokenOffset() + 1 ));
280
281 tp.printRange( getModifiedStartValue( defNode.getFirstTokenOffset()),
282 getModifiedEndValue( defNode.getLastTokenOffset() ), true );
283 tp.flush();
284 tp.saveDataSink( ps );
285 ps.println( "#" );
286 ps.println( "# end of DEF " + defNode.getFirstTokenOffset() + " to " + defNode.getLastTokenOffset() );
287 ps.println( "# modified: " + getModifiedStartValue( defNode.getFirstTokenOffset()) + " to " + getModifiedEndValue( defNode.getLastTokenOffset()) );
288 }
289
290 /** Get the non-DEFfed node that contains this one, this is used
291 * for the case where we have to also generate a sensor node parent,
292 * since the sensor only makes sense in the context of its parent.
293 */
294 Node getSensorContainingNode( int offset ) {
295 // System.out.println( "Searching for sensor containing DEFs" );
296 int numberSensorContainingNodes = sensorContainingNodes.size();
297 for ( int i = 0; i < numberSensorContainingNodes; i++ ) {
298 Node n = (Node)sensorContainingNodes.elementAt( i );
299 if (( n.getFirstTokenOffset() < offset ) && ( n.getLastTokenOffset() > offset )) {
300 // System.out.println( "FOUND ONE" );
301 return( n );
302 }
303 }
304 // System.out.println( "Didn't FIND ONE" );
305 return( null );
306 }
307
308 /** Add a sensor containing node */
309 public void addSensorContainingNode( Node n ) {
310 sensorContainingNodes.addElement( n );
311 }
312
313 /** Get the DEF that contains another DEF
314 *
315 * @param scene the authority on all DEFs
316 * @param defOffset the tokenOffset of the DEF to check, it is contained in
317 * another DEF if that other DEF's token offsets surround it
318 *
319 * @return the first containing DEF found, or null if none
320 */
321 DEFUSENode getContainingDEF( Scene scene, int defOffset ) {
322 int numberDEFs = defs.size();
323 // System.out.println( "getContainingDEF for offset " + defOffset );
324 for ( int i = 0; i < numberDEFs; i++ ) {
325 String defName = (String)defs.elementAt( i );
326 DEFUSENode test = scene.getDEF( defName );
327 if ( test != null ) {
328 if (( test.getFirstTokenOffset() < defOffset ) && ( test.getLastTokenOffset() > defOffset )) {
329 return( test );
330 }
331 }
332 }
333 return( null );
334 }
335
336
337 /** Generate text for a ROUTE
338 *
339 * @param ps PrintStream destination for generated text
340 * @param beanJointToDef table that maps Joint names to DEF names for a particular HumanBean
341 * @param r the ROUTE to convert to text
342 */
343 void generateRoute( PrintStream ps, Hashtable beanJointToDef, ROUTE r ) {
344 String source = r.getSourceDEFname();
345 String dest = r.getDestDEFname();
346 String jointSource = (String)defToJoint.get( source );
347 String jointDest = (String)defToJoint.get( dest );
348 if ( jointSource != null ) {
349 source = (String)beanJointToDef.get( jointSource );
350 }
351 if ( jointDest != null ) {
352 dest = (String)beanJointToDef.get( jointDest );
353 }
354 ps.println( "ROUTE " + source + "." + r.getSourceFieldName() + " TO " + dest + "." + r.getDestFieldName() );
355 }
356 }