Skip to main content

Full text of "DTIC ADA392322: An Object Description Language for Distributed Discrete Event Simulations"

See other formats


REPORT DOCUMENTATION PAGE 


Form Approved 
0MB No. 0704-0188 


_I_ 

The public reporting burden for this collection of information is estimated to average 1 hour per response, including the time for reviewing instructions, searching existing data sources, 
gathering and maintaining the data needed, and completing and reviewing the collection of information. Send comments regarding this burden estimate or any other aspect of this collection 
of information, including suggestions for reducing the burden, to Department of Defense, Washington Headquarters Services, Directorate for information Operations and Reports 
(0704-0188), 1215 Jefferson Davis Highway, Suite 1204, Arlington, VA 22202-4302. Respondents should be aware that notwithstanding any other provision of law, no person shall be 
subject to any penalty for failing to comply with a collection of information if it does not display a currently valid 0MB control number. 

PLEASE DO NOT RETURN YOUR FORM TO THE ABOVE ADDRESS. 


1. REPORT DATE/DD-ZW/W-YY/W 2. REPORT TYPE 

24/May/2001 THESIS 

3. DATES COVERED (From - To) 

4. TITLE AND SUBTITLE 

AN OBJECT DESCRIPTION LANGUAGE FOR DISTIBUTED DISCRETE 
EVENT SIMULATIONS 

5a. CONTRACT NUMBER 

5b. GRANT NUMBER 

5c. PROGRAM ELEMENT NUMBER 

6. AUTHOR(S) 

MAJ ANDREWS HAROLD G 

5d. PROJECT NUMBER 

5e. TASK NUMBER 

5f. WORK UNIT NUMBER 

7. PERFORMING ORGANIZATION NAME(S) AND ADDRESS(ES) 

TUFTS UNIVERSITY 

8. PERFORMING ORGANIZATION 

REPORT NUMBER 

CIOl-75 

9. SPONSORING/MONITORING AGENCY NAME(S) AND ADDRESS(ES) 

THE DEPARTMENT OF THE AIR FORCE 

AFIT/CIA, BLDG 125 

2950 P STREET 

WPAFB OH 45433 

10. SPONSOR/MONITOR’S ACRONYM(S) 

11. SPONSOR/MONITOR'S REPORT 
NUMBER(S) 


12. DISTRIBUTION/AVAILABILITY STATEMENT 

Unlimited distribution 

In Accordance With API 35-205/AFIT Sup 1 


13. SUPPLEMENTARY NOTES 


14. ABSTRACT 


20010720 039 


15. SUBJECT TERMS 


16. SECURITY CLASSIFICATION OF: 

17. LIMITATION OF 
ABSTRACT 

18. NUMBER 
OF 

PAGES 

19a. NAME OF RESPONSIBLE PERSON 

a. REPORT 

b. ABSTRACT 

c. THIS PAGE 






435 

19b. Telephone number (include area code) 


Standard Form 298 (Rev. 8/98) 

Prescribed by ANSI Std. Z39.1S 







































- —- 

An Object Description Language for Distributed 
Discrete Event Simulations 

by 

Harold Gregory Andrews II, Major, USAF 

Submitted to the Department of Electrical Engineering and Computer Science on 13 April 2001, in partial 
fulfillment of the requirements for the Degree of Doctor of Philosophy of Computer Science 

Abstract 

Digital simulation is a useful tool for developing a better understanding of physical or hypothetical 
systems. It has been used with great success since the advent of the digital computer in such varied fields 
as weather prediction, planning military operations, and training. As digital computers become more 
capable and network communications systems more prevalent, the notion of synergistically combining the 
two to perform distributed simulation has led to some tremendous improvements in simulation speed and 
fidelity. 

This dissertation describes a new programming language that is useful in creating distributed discrete event 
simulations without burdening simulation developers with the difficult and error-prone task of 
synchronizing nodes in a distributed simulation. Developers can instead focus on specifying the behavior 
of the objects in the virtual environment with little effort devoted to lower level concerns. 

The language structure follows the notions of stimulus-response and completely isolates simulation object 
instances from each other. Inter-object communication occurs solely through message passing. Several 
example applications are described. 


Thesis Advisor: Professor David W. Krumme, Associate Professor of Computer Science 
Technical Supervisor: Dr. Owen L. Deutsch, Senior Member of the Technical Staff 


1 








An Object Description Language for Distributed 
Discrete Event Simulations 

A dissertation submitted by 

Haix)ld Gregory Andrews II, Major, USAF 

MS Mathematics, University of Texas, San Antonio, 1997 
MBA Management, Rensselaer Polytechnic Institute, 1992 
BS Mathematics, Northeastern University, 1988 
BS Computer Science, Nordieastem University, 1988 

Submitted to the Department of Electrical Engineering and Computer Science in partial fulfillment for the 

requirements for the degree of 

Doctorate of Philosophy in Computer Science 

TUFTS UNIVERSITY 


May 2001 


© 2001 Harold Gregory Andrews n. All rights reserved 

The author hereby grants to Tufts University permission to reproduce and to distribute publicly paper and 
electronic copies of this dissertation document in whole or in part. 


Thesis Advisor: Associate ProfiEssor David W. Kimmiie 
Draper Technical Supervisor: Dr. OwenL. Deutsch 
Thesis Conunittee Memhen Associate Professor Ansehn Blumer 
Thesis Committee Member: Associate Professor Robert J. K. Jacx)b 








11 








ACKNOWLEDGEMENT 

13 April 2001 


I first and foremost acknowledge the intervention from the Almighty God who, through His divine 
providence, saw fit to allow me to attend school and work on this project. 

Next, I thank my wife and best friend Barbara whose love, devotion, and willingness to deal with much in 
terms of caring for our children, what seems to have been more sickness than health, and my vacant stares 
while I pondered some troubling technical triviality, freed me from having to fret over managing many of 
our household affairs; they have indeed been in good hands. 

Special thanks go also to my Tufts University thesis advisor. Professor David Krumme, and my Draper 
Laboratory technical supervisor. Dr. Owen Deutsch for their help, guidance, and insight as I worked on this 
dissertation and the related project. In addition, thanks to my thesis committee members Professors 
Anselm Blumer and Robert Jacob for their willingness to wade through the musings contained herein, and 
quiz me about them. I would also like to thank Professor Karen Panetta for her participation as an 
examiner during my orals, and for the observations, comments and insights she offered; they were very 
helpful. 

Additional thanks go to current and former personnel at the Air Force Institute of Technology, office of 
Civilian Institution Programs: Colonel Paul Copp, for fighting for me to go to school, when the Air Staff 
was opposed to the idea; Majors Ralph Tolle and David Schluckebier for acting as my voice at AFIT; 
Captain Rick Sutter, who suggested a novel way of dealing with administrative difficulties. Also, thanks 
go to the faculty of the computer science department at the United States Air Force Academy who were 
willing to take a chance on an unknown. 

In no particular order I also acknowledge, and humbly thank the following for their prayers, advice, 
support, and words of encouragement: my mother, Jill Andrews, for her help beyond the call of duty 
during the aforementioned periods of sickness; my father, Harold Andrews, for providing me with an 
environment in which I was free to explore my various interests; my mother-in-law, Veronica Vogt, for her 
help, prayers, encouragement and mastery of the English language; my father-in-law, Kenneth Vogt who 

iii 







helped me to keep my sense of humor; my former high school math teacher, Eugene Kaczowka, who in 
many ways started this ball rolling lo those many years ago; Major William Szarek, whose dedication to the 
notion of service above self provided me with as a model to emulate; Majors Michael Ward and Barrie 
Wheeler, for helping me to see how things really are; Lieutenant Colonel Skip Youngberg for giving me a 
well deserved and much needed kick in the pants; Colonel Larry Carr and Lieutenant Colonel Richard Saint 
Pierre, who provided me with counsel, wisdom, and the perspective of their experience; Robert Balusek, 
Dr. John Romo, and Dr. Stephen Welstead, for their advice in matters of higher education; and to those 
unmentioned by name here who through their prayers have allowed this work to come to a merciful 
conclusion. 

This thesis was prepared at the Charles Stark Draper Laboratory, Inc., under projects 13025 (Multi-Agent 
Collaboration Services), 15052 (SOS Large Scale Analysis Capability), 18527 (C4I Laboratory Capability 
Development), and 13360 (Tactical Node). 

Publication of this thesis does not constitute approval by Draper or the sponsoring agency of the findings or 
conclusions contained herein. It is published for the exchange and stimulation of ideas. 


Harold Gregory Andrews II, Major, USAF 


IV 







Contents 

CHAPTER 1. RATIONALE AND BACKGROUND.1 

1.1. Overview.1 

1.2. Alternative APPROACHES.4 

1.2.1. Full implementations . 4 

1.2.2. Modular simulation systems . 6 

1.2.3. 4‘^ generation simulation programming languages .7 

1.2.4. Distributed simulation standards .9 

1.3. SODL SYSTEM DESCRIPTION.9 

1.4. Scope.12 

CHAPTER 2. DIGITAL SIMULATION.13 

2.1. Overview.13 

2.2. Modeling.14 

2.3. Digital simulation.18 

2.3.1. Continuous time simulation . 21 

2.3.2. Discrete event simulation . 21 

2.3.3. Distributed discrete event simulation (DDES) . 23 

CHAPTER 3. OVERVIEW OF OPTIMISTIC SYNCHRONIZATION.27 

3.1. Overview.27 

3.2. State saving.30 

3.3. Fossil collection.31 

3.4. Rollback (state recovery).33 

3.5. Global Virtual Time computation.34 

CHAPTER 4. SODL RUN-TIME SYSTEM ARCHITECTURE.41 

4.1. Overview.41 

4.2. Message CONSTRUCTS.42 

4.2.1. Message Type Specifier . 42 

4.2.2. Message destination list. . 42 

4.2.3. Message time stamp . 43 

4.2.4. Message transmission flag . 43 

4.2.5. Message identifier . 43 

4.2.6. Message data payload . 43 

4.2.7. Message methods . 44 

4.3. Process constructs.44 

4.3.1. Process time stamp . 45 

4.3.2. Process identifier . 45 

4.3.3. Process state data . 45 

4.3.4. Process methods . 46 

4.3.5. Process modes . 46 

4.3.6. Process nodes . 46 

4.3.7. Process inheritance . 47 

4.3.8. Fossil collection in the process instance . 47 
















































4.4. Process Controllers.47 

4.4.1. Identifier . 48 

4.4.2. State queue . 48 

4.4.3. Process controller message receiver . 48 

4.4.4. Process controller message transmitter . 49 

4.4.5. Rollback . 49 

4.4.6. Fossil collection . 49 

4.5. Engines.50 

4.5.1. Local clock .52 

4.5.2. Pending message queue (event queue) .52 

4.5.3. Antimessage queue .52 

4.5.4. Processed message queue .52 

4.5.5. Output message queue .52 

4.5.6. Process controller array .55 

4.5.7. Fossil collection schedule .55 

4.5.8. Engine message receiver .55 

4.5.9. Engine message transmitter . 54 

4.5.10. Engine advancement . 54 

4.5.11. Rollback . 54 

4.5.12. Fossil collection .55 

4.6. Engine Stand.55 

4.6.1. Idle listener interface . 56 

4.6.2. Engine List .56 

4.6.3. Message forwarder .56 

4.6.4. Local virtual time (LVT) calculator .57 

4.6.5. Global virtual time (GVT) estimator .57 

4.6.6. Fossil collection .57 

4.7. Message Passing Interface (MPI).58 

4.8. View MANAGER.58 

4.8.1. Text view manager . 58 

4.8.2. GLUT view manager .59 

CHAPTER 5. SODL PARSER USAGE.61 

5.1. Overview.61 

5.1.1. Cautionary notes . 61 

5.2. Installation.61 

5.3. Directory Structure.62 

5.3.1. ./bin .62 

5.5.2. ./config .62 

5.5.5. ./doc .65 

5.3.4. ./object .65 

5.5.5. ./sample .65 

5.5.6. ./src .67 

5.5.7. ./template .67 

5.4. Command Line Options.67 

5.5. CONHGURATION FILES.68 


VI 




















































CHAPTER 6. SODL LANGUAGE STRUCTURE.71 

6.1. Overview.71 

6.2. Approach.71 

6.3. Constructs.72 

6.3.1. Message constructs . 73 

6.3.2. Process constructs . 73 

6.4. Import declarations.74 

6.4.1. Importing SODL constructs . 74 

6.4.2. Importing non-SODL files .75 

6.5. Member Variable Declarations.76 

6.5.1. Basic data types . 76 

6.5.2. Extended data types .77 

6.5.3. Process constructs as data members . 78 

6.5.4. A note on references and pointers . 80 

6.6. Method Declarations.81 

6.7. Messages.82 

6.7.1. System-defined message member variables . 83 

6.7.2. System-defined message methods . 84 

6.7.3. System-defined messages . 85 

6.7.4. Message Handles . 86 

6.8. Processes.86 

6.8.1. System-defined process member variables . 87 

6.8.2. System-defined process methods . 87 

6.8.3. Process Handles . 88 

6.8.4. Special Processes . 89 

6.9. Mode and Node Declarations.89 

6.9.1. Modes . 89 

6.9.2. Nodes . 90 

CHAPTER 7. C++ CODE GENERATION.97 

7.1. Message construct files.97 

7.1.1. A simple message construct . 98 

7.1.2. Message construct with user-defined methods and data members . 99 

7.2. Process construct hles.101 

7.2.1. A simple process construct . 101 

7.2.2. Process constructs with data members and methods . 102 

7.2.3. Mode and node declarations . 104 

CHAPTER 8. GLUT-BASED USER INTERFACE.Ill 

8.1. Output concerns in an optimistic simulator .Ill 

8.2. Overview of the SODL/GVM subsystem .Ill 

8.2.1. SODL/GVM scene graphs . 113 

8.3. SODL/GVM USAGE .116 

8.4. SODL/GVM Architecture.118 

8.4.1. SODL view controllers, the process:View . 119 

8.4.2. Messaging on the GVM side . 121 

vii 



















































CHAPTER 9. SODL SAMPLE PROGRAMS.123 

9.1. Single Node Textual Simulations.123 

9.1.1. Simple 1 . 123 

9.1.2. Simple2 . 125 

9.1.3. Simple3 . 125 

9.1.4. Ping . 127 

9.1.5. Ringl . 128 

9.1.6. Ring2 . 131 

9.1.7. Brigade2 . 132 

9.2. Multiple Node Textual Simulations.135 

9.2.1. Relay 1 . 135 

9.2.2. Relay2 . 136 

9.2.3. Relay3 . 138 

9.2.4. Relay4 . 139 

9.2.5. Relay5 . 141 

9.2.6. Relay6 . 143 

9.3. GLUT BASED DEMONSTRATIONS.145 

9.3.1. Bounce 1 . 146 

9.3.2. Bounce2 . 148 

9.3.3. Brigadel . 151 

9.3.4. Hierarchy . 152 

9.3.5. Battle . 153 

CHAPTER 10. CONCLUSIONS.165 

10.1. Contributions of this work.165 

10.1.1. SODL system . 165 

10.1.2. Simulation Formalism . 165 

10.1.3. Asynchronous Global Virtual Time Algorithm . 166 

10.2. Potential future work.166 

10.2.1. Distributed SODL run-time system . 166 

10.2.2. Graphics Subsystem . 167 

10.2.3. User Interface . 167 

10.2.4. Process Migration and Load Balancing . 167 

10.2.5. Analysis tools . 168 

10.2.6. Multiple inheritance . 168 

APPENDIX A. SODL LANGUAGE PARSER SPECIFICATION.169 

APPENDIX B. SODL RUN TIME ENGINE CLASS REFERENCE.173 

B.l. Overview.173 

B.2. SODL Run-Time System C-i-+ Classes.173 

B.2.1. .-.-Exception . 173 

B.2.2. ::Exception:.-BadCast . 173 

B.2.3. :.-Exception:.-CausalityError . 174 

B.2.4. :-.Exception::Nonspecific . 175 

B.2.5. ::Exception:.-RangeError . 176 


viii 


















































B.2.6. sodl::AntiMessage . 

B.2.7. sodl:: Clock . 

B.2.8. sodl::Defs . 

B.2.9. sodl::Earlier . 

B.2.10. sodl::EndSimulation . 

B.2.11. sodl:.'Engine . 

B.2.12. sodl::EngineStand . 

B.2.13. sodh.'GLUTViewManager . 

B.2.14. sodl: .'Handle . 

B.2.15. sodl::IdleListener . 

B.2.16. sodl:.'Later . 

B.2.17. sodl: .'Message . 

B.2.18. sodl.'.'MessageHandle . 

B.2.19. sodl::Process . 

B.2.20. sodl:.'ProcessController . 

B.2.21. sodl:.'ProcessHandle . 

B.2.22. sodl::ProcessMode . 

B.2.23. sodl::ProfileTools . 

B.2.24. sodl: .'Random . 

B.2.25. sodl::ScheduleItem . 

B.2.26. sodl::StartSimulation . 

B.2.27. sodl::SystemMessage . 

B.2.28. sodl::TextViewManager . 

B.2.29. sodl::TimeStamp . 

B.2.30. sodl:.'Trace . 

B.2.31. sodl::UpdateGVT . 

B.2.32. sodl::ViewManager . 

B.2.33. SODL run-time system items not associated with a specific class. 

B.3. SODL - GLUT INTERFACE. 

B.3.1. message:AddNode . 

B.3.2. message:AddNode2D . 

B.3.3. message:AddNode3D . 

B.3.4. message.'AddShape . 

B.3.5. message:AddShape2D . 

B.3.6. message:AddShape3D . 

B.3.7. message:AddSubordinate . 

B.3.8. message:AddVertex . 

B.3.9. message:AddVertex2D . 

B.3.10. message:AddVertex3D . 

B.3.11. message.'AddView . 

B.3.12. message:RefreshDisplay . 

B.3.13. message.'Register . 

B.3.14. message:RegisterNode . 

B.3.15. message:RegisterNode2D . 

B.3.16. message:RegisterNode3D . 

B. 3.17. message.'RegisterShape . 


.177 

.177 

.179 

.181 

.181 

.182 

.186 

.190 

.194 

.195 

.195 

.196 

.200 

.200 

.203 

.205 

.206 

.207 

.207 

.208 

209 

210 
210 
211 
212 

214 

215 
215 
221 
222 
222 
222 
223 

223 

224 

224 

225 
225 

225 

226 
226 
227 
227 

227 

228 
228 


IX 




















































B.3.18. message:RegisterShape2D . 228 

B.3.19. message:RegisterShape3D . 229 

B.3.20. message:RegisterVertex . 229 

B.3.21. message:RegisterVertex2D . 229 

B.3.22. message:RegisterVertex3D . 230 

B.3.23. message:SelectiveActivate . 230 

B.3.24. message:SetActive . 231 

B.3.25. message:SetAffine . 232 

B.3.26. message:SetAffine2D . 232 

B.3.27. message:SetAjfine3D . 234 

B.3.28. message:SetColor . 235 

B.3.29. message:SetConeSize . 236 

B.3.30. message.'SetCubeSize . 237 

B.3.31. message.SetCylinderSize . 237 

B.3.32. message:SetDefaultActive . 238 

B.3.33. message:SetLabel . 239 

B.3.34. message:SetMode . 239 

B.3.35. message:SetPointSize . 240 

B.3.36. message:SetPosition . 241 

B.3.37. message:SetRefresh . 241 

B.3.38. message:SetRotation2D . 242 

B.3.39. message:SetRotation3D . 242 

B.3.40. message:SetRotationCenter2D . 242 

B.3.41. message:SetRotationCenter3D . 243 

B.3.42. message:SetScale2D . 243 

B.3.43. message:SetScale3D . 243 

B.3.44. message:SetScaleCenter2D . 244 

B.3.45. message:SetScaleCenter3D . 244 

B.3.46. message.'SetSize . 244 

B.3.47. message:SetSphereSize . 245 

B.3.48. message.SetTorusSize . 245 

B.3.49. message:SetTranslation2D . 246 

B.3.50. message:SetTranslation3D . 246 

B.3.51. message:SetValue . 247 

B.3.52. message:SetVector . 247 

B.3.53. message:SetVector2D . 248 

B.3.54. message:SetVector3D . 249 

B.3.55. message:SetVertex2D . 249 

B.3.56. message:SetVertex3D . 250 

B.3.57. process:Cone . 250 

B.3.58. process:Cube . 251 

B.3.59. process .-Cylinder . 252 

B.3.60. process .'Dodecahedron . 253 

B.3.61. process .-Icosahedron . 253 

B.3.62. process:Node . 2JJ 

B.3.63. process:Node2D . 254 





















































B.3.64. process:Node3D . 256 

B.3.65. process:Object . 258 

B.3.66. process .'Octahedron . 259 

B.3.67. process:Polygon2D . 260 

B.3.68. process:Polygon3D . 260 

B.3.69. process.'Shape . 261 

B.3.70. process:Shape2D . 263 

B.3.71. process:Shape3D . 263 

B.3.72. process:Sphere . 263 

B.3.73. process.'Tetrahedron . 264 

B.3.74. process.'Torus . 264 

B.3.75. process .'Vertex . 265 

B.3.76. process:Vertex2D . 265 

B.3.77. process:Vertex3D . 266 

B.3.78. process .'View . 267 

B.3.79. process:View2D . 269 

B.3.80. process:View3D . 272 

B.4. GLUT View Manager (gvm) Classes.275 

B.4.1. gvm::AddNode . 275 

B.4.2. gvm:.'AddShape . 276 

B.4.3. gvm::AddVertex . 276 

B.4.4. gvm::Cone . 277 

B.4.5. gvm::CreateObject . 278 

B.4.6. gvm:.'Cube . 279 

B.4.7. gvm:.'Cylinder . 280 

B.4.8. gvm:.'Dodecahedron . 281 

B.4.9. gvm:.'Icosahedron . 2S2 

B.4.10. gvm::Message . 282 

B.4.11. gvm:.'Node . 284 

B.4.12. gvm::Node2D . 287 

B.4.13. gvm::Node3D . 288 

B.4.14. gvm:.'Object . 289 

B.4.15. gvm:.'Octahedron . 294 

B.4.16. gvm::Polygon2D . 295 

B.4.17. gvm::Polygon3D . 296 

B.4.18. gvm:.'Refresh . 296 

B.4.19. gvm:: Set Active . 297 

B.4.20. gvm::SetColor . 297 

B.4.21. gvm::SetConeSize . 298 

B.4.22. gvm::SetCubeSize . 299 

B.4.23. gvm::SetCylinderSize . 300 

B.4.24. gvm::SetLabel . 301 

B.4.25. gvm::SetMode . 301 

B.4.26. gvm::SetPointSize . 302 

B.4.27. gvm::SetPosition . 303 

B.4.28. gvm::SetRotation . 303 


XI 





















































B.4.29. gvm::SetRotationCenter . 304 

B.4.30. gvm::SetScale . 305 

B.4.31. gvm::SetScaleCenter . 305 

B.4.32. gvm::SetSize . 306 

B.4.33. gvm::SetSphereSize . 307 

BA.34. gvm::SetTorusSize . 308 

B.4.35. gvm::SetTranslation . 309 

B.4.36. gvm::SetVertex . 309 

B.4.37. gvm::Shape . 310 

B.4.38. gvm::Shape2D . 312 

B.4.39. gvm::Shape3D . 312 

B.4.40. gvm::Sphere . 313 

B.4.41. gvm:.-Tetrahedron . 314 

B.4.42. gvm::Torus . 315 

B.4.43. gvm:.-Vertex . 316 

B.4.44. gvm::Vertex2D . 317 

B.4.45. gvm::Vertex3D . 317 

B.4.46. gvm::View . 318 

B.4.47. gvm::View2D . 325 

B.4.48. gvm::View3D . 326 

B. 4.49. GVM Definitions not associated with a specific class . 328 

APPENDIX C. SAMPLE CODE LISTINGS.331 

C.l. Battle.331 

C. 1.1. Add Environment.msg . 331 

C.l.2. AddTrackmsg . 331 

C.l.3. AdjustFormation.msg . 331 

C.l.4. AttacLmsg . 332 

C.l.5. Battle.proc . 332 

C.l.6. BattleView.proc . 332 

C.l.7. BlueCompany.proc . 333 

C.l.8. ChangeTrackmsg . 334 

C.l.9. CommandPost.proc . 334 

C.l. 10. Company.proc . 335 

C.1.11. Destroyed.msg . 337 

C.l.12. Environmnent.proc . 337 

C.l. 13. Explosion.msg . 342 

C.l. 14. Fire.msg . 342 

C.l. 15. FormationMove.msg . 342 

C.l. 16. Ground.proc . 342 

C.l.n.Hit.msg . 343 

C.l. 18. HoldPosition.msg . 343 

C.l. 19. Impact.msg . 343 

C.l.20. LoseTrackmsg . 343 

C.1.21. MoveFormation.msg . 343 

C.l.22. MoveTo.msg . 343 

C.1.23. MovementComplete.msg . 344 

xii 





















































C.1.24. Munition.proc . 344 

C.1.25. NewtonianMotion.proc . 345 

C.1.26. Platoon.proc . 346 

C.1.27. RedCompany.proc . 350 

C.1.28. RegisterEnvironmentObject.msg . 351 

C.1.29. ScheduleAddTmck.msg . 351 

C.1.30. ScheduleLoseTracLmsg . 352 

C.1.31. ScheduleTrackEvent.msg . 352 

C.1.32. SensorTracLproc . 352 

C.1.33. SetAngularAcceleration.msg . 353 

C.1.34. SetAngularPosition.msg . 353 

C.1.35. SetAngularVelocity.msg . 354 

C.1.36. SetEnvironment.msg . 354 

C.1.37. SetPormation.msg . 354 

C.1.38. SetLinearAcceleration.msg . 354 

C.1.39. SetLinearPosition.msg . 354 

C.1.40. SetLinearVelocity.msg . 354 

C.1.41. SetMotion.msg . 354 

C.1.42. SetNewtonianMotion.msg . 354 

C.1.43. SetTankState.msg . 355 

C. 1.44. Stop, msg . 355 

C.1.45. StopAzimuthSlew.msg . 355 

C.1.46. StopElevationSlew.msg . 355 

C.1.47. StopSlew.msg . 355 

C.1.48. Tanhproc . 355 

C.1.49. TrackEvent.msg . 360 

C.1.50. TrackMotionEvent. msg . 360 

C.1.51. UnitSetup.msg . 360 

C.1.52. Vehicle.proc . 361 

C.1.53. gvm/gvmAddTrack.h . 364 

C.1.54. gvm/gvmAddTrack.cxx . 364 

C.1.55. gvm/gvmBattleView.h . 364 

C.1.56. gvm/gvmBattleView.cxx . 365 

C.1.57. gvm/gvmChangeTrack.h . 368 

C.1.58. gvm/gvmChangeTrack.cxx . 368 

C.1.59. gvm/gvmCommandPost.h . 369 

C.1.60. gvm/gvmCommandPost.cxx . 369 

C.1.61. gvm/gvmDeleteTrack.h . 370 

C.1.62. gvm/gvmDeleteTrack.cxx . 370 

C.1.63. gvm/gvmExplosion.h . 371 

C.1.64. gvm/gvmExplosion.cxx . 371 

C.1.65. gvm/gvmGrid.h . 372 

C. 1.66. gvm/gvmGrid.cxx . 372 

C.1.67. gvm/gvmGround.h . 373 

C.1.68. gvm/gvmGround.cxx . 373 

C.1.69. gvm/gvmMunition.h . 374 






















































C.1.70. gvm/gvmMunition.cxx . 374 

C.1.71. gvm/gvmNewtonianMotion.h . 376 

C.1.72. gvm/gvmNewtonianMotion.cxx . 376 

C.1.73. gvm/gvmSetNewtonianMotion.h . 377 

C.1.74. gvm/gvmSetNewtonianMotion.cxx . 377 

C. 1.75. gvm/gvmSetTankState.h . 378 

C.1.76. gvm/gvmSetTankState.cxx . 378 

C.1.77. gvm/gvmTacticalGrid.h . 379 

C.1.78. gvm/gvmTacticalGrid.cxx . 379 

C.1.79. gvm/gvmTacticalView.h . 380 

C.1.80. gvm/gvmTacticalView.cxx . 380 

C.1.81. gvm/gvmTank.h . 382 

C.1.82. gvm/gvmTank.cxx . 383 

C.1.83. gvm/gvmTrack.h . 384 

C.1.84. gvm/gvmTrack.cxx . 385 

C.1.85. spt/sptAngularMotion.h . 386 

C.1.86. spt/sptAngularMotion.cxx . 387 

C.1.87. spt/sptDefs.h . 390 

C.1.88. spt/spt/Defs. cxx . 390 

C.1.89. spt/sptEnvironmentObject.h . 391 

C.1.90. spt/sptEnvironmentObject.cxx . 391 

C.1.91. spt/sptLinearMotion.h . 392 

C.1.92. spt/sptLinearMotion.cxx . 392 

C. 1.93. spt/sptNewtonianMotion. h . 395 

C.1.94. spt/sptNewtonianMotion.cxx . 395 

C.2. BOUNCEI.397 

C.2.1. bounce.proc . 397 

C.2.2. grjupdate.msg . 397 

C.2.3. hit.msg . 397 

C.2.4. particle.proc . 398 

C.2.5. set_system.msg . 399 

C.2.6. start.msg . 399 

C.3. Bounce2.399 

C.3.1. bounce.proc . 399 

C.3.2. bounce_view.proc . 400 

C.3.3. hit.msg . 400 

C.3.4. particle.proc . 400 

C.3.5. set_motion.msg . 402 

C.3.6. gvm/gvmBounceView.h . 402 

C.3.7. gvm/gvmBounceView.cxx . 403 

C.3.8. gvm/gvmParticle.h . 403 

C.3.9. gvm/gvmParticle, cxx . 404 

C.3.10. gvm/gvmSetMotion.h . 404 

C.3.11. gvm/gvmSetMotion.cxx . 405 

C.4. BrigadeI.405 

C.4.1. battalion.proc . 405 


XIV 





















































C.4.2. brigade.proc . 406 

C.4.3. company.proc . 407 

C.4.4. order.msg . 407 

C.4.5. platoon.proc . 407 

C.4.6. report.msg . 407 

C.4.7. set_parent.msg . 408 

C.4.8. soldier.proc . 408 

C.4.9. squad.proc . 408 

C.4.10. unit.proc . 409 

C.5. Brigade2.411 

C.5.1. battalion.proc . 411 

C.5.2. brigade.proc . 411 

C.5.3. company.proc . 411 

C.5.4. order.msg . 412 

C.5.5. platoon.proc . 412 

C.5.6. report.msg . 412 

C.5.7. set_parent.msg . 412 

C.5.8. soldier.proc . 412 

C.5.9. squad.proc . 413 

C.5.10. unit.proc . 413 

C.6. Hierarchy.415 

C.6.1. generic.msg . 415 

C.6.2. hierarchy.proc . 415 

C.7.PING.417 

C.7.1. Generic.msg . 417 

C. 7.2. Ping.proc . 417 

C. 7.3. Pong.proc . 417 

C.8. RelayI.418 

C.8.1. generic.msg . 418 

C.8.2. reflector.proc . 418 

C.8.3. relay.proc . 418 

C.9. Relay2.419 

C.9.1. generic.msg . 419 

C.9.2. reflector.proc . 419 

C.9.3. relay.proc . 419 

C.9.4. SetPartner.msg . 419 

C.IO. Relays.420 

C.10.1. child.proc . 420 

C.10.2. generic.msg . 420 

C. 10.3. relay.proc . 420 

C.10.4. setup.msg . 421 

C.ll. Relay4.421 

C.11.1. child.proc . 421 

C.11.2. generic.msg . 422 

C.11.3. relay.proc . 422 

C. 11.4. setup.msg . 422 


XV 






















































C.11.5. subscribe, msg . 422 

C.11.6. subscription.proc . 422 

C.11.7. unsubscribe, msg . 423 

C. 12. Relays.423 

C.12.1. base.proc . 423 

C.12.2. generic.msg . 423 

C.12.3. relay.proc . 423 

C.12.4. sinLproc . 423 

C.12.5. source.proc . 424 

C.13. Relay6.424 

C.13.1. base.proc . 424 

C. 13.2. generic.msg . 424 

C.13.3. relay.proc... . 424 

C.13.4. sinkproc . 425 

C.14.RING1.425 

C.14.1. Generic.msg . 425 

C.14.2. Reportlndex.msg . 425 

C.14.3. ReportSize.msg . 425 

C.14.4. ReportValue.msg . 425 

C.14.5. Ring.proc . 426 

C.14.6. RingMember.proc . 426 

C. 14.7. Setup.msg . 426 

C.14.8. Subscribe.msg . 427 

C. 14.9. Subscription.proc . 427 

C.15.RING2.427 

C. 15.1. Generic.msg . 427 

C.15.2. Reportlndex.msg . 427 

C.15.3. ReportSize.msg . 427 

C.15.4. ReportValue.msg . 427 

C.15.5. Ring.proc . 427 

C.15.6. RingMember.proc . 428 

C.15.7. Setup.msg . 428 

C.15.8. Subscribe.msg . 429 

C.15.9. Subscription.proc . 429 

C.16. SiMPLEl.429 

C.16.1. Generic, msg . 429 

C.16.2. Simple.proc . 429 

C.17. Simple2.430 

C.17.1. Generic.msg . 430 

C.17.2. Simple.proc . 430 

C.18. SIMPLE3.430 

C.18.1. Child.proc . 430 

C.18.2. Generic.msg . 431 

C.18.3. SetParent.msg . 431 

C.18.4. Simple.proc . 431 

APPENDIX D. REFERENCES.433 


XVI 





















































Tables 

Table 1-1 Alternative Simulation Approaches.4 

Table 1-2 Some simulation systems used for analysis or training purposes.5 

Table 1-3 Current popular games using simulation technologies.6 

Table 1-4 Examples of modular simulation systems.7 

Table 1-5 Some commercially available 4* generation sequential simulation packages... 8 

Table 2-1 Distributed Discrete Event Simulation approaches.24 

Table 3-1 Data structures needed for implementing the Time Warp algorithm.28 

Table 3-2 Routines used in the asynchronous GVTE computation.37 

Table 3-3 Data Structures used in the asynchronous GVTE computation.37 

Table 5-1 Methods for making the SODL parser.61 

Table 5-2 Makefile commands.62 

Table 5-3 Sample simulation system directory structure.63 

Table 5-4 Make command line arguments for building demonstrations.63 

Table 5-5 Command line options for sp.67 

Table 5-6 Configuration file specification in the sp eommand line.68 

Table 5-7 Configuration file key/value descriptions.69 

Table 6-1 SODL Basic construct types.72 

Table 6-2 SODL construct member variable basic types.76 

Table 6-3 Sample SODL member variable declarations.77 

Table 6-4 Sample extended data type declaration.78 

Table 6-5 Process construct declarations.79 

Table 6-6 Engine specification for process declaration.80 

Table 6-7 System-defined message member variables.84 

Table 6-8 Common system-defined message methods.85 

Table 6-9 System-defined messages.85 

Table 6-10 System-defined process member variables.87 

Table 6-11 System-defined methods for process classes.88 

Table 6-12 sodl::ProcessHandle type routines.88 

Table 6-13 Mode system-define methods.90 

Table 9-1 Performance comparison of Bounce demos.149 

Figures 

Figure 3-1 Synopsis of the Time Warp algorithm.29 

Figure 3-2 Saved state data of sample logical process at time 20.30 

Figure 3-3 Result of fossil collection with GVT=7.5.32 

Figure 3-4 Results of a state rollback on LP, to time 6.0.33 

Figure 3-5 Possible unrecoverable causality errors in asynchronous token passing GVTE 

calculation.36 

Figure 3-6 Asynchronous GVTE algorithm.38 

Figure 4-1 SODL system hierarchy.41 

Figure 4-2 SODL message construct.42 

Figure 4-3 SODL process construct.44 

Figure 4-4 Proeess Controller message flow.48 


xvii 














































Figure 4-5 Fossil collection cycle in a SODL process controller.50 

Figure 4-6 SODL engine structure.51 

Figure 4-7 Engine stand structure.55 

Figure 5-1 SODL Parser (sp) installation instructions.62 

Figure 5-2 User project default directory structure.68 

Figure 6-1 Depiction of the stimulus/response notion of a SODL process.71 

Figure 6-2 SODL project build steps.72 

Figure 6-3 Basic construct form.72 

Figure 6-4 Sample message constructs.73 

Figure 6-5 Sample process constructs.73 

Figure 6-6 Import directive specification.74 

Figure 6-7 Sample import directives for message and process constructs.74 

Figure 6-8 Sample non-SODL construct imports.75 

Figure 6-9 SODL construct member variable declaration.76 

Figure 6-10 Sample member variable declarations in SODL constructs .77 

Figure 6-11 Process member variable declaration.78 

Figure 6-12 Forms for specifying controller simulation engine.79 

Figure 6-13 General method form.81 

Figure 6-14 Sample methods.82 

Figure 6-15 Message construct form.82 

Figure 6-16 Message flo\v from sending process to receiving processes.83 

Figure 6-17 Process declaration syntax.86 

Figure 6-18 Mode declaration syntax.90 

Figure 6-19 Node declaration syntax .90 

Figure 6-20 Output message form.91 

Figure 6-21 Stand along input message usage.91 

Figure 6-22 Explicit specification of destination & timestamp in body.92 

Figure 6-23 Message count specification for output message arrays.93 

Figure 6-24 Adding default destinations to output messages.94 

Figure 6-25 Default time stamp specification.95 

Figure 6-26 Combined default destination and time stamp specification.96 

Figure 7-1 A simple message construct in Generic.msg .98 

Figure 7-2 Relevant output derived from Generic.msg .98 

Figure 7-3 AddSubordinate.msg with one data member and three methods.100 

Figure 7-4 C- 1 -+ Files resulting from AddSubordinate.msg .100 

Figure 7-5 An initialized array as a data member in a message construct.100 

Figure 7-6 Initialization of the array specification in Figure 7-5.101 

Figure 7-7 A simple process construct, ShapeZD.proc .101 

Figure 7-8 Relevant output from ShapelD.proc .102 

Figure 7-9 Simple.proc declaration of a subordinate process instance to process:Simple 

.103 

Figure 7-10 C-I-+ declaration and allocation of a subordinate process.103 

Figure 7-11 bounce.proc declaration of subordinate processes on non-default engines 103 

Figure 7-12 Declaration and allocation of an array of subordinate processes.104 

Figure 7-13 Simple.proc sample mode & subordinate nodes.105 

Figure 7-14 Simple.h C++ relevant components of header file for Simple.proc .105 


xviii 



















































Figure 7-15 Simple.cxx, message handling implementation.106 

Figure 7-16 hierarchy.proc with an output array of messages.108 

Figure 7-17 Relevant portions of hierarchy.cxx implementing message arrays.108 

Figure 8-1 Depiction of relationship between SODL processes and GVM objects.Ill 

Figure 8-2 Message propagation for scene update requests.112 

Figure 8-3 Sample scene graph; Affine transformations are Ax, polygons are Py .114 

Figure 8-4 Portions of SODL scene graph displayed on multiple views.115 

Figure 8-5 dode.proc, a simple view with a single affine transformation node and shape 

.117 

Figure 8-6 Output of dode.proc.118 

Figure 8-7 SODL side messaging.120 

Figure 8-8 Scheduling, revoking and processing messages in the GVM message queue 

.121 

Figure 9-1 Schematic of Simplel.123 

Figure 9-2 The Simplel Simulation.124 

Figure 9-3 Process construct for the Simple2 simulator.125 

Figure 9-4 Message transport in Simple3.126 

Figure 9-5 Simple3 process construct declarations.126 

Figure 9-6 Source code for the Ping demo.127 

Figure 9-7 Ping message transport.128 

Figure 9-8 Ringl Token ring using a subscription service.128 

Figure 9-9 Ring.proc; The parent process for the Ringl simulation sample.129 

Figure 9-10 Processes controlling (a) the subscription service and (b) individual ring 

members.130 

Figure 9-11 Message routing for Ring2 simulation.131 

Figure 9-12 Ring.proc; The parent process for the Ringl simulation sample.132 

Figure 9-13 Process ownership in Brigade2 demonstration.133 

Figure 9-14 Communication between parent and subordinate units in Brigade2 demo. 133 

Figure 9-15 Unit.proc; Basic unit construct in the Brigade2 demonstration.134 

Figure 9-16 soldier.proc; Declaration of the process;5oW/er construct in the Brigade2 
demo.135 


Figure 

Figure 

Figure 

Figure 

Figure 

Figure 

Figure 

Figure 

Figure 

Figure 

Figure 

Figure 

Figure 

Figure 

Figure 


9-17 Message routing in the Relay 1 demo.135 

9-18 Source code for Relayl process constructs.136 

9-19 Relay2 demo message routing diagram.137 

9-20 Relevant code for the processirelay and processireflector constructs.137 

9-21 Relevant code for the Relay3 sample.138 

9-22 Relay4 support process constructs.140 

9-23 process:c/iiW construct declaration for Relay4 sample.141 

9-24 process:base construct in the Relay5 sample.142 

9-25 Code segments for instantiated Relay5 process constructs.142 

9-26 Message routine in the Relay5 sample.143 

9-27 Message routine in the Relay6 sample.144 

9-28 Relay6 process construct for the processiZjoi'e.144 

9-29 Relevant Relay6 code segments.145 

9-30 Output from Bouncel demonstration.146 

9-31 ball.proc; Code governing ball motion and scene graph update.147 


XIX 
















































Figure 9-32 Messages for screen update in Bouncel demo.148 

Figure 9-33 Bounce2 output with 2,000 particles.150 

Figure 9-34 Messaging to update the scene graph in Bounce2.150 

Figure 9-35 particle.proc - relevant code for updating scene graph in Bounce2.151 

Figure 9-36 Brigadel sample output.152 

Figure 9-37 Output from the Hierarchy demonstration.152 

Figure 9-38 Sensor track detection and change notification in Battle demo.154 

Figure 9-39 Notification of sensor track detection and loss.155 

Figure 9-40 Messages governing vehicle motion in the Battle demo.156 

Figure 9-41 Predefined Tank Formations.157 

Figure 9-42 Messaging during formation movement.158 

Figure 9-43 Fire control sequence for a tank.159 

Figure 9-44 Clean-up after a unit destruction.161 

Figure 9-45 Shots of the initial platoon configurations in the BattleView of the Battle 

demo.161 

Figure 9-46 Red and Blue tactical views showing each side’s knowledge of the 

environment.162 

Figure 9-47 Tactical views shortly after the opposing forces encounter each other.162 

Figure 9-48 Sample engagement of opposing forces.163 


XX 























“In theory, there is no difference between theory and practice; 
in practice, there is. ” 

- Chuck Reid 


Chapter 1. Rationale and background 
1.1. Overview 

Simulation in recent years has become increasingly important in understanding natural systems. In 
particular, digital simulation provides a method of hypothesis testing, training, and planning that might 
otherwise be prohibitive because of either cost or risk. For instance, it’s cheaper and safer to train nuclear 
power plant operators on digital simulations than on fully functional equipment. The same claim can be 
made for manned space flight operations, as well as, to a somewhat lesser extent, aircrew training. 


Industry has become increasingly interested in simulation technologies as the cost of developing prototypes 
has dramatically increased. An example of this would be the development of the Boeing 777 airbner. 
Boeing designed and digitally tested the 111 for performance and manufacturability before ever building 
the first prototype. These tests were conducted using digital simulation (Boeing 2001). 


The military has also increasingly relied upon simulation to provide insight into deployment, operations 
and support plans, and was instrumental in developing early flight simulators to provide additional training 
hours to pilots. More recently, ground and naval combatants have used digital simulation for developing 
tactics and training purposes. Simulation systems to assess the overall effectiveness of new weapons and 
tactics are also in widespread use throughout the U.S. Department of Defense (DoD). It has become so 
important that in 1991, DoD established the Defense Modeling and Simulation Office (DMSO) to 
coordinate simulation activities throughout the department (DMSO 2001). 


It was in the arena of military simulation that the notion of this dissertation took shape. In 1997, the author 
was involved in a simulation exercise to ascertain the operational effectiveness of non-lethal weapons 
under a variety of scenarios. The client organization insisted these exercises use a simulation system 


1 







known as the Joint Tactical Simulation (JTS)'. JTS did not have the capability to simulate the effects these 
new weapon systems were thought to produce in the targets. Specifically, a target was either alive or dead. 
Since the purpose of the weapon system was to coerce a behavior in the target, rather than to outright kill it, 
there was a substantial effort involved in trying to get the simulation results to reflect the hypothesized 
effects. 

If there had been the capability to change the behavior of various objects in the simulation that end users 
could modify while maintaining the overall system integrity, then the effects of these new weapons could 
have been introduced quite simply. It would not have required re-verifying the entire simulation system, 
but only the altered portions. This became the ultimate motivation for providing a framework for 
describing simulation object behavior in an extensible manner. 

The result of this line of thought, and the object of this dissertation, is the Simulation Object Description 
Language (SODL - pronounced any way the reader prefers). SODL is an object oriented language, in that 
it has the standard ability to inherit behavior from parent classes. It is also completely event driven, in that 
object instances can only communicate with each other via message passing. This provides for the 
possibility of allowing the simulation to be transparently distributed across a heterogeneous network of 
computers while freeing the developer from actually producing the code to perform run-time 
synchronization, networking, message sequencing and delivery required to ensure that distributed 
simulations process messages in the proper order. Having stated this, we should point out that the actual 
run-time system developed to test and support SODL programs runs only on a single processor at the time 
of this writing. We took great care to ensure that it could easily be modified to a fully distributed 
implementation. 

Digital simulation is performed in any of a number of fashions, some of which will be discussed herein at 
some length. A digital simulation is broken down into a finite sequence of events. When each event is 
handled, the state of the simulation changes in some fundamental way. The difficult part for some types of 
simulation is ensuring that these events are handled in the proper order. This is not particularly difficult for 

' JTS was combined with the Joint Conflict Model (JCM) to form the Joint Conflict and Tactical Simulator 
(JCATS). These simulators were all developed at the Lawrence Livermore National Laboratory, Livermore 


2 







simulation systems that ran in only one process. In fact, events normally have some sort of time stamp 
associated with them, which, if these events are generated out of their intended processing order, can be 
placed into a priority queue or some other sorting mechanism to ensure that they are processed in the 
correct order. 

The situation is not nearly as clear-cut in distributed simulation. On the one hand, if multiple processors 
can be brought to bear upon a simulation problem, then an answer can be arrived at in a shorter period of 
time than would otherwise be possible. On the other hand, issues such as network latency, lost packets, or 
packets received out of order, and clock drift between CPUs in a distributed system can cause 
desynchronization between those nodes to occur, allowing events to be processed out of order. When 
events are processed out of order, it creates what is known as a causality error (Fujimoto 1990). 

There are two basic approaches for dealing with these causality errors in distributed simulation. 
Conservative synchronization (Bryant 1977), (Chandy 1979, 1981) seeks to avoid creating these errors by 
blocking processing on nodes in a distributed simulation system until additional processing can proceed 
without the risk of creating a causality error. Alternatively, optimistic synchronization (Jefferson 1982, 
1985a, 1987) provides a mechanism for detecting and recovering from these causality errors. It does this 
through state saving, allowing for the possibility of recovery from potential causality errors. When one is 
actually encountered, a cascading rollback and event revocation algorithm is used to restore the simulation 
system to a state whereby processing the event is temporally consistent with the state of the simulation. 
Since it does not block, it can provide a significant speed-up in simulation execution (Fujimoto 1990). The 
SODL implementation discussed herein uses an optimistic simulation engine to perform process 
synchronization. 

Neither of these approaches is appropriate for all simulation systems. Systems where there is a high degree 
of interdependence between objects within the simulation may lend themselves to a conservative approaeh. 
Optimistic synchronization may be appropriate for systems in which the components are interdependent to 
a limited extent. (Fujimoto 1990) provides a more extensive analysis of the benefits and limitations of eaeh 
approach. 


CA. For more information refer to (LLNL 1998). 


3 








The primary focus of this dissertation is the structure of the SODL language. Even though we implemented 
a sequential (i.e. non-distributed) runtime system, its usefulness stems mainly from its ability to test the 
SODL language structure. The primary rationale for this approach is that the body of work on this subject 
continues to grow. The run-time system provided is intended to act as a framework for incorporating these 
new results later, and should not be considered a final product. 


1.2. Alternative approaches 

There are a number of different approaches available to simulation system developers. What follows is a 
list of the various types of approaches that are currently in use, their strengths, weaknesses, and some 
examples. Table 1-1 provides a brief overview of these alternative approaches. 


Approach 

Description 

Full-Implementation 

All aspects of the simulation engine are built from scratch for a particular 
system. 

Modular System 

Portions of the simulation engine are linked into a final executable to perform 
some of the more mundane aspects of simulation such as message delivery and 
node synchronization. Calls to these library functions are still needed to make 
use of the simulation engine. 

Simulation Languages 

The simulation engine is provided as a run-time environment in which 
simulation developers describe the behavior of the simulation objects. It is 
rarely, if ever, necessary for a developer to make any direct calls to the 
simulation engine for messaging, sequencing, or synchronization. 


Table 1-1 Alternative Simulation Approaches 


1.2.1. Full implementations 

Early digital simulations were implemented first in low-level machine languages, and later, as compilers 
became available, in traditional structured programming languages such as Fortran, C, and others. All 
aspects of the simulation engine, from event sequencing through node synchronization were written and 
custom tailored to the specific system implementation. The JTS and JCM simulators mentioned earlier 
evolved from simulation systems developed in the 1970’s (LLNL 1998). One can open any journal on 
simulation to find this approach in use, even to this day. 

More recently, beginning in the late 1980’s, object oriented programming languages such as C-h-i-, Modula- 
2, and Smalltalk made certain aspects of simulation development somewhat easier and significantly more 


4 













intuitive. This increased ease did not extend to the core simulation engine, however. Still remaining were 
the complexities associated with process synchronization in distributed simulation systems. 

This approach has the advantage that there is a substantial amount of control developers have over 
optimizing system performance for a particular task. This flexibility, however, comes at a rather 
substantial cost in coding and debugging effort. This is particularly the case for distributed simulation, 
where node synchronization issues require extensive and complex code to properly address. 


Some examples of full simulation system implementations are listed in Table 1-2. 


Name & Producer 

Description 

Joint Conflict and Tactical Simulator 
Latvrence Livermore National 

Laboratory 
(LLNL 1998) 

A sequential simulation system with a distributed user 
interface, allows multiple operators to perform combat 
exercises to test new tactics and weapon systems. Used also 
for training purposes. Written primarily in C++. Users 
include the U.S. Departments Defense, Energy, and 
Treasury, as well as some international users. 

Joint Simulation Systems (JSIMS) 

Defense Modeling & Simulation Office 
JSIMS Program Office 

A distributed simulation system that is currently under 
development, using the High Level Architecture (HLA) Run 
Time Infrastructure (RTI) as a packet transport mechanism. 

It is intended to provide an extensive simulation capability 
for conducting training and analysis and doctrine 
development. 

Diffract 

MM Research 

Tucson, AZ 

Diffract is an optical simulation system used for simulating 
coherent light through optical systems. It can be used to 
simulate optical aberrations and interference patterns in 
optical equipment (Mansuripur 1997). 


Table 1-2 Some simulation systems used for analysis or training purposes 


In addition to the specific simulation systems mentioned in Table 1-2, there are numerous other simulation 
packages designed for fields as diverse as computational fluid dynamics to manufacturing. Others are 
designed to provide insight into the motions of stars and galaxies, to the workings of the smallest known 
particles. Many of these custom simulation systems, though very capable in what they do, are intended for 
a specific use that does not readily extend to other uses. 


Another area in which simulation is gaining some popularity is in the field of interactive digital 
entertainment (by which we mean computer video games). Most of these are built on proprietary special 
purpose simulation engines that facilitate event sequencing. Many of the newer games provide the ability 
to perform distributed game play over the Internet or via direct dial-up. Some recent examples of these 


5 












interactive games are listed in Table 1-3. Distributed game play has become increasingly popular, as more 
households are equipped with dial up and broadband Internet access. There are even massively parallel 
games in which an entire persistent virtual world has been created. Players can come and go as they please, 
joining forces with other players to battle against computer-controlled entities, or against other players. 
With faster Internet access becoming more widely available, online gaming is likely to continue growing in 
popularity and the games themselves will also continue to increase in their complexity as well. 


Name & Publisher 

Description 

The Sims 

Electronic Arts 

A successor to the popular SimCity. The Sims allows players to 
control people in the virtual environment as they interact with others. 
Virtual characters are affected by the inputs their real life controllers 

Battlezone and Battlezone 11 
Activision 

Players control a virtual army in an immersive 3-D environment and 
can issue orders to their subordinate units, manage resources and 
engage in combat all from a first person perspective. Multi-player 
modes are available to play over the Internet and over a LAN. 

Flight Simulator 2000 Pro 
Microsoft 

Players can take control of one of a variety of aircraft, each modeled 
to resemble the actual performance and handling characteristics of 
the real world counterpart. Microsoft also provides the capability to 
download from the Internet current real-world weather conditions. 

Ultima Online 

Electronic Arts 

A massively parallel virtual and persistent world where players 
control a virtual character who can fight wars with other player, build 
structures, or even complete cities. Any changes the players make 
persist so that others may interact with those changes. 


Table 1-3 Current popular games using simulation technologies 


1.2.2. Modular simulation systems 

It is sometimes possible to encapsulate portions of the central simulation engine functions in a collection of 
libraries, such as event sequencing and some rudimentary message delivery. Some libraries may perform 
network communications and synchronization required in distributed simulation. Others provide additional 
functionality defining, for instance, the behavior of simulation objects incorporated into a separate 
development initiative. The intent of these approaches is to use the libraries with a more commonly 
available programming language such as C/C-i-t-, Fortran, or Java. Other approaches, such as CORBA, 
require programmers to write portions of the simulation system in a different language, the Interface 
Description Language (IDL) in the case of CORBA. A compiler translates this code into a more common 
object oriented language. Standard compilers then compiles and links with the run-time infrastructure in 
the library. 


6 














Name & Developer 

Description 

Modular Semi-Autonomous Forces (ModSAF) 
United States Army Simulation, Training, and 
Instrumentation Command (STRICOM) 

A semi-distributed simulation system which has a 
number of military entities described in detail as a 
series of C modules. Simulation entity behavior is 
intended to mimic real world behavior to facilitate 
training and analysis for military operations. 
Simulation state is stored in a central database that 
is accessed via run-time engine (STRICOM 1999). 

High Level Architecture (HLA) 

Defense Modeling and Simulation Organization 
(DMSO) 

HLA provides a run-time infrastructure to 
facilitate distributed simulation. It primarily deals 
with network communications, not 

synchronization, which must be performed in 
custom simulation engines. 

Common Object Request Broker: Architecture 
(CORBA) 

The Object Management Group 

CORBA is a distribute object system that can be 
applied to simulation. As in HLA, a 

synchronization mechanism must be provided to 
ensme events are processed chronologically. 


Table 1-4 Examples of modular simulation systems 


While this approach does free developers from writing the complex code associated with these functions, 
there is still a great deal of interfacing with those libraries that must take place. Considerable time must be 
spent on the part of the developer to actually learn the API for the library. It is also usually necessary, in 
order to get the proper results out of the system, for the developer to have a keen understanding of the 
manner in which the routines or classes function (particularly in the case of libraries which provide object 
behavior descriptions and those that provide synchronization in distributed simulation). 


Some examples of systems that provide this modular approach are listed in Table 1-4. 


1.2.3. 4**^ generation simulation programming languages 

Recent years have seen the development of a number of special purpose simulation languages. Each of 
these has been designed to fill a certain niche. The majority of these languages have been sequential 
systems, by which we mean that they are intended to operate on only one host computer (i.e. they are not 
distributed). They can generally be broken down into two general categories: continuous time simulators 
(CTS) and discrete event simulators (DES). The biggest difference between CTS and DES systems regard 
how events are generated. CTS events are generated sequentially, while DES systems may generate their 
events out of order. Most simulation languages are sequential systems, not capable of operating in a 
distributed maimer. Some of these sequential systems are listed in Table 1-5. 


7 












Name & Developer 

Description 

Simulink 

The MathWorks 

Provides a graphical user interface to perform 
continuous time simulation of systems governed 
differential equations. Fully interoperable tvith 
Matlab, and extensible with custom routines from 
other programming languages. (MathWorks 2001) 

ProModel 

ProModel Corp. 

Graphical tool for simulating and analyzing 
processes. Extensive analysis tools are provided 
as well as the ability to export data to third party 
spread sheets for custom analysis. 

SOAR 

Carnegie Mellon University, University of 
Michigan, University of Southern California, and 
others, Soar Technologies, Explore Reasoning 
Systems, Inc. 

The SOAR project is a combined effort between 
several academic institutions and commercial 
ventures. It intended purpose is to bring intelligent 
behavior to simulation entities. It is built on top of 
the Tcl scripting language. (Rosenbloom 1994) 

ModSim, ModSim 11, SimProcess, SimScript 

CACI 

Programming languages for sequential simulation. 
ModSim and ModSimll are based on Modula-2. 


Table 1-5 Some commercially available 4“’ generation sequential simulation packages 


While sequential simulation technologies are useful for small to moderately sized problems, there are 
problem scales where the additional computing resources available from distributed simulation become 
necessary. SODL, the language described in this dissertation, is in this category, along with two others: 
Yet Another Distributed Discrete Event Simulator (YADDES) (Priess 1990), and A Parallel Object- 
oriented SimulaTion LanguagE (APOSTLE) (Wonnacott 1996). 


YADDES programs are translated into C and compiled using a standard C compiler. As such, much of the 
benefits of object-oriented programming are not realized in YADDES. It comes with both an optimistic 
and a conservative synchronization engine for handling messages and processing state changes. The fact 
that it can use either paradigm leads to some additional complexity in the language structure, namely that in 
order to realize any efficiencies in the conservative techniques, the process topology must be specified at 
compile time. It accomplishes this through an extensive specification of connections between topologically 
adjacent processes. 


APOSTLE is similar in intent to YADDES, but differs by generating C-i-i- code. This enables APOSTLE 
to be an object-oriented language, which it is. However, like YADDES, it is designed to work with both 
conservative and optimistic synchronization engines (though as of this writing, only the optimistic engine 
had actually been implemented). This again requires that the topology of the distributed simulation be 


8 













specified prior to run time, by stating which outputs feed which inputs. One restriction on the APOSTLE 
system is that it only runs on Sparc platforms. 

1.2.4. Distributed simulation standards 

During the 1980’s, the U.S. Department of Defense introduced the Distributed Interactive Simulation (DIS) 
standard allowing host processes performing a distributed simulation to communicate with each other using 
a common interface. Extensive libraries were developed for DIS, and it became a popular method of 
distributing simulation systems. Any DIS certified simulation system was able to send packets to or 
receive packets from any other DIS compliant system. This allowed different simulation systems to 
operate with each other, even though they had not originally been designed to do so. That is, simulation 
system X while DIS compliant may have been intended to work with simulation systems Y and Z. 
However, Z may not have been designed to operate with X. Thus, any messages passed from X to Z might 
be ignored when they arrive at their destination. 

These interoperability issues led the DMSO to propose in 1995 the High Level Architecture (HLA). Here 
constructs called “federations” are established for each group of simulation system developers wishing to 
have their simulation systems interoperate (Kuhl 1999). These federations define standard object types and 
message formats allowing the different simulation systems within the federation to communicate with each 
other. DMSO has provided the HLA Run Time Infrastructure (RTI), an extensive set of communications 
routines, similar to CORBA. Actual distributed simulation implementations making use of HLA require 
developers to write a great deal of code to address the specific issues pertinent to their system’s 
requirements. Apart from the format of the interface between components, a great deal of effort is also 
required to ensure compatible semantic content (i.e. ensuring that the same message means the same to all 
of the simulation components). 

1.3. SODL system description 

Neither YADDES nor APOSTLE makes assumptions about the underlying mechanism managing node 
synchronization in a distributed simulation. Since some methods^ require a rigid pre-specification of the 

■ Conservative synchronization, described in more detail in Chapter 2 is one such mechanism. 


9 







communications topology for optimization purposes, this topology must be specified regardless of the 
synchronization method employed. This provides additional opportunities for programmers to introduce 
errors and complicates message passing in a purely dynamic fashion. More fundamentally, this 
specification requires modelers to think in terms that may not be appropriate for their specific application. 
For instance, some models might naturally require arbitrary interaction between virtual objects. This 
means that the communications topology would need to be fully connected, or some mechanism to forward 
messages needs to be introduced into the model. 

This thesis introduces the Simulation Object Description Language (SODL) and takes a somewhat different 
approach. Like YADDES and APOSTLE, we designed SODL to facilitate distributed discrete event 
simulations (DDES). It does this by converting SODL source files into C++. It is therefore, like 
APOSTLE, an object oriented language, though perhaps not to the same extent. Where it primarily differs 
is in its assumption about the underlying simulation engine, namely that it will always be optimistic^ in 
nature. Optimistic synchronization methods do not take into account communications pathways of a 
distributed simulation in any of its optimizations, freeing developers from specifying the distributed 
simulation system topology. Messages are simply addressed and delivered to the members in the recipient 
list. This notion makes SODL, a completely event driven language requiring inter-process communication 
to occur exclusively through message passing. 

The guiding principle directing design decisions of the SODL system has been to make as clean a split as 
possible between the simulation engine and the behavior of the objects witbin the simulated environment. 
The behavior specification derives directly from the model description, and only rarely do simulation 
system restrictions interfere. Thus, SODL provides developers the freedom to express object behavior in 
terms that naturally arise from a model without having to be distracted with performing unnecessary run¬ 
time system declarations or calls to the underlying simulation engine to handle some action the engine 
could perform on its own. 

SODL provides a framework upon which simulation system developers can simulate models that make 
extensive use of the notions of stimulus-response. That is, each of the objects in a distributed simulation 


10 







system has a state and makes changes to that state based upon external stimuli. These stimuli can originate 
from any of the other objects in the simulated environment and need not flow over fixed communications 
pathways. SODL does retain the ability to optionally specify the communications pathways, but the 
decision to use this feature is solely at the discretion of the model maker and simulation system designer, 
and is not imposed upon them by constraints within the SODL language specification or its run-time 
system. Thus, most any model that can be framed in terms of stimulus-response can be directly coded into 
SODL source files from such an interaction specification. 

There are some drawbacks to this approach. By removing the necessity of defining the topology, we lock 
ourselves into an optimistic approach, which is not always the best for a given application (Fujimoto 1993). 

Another problem is that there is no convenient way for one object instance A to directly manipulate or 
access the data of another instance B. Instead, A must send a message to B, and it is B’s responsibility to 
manipulate its own data, or to reply to A’s query about its internal state data. While this may seem 
awkward to code, it does more closely reflect the way things happen in the real world. That is, when 
objects interact in the physical world, they do so primarily by sending messages of one form or another. 
One person will speak to another. When an anti-armor round strikes a tank, it can be thought of as having 
sent the message “I just hit you” to the tank. Thus SODL’s use of pure-event programming is, in the end, 
rather natural. 

SODL draws a distinction between simulation objects (which SODL calls processes) and the data that is 
transferred between them (which SODL calls messages). Messages can have arbitrary data fields and 
methods to act upon them in a manner analogous to objects in traditional object oriented programming 
languages. The message with its payload is transferred between objects in a completely dynamic manner 
(meaning that no pre-specified topology is required to direct the message traffic). Processes, in addition to 
having internal data and methods, are able to send and receive messages. The process modifies its internal 
data upon receipt of a message. 


^ Optimistic simulation is described in more detail in Chapter 2. 


11 








SODL processes are also modal in nature; they can turn modes of operation on an off based on the message 
stream they reeeive. This allows a process to act one way upon receipt of a message at one time, and act 
completely differently upon receipt of another message with the same payload while in a different mode. 
This conveniently provides developers with the capability of radically changing a simulation object’s 
behavior to a given stimulus (message receipt) with little difficulty. For instance, a simulation of an ant 
colony might have the ants behave in one fashion when they are searching for food, another when they 
actually find some and gather it, and still another when their nest comes under attack from a neighboring 
colony. A developer need only change modes when a certain condition is met, thereby fundamentally 
changing the object’s behavior. 

1.4. Scope 

The primary purpose of this research was to provide a logical framework for defining object behavior in a 
virtual environment. To this end, we introduce a conceptual framework for discussing simulation, and 
upon this framework, define a language structure allowing simulation system developers to easily and 
quickly specify these object behaviors. When it became clear that a stimulus-response description could 
provide this specification, distributing the simulation across a network of computers seemed like a logical 
but secondary extension of the underlying work. 

What we specifically avoid in our analysis are any measures of overall system performance. The rationale 
is that the current SODL run-time system can be modified to optimize its performance by taking advantage 
of new algorithms or techniques. We instead concentrate on the language specification itself and note that 
there is little in the way of programmer interface with the simulation system. This allows simulation 
system developers to concentrate on implementing a simulation of a model, rather than with the mundane, 
often error prone additional work other simulation systems require. 

We provide in this document a description of the sequential simulation system, intended to simulate a 
distributed system and used to test SODL. In addition, a number of sample programs and associated 
descriptions are provided to gain some insight into the capabilities and limitations of the language 
specification and any run-time system that might eventually be employed to support it. 


12 







Chapter 2. Digital simulation 
2.1. Overview 

sim u-late - vf. 1. To give a false appearance of; feign 2. To look or act like.'* 

Simulation has historically allowed scientists and analysts of various fields to test hypotheses about 
naturally occurring or hypothetical systems. For much of its history, simulation involved either a series of 
hand computations or analog devices designed to simulate some physical system. More recently, digital 
computers have allowed more sophisticated simulations in terms of their computational complexity, and 
been used in a wider variety of ways. Consider Bernoulli’s description of airflow through a venturi. Prior 
to digital computers, aeronautical engineering relied heavily upon hand computations and wind tunnel 
testing (analog simulation). With the advent of digital computer technology, we now have the ability to 
cheaply and easily perform high fidelity digital simulations of airflow around an airframe. 

While digital simulation systems are useful in describing physical systems that assist in analysis, other have 
been applied to training people to operate equipment that is either too expensive or too dangerous to 
actually train on. Examples of simulation for training include space flight operations, as well as nuclear 
power plant operations. 

For digital simulation, we can create virtual environments with which people can interact more safely, and 
in some cases more cost effectively than the real-world systems. Yet, these virtual environments only exist 
as a collection of I’s and O’s in a computer’s memory system and only reflect the real system in a way that 
is meaningful to those conducting the analysis or participating in the training. 

The range of applications in which simulation might be useful is quite varied. As such, no one approach to 
simulation will be appropriate in all circumstances. In some instances, a few lines of equations scribbled 
on the back of an envelope might be sufficient for a particular purpose. In others, hundreds of digital 
computers working in concert with each other might only scratch the surface of some complex system 
dynamics. 

* Webster’s New World Dictionary of the American Language, David B. Guralink, Ed. 1979 


13 








For purposes here, we will be considering primarily simulations performed with a digital computer. 

2.2. Modeling 

Before aetually getting to the point where a simulation is of any use, we quite often need to describe in 
some unambiguous manner the dynamics of the system under consideration. Modeling is this process of 
describing the system, and although it is not dealt with in any detail here, it is an integral part of the overall 
simulation process, and needs to be adequately tackled prior to writing any code. The modeling process in 
many cases will provide significant insights into a system’s dynamics - insights that may actually obviate 
the need for simulation. 

Before dealing directly with modeling, however, we need to provide some context. What follows is a 
framework around which we might construct some pertinent notions and to promote an understanding of 
some of the constraints inherent in digital simulation. While we make no claim as to whether or not any the 
following formalization of the modeling and simulation process appears in prior work, we developed and 
included it here because of its apparent absence in texts on the subject. Prior to formalizing this context, 
we provide a brief overview of these notions. 

We start by introducing the notion of a universe. A universe may have multiple time dimensions (called 
the universe’s temporal component). For each element in the temporal component, the universe has exactly 
one state. We normally are interested in universes with only a one-dimensional temporal component 
subject to some strict ordering. This induces an ordering on the universe’s states, and allows us to impose a 
causal relationship between states. 

Since a universe has a broader scope than we are normally interested in, we pare away much of the state 
information for a universe and concentrate upon one small portion of the universe, called a system. For the 
sake of discussion, let us consider the system of an object undergoing projectile motion under the influence 
of gravity. When considering this physical system we can ignore many of the minute influences that act 
upon this object, and look only at the very limited scope of the object and the primary gravitational sources 
acting upon it. 


14 







We then develop a model describing the system. Modeling formally describes how the system behaves. In 
the case of the object undergoing projectile motion, we look to Sir Isaac Newton’s laws of motion to 
describe how the projectile moves. We note that the model is something that we humans have done to 
describe a physical process. It in no way dictates how the physical system really behaves. For instance, 
Newton’s laws of motion are only an approximation of how objects really move. Finer predictions are 
possible with the introduction of Einstein’s theories on Relativity. The physical system always behaved in 
a certain way regardless of what people say or think about it; it took Newton and Einstein to propose 
models describing this behavior. 

Finally, we will want to study this model, to see how well it predicts physical systems, and perhaps learn 
new things about the system. We use simulation to perform this prediction. In the case of ballistic motion 
in a vacuum, we can simulate the behavior of physical systems quite easily with a pencil and paper. By 
employing a digital computer, we can incorporate other aspects of Newtonian motion (windage, or N-body 
interactions) to perform higher fidelity simulations in shorter amounts of time. 

From here, we formally introduce the concepts outlined above. 

Given an indexing set P and family of sets Vp, peP, let us define the Cartesian product pp 


pel' 


(2-1) 


and the family of canonical projections (Hungerford 1974) Ttp 


Jip-Pp^ Vp 

where ^(v)=Vp, the p* component of v. 

From this, given a set of parameters P, we can define a universe Up 


( 2 - 2 ) 


15 







(2-3) 


U„cp,.^Y[V, 

P^P 

That is, for purposes here, the universe is simply a subset of the Cartesian product of the sets Vj. In order to 
maintain a degree of generality, we make no assumptions about the set P, or any Vp, pe P, specifically, we 
make no claims as to their cardinality nor of the elements they may contain. 

Let TqP, P’=P-T. We can redefine Up in terms of P’ and T by 

Up ^ Pp' X Pp (2-4) 

We refer to T as a temporal component of P exactly when all of the following conditions are both satisfied; 

Ul. For all te pp there exists a unique p(t)^ Pp- such that (pit), t)e Up. 

U2. Up^[j{p{f\t) 

tepr 

When T=0 is the only temporal component of P for the universe Up, then we say that Up is a static 
universe. When Up is not static, it is said to be a dynamic universe. Though in general there is no 
restriction on the set T, we normally think of universes having r={R), where R is the set of real numbers, 
as their only temporal component. This provides a natural ordering of the states in the universe, and offers 
a convenient glimpse into how we might perform digital simulation - by calculating states in chronological 
order. 

We will not usually be interested in considering the whole of Up, but rather some suhspace of it. Therefore, 
given a universe Up, we define a system SpXpp over a collection of parameters PcP’ as 


SpXpp e YlV, 

xsRUT 

We then define r.pj-^Sp such that 

tti(r(t)) = tti(p(t)) for all te pr and ieR. 


(2-5) 


(2-6) 


16 









to ensure that the parameters in the system take on the same value as their associated parameters in the 
larger universe given the same time value. 

From this, we can create a model in which we simplify the actual behavior by aggregating system 
parameters and create rules governing how these parameters interact. 

First, we pick a finite set D to serve as an index for parameters in our model MpXpj-. We then choose a 
function/:/?—to aggregate all the system parameters into more manageable modeling parameters. So as 
not to overly complicate the model, we will impose the restriction on D and/that for all yeD, there exists 
xeR such thaty(x)=y. That is, / is surjective. If we were not to have this limitation, we could have a 
collection of model parameters that would need to be tracked, even though there is no analogous collection 
of parameters in the system Sr we are considering. 

Next, we need to define another surjective function g.pR-^pp aggregating the state of the system Sr into 
some state in the model Mp- The specific definition of this function, like /, is at the discretion of the 
modeler. 

From this we get the definition of a model over a collection of modeling parameters D, Mp^rhy 

Md^pt ^ 

xtouT ( 2 - 7 ) 

and we define d:pr —>Mp such that 

^(.dit)) = ?i;(g(r(f))) for all te pr and IgD (2 

(2-8) requires the model to approximate the system SrXPt to some problem specific degree and requires a 
great deal of discretion on the part of the individual defining the model to determine an acceptable error 
level. 

The last part of the model is to describe how the various parameters interact with each other. This involves 
explicitly defining the family of functions TVi^dlf)) in a manner that will satisfy (2-8) to the desired degree. 


17 







An important consideration here is that models are closed, in that there is no influence upon the model 
parameters from a source other than other model parameters. Any such external dependence would 
become part of the model. There is no restriction on the system from which the model is derived. 
However, we are generally well advised to pick the system parameters wisely so that there is a reasonable 
expectation that no outside influenee can significantly adjust any of the system parameters. We make no 
claim about the nature of the behavior of f/p or of how its parameters interact. 

As mentioned earlier, the process of modeling a system through this abstraction is at least as important, and 
quite often just as informative, as actually performing a simulation. It can provide a great deal of insight 
into the underlying dynamics of the system that would have otherwise been unrealized. Picking the right 
parameters in the system and properly aggregating them is critical in developing an adequate understanding 
of the system under consideration. It is somewhat of an art form to create a model, given only raw data and 
observations; an art form we will explore no further in the confines of this publication. There is a great 
deal of material available for creating models and some assistance in this regard may be found in books 
such as (Fishwick 1995) and (Morrison 1991). Both have an extensive list of additional references that 
could be useful in specific modeling applications. 

2.3. Digital simulation 

From this point on, we will only be dealing with universes with the temporal component T^jR}, the 
collection of real numbers. This imposes a complete order on the universe’s states if we consider them in 
terms of the time at which each state occurs. When performing a simulation, we are concerned with 
causality. That is, a universe’s state p(t) can only be affected by earlier states p{s) s<t. Simulation will 
seek to calculate the state of models for a collection of times of interest to the modeler. 

Once we have a model of a system, the next task is to get a computer to tell us interesting things about the 
model we can project back to the system under consideration. The problem with digital computers, 
however, is that they are not very good at expressing with arbitrary precision state values we would like to 
consider in our model. They are also hamstrung by the fact that each operation a computer performs 
requires some non-zero time to compute. We formalize these constraints as follows: 


18 








1. Digital computers perform only finite precision arithmetic 

2. Each operation on a digital computer takes some time, t>0, to perform 

We note that by virtue of these restrictions, each simulation of a model must be broken down into a finite 
number of discrete, contiguous intervals. We will denote these intervals la, h, ... I„.i and define each by 
ti+i) with to<ti< ... <t„. 

Condition 1 also requires us to limit our notion of the sets D and T. Dealing with T is a straightforward 
matter if we define R* - {to, fi, fn i). and 7^=={/?*). D, on the other hand, is not as easily handled in a 
general and formal sense. Each set of model parameter values W,-, ieD, must have a finite collection of 
associated approximations W* that a digital computer can represent and manipulate. 

We once again perform an abstraction, this time from MoX-pj to the digital simulation LoX-Pr*. 


jreD 


( 2 - 10 ) 


We go on to further describe the properties of L^xR* with lr*-rl<E, e>0 and sufficiently small, we define 
1'.Pt*—^Lo 


7tiil(t*)) = ^{d(,t)) V t*€ Pr», te D (2-11) 

All of these abstractions and simplifications of the underlying system lead us inevitably to conclude that 
there can be a substantial difference between a real world system, and a digital simulation. This in no way 
mitigates the importance of simulation, but instead serves as a caution not to put too much credence in the 
output of one, especially if sufficient testing of the model and an assoeiated simulation has not been 
performed. Speeifically, there needs to be on the part of the modeler a rather deep understanding of the 
system being considered. This understanding needs to include an awareness of the degree of dependence 
upon initial conditions of the system (how chaotic the system is), and how closely the model tracks the 
system’s behavior in reality. 


19 







To address these concerns, there is a Validation, Verification, and Authentication (VV&A) process within 
the United States Department of Defense whereby models and simulations are fine-tuned to more closely 
reflect the way the system actually works (DODI1996). 

Verification involves testing a simulation system to ensure that it reflects to an acceptable level of accuracy 
the specific model behavior for the system under eonsideration. That is, it verifies that the simulation 
system produces results that are consistent with the model it is supposed to simulate. Validation is the 
process of making sure that the model is a reasonably aeeurate representation of the system under 
consideration. This is normally accomplished after verifying the simulation by comparing results from the 
simulation with observations of the physical system. 

Validation and Verification are two steps in an iterative process. A model is initially created to represent 
some system. It then is coded into some sort of digital simulation. Results Irom the simulation are 
compared to those predicted by the model, to ensure that it aceurately refleets the intentions of the model 
makers. Once that is done, the simulation results are eompared against real-world data to see if the model 
is an aceeptable portrayal of the real world system. Refinements to the model are then made so that it more 
closely represents the system. These changes are then coded in the simulation, which must, in turn be 
verified again. This process is repeated until the simulation produces results within an acceptable toleranee 
of the real-world system. 

Onee the model and simulation are validated and verified respectively, an accreditation agent will certify 
that the model and simulation are fit for some specific use. Any enhaneements to the model will require 
repeating the full VV&A process. Simulation changes require only verification and accreditation. 

There are a number of techniques for actually performing the simulation on a computer. The techniques 
can be grouped into two main camps: Continuous Time Simulation (CTS) and Discrete Event Simulation 
(DBS). 


20 









2.3.1. Continuous time simulation 

Models requiring CTS approaches change their state in a continuous fashion and represent some continuous 
change in the system being analyzed. Such models quite often have as their rules a collection of partial 
differential equations governing parameter interaction. The field of numerical analysis is filled with 
numerical methods for simulating such equations. Euler’s method is a simple approach that may be 
appropriate for some applications. Other systems may require more complex approaches such as Runge- 
Kutta. In any event, (Press 1992) provides a rich source of C code and some brief explanations for many of 
the most popular numerical methods for simulating systems of partial differential equations. (Atkinson 
1989) is more theoretical in nature, not providing much in the way of source code, but providing helpful 
insights into some of the more common approaches. Finally, (Isaacson 1966) provides even more insight 
at the cost of being quite difficult to read. 

A continuous time model is then formally defined as an M^xpr such that there exists Pt such that for all 
e>0 there exists (Jo-E) •So+e) satisfying 

d{s,)*d{s,) ^ 2 - 12 ) 

As indicated above, even though the model state may continuously change with respect to time, the 
limitations of digital simulation require that it be broken up into suitably small time slices. Certain 
numerical techniques may change the size of these time slices, so we will make no specific assumptions 
about them. The goal of the simulation developer is when given the history l(to), /(fi), ... , l{t^.\) to 
determine /(f„) for m <n. 

Since CTS systems are not the focus of this dissertation, we will not discuss them any further. More 
information on CTS systems is available in books such as (Hockney 1988), which offers a very extensive 
list of references that can be useful for specific applications. 

2.3.2. Discrete event simulation 

Models that can be thought of as changing in some fundamental way at only discrete instances of time are 
known as discrete event models. Though the system the model is meant to represent may be changing 


21 


continuously, the model need not reflect that change. This is especially true for models of continuous 
processes, the partial differential equations of which can be exactly solved. Ballistic motion for instance 
can be solved exactly if certain simplifying assumptions can be made. Though an object undergoing 
ballistie motion is continuously changing its position, the parameters governing that motion are only 
changed at discrete instances of time. Thus, it can be modeled quite simply using a DES system. 


Like CTS systems, DES systems are broken into discrete time slices. Unlike CTS systems, however, the 
model state is considered static between these iterations. Specifieally, for all te. [t„, tm+i), 0<m<n and for all 
SG [tp.], tp), 0<p<n we have: 




(2-13) 


d(tp)^d(s) 


(2-14) 


This translates well into the actual simulation. Equation 2-10, since we are forced to deal with things in 
discrete time slices by virtue of our restrictions. 


Another major difference between CTS and DES systems is that the events needing to be processed in a 
DES system may not be generated in the order they are to actually be processed^. This has the potential to 
impact simulation system performance since sorting an unsorted list of objects has Q(n logn) time 
complexity. Practically speaking, however we might be able to do a little better. First, we note that we can 
never schedule an event to take place in the past, since that would violate causality. If we can further 
assume that the number of pending events does not exceed some constant value M (which is independent of 
n the total number of messages processed in a simulation run) then insertion into the pending event queue 
can be performed in O(logM) = Q(l) time. Thus the overall time complexity for processing n events 
actually ends up being Qfn logA/) = Q(n) £l(logAf) = £2(n). This is a reasonable simplifying assumption. If 
there is no such M then the number of pending events is not bounded, and will grow to fill the system 


^ This is perhaps the most important difference between continuous time and discrete event simulations. If 
discrete events are generated out of order, a discrete event simulation system is required to ensure that they 
are processed in the proper order. 


22 







memory eventually causing an abnormal termination of the simulation. In such cases, we will have to 
bound n. 

2.3.3. Distributed discrete event simulation (DDES) 

The focus of this dissertation is on distributed discrete event simulation. By this, we mean a discrete event 
simulation that is performed in a multiple instruction, single data (MISD) or a multiple instruction, multiple 
data (MIMD) environment (Fishwick 1995). Significant speedups can be achieved when additional 
processing power is applied to a simulation problem. The major complication in doing this stems from the 
fact that events need to be processed in the proper order, requiring a certain level of synchronization 
between the various nodes in the distributed simulation. This can be mitigated largely in MISD simulation 
topologies with some additional code to provide for proper synchronization. This is not nearly as 
straightforward in the case of MIMD topologies. It is not hard to imagine a circumstance whereby a 
message is delivered for processing to a node in a distributed simulation, only to discover that the node has 
already progressed beyond the intended processing time of the incoming event. Such errors are called 
causality errors (Fujimoto 1990). 

At this point, we introduce notation common in most of the literature on distributed discrete event 
simulation, that being Physical Processes (PP) and Logical Processes (LP). If we further partition D into 
the N sets Dq, D\, i. we can induce a collection of physical processes PP*, i<N, by 


/■/-‘c Ylw, 


(2-15) 


with the following properties for each PP*, i<N and we can define PPiiUj—^PP* which satisfies 
7^(PPi(t)) = n^id{t)) for all fe Ut, xeDj (2-16) 

This then induces the logical processes LP* similarly by 




(2-17) 


23 







and we define LPi.pj*-^LP* so that it satisfies 


TtJiLPiit)) = 7Cx{l{i)) for all te pr*, Di ( 2-1 ) 

From this point, we will drop the * from LP* and PP* when referring to the logical and physical process. 
We will explicitly use LP,(t) and PP,(t) to refer to the states of LPi and PP, at time t, respectively. 

Simulation then becomes defining or computing the collection of functions, Cijitg, 4 ), called events, that 
transform LPfl^.i) to LPj{tk) for j<N, 0<g<k<n. This notation indicates LPtifg) scheduled the state change 
from LPj(tt.{) to LPj(tk). These events can be thought of as messages transmitted between logical processes, 
containing enough information to allow receiving logical processes the opportunity to properly change their 
state. Thus, upon receipt of a message LP, will change its internal state and issue additional output events. 



Description 

Conservative 

Causality errors are prevented fi-om occurring, usually through some sort of 
blocking mechanism on each LPj. 

Optimistic 

Causality errors are detected and, when they occur, the simulation system will 
recover from them. 


Table 2-1 Distributed Discrete Event Simulation approaches 


In distributed simulation, each LPj could reside on different host processors, making communication via 
message passing a natural mechanism for inter-process communication. The problem becomes how to 
order events on each of these logical processes without creating causality errors. Alternatively, an 
approach at distributed simulation might allow causedity errors to occur, but with sufficient care, a 
mechanism to detect and recover from them might instead be employed. These are the two main 
approaches used to ensure that the temporal integrity of each logical process remains intact. They are 
contrasted in Table 2-1. 

Conservative techniques were the first to be adopted and employed in distributed simulation. There are a 
number of different algorithms available; two of the most popular conservative approaches are the Null 
Message Algorithm (NMA) (Chandy 1979), (Bryant 1977) and the Chandy-Misra Algorithm (CMA) 
(Chandy 1981). NMA prevents deadlocking states from being achieved while CMA has a mechanism to 


24 












detect and recover from them®. These algorithms normally require the specification of a rigid 
communication topology whereby messages are sent from the outputs of one LP to the inputs of another 
through fixed channels. Knowledge of this topology - specifically, the topology’s dependency graph - is 
critical in either avoiding or detecting and recovering from deadlocked states and for preventing causality 
errors. 

Optimistic techniques have their origins in the notion of Virtual Time (Jefferson 1985a). Here all events 
and LPs have a time stamp that is used to maintain temporal consistency. Suppose an LP at time U receives 
a request to schedule an event at time tk<ti. The LP then must restore its state to time 4 . It must also revoke 
any events it issued after time 4 . The LP can then process all of the events it has for time 4 and later. The 
memory obsolete data occupies is periodically reclaimed in a process known as fossil collection. 

The Time Warp Operating System (TWOS) (Jefferson 1987) was a research initiative in the late 1980's and 
early 1990's to investigate the performance improvement that could be realized through optimistic 
synchronization applied to distributed simulation. There is considerable literature available on the actual 
implementation (Reiher 1992, 1990c), debugging and optimization (Reiher 1990a, 1991b), and related 
topics (Reiher 1990b, 1991a). 

The SODL system described herein makes use exclusively of optimistic synchronization based heavily on 
the approach in TWOS. While this may be problematic for some applications, (notably those with a high 
degree of coupling between logical processes) these are sufficiently extreme cases that their exclusion 
seemed a reasonable tradeoff, especially since other simulation languages (YADDES and APOSTLE, for 
instance) are designed to work with either conservative or optimistic approaches. The plus side of this 
tradeoff is that the simulation system developer is able to construct a much more loosely coupled 
simulation topology (since specification of communications channels are not necessary in optimistic 
simulation). 


® A deadlock state in a distributed simulation is one whereby, due to a circular dependence of each of the 
logical processes, none of them can make any progress. It can be considered a generalization of the Catch 
22 problem. 


25 








This then is context in which we conduct simulation. We have a universe, perhaps the one we all enjoy, or 
some hypothetical one, which has a host of systems within it. Some of these systems may exhibit behavior 
we would like to better understand. In the modeling process, we make formal behavioral descriptions of 
these interesting systems, based on the behavior we either conjecture or observe. We then simulate these 
systems to see if our model is an adequate representation of the system we are trying to understand, and 
make changes to more closely reflect it if it is not. Once the simulation and the model both adequately 
represent the system, we use the simulation to draw new conclusions, allowing us to better understand our 
world. 

Chapter 3 discusses in more detail the notion of optimistic simulation, and describes in general terms how it 
is employed in the SODL run-time system. 


26 







Chapter 3. Overview of optimistic synchronization 
3.1. Overview 

The notion of optimistic synchronization came about in the mid 1980s (Jefferson 1982) in response to one 
of the criticisms of conservative methods. This criticism was that poorly balanced loads tended to render 
many LPs idle while they wait for slower LPs to complete their designated tasks, even when there may not 
be any specific dependency between the blocked LPs and those performing computations. While poor load 
balancing still adversely impacts optimistic synchronization, it does so only in cases where there is a data 
dependency between faster LPs and slower ones. Still, it is this one characterization that distinguishes 
conservative from optimistic synchronization; conservative synchronization attempts to avoid causality 
errors, while optimistic synchronization attempts to recover from them (Fujimoto 1993). 

One of the problems with distributed simulation is that messages in transit between sender and receiver 
nodes are not always completely accounted for. These messages in transit can take an arbitrary amount of 
time to be delivered, and they may not necessarily be delivered in the order they were transmitted. They 
need to be accounted for in any distributed simulation algorithm. 

In this chapter, we continue the analysis begun in Chapter 2, directed at the notion of optimistic simulation. 
Recall that a distributed simulation has a collection of logical processes LP„ i<N, and a finite number of 
states LPiitj) for tje T*. Logical processes transition from LPjitj.i) to LPi{ti) in response to processing an 
event tj), which was generated on LPft^. In this case, LPj is the destination logical process, and LPj 
is the source logical process of the event. The event processing time stamp, the virtual time at which the 
event is actually processed, is tj. Finally, the event generation time stamp, the virtual time at which the 
event was actually generated, is tg. We also will use the terms “event” and “message” more or less 
interchangeably throughout the remainder of this presentation. 

We impose some restrictions on how the various LPs in the distributed simulation may behave; 

1) In response to an event tj), LPjita i) may change its internal state to LPjit^, and transmit a 
(possibly empty) collection of output events. 

2) No LP may directly access the internal state data of another LP. 


27 







3) Events are time stamped. All events processed by a particular LP must be processed in time stamp 
order. 

4) All events, tj), must be scheduled for some future time. That is tj>tg. 

That in mind, we describe the basic Time Warp algorithm (Jefferson 82). We start by describing the 
various data structures we will need to facilitate node synchronization on each LP. Table 3-1 lists these 
data structures and describes how we will be using each of them. 


We need to provide a mechanism for revoking messages that have been transmitted, in the event that this 
should become necessary. We therefore introduce the concept of an antimessage. Each antimessage 


tj) is associated with a particular event, CyCtj, tj). If LPi receives an antimessage for an event, LP, removes 
it from its event queue without processing it. 


Data Structure 

Description 

event_p_queue(i) 

This data structure is priority queue' that places the earliest 
event at the top. There should be some mechanism whereby no 
two messages have the same chronological value, despite 
having the same time stamp value. This can be accomplished 
by appending a unique ID field to act as a tiebreaker in the 
event of identical time stamp values. The next event to process 
is at the queue’s top. 

antimessage_p_queue{i) 

This data structure is also priority queue that stores inbound 
event revocation requests. They are ordered in the same 
chronological order as their associated events, with the earliest 
antimessage always at the top of the queue. 

statequeue(i) 

This data structure, a traditional double-ended queue, retains 
copies of the state of LPj for each event that is processed. Later 
states are at the back of the queue, while older states are at the 
front. The current state is normally inserted at the back, and 
older ones are removed from the front. 

processed_event_queue{i) 

This data structure, which can also be implemented as a double- 
ended queue, retains a copy of each of the events that LPj 
processes. As each event is processed, it is inserted at the back 
of the queue. Older events are at the front of the queue. 

outputeventqueueii) 

This data structure can also be implemented as a double-ended 
queue. It retains a copy of all messages generated on LPj, with 
the latest ones being pushed to the back of the queue, and the 
oldest in the front of the queue. They are ordered according to 
their generation time, t„, not their delivery time, ta. 


Table 3-1 Data structures needed for implementing the Time Warp algorithm 


’ Priority queues are discussed in more detail in (Cormen 1990), ppl49-150. 


28 


















1) The state of each LPi(start_time) is initialized. Bootstrapping events are also scheduled in 
each event_p_queue{i). The remaining queues should be empty. 

2) While there is an LP, with at least one message to process or there are messages in transit: 

3) Push the current state, LP,(0. into the back of state_queue{i). 

4) While the next message in antimessage_p_queue(i) revokes the next message in 
event_p_queue(i), remove and discard the top message of each priority queue. 

5) Process the next event ejjitg, tj) in event_p_queue{i), and push it into the back of 
processed_event_queue{i). This results in setting the state of LP, to LPi{tj) and sending any 
outbound messages to the intended recipient LP’s. A copy of each of these outbound 
messages is pushed into the back of the output_event_queue(i). 

6) Upon receipt to LPi(t) of the event ej i(tg, tj), if tj>t, it is inserted into the event j_queue{i) to 
be processed in chronological order with the other pending events. If tj<t, then a rollback to 
time tj is performed, and ej iitg, tj) is scheduled with the remaining events. 

a. To recover state LPi(tJ), pop from the back of state jqueue{i) until the back element 
has a time stamp i<tj. 

b. Remove from the back of processed_event_queue{i) each event eji{tg% tj) with time 
stamp td^td, and reinsert it into event__p_queue{i). 

c. Remove from the back of output_messagejqueue{i) each event ei^j^tg, td) that has 
generation time stamp tg>td, and send the associated antimessage td') to LP*. 

7) Upon receipt to LPi{t) of the antimessage td), if t<td, then insert td) into 

antimessage_p_queue{i). If t>td, then perform the rollback to time td described in 6 a-c above. 

8) Periodically update the local estimate of the global virtual time, GVTP, 

a. Pop from the front of state_queue{i) all states prior to GVTEj except the latest one 
prior to GVTEi. 

b. Pop from the front of processedjeventjqueue{i) all events with delivery time stamp, 
td<GVTEi. 

c. Pop from the front of output_event_queue{i) all events with generation time stamp, 
tg<GVTEi. 


Figure 3-1 Synopsis of the Time Warp algorithm 

We also need to introduce here the concepts of Global and Local Virtual Time (GVT & LVT respectively). 

Deflnition 3-1: Given a logical process LP„ the Local Virtual Time for LPj, LVTi{r) at real world time r is 
defined to be the time stamp, td,, of the last event processed epitgu tdd at or prior to real world time r. 


29 











Definition 3-2: The Global Virtual Time at any real-world time r is defined as: 


GVT(r) = min 


\jLVTXr)UMT{r) 


V ' 


(3-1) 


Where MT{r) is the set of message processing time stamps of messages in transit at real world time r. 

Here, LVT,(r) is defined as the local virtual time of LP, at real world time r. Practically speaking, the GVT 
computation is fairly complex, though there are a number of algorithms available, notably (Bellenot 1990), 
(Fabbri 1999), and (Lin 1989). In all cases, the GVT value that is actually used approximates the real 
GVT. This is fine provided it does not overstate the actual GVT value. 


We describe the basic Time Warp algorithm in Figure 3-1, using variables described in Table 3-1. There 
are a number of algorithms available to perform the GVTE, computation referenced in step 8. We discuss 
one such algorithm in section 3.5 below. 


3.2. State saving 



(c) output_event queue(i): Each e/,„ ey, C/ t, and e/,/ will be delivered to LPi, LP/, 
LPi„ and LPi respectively for processing at the proper time. 

Figure 3-2 Saved state data of sample logical process at time 20 


30 











When LP, receives a straggler, which is a message with a processing time stamp less than the LVTj at the 
time of delivery (Fujimoto 1993), LP,’s state must be restored to a time that makes processing the straggler 
temporally consistent. Therefore, there is certain data needing to be saved in order to facilitate this state 
recovery. This includes the state of each LP,, all processed events prompting state changes in LP„ and any 
events LP, generated because of processing earlier events (Reiher 1990b). Figure 3-2 depicts a typical 
implementation of the state saving process. This is somewhat different in the SODL run-time 
implementation, where several LPs are aggregated together into what is called an Engine. However, the 
same general idea is employed. 

Figure 3-2 (a) shows the various LPj(f) values as the state of the logical process at time t. Each Cijitg, tj) is 
an event scheduled for LPj generated at time stamp tg and intended to be delivered at time stamp tj. The tg 
time stamp is only used by LP, to facilitate the event revocation in the rollback mechanism. The tj time 
stamp is used only by LPj to properly order the event. The bold items in the figure 3-2 (a), (b), and (c) 
represent the components added to their respective double ended queues after processing e((5.5, 20.0). 

As events are processed and removed from event_p_queue{i), they are stored in a 
processed_event_queue{i). When an event for LPj is processed for a time stamp later than the one that is 
currently at the back of state_queue{i), a copy of the back element is added at the queue’s back end, and 
the time stamp changed to reflect the event time stamp. The event is processed on the new back element, 
and any outgoing events are inserted at the back of outputjevent_queue{i). The processed event is also 
inserted at the back of the processed jeventjqueuetj) after the LP has completed processing the event. 

3.3. Fossil collection 

Sinee events can be scheduled to occur only in the future (per restriction 4 above) we can be assured there 
are no events processed before the Global Virtual Time (GVT). 

Figure 3-3 reflects the data stored in the LP,- depicted in Figure 3-2 after receiving a notification that the 
GVT is not earlier than 7.5. 


31 







Upon notification of an update of the GVT, the process of reclaiming memory occupied by obsolete data 
ean be performed. By restriction 4 above, no event can be sent into the past. Therefore, since the GVT is 
the lowest time stamp of all of the LPs in a distributed simulation, no pending events prior to the GVT 
remain. This fact allows us to reclaim most of the saved LP internal data with a time stamp prior to the 
GVT. Specifically, all members of processed_event_queue(i), ep(tg, tj) where t^GVT can be removed 
from the front of the processed_event_queue{i). This is easy to do since they were entered into the 
processed event from the baek by their td value. Similarly, all members of output_eventjjueue{i) 
generated on LP, where f^<GVT can likewise be reclaimed as no rollbacks can restore the LP to a state with 
time stamp prior to the GVT. 





& 

S' 


(a) state_queue(i) 



(b) processed_event_queue{i) 


"S’ a 



!&• 

& 

g. 


(c) output_event_queue(i) 


Figure 3-3 Result of fossil collection with GVT=7.5 

The story is slightly different for the saved state data in the state jqueue{i). Since the GVT is 7.5, we need 
to be able to recover state data for any time after 7.5. However, the earliest state we have after 7.5 has time 
stamp 10.5. This will do us no good if we need to recover state data for time 8.0, should that be necessary. 
The solution is to remove saved state data from state jqueue{i) up to, but not including the last time prior or 
equal to the GVT. Having done this, we can now recover the state to any point after 7.5. Since there ean 
be no state changes in the LP for time stamp values in the range [5.5, 7.5], it will just be LP,(5.5). 


32 











This fossil collection process can serve a second purpose, other than just reclaiming memory. Specifically, 
during fossil collection, we can perform any irrevocable activity. Such activity could include writing data 
to a log file (or any lO activity for that matter) or allocating or deallocating memory not specifically related 
to the synchronization protocol. 



(c) output_event_queue(i)\ Events e//20, 21) and e,,i(20, 21) were revoked by 
sending antimessages a/,/20,21) and a/,/20,21) to LPj and iP* respectively. 


Figure 3-4 Results of a state rollback on LPi to time 6.0 

Given LE,(r) and a newly received event CfciCtg, tj) is called a straggler any time tj<t (Fujimoto 1993). Upon 
receipt of a straggler, LP,(0 must become LPiitj) in order to proeess the new event in a manner consistent 
with causality. Rollbacks can also occur when reeeiving an event revocation, ajiitg, tj) with tj<t. 

Figure 3-4 shows the effect of a rollback to time 6.0 from the state indicated in Figure 3-2. 


Here, we see that all of the processed messages with t^6.0 were popped from processed_event_queue{i) 
and reinserted into event_p_queueii) for processing after the new event. All of the members of 
output_event_queue(i) with tg>6.0 were revoked and their associated antimessages were sent to annihilate 
them. The members of statejqueuetJ) with time stamp t>6.0 are also removed and the memory they 


33 

















occupied is reclaimed. The new state, from which we can now process the new incoming message, has 
time stamp 5.5 

We note that the state saving and rollback mechanisms used in the Time Warp algorithm keep all of the LP 
queues state_queue{i), processed_event_queue{i) and output_event_queue{i) in chronological order 
according to their time stamps, t, tj, and tg respectively. This makes managing them straightforward, 
permitting all operations to take the form of either popping from or pushing onto the baek or front of the 
respective queues. 

3.5. Global Virtual Time computation 

Though the Time Warp algorithm makes no specific mention of an algorithm for the Global Virtual Time 
computation, it is an integral part of the fossil collection process. Most algorithms used in GVT 
computation require that it be done synchronously. That is, all of the LPs need at the same point in real 
time to somehow communicate their current LVT with all of the other LPs. This ean then be used to 
perform the fossil collection described above. This can impact system performance as eaeh LP has to stop 
what it’s doing, and wait for the computation to be completed. It then needs to perform the fossil 
collection. As a result, the system will periodically pause while all of this is going on. Some of the more 
popular synchronous GVT computations are (Bellenot 1990), (Fabbri 1999), (Lin 1989). (Bauer 1992) and 
(D’Souza 1994) both proposed somewhat different general asynchronous approaches to performing the 
GVT calculation, each with their drawbacks. (Fujimoto 1997) and (Xiao 1995) describe asynchronous 
methods specifically geared towards shared-memory multiprocessing systems. 

We would like to make an observation about GVT, and try to relax requirements that might otherwise 
eonstrain various methods. We first show that the GVT increases monotonically given all events e,j(f^, td) 
in a distributed simulation, where td>tg. 

Theorem 3-3: let x, ye R, such that x<y. Then GVT{x)<GVT(y). That is, GVT increases monotonically. 

Proof: Suppose otherwise. Then there exists x, ye R, such that x<y but GVr(x)>GVT(y). Then at some 
time x'£[x, y) there was a rollbaek on some LPt such that LVTk{x')<GVT{x), or an event t^) was 


34 








generated on LP^ where tj<GVT{x). This second possibility can be dismissed quite simply by noting that 
such a case would violate the principle that a message have a processing time stamp strictly greater than its 
generation time stamp. In other words, it violates the requirement that tg<tj. 

Let us therefore examine the first possibility in some detail. must have received from some LPj an 
event tj) causing a rollback. Let us note that tg<tj=LVTi^x’)<GVT(x) for this event. Now, either LPj 
generated ejjltg, tj) before, at, or after real world time x. Let us consider each of these cases separately 

I. ^j.k(.tgr td) was generated before real world time x. Then it was delivered to LP^ after real world 
time X, meaning that it was a message in transit during the entire interval [x, x’). Specifically 
it was in transit at time x, implying that tj&MT(x). From above we see tj<GVTix), and we get 
GVT{x)<imn(MT(x))<tj<GVT{x) resulting in a contradiction. 

II. ^j.kOg’ was generated at or after real world time x. Then we are forced to conclude that 
GVT(x)>LVT;,(x’)=tj>tg^VTj(x)>GVT(x), which is another contradiction. 

Hence, the circumstance that GVT(x)>GVT(y) cannot ever arise. ■ 

From here, we note that we can relax somewhat the requirements of the local estimate of the global virtual 
time on each LP, without impacting the validity of the Time Warp algorithm. 

Theorem 3-4: Given a collection of logical processes, LPq. •••> LPn \, each with local virtual times 
LVTo(r), LVT\(r), ..., LVTna (r), at some real time r, let GVTEi{r) be the local estimate of the GVT{r) on 
LP, at real world time r. Then no LP will ever have an unrecoverable causality error provided that 
GVTEiir)<GVTir). 

Proof: Since LVTi(r)>GVT(r) for ie (0, 1, ... N-1}, any tj) generated on LP, at or after real world 
time r will have tj>tg^VTi{r)>GVT{r). Now tj) will be delivered to LP^ at some real world time r+5. 
But, until delivery of is actually performed, it is a message in transit and we get 

tj>tg>GVT{r+S)>GVTEi,{r+S), allowing us to perform any necessary rollbacks on LP^. ■ 

Corollary 3-5: If GVTEi{r)>GVT{r) for some i, r, then there is a possibility that an unrecoverable causality 
error may occur. Thus, the state of the distributed simulation in such a case is invalid. 


35 








Proof: Let i be such that GVTEi(r)>GVT{r). Since GVT(f) is the minimum of all the local virtual times 
and all messages in transit, there is either a j such that LVTj{r)<GVTEi(r) or a message in transit with time 
stamp GVT{r). It is possible that either LPj, or a message in transit with time stamp less than GVTEi(r) 
causes (ether directly or indirectly) a roll back to a time prior to GVTEi{r). ■ 

An important upshot of Theorem 3-4 is that as long as no GVTEi(r) overstates the actual GVT{r) no two 
GVTEiir) values need to be the same. 

One might be tempted to use these results to develop a token-passing asynchronous GVTE computation. 
For instance, consider an algorithm whereby a token is passed around a ring of nodes in a distributed 
simulation system. This token contains a payload allowing the receiving LP; to compute GVTB,(r). It then 
adjusts the payload of the token, and passes it along to Z.P(,+i) mod n- Each LP, will have different estimates 
of the GVT, and at first glance, it would appear that the hypothesis of Theorem 3-4 is satisfied. This is not 
the case. 

Consider the situation depicted in Figure 3-5. In this case, the GVTEiir) is based upon the state of LP’s at 
real world times earlier than r. It is possible that in the intervening time, some LPj could have had a 
rollback to some time LVTj(r)<GVTEiir), meaning that GVT(r)<GVT£,(r), opening the possibility of an 
unrecoverable causality error. 


I 


(a) - LVT values used to (b) - Actual LVT values at 

compute GVTEiirnj^ time r^y 

Figure 3-5 Possible unrecoverable causality errors in asynchronous token passing GVTE calculation 

This problem is not limited only to a token ring approach, but any GVTE computation that uses data based 
upon obsolete LVT values. This explains in part the popularity in synchronous approaches such as 
(Bellenot 1990) and (Lin 1989). The problem with most synchronous approaches to GVT computation is 
that they require processing to stop on all the nodes in the distributed simulation while the GVTE 


Z,fTo(r,(o)) 

LVTiM 

LVTjir 


LVToirn2)) 

LlT2(r,(2)) 


36 
















computation is performed. This has the possibility of adversely impacting performance of the simulation 
system. 

Several asynchronous algorithms have been suggested, notably (Concepcion 1990), (Bauer 1992) and 
(Mattem 1993). We will focus particular attention her upon one of these approached. 

(Mattem 1993) suggested coloring LPs either white or red. A white LP sends white messages, and a red LP 
sends red messages. All LPs are initially white. The approach is essentially to count the number of white 
(red) message that have been sent and received while the color of all the LPs is red (white). Once the 
difference between the sent and received messages is zero, a lower bound of the GVT can be calculated by 
getting the minimum time stamp that occurred during the collection of white (red) messages. 

Mattem provided a formal description of his algorithm as it specifically applied to ring topologies, but 
provide no formal correctness proof. We suggest a more general version of Mattem’s formal algorithm, 
making no assumption about the simulation topology, and prove its correctness. 


Table 3-2 lists the various routines that we use in the general algorithm, as well as a description of then- 
function. Table 3-3 lists the various data stractures and a description of the data they contain. Finally, 
Figure 3-6 lists a generalized variation on the original ring-topology algorithm Mattem proposed. 


Routine 

Description 

Global_Min(Xi) 

A distributed procedure to return the global minimum of all the values, Xt for i<N. 
This value is returned to each LPi at the completion of the call. 

GlobalSum{xi) 

A distributed procedure returning the sum of all the values, x, for i<N. This value is 
returned to each LP, at the completion of the call. 

SynchronizeQ 

A distributed procedme to force all of the nodes in the distributed system to start at 
the same point in the GVTE computation at approximately the same real world time 

Reset M/«,0 

Sets a local variable, Pw, to the current i FT) on the host making the call. 

Get_Min,{) 

Returns the minimum LFT) value that occurred since the last Reset MinQ call. This 
minimum is adjusted if necessary every time a rollback occurs on LP,. 


Table 3-2 Routines used in the asynchronous GVTE computation 


Data Structure 

Description 

PiE {red, white} 

This “color” is toggled between the two possible values between successive 
iterations of the algorithm. Each event ei /(tg, tj) takes on the color at the real 
world time it was generated. 


The number of messages sent from LPi with color pj initialized to <0.0, 0.0>. 


The number of messages received by LP, with color pi initialized to <0.0, 0.0>. 


Table 3-3 Data Structnres used in the asynchronous GVTE computation 


37 

























GVTEjComputationO is ran on a separate execution thread on each LPi during the entire distributed 


simulation ran. This allows the main simulation engine to continue processing events during the GVTE 
computation. Actual implementation can make some modifications to the parameters of the various loops 
allowing processing to continue in the main portion of the simulation engine without occupying too much 
time in this routine. 


GVTEComputationQ 

int outstanding H Number of outstanding messages with phase p, 

int old j), // Current value of Pi, prior to being incremented 

SynchronizeQ-, H Make certain all are doing this for the same p, 

while(/7-Me) 

old j) i <— p, H Pre-incremented value of pi is used in the computation 

Reset_Min,Q H Get the current simulation time. 

Pi <— (p/+l) mod 2 // Pass messages with this new Pi. 

do 

outstanding <— Global Sum(senti[old_p^ - received,{old _p/]) // Count them 
while {outstanding > 0) // Until all messages w/ phase old_pi are received 

GVTE, Global_Min{Get_MintO)// Get the current global virtual time estimate 


Figure 3-6 Asynchronous GVTE algorithm 

The main portion of the simulation engine increments sent{pi] and received, [p,] as it sends and receives 
messages with color pi, respectively. At the start of the main loop, we store the current simulation time, 
retain the current value of p, in old_Pi, and increment the color, p,. In the inner loop, the variable 
outstanding will be non-zero until all messages with color old_pi are received. Any rollbacks on LPi to a 
time less than the minimum value retained at the Reset_MinO call adjust that minimum to the new, lower 


LVTi value. 


Theorem 3-6: During applieation of the algorithm in Figure 3-6, GVTEi{r)<GVT{r) for all r during which 
the algorithm is in use. 


Proof; When all of the messages with color old_pi are eventually reeeived we have Get_MiniQ^VTi{r) for 
all i, leading to Gy7£',(r)=niin(Get_Afira,())<min(LVT((r)). 










We now need to show that the inin(Gcf_Afiii,())<inin(M7’{r)). Since all remaining messages in transit 
td) have color they have td>tgtGet_MiniQ. It follows, therefore, that 

iiiin(Gef_MjMj())<imn(Mr(r)). 

Thus min{Get_Mini{))<min(LVTi(r)uMT{r))=GVT(r), satisfying the hypothesis of Theorem 3-4 and 
ensuring that the algorithm is correct. ■ 

If message acknowledgement is used as part of the communications protocol, the above algorithm can be 
slightly modified to explicitly wait until all messages with color old_Pi have been acknowledged. This 
modification would be in lieu of the summing of the sent^ and received,[] arrays. 


39 













Chapter 4. SODL Run-Time System Architecture 
4.1. Overview 

Discrete event simulations require that processes change their state in accordance with some sequence of 
chronologically ordered events. In the SODL system, these changes are invoked because of receiving a 
message. Each message has a time stamp dictating when in the simulation run it is to be processed. The 
SODL run-time system is built to simulate a distributed simulation system to demonstrate that the language 
can be used in conjunction with optimistic synchronization mechanisms. 

The purpose of the SODL simulation run-time system is to ensure that messages are delivered in the proper 
order to the proper simulation process. It does this through a modified version of the Time Warp algorithm 
discussed in Chapter 3. Whereas the basic Time Warp algorithm aggregates a great deal of behavior into a 
logical process, the implementation of the SODL run-time system disaggregates portions of the algorithm 
to provide certain economies of scale, improved granularity control, and sequential mode testing. 


Engine Stand 


Engine 0 


Engine 1 



Engine N 


1 

process (0.0) 

process (0,1) 

1 

process (O.iio) 

1 

1 

process (1,0) 

process (1,1) 

1 

process (l,ni) 

1 

1 

process (N,0) 

process(N,l) 

1 

process (N,ni) 

II 


Figure 4-1 SODL system hierarchy 


SODL has two types of user-defined objects, called constructs. Message constructs allow process 
constructs to interact with each other. There are a number of other objects in the SODL run-time system 
provided with the SODL distribution package. Industrious end-users may change or completely rewrite 
this run-time system to meet their particular needs. The overall architecture of the SODL run time system 
can be thought of in hierarchical terms. There is an engine stand, which can be thought of as distributed 
simulation system for purposes of testing the SODL system. This stand contains one or more simulation 
engines, each of which can be thought of as a node in a distributed simulation system. These engines act 


41 
















independently of each other, in a manner similar to nodes in a distributed simulation. Each engine has a 
number of processes it controls. This hierarchy is depicted in Figure 4-1. 


4.2. Message constructs 

Messages provide the means for objects within a simulated environment to communicate with each other. 
SODL messages have a designated type, which may be derived from another message type (ala object 
oriented inheritance). Figure 4-2 depicts the structure of message constructs. 


Message Construct 


Message Type Specifier 


Destination List 


Time stamp 


Transmission flag 


Identifier 


Data Payload 


Methods 


Figure 4-2 SODL message construct 


4.2.1. Message Type Specifier 

Each message has an associated type. This type determines how processes receiving the message will react 
to it. Since SODL aspires to be an object oriented programming language to some degree, messages can 
inherit portions of their functionality from parent messages construets. Unlike some other languages, (most 
notably C-h-h) only single inheritance is allowed. This design consideration was made primarily to simplify 
implementation. Messages of type B, derived (either directly or indirectly) from some message type A, are 
said to be of type A and B. This abstraction allows additional flexibility in message delivery and 
processing. Routines provided in the run-time system can make use of these relationships. 


4.2.2. Message destination list 

The destination list is determined at run time and need not be the same for any two messages. Each 
destination has a unique identifier that acts as an associative address for delivering the message. Users can 


42 








establish a default recipient list at compile time for a given message. They can, alternatively override or 
augment the default recipients at run time. 

4.2.3. Message time stamp 

In order to ensure that messages are processed in the proper order, each message has a time stamp. 
Messages with earlier time stamps will be processed before those with later time stamps. In cases where 
two messages with the same time stamp value are encountered, the message identifier is used as a 
tiebreaker. This allows all messages generated to fall into a unique ordering, regardless of the order 
generated. 

4.2.4. Message transmission flag 

The SODL language requires that all possible outgoing messages be declared prior to compiling the source 
code files. In certain cases, it may not he desirable to actually transmit all of the messages that could be 
transmitted in response to an incoming message. The SODL run-time system provides programmers with a 
mechanism to preempt message transmission. They can set the message transmission flag to false to 
accomplish this end. The message transmission flag is set by default to true and must be either changed 
directly or by overloading the function called to examine the message delivery flag. See Chapter 6 for 
more details. 

4.2.5. Message identifier 

Each message has a unique identifier that allows it to be tracked down in the event of a revocation, and to 
provide a complete ordering in the event that two messages have the same time stamp value. This identifier 
has two components. The first is the index of the engine instance (see section 4.5 below) where the 
message was initially generated. The second is the actual instance count of the message generated on that 
engine. 

4.2.6. Message data payload 

The simulation developer specifies the data payload at compile time. This payload is analogous to the data 
members in a traditional object oriented programming language. 


43 







4.2.7. Message methods 

Methods are analogous to the methods found in object oriented programming languages. They are intended 
to act on the data members of the particular message instance to which they are associated. 


4.3. Process constructs 



Figure 4-3 SODL process construct 


From the programmer’s perspective, all of the functiontdity associated with a logical is encapsulated into a 
SODL process construct. Each process can send messages and change its internal state upon the receipt of 
a message. There are enhancements allowing certain tasks to be performed somewhat easier, namely the 
notion of a process mode. Figure 4-3 shows the basic structure of a SODL process. This particular 
example shows a process with two modes, but in can in general have any number of them. Each mode has 
a collection of transmit/receive nodes. Each of these nodes accepts one message of a stated type (which 
implicitly includes all derived types), changes the internal state data when it receives a message, and 


44 














produces output messages. There is no linguistic limit to the number of modes, the number of nodes in 
each mode, nor to the number of output messages a node can transmit®. 

It might seem a little odd that node (1, ni) receives both messages of type 1 and 2., while node (0, 1) can 
only handle messages of type 1. Messages are declared as types that can inherit data and methods from a 
parent message. If message type 1 is a parent message of type 2, and if node (I, nO is intended to receive 
messages of type 1, then technically, any message of type 2 is also a message of type 1, and node (1, ni) 
can process it. 

4.3.1. Process time stamp 

Each process instance has a time stamp associated with it. This time stamp is changed to the time of the 
message that is currently being processed. The process controller (see Section 4.4) uses the process time 
stamp to facilitate state saving and recovery. 

4.3.2. Process identifier 

Each process has associated with it a unique identifier. This identifier is a pair of numbers that correspond 
to the process’s owning SODL engine (See section 4.5) and an index for distinguishing between all of the 
processes the parent engine controls. 

4.3.3. Process state data 

State data is analogous to the member data found in an object oriented class definition. This state data has 
an associated time stamp. The process is said to have the state of its data elements at the time of its time 
stamp. Changes to the state data are considered instantaneous. This leads to the situation where a state 
may not be completely up to date if messages with the process’s current time stamp are still pending. 
Though the order of message delivery is the same from run to run of the simulation, for practical purposes 
developers should not rely on any particular processing order for messages with identical time stamps. 

State data should not contain references, since the C++ standard has trouble copying objects with them if 
the copy constructor is not explicitly defined. Pointers can be used, but doing so should be done carefully. 

® Any limitations stem fi'om the architecture and capabilities of the machine running the simulation. 


45 








Since the pointer value is copied in a copy constructor, and not the data that is being pointed to, great care 
must be taken to ensure that the state of the data being pointed to is consistent, and can be rolled back if it 
is dynamic in nature. SODL provides callbacks allowing developers to perform some processing in the 
event of a rollback and during fossil collection so that, among other things, data in a pointer may be 
corrected and processed if necessary. 

4.3.4. Process methods 

Methods can be used to perform calculations or modify internal process state data. The SODL engine hides 
references to other processes, so these methods cannot generally be called on other process instances, even 
instance of the same type. 

4.3.5. Process modes 

Modes can be thought of as a collection of transmit/receive nodes (described in section 4.3.6) and can be 
activated or deactivated independently of each other. Only nodes in active modes can receive messages. 
At startup, all of the modes are active. Each process receives a bootstrapping message that can be used to 
deactivate modes that are intended to be dormant at the start of the simulation. Alternatively, prior to 
actually sending a message to a process, an initialization method is called in each process instance that can 
also be used to deactivate desired modes. This is discussed in more detail in Chapter 6. 

4.3.6. Process nodes 

Each mode can have a collection of subordinate nodes. Each of these nodes handles exactly one type of 
input message, including any messages derived of that type. The node is directly responsible for 
processing the message, ensuring that proper updates to the process state are made, and that the data fields 
in any outgoing messages contain the proper values. Input messages are passed into the node by value, 
instead of by reference. Output messages are passed by reference. The reason for this is that input 
messages may be used by other processes, or by the very process currently handling the message. This also 
hides data fields from derived message types. Output messages are passed by reference so that they may 
retain any changes to them the node may make. 


46 







4.3.7. Process inheritance 

Like messages, process can singly inherit behavior from parent process constructs. There are some 
intricacies associated with this practice that make this somewhat more complicated than inheritance in the 
traditional object-oriented sense. Inheritance of the process methods and data members is like that in C-n-. 

Modes and nodes are a little different, though. Specifically, modes with the same name across an 
inheritance are actually the same mode instance. That is, if process A had a mode M, and a process B 
derived from process A also has a mode M, then these are the same mode in each instance. M can be 
activated or deactivated within the context of either A or B, affecting its activity state in both contexts. 

Nodes also need to have their behavior defined more clearly, since there is no analogous feature in other 
object-oriented programming languages. Mainly, any nodes in active modes can have process messages of 
their input type. Overloading a node in a derived process construct will not prevent the message from 
being delivered to the parent’s context. For instance, in the example above, assume that M has a node 
named N in both processes A and B. In this case node N in both A and B will process the message. The 
programmer does not explicitly pass the message to the parent class; the run-time system will do this 
implicitly. The reason for this is that, even though the name of the node may be tbe same, the output 
messages may differ, and for ease of implementation, this approach was implemented. 

4.3.8. Fossil collection in the process instance 

When the process controller performs fossil collection, it is safe to produce any output that may be pending 
for the state at its designated time stamp. SODL provides the ability for developers to overload the 
fossilCollect method; all output should be performed in this method. The remaining SODL run-time 
system ensures that fossil collection occurs in the proper process order, so that output appears in its proper 
sequence as well. 

4.4. Process Controllers 

A process controller manages many of the Time Warp specific functions associated with each process. 
Process controllers ensure that the state is saved prior to processing messages that will change the time 


47 







stamp of the process it controls, and when directed to do so, performs fossil collection and rollbacks. It 
also acts as a conduit for sending messages to and from its process. Figure 4-4 shows the general structure 
of a process controller and how it receives and transmits messages to and from the process. 

4.4.1. Identifier 

Each process has a unique identifier, which we discussed in section 4.3. The process controller has an 
identifier with a little additional information, including typing data that makes possible screening inbound 
messages from only specific types of messages. 



Figure 4-4 Process Controller message flow 


4.4.2. State queue 

The state queue holds the process states for specific points in time. The back element of the state queue is 
called the current state. Due to the Time Warp algorithm as implemented in the SODL system, the process 
states are in descending order of their time stamp value from the current state at the back and earlier states 
toward the front. States are saved onto the back of the queue, and they can be removed either from the 
back of the queue (in the case of a rollback) or from the front (during fossil collection). 

4.4.3. Process controller message receiver 

Upon receipt of a message delivery to the process controller, the time stamp of that message is compared 
with that of the back element. There are three cases, with which to contend: 






















I. Time stamp of incoming message < Time stamp of current state: It should not normally 
happen that a message is received with an earlier time stamp than the current state. 

II. Time stamp of incoming message > Time stamp of current state: Create a new current 
state by copying the old one onto the back of the state queue and changing its time stamp 
value to that of the incoming message. The new back element is now the new current state. 
The process controller also registers a fossil collection event with the controlling engine so 
that this new state can be reclaimed later. We now deal with the message as if it has the same 
time stamp as the current state, in case III below. 

III. Time stamp of incoming message = Time stamp of current state: The message is passed to 
the current state, which can make modifications to its internal data and generate outgoing 
messages. 

4.4.4. Process controller message transmitter 

Requests for message transmission originate in the process instance associated with the controller. Output 
messages are preprocessed and screened. The process controller will examine each output message and 
reject transmission of any that have their transmission flag set to false or any that have an empty destination 
list. Also, since we requires output messages from a node to have a greater time stamp value than their 
current time stamp, each outgoing message time stamp is set to a value slightly greater than the current time 
stamp if this condition is not satisfied. 

4.4.5. Rollback 

The SODL engine managing the process controller may periodically direct a rollback to a time t. All states 
saved in the state queue that have a time stamp not earlier than t are removed from the state queue. Since 
they have been placed into the queue in ascending order of their time stamps, this is simply a matter or 
removing the back element from the queue until the queue’s back element has a time stamp strictly less 
than t. There is no easy way to revoke the fossil collection scheduled for this rollback, so when we are 
notified that one must take place for any rolled back states, the request is ignored. 

4.4.6. Fossil collection 

Some intricacies associated with fossil collection bear mentioning. First, in order to perform a rollback to a 
time t, a state with time stamp prior to time t must remain. That is, we must always have a state remaining 
that is prior to the current GVT. Secondly, since we are performing operations such as output during the 
fossil collection phase, we need to ensure that we also perform this output in time stamp order. These 


49 








considerations led to an arrangement whereby fossil colleetion was conducted in two phases. Each fossil 
collection event the process controller performs has an associated time stamp, which is the time stamp of 
the process state that is obliged to perform some form of output. Any states with earlier time stamps have 
their memory reclaimed, but the one that performs the output is retained until the next cycle of fossil 
collection for that process controller. This is depicted in figure 4-5. 





collection 


fossil collection 


of fossil collection 


Figure 4-5 Fossil collection cycle in a SODL process controller 

This approach allows the fossil collection to be conducted in a manner consistent with SODL system 
requirements. By performing the output at the designated fossil collection time, we guarantee that the 
output is produced in the proper time stamp order. By retaining the state that had just produced the output 
until the next fossil collection round, we provide for the possibility of rolling back to that state. 


4.5. Engines 

Engines aggregate multiple processes and provide some improvements in memory management and 
granularity control over the way a simulation run. All messages addressed to a particular process are 
passed first to the engine controlling that process and placed in an event queue for scheduling. The engines 
also retain sodh\AntiMessage instances for all messages that have been produced in subordinate processes 
so that those messages can be rolled back in the event that becomes necessary. The engine structure is 
depicted in Figure 4-6. All processes in an engine are considered to have the same time stamp value, 
though in practice this need not occur. This time stamp value is that of the current message being 






















processed or, if no message is being processed, the time stamp value of the last processed message. Upon 
reeeipt of a message from another engine with a time stamp that is less than the current engine time stamp, 
a rollback to the new message time stamp is required of all subordinate proeess controllers. 


SODL Engine 



Fossil Collection Schedule 


fossil; 


fossil 


fossil^tr 


Pending Message Queue 


Process Controllers 

message,,, 




processi) 

message,,,^] 


-► 


process 1 




message,,,^,, 




processx 


§ 

h. 

s- 


c 

UJ 


Antimessage Queue 


Processed Messages 


Output Messages 


antiniessage„ 




message, 




antimessageq 



antimessage„^i 




message ,„.2 




antimessage,j+t 








antimessage a*!, 



_ 

message, „.p 




antimessagegtr 

I 


Figure 4-6 SODL engine structure 

Each engine has an associated node number. This node number is unique among all of the other engines 
that may be in a SODL system. This number corresponds to the first part of the identifier for processes 
eontrolled on the engine, and of messages originating on the engine. 


51 






































4.5.1. Local clock 

The clock provides a convenient central way of checking the current simulation engine time stamp, and 
determining the time stamp of outgoing messages, should the user-defined portion fail to provide an 
adequate value. Earlier implementations of the clock had a real-time mode that allowed messages 
processing to occur at some rate proportional to the real world flow of time. This was found to be an 
unnecessary feature, though the general capability remains if developers wish to restore this capability. 

4.5.2. Pending message queue (event queue) 

The pending message queue prioritizes pending messages so that the next message in the queue has the 
lowest time stamp value of any others in the queue. For that reason, this is implemented as a standard 
library priority_queue (Josuttis 1999). 

4.5.3. Antimessage queue 

The antimessage queue stores antimessages are associated with messages in the pending message queue. 
The order restrictions on the two queues are identical, so if a message has been revoked, it can be checked 
with the top element of the antimessage queue when it is considered for delivery to its destinations. If the 
antimessage aimihilates the message, both are removed from their respective queues and destroyed. 

4.5.4. Processed message queue 

Each message is inserted into the back of the processed message queue after the engine has processed it. In 
the event of a rollback, elements from the back of the processed message queue can be removed and 
reinserted into the pending message queue as necessary. During fossil collection, messages can be 
reclaimed from the front of the message queue. The messages in the processed message queue are ordered 
by their time stamp value. 

4.5.5. Output message queue 

During message transmission, a copy of the outgoing message’s associated antimessage is retained in the 
event that a rollback is necessary. These messages, unlike those in the processed message queue, are not 
ordered by their time stamp values, but by the time stamp of the process state that created them (i.e. their 


52 







creation time). The reason for this distinction is that during a rollback, any messages that need to be 
revoked are done so because the process that created them became invalid. We are not interested in the 
delivery time, but in revoking them because since they never should have been created in the first place. 
Thus, they are inserted into the output message queue in the order of their creation. During rollback, the 
antimessages are removed from the back of the queue and transmitted. During fossil collection, they are 
removed from the front and their memory reclaimed. 

4.5.6. Process controller array 

Each engine has a collection of processes it owns. Each of these processes has its process controller. The 
engine does not actually have any direct manipulation of the processes themselves, but can interact with the 
process controllers. Pointers to these controllers are stored in a standard library vector. Each process can 
be uniquely addressed by a pair of numbers, the index of its owning engine, and the index in the vector that 
has the pointer to the process controller. This is in fact the basis for the identifier in the process controllers 
and their processes. 

4.5.7. Fossil collection schedule 

The engine needs to keep track of any new states that have been created by the owned process controllers 
so that when fossil collection occurs, those states can be reclaimed in a chronologically correct sequence, 
ensuring proper formatting of the output. A fossil collection schedule is implemented as a priority_queue 
that has the earliest scheduled fossil collection event at the top. 

4.5.8. Engine message receiver 

Messages destined for a process controlled on some engine must first be passed to the engine’s receiver. If 
the incoming message has a time stamp less than the current clock time stamp tc, then the engine initiates 
a rollback to the message time stamp. If the incoming message is an antimessage, it is inserted into the 
antimessage queue; otherwise it is placed in the pending message queue. 


53 






4.5.9. Engine message transmitter 

Any of the process controllers owned by an engine can request a message to be transmitted. The engine 
does not consider the destination of the message at this point, but blindly forwards the message to the 
engine stand (see section 4.6 below) for the local node in the distributed simulation for delivery to the 
proper engines. An antimessage for the outgoing message is retained in the event a rollback requires its 
revocation. 

4.5.10. Engine advancement 

Periodically, the engine stand will instruct each engine it controls to process a message. When this occurs, 
the engine removes from the top of the pending message queue the first message that does not have an 
antimessage waiting for it in the antimessage queue. All message-antimessage pairs are removed and 
eliminated. The message is then sent to the controllers for all of the destination processes the engine owns. 
Once that is completed, the message is then inserted in the back of the processed message queue in case it 
needs to be reinserted into the event queue due to a rollback. 

4.5.11. Rollback 

An engine rollback is performed any time it receives an incoming message with a time stamp tr less than 
the time of the local clock. When this occurs, each process controller the engine owns performs its 
rollback, as described in section 4.4.5. The engine must also rollback portions of its data structure as well. 
This is accomplished by reinserting into the pending message queue all of the messages in the processed 
message queue with time stamps less than or equal to Any antimessages in the output message queue 
with time stamps less than or equal to tr are transmitted so that their associated messages can be revoked as 
well. 

The fossil collection schedule remains unchanged. It is difficult and time consuming to weed out fossil 
collection events made irrelevant because of the rollback. When they are processed, the process controller 
can easily recognize that they have been the result of a rollback, and they are ignored. 


54 







4.5.12. Fossil collection 

Fossil collection is broken into two phases, known as incremental fossil collection and gross fossil 
collection. 

4.5.12.1. Incremental fossil collection 

Incremental fossil collection is geared primarily towards the process controllers. The engine stand during 
the overall fossil collection process will query the engine as to the time of the event in the fossil collection 
schedule. When certain conditions are satisfied (see Section 4.6) the engine will be allowed to perform an 
incremental fossil collection, allowing the process state with the lowest time stamp value remaining to be 
fossil collected (see section 4.4). This will ensure that from the engine’s perspective all of the output 
governed by the engine is generated in the proper order. 

4.5.12.2. Gross fossil collection 

Gross fossil collection takes place after incremental fossil collection at the direction of the engine stand, 
and is for reclaiming all of the engine’s data with a time stamp value less than some tf. Any messages in 
the processed message queue or the output message queue with time stamps earlier than tf are removed 
from the backs of their respective queues, and their resources are reclaimed. 

4.6. Engine Stand 



Figure 4-7 Engine stand structure 


55 






















Each node in the distributed simulation has a unique engine stand, depicted in Figure 4-7, which acts as the 
primary controller for all of the engines managed on that node. It was introduced primarily as a means to 
implement the Time Warp algorithm without introducing problems associated with actually distributing the 
system. It proved useful in this regard in tracking down errors within the implementation of Time Warp in 
the SODL system. 

It is retained because its value does extend beyond simply debugging purposes in SODL system 
development. Specifically, it provides a mechanism for testing and optimizing possible distributions of 
processes across engines, while keeping at bay network errors that might occur specifically in a fully 
distributed implementation. 

4.6.1. Idle listener interface 

The idle listener interface provides a mechanism for the view manager (see section 4.8) to control the 
engine stand. This control comes in two forms. The first is a request to perform any initialization required 
to get the simulation correctly configured for startup. This can include establishing initial bootstrapping 
messages, and process state initialization. The second form of control allows the engine stand to progress 
in the simulation. This is actually implemented by allowing each of the engines under control of the engine 
stand to advance. The view manager is notified if no pending messages remain so that it can end the 
simulation run, if that is its behavior. 

4.6.2. Engine List 

The engine list contains a reference to all of the engines in the simulation. Only certain engines are 
actually controlled by the engine stand that owns it in a distributed simulation. This provides an easy 
method of making certain that process instantiation is done consistently across the distributed simulation. 

4.6.3. Message forwarder 

The message forwarder sends and receives messages between engine instances. All messages are 
forwarded from the simulation engine to the sodl::EngineStand::stand instance. From there, the 


56 







distribution list is queried, and copies of the message are sent to each of the sodlr.Engine instances with 
processes listed as recipients. 

4.6.4. Local virtual time (LVT) calculator 

The LVT calculator keeps track of messages that have been processed and acknowledgements of messages 
transmitted to other engine stands. It then keeps track of the local virtual time for them engine stand, as 
defined in Chapter 3. 

4.6.5. Global virtual time (GVT) estimator 

The GVT estimator receives periodic requests for input into a global virtual time calculation. The GVT 
calculator gets the current LVT from the LVT calculator, and passes that on to the message forwarder to be 
provided for the GVT computation. At the end of the GVT computation, each engine stand receives the 
newly estimated GVT and passes it from the message forwarder to the GVT estimator. The GVT estimator 
updates the local estimate of the GVT, and conducts fossil collection. 

4.6.6. Fossil collection 

As in the engines, engine stand fossil collection is conducted in two phases. Upon an update of the local 
estimate of the GVT, incremental fossil collection is performed, followed by gross fossil collection. 

4.6.6.1. Incremental fossil collection 

The incremental fossil collection involves polling all of the locally controlled engines for their next 
scheduled fossil collection event. These are sorted and processed in time stamp order up to the GVT. 
Upon completion of an incremental fossil collection event on engine e, the engine stand again polls e for its 
next fossil collection event. In this way, all of the fossil collection events prior to the GVT are performed 
in proper time stamp order on all the engines the stand controls. 

4.6.6.2. Gross fossil collection 

After completion of the incremental fossil collection up to the new local estimate of the GVT, each engine 
the stand controls is given the opportunity to perform gross fossil collection to reclaim memory occupied 
by obsolete data directly under the control of the engine instance. 


57 







4 . 7 . Message Passing Interface (MPI) 

The SODL run-time system was intended to work with the Message Passing Interface (MPI), a standard 
library linkable with C, C-h-i-, and Fortran programs (Gropp 1998, 1999a, 199b). It is primarily used in 
general distributed programming, not specifically distributed simulation. However, certain features make it 
a useful tool in distributed simulation: 


• It has a standard to which all implementations must adhere. It has also been widely used for other 
purposes, meaning that most implementations are reasonably mature and stable. 

• It remotely starts up all of the nodes in the distributed simulation with the need for the simulation 
operator to do this manually. 

• It has been ported to multiple platforms. In particular, it can operate heterogeneously with a 
variety of Unix platforms. It has also been ported to Microsoft Windows NT, but it does not 
interoperate with MPI on Unix platforms. 

• Since the library has been ported to multiple platforms SODL run-time systems using MPI can 
easily be ported to those platforms with little or no code changes. 

This aspect of the simulation system was not implemented prior to this writing, though it is hoped that 

derived work will establish a fully distributed implementation of the SODL run-time system using MPI for 

network communication. 


4.8. View manager 

The View Manager is a configurable subsystem that is designed to facilitate graphical output from a 
standard API. It controls exactly one sodlrJdleListener instance (of which sodhiEngineStand is a 
subtype). View managers have a start method that, when called starts the simulation running. This 
includes initializing the idle listener (which in turn initializes all of the simulation components) and 
incrementally stepping the simulation (by processing some non-zero number of pending events). SODL 
comes with two view managers, though developers can easily write new ones to work with API’s not 
currently supported. 

4.8.1. Text view manager 

The Text view manager provides no graphics support. It is intended for producing output only to stdout or 
log files. Upon starting the view manager, it immediately calls the initialization routine in the idle listener. 


58 







It then calls the idle method in the idle listener until no messages remain in the simulation system to be 
processed. When that happens, the text view manager returns control to main. 


4.8.2. GLUT view manager 

The GLUT view manager provides a basic interface with the GL User Toolkit (GLUT) API. During 
initialization, any simulation objects that own a gvmv.View instance registers it with the controlling GLUT 
view manager. Any user input events are then forwarded to the appropriate view. Multiple views can be 
added to the GLUT view manager, and it will ensure that the user inputs are sent to the proper display 
controller. GLUT provides a mechanism whereby during idle times in the graphics subsystem, a callback 
can be made to a static method. This mechanism is used to allow the simulation to process some pending 
events. 

The GLUT view manager requires extensive additional support in the form of SODL processes and 
messages in order for developers to make use of it. This interface is described in more detail in Chapter 9. 
To summarize, there is a SODL process associated with each graphics object under the management of a 
GLUT view manager. These views may be distributed across a network, or they may be consolidated on 
one host machine. Inside each view is a scene graph that corresponds to the hierarchy of graphics 
processes in the distributed simulation. Messages can be sent to these SODL processes causing some state 
change in the receiving process. These changes are then forwarded to all of the views that have elements in 
their scene graph associated with the process. Upon receipt of these messages, the view generates a 
message and places it into a queue for processing during the fossil collection phase. It is only at the fossil 
collection phase that these changes to the scene graph are acmally committed. 


59 







60 






Chapter 5. SODL Parser Usage 

5.1. Overview 

The SODL parser is a software tool that translates SODL construct files into a collection of C++ source 
code files. It also creates a makefile for compiling the generated C++ source code. This makefile is 
intended for use with GNU make 3.79. Generated C++ source code files can be compiled using the GNU 
C++ Compiler (GCC version 2.95.2). 

5.1.1. Cautionary notes 

Programmers developing under Win32 operating systems will need to obtain a copy of the GNU make 
utility (version 3.79 or later). They are strongly advised to obtain the latest version of Cygwin (available at 
http://sources.redhat.com/cygwin/) and use that as a development environment. I personally recommend 
Emacs for Win32 (which is available at http://www.gnu.org/software/emacs/windows/). 

In the event that a distributed simulation system is eventually produced with MPI, SODL will be restricted 
to running in a distributed mode under Unix systems until a robust version of MPI is produced to work in 
the Cygwin environment; as of this writing, there is no such implementation. 

5.2. Installation 

The parser is distributed as a tarred and bzipped source code with a variety of makefiles that can be used to 
build the parser for a variety of platforms. The makefile may be edited to direct the executable build to a 
specific location if the default settings are not satisfactory. Currently the following platforms are supported 
(though if I’ve been a good programmer, others should be equally well supported without major changes to 
the parser source code): 


Platform (Compiler) 

Makefile name 

Build command line 

Cygwin (GCC) 

src/Makefile.Cygwin 

make cyg 

Linux (GCC) 

src/Makefile.linux 

make Inx 

Solaris (GCC) 

src/Makefile.solaris 

make sol 


Table 5-1 Methods for making the SODL parser 


Installation and compilation instructions are in Figure 5-1. 


61 
















1) Download the latest version of the software and copy it to a desired location. 

2) tar -XVj f sodl-x.x.xxx.tar.bz2 where x.x.xxx is the version number of the 
SODL distribution. If your version of tar does not support this form of decompression, use 

bunzip2 sodl-x.x.xxx.tar.bz2 
tar -xvf sodl-x.x.xxx.tar 

3) cd sodl-x.x.xxx 

4) make platform-abbreviation 

5) make clean 

Figure S-1 SODL Parser (sp) installation instructions 

5.3. Directory Structure 

There are some makefdes available to perform various tasks for managing the contents of the directory 
structure. 



Function 

Make build 

Builds SODL parser and sample programs; the default platform is Cygwin. 

Make clean 

Removes garbage files created during the build process. 

Make fullclean 

Removes garbage files and executables created during the build process. 


Table 5-2 Makeflle commands 


Upon extraction, the there will be a number of directories along with the make files to build the executable. 


5.3.1. ./bin 

The executable is placed in this location after it is built. Add this to your path or move sp to a place in your 
path. 


5.3.2. ./config 

Contains configuration files for various platform and option combinations. The platforms are those listed 
above, and the options involve the view manager and whether or not the simulation is to run in a distributed 
mode. 


62 


















5.3.3. ./doc 


Some HTML-formatted documentation is available here. It is largely portions of this dissertation converted 
to HTML for portability. 


5.3.4. ./object 

Object files generated during the build process are places here. They can conveniently be removed by 
using the “make clean” after building is complete. 


5.3.5. ./sample 


Directory 

Contents 


Location of the binary executable after the build is complete 

./sample/xxx/build 

Location of the C++ files generated by the SODL parser 


Location of the object files generated by the compiler during the build process 

./sample/xxx/plan 

Location of the SODL source files which are used to generate the simulation 


Table 5-3 Sample simulation system directory structure 


Make command line 

What it builds 

make 

all samples 

make glut 

battle, bounce 1, bounce2, brigade 1, hierarchy 

make text 

brigade2, ping, ringl, ring2, simple 1, simple2, simple3 

make dist 

relayl, relay2, relay3, relay4, relayS, relay6 

make battle 

battle 

make bounce 1 

bounce1 

make bounce2 

bounce2 


brigade 1 


brigade2 

make ping 

ping 

make relay 1 

relayl 


relay2 

make relayS 

relay3 

make relay4 

relay4 

make relayS 

relays 

make relay6 

relayd 

make ringl 

ringl 

make ring2 

ring2 

make simple 1 

simple 1 

make simple2 

simple2 

make simpleS 

simple3 


Table 5-4 Make command line arguments for building demonstrations 


The sample directory contains a number of SODL sample programs and a collection configuration files for 
various platform/option combinations. It includes the makefiles needed to manage the subdirectory 
contents. Each demonstration directory has a number of subdirectories that are used to build the samples. 


63 


















































These direetories are described in table 5-3. Table 5-4 describes the shows the command line make 
arguments for build some or all of the demonstrations. 

The next few sections provide brief descriptions of each of the demonstrations. They are more fully 
described in Chapter 9. 

5.3.5.1. ./sample/battle 

The battle demonstration is an autonomous tank battle simulator. Two opposing forces each with 25 tanks 
and 1 command post start out in some initial configuration and attempt to destroy the opposing force’s 
command post. 

5.3.5.2. ./sample/houncel 

The bouncel demo simulates a collisionless system of particles in a closed container. It uses the GLUT 
view manager to display the simulation state. 

5.3.5.3. ./sample/bounce2 

This appears essentially the same thing as bouncel above, but it is done with fewer messages 

5.3.5.4. ./sample/brigadeI 

This is another GLUT view manager demonstrator that shows the progress of a military brigade performing 
some task. It also performs a great deal of output to stdout indicating which components are doing what. 

5.3.5.5. ./sample/brigade2 

This simulates the same thing as the brigade 1 demonstration above, but without the GLUT view manager. 
It produces only textual output. 

5.3.5.6. ./sample/hierarchy 

Hierarchy is a single process GLUT view manager demonstration. Its notion is somewhat similar to the 
brigade demonstrations, but it does things in an apparently more orderly manner. 


64 







5 . 3 . 5 . 7 . Jsample/ping 

Ping simulates a token being bounced between two processes. It produces only textual output. 

5 . 3 . 5 . 5 . ./sample/relay 1 

Relay sets up a multi-engine simulation with two processes. One transmits a token to the other, which is 
then repeatedly bounced between them until the user stops the simulation. It was intended to act as a 
simple test of multiple engines without the possibility of a rollback ever occurring. 

5.3.5.9. ./sample/relay2 

This simulation is intended as a stress test of the rollback mechanism in the SODL run time system. For 
every message delivered, two are generated, so this simulation will eventually run out of memory and cause 
an abnormal termination. Each process resides on different engines and, upon receipt of a message 
transmits two messages, one to itself, the other to the partner process. Each of these messages has a 
random time stamp, which may cause a rollback to occur on the other. 

5.3.5.10. ./sample/relay3 

This simulation system is also intended to stress test the rollback mechanism and memory management of 
the SODL system. One controller process owns 1000 subordinate processes distributed 10 each on 100 
engines. At startup, the controlling process sends a message to all of the subordinate processes. Upon 
receipt of such a message, each of these subordinates sends a message to a random subordinate at a random 
time. 

5.3.5.11. ./sample/relay4 

This simulation consists of a controller process, two subscription processes, and four child processes. The 
child processes each reside on their own engine. Each child subscribes to one of the two subscriptions 
processes. Messages sent to a subscription process are forwarded to all of its subscribers. The simulation 
starts when the controller sends a message to each of the subscriptions. That message is then forwarded to 
all of the children processes. Upon receipt of a message from the subscription, each child process sends a 
message to a random subscription, a message to unsubscribe from a random subscription, and a message to 


65 







subscribe to a random subscription. Each of these messages has a random time stamp. Like relay2 above, 
this normally will create more messages each cycle than are consumed, and it therefore will eventually 
terminate abnormally due to lack of memory. 


5.3.5.12. ./sample/relay5 

Relay5 has three processes, a source, a relay, and a sink. Each process resides on a different engine. 
Messages periodically originate in the source and are sent to the relay. The relay forwards a message to the 
sink, which sends no messages. 

5.3.5.13. ./sample/relayS 

Relayb is another test of the rollback mechanism. There are two processes on different engines, one 
process makes fast progress, and the other makes slower progress but at specific points in time, sends a 
message to the faster. This causes rollbacks to occur at predictable points in simulation time. 

5.3.5.14. ./sample/ring 1 

This demonstration has a controller and ten ring elements arranged in a ring topology. Upon receipt of a 
message, each ring member transmits a message to the next member in the ring. The simulation is started 
when the controller sends a message to the first element in the ring. The ring topology is actually glued 
together with a subscription similar to that described in relayd. 

5.3.5.15. ./sample/ringl 

This behaves much the same way as ringl above, except that when started, the controller broadcasts a 
message to all of the child processes. This results in each process processing messages in parallel (Irom a 
virtual time perspeetive) rather than sequentially as in ringl. 

5.3.5.16. ./sample/simplel 

This is a simple test of a single process that sends a message to itself upon receipt of one. It does this 100 
times before stopping. 


66 







5.3.5.17. ./sample/simple2 

This behaves like simple 1, except that it does not stop. It was used to check for memory leaks in the main 
simulation engine. 


5.3.5.18. ./sample/simple3 

SimpleS behaves like the ping demonstration mentioned earlier, except that the messages between the two 
processes stop only when the user terminates the program. 

5.3.6. ./src 

This directory contains the source code required to build the SODL parser. 


5.3.7. ./template 

This directory contains the source code for the SODL run-time system. This includes a number of SODL 
construct files for the GLUT view manager, and, in the ./template/gvm subdirectory, the actual graphics 
engine that the GLUT view manager uses for graphics display. 


5.4. Command Line Options 

Once the parser is installed, you can run it by entering ’sp’ followed by a collection of flag values and the 
root process name. 

sp [-abaseDir] [-bbinSubdir] [-ccfgDir\ [-idisplciy\ {-\bldSubdir\ \-\platform\ [ -mmode^ 

[-oobjSubdir\ [-TpplnSubdir^ [-XtmpltDir] [-v] RootProcess 



Meaning [Default Value] 

-a 

Specify base directory for the others below [./] 

-b 

Specify the binary subdirectory, where the executable will be generated [bin/] 

-c 

Specify configuration file subdirectory [$(SODLHOME)/config/] 

-d 

Specify either 'Text' or 'GLUT' display type [Text] 

-i 

Specify intermediate build subdirectory [build/] 

-1 

Specify platform name [platform used in building the parser] 

-m 

Specify simulation mode, either 'single' or 'dist' [single] 

-0 

Specify object file subdirectory [object/] 

-p 

Specify plan file subdirectory [plan/] 

-t 

Specify template location [($SODLHOME)/template/] 

-V 

Timis verbose mode ON [OFF] 


Table 5-5 Command line options for sp 


67 




















All subdirectories except tmpUDir and cfgDir are relative to baseDir. RootProcess is expected to be a 
process construct file in the planSubdir directory (relative to the baseDir value). 

For user projects, the default directory structure is illustrated in Figure 5-2. 



Figure 5-2 User project default directory structure 


5.5. Configuration Files 

The SODL parser uses a configuration file to specify various parameters for building the final product. 
These configuration files will differ from each other based upon the platform and compiler in use. The 
location of the configuration file defaults to $(SODLHOME)/config. Users can specify their own 
configuration file location using the -c option in the parser’s command line invocation. The actual file 
name that the parser will look for in that directory is mode.display.platform where the -m option specifies 
the mode value, -d specifies the display component, and -1 specifies the platform. Some examples are 
shown in Table 5-6. 


Command Line 

Configuration file used 

sp -Icygwin... 

$(SODLHOME)/config/single.text.cygwin 

sp -ctemp -llinux -dglut... 

./temp/single.glut.lmux 

sp -mdist -Icygwin -dtext... 

$(SODLHOME)/foo/smgle.text.cygwin 


Table 5-6 Configuration file specification in the sp command line 


This mechanism enables end user to specify their own graphics library and user interface, should they 
(perhaps wisely) opt out of the rather limited one provided with the SODL system. It also provides the 
means by which an end user can write their own simulation engine with which the code sp produces would 
interface. 

The configuration files have key/value pairings that the parser and code generator can use to produce 
proper Makefiles. Here is a list of the keys and a description of how their values are used. These pairings 
take on the form key-name = "key-value”. 


68 



















Table 5-7 describes the key/value pair settings available to users to specify how the eventual product is 
built. Figure 5-3 has examples of some configuration files distributed with the SODL system. 


When sp is run, it produces a number of files. There are several C-I-+ source code files written to the 
directory specified -o option in the sp command line. There is also a Makefile written to baseDir used to 
actually build an executable simulation. A typical build will have one call to sp to produce the C-I-+ files 
and the Makefile, followed by a ‘make’ to produce the final executable. 



Value Meaning 

BIN 

Location of the executable (relative to baseDir) produced as a result of complete SODL 
build process.(same as the -b option in the sp command line) 

BUILD 

Directory (relative to baseDir) to write the C-t-+ files the SODL parser produces (same as 
-i in sp command line). 

CC 

Compiler command line invocation to use to compile C-i-H files. 

CCFLAGS 

Command line flags for the compiler. It is required to have as its last argument the 
option for naming the output file. 

DISPLAY 

Display type (same as -d option in SODL parser conunand line) 

EXEEXT 

Specifies the file extension for the executable image (required for Win32 to be .exe) 

LD 

Linker command line invocation to use to link the object files. 

LDFLAGS 

Linker command line options and flags. It is required to have as its last option the flag 
for naming the output file. 

MODE 

Simulation mode (same as the -m option in the SODL parser command line) 

OBJECT 

Directory in which the compiler places the object files after compilation (same as -o in 
SODL parser command line). 

OBJEXT 

File extension for the object files. It is normally “.o” 

PLAN 

Directory (relative to baseDir) where the root process declaration is located (same as -p 
in SODL parser command line). 

REMOVE 

Command for removing files {rm for Unix shells, del for MS-DOS like command shells) 

TEMPLATE 

Directory (relative to baseDir) where the SODL simulation engine and support source 
files are located (same as -t in SODL parser conunand line). 


Table 5-7 Configuration file key/value descriptions 


69 





























OBJEXT = ".o" 

EXEEXT ="" 

CC = "g-H-" 

LD = "g++" 

CCFLAGS = " -DLINUX -ftemplate-depth-64 -c -I/usr/include -02 -o " 

LDFLAGS = "-L/usr/Xl 1R6/Iib -Iglut -IMesaGLU -IMesaGL -IXext -1X11 -Im -IXi -IXmu -o " 
REMOVE = "rm -f" 


a - $(SODLHOME)/config/dist.GLUT.linux 


OBJEXT = ".o" 

EXEEXT = "" 

CC = "g-H-" 

LD = "g-i-H" 

CCFLAGS = "-DLINUX -ftemplate-depth-64 -02 -c -o 
LDFLAGS = "-0 " 

REMOVE ="nn -f" 


b - S(SODLHOME)/config/dist.Text.linux 


OBJEXT = ".o" 

EXEEXT ="" 

CC = "g-H-i-" 

LD = "g-H-" 

CCFLAGS = "-DLINUX -ftemplate-depth-64 -I/usr/include -02 -c -o " 

LDFLAGS = "-L/usr/Xl 1R6/Iib -Iglut -IMesaGLU -IMesaGL -IXext -1X11 -Im -IXi -IXmu -o " 
REMOVE = "rm -f" 


c - S(SODLHOME)/config/singIe.GLUT.linux 


OBJEXT = ".0" 

EXEEXT ="" 

CC = "g-i-H" 

LD = "g-H-" 

CCFLAGS = "-DLINUX -ftemplate-depth-64 -02 -c -o " 
LDFLAGS = "-0 " 

REMOVE = "rm -f" 


d - $(SODLHOME)/config/single.Text.linux 
Figure 5-3 SODL configuration files for Linux platform 


70 














Chapter 6. SODL Language Structure 
6.1. Overview 

The Simulation Object Description Language (SODL) is designed to provide an enhanced event driven 
response representation for controlling entity activity in distributed discrete event simulations. It is a 
purely event driven language, and has some features of more traditional object oriented languages such as 
inheritance. SODL object descriptions are represented as a collection of stimulus/response handlers. That 
is, upon receipt of some stimulus, a simulation object will produce a possibly empty collection of internal 
and external responses to that stimulus. Here, stimuli are incoming messages, internal responses are state 
changes, and external responses are outgoing messages, as depicted in figure 6-1. 



Figure 6-1 Depiction of the stimulus/response notion of a SODL process 

This is not the case in SODL. The only mechanism provided for simulation object interaction is message 
passing between different instances. Methods are provided for individual instances to manipulate their own 
internal state. 

6.2. Approach 

SODL is a completely event driven language. It is heavily based upon C+-i-, and relies upon many of the 
constructs of that language. Source code in SODL is passed through a parser and generates a collection of 
C+-)- files. These files are then compiled using a standard C-t-i- compiler. Though the overall structure of 
the programming language is different from C-H-, the internal code executed when handling events is 
entirely C-i-i-. The build process is depicted in Figure 6-2. 


71 














Figure 6-2 SODL project build steps 

This approach is similar to others that have been employed for distributed simulation systems. In 
particular, both YADDES (Priess 1990) and APOSTLE (Wonnacott 1996) use this approach of translating 
a simulation specification from their respective languages, translating these user files into C and C++ files 
respectively, and then using standard compiling and linking tools to create an executable. It has the benefit 
of allowing systems to be ported to other platforms without having to write platform dependent binary 
code. 

6.3. Constructs 

A construct is the basic building block of SODL. In many aspects, a construct is roughly analogous to a 
C++ or Java class. Figure 6-3 shows the basic form of a construct. 


{ construct-type'.construct-name [ (parent-construct) ] 

5 I 

{ construct-definition } 

} 

Figure 6-3 Basic construct form 

Constructs are defined in files with a specific extension. Valid construct types and their associated file 
types are listed in Table 6-1. 



File 

Extension 

Description 

message 

.msg 

Defines a message that can be passed between object instances. It can 
contain data and method descriptions. Messages have an associated 
delivery time. 

process 

•proc 

Defines behavior of a simulation object instance. It can receive and send 
messages, it has state variables, as well as method descriptions. It also 
contains stimulus/response definitions for handling messages. 


Table 6-1 SODL Basic construct types 


Optionally, constructs can inherit some functionality from a parent construct. Inheritance takes its form by 
enclosing the parent construct type into a set of parentheses following the declaration. Some constructs can 


72 





















optionally contain no functionality of its own, or extensive definitions of internal data, methods, and 
additional construct dependent definitions. 

6.3.1. Message constructs 

SODL files with .msg extensions contain message eonstructs. These message constructs are the means by 
which various processes within the simulation communicate with each other. They can have internal data, 
called a payload, and methods that act upon that payload. Sp creates two C++ files for each message it 
processes. The first is a header file, and the other is a source code file. Details on these files are described 
in Chapter 7. Entries for compiling them are plaeed into a Makefile for building the final executable. They 
are explored in depth in section 6.7. Sample message constructs are depicted in Figure 6-4. 

{message:generic;} 

a - generic.msg, a simple message with no data or methods. 

{message: child_message(parent_message);} 

b - child_message.msg, a simple message with no data or methods, with inheritance 
Figure 6-4 Sample message constructs 

6.3.2. Process constructs 

{process:simple;} 

a - simple.proc, a simple process with no data, methods or modes. 
{process:child_process(parent_process);} 

b - child process.msg, a simple process with no data, methods or modes, inherited from parent. 

Figure 6-5 Sample process constructs 

SODL files with .proc extensions contain process constructs. A process construct has internal data, called a 
state, and methods that act upon that state data. Unlike message constructs, process construets also have 
modes, which in turn have nodes. Each mode can be activated and deaetivated independently. Each node 
can receive a message of fixed type, which changes the internal state of the process, and transmit messages. 
Nodes can only receive messages when their parent mode is active. 


73 


















From one process construct, sp creates a pair of files to define in C++ the functionality of the process 
within the simulation engine. Details on these files are described in Chapter 7. Sample process constructs 
are depicted in Figure 6-5. 

6.4. Import declarations 

In order to make use of more than one construct, the programmer must reference them from within the 
body of all referencing files. This is done through an import directive. Import directives take on the form 
depicted in Figure 6-6. 

{import [ construct-type ] 

{ 

construct-namel [,construct-name2 [,... ] ] 

} 

} 

Figure 6-6 Import directive specification 

Import directives should be located at the top of a source code file, prior to the file’s construct definition. 
There are two varieties of imports, the first imports SODL constructs for use within the importing 
construct, the other imports non-SODL declarations, such as C++ header files. 

6.4.1. Importing SODL constructs 

{import message {root message} } 

{message: start_sim(root_message);} 

a - start sim.msg; Imports messageiroot message it to be used in the messages body. 

{import message {start sim, SetView} } 

{import process {View3D} } 

{process:root_process(View3D);} 

b - root_process.proc; Imports messageistart sim and messager^etFieiv, as well as 
process: Ficm’JD. process:r 0 ot jtrocess inherits functionality from process: FiewJD. 

Figure 6-7 Sample import directives for message and process constructs 

For importing SODL constructs, the construct-type in the import directive must match the construct-type in 
each of the files associated with the construct name. That is, when importing messages, the keyword 


74 














message is used as the construct-type, and similarly for importing process constructs. This is necessary 
because different construct types are used differently in SODL, and the distinction in important. Figure 6-7 
illustrates how some simple constructs import other constructs. 

Message and process imports must be in either the planDir or the tmpltDir, as defined by the sp command 
line parameters. 

It’s not normally useful for message constructs to import process constructs. However, the language does 
provide for this possibility, even though any attempt to declare a process within a message produces an 
error when sp is run. 

6.4.2. Importing non-SODL files 

{import {<stdlib.h>, <stdio.h>} } 

{message:start_sim(root_message);} 

a - start_sim.msg; Imports stdlib.h and stdio.h for use in the start_sim message. 

{import std {<vector>} } 

{process:root_process;} 

b - root_process.proc; imports the declaration for the stdr.vector class. 

{import gvm {Node} } 

{process:View3D;} 

c - View3D.proc; imports the declaration for the gvm::Node class. 

Figure 6-8 Sample non-SODL construct imports 

Import directives also allow importing C-t-i- header files with a way to allow compilation with external C-I-+ 
source code. Figure 6-8 illustrates some examples. 

Here Figure 6-8a imports the stdlib.h and stdio.h header files into the header file produced for the 
inessage:start_sim construct. This form (omitting the construct-type) should be used for any includes 
which are in the C+-(- global namespace. 



















Figure 6-8b imports the header file for the std::vector<T> class. In this case, we use this form for 
including headers for declarations in the std namespace. 

Finally, figure 6-8c imports the header file gvm/Node.h, which must reside either in planSubdir/g\m or in 
tmpUDirlgwm. It will also compile into the final executable the file gvm/Node.cxx. Here any declarations 
should be in the gvm namespace. 

6.5. Member Variable Declarations 

Message and process constructs both allow declaration of member variables. Member variables are 
declared inside the construct declaration, and take on the illustrated in figure 6-9. 

[namespace::\data-type:variable-name [ [] | [size\ ] [: initial-value ]; 

Figure 6-9 SODL construct member variable declaration 

From this we can declare variables of variety of types, including arrays, each of which can be initialized 
with a certain value. 


6.5.1. Basic data types 



Description 

bool 

Boolean value (takes one of the values {true, false}) 

byte 

8-bit unsigned character value. Defined by typedef unsigned char byte; 

char 

8-bit signed character value 

double 

Double precision floating point value 

float 

Single precision floating point value 

int 

Single precision signed integer value 

long 

Double precision signed integer value 

mtype 

A Message type value. Defined by typedef sodl::Defs::MessageType sodl: :mtype; 

rand 

A random number stream. Defined by typedef sodl: ‘.Random sodl::rmA. 

process 

A handle to a process. Defined by typedef sodlwProcessHandle sod/::process; 

profile 

A profiling tool class. Defined by typedef sodh'.ProfileTools sod/::profile. 


A Process type value. Defined by typedef sodhiDefs::ProcessType sodl: iptype; 

uint 

Single precision unsigned integer value. Defined by typedef unsigned int uint; 

ulong 

Double precision unsigned integer value. Defined by typedef unsigned long ulong; 


Table 6-2 SODL construct member variable basic types 


Member variables can take on any of the basic types listed in table 6-2. Each of these data types has 
exactly the same properties as the C-t-i- data types of the same name, or typedef as the case may be. In fact, 
they are instantiated as those very same C-i-i- data types. 


76 































Table 6-3 provides some sample declarations and their meaning within the SODL language. 


SODL Declaration 

Description 

mt:x; 

Creates an integer value named x with an undefined initial value. 

double:y(0.0); 

Creates a double precision value named y initialized to 0.0 

float:zf31(1.0); 

Creates an array of 3 floating-point values, each initialized to 1.0. 

char:xn; 

Creates an empty array of characters. Initializer cannot be used in this case. 

long:y[4]; 

Creates an array of 4 uninitialized long integers with imdefined initial values. 


Table 6-3 Sample SODL member variable declarations 


{ 

messageistart 



{ 

int:x; 

// messagerstart 
// Single precision integer 



double:y(0.0); 

// Double precision floating-point number initialized to 0.0 



float:z[3](1.0); 

// Array of 3 floats each initialized to 1.0 

} 

} 


// message:start 


a - member variables constituting the payload of mes$age:start instances 


{ 

process :root 
{ 

// process:root 


char:x[]; 

// Uninitialized array of characters of unspecified length. 


long;y[4]; 

// Uninitialized array of 4 long integers 

} 

} 

// processTOOt 


b - member variables constituting the state of process:root instances 


Figure 6-10 Sample member variable declarations in SODL constructs 

Arrays are implemented using the C+h- Standard Template Library’s std:'.vector<'T> making them 
somewhat more flexible than the traditional C-style technique of using a pointer. Pointers can still be 
declared if programmers explicitly state the namespace (i.e. the global namespace). This technique is 
covered in section 6.5.2. Some sample variable declarations are provided in figure 6-10. 

6.5.2. Extended data types 

You can alternately create structures or other data types in a C-f-H- namespace (including the global 
namespace). By explicitly specifying the namespace, programmers can provide instances of any variable 
type that could be instantiated in a C-i-i- program. Any structures in namespaces (including the global 


77 

















namespace) need to be retrieved through an import directive. Some sample variable declarations are shown 
in Table 6-4. 


Declaration 

Import Needed 

Description 

std:: set<double> :x; 

{import std {<set>} } 

st</::se/<double> instance 

named x, uninitialized. 

std::string:y("Hello") 

{import std {<string>} } 

stdy.string instance named y, 
initialized to "Hello" . 

GLenum:mode[4](GL_POLYGON); 

{import {<GL/glut.h>} } 

An array of 4 GLenum 's 
named mode, initialized to 
GL POLYGON. 

gvm; :Node* :gr_node(NULL); 

{import gvm {Node} } 

An instance of gvm::Node* 
named gr node, initialized to 
NULL. 


Table 6-4 Sample extended data type declaration 


As in the standard data types, arrays are instantiated as sfd::vector<T>. 


6.5.3. Process constructs as data members 

It is also possible to declare processes as variables within process construct declarations. These 
declarations can take on either of the two forms shown in Figure 6-10. 


When a process is declared within a process construct, it is important to note that the variable associated 
with the process declaration is only a handle to the actual process instance, and not the instance itself. No 
methods or internal data can be accessed through this handle. The handle acts as an address for message 
delivery, and for filtering incoming messages. It does not have any type information associated with it, 
though a typed handle can be resolved to gather that information. 


When the first form in figure 6-11 is used to declare a process, an actual instance (or collection of them in 
the case of arrays) of the type specified by the construct name field is created. The simulation engine 
specified in the node-distribution field will then manage the activities of its instances. This form can only 
be used in process construct declarations, and not within message construets. 


construct-namewariable-name [ [ size ] ] [: node-distribution ]; 
process:vflriab/e-«awe [ [] 1 [ size ] ]; 


Figure 6-11 Process member variable declaration 


78 


























The second forai allows the variable to act as a placeholder and does not actually create any process 
instances. Instead, an empty handle (or an array of them) is created which allows local storage of 
references to arbitrary process instances. This form can be used in either process or message constructs. 
Some sample process declarations are listed in Table 6-5. 


Declaration 

Import Needed 

Description 

*a//:b[1000]; 

{import process {ball} } 

Array of 1000 handles to process:6a// instances 
named b. 


{import process {NodeSD} } 

Handle to a processrAbrfeiD named n. 

process :sAa/>e[]; 


Uninstantiated and unspecified process handle array. 


Table 6-5 Process construct declarations 


Process instances are statically assigned to a specific simulation engine for the duration of the simulation 
run®. The node-distribution field can be used to specify which simulation engine controls each of the 
instances declared in the construct. When the node-distribution is omitted for a process declaration, the 
construct will be instantiated and controlled in the engine where the construct declaring the process is 
controlled. 

engine-node-number 

< element-node-equation > 

Figure 6-12 Forms for specifying controller simulation engine 

The two forms for specifying the node-distribution are shown in Figure 6-12. The first form can be used 
either with single instances or on arrays or processes. It specifies that the process instance (or all of them in 
the case of an array) be controlled by the engine-node-number. 

The second form allows for different elements of an array of processes to be controlled by different 
simulation engines. This second form allows general C-)~)- code which, when evaluated, produces an 
unsigned integer value, to be placed between angle brackets. There is also a macro substitution for the 
characters ‘@’ and “#’. The character evaluates to the index in the array of the process. The ‘#’ 
evaluates to the total number of elements in the array. Each element is then assigned to the engine to which 
its element-node-equation evaluates. Some examples are listed in Table 6-6. 


® There is no process migration in the current SODL simulation engine, though there is nothing to preclude 
this feature from future implementations. 


79 




















Process declaration 

Description 

View3D:view:3; 

A new process: ViewSD instance is created on simulation 
engine 3. 

Node:nodes[200]: 10; 

An array of 200 processrWnrfe instances is created on 
simulation engine 10. 

Simple:simple[10000]: @%((long) sqrt(#)); 

An array of 10000 processt.S'imp/e instances. Engine 
i%100 controls simple[i]. 


Table 6-6 Engine specification for process declaration 


One final note here is that message constructs cannot have as data members explicitly typed process 
declarations. That is, only the second form in figure 6-10 is allowed within a message construct. 

6.5.4. A note on references and pointers 

Because of certain constraints associated with the Time Warp algorithm, C-i-i- references, although ignored 
during the initial processing of SODL files (i.e. with sp), may cause problems with C-i-i- compilers. This 
stems from the fact that copying class instances containing as member variables references causes some 
(perhaps all) compilers to complain without explicit declaration of the copy constructor. The decision to 
forgo this declaration (for performance concerns) has made it necessary to likewise forgo the use of 
references. 

Pointers may be used in lieu of references, though they are stylistically and functionally inferior to 
references in C++. Still, even though permitted, their use should generally be eschewed. That’s not to say 
that there is no use for them; a pointer was used for the GLUT view manager as a means of allowing a 
process to reference the same view instance regardless of its timestamp. This provided a significant 
performance improvement while retaining a suitably generalized mechanism for producing graphical 
output. 

Stepping back to consider the implications of a pointer in a general SODL environment, one sees that any 
uses of a pointer must be atemporal in their nature when defined in terms of a process construct, and highly 
questionable in terms of a message construct. In the case of a message eonstruct, a pointer really has no 
meaning since the destination of the message may be a process instance loeated on a different host 
maehine. In the case of a process construct, only the pointer is copied to each process instance during the 
state saving portion of the Time Warp algorithm; the data that the pointer points to is not copied. 


80 












Therefore, unless arrangements are made to reallocate data at each of these temporal transitions, all process 
instances associated with a particular process (i.e. the same process at different points in time) point to the 
same memory location and can interact with the data in that memory location atemporally. 

6.6. Method Declarations 

Both types of constructs can also declare methods. For messages, these methods behave the way they do in 
most object oriented programming languages. That is, a programmer can call a method defined within a 
message construct declaration. However, in process construct, since both messages and processes only 
have handles to other processes, there is no way to call the methods of another process. The reason for this 
is that a process may be instantiated on a remote simulation node, and not directly accessible to the local 
process instance. Methods may be declared as indicated in figure 6-13. 

method:method-name(access-specifier‘, return-type', [ variable-specifier', [variable-specifier; [... ] ] ] ) 

{ 

method-body 

} _ 

Figure 6-13 General method form 

The access-specifier is one of {public, protected, private}. It tells the parser what sort of access to the 

method is allowed. The meanings are analogous to the respective C-I-+ keywords. 

The return-type is simply a C-(-f data type or any of the data types listed in Table 6-2. Any types not listed 
in Table 6-2 need to be prefaced with their C-H- namespace identifier, including any types defined in the 
global namespace (in this case they need to be prefaced with 

Method parameters are defined just like member variables, as described in Figure 6-9. They also require 
that any non-standard data types be prefaced with the identifier for the namespace in which they were 
defined. 

The method-body is nothing more than some C+-i- code that performs the desired function of the method. 
This code is cut from the SODL program and pasted directly into the resulting C+-i- source code files 


Method names must be unique within the process or message in which they are declared. 










{ 

message: start 



{ 

double :x[ 10]; 

// message:start 

// Uninitialized array of 10 doubles 



method:getX(public; double; uint 
{ 

:i;) 

// method:getX(public; double; uint:i;) 



return (i<x.sizeO) ? x[i] 

0.0; // Return x[i] if i is in [0, x.sizeQ) 



} 

// method:getX(public; double; uint:i;) 



method:setX(public; void; uint:i; 

doublerv;) 



{ 

// method:setX(public; void; uint:i; double:v;) 



if (i<x.sizeO) x[i]=v; 

// Set x[i] to V if I is n the proper range 



} 

// method:setX(public; void; uint:i; doublerv;) 



method:init(public; void;) 

{ 

// method:init(public; void;) 



for (int i=0; Kx.sizeQ; ++i) x[i]=0.0; // Initialize all elements of x to 0.0 



} 

// method:init(public; void;) 

} 

} 


// message:start 


Figure 6-14 Sample methods 


A sample method declaration for a message construct is presented in figure 6-14. Process constructs handle 
methods in exactly the same way. 


6.7. Messages 

{message:wmage-«a/«e[ (parent-message) ] 

5 I 

{ message-definition} 

} 

Figure 6-15 Message construct form 

Messages are packets of information passed between process instances and provide the only mechanism for 
inter-process communication. Each message has a source process and a collection of destination processes. 
They flow from the source process to the processes in the destination list. Figure 6-15 shows the general 
form of a message construct. Figure 6-16 shows how the messages flow from source to destination in 
detail. 


82 










The message-name is a unique type identifier for the message eonstruct being declared. All message 
instances have as their type one of the Defs::MessageType enumerators, Defsi:SMT_message-type. The 
parent-message allows for inheriting the data members and methods of the parent-message message type. 



Figure 6-16 Message flow from sending process to receiving processes 


Messages contain a collection of system-defined parameters, and possibly a user-defined payload and 
collection of methods. The message-definition is the user-defined portion of the message definition and 
consists of these member variables and methods declared as indicated in sections 6.5 and 6.6 respectively. 

In addition to the user-defined portion of a message, there are a number of system-defined methods and 
member variables. 


6.7.1. System-defined message member variables 

The system-defined member variables allow users to customize certain aspects of message delivery. 
Though these variables should be manipulated through the provided accessor functions described in section 

6.7.2. 


83 












































The member variables in Table 6-7 are declared as protected (except for genTime, which is private) and 
non-static. 


Message 
member variable 

Description 

destinationlist: dest 

This is the list of destination processes for the message, destination list is a 
typedef of s/d::iMflp<ulong, s/d::se#<ulong> >. 


Simulation time at which this message was generated. 


A unique identifier for the owning message instance. 

ulong:fio</e 

Engine node number controlling the message instance. This may be different 
from the engine node number where the message was actually generated. 

\ioo\:preempt{f 2 Ast) 

The user can, at compile time specify a collection of destinations for the 
message. If preempt is set to true, the message is not sent to those destinations. 
Any destinations added during the handling of the message are retained. 

process:soarc£ 

This is shorthand for the source process of this message, source.first is the node 
in the distributed simulation managing the source process, source.second is the 
specific index of the source process. 

double:/iffte 

Simulation timestamp for message delivery. 


The user can set tx to false to prevent message transmission, tx defaults to true. 


Table 6-7 System-deflned message member variables 


6.7.2. System-defined message methods 

Access to the member variable listed in Table 6-7 should be performed through the member functions, 
rather than direct manipulation of them. Table 6-8 provides a list of useful system-defined message 
methods, some of which can be overloaded to change their behavior. 


Each of the methods can be overloaded in a message construct declaration, though the only two methods 
where this serves any clear purpose are init and getTX. 


In the case of init, the default behavior does not do anything; it is an empty function. However, some 
initialization of member data may not easily be initialized at the location of its declaration. The init method 
is provided as a means to address that shortcoming. 

The getTX function can be overloaded to provide a means to test certain conditions that will either permit 
or refuse transmission of the message. 


In all cases of method overloading, users should ascertain whether they desire the default behavior of the 
parent construct to be an aspect of the child construct’s behavior. If so, calls to these parent construct 
versions need to be explicitly made from within the construct method of the child construct. 


84 

























Message method 

Description 

method:a(/</2)est(public; void; process:/;;) 

Adds p to the list of destinations. 

method:a</d!Dest(public; void; st</::vectoi<process>:/>;) 

Adds p[i\ to the list of destinations, 
i<p.sizeO 

method:c/earZ)e5t(public; void;) 

Clears the message's destination list, and 
sets preempt to true. 

method:;fetGe«r/#fic(public; double;) 

Returns genTime to the calling routine. 

method:;?el'/D(public; sadly. Handle',) 

Returns a copy of me to the calling routine. 

method:)?£t/Vb</e(public; long;) 

Returns node to the calling routine. 

method:;?£/^ource(public; process;) 

Returns source to the calling routine. 

metbod:;^e/7'{ine(public; double;) 

Returns time to the calling routine. 

method:^etrY(public; bool;) 

Returns true if and only if tx is true and the 
destination list is not empty. It can be 
overloaded to check for additional criteria 
for message transmission. 

method:^et7y/)e(public; mtype;) 

Returns the message type to the calling 
routine. 

method:^et7ypeA^fliMe(public; std'. '.string;) 

Returns a string representation of the 
message type to the calling routine. 

methodriniXpublic; void;) 

An initializer that is called immediately 
after message generation. It can be used to 
perform specific initialization. 

method:is7’re£mpte</(public; bool;) 

Returns preempt to the calling routine 

method:{s7ype(public; bool; mtype:/;) 

Returns true if and only if the message 
instance is of type (or a subtype of) /. 

method:setPreempted(p\ihlK; void; bool:p;) 

Sets the preempt flag to p. 

method:s£/rime(public; void; double:/;) 

Sets time to /. This is adjusted during 
message transmission to ensure that it 
occurs at some time after the message 
generation time {genTime). 

method:se/7X(public; void; bool:v;) 

Sets the tx flag to v. 


Table 6-8 Common system-defined message methods 


6.7.3. System-defined messages 



Description 

mtssAgC'Jintimessage 

Antimessage instances are paired with message instances and saved on the 
simulation engine that owns the process that is the source of the message. 
An Antimessage is transmitted when a rollback causes the engine to revoke 
messages that have previously been transmitted. When a message and its 
associated Antimessage are combined, they annihilate each other, ensuring 
that the original message is never delivered. 

messagc'.EndSimulation 

EndSimulation messages are time stamped with the end simulation time 
(le307) and are intended to be delivered to all simulation processes by the 
time the simulation is complete. This has the affect of setting the system 
clock of each engine on an engine stand to the end simulation time when it 
has no messages remaining to process 

message:StartSimulation 

Each process in the simulation receives a StartSimulation message when 
the simulation begins. This is time stamped with some time prior to 0 
(defaults to -1). This allows individual processes to perform some last 
minute initialization and set up prior to actually beginning the simulation 
run and as a bootstrapping device. 


Table 6-9 System-defined messages 


85 






































A number of messages are used within the run time system to perform various functions. Users defined 
processes can, in some instances receive these messages. Each is defined in detail in the following 
sections. Additional messages are defined for the GLUT view manager, and are discussed in more detail in 
Chapter 9. These system-defined messages are listed in Table 6-9. 

6.7.4. Message Handles 

Each message has a message handle, me, containing identifier information. This is a sodl::MessageHandle 
instance (which has a typedef to message) and contains the index of the engine where the associated 
message was generated, and the message instance count for that message. Each message generated has a 
unique message handle. Users should not modify these values, as it may cause problems with message 
revocation. 

6.8. Processes 

Process can be thought of as a self-contained package of state information that responds to incoming 
messages. The process will change its state information and send new messages in response to an 
incoming message. Messages are processed in time stamp order. The process takes on the time stamp 
value of the last message it processed. 

{ process:process-name [ (parent-process)] 

;l 
{ 

process-definition 

} 

} 

Figure 6-17 Process declaration syntax 

Processes are isolated message handlers. They are unable to communicate with each other directly, and 
must rely exclusively upon message passing to perform this function. Process declarations have the form 
specified in Figure 6-17. The process-name is a unique type identifier for the process class being declared. 
All process instances have as their type one of the Defsv.ProcessType enumerators, Defsr.SMT_process- 
type. The parent-process allows for inheriting the data members and methods of the parent-process 
process type. The process-definition is the user-defined portion of the process definition. It consists of 


86 








member variable and method declarations as described in sections 6.5 and 6.6. It is in this process- 
definition section that we also define the process modes and node, which allow the process to handle 
messages. 


The C-I-+ code sp generates declares one class for each process type defined named sodl::process-name. It 
performs the functions associated with the process. Instances of these classes receive messages and 
manage the responses to those messages. The structure and format of these files is discussed in more detail 
in Chapter 7. 

6.8.1. System-defined process member variables 

There are a number of system-defined member variables associated with a process. These member 
variables, listed in Table 6-10, may be useful in governing a process response to incoming messages. Other 
system-defined variables are accessed through the accessor functions in section 6.8.3. 


Process 

Variables 

Purpose 

process:me 

An identifier for this instance of the process. Useful for addressing messages to owning 
process. The resolve method can be called to get a reference to the actual handle 
associated with the process. 

rmA:random 

A random number stream. This is a static member in the sodhiProcess declaration, so 
all such instances share the same random number generator (RNG). Programmers can 
provide their own RNG instance (as a process member variable) in the process if 
desired. 


Type information regarding the specific process instance. 


Table 6-10 System-defined process member variables 


6.8.2. System-defined process methods 

There are several system-defined methods useful in ascertaining or establishing certain parameters related 
to a process state. The methods listed in Table 6-11 are intended to assist the programmer in making 
decisions regarding the process state and to perform various tasks associated with process management. 
Most of the methods are not intended to be overloaded, and should not be without great care taken. The 
exceptions to this are methods backup, fossilCollect, init, and restore. These methods provide mechanisms 
for dealing with initialization (jnit) and as a means by which a process can interact with the underlying 
simulation synchronization protocols. 


87 













Process System Define Methods 

Purpose 

method:hacAi{p(public; void;) 

When the process is backed up for the purpose of sate 
saving, the time stamp of the new state is set and the 
backup method is called. If programmers want to 
perform some action at this point, they may overload the 
backup methods to perform it. 

method:/o55i/Co//£cr(public; void;) 

When a process state is about to be fossil collected, its 
fossilCollect method is first called. Programmers may 
overload this method to perform any required output or 
irrevocable action prior to actual fossil collection. 

method:^er£'n^{ffe(public; sodb :Engme&;) 

Returns a reference to the engine controlling this 
process instance to the calling routine. 

methodt^ef/ndexCpublic; long;) 

Returns to the calling routine the unique index on the 
simulation engine for the associated process instance. 

method:gef7Vbde(public; long;) 

Returns to the calling routine the simulation engine 
controlling the associated process instance. 

method:^£r7y/;e(public; ptype;) 

Returns to the calling routine the specific type of the 
associated process instance. 

method:^ef7'/in£(public; double;) 

Returns the process timestamp of this instance to the 
calling routine. 

methodiiifiXpublic; void;) 

Overloading init allows initialization. It is called shortly 
after process instantiation, and before the first 
simulation message is delivered. 

method:u7);pe(public; bool; ptype:/;) 

Returns to the calling routine true if and only if *this is 
an instance of a process of type t. This includes 
subclasses of type t. 

method:res/ore(public; void;) 

When a process state is restored due to a rollback, the 
process controller calls the restore method to allow the 
process the opportunity to perform any functions that 
might be required. 


Table 6-11 System-deflned methods for process classes 


6.8.3. Process Handles 


Method 

Description 

ptype ProcessHandle: :getType(yoid) 

Returns the type for the sodlr.Process instance associated with 
this sodkiProcessHandle instance. 

bool ProcessHandle::isType(ptype t) 

Returns true exactly when the sodl::Process instance 
associated with this sodl::ProcessHandle is of type t. 


Table 6-12 sodl::ProcessHandle type routines 


Process handles are unique identifiers for process instances. Each process instance has its handle in the 
member variable, me. This is of type sodl::ProcessHandle, and has an associated typedef, process. Each 
handle contains engine and instance index information used to identify a specific process instance. Process 
handles can also be used as a reference to process instances. In this case, type information can be derived 
from the process handle instance. Methods to perform this sort of type-query are listed in Table 6-12. 


88 






























6.8.4. Special Processes 

There are no special system-defined processes, in the sense. However, the root process occupies a unique 
position in the simulation system. It is always instantiated and controlled on engine 0 in the distributed 
simulation, and has index 0. It is the base process specified in the sp command line argument. 

6.9. Mode and Node Declarations 

Only process constructs may define modes, and nodes within those modes. A mode may be either active or 
inactive. Only nodes defined within active modes can process messages. This feature is useful if there are 
a different behaviors associated with different process modes. For instance, suppose a user wanted to look 
at the dynamics associated with an ant colony. Individual ants might have different functions that change 
with respect to certain external stimuli. At one point, the ant might be out looking for food. At another 
time, it might be taking the food back to the nest. Still another time it might be defending the nest from an 
intruder. Each of these different behaviors can be activated and deactivated inside the code simulating the 
ant’s behavior, and messages will only be delivered to nodes that reside in active modes. 

Each node is declared so that it responds to a specific message type. These nodes produce state changes 
within the receiving process and subsequently transmit a (possibly empty) collection of output messages in 
response. 

6.9.1. Modes 

Modes provide a mechanism for easily changing process behavior without the programmer having to 
explicitly perform a number of checks, and handle the message differently depending upon certain internal 
state data. Each mode in a process construct has a unique name corresponding to a process member 
variable of type sodhzProcessMode, a C-i-i- class defined as part of the underlying SODL system. The 
format for declaring a mode is illustrated in Figure 6-18. sodhzProcessMode instanees have methods, 
listed in Table 6-13, for managing the mode state. 


89 






moAc’.mode-name 

{ 

node-declarations 

} 

Figure 6-18 Mode declaration syntax 

Modes are inherited from parent processes, though the mechanism governing this inheritance may not be 
immediately obvious. Let process construct a, with a mode named m, be a sub-process (i.e. inherited from) 
of process construct b, also with a mode named m. In the class declarations sp generates, a: :m and b: :m are 
actually the same declaration. That is, only b: :m is declared; any changes to m from a methods or node in a 
change the declaration in the parent class. Thus, whatever state changes are made from the perspective of a 
are also made from the perspective of b, and vice versa. 


Mode Methods 

Purpose 

bool sodb :ProcessMode\:isActive(yo\dl) 

Returns true if the mode is active or false if it is not. 

void sodl::ProcessMode::setActive(bool a) 

Activates (fl=true), or deactivates (a=false) the mode 


Table 6-13 Mode system-define methods 


When the setActive method for a mode is called, the change does not immediately occur. The change will 
be finalized only when the process clock is advanced. The reason for this is that there may be multiple 
messages with the same timestamp addressed for the same process. If one of them changes the active flag 
for a mode, and were that change to be immediately reflected, handling of the other message (if it’s done 
afterwards) might be affected. By waiting until the process time stamp is advanced to commit changes to 
the active flag, all messages will be processed consistently vis-a-vis the mode active flags, regardless of the 
order in which the messages are actually handled. 


6.9.2. Nodes 


node:node-name [ message-typeunput-message-name ] 

[ output-message^, output-messagei ,..., output-message„ ] 

{ 

node-body 

} 


Figure 6-19 Node declaration syntax 


90 










Nodes are defined within a mode. No two nodes within the same mode declaration may have the same 
name, though nodes in different modes may. The declaration format for a node is illustrated in Figure 6-19. 
The form for output message specifications is depicted in Figure 6-20. 


output-message-type:output-message-name 

[ 

[ [ size-specifiaction ] ] 

] 

[ 

=> {destination^, destination\', ...; destinationi,) 

] 

[ 

'.{time-specifier) 

] 


Figure 6-20 Output message form 


6.9.2.1. Input message 


{import message {Generic} } 

{import std {<iostream>} } 


{ 

process:Simple 


{ 

//process:Simple 

mode:Default 

{ 

// mode:Default 

node :rutmer[Generic;in] [] 

{ 

// node:ruimer[Generic:in][] 

std::cout«in.data « std::endl; 

// Display input data 

} 

// node:runner[Generic:in][] 

} 

// mode:Default 

} 

} 

// process: Simple 


Figure 6-21 Stand along input message usage 


Nodes are defined to accept all messages of type message-type that are transmitted to the owning process 
instance. The node will also accept messages of types derived from message-type. So, if node n accepts 
messages of type m, and message p is a sub-construct of message m, any messages of type p will also be 
handled in node n. p will be first cast to type m, and any methods or member variables in type p, not in 
type m will not be accessible to 


In fact such attempts at a dynamic cast will probably lead to an abnormal program, termination since the 
messages are passed to the node by value, and not by reference. 


91 












From the programmer’s perspective, a variable with name input-message-name acts as the interface to the 
message. It is a message instance that is passed to the node handling the message and has a parameter of 
type input-message-type with name input-message-name. An example of how declare and interact with an 
input message is depicted in Figure 6-21. 

6.9.2.2. Output messages 

The output message format allows programmers the flexibility of compactly specifying the default number 
of messages, default destinations, and default time stamps. In most cases these call all be overridden (the 
only exception being that the array form must be used in order to send multiple instances of the same 
message type from the output message specifier). 


6.9.2.2.I. Default output message form 


{import message {Generic} } 

{import std {<iostream>} } 


{ 

process: Simple 


{ 

// processiSimple 

mode:Default 

{ 

// mode:Default 

node:runner[Generic:in] [Generic:out] 

{ 

// node:runner[Generic:m][] 

std::cout« m.data « std::endl; 

// Display input data 

out.addDest(me); 

// Return the message to me 

out.setTime(in.getTimeO+1.0); 

// Schedule for later return 

} 

// node:runner[Generic:in][] 

} 

// mode:Default 

} 

} 

// process: Simple 


Figure 6-22 Explicit specification of destination & timestamp in body 


The default output message form does not specify a size, destination, or timestamp. In this case, the 
process creates exactly one message instance of output-message-type, named output-message name. The 
destination list is empty, meaning that it will not be delivered to any process unless some destinations are 
added via the output-message-nameMddDest{...) method. The default time stamp is also used, which is 
some point after the time stamp value in the process handling the message. The message timestamp can be 
set using the output-message-name,setTime{...) method. It accepts a double precision floating point 


92 







number serving as a timestamp for the message. Figure 6-22 modifies somewhat the code in Figure 6-21 to 
demonstrate how a node may customize the message internal data. 


If the output message time stamp is not explicitly set anywhere in the body of the node, the simulation 
engine will set it to a value slightly larger than that of the input message. The actual value of this 
timestamp is given in Equation 6-1. 


next - time(t) = 


o' 

V 

(l-lO 

t = 0, 

10-307 

t>0, 

(l + lO 


)■ current - time 
)• current time 


( 6 - 1 ) 


6.9.2.2.2. Output message size specification 


node:nmner [Generic:in] 

// Upon receipt of an input message 


[Generic;outl[], 

// Create unspecified number of outputs 


Generic:out2[5]] 

// And an array of five of them 

{ 

for (int i=0; i<out2.size(); -H-i) 

{ 

outl .push_back(me); 

// node:runner[Generic;in][...] 

// Loop over the output arrays 


// Create a new message with source me 


outl .back0.addDest(me); 

H Add me as a destination process 


outl.back0.setTime(getTimeO+i); //Schedule for later return 


out2[i] .addDest(me); 

// Add me as a the destination process 


} 

// for (int i=0; i<out2.size0; ++i) 

} 


// node:runner[Generic:m][...] 


Figure 6-23 Message count speciflcation for output message arrays 


The output message size specification allows the programmer to create multiple message instances each 
independently addressed, time stamped, and to have differing payloads. When either the '[]’ or ‘[jize- 
specification]’ form is used, output-message-name is passed to the node as a std::vector<output-message- 
type> instance. In the first case, the vector is empty, and new messages can be added to the vector by 
calling output-message-name.push_front{me). Messages can also be removed after their creation if the for 


some reason do not merit additional consideration. This can be accomplished through use of the 
std::vector<T>pop JrontQ method. 


93 









This can also be done with the ‘[size-spcificationY form if additional messages are required, or the 
programmer decides not to send some. 


Figure 6-23 further modifies the code in the previous two figures (omitting the redundant code) to 
demonstrate how to use either form. In the case of the messages in the out2 array, their timestamps will be 
given by the default value defined by Equation 6-1. This means that it will take a very long time before 
any of the outl messages will be processed. This is in fact going to lead to the situation where the memory 
of the computer is eventually consumed by pending messages leading to an abnormal program termination, 
quite likely prior to any of the outl messages ever being processed. The purpose of this code segment, and 
many that follow is to provide some insight into the mechanisms for controlling message delivery, not 
necessarily to provide a logical framework for nodes in systems that will eventually do anything useful. 

6.9.2.2.3. Destination specification 

Each output message can have an arbitrary number of default destinations. Each destination needs at run 
time to resolve to a process or std'.:vector<proce.ss> instance. For arrays of output messages, this might be 
somewhat limiting, so a mechanism for independently addressing different elements of an array of 
messages, similar to that used in the initialization of arrays of variables, has been provided to allow 
programmers this flexibility in a compact form. Any characters will be replaced with the index of the 
array element, and any “#’ will be replaced with the size of the message array. 


node:runner [Generic:in] 

// Upon receipt of an input message 


[Generic:outl []=>(process(0,@);). 

// Create unspecified number of outputs 


Generic:out2=> (me;)] 

// And an individual one 

{ 

for (int i=0; i<5; -H-i) 

{ 

out 1 .push_back(me); 

//node :runner[Generic: in] [... ] 

// Loop over the output arrays 


// Create a new message with source me 


outl .back0.setTime(getTimeO+i); 

H Schedule for later return 


} 

// for (int i=0; i<5; +-i-i) 

} 


// node:runner [Generic: in] [...] 


Figure 6-24 Adding default destinations to output messages 

Figure 6-24 shows an example of how this default addressing is specified. In the case of out2, (which has 
changed from an array to a single message instance) the destination is explicitly declared in the node 


94 









header. In the case of outl, a C++ expression resolves to a process instance. In particular, message outl[i\ 
is sent to process(0,0, which is the i* process on simulation engine 0 (which for the moment we will 
assume actually exists). In both cases, additional destinations can be added, each terminated with a 
semicolon. 

The expressions serving as message destinations are actually evaluated and added to the destination list 
after the node has finished processing. The reason for this is that any changes to local variables used in 
computing the destinations might be modified in the node body. However, the programmer can preempt 
adding these destinations by calling the message’s clearDestQ method. This will clear any destinations 
previously added to the destination list while setting a flag that is checked prior to adding the default 
destinations. 


6.9.2.2.4. Time stamp speciHcation 


node;runner 

[Generic;in] 

// Upon receipt of an input message 



[Generic;outl [];(getTimeO+@), 

// Create unspecified number of outputs 



Generic:out2:(getTimeO*2.0)] 

// And an individual one 

{ 

for (int i 
{ 

=0; i<5; ++i) 

// node:runner[Generic:in][...] 

// Loop over the output arrays 


outl .push_back(me); 

// Create a new message with source me 



outl .back0.addDest(me); 

// Add me as a destination process 


} 


// for (int i=0; i<5; ++i) 


out2.addDest(me); 

// Add me as a the destination process 

} 



// node:runner[Generic:in][...] 


Figure 6-25 Default time stamp specification 

As with destinations, output messages can also have a default time stamp. It can also be set differently for 
each message instance in an array. We again substitute the array element index for the in the time 


stamp specification, and the array size for the ‘#’ character. We see this illustrated in Figure 6-25. 

Here we have each of the message time stamps for outl[i] set to getTime{)+i. For out2, we specify the 
message time stamp to getTimei)*2.0 (assuming that the current time is strictly positive). 

As in the destination specification, the default time stamp value is set after the node complete processing 
the input message. If in the course of handling the input message, the node has code to alter the default 


95 








time stamp value, an internal flag is set in the message indicating that it should not attempt to set the 
message time stamp with the default value. 

6.9.2.2.5. Combining destination and time stamp specifications 

The default time stamp and destination specifications can both appear in the output message declaration 
provided they appear in the order they appear in Figure 6-19. An example of this is shown in Figure 6-26. 


node:runner [Generic:in] // Upon receipt of an input message 

[Generic:outl[]=>(process(0,@););(getTime()+@), 
Generic:out2=>(me;):(getTimeO*2.0)] 

{ //node:runner[Generic:m][...] 

for (int i=0; i<5; ++i) // Loop over the output arrays 

outl .push_back(me); // Create a new message with source me 

} //node:runner[Generic:m][...] 


Figure 6-26 Combined default destination and time stamp specification 

6.9,2.3. Node body 

As in methods, the body of the node declaration is merely a block of C-H+ code. This code dictates how the 
node responds to the incoming message. This response may include creating state changes in the parent 
process and formatting output message payloads, time stamps, destinations or other aspects of message 
formatting. 


96 










Chapter 7. C++ code generation 

When the SODL parser (sp) is run, it generates a number of files in baseDirlbldSubdir. For each message 
and process file, two C++ source code files are created, one with header information and one with the 
actual method definitions. Three additional files, Defs.h, Defs.cxx, and Main.cxx are also created. 

Both process and message constructs in the SODL language become C++ classes in the resulting output 
files. Data members and methods within the SODL constructs are incorporated into the resulting C++ 
classes in the obvious fashion of direct inclusion. There is also a great deal of functionality that must he 
incorporated into the classes to ensure they properly interface with the simulation engine. 

The SODL parser also creates entries into a Makefile in baseDir with a largely correct dependency 
specification so that minor changes to one file do not normally necessitate rebuilding the entire simulation 
system. Additionally, in a further effort to reduce unnecessary builds, C++ files are not written to the hard 
drive if doing so would not change the contents of the resulting file. 

One other point that should be made here is that it is not immediately obvious how to create a universal 
library of the simulation engine that can be linked (either statically or dynamically) to create a final 
executable. The reason for this is that there is a great deal of type information generated during the SODL 
parser run needed within the SODL run-time system. Possible future enhancements include eliminating 
this restriction, due to the lengthy time required to rebuild the run-time system for each simulation project, 
or each time a given project changes in some substantive manner. 

7.1. Message construct files 

Member data and methods from message constructs are incorporated into the resulting C++ files 
straightforward manner. The message class declaration provides the necessary functionality to operate 
properly with the SODL run-time system. 


97 







7.1.1. A simple message construct 

A basic message, one with no member data or user-defined methods still needs to provide typing 
information so that it can be properly forwarded to the desired node within a process instance. Consider a 
message construct with no explicitly defined parent type and no member data or methods, called generic. It 
is depicted in figure 7-1, with the relevant output in figure 7-2. 


{message:Generic; } 


Figure 7-1 A simple message construct in Generic.msg 


namespace sodl 
{ 

class Generic : public Message 

namespace sodl 
{ 

Generic: :Generic(process p) 

{ 

: Message(p.getNodeO, p.getIndexO, SMT Generic) 

public: 

{ 

Generic(process p); 

Generic::instanceInitO; 

static void typelnit(mtype t); 

initO; 

virtual Message& copy(void); 
virtual Message& copy(ulong); 

} 


Generic: :Generic(ulong n, ulong i, mtype t) 

protected: 

: Message(n, i, t) 

Generic(ulong n, ulong i, mtype t); 

{ Generic: :instancelnit0;} 

protected: 

voidGeneric::instanceInit(void) {} 

virtual void instanceImt(void); 

Message& copy(void) 

}; 

{ return new Generic(*this);} 

} 

Message& copy(ulong) 

{ Message& rv = copyO; rv.setEngine(i); return rv; } 

void Generic: :typelnit(mtype t) 

{ 

msgTypes[t][SMT_Generic] = true; 

Message::typelnit(t); 

} 

} 


(a) - GenericM (b) - Generic.cxx 


Figure 7-2 Relevant output derived from Generic.msg 

Here there are no user defined methods or data members. The focus of the message is to provide type 
information that can be used in determining how any recipient processes will process instances of this 
message type. 


98 











The public class constructor GencjTc(proccss p) is used to create a Generic class instance, and not one of 
its subclasses. The parameter p is the process identifier of the message source. The call to the parent class 
constructor breaks up the source process handle into its component parts and adds the type information. 
From this, the root Message constructor sets the message source process handle and its type information, 
and derives message generation time. The constructor then calls Generic•.linstancelnit to perform member 
data initialization, and GenericiUnit to perform user-specified instance initialization. In this case, since 
there is no user-define init method, this results in calling Messagey.init, which is empty. 

Derived classes use the protected class constructor, Generic(\x\ong n, ulong i, mtype 0. to pass source and 
type information on to the Message class constructor. Like the public constructor, this constructor also 
calls Genericr.instancelnit to perform local data member initialization. It does not call the user-specified 
initializer, since the language specification requires it to be explicitly called from the init method in the 
derived class being instantiated. 

The Generic::copy(\oid) and Generic::copy(iilong) methods are used to produce copies of the message. 
The former returns a strict copy, while the other returns a copy but changes the message engine setting so 
that it can be controlled by an engine instance other than the one on which it was created. 

Static method Genericy.Typelnitimtype t) is used to initialize the sodl::Defs::msgTypes array containing 
the parent-child relationship between different message types. This routine is called once during the 
simulation system setup, prior to any messages actually being delivered. 

7.1.2. Message construct with user-defined methods and data members 

User-defined methods within a message construct are placed into the resulting Ch-h- class declaration in the 
obvious fashion. Given the message construct depicted in Figure 7-3, its data members and methods 
declaration are added to the C+h- class in the fashion depicted in Figure 7-4. 

The instancelnit method is really only used when there is a need to initialize an array to some preset value. 
For instance, in Figure 7-5, we declare the message construct message:send_vector and specify a default 
initial value for the array, an equation relating each component of the array to its index in the array. 


99 







{ 


messageiAddSubordinate 

{ 

process: subordinates []; 

method:add(public; void; processrn;) 

{ subordinates.push_back(n);} 

method:getTX(public; bool;) 

{ return Isubordinates.emptyO && Message: :getTX0; } 

method:size(public; ulong;) 

{return subordinates.sizeO;} 

} 

} 


Figure 7-3 AddSubordinate.msg with one data member and three methods 


namespace sodl 
{ 

class AddSubordinate : public Message 
{ 

public: 

namespace sodl 
{ 

std::vector< process > subordinates; 

void AddSubordinate ::add(process n) 

{ subordinates.push_back(n); } 

public: 

bool AddSubordinate::getTX(void) 

virtual void add(process); 

{ return Isubordinates.emptyO && Message::getTX0;} 

virtual bool getTX(void); 
virtual ulong size(void); 

ulong AddSubordinate::size(void) 

}; 

{return subordinates.sizeO;} 

} 

} 


(a) - AddSubordinate.h (b) - AddSubordmate.cxx 


Figure 7-4 C-H- Files resulting from AddSubordinate.msg 

{ 

message:send_array 

{ 

long:x[100] ( (3 l*@%((long) sqrt(#))) ); 

} 

} 

Figure 7-5 An initiali ed array as a data member in a message construct 

When the developer specifies an initial value for a data member, the actual initialization normally takes 
place in the class constructor proper. However, since SODL provides some additional flexibility in 
initializing arrays, this needs to be aceomplished in a routine where we can perform different computations 


100 














Figure 7-7 A simple process construct, Shape2D.proc 


The simplest process construct is one with no data members, methods or modes. An example of such a 
process is depieted in Figure 7-5 and the resulting C-t-i- code is in Figure 7-6. 

Like the class declaration resulting from a message construct, process constructs result in class declarations 
with two constructors. The first is a public constructor that is used to create an instance of that class, not a 
derived elass. The one parameter in the public constructor is the index of the engine where the process will 
reside. The host engine is polled for its next available index which, when combined with the engine index, 
is used to create the unique handle identifying the newly created instance. This handle information and the 
type information is passed to the parent constructor all the way to the sodl::Process constructor which will 
create a new process controller, and perform some setup on the actual process instance. The second 


101 













constructor is protected, and is intended can to be called from the construetor of a derived elass. In both of 
these eases, the instancelnit method is called to perform some additional initialization. Unlike the C++ 
elass deelaration resulting from a message construct, the user-defined init method (if any) is not ealled at 
this point. The init method is ealled at the beginning of the simulation system initialization, but after all of 
the processes have aetually been instantiated. 


namespace sodl 
{ 

class Shape2D : public Shape 

namespace sodl 
{ 

Shape2D::Shape2D(ulong n) 

{ 

: Shape(n, nextProcess(n), SPT_Shape2D) 

protected: 

{ Shape2D::instanceInitO; } 

Shape2D(ulong n, ulong i, ptype t); 

Shape2D::Shape2D(ulong n, ulong i, ptype t) 

public: 

: Shape(n, i, t) 

Shape2D(ulong n); 
static void typelnit(ptype t); 

{ Shape2D::instanceInit0;} 

virtual Process& copy(void); 

void Shape2D::instanceInit(void) {} 

Process& Shape2D::copy(void) 

protected: 

{ return new Shape2D(*(this);} 

virtual void instancelnit(void); 

}; 

} 

void Shape2D::typeInit(ptype t) 

{ 

procTypes[t][ SPT_Shape2D] = true; 

Shape::typelnit(t); 

}} 


(a) Shape2D.h (b) Shape2D.cxx 


Figure 7-8 Relevant output from Shape2D.proc 

The copy method is used to return a copy of the process instance, typelnit performs precisely the same 
function as the method by the same name in the message construet derived elass deelaration, exeept that it 
initializes the parent-ehild relationship between process types rather than message types. 


7.2.2. Process constructs with data members and methods 

Methods and non-process data members are handled identieally to message construets. For a more 
complete treatise on this aspect of C++ code generation for the SODL system, refer to seetion 7.1.2. 
However, a process ean declare subordinate process instances within its definition. These process instance 
declarations are handled similarly to regular data members, but there are some differences, as depicted in 
Figures 7-9 and 7-10. 


102 










Note that the child’s data type in the C++ class definition is type sodfiiprocess. Also, note that the new 
sodlr.Child instance associated with child is actually allocated in the class constructor. This has the default 
effect of creating a subordinate process on the same engine as the instance owning the sodl::Child. 


{import process {Child} } 

{ 

process: Simple 

{ 

Child: child 

} 

} 


Figure 7-9 Simple.proc declaration of a subordinate process instance to process:5'/mp/e 


namespace sodl 
{ 

class Simple : public Process 
{ 

protected: 

#include “Child.h” 

namespace sodl 
{ 

Simple::Simple(ulong n) 

process child', 

: Process(n, nextProcess(n), SPT Simple), 

childi (new Childime.getNodeO))->getIDO ) 

protected: 

{ Simple::instanceInitO;} 

Simple(ulong, ulong, ptype); 

Simple: :Simple(ulong n, ulong i, ptype t) 

public: 

; Process(n, i, t), 

Simple(ulong); 

static void typelnit(ptype); 

child{ (new Child(me.getNodeO))->getIDO) 


{ Simple: :instancelnit0; } 

protected: 

virtual void instancelnit(void); 

}; 

} 



(a) Simple.h (b) Simple.cxx 


Figure 7-10 C++ declaration and allocation of a subordinate process 


{import process {ball} } 

{ 

process:boimce 

{ 

ball:b[400]:l+(@%2); 

} 

} 


Figure 7-11 bounce.proc declaration of subordinate processes on non-default engines 


103 
















While this method works well for single process instances in a given process definition, it does not easily 
extend to arrays of processes. A mechanism similar to array initialization described in section 7.1.2 is 
employed for this purpose and is depicted in Figures 7-11 and 7-12. 


Note again the macro substitution in instancelnit of ‘bindex’ for ‘@’. This splits the actual process 
instances across two engines, one on engine 1 and the other on engine 2. 


namespace sodl 
{ 

class bounce : public Process 
{ 

protected: 

#include “ball.h” 

namespace sodl 
{ 

bounce::bounce(ulong n) 

s/</::vcctor<process> A; 

: Process(n, nextProcess(n), SPT bounce) 

{bounce: :instancelnit();} 

protected: 


bounce(ulong, ulong, ptype); 

bounce::bounce(ulong n, ulong i, ptype t) 


: Process(n, i, t) 

public: 

{bounce: :instancelnit0;} 

bounce(ulong); 

static void typelnit(ptype); 

void bounce: :instanceInit(void) 

{ 

for (long bindex={i; bmdex<400; ++bindex) 

protected: 

virtual void instanceInit(void); 

}; 

} 

bfush back{ (new ball{l+{bindexVa2)))-^getID {)); 

} 

} 


(a) bounce.h (b) bounce.cxx 


Figure 7-12 Declaration and allocation of an array of subordinate processes 


7.2.3. Mode and node declarations 

The primary purpose of a process is to receive input messages, change its internal state, and to format and 
transmit outgoing messages in response to those input messages. Each of these steps must be performed 
somewhere in the C+-(- code produced. The developer explicitly defines the way state data is changed in 
response to an input message of a given type, as well as ensuring that outgoing messages have the proper 
payload. The remaining functions of actually directing an input message to the proper node or nodes and 
forwarding the output messages to the intended recipients is accomplished behind the scenes in the C-H- 
code produced by parsing the SODL process files. 


104 









7.2.3.1. Specifying non-array output messages 


{import std {<iostream>} } 

{import message {Generic, StartSimulation} } 

{ 

process: Simple 

{ 

mode: start 

{ 

node:proc[StartSimulation:strt][Generic:om=>(me;):(0.0)] 
{start, set Active(false);} 

} 



mode:nm 

{ 

node:proc[Generic:im][Generic:om=>(me;):(getTimeO+10)] 
{ om.setTX(getTimeO < 100.0);} 

} 


Figure 7-13 Simple.proc sample mode & subordinate nodes 


namespace sodl 

{ 

class Simple : public Process 

{ 

public: 

ProcessMode run; 

ProcessMode start; 

protected: 

Simple(ulong, ulong, ptype); 
public: 

Simple(ulong); 

virtual void receiver(Message& msg); 
virtual void setrirfte(double t); 

public: 

virtual void runproc(Generic in, GenericSi out)', 
virtual void startprociStartSimulation strt, Generic& om); 

protected: 

virtual void setupSimplerunproc{Generic& in, Generic& out); 
virtual void setupSimplestartproc(StartSimulation& strt, Generic& om); 

}; 

} 


Figure 7-14 Simple.h C++ relevant components of header file for Simple.proc 














namespace sodl 

{ 

void Simple::setTime(double t) 

{ 

Process: :setTime(t); 

run.setTime(t); 

start.setTime(t); 

} 

void Simple: :receiver(sodl::Message& msg) 

{ 

if (run.isActiveO) 

{ 

if (msg.isType(SMT_Generic)) 

{ 

Generic om(me); 

Generic& im = dynamic_cast<Generic&>(msg); 
runproc(im, om); 
setupSimplerunproc(im, om); 
getController0.transmit(om); 

} 


} 

if (start.isActiveO) 

{ 

if (msg.isType(SMT_StartSimulation)) 

{ 

Generic om(me); 

StartSimulation& strt = dynamic_cast<StartSimulation&>(msg); 
startproc(strt, om); 
setupSimplestartproc(strt, om); 
getControllerO.transmit(om); 

} 

} 

} 

void Simple: :runproc(Generic im, Generic& om) { om.setTX(++coimt < 100);} 

void Simple::startproc(StartSimulation strt, Generic& om) { start.setActive(false);} 

void Simple::setupSimplerunproc(Generic& im, Generic& om) 

{if (!om.isPreemptedO) om.addDest(me); } 

void Simple::setupSimplestartproc(StartSimulation& strt, Generic& om) 

{ 

if (lom.isPreemptedO) om.addDest(me); 
if (lom.timeOverrideO) om.setTime(O.O); 

} 

} 


Figure 7-15 Simple.cxx, message handling implementation 


106 











Figures 7-13 through 7-15 depict the C-i-i- code generated that performs the functions associated with 
message handling within a process construct. We first note that each mode is declared as 
sodliiProcessMode instances. The first portion of the message cycle is for each process to receive the 
message and check to see if any of the active nodes can handle it. This is performed in the receive method. 

Upon receipt of an inbound message, the process controller calls the receiver method for the process. From 
there, each of the process modes is polled for its active status. All active modes will get a chance to 
process the incoming message, provided they have nodes capable of processing messages of the input 
message type. Upon entry into a code block representing an active mode, each node checks to see if it can 
accept a message of the given type or of a derived type. Upon finding a node that can actually handle the 
message, msg is cast to the proper type and output messages are declared. At this point, a properly typed 
copy of the input message and a collection of references to output messages are passed to the method given 
by the name Mode-NameNode-Name which contains the user specified code for manipulating the internal 
state data of the process, as well loading the output message payloads. This code is copied directly from 
the user code that was in the input process construct declaration. 

Once the user-defined portion of handling the message has been performed, final output message setup is 
handled in method setupProcess-NameMode-NameNodeName. This involves adding any default 
destinations and time stamp values specified in the output message declaration. This method checks 
internal flag values to ensure that code in the user-defined portion of the message handling did not override 
these default values. If there was no override, the default destinations are added, and the message time 
stamp is set to its default value, if specified (if it was not specified, either in the node header or body, then 
Equation 6-1 is invoked). 

The setTime method may at first glance seem to be out of place and unnecessary. However, when 
sodliiProcessMode variables change their active flag, this change cannot take place immediately, since 
there may be additional pending messages with the same time stamp value as the current message being 
processed. Since the order of these identically time stamped messages is not defined, we require that they 
be processed in a well-defined manner regardless of the order in which they were actually received. Thus, 
any changes to the mode active flags cannot take effect until the process time stamp advances. By 


107 








overloading the setTime method, we can set the time stamp value in the sodh'.ProcessMode instances in a 
manner that allows consistent message processing. 


7.2.3.2. Output message arrays 


{import process {Vertex2D} } 
{import message {SetVertex2D } } 


process:hierarchy 

{ 

Vertex2D:vert[4]; 

mode:Default 

{ 

node: start 

[StartSimulation:in] 

[SetVertex2D:out_sv[4]=>(vert[@];):(@*(-le-9))] { ... } 

} } } 


Figure 7-16 hierarchy.proc with an output array of messages 


void hierarchy: :receiver(Message& msg) 

{ 

if (Default.isActiveO) 

{ 

if (msg.isType(SMT_StartSimulation)) 

{ 

vector<SetVertex2D> out_sv; 

for (int out_sv_indexa = 0; out_sv_indexa < 4; ++out_sv_indexa) out_sv.push_back(me); 

StartSimulation& in = dynamic_cast<StartSimulation&>(msg); 

Defaultstart(in, out sv); 
setuphierarchyDefaultstart(in, out sv); 

for (int out sv indexb = 0; out sv indexb < out sv.sizeQ; ++out_sv_indexb) 
getController0.transmit(out_sv[out sv_indexb]); } } } 

void hierarchy::Defaultstart(StartSimulation in, vector<SetVertex2D>& out sv) { ... } 

void hierarchy: :setuphierarchyDefaultstart( StartSimulation& in, vector<SetVertex2D>& out sv) 

{ 

for (ulong out_svindex=0; out_svindex<out_sv.sizeO; -H-out_svindex) 

{ 

if (!out_sv[out_svmdex].isPreemptedO) out_sv[out_svindex].addDest(vert[out_svindex]); 
if (!out_sv[out_svindex].timeOverride0) out sv[out_svindex].setTime( (out svindex*(-le-9)) ); 
}} 


Figure 7-17 Relevant portions of hierarchy.cxx implementing message arrays 


108 











As with other type of arrays within the SODL language, certain aspects of output message arrays can be 
initialized using the macro substitutions described in Chapter 6. Figures 7-16 and 7-17 depict the C-(-+ code 
resulting from processing a SODL construct with an output message array. 

Here we again see the three relevant methods. The first is the receiver method sets up the output messages, 
though this time it is in the form of an array. This array along with the input message is passed to the user- 
defined message handler where the message array elements are manipulated. The messages are then passed 
to the setup routine to undergo final addressing and time stamping. Note again the macro substitution of 
‘out_svindex’ for the in the original node declaration. 


109 







110 



Chapter 8. GLUT-Based User interface 

8.1. Output concerns in an optimistic simulator 

Optimistic simulation requires that all process states be recoverable in the event of a rollback. While this 
may be useful for actually performing the calculations associated with the simulation, it has its drawback in 
the vital area of I/O operations. 

Output operations in particular are inherently irrevocable, and therefore must be handled differently from 
the actual simulation system. For simple console or file output, the SODL system provides the 
fossilCollect method. Suppose the LPj(/s) process state is being fossil collected. Then tj<GVT meaning 
that all remaining messages in the simulation system have time stamps strictly greater than Therefore, 
any output file or console output we would have liked to perform in LPi(Q can be safely performed during 
fossil collection without the risk of it ever becoming invalid. 

The SODL graphics sub-system, known as the GLUT View Manager (GVM) uses a buffering mechanism 
to store pending changes to a scene graph. Those changes are made during fossil collection, and can be 
rolled back if the need to do so arises before actually being committed to the output device. 

8.2. Overview of the SODUGVM subsystem 



Figure 8-1 Depiction of relationship between SODL processes and GVM objects 

The means by which graphical displays are generated within the SODL system is a mixture of SODL 
process and message constructs, and a collection of C-t-i- classes that make up the GVM. The SODL 


111 



















process and GVM object instances can be thought of as mirror images of each other. SODL provides a 
collection of process and message constructs that allow programmers to specify their intent with regards to 
graphieal output. This intent is then communicated to the GVM portion to actually implement those 
requests in a manner that is consistent with the notions of optimistie simulation, namely that output not 
occur until it is certain that it cannot be revoked. 

Figure 8-1 depicts in somewhat more detail the notion of this SODL process-GVM object association. 
Each SODL process sends messages to any view processes in which it has been registered when it receives 
a request to change one of its parameters. The SODL view process then forwards to its GVM view object a 
request to update the actual GVM object responsible for rendering the object in the display. During fossil 
collection, this change is finalized. The next time the scene is actually drawn, the change is reflected on the 
screen. 



Figure 8-2 Message propagation for scene update requests 


Figure 8-2 shows how updates are made to the scenes actually rendered. In order to see a change in the 
rendered scene, a message must be sent to the SODL process being changed corresponding to the GVM 
object being ehanged. This message is buffered and sorted according to its time stamp, possibly causing a 
rollback. When it is delivered, the reeeiving process will generate a message for each of the views in whieh 
it has a rendered object corresponding to itself to inform those views of the ehange. Those messages must 
also be transmitted, buffered and sorted before delivery to the view process responsible for a particular 


112 









output display. Each view then schedules the change to occur in the actual rendered scene during fossil 
collection. 

One drawback of this approach is that it is a bit slow. However, given the constraints of the SODL system, 
and ill-fated attempts at other approaches, this seemed the best way to solve the problem of providing a 
flexible output capability that could be, from the programmer’s perspective, distributed across a collection 
of host machines. 

Although not implemented in the current SODL system release, this approach provides a way for users to 
communicate with processes corresponding to objects in the rendered scene. There is considerable work 
remaining to get this to happen, but it is a natural extension of that already done in this area. 

8.2.1. SODL/GVM scene graphs 

At the core of the GVM sub-system is the scene graph. The notion of a scene graph is described in more 
detail in (Foley 1996), but we will provide some basics here. In 2 and 3 dimensional graphics output, 
polygons are displayed to the screen, after having been manipulated by an affine transformation. These 
affine transformations are actually implemented as matrices that when multiplied together cause a 
succession of transformations to take place". For example let v be a vector and let A] and Aj be affine 
transformations. Then the process of applying Ai to v then Ai to that result can be expressed as the 
multiplication sequence A 2 Ai V. These affine transformations can perform a number of functions, but 
among the most common are translation, rotation, and scaling. When combined in different orders, these 
transformations can be used to manipulate polygon vertices in very complex manners. 

The GVM scene graph can be thought of as a tree structure. Internal nodes are affine transformations, and 
leaves are polygons or other shapes. The affine transformation at any point in the graph is the product of 
the affine transformations in all of the ancestor nodes up to the root. When a given polygon is displayed to 
the screen, the affine transformation applied to its vertices is the product of all those transformations in the 
ancestor nodes. 


113 







An example of this relationship is depicted in Figure 8-3. Each internal node in the tree in Figure 8-3 
represent an affine transformation labeled A^.. This affine transformation is appended to the list of 
transformations from the parent nodes, and is explicitly shown as the second line in each of the internal 
nodes. Each vertex in polygon Py is then multiplied by the aggregate affine transformation to get its final 
location in the rendered scene. 



Figure 8-3 Sample scene graph; Affine transformations are A^, polygons are Py 


What this accomplishes is that objects displayed to the graphics device can have a hierarchy of sorts 
allowing individual components of a larger structure to be moved around with that larger structure. If the 
smaller components are capable of moving with respect to the larger structme, adjusting the affine 
transformation parameters affecting only the smaller component will cause such movements to appear in 
the final rendered scene. 


The complication that GVM must contend with is that a simulation system may periodically send 
instructions to the GVM viewer to change some aspect of the scene graph. This implies that each of the 
objects associated with a specific view need to be able to receive messages, making them like processes. It 
might also be useful to be able to have a single SODL object correspond to objects displayed in multiple 
views of the same scene.'^ Therefore, a change in one affine transformation node could affect scenes 


" Matrices are by definition linear transformations. It is possible, by adding an additional dimension to get 
them to mimic affine transformations in the lower dimension. A thorough description is available in any 
reference on computer graphics, but notably (Foley 1996) 

This would be particularly usefiil in a distributed implementation of the SODL system, allowing the 
scene to be displayed independently on different host computers in the distributed simulation. 


114 
































rendered in more than one view. Since the intent was to provide a distributed simulation capability, direct 
manipulation of a view’s scene graph cannot occur, since, in such distributed implementations, the scene 
graph may not reside locally. 

What GVM has done is to have two separate but identical scene graphs. The first is the one that can send 
and receive messages, and is therefore actually implemented as a collection of SODL process and message 
files. Programmers making use of it send messages to these processes which forward requests to modify 
the scene graph in the actual view where the scene is rendered; this is the second scene graph. The SODL 
processes must retain all of the state information that is in the rendered scene, so that if a graphics object is 
added to a new view, it can inform that view as to its state and any subordinate objects it may own. 



GVM rendered scene - View C 

Figure 8-4 Portions of SODL scene graph displayed on multiple views 


Figure 8-4 provides a pictorial representation of the GVM architecture. Depicted here is the SODL portion 
of the scene graph at the center of the figure. Portions of the SODL scene graph are replicated on different 
rendered scenes in different views. An update in one of the SODL objects will propagate to all of the 
scenes in which the object has corresponding rendered objects. The end result is that programmer does not 
need to manage the details of the individual scenes rendered, but ean instead manage where different 
objects are viewed, and the relationship between the various rendered objects. 


115 






8.3. SODUGVM usage 

To actually make use of the SODL/GVM system, programmers need only declare in their process 
constructs a process: View derived instance, along with the affine transformation nodes and shapes to 
actually display. 

There are two types of process: View constructs defined, process: V/ew2D for displaying two-dimensional 
information and process: VicwJD for three-dimensional displays. Programmers should use either of these 
two process constructs, or derive new ones based upon their specific requirements. The view can be 
considered the root node, and contains the affine transformation for the view orientation and position. This 
viewing transformation is then applied to all subordinate nodes. 

Each view can have a collection of subordinate nodes. These are either process:iVoi/c2D or 
pTocess:Node3D instances, as appropriate. The only real difference between them is the size of the various 
arrays they have governing the affine transformations they represent. They have the ability to govern 
translation, scaling, and rotation. The center of rotation and center of scaling can also be specified. To add 
a node with process handle (Af„, I„) to a view with process handle (Ny, ly), a user sends a 
message:AddNode2D or message'AddNodeSD (both derived from the message:Aifi//Voi/e construct) as 
appropriate, to (Ny, ly). The messageAddNode derived messages have the ability to add multiple nodes. 
To load the message, call its inethod:a<fdf(public; void; process:^) method to add a sod/::process value to 
the list of them. These added values are the proeess handles of the nodes we would like to add as 
subordinates to the destination. 

A process:iVodc instance can reside on multiple views. To do this, simply send one of the 
message AddNode derived constructs to each view instance where the process:/Vo</e is intended to reside. 

The system provides the capability to, not only add process:fVoi/e instances to a process: V/cw, but also to 
other process:Aodc instances. We can use the message AddNode derived constructs for this as well, 
though we send it to the processiNode construct instance that will be getting the new subordinate 
process:/Voi/c instances. Any view in which the receiving node has been registered will automatically be 
informed about the addition of the new subordinate. 


116 






{import process {View3D, Node3D, Dodecahedron} } 

(import message {AddNode3D, AddShape3D, StartSimuIation} } 

{ 

processidode 

{ 

View3D:view; 

Node3D:root; 

Dodecahedron:shape; 

mode:Default 

{ 

node;start sim [StartSimulation:strt] // Bootstrap message 

[AddNode3D;an=>(view;), // Add root to view 

AddShape3D:as=>(root;)] // Add shape to root 

{ 

an.add(root); // Set the root node as a subordinate of the view 
as.add(shape); // Set the shape as a subordinate of the root node 

} 

} 

} 

} _ 

Figure 8-5 dode.proc, a simple view with a single afflne transformation node and shape 

There are several predefined shapes for the three-dimensional views. They include process:Cone, 

process:Cuhe, processiDodecahedron, processtlcosahedron, process:Octahedron, process:Polygon3D, 

process:Sphere, process:Tetrahedron, and process:Torus. All of these are derived from the 

process:Shape3D construct. With the exception of the process:Po/ygo/i3D, all of them are fixed in terms 

of their vertices and edges. The process:Pofygo«JD construct can have subordinate process: Vcrtex3£) 

constructs, the position of which can be changed via message passing. There are some limitations to how 

these vertices may be used. Specifically, in certain OpenGL drawing modes, the locations of all of the 

vertices must be coplanar and form a convex polygon. Since all polygons can be decomposed into a 

collection of convex polygons, this is more of a nuisance than a real limitation of the system. For two- 

dimensional views, the only predefined construct is the process:Po(ygo«2D, which has the same 

limitations as the pTocess:Polygon3D, and restricts ownership to only process: Vie/tcx2D. 


// Display to this view 
// Node for the display 
// A dodecahedron to view 


117 










To add a shape to a process:Node, send the appropriate message'AddShape derived construct (either 
messag/eAddShape2D or mtssage:AddShape3D) to the node to which the shapes will be subordinated. 
Again, the addition of this new shape will be forwarded to all views in which the node has been registered. 



Figure 8-6 Output of dode.proc 

Figure 8-5 has code for displaying a simple geometric shape in a process: VitVw5Z) window. Figure 8-6 
shows the output for the program in Figure 8-5. 

8.4. SODUGVM Architecture 

Detailed descriptions of both the SODL and GVM portions of the display subsystem are documented in 
Appendix B, sections 3 and 4 respectively. We provide a somewhat broader overview here to help 
programmers wishing to use the system to do so effectively. 

As stated earlier, for each SODL process used to control display content, there is an associated GVM class 
to actually manipulate and display the scene graph. The programmer only needs to send messages to the 
SODL side of the system. The updates to the displayed scene are handled at first within the SODL system, 
and then passed to the GVM portion where these update requests are buffered until fossil collection. 
During fossil collection, the requests are used to actually perform updates to the scene. 


118 



8.4.1. SODL view controllers, the process:View 

From the SODL perspective, a single process: instance controls exactly one GLUT view port. All 

objects displayed in the window, down to the individual vertices making up polygons, must be registered 
with the process: View instance associated with the GLUT window displaying them. All such objects are 
derived from the process:06/ect construct. This registration takes two forms. The first is to have a 
processiNode instance be added to a view’s list of root nodes via the messages derived from 
messageuiddNode. The second is to have a parent object be registered with a new view, in which case all 
subordinate objects in the scene graph are also registered. 

SODL views come in two flavors, process:VicwiD and process:VieH’3D instances. Programmers should 
not directly instantiate a process: View, since much of the fimctionality associated with manipulating views 
is implemented only in these two derived classes. The processiNode instances should be instantiated as 
either process:iVoife2D or process:iVo</c3D for essentially the same reason, process:Vicw2D and 
process:iVo</c2D instances are used for controlling two-dimensional display data, while process: V/ew3i) 
and pTOcess:Node3D instances are for three-dimensional displays. The reason for this separation is that 
OpenGL can perform some optimizations for 2D displays making display of such data somewhat faster 
than it would otherwise be. 

Figure 8-7 depicts how this registration is actually performed. The first step in the process comes when a 
request is made of a process: View instance to add a new root node to its list of them. The view maintains a 
list of known process handles, and checks to see if the handle for this process:iVoife instance is among 
them. If not, the process:View schedules the creation of a corresponding gvmiiNode instance with the 
gvmizView instance it maintains. This returns a gvmjndex value that can be used to identify that specific 
gvmr.Node instance in the future. With this gvmjndex in hand, the process:View sends a 
messagerAdrfView to the node it has just added. Included in the payload of the message is the gvm_index 
value. Any messages the process:iVo</e sends to that specific view must contain the proper gvmjndex 
value so that instructions can be properly forwarded to the node’s counterpart in the rendered scene graph. 
Therefore, the node maintains an associative memory with the handle for the process: View as a key and the 
gvmjndex as its value and uses the index in any future communications with that view. Not depicted in 


119 







Figure 8-7 is the message process:Node sends back to the view. These messages notify the view as to the 
current state values of the node so that the proper values are relayed to the gvm::Node instance when it is 
created. 



(a) View #1 receives request to add Node #1 as a root node. Sends AddView #1 to Node 
#1 and schedules creation of a corresponding node with its gvmr.View instance 



(b) Node #1 receives request to add Shape #1 and sends Register Shape #1 to View #1 
which schedules with its gvmr.View creation of a corresponding shape and 
establishment of subordinate relationship; sends an Add View #1 message to Shape U1 


Figure 8-7 SODL side messaging 

When a new shape is added to the list of subordinate objects for the node, essentially the same procedure is 
repeated. This time, the node sends a messageiRegisterShape to the process: View instance along with the 
process handle of the shape to add. Again, the process: V/ch’ will check its list of known processes, and if 
the handle is not yet registered, it will schedule a gvmiiCreateObject event with its gvmiiView to actually 
create the new shape instance. It also will schedule an update in the scene graph to add the newly created 
gvm::Shape instance as a subordinate shape to the gvmr.Node instance it created earlier. The 
process:View instance will then send its message^4dfdView to the new shape, along with the gvmjndex 
returned from scheduling the gvmiiCreateObject. The shape retains the gvmjndex for use in any future 
communications with the view. Again, not pictured is the phase where the newly registered shape relays 
back to the process: View its state data so that additional gvmiiMessage instance can be scheduled with the 
gvmiiView to provide current state information to the scene graph. 


120 














The same basic idea is used any time the state of a processrOty'ect derived construct instance changes its 
state. It will forward a message to the views with which it has been registered, along with the gvmjndex 
value for that view, notifying it of the change in its state. The process rFiew will schedule messages with 
the gvm::View notifying it of the change in the scene graph state. 


8.4.2. Messaging on the GVM side 



scheduled in the gvnt::View instance by adding the event to 
the front of the deque. 



(b) A rollback occurred restoring the state with time stamp 
8.6. Events schedule for 11.0 and 13.5 were removed from 
the deque. 





VO 

3.0 



o6 

1 

1 


KB 

eg 

a 

Co 


■ 

V. 

1 

s 

1 

gvm 

5 


(c) Fossil collection for time 1.0 was performed. The 
specified objects were created and the scheduled events 
were removed from the back of the deque. 


Figure 8-8 Scheduling, revoking and processing messages in the GVM message queue 
When a processiViCH’ construct instance receives a message that one of the process:Object instance 
registered with it has had a state change, or when new process instances are being initially 


121 



























registered with the processiF/ew instance, these changes must be reflected in the scene graphs that are used 
to actually generate imagery to graphics output devices. However, each of these requests must occur in the 
proper time stamp order, and, since there is no guarantee that they are actually received in the proper order 
until fossil collection can be performed, it is only at that time that these changes to the scene graph can 
actually be committed. 


There are three phases to the GVM side of the graphics subsystem as depicted in Figure 8-8. 


1. Scheduling. All events are time stamped and, by virtue of the optimistic simulation 
mechanism the SODL system uses, are inserted into the front of a double-ended queue 
(also called a deque) in time stamp order. 

2. Rollback. Since the messages are inserted into the deque in time stamp order, the 
messages can be removed from the front of the deque until the front most element has a 
time stamp not greater than the rollback time. Any events scheduled for a later time will, 
by definition be of a later time stamp value, thus the chronology of scheduled scene graph 
adjustments remains intact. 

3. Fossil collection. All events with time stamps at or before the fossil collection time can 
be processed, allowing the scene graph updates to be made without the possibility of a 
rollback making those changes invalid. 


122 







Chapter 9. SODL Sample Programs 

The main advantage of the SODL system is that it provides developers with the means to quickly and 
succinctly define the behavior of simulation objects without being bogged down with the details normally 
associated with simulation systems. As a means of providing some insight into how the SODL system 
works, and how it may be used to rapidly develop complex simulation systems, this chapter will explain in 
detail how the various demonstrations that come with the SODL distribution work and what they are 
intended to do. 

Many of these samples produce output, but for brevity, the code segments listed here generally do not show 
how this out is actually produced. Complete listings of all of the demos and support code (if necessary) are 
provided in Appendix C. 

9.1. Single Node Textual Simulations 

These simulation are early demonstrations intended to test various aspects of the SODL system when all 
the simulation objects residing on the same engine. There is, therefore, no chance of ever getting a 
rollback, and the fossil collection is trivially completed. They are not intended to simulate anything in 
particular, just provide some basic samples for testing and illustration purposes. 

9.1.1. Simplel 

The Simplel simulation, depicted in Figure 9-1, with constructs listed in Figure 9-2. 


messagerCenenc 



Figure 9-1 Schematic of Simplel 

The purpose of this simulation was to test the basic message passing mechanism in the simplest possible 
configuration. A process:Simpfe instance upon start up (i.e. receipt of the messageiStartSimulation 


123 








instance at time -1) will send a messageiGeneric with time stamp 0.0 to itself. Every time it receives a 
message:Generic, it transmits another one to itself, and increments a counter. Upon receiving 100 of these 
message:Generic instances, the lOU* instance has its transmission flag turned off, preventing actual 
transmission of the message. 


{message:Generic;} 


(a) Generic.msg 


{import message {Generic, StartSimulation} } 

{import std {<iostream>} } 

{ 

process: Simple 

{ 

mt:count(-l); //Hit counter 

method:mit(public; void;) { std::cout.precision(15); } 

method;fossilCollect(public; void;) 

{ 

if (getXimeO >= 0) // If this is a good time to produce output? 

{ 

if (getXimeO = 0) std::cout« me « starting"; 

else std::cout« me « “ « count« " @ time " « getXimeO; 

std::cout« std::endl; 

} 

} 

mode: start 

{ 

node:proc[StartSimulation:strt][Generic:om=>(me;):(0.0)] 

{ start.setActive(false); } } 


mode:run 

{ 

node:proc[Generic:im][Generic:om=>(me;)] 

{ om.setXX(-H-count < 100);} } } } 

(b) Simple.proc 

Figure 9-2 Xhe Simplel Simulation 

Xhe process starts out with all of the modes active. After the initial bootstrapping message is received, 
laodf.start is deactivated so that the nodes within it are no longer polled for the remaining messages. All 
remaining messages are handled in mode:r«n. 


124 











After each message is received, during the fossil collection phase, the process produces textual output to 
the screen to inform the user of the status of the simulation. Since there is only one process, and the 
possibility of a rollback is exactly 0, this output could have been handled in the node receiving the 
message: Generic instance. It is, instead placed in the meihoA’.fossilCollect to maintain the intent that 
output only be performed during fossil collection. 

9.1.2. Simple2 

Simple2 is in essence the same as Simple 1, but was developed to test for memory leaks in the basic 
scenario. Its primary difference is that it does not end until the user manually terminates the run. To limit 
the amount of output sent to the screen, it produces output once in 10,000 iterations, rather than after each 
iteration as in Simple 1. 

The functionality of Simple! is depicted, like that of Simple 1, in Figure 9-1, and it uses the same definition 
of message:Gcncric, depicted in figure 9-2. The code running the process is depicted in Figure 9-3. 

{import message {StartSimulation, Generic} } 

(import std {<iostream>} } 

{ 

process: Simple 

{ 

long:count(-l); // Counter 

modeistart 

{ 

node:proc[StartSimulation:in][Generic:out=>(me;):(0.0)] 

{ start, setActive(false);} } 

mode:run 

{ 

node:proc[Generic:in][Generic:out=>(me;)] { count-H-;} } } } 

Figure 9-3 Process construct for the Simple! simulator 

9.1.3. SimpleS 

The SimpleS demo is an example of a two-process simulation, and is depicted in Figure 9-4, with code 
displayed in Figure 9-5. The processiSimple declares a processiChild instance. Upon bootstrapping and 
some initialization to inform the child process who its parent is, messageiGeneric instances are passed 
back and forth between the two process instances ad infinitum. 


125 









message:5'e/'PareM/ 


message: G^nmc 


Figure 9-4 Message transport in Simple3 


{import message (StartSimulation, Generic, SetParent} } 

(import process {Child} } 

{ 

process:Simple 

{ 

long:count(-l); //Counter 

Childxhild; // Child process 

mode: start 

{ 

node:start[StartSimulation:strt] 

[Generic:out=>(child;):(0.0), SetParent:sp=>(child;)] 

{start.setActive(false);} } 

mode:run { node:bounce[Generic:in][Generic:out=>(chiId;)] { count-H-;} } } } 


(a) Simple.proc 


{import message {SetParent, Generic} } 

{ 

processiChild 

{ 

process iparent; // Handle to the parent process 

long:count(-l); //Counter 

mode;start 

{ 

node;setParent[SetParent:in][] 

{ 

parent = in.getSourceO; // Set the parent reference 

start, setActive(false); } } // Turn this mode off 

mode:run 

{ 

node:bounce[Generic:in][Generic:out=>(parent;)] { count++;} } } } 


(b) Child.proc 


Figure 9-5 Simple3 process construct declarations 


126 












The message:Genmc is as defined in Figure 9-2, and va.cs.sage\SetParent is essentially the same, though 
its type setting ensures that when it is delivered to the processrCAiW instance, the sender can be retrieved 
and saved for future reference. 


9.1.4. Ping 

{import message (Generic, StartSimulation} } 

(import process (Pong) } 

{ 

process:Ping 

{ 

int:count(-1); // Count of the number of messages received 

Pong:pong; // Something to send a message to 


mode;start 

{ 

node:start[StartSimulation;strt] [Generic:out=>(me;):(0.0)] 

{ start.setActive(false); } } 

mode:run 

{ 

node:ponger[Generic:in][Generic:out=>(pong;)] 

( out.setTXf ++count < 20 );} } } } 

(a) Ping.proc 

(import message {Generic} } 

{ 

processiPong 

{ 

int;count(-l); // How many times have we received a message 

mode:Default 

{ 

node:pinger[Generic:in][Generic:out=>(m.getSource();)] 

( out.setTX(-H-count<20);} } } } 

(b) Pong.proc 

Figure 9-6 Source code for the Ping demo 

The Ping demo is similar to SimpleS. Figure 9-7 depicts how messages are passed between processes, and 
Figure 9-6 is the code for the Ping simulator. The main difference is that instead of retaining a reference to 
the sending process of message:Ge«mc instances, the follow-on message is simply returned to the sender. 
As such, there is no message:SetPare/it to inform the subordinate process of its parent as there is in 
SimpleS. In addition, after each process has received 20 messageiGenerrc instances, the simulation stops 


127 










automatically. This was an early demonstration to test the basic message passing capability of the SODL 


system. 



message: Ge/ieWc 


Figure 9-7 Ping message transport 


9.1.5. Ringl 



Figure 9-8 Ringl Token ring using a subscription service 


One of the original intentions of the SODL project was to provide a subscription service, whereby 
processes eould sign up with a subscription and any message sent to the subscription would automatically 
be forwarded to its membership list. In the end, this proved to be too cumbersome to adequately implement 
in conjunction with the time warp algorithm, and this feature was dropped from the final system. 


128 




































Ringl, and Ring2 are two demos that originally used these subscriptions. They have been re-implemented, 
this time with the subscription defined as a process. Ringl consists of one controller process, called 
processtRing, a subscription process called processtSubscription, and 10 ring members, declared in the 
processiRingMember construct. 


During bootstrapping and initialization in the Ringl demo, each ring member is informed as to the process 
handle of the subscription process. With this handle available, the ring members then request to subscribe 
to the subscription. As the subscription receives each request, it replies with a subscription index. 
Messages intended to be forwarded by the subscription can either be sent to individual subscribers, by 
specifying the index in the incoming message, or to all subscribers, when the recipient index is not 
specified. 


{import process {RingMember, Subscription} } 

{import message {Generic, StartSimulation, Setup, ReportSize} } 


process:Ring 

{ 

RingMember:ring[ 10]; // Ring of elements 

Subscription-.sub; // Subscription list 

mode:Default 

{ 

node:start[StartSimulation;strt] 

[Setup:s=>(ring;), 

ReportSize;r=>(sub;);(-0.5), 

Generic:out=>(ring;):(0.0)] 

{ out.set(0); s.set(sub);} } } } 


Figure 9-9 Ring.proc; The parent process for the Ringl simulation sample 

Once this portion of the setup is completed, the process:Ring instance requests that the subscription 
provide the subscriber count to all of the subscribers. Once this is completed, the real simulation starts. 


129 








{import message {Subscribe, ReportSize, Reportindex, Generic} } 

{ 

process:Subscription 

{ 

process:subscribers[]; // List of subscribers 

mode:Default 

{ node:subscribe[Subscribe:m][ReportIndex:out=>(in.getSourceO;)] 
{ out.set(subscribers.sizeO); // Index value to report back 
subscribers.push_back(in.getSourceO);} 

node:reportSize[ReportSize:in][ReportSize:out=>(subscribers;)] 

{ out.set(subscribers.sizeO);} 

node: forward [Generic: in] [Generic: out] 

{ if (in.getO < subscribers.sizeQ) 

out.addDest(subscribers[in.getO]); 

else 

out.addDest(subscribers); 
out.set(in.getO);} } } } 

(a) Subscription.proc 

{import message {Generic, Setup, ReportSize, Reportindex, Subscribe} } 

{ 

process:RingMember 

{ 

process:sub; // Handle to the subscription process 
long:count(-l); // A simple counter 
long;next(0); // Index of next instance 
long:index(0); // Index of this instance 


mode:Default 

{ 

node;setup[Setup:in][Subscribe:out=>(sub;)] { sub=in.getO;} 
node:setIndex[ReportIndex:in][] { index=in.getO; } 
node:setNext[ReportSize:in][] { next = (index+1) % in.getQ;} 

node:run[Generic:in][Generic:out=> (sub;) ] 

{ 

out.set(next); // Set index of the eventual destination 
out.setTX(count++<10); } } } } 

(b) RingMember.proc 

Figure 9-10 Processes controlling (a) the subscription service and (b) individual ring members. 


130 












The notion of Ringl is to perform a token ring simulation whereby a message is sent to the first element in 
the ring, which sends a message to the second, and so on until it gets to the last element. This last element 
will then forward a message back to the first. Rather than relying upon storing the handle of the next 
process, each process:/?ingMeni6er instance instead retains its index so it can figure out which subscriber 
is next to get the message. The token, in this case another message;G£/imc instance is forwarded to 
successive elements until it has made ten complete circuits of the ring. This final phase of the simulation is 
depicted in Figures 9-8. Figures 9-9 and 9-10 contain the source code for the process constructs in the 
Ringl demonstration. 

The result is that each of the process'.RingMember instances takes a turn receiving the token and passing it 
to the next subscriber. No two ring members have a token at the same simulation time, so the sequence of 
token passing is sequential, from one ring member to the next. 

9.1.6. Ring2 



131 





















Ring2 is essentially the same as Ringl, except with regards to one minor change in the process:/?i«g 
construct declaration. This change broadcasts the initial laessageiGeneric instance to all of the subscribers 
listed with the processiSubscription instance. 


Figure 9-11 depicts the message flow following the setup portion of the simulation. Figure 9-12 shows the 
code change in Ring.proc to result in this change. Note that we removed the outset(0) in node:stort of 
Figure 9-10. This directs the subscription to broadcast the messageiGeneric instance to all of its 
subscribers. 


{import process (RingMember, Subscription} } 

{import message {Generic, StartSimulation, Setup, ReportSize} } 


process:Ring 

{ 

RingMember:ring[ 10]; // Ring of elements 

Subscription:sub; // Subscription list 

mode:Default 

{ 

node:start[StartSimulation:strt] 

[Setup:s=>(ring;), 

ReportSize:r=>(sub;):(-0.5), 
Generic:out=>(ring;):(0.0)] 

{ s.set(sub); } } } } 

Figure 9-12 Ring.proc; The parent process for the Ringl simulation sample 


9.1.7. Brigade2 

The Brigade2 simulates a brigade of soldiers performing some unspecified task. The brigade is broken into 
four battalions, which are in turn broken into four companies each. Each company is broken into four 
platoons, and these platoons are each broken into four squads. Finally, each squad is composed of ten 
soldiers. A quick computation reveals that there are, 2,901 units the simulation system must managed. 
This hierarchy is illustrated in Figure 9-13. 


During a setup phase, each unit sends a message to each of its subordinate units to establish with that 
subordinate that the owning unit is its parent. Communication occurs only from one level to the next. That 
is, a unit can communicate only with its parent unit and its immediate subordinates. 










Figure 9-13 Process ownership in Brigade2 demonstration 



Figure 9-14 Communication between parent and subordinate units in Brigade2 demo 

When the simulation actually gets under way, the brigade issues an order with different randomly 
determined time stamps to each of its subordinate battalions. These battalions then issue orders to each of 
their subordinate companies, again a random time stamps. This continues all the way down to the 
individual soldier units. After a period of time, the soldier will report to its parent unit that it has completed 
its task. When all of the soldiers in a particular squad have informed it that they have completed their 
assigned tasks, the squad will report this back to its parent platoon, and so on. The simulation ends when 
the brigade has completed its task. This is illustrated in Figure 9-14. 

The bulk of the work is done in the process:(7ni^ construct. All of the process constructs in the Brigade2 
demo are derived from process:t/niY, which is summarized in Figure 9-15. processzSoldier is shown in 


133 

















































Figure 9-16 since it is fundamentally different from other units, in that it does not have any subordinate 


units. 


{import message {report, order, set_parent, StartSimulation} } 

{ 

process :unit 

{ 

intrinstance; // Subordinate instance of this unit 

int:sub_count; // # of subordinates Squad/soldier needs to change to 10/1 

process:parent; //Parent unit 

process:subs[]; // Handles to subordinate units 

double:sub_times[]; // Timestamp for subordinate units 

mode: start 

{ 

node:setParent[set_parent:in][set_parent:out[]=>(subs[@];)] 

{ 

// Perform unit & subordinate initialization 
start.setActive(false); // Deactivate start mode 

waiting_fororders.setActive(true); // Activate waitingfororders 

}} 

mode;waiting_for_orders 

{ 

node:startSimulation[StartSimulation:in][] 

{ waiting_for_orders.setActive(false);} 

node:receive[order:ord][order:out[]=>(subs[@];):(sub_times[@])] 

{ 

// Non-soldier instances configure orders to subordinates 
waiting_for_orders.setActive(false); // Not waiting 

working, setActive(true);} } // We are now working 

moderworking 

{ 

node:startSimulation[StartSimulation:in][] {working.setActive(false); } 

node:status[report:in][report:out=>(parent;)] 

{ 

// Check to see if all subordinates are done 
// If not set transmission flag in out to false 

}}}} 

Figure 9-15 Unit.proc; Basic unit construct in the Brigade2 demonstration 


134 










{import process {imit} } 

{import message {order, report} } 

{ 

process:soldier(unit) 

{ 

mode: waitingfororders 

{ 

node:receive[order;ord][report:out=>(me;):(sub_times[0])] 

{ 

// Set report time to some random point in the future. 

}}}} 


Figure 9-16 soldier.proc; Declaration of the processxsoldier construct in the Brigade! demo 

9.2. Multiple Node Textual Simulations 

The multi-node samples were developed to test the Time Warp mechanism for synchronizing nodes in a 
distributed simulation system. They are, like the single node examples in the previous section, rather 
simple, and serve primarily as a test of the underlying system. 


9.2.1. Relayl 

Relay 1 is a simple reflector demo similar to the Ping demo described above. The main difference is that 
the two processes reside on different simulation engine instances. Figiue 9-17 shows a simple schematic of 
the Relayl demo. Figure 9-18 contains the relevant code segments. 


Note that the processrrctey construct is derived from the process:rc/Zcctor, so that the behavior of 
bouncing message:genmc instances back to the sender is inherited in process:retey. Also, in the 
declaration of the processireflector instance in the processrretoy construct, the affinity specification forces 
the subordinate reflector to be instantiated on simulation engine 1. The processiretoy is the root process, 
and is therefore instantiated on simulation engine 0. 



Figure 9-17 Message routing in the Relayl demo 


135 









{import message {generic} } 


processrreflector 

{ 

mt:count(-1); // Coimt of the number of messages received 


mode:Default 

{ 

node:reflect[generic:in][generic:out=>(in.getSourceO;)] 
{ count++;} } } } 


(a) reflector.proc 


{import message {generic, StartSimulation} } 
{import process {reflector} } 


process:relay(reflector) 

{ 

reflectors; 1; // Something to send a message to on simulation engine 1 

modeiDefault 

{ 

node:start[StartSimulation:strt][generic:out=>(r;):(0.0)] { } } } } 


(b) relay.proc 


Figure 9-18 Source code for Relayl process constructs 

9.2.2. Relay2 

Like the Relayl demo, Relay2 has two processes that reflect messages between each other. The 
bootstrapping phase consists primarily of establishing a partnership between the two processes. This 
involves the root process, which is a process;re/ay construct, transmitting a message to its subordinate 
process:reyZecfor instance that the two are partners (i.e. it informs the subordinate that it makes up the other 
half of the system). After this, the root process issues a message with time stamp 0.0 and sends it to the 
subordinate process instance. Upon receipt of a messagetgeneric instance will return another 
message:gcnejTC instance to the sender. It will also send a message:generic instance to itself. Both of 
these messages have time stamps at some random time in the future. There is no termination condition in 
the program, so it theoretically can keep going forever. 


136 










What we end up with in this demonstration is a situation whereby for each message processed, two are 
generated. While this may be fine for abstract analysis, it will eventually cause problems with system 
memory as the number of pending messages grows linearly with the number already processed. 

Figure 9-19 illustrates how the process instances interact. Figure 9-20 is the relevant code in the two 
process constructs. 


messa^eiStartSimulatwn ^ | 

process:re/flj; 

message:;;enmc 

processrr^ector 




—^^ 



rn 


messager^^itmc 




_ 

_1 

1 



messsig,e:generic messageigeneric 


Figure 9-19 Relay2 demo message routing diagram 


{import message {generic, set_partner, StartSimulation} } 

{import process {reflector} } 

{ 

process:relay(reflector) 

{ 

reflectors: 1; // Something on another simulation engine to receive messages 

mode:Default 

{ 

node:start[StartSimulation:strt] 

[generic:out=>(r;):(0.0), set_paitner:sp=>(r;):(-0.9)] 
{p[0]=me;p[l]=r;}}}} 


(a) relay.proc 


{import message {generic, set_partner} } 

{ 

process;reflector 

{ 

process:p[2]; // Pair of processes to send messages to 

mode:Default 

{ 

node:setPartner[set_partner:in][] { p[0]=me; p[l]=in.getSourceO;} 
node :reflect[generic :in] 

[generic:out[2]=>(p[@];):(getTimeO+random.nextDouble(10.0))] 

{•••}}}} 


(b) reflector.proc 

Figure 9-20 Relevant code for the process:relay and process:reflector constructs 


137 














9.2.3. Relays 


{import message {StartSimulation, generic, setup} } 

(import process {child} } 

{ 

process:relay 

{ 

child:children[1000];@%100+l; // Distribute children evenly over 100 engines 

mode:Default 

{ 

node:start[StartSimulation:strt] 

[setup:s=>(children;), generic:out=>(children;):(0.0)] 

{ s.set(children.sizeO, EngineStand::stand.engineCountO-l);} } } } 


(a) relay.proc 


{import message {generic, setup} } 

{ 

processxhild 

{ 

ulongxc; // Number of children 

ulong;ec; // Number of engines 

mode:start 

{ 

node:relay[setup:in][] 

{ 

// Setup cc and ed 
start.setActive(false); } } 

mode:run 

{ 

node:relay[generic:in] 

[generic:out;(getTime()+random.nextDouble(1.0))] 

{ 

ulong di = random.nextlnteger(cc); // Destination index 

out.addDest(process(di%(ec-l)+l, di/ec)); } } } } //Set dest 


(b) child.proc 

Figure 9-21 Relevant code for the Relay3 sample 

The Relays simulation has 1,001 individual processes distributed across 101 different engines. The only 
process on engine 0 is the root process, a process:refoy instance. The remaining 1,000 processes are 
proccssichild instances evenly distributed across all of the remaining 100 engines. During initialization, 
the processirelay informs all processinstances of some basic information regarding the number of 
processes and how they are distributed among the various engines. The simulation starts when the 


138 











processire/aj instance sends a messageigencric instance to all of the processichild instances. Each 
process:c/n7d will, in response to a messagetgenenc input send another message:genertc instance to a 
random processichild instance intended to be processed at some randomly determined time in the future. 
The simulation will run until the user terminates it. While statistically the number of pending messages 
should remain relatively constant, some engines may experience higher volumes of input messages than 
others, thereby unbalancing the system, possibly leading to excessive memory consumption. 

The purpose of this sample was to test the roll back mechanism and to ensure that large numbers of engines 
were possible in terms of memory management issues. Figure 9-21 illustrates provides the relevant code 
segments for the simulation. 

9.2.4. Relay4 

The Relay4 sample uses a subscription process to allow broadcasts to multiple processes without regard to 
the actual membership of the subscription at the time the message is generated. This was initially used to 
test the SODL subscription feature, but was modified to use a user specified subscription process when the 
SODL subscriptions were removed. 

There are seven processes in this sample. The only processirelay instance is the root process for the 
simulation. There are in addition two processisubscription instances and four processichild instances. 
Each process’.child resides on a different simulation engine. During setup, each of the four proccssichild 
instances is informed of the process handles of the process’.subscription instances. Each processichild 
then sends a messageisubscribe to one of the proc^szsubscription, which will add that child to the 
subscriber list for that subscription. 

Once completed, the processirelay instance will send a messageigeneric instance to both of the 
processisubscription instances. This message will be forwarded to all of the subscribers of each 
subscription, which should be each of the processichild instances. 


139 








import message {StartSimulation, generic, setup} } 

{import process {child, subscription} } 

{ 

process:relay 

{ 

subscription:sub[2]; // Subscription instances for the program 

child:children[4]:@; // Child processes 

mode:Default 

{ 

node:start[StartSimulation:strt] 

[setup:s=>(children;), generic:out=>(sub;):(0.0)] 

{ s.set(sub);} } } } 


(a) relay.proc 


{import message {generic, subscribe, unsubscribe} } 

{import std {<set>} } 

{ 

process: subscription 

{ 

std::set<process>;subscribers; // The list of subscriber processes 

mode:Default 

{ 

node:subscribe[subscribe:in][] { subscribers.insert(in.getSourceO);} 
node:unsubscribe[unsubscribe:in][] { subscribers.erase(in.getSourceO);} 

node:forward[generic:in][generic:out[]] // Forward a generic message 

{ 

out.push_back(in); // Copy the input message 

out.back0.clearDest0; // Clear the destination list 

std;:set<process>:;iterator i; // subscribers iterator 

for (i=subscribers.beginO; i!=subscribers.endO; ++i) 
out.back0.addDest(*i);} } } } 


(b) subscription.proc 


Figure 9-22 Relay4 support process constructs 

Upon receipt of a messageigencric instance, a processichild will send three messages, each with randomly 
generated time stamps, and each to a randomly selected process:subscription instance. The first message 
is a messagezsubscribe, the second a messageiunsubscribe, and the third a message:gcnm'c. The 
messageiunsubscribe will remove the sender from the subscription list for the receiving 
processisubscription instance. The other messages will exhibit the behavior described earlier. 


140 










The simulation will run until there are no longer any messages to process, or until the user stops the 
simulation. Relevant source code for the Relay4 sample is listed in Figures 9-22 and 9-23. 


{import message (generic, setup, subscribe, unsubscribe} } 

{ 

processxhild 

{ 


ulongxt; 
long:di[3]; 
double:ts[3]; 
process; subscriptions []; 


// Number of children 
// Destination index 
// Outgoing message timestamp 
// Subscription process handles 


mode:start 


node:relay[setup:in] 

[subscribe:sub=>(subscriptions[di[l]];):(ts[l])] 

{ 

subscriptions = in.getO; // Get subscription handles 

ct = ((ulong) subscriptions.sizeO); // Number of subscriptions 
di[l] = random.nextInteger(ct); // Join random subscription 
ts[l] = -0.9; // Subscription event timestamp 

start.setActive(false);} } // Turn off the start mode 


mode:run 


{ 

node;relay[generic:in] // Generic inbound message 

[generic:out=>(subscriptions[di[0]];):(ts[0]), 
subscribe;sub=>(subscriptions[di[li];):(ts[l]), 
unsubscribe:unsub=>(subscriptions[di[2]];);(ts[2])] 

{ 

count-H-; // Log the reflection 

for (long i=0; i<3; -H-i) // Loop over the messages 

{ 

di[i] = random.nextlnteger(ct); // Get destination 
ts[i] = getTimeO+random.nextDouble(1.0);} } } } } 


Figure 9-23 processichild construct declaration for Relay4 sample 


9.2.5. Relays 

The Relays sample was developed to debug a portion of the SODL run time system involving the Time 
Warp algorithm. There are three process instances, each on separate engines and all derived from the 
process:6ase construct. The root process is a processtsonrce instance, which sends message:generic 
instances to itself and a process:re/ay instance it owns. In response, this processrreloy then sends another 
message:gc«mc to a process:si/tft that it owns. The processzsink does nothing except act as a message 
sink. 


141 









{import message {generic} } 

{ 

process:base 

{ 

long:count(-l); // Counter 


mode;Default 

{ 

node:routine[generic:in][] {-H-count;} } } } 

Figure 9-24 process:base construct in the RelayS sample 

{import process {base} } 

{process;sink(base);} 

(a) sink.proc 

{import message {generic} } 

{import process {base, sink} } 

{ 

process;relay(base) 

{ 

sink:s:me.getNodeO+l; // Sink for message stream instantiated of different engine 

mode:Default 

{ 

node:run[generic:in][generic:out=>(s;)] {} } } } 

(b) relay.proc 

{import message {StartSimulation, generic} } 

{import process {relay, base} } 

{ 

process:source(base) 

{ 

relay:r:me.getNodeO+1; // Relay process instantiated on a different engine 

mode;Default 

{ 

node:start[StartSimulation;s][generic:out=>(me; r;);(0.0)] {} 
node:run[generic:in][generic;out=>(me; r;)] {} } } } 

(c) source.proc 

Figure 9-25 Code segments for instantiated RelayS process constructs 
The process:ftase construct, portions of which are listed in figure 9-24, provides support for a common 
output scheme and message counter for each of the derived constructs. 


142 


















Relevant code from the processibase derived constructs is shown in Figure 9-25. Figure 9-26 illustrates 
how the messages are passed within the system. 



Figure 9-26 Message routine in the RelayS sample 


9.2.6. Relays 

On the surface, Relay6 may seem to be a relatively simple simulation, and the code itself would seem to 
confirm this. However, looking at a run of the simulation reveals some interesting dynamics that are not 
immediately obvious. The original intention of this sample was to test the basic functionality of the 
rollback mechanism to ensure that it was performing this task satisfactorily. It did this by running two 
processes at different “rates” (by which we mean the virtual time between successive messages delivered to 
each process was different) and then periodically sending a message Ifom the slower process to the faster 
one, thereby inducing predictable rollbacks. 

There are two processes, each on separate engines and derived from the process:frase construct. The 
process:re/ay construct is the root process, and it sends a message:generic instance starting at time stamp 
0.0. Upon receipt of a message:ge/icric instance, the process:re/ay instance will send itself another at time 
cur rent_time+0.\. Every tenth message :gencric the processtrefay receives will cause it to forward the 
next message:ge«mc to both itself and a subordinate processtsinA:. The processrsiwA:, on the other hand 
will, upon receipt of a message:generic issue another to itself with time stamp current_time+1.0. This 
message flow is depicted in Figure 9-27. 


143 










inessage:g£neric 

current time^Q. 1 

messaz^:StartSimulation 

This switch opens every tenth 
messageigeneric invoking a 
rollback on the process:5in&. 

messagerge/ieric 

current_time+l.O 

Figure 9-27 Message routine in the Relay6 sample 

{import message {generic} } 

{ 

process:base 

{ 

long:count(-l); //Counter 

method:roxmd(public; double; doubled;) { return floor(t*10.0+0.5)/10.0;} 

mode:Default 

{ 

node:routine[generic:in][] { -H-count;} } } } 

Figure 9-28 Relay6 process construct for the processibase 
The process:base construct, portions of which are shown in Figure 9-28, governs output and counting to 
provide some state information. It also declares method;roun(/ to eliminate minor differences in time 
stamps that can occur between the two derived process time stamps. 

The interesting dynamics comes into play when the number pending message:generic instances for the 
processrsinA: instance begin to grow linearly with the number of messages it receives from the 
processireAzy. If not mitigated, there would be one pending message in the processisinA for every 
message tgewcric sent to it from the process :retoy. 

This problem can be resolved by requiring the process;sinA instance to send new message:generic 
instances to itself only when sufficient time has passed since the last transmission. This check is performed 



144 











in the nodecrun declaration in processrsin^, which sets the transmission flag to false when the last 


message processed prior to the one currently under consideration has a time stamp that is too close to the 
current timestamp. Relevant code for the process:6ase derived classes is shown in Figure 9-29. 


{import message (generic, StartSimulation} } 

(import process (base, sink} } 

{ 

process:relay(base) 

{ 

sink: s: 1; // Sink for the message stream 

ulong:ct(0); // Counter 

moderDefault 

{ 

node:start[StartSimulation:in][generic:out=>(me;s;):(0.0)] {} 

node:run[generic:in][generic:out=>(me;):(round(getTimeO+0.1))] 

{if (+-tct%10==0) out.addDest(s);} } } } // Send to s every 10 times 


(a) relay.proc 


(import message {generic} } 

(import process {base} } 

{ 

process:sink(base) 

{ 

double:lt(-1.0); // Time stamp of last message:generic received 

mode:Default 

{ 

node:run[generic:m][generic:out=>(me;):(round(getTimeO+1.0))] 

{ 

out.setTX((getTime0-lt)>0.01); // Stop rampant messaging 
lt=getTimeO; } } } } 


(b) sink.proc 

Figure 9-29 Relevant Relay6 code segments 

9.3. GLUT based demonstrations 

The GLUT demonstrations are somewhat more complex than demonstrations discussed thus far. This 
complexity is largely the result of having to create and mange the scene graph on the SODL side. 


One of the main problems with the basic GLUT View Manager (GVM) is that in order to display properly, 
the simulation engine must schedule screen updates. All of the SODL processes that have potential input 
are required prior to updating the display, to post updated information about their state to the GVM. This 


145 













takes place in the form of messaging, and becomes quite time consuming for event relatively few objects 
on the screen. Some of the samples below augmented the basic GVM to reduce these periodic redraw 
cycles to only one message, instead of the hundreds or thousands that would have otherwise been 
necessary. 


9.3.1. Bouncel 


□ X 



Figure 9-30 Output from Bouncel demonstration 

The Bouncel demonstration depicts a cube with a collection of particles bouncing around the cube’s 
interior. A sample output is provided in Figure 9-30. The simulation consists of a processrhounce 
instance. 


146 





{import message (hit, start, gr update, SetVertex3D, AddVertexSD, SetSystem} } 
(import process (NodeSD, Vertex3D} } 

{ 

process;particle 

{ 


Vertex3D:vrt; 

double:pos[3]; 

double:vel[3]; 

double:nextTime[3]; 

double:time(0.0); 


// Screen vertex 

// Position vector 

// Velocity vector 

// Next impact times for each axis 

// Time for the last velocity change 


method;init(public; void;) // Initialize particle position & velocity 
method:setNextHitTime(private; void; intri;)// Get next hit time for axis i 
method:move(private; void;) // Move particle to “current” position 
method:getMinAxis(private; int;) // Return axis which will next have a collision 


mode;Default 

{ 

node:start_sim[start:s] [hit;out=>(me;):(nextTime[out.axis])] 
{ out.axis = getMinAxisO;} // Schedule first collision 


node:update[gr_update;in][SetVertex3D:out=>(vrt;)] 

{ 

moveO; // Move the particle to the current position 

out.set(pos);} // Update the vertex position 


node:change[hit:in][hit:out=>(me;):(nextTime[out.axis])] 

{ 

moveO; // Move particle to current position 

vel[in.axis] = -vel[in.axis]; // Change particle the velocity 

setNextHitTime(in.axis); // Set next hit time for the specified axis 

out.axis = getMinAxisO;} } } } // Axis for the impact 


Figure 9-31 balLproc; Code governing ball motion and scene graph update 


The user interface is a process:V ich’JD instance, and includes a system defined processiCube instance as 
well as a process:Po(ygo/iJi> used to actually display the particles in the cube’s interior. The simulation 


mainly concerns itself with the 200 particles bouncing around the interior of the cube. These particles, the 
behavior of which is declared in the processiparticle construct, each have a process:Vcrtex3£) that is 


added as a subordinate to the process:Polygon3D instance. As the simulation proceeds, the particles 
schedule the next “bounce” they have as a message:Ad instance. The root process also schedules periodic 
updates of the scene, and broadcasts to all of the process:particle instances to report their position to the 
view so that the scene graph may be updated. 


This leads to some problems with respect to performance, and points to areas where the SODL system has 
some limitations. In this instance, since each particle is responsible for explicitly updating its position in 


147 









the view for each frame, there is a great deal of message traffic. Each processiparticle instance, in each 
frame first must receive a request to update the scene graph, then forward an updated position its 
process: Viertex3Z? instance. From there, another message is forwarded to the actual view. The view then 
schedules an update to the scene graph in the form of yet another message. This final message queue has 
messages inserted in time stamp order, so no sorting is necessary. However, each of the other messages 
must be generated and processes in chronological order. This in turn needs to be done for each particle, in 
each Ifame, leading to a reduction in the overall performance. Relevant code for the processiparticle is 
shown in Figure9-31 and message transmission is depicted in more detail in Figure 9-32. 



Figure 9-32 Messages for screen update in Bouncel demo 

9.3.2. Bounce2 

The Bounce2 demo looks somewhat similar to the Bouncel demo described above. It differs primarily in 
the load distribution between the SODL simulation engine and the GVM graphics engine. 

The Bouncel demo suffered from substantial performance degradation because each particle was required 
to provide the graphics engine with explicit position updates. This required an additional two messages per 
frame per particle. 

There is in essence nothing changing between successive particle-wall collisions. The velocity of the 
particles remains unchanged between successive bounces, and a quick calculation based upon the location 


148 











and time of the last bounce as well can accurately determine the location of the particle for anytime prior to 
the next bounce. The Bounce2 demonstration takes advantage of this by offloading these computations to 
an extension of the GVM. None of the scene update messages necessary in the Bouncel demo are 
performed in Bounce2. The only events scheduled (after initialization) are for the particle-wall collisions, 
and periodic update requests to display the scene graph at the next time interval. The result is a significant 
improvement in performance. 


Particle Count 

Bouncel CPU time (seconds) 

Bounce2 CPU time(seconds) 

1 

2.202 

1.876 

5 

1.938 

1.765 

10 

1.766 

1.906 

50 

0.704 

1.876 

100 

1.375 

1.876 

500 

10.94 

1.890 

1000 

33.03 

1.313 

5000 

Not Calculated 

2.750 


Table 9-1 Performance comparison of Bounce demos 


The performance measurements displayed in Table 9-1 are taken from successive runs on a dual processor 
700MHz Pentium III based computer system with an nVidia GeForce 256 based graphics card which 
provides hardware acceleration to perform geometric transformations. The tests were performed under the 
Windows 2000 SPl operating system with the size of the window in which the simulation was displayed 
set to 800x600 pixels. The results shown are based upon 10 seconds of simulation time, amounting to 
about 400 rendered frames. The times listed do not include simulation initialization and process 
dependency setup. The image in Figure 9-33 shows the Bounce2 demonstration with 2,000 partieles. 
Figure 9-34 shows the manner in which the messages are passed, and 9-35 the relevant code from the 
process:parfic/e construct. 


There were sufficient computing resources remaining to adjust the simulation somewhat and to incorporate 
gravity into the demonstration, as well. Thus, the particles in the final Bounee2 demonstration undergo 
parabolic motion. This feature was disabled during the test runs highlighted in Table 9-1. 


149 


























Figure 9-33 Bounce! output with 2,000 particles 



Figure 9-34 Messaging to update the scene graph in Bounce! 


150 








{import message {hit, set motion, SetVertexSD, StartSimulation, AddVertexSD, AddView} } 

{import process {VertexSD} } 

{import std {<vector>} } 

{import {"Exception.h"} } 

{ 

process:particle(V ertex3D) 

{ 

double:vel[3]; // Velocity vector 

double:acc[3]; // Acceleration vector 

double:nextTime[3]; // Next impact times for each axis 

double:time(0.0); // Time for the last velocity change 

method:init(public; void;) { ... } // Initialize the particle motion parameters 

method:setNextHitTime(private; void; int:i;) { ... } // Get next hit time for axis i 

method:move(private; void;) { ... } // Update particle state to current time 

method:getMinAxis(private; int;) { ... } // Get the minimum time axis 

mode:Default 

{ 

node:start[StartSimulation:s][hit:out=>(me;):(nextTime[out.axis])] 

{ out.axis = getMinAxisQ; } 

node:addView[AddView:in][set_motion:out=>(in.getSourceO;)] 

{ 

out.set(time,pos,vel,acc); // Set parameters in new view 

out.index = in.index;} // Set the index value, too 

node:change[hit:in][hit:out=>(me;):(nextTime[out.axis]),set_motion:sm[]] 

{ 

moveO; // Move particle to current position 

vel[in.axis] = 'Vel[in.axis];// Change the velocity 
setNextHitTime(in.axis); // Set next hit time for specified axis 
out.axis = getMinAxisQ; // Axis for the impact 

std::map<process, gvm::object_index>::iterator i; // For index 
for(i=views.beginO; i!=views.endO; ++i) // Loop over map 

{ 

sm.push_back(me); // Make new msg 

sm.back0.addDest(i->first); // Add destination to it 

sm.back0.index = i->second; // Specify the index 
sm.back0.set(getTimeO,pos,vel,acc);} } } } } 

Figure 9-35 particle.proc - relevant code for updating scene graph in Bounce2 

9.3.3. Brigadel 

The Brigadel demonstration is an extension of the Brigade2 demonstration described earlier. This 
extension provides a graphical representation of the state of progress during the brigade completing its task. 
A sample output is shown in Figure 9-36. 


151 










^2<j; 


■ • 11 fl ■ I i 

I I I II I II Mil r I II I III II II II 

II 11 nil nil II Mil ir i iiii ' i ii n r i ii i i ini ini in in 

II II nil nil II nil i ii itii ii ii: in i li i i itii ini nii in 

; I nil nil II Mil II I Ml II II n in i l mi ini ini nii in 

1 II III nil II iin II II iiir n ii n i i' l Mi ii ini nil ini n 

<11 III nil II Mil l II Ml M M I nil I f I nil <ii in in 

n II nil ii'i II Mil I II nil ti ii n mi' Ml ii ini im n i ni 

I II INI nil 11 Mil ir II II I M ir i iii ll i n iiii ni ini ni 

I II nil nil 11 Mil ii I I ir ii ii <i in ll n n iin'm in ni 

n 1! nil III II' Mil ll II Ml M II ! I I ' III M Mil nil ii i ni 

I II INI nil II Mil II II mi M IIII nil I m n in iin ini ni 


Figure 9-36 Brigadel sample output 


The top bar in the display represents the brigade, the four bars immediately beneath the four subordinate 


battalions, followed by the companies, platoons and squads. Under each squad is a column of ten soldiers. 


Red units are awaiting orders, yellow units are working on their assigned task, and green units have 


completed their task. 


9.3.4. Hierarchy 

The Hierarchy demonstration is a simple test of the two dimensional display capabilities of the GLUT view 


manager, and acts as a sort of predecessor to the Brigadel demonstration above. It can be thought of as a 


binary tree that is being traversed. The scene starts out with all of the elements in the display colored blue. 


While a branch is being traversed, it changes to cyan. When the branch is finished being traversed, it 


changes color to green. 


A sample output of the Hierarchy demonstration is depicted in Figure 9-37. 



Figure 9-37 Output from the Hierarchy demonstration 




152 
































































































9.3.5. Battle 

This demo is the most complex of those discussed here. It is a hierarchical in its structure, with two 
opposing forces (designated RED and BLUE) consisting each of one company. Each company owns one 
command post, and five tank platoons. Each tank platoon has five tanks. The object is for one team to 
destroy the command post of the opposing force. There are three views associated with the demo; a 3D 
view of the environment and a tactical view for each force, representing the knowledge of the environment 
for the side related to the view. 

The simulation starts with the two opposing forces in opposite comers of a square 400km^ play field (20km 
to a side). All of the platoons of team RED initially move to take up a defensive position stretching across 
the center of the playfield. Two platoons of team BLUE move to defensive positions about 2,500m from 
the BLUE command post. The remaining three platoons move toward the RED command post. 

9.3.5.1. Newtonian Motion 

All of the objects in the simulation that are participants of the battle in one respect or another undergo 
Newtonian motion. This motion is broken into linear and angular components. Linear motion includes 
position within the play-space, linear velocity, and linear acceleration. Angular motion includes object 
orientation, rotation rate, and rotational acceleration. There is also a start and stop time associated with 
motion. The behavior directing the motion is incorporated into a C-i-i- class, spt::NewtonianMotion. This 
is done since the class is needed in multiple places within both the simulation engine and the rendering 
engine. 

Changes in motion parameters are handled via message passing to processiNewtonianMotion instances. 
Each of these instances owns and manages the motion parameters in an spt::NewtonianMotion instance. 
Any changes to the motion parameters are passed to interested parties, namely the environment (see section 
9.3.5.3) and any views rendering representations of the object undergoing Newtonian motion. 

9.3.5.2. Sensing 

Objects in the simulation have the ability to sense other nearby objects. Tanks have the ability to sense 
objects within a 2km range, while eommand posts can sense objects within a 5km range. To keep things 


153 







simple, sensing objects are only notified when they detect enemy objects. Upon notification of a new 
sensor track, a tank will notify its parent platoon of the track. At present, the tank will then direct fire 
against the new track (see section 9.3.5.6 below). If the platoon did not previously know about the track, it 
will forward a notification to the parent company, and likewise await further orders. It will also maintain a 
list of tracks that subordinate tanks have sensed so that the platoon can make local decisions about how to 
deal with the situation. Upon notification to the company of the track, the company will update its list of 
tracks if it had not previously been aware of the track, and issue orders to address the situation. Figure 9-38 
illustrates this situation. 

mt%%zgf.AddTrack 



Figure 9-38 Sensor track detection and change notification in Battle demo 

The procedure is the same for notification of changes in the track motion parameters. The switch between 
the platoon and company allows notifications to the platoon to be forwarded to the company only when 
such notifications from a subordinate tanks was new information. A similar notification mechanism is used 
for notifying parents of the loss of a sensor track. In that case, however, the platoon only forwards 
notification of the loss when all of its subordinate tanks had reported that it lost the track. This helps to 
reduce the number of messages that need to be passed from one level in the hierarchy to the next. 

9.3.5.3. Environment 

The environment is a process designed to govern interactions between objects in the simulation. It is 
primarily responsible for notifying objects of new tracks, changes in the track motion characteristics, and of 
lost tracks. The environment will also notify any objects affected by the impact of a munition that it was 


154 









hit. Though the environment does not strictly perform collision detection between objects, it is logical to 
place it here in the event that this function is eventually incorporated into the simulation. 

During initialization, each sensing or trackable object must register itself with the environment. This 
registration informs the environment as to the objects Newtonian motion parameters, sensing radius, and 
team membership (either BLUE or RED). An initial round of sensing event creation accompanies each 
new registration. In addition, any time an objects motion parameters change, the environment is notified so 
that a new collection of sensing events can be scheduled. 

The environment schedules sensing events to occur at some point in the future. These scheduled events 
include detection events and loss events. Since there is no way to revoke sensing events that have already 
been scheduled, the environment performs one last check before the sensing object is informed to ensure 
that the sensor does in fact detect or lose the track, as appropriate. Only those sensing events that occur 
before both the track and sensor motion stop time will be scheduled. This also reduces the total number of 
messages that must be scheduled. Figure 9-39 illustrates this notification of track detection and loss. 



Figure 9-39 Notification of sensor track detection and loss 


One assumption that is made regarding the sensor detection is that the sensors implicitly detect friendly 
units, but must explicitly detect enemy ones. This allows the environment to notify sensing objects only of 
detection events for enemy tracks, reducing the number of messages that must be passed any time an object 
changes it motion parameters. 


155 










9.3.5.4. Vehicle Movement 


Tank motion is derived from the fact that it inherits this behavior from the process:Ve/iic/e construct. 
Since there is really only one type of vehicle, encapsulating vehicle motion in this manner is, strictly 
speaking not necessary. It does provide a convenient mechanism for adding new vehicles to the simulation 
in the event that is eventually desired. 



Figure 9-40 Messages governing vehicle motion in the Battle demo 


Vehicles move about the battlefield when they receive movement request messages. These movement 
request messages contain a destination location and orientation. Upon receipt of one, the vehicle stops any 
motion that it may currently be performing and turns to face the destination. It then moves to the 
destination, and upon arriving there, will stop and turn to the final orientation. Each step in this sequence 
of events needs to be scheduled in the proper order. Furthermore, if a new movement request arrives 
during a move, the vehicle must disregard any commands issued in support of the initial move. To 
facilitate this sequencing of movement events, the vehicle switches modes as it prepares to perform the next 
phase of the movement command. Specifically, during the initial turn toward the destination, 
moAe:turnJo_dest is active. Once the turn is complete, it is deactivated and moAG'.moveJoJiest becomes 
active. Similarly, the final turn to the eventual heading is performed while mode:tum_to_heading is 
active. The transition from one phase of the movement to the next occurs when the vehicle receives a 
messagetSfqp instance it had previously scheduled for itself. At the beginning of each transition, 
messageiSetNewtonianMoHon messages inform the interested processes as to the new motion parameters 
for the vehicle. These processes are any GVM views with which the vehicle has been registered, the 


156 











environment, and the parent object (in the case of tanks, the platoon to which the tank belongs). Upon 
completion of the final leg of the movement, the vehicle sends a message:MovementComplete message to 
its parent. Figure 9-40 illustrates this process. 


P.3.5.5. Formation Movement 

A 

A 

A 

A 

(a) Column 



(b) Line Abreast 



(d) Forward Sweep 


Figure 9-41 Predeflned Tank Formations 

Tanks are organized into platoons and are capable of moving in formations. These formations have a 
position, orientation, and left and right leg angles. These formations act as a template for positioning tanks 
relative to some reference point. Normally, the second (i.e. middle) tank acts as the lead for the remaining 
tanks. When ordered to a new position, the lead tank takes up that position, and the remaining tanks take 
up positions relative to the lead tank as specified by the formation structure. The orientation is a vector, the 
angle of which is used to dictate the direction the formation will face, and the magnitude the distance 
between adjacent tanks. The leg angles refer to a radial along which tanks will align themselves relative to 
the lead tank. A formation with a positive leg angle for a given side arranges tanks along that radial 
forward of the lead tank. Likewise, negative leg angles arrange tanks along a radial behind the lead tank. 
Figure 9-41 depicts some predefined formation arrangements. In this case, the column formation has a left 


157 







leg angle of Till and a right leg angle of-7t/2. The line-abreast formation has a left and right leg angle of 


zero. The V-Formation has a left and right leg angle of -Jt/8, and the forward sweep has left and right leg 
angles of 7t/8. Other formations are possible as well by explicitly specifying the left and right leg angles. 



Figure 9-42 Messaging during formation movement 

There are two message constructs used to establish a platoon formation. Both are derived from 
laessagciAdjustFormation. The first, messagezSetFormation is used diuing initialization to establish an 
initial formation. Use of this message will cause the platoon to explicitly specify the location and 
orientation of the tanks in the platoon. The second, messagc:MoveFormation is used to move a platoon 
from its current position, orientation and arrangement to some destination position, orientation and 
arrangement. A^hen the platoon receives an instance of this second message type, it will turn the platoon to 
face the final destination, move the formation to the destination, and then face the platoon in the direction 
and arrangement specified by the destination orientation. Like vehicle movement, the platoon uses a 
collection of modes (mode:turn_to_dest, mode:move_to_dest, and mode:turn_to_heading) to perform 
each phase of the movement. Each tank will report with a messageiMovementComplete to the platoon that 
it has completed its movement instruction for that phase (as shown in Figure 9-40). Only when all tanks in 
the platoon are in the proper position will the next phase of the movement commence. The process:Platoo/t 


158 
















accomplishes this by issuing the next set of movement commands and transitioning to the next mode in the 
sequence. Figure 9-42 illustrates how the messages are passed from the platoon to the member tanks. 


9.3.5.6. Fire control 

Since the processiEnvironment only notifies processrlank instances when they detect enemy units, any 
new track is assumed an enemy. This track is added to a target queue, and is associated with a 
process'.SensorTrack instance in the simulation space. The processiSensorTrack construct is a parent 
construct for all process:Vefeic/c and processiCommandPost constructs. At present if the sensing tank has 
no other targets it is tracking, it will enter an attack mode whereby it will calculate a firing solution to the 
target and shoot a processiMunition at it. 


mes$»ge:AddTrack 

mt$iage:CliangeTrack 

mtssageiLoseTrack 



Figure 9-43 Fire control sequence for a tank 


When firing at a target, the tank must first line up its gun so that when it fires the munition, it can be 
guaranteed of hitting the target. It does this by changing the azimuth and elevation of the gun, operations 
requiring some time to complete. While aiming, any changes in the target movement parameters cause the 
tank to recalculate the firing solution, and restart the aiming process. If the track is lost, it the tank will 
direct fire against the next target in its target queue. When the process: Tank finishes aiming its gun, it 
then fires a projectile. Upon impact, the processiMunition notifies the processiEnvironment of its impact 
location. The environment then looks at all objects registered to it and notifies any within 3m of the 


159 












munition impact point that that it has been hit. The munition is also notified of the tracks it struck, so that it 
may inform the firing tank of the target(s) the munition destroyed. If the munition missed the intended 
target, the sequenee is repeated until the target is destroyed. Once eomplete, the tank moves on to the next 
track that it has and repeats the process until all of its tracks are destroyed. Figure 9-43 illustrates the 
message passing in the fire control and subsequent notification. 

9.3.5. 7. Target Destruction 

Once a target is struck with a munition, the simulation system assumes that it has been destroyed. There is 
some clean up that the system must perform afterwards to ensure that a destroyed object behaves that way. 
The first thing that must be done is for the destroyed object to inform its parent that it has been destroyed 
and to eliminate any tracks the parent may have as a result of the newly destroyed object’s sensors. To this 
end, the object sends a messageiDesiroycrf to its parent, followed by a collection of mtssag'^'.LoseTrack 
instances. In the case of a tank, the parent platoon also informs the process:Company instance so that the 
tactical view (see section 9.3.5.8) can be properly updated to reflect this loss. Likewise, if the object was 
the last in the parent unit to be tracking a particular enemy unit, then this must also be passed up the chain 
via another message:Loserrac4. If the destruction of the subordinate unit results in the loss of the parent 
(i.e. all five tanks in a platoon are destroyed), then this must be passed further up the ehain with another 
message :De£rroye<f instance. 

The destroyed unit notifies the process:Environment that it has been destroyed'^. Within the 
proccss’.Environment, several steps need to be taken to ensure that the objects sensing the newly destroyed 
unit can no longer track it. This is performed with one messageiLoseTrack forwarded to all objects that 
had previously been able to sense the unit. The environment ignored events it may have scheduled based 
upon the future location of the now destroyed unit (a future sensor detention, for instanee). 

Figure 9-44 depiets some of the messages that need to be passed to perform this clean up. 


Even though the proccssiEnvironment notified the target of the fact that it was hit by a munition, it 
makes no assumptions about how many hits will actually destroy a target. This allows for some flexibility 
if the simulation is to one day be expanded. 


160 








Figure 9-44 Clean-up after a unit destruction 

9.3.5.8. Viewers 



Figure 9-45 Shots of the initial platoon configurations in the BattleView of the Battle demo 


Three viewers are used in the battle demo. The first is a three dimensional representation of the virtual 
environment. In this view, all of the objects can be seen with some degree of detail. Users can perform a 
virtual fly-by within this view to look at the layout of the various formations, or the location and orientation 
of an individual tank. Sample images are provided in figure 9-45. 

The other views represent the world as seen by the Blue and Red teams individually. These are called the 
Red and Blue Tactical Views respectively. Friendly units in each view are colored with the team color, and 


161 








have a disk around them that indicates the range of that particular unit’s sensors. Hostile units will appear 
in their team color as they become visible to the friendly units. The tactical views for Figure 9-45 are 
provided in Figure 9-46. 



Figure 9-47 Tactical views shortly after the opposing forces encounter each other. 

Figure 9-47 shows some additional tactical views as the simulation progresses, with its associated battle 
view depicted in figure 9-48. 


162 































Figure 9-48 Sample engagement of opposing forces 

The user can change viewing parameters within the views. Table 9-2 shows the key/mouse commands 
used to change the view and to do other simple tasks. Most of the keys and all of the mouse commands 
operate only in the BattleView. The keys that work in the Tactical View are ESC, ‘h’, ‘H’, ‘r’, and ‘R’. 

As a side note, the simulation is in a pause state at the beginning of the run, and must be resumed in order 


for the simulation to progress. 


Key/Mouse command 

Function 

‘a’, ‘A’ 

Translate the view to the left 

‘d’, ‘D’ 

Translate the view to the right 

‘e’, ‘E’ 

Translate the view down 

‘h’, ‘H’ 

Halt (pause) the simulation 

‘q’, ‘0’ 

Translate the view up 

‘r’, ‘R’ 

Resume simulation 

‘s’, ‘S’ 

Translate the view back 

‘w’, ‘W’ 

Translate the view forward 

1,2, 3, 4, 5, 6, 7, 8,9 

Translation speed, n+1 translates at twice speed of n. 

ESC 

Quit the program 

t_[_5 t_) 

Zooms into the scene 


Zooms out of the scene 

Mouse Left down & Drag left/right 

Rotates the view about the view z axis 

Mouse Left down & Drag up/down 

Rotates the view about the view y axis 

Mouse Right down & Drag left/right 

Rotates the view about the view x axis 

Mouse Center down & Drag up/down 

Zooms into and away from scene 


Table 9-2 BattleView keyboard/mouse commands 


164 































Chapter 10. Conclusions 

Though we did not accomplish all we set out to with the SODL system, we have contributed to the body of 
knowledge, specifically in the field of distributed simulation. 

10.1. Contributions of this work 

10.1.1. SODL system 

The SODL system is intended to provide a mechanism to facilitate development of distributed discrete 
event simulations based upon the notions of stimulus-response. Overall, the language structure 
successfully accomplishes this goal. The simulations highlighted in Chapter 9 and listed in Appendix C 
reveal that comparable systems developed from scratch would have had to contain considerable code to 
ensure that messages were delivered in the proper order. Likewise, systems built on top of existing 
libraries would have to include interfaces into those libraries that would again detract from actually 
defining the object behavior in the simulation system. Those fourth generation languages intended for use 
with either optimistic or conservative synchronization (namely YADDES and APOSTLE) require rigid 
specification of the message passing topology. 

In the introduction, we claimed that the guiding principle of SODL was to split the simulation engine 
performing the mechanics of simulation from the behavior of the objects within the simulation. SODL 
largely succeeds at hiding many of the artifacts of performing a distributed simulation from the simulation 
system developer (the most notable exception being I/O operations) without sacrificing the generality 
available in other approaches. This allows developers to generate SODL code that closely resembles 
models they have developed without having the language or associated run-time system intrude upon that 
model. Additionally SODL provides an extensive (albeit non-exhaustive) collection of visualization tools 
to help facilitate analysis. 

10.1.2. Simulation Formalism 

Chapter 2 of this dissertation provides a formal description of the process of modeling and simulation and 
how it relates to real-world or hypothetical systems. This formalism provides a basis for discussing 


165 






simulation within a larger context than has been provided within previous work. This formalism builds on 
top of the existing body of work and relates the larger context formally to the notions of distributed discrete 
event simulation prevalent in that existing work. 

10.1.3. Asynchronous Global Virtual Time Algorithm 

Chapter 3 concludes with the formal description of a generalized version of Mattem’s algorithm for 
performing asynchronous Global Virtual Time (GVT) estimates. This generalization makes no 
assumptions about the underlying simulation topology in use for inter-process communications, while still 
maintaining the conditions necessary to ensure that local estimates of the GVT are lower bounds of the 
actual GVT. We go on to formally prove the correctness of this generalization. To the knowledge of this 
author, both the generalization and the formal proof of that generalization are original work. 

10.2. Potential future work 

While the amount of work that went into the SODL system is quite extensive, it falls short of some of the 
original notions surrounding it. This section highlights these issues, and introduces some others that are a 
natural extension of the work presented here. 

10.2.1. Distributed SODL run-time system 

The original intention of this work was to develop an operational distributed simulation system. While a 
number of factors seem to have played a role in keeping this feature out of the final system, ultimately it 
has been this author’s responsibility for decisions made and actions taken that forced the decision to drop 
this capability. 

The current SODL system implements a full optimistic simulation engine that can be fitted with the proper 
networking code to provide a fully distributed simulation system capability. The notion has always been to 
use the Message Passing Interface (MPI) as a means of distributing the simulation system, and it is this 
authors hope that this can be implemented in fairly short order. 


166 






10.2.2. Graphics Subsystem 

While the GLUT View Manager (GVM) is useful as a research tool for visualizing the simulation system, 
more advanced approaches will likely require more sophisticated graphical representation, to include such 
things as solid rendering, lighting, curved surfaces, texturing, collision detection, and other features found 
in contemporary graphics systems. None of these advanced feamres are currently implemented in GVM. 
With some additional work, they could be incorporated easily. 

10.2.3. User Interface 

The user interface in the SODL system is very limited. Currently, it cannot be used to interact with objects 
in a rendered scene. There are a number of mechanisms within OpenGL allowing such interactions; such 
mechanisms could be used as a basis for allowing users to send messages to objects within the virtual 
environment. At the current time, the SODL system is not intended to support such efforts, and little 
thought has been given to how this might be accomplished. 

10.2.4. Process Migration and Load Balancing 

One problem associated with distributed simulation is load balancing, ensuring that no one node in a 
distributed simulation system has a significantly larger number of messages to process relative to its 
processor speed than other nodes. In conservative simulation, this problem leads to excessive blocking of 
the faster nodes, slowing down the overall simulation execution. In optimistic simulation, faster nodes will 
be required to use more memory to store old state and message information in the event a rollback is called 
for. This can be mitigated through process migration, directing a redistribution of the workload so that no 
one node is excessively burdened with a disproportionate workload. 

This problem was never addressed in the SODL system, as it was beyond the scope of the research 
described herein, and because a distributed implementation of the SODL run-time system was never 
acmally produced. If a distributed SODL run-time system is developed, an obvious mechanism to deal 
with these issues would be to actually continue instantiating all of the processes locally and then merely 
turning different processes on and off on different nodes depending upon load on each node. 


167 






10.2.5. Analysis tools 

The focus of the SODL system has primarily been upon the language structure and to a lesser extent, the 
run-rime system. When simulation is used to perform analysis of one sort or another, it is quite often useful 
to provide tools to facilitate this analysis. This facilitation could be anything from formatting data that can 
be incorporated into an existing analysis tool, or through internal tools that can be called upon during or 
after a simulation run. There are no tools within the SODL system allowing a direct analysis of data 
generated. Such tools could be incorporated into later releases. 

SODL also lacks any reasonable random number generation capability for serious analysis. (Press 1992) 
contains a number of algorithms for generating random numbers of various distributions. Such algorithms 
can be incorporated into a C-t-i- class dedicated to random number generation. Alternatively, third party 
software with liberal copyright restrictions (e.g. GNU Public License) might also be useful. 

10.2.6. Multiple inheritance 

At times during the development of some of the demonstrations, the author discovered instances where 
multiple inheritance could be a useful tool. While work-arounds resolved many of the problems, they 
tended to be somewhat clumsy. As such, the overall system could greatly benefit from multiple 
inheritance. 


168 







Appendix A. SODL Language Parser Specification 

The SODL Parser, sp, uses the following specification to parse SODL program files. 


line-specifier : { import-specifier } line-specifier 
I { debug bool-value } line-specifier 
I { message-specifier } 

I {process-specifier} 

import-specifier : import import-list 

I import identifier :: import-list 
I import:: import-list 
I import message sim-import-list 
1 import process sim-import-list 

import-list : {imports } 

imports : C++-#include-parameter 

I C++-#include-parameter, imports 

sim-import-list : identifier 

I identifier , sim-import-list 

message-specifier : message : identifier { message-definition } 

I message : identifier ; 

I message : identifier (identifier ) { message-definition } 

I message : identifier (identifier ) ; 

message-definition : variable-specifier 
I method-specifier 

I variable-specifier message-definition 
1 method-specifier message-definition 

variable-specifier : identifier : identifier process-qualifiers ; 

I identifier :: type-specifier : identifier variable-qualifiers ; 
I :: type-specifier : identifier variable-qualifiers ; 

1 scalar-specifier : identifier variable-qualifiers ; 

process-qualifiers : null 

I affinity-specifier 
I size-specifier 

I size-specifier affinity-specifier 

variable-qualifiers : null 

I initialization-specifier 
I scalar-specifier 

I size-specifier initialization-specifier 
type-specifier C++-type-expression 
affinity-specifier : : modified-C++-integer-expression ; 


size-specifier : [ integer-value ] 
initialization-specifier : {modified-C++-expression) 


169 








scalar-specifier 


method-specifier 

method-parameter-list 

access-specifier 


method-parameters 

process-specifier 


process-definition 

mode-declaration 

Node-list 

node-specifier 

input-message 

output-message-list 

output-message 

output-qualifiers 

time-specifier 


bool 

byte 

char 

double 

float 

int 

long 

uint 

ulong 

rand 

process 

profile 

method : identifier ( method-parameter-list ) { C++-code } 

access-specifier ; type-specifier ; method-parameters 

public 

protected 

private 

null 

variable-specifier ; method-parameters 

process : identifier ; 

process : identifier ( identifier ) ; 

process : identifier { process-definition } 

process : identifier ( identifier ) { process-definition } 

message-definition 

mode-declaration 

message-definition process-definition 
mode-declaration process-definition 

mode : identifier { node-list } 
mode : identifier ; 

node-specifier 
node-specifier node-list 

node : identifier [ input-message ] [ output-message-list ] { C++-code } 

identifier : identifier 

null 

output-message , output-message-list 

identifier : identifier output-qualifiers 

identifier : identifier [ ] output-qualifiers 

identifier : identifier [ integer-value ] output-qualifiers 

null 

: ( time-specifier ) 

=> ( destination-list ) 

=> ( destination-list ) : ( time-specifier ) 

modified-C++-double-expression 


destination-list 


modified-C++- destination ; 
modified-C++- destination ; destination-list 


170 








identifier is an alpha-numeric string of characters starting with a letter. It can include the character. 
integer-value is C+-i- specification of an integer constant. 
bool-value is one of the two constants, trae or false. 

C-\--\-#include-parameter is a text stream that is suitable for inclusion immediately following an Mnclude 
directive in a C-t-i- source code file. 

C++-type-expression is a C-t-i- expression that describes a C-t-i- type. 

modified-C++-integer-expression is a C-t-i- expression that when modified, will evaluate at run-time to an 
integer. It is modified by changing any instance of the and ‘#’ characters to an array index value and 
array size respectively. 

modified-C++-expression is a C-I-+ expression that when modified, will evaluate at run-time to a value of 
the desired type. It is modified by changing any instance of the and *#’ characters to an array index 
value and array size respectively. 

C++-code is a block of C-t-t- code. 

modified-C++-double-expression is a C-i-i- expression that when modified, will evaluate at run-time to a 
double precision floating point number. It is modified by changing any instance of the and 
characters to an array index value and array size respectively. 

modified-C++-destination is a C-f-t- expression that when modified, will evaluate at run-time to a process 
handle. It is modified by changing any instance of the and *#’ characters to an array index value and 
array size respectively. 


171 








172 







Appendix B. SODL Run Time engine class reference 
B.1. Overview 

The SODL simulation engine and support library is designed to provide the basic infrastructure for passing 
messages between simulation processes. This documentation highlights the data members and methods for 
this infrastructure. 

B.2. SODL Run-Time System C++ Ciasses 

The SODL run-time system is responsible for ensuring that messages are delivered to the proper process in 
the proper time stamp order. It provides the basic infrastructure for this, and provides extensible class 
declarations for messages, processes, and support for lO operations. All classes in the Run-Time system 
are in the sodl:: namespace unless otherwise stated. 

B.2.1. ::Exception 

The "Exception class is a holding place for a collection of nested classes, each of which are different types 
of exceptions that the SODL run-time system may from time to time make use of when recognizing some 
problem from which it cannot recover. These nested classes are all publicly available and are described in 
the following sections. 

Parent Classes: None 

Derived Classes: None 

B.2.2. "Exception:: Bad Cast 

This exception class is used when an attempt to cast an object from one type to another (usually 
dynamically) fails. This is a somewhat unusual circumstance for the SODL system since the only objects 
that are normally cast from one type to another are derived either from sodh'.Process or sodl::Message 
classes. Since these have fields for defining the actual type of the instance in question, dynamic casting 
should be straightforward. Thus, when an iiExceptioniiBadCast is thrown, it is usually indicative of a 
deeper and more serious problem than simply a typing mix up. 


173 







Parent Classes: public ::Exception::Nonspecific 

Derived Classes: None 

Protected Data Members: 

stdy.string ::Exception::BadCast::from - String representation of the type being cast from. 
std::string ::Exception::BadCast::to - String representation of the type being cast to. 

Public Constructors: 

::Exception::BadCast::BadCast(std::string t, stdiistringj) - This constructor initializes to to t and from 
to/. It also calls the parent constructor ::ExcepHon::NonspecificC‘Bad cast from”). 

::Exception::BadCast::BadCast{std::string m, std::string t, std::string f) - This constructor initializes to 
to t and from to /. It also calls the parent constructor t:Exception::Nonspecific{m). 

Public Methods: 

virtual void ::Exception::BadCast::seriailize(std::ostream& os) const - This method displays the error 
message to stream os. 

B.2.3. ::Exception;:CausalityError 

When a sodlr.Engine instance receives a straggler with a time stamp t, and for some reason the engine or 
one of its subordinate process controllers cannot rollback to time t (due primarily to a programming bug) 
then the engine or process controller will throw an ::Exceptioni:CausalityError. 

Parent Classes: public ::Exception::NonspeciJic 

Derived Classes: None 

Protected Data Members: 


174 







double ’.:Exception::CausalityError::att - Time stamp to which the SODL run-time system is attempting 
to rollback. 

double ::Exception::CausalityError::gvt - Last possible time to which this particular function can 
perform a rollback.. 

Public Constructors: 

•.:Exception::CausalityError::CausalityError(douhle g, double a) ~ This constructor initializes gvt to g 
and att to a. It also calls the parent constructor ::Exception::Nonspecific{“CauScLlity error: Attempt at 
time ”). 

::Exception::CausalityError::CausalityError(std::string m, double g, double a) - This constructor 
initializes the member variables gvt to g and att to a. It also calls the parent constructor 
; '.Exception ; :Nonspecific{m). 

Public Methods: 

virtual void ::Exception::CausalityError:',seriailizeistd:wstream& os) const - This method displays the 
error message to stream os. 

B.2.4. ::Exception::Nonspecific 

Any of a number of non-specific errors can be generated during the execution of a simulation instance. 
This exception is thrown when such an error is detected. 

Parent Classes: None 

Derived Classes: None 

Protected Data Members: 

std::string :zException::Nonspecific::msg - Error message to display when called upon to do so. 

Public Constructors: 


175 





::Exception::Nonspecific:’.Nonspecific(std::string m) - This constructor initializes msg to m. 

Public Methods: 

virtual void ::Exception::Nonspecific::seriailize(std::ostream& os) const - This method displays the 
error message to stream os. 

B.2.5. ::Exception::RangeError 

There are a number of stdwvector instances throughout the SODL run-time system. When an attempt is 
made to access an element outside of the vector bounds, a range error is thrown. 

Parent Classes: public ::Exception::Nonspecific 

Derived Classes: None 

Protected Data Members: 

ulong ::Exception::RangeError::attVal - Vector index that was attempted to be accessed, 
ulong ::Exception::RangeError::size - Actual size of the vector that is being accessed. 

Public Constructors: 

'.:Exception"RangeError::RangeError{vi\ong s, ulong a) - This constructor calls the parent constructor 
::Exception::Nonspecific(“Range error: Attempt at index ”) and initializes size to j and attVal to a. 

::Exception::RangeError::RangeError(std::string m, ulong s, ulong a) - This constructor initializes the 
member variables size to s and attVal to a. It also calls the parent constructor 
: :Exception::Nonspecificim). 

Public Methods: 

virtual void ::Exception::RangeError::seriailize(std::ostream& os) const - This method displays the 
error message to stream os. 


176 






B.2.6. sodl;;AntiMessage 

sodlrAntiMessage instances are used in the Time Warp algorithm to revoke messages that have lost then- 
validity in the simulation execution. The simulation engine (sodlr.Engine) creates a sodhAntiMessage 
when it becomes clear that messages transmitted need to be revoked. This is normally the result of a 
rollback to an earlier time than the current time stamp in the sodlr.Engine instance issuing the 
sodlrAntiMessage instance. 

Parent Classes; public sodlr.SystemMessage 

Public Constructors: 

AntiMessage:AntiMessage(sodl::Message& msg) - Creates a sodlrAntiMessage instance that will, if 
sent to do so, revoke the sodlr.Message instance msg and any copies made of it. 

Public Methods: 

static void AntiMessage::typeInit(sodl::mtype t) - Performs type data initialization used in ascertaining 
the type of message instance transmitted. 

virtual bool AntiMessage::annihilate{const sodlr.Message St msg) - Returns true if and only if this 
sodlrAntiMessage instance is supposed to annihilate msg. 

B.2.7. sodl;:Clock 

The sodlr.Clock class is responsible for managing time. It has a discrete mode where the sodlr.Engine 
class can specifically set the clock time, and provides a framework for extending its functions to include 
operation in a real time fashion. Each sodlr.Engine instance has one sodlr.Clock instance. Processes 
controlled by a particular engine can call getEngineO-getClockQ to obtain a reference to their local clock. 

Parent Classes: public sodlr.TimeStamp 

Derived Classes: None. 

Private Data Members: 


177 







static double sodlr.Clockr.pos - Used to determine the next possible time for eurrent times strictly greater 
than 0. This is currently set to I+IO '^. 


static double sodl::Clock::neg - Used to determine the next possible time for current times strictly less 
than 0. This is currently set to 1-10"'^. 

static double sodlv.Clockr.endTime - Used as delimiter as the last possible simulation time. No messages 
scheduled to occur after sodl::Clock::endTime will be handled. The default value for this member variable 
is 10'°l 

static double sodl::Clock::startTime - The time stamp of the messageiStartSimulation. Its default setting 
is -1. 


Public Constructors: 

sodl::Clock::Clock(v\ong n) - The primary purpose of this constructor is to call the parent constructor 
sodl::TimeStamp{-sodl::Clock::endTime, n) to establish the clock time stamp and associate it with a 
specific sodl::Engine instance. 

Public Methods: 

static double sodl::Clock::getEndTime{\oid) - Returns a copy of the static member variable endTime to 
the calling routine. 

virtual double sodl::Clock::getNextTime(yoid) const - C++ does not provide a routine (of which this 
author is aware) that when given a double precision floating point number, t, will return the smallest double 
precision floating point number that is strictly greater than t. getNextTimeQ fills this niche, albeit 
imperfectly. It will return next-time{current-time) as defined in Equation 6-1. 

static double sodl::Clock::getStartTime{\oid) - Returns a copy of the static member variable startTime to 
the calling routine. 


178 







B.2.8. sodl::Defs 

The sodliiDefs class is responsible for managing all of the system-defined types and some common 


routines of which various other classes in the SODL simulation run-time system can make use. Sp 
generates both baseDir/buildSubdir/Defs.h and baseDir/buildSubdir/Defs.cxx. This creates a different 
sodhzDefs definition for different programmer-defined simulation systems. 

Parent Classes: public sodhiTrace 

Derived Classes: sodhzClock; sodU'.Earlier, sodlr.Engine; sodlv.Handle\ sodlr.IdleListener; sodhiLater, 
sodlizMessage, sodl::Process; sodUzProcessController, sodliiProcessMode', sodh‘.ProfileTools\ 
sodh'.Random-, sodhiSchedule; sodl'.zScheduleltem', sodhzViewManager, 

Public Enumerators: 

enum sodl::Defs::MessageType - This is an enumeration of all of the message types. The enumerator 
names have the form SMTjnessage-type where message-type is the programmer defined type name of a 
message. The last enumerator in the list of them has name SMTJLAST. 

enum sodl::Defs::ProcessType - This is an enumeration of all of the process types. The enumerator 
names have the form SPT_process-type where process-type is the programmer defined type name of a 
process. The last enumerator in the list of them has name SPT_LAST. 

Private Data Members: 

static std::vector<std::string> sodl::Defs::msgNames - A string representation of the message types. In 
general msgNames[sodl::Defs::SMT_message-type] = “message-type”. 

static std::vector<std::string> sodl::Defs::procNames - A string representation of the process types. In 
general procNames[sodl::Defs::SPT_process-type] - “process-type”. 

Protected Data Members: 


179 







static stdv.vector<std\xvector<3ooo\> > DefsizmsgTypes; - Relationship between related message types. 
msgTypes[tl][t2] (where tl and t2 are both MessageType instanees) is true exactly when tl is associated 
with a message class which is a parent of the message class associated with t2 or tl=t2. 

static std::vector<std::vector<hool> > DefsixprocTypes; - Relationship between related process types. 
procTypes[tl][t2] (where tl and t2 are both ProcessType instances) is true exactly when tl is associated 
with a process class which is a parent of the process class associated with t2 or tl=t2. 

Public Methods: 

static void sodl::Defs::startup(\oid) - Performs a number of static initialization functions including 
populating the static string arrays, msgNames and procNames as well as initialization of the msgTypes and 
procTypes arrays. 

static void sodl::Defs::shutdown(\oid) - Performs functions associated with shutting down the simulation. 
As of this writing, this routine does not perform any specific function, but is provided as a counter-point to 
the startupO method described above. 

static bool sodl::Defs::isType(sodl::Defsi:MessageType a, sodl::Defs::MessageType b) - A convenience 
function which returns msgTypes[a][b] to the calling routine. 

static bool sodl::Defs::isType{sodl::Defs::ProcessType a, sodl::Defs::ProcessType b) - A convenience 
function which returns procTypes[a][b] to the calling routine. 

static std’.’.string sodl::Defs::msgName{sodl::Defsi:MessageType t) - This returns the type name 
associated with MessageType t. Specifically, it returns the array value msgNames[t] to the calling routine. 

static std::string sodl::Defs::procName(sodl::Defs::ProcessType t) - This returns the type name 
associated with ProcessType t. Specifically, it returns the array value procNames[t] to the calling routine. 

virtual void sodl::Defs::serializeistd::ostream& os) const - A stub which may be used to produce class 
dependent formatted output to a stream. Any derived classes should overload it to properly format the 


180 








output, to avoid the default output (" **** OVERLOAD ME **** "). It was not declared as an abstract 
method since there may be derived classes without any need to produce output. 


B.2.9. sodl;:Earlier 

This class provides an operator for comparing the time stamps of pointers to two messages. It is used in the 
sodl::Engine class to properly order the messages in the event queue. 

Parent Classes: public sodl::Defs. 

Derived Classes; None 

Public Methods: 

static bool sodl::Earlier::comp(sodl::Message* a, sodlzzMessage* b) - This operator compares the time 
stamps on the two message pointers. It returns true exactly when the time stamp of *a is earlier than the 
time stamp of *b. In the event that the time stamp of the two messages are the same, the message handles 
are used, first comparing the engine index upon which the message was initially generated, and then the 
message instance number for that originating sodl::Engine instance. 

virtual bool sodlizEarliemoperatorQ {sodliiMessage* a, sodliiMessage* b) - This merely returns the 
value returned by calling comp{a, b). 

B.2.10. sodl;:EndSimulation 

The sodhiEndSimulation class is a message time stamped with the last possible value. Though its use may 
not be necessary, it made many aspects of the optimistic synchronization implementation employed in the 
SODL system somewhat more intuitive and straightforward. 

Parent Classes: public sodhiSystemMessage 

Derived Methods: None 

Public Constructors: 


181 







sodl::EndSimulation::EndSimulation(\oid) - This constructor initializes the message time stamp to 
sodl::Clock::getEndTimeO, and calls the parent constructor in such a way as to make the root process the 
source of the message in all cases. 

virtual bool sodl::EndSimulation::getTX{\oid) - Overloads sodl::Message::getTXO so that it always 
returns true. 

static void sodl:iEndSimulation::typeInit(sodl::mtype t) - Used to perform type initialization during the 
sodl::Defs::startupQ call. 

B.2.11. sodl::Engine 

The sodliiEngine class is primarily responsible for message delivery for the processes it controls. It also 
manages the virtual time of all the processes under its control, directing fossil collection activities and 
rollbacks. It also manages the sodhiAntiMessage instances associated with messages that have been 
transmitted from subordinate processes. 

Parent Classes: public sodl::Defs. 

Derived Classes: None 

Private Data Members: 

std::priority_queue<sodl:'jintiMessage*^td::vector<sodl:’AntiMessage*>,sodl::Later> 
sodl::Engine::antimessages - A list of pending sodhiAntiMessage instances. The top element of the 
event queue is compared with the top element of the antimessage queue. If they annihilate each other, they 
are both removed and destroyed and the original message is never processed. 

sodliiClock sodUiEngine'.’.clock - The simulation time clock for the sodliiEngine instance. 

stdiipriority_queue<sodliiMessage*, stdiivector<sodliiMessage*>, sodliiLater> sodliiEngineiievQueue 
- List of pending messages, with the earliest message being at the top. Ties are broken using the message 
handle, so all they are executed in a unique order. 


182 







sodlr.schedule sodl::Engine::fcSched - The fossil collection schedule. When new process states are saved 
for later rollback, they schedule a fossil collection event with the engine. This schedule is maintained in 
fcSched. 

bool sodl::Engine::hold - This contains true when this engine is in a hold status (i.e. it’s waiting for the 
user to allow the simulation to proceed). It contains the value false otherwise. 

ulong sodl::Engine::msgCount - The message count. Processes owned by a particular sodliiEngine 
instance will have as the index portion of their handle the current message count (msgCount). This value is 
then incremented in anticipation of the next message. 

ulong sodl::Engine::node - This member acts as an index on the engine instance. Each sodl::Engine 
instance is given a unique node number to be used in the node portion of the handles for all processes the 
engine controls, as well as all messages originating from any such processes. 

std::deque<sodlyAntiMessage> sodl::Engine::outMessages - sodlrAntiMessage instances associated 
with all messages transmitted from each engine are stored so that, when a rollback is necessary, the 
transmitted messages can be revoked. These sodhAntiMessage instances are inserted into the double 
ended queue in the order they were created. Thus, they are sorted by generation time, making revocation 
and fossil collection a straightforward matter of removing elements from either the back or the front of the 
queue. 

std::deque<sodl::Message*> sodl::Engine::processedMessages - Each message is processed in time 
stamp order. After being processed, they are inserted into the processed message queue, allowing rollbacks 
to occur should this be necessary. Since they are inserted into the front of this double-ended queue in time 
stamp order, rollback and fossil collection is simply a matter of removing from either front or back of the 
queue, respectively. 

std::vector<sodl::ProcessController*> sodl::Engine:iprocList - This is the collection of process 
controllers governed by a sodl::Engine instance. As new processes are added, space in the vector is added 
to accommodate the sodliiProcessController instances. 


183 







Public Constructors; 


sodl::Engine::Engine(ulong n) - Initializes sodl::Engine::msgCount to 0, sodlv.Engine::node to n, and 
calls the constructor clock(n). 

Public Methods: 

virtual void sodl::Engine::fossilCollect(douhle t) - After incremental fossil collection is completed, 
regular fossil collection can be performed. This involves removing saved antimessages (from 
outMessages) and previously processed messages (from processedMessages) with time stamp values 
strictly earlier than t. 

virtual sodl::Clock& sodl::Engine::getClock(yoid) - Returns a reference to the engine’s clock clock. 

virtual sodl::ScheduleItem sodl::Engine::getNextFossilCollectEventi\oid) - Returns to the calling 
routine a schedule item with the time stamp of the engine’s next fossil collection event and index of the 
engine’s node. Specifically, it returns to the calling routine sodl:iScheduleItem{time_stamp, node), where 
time_stamp takes on the value Clocki:getEndTimeQ if no fossil collection events remain, or to the time 
stamp of the next event fcSchedOJopO,getTime{) otherwise. This is used in the sodhiEngineStand to 
schedule engines for incremental fossil collections to ensure that all output and other irrevocable activities 
occur in the proper time stamp order. 

virtual long sodl::Engine::getNode(\oid) - This method returns the node value to the calling routine. 

virtual void sodl"Engine•.•.incrementalFossilCollect{dou\i\c t) - The top element in the fossil collection 
schedule, fcSched, should have time stamp t, and is scheduled for process controller n on the engine. 
Process controller n is allowed to perform fossil collection up to time t, which should allow only the earliest 
fossil not previously collected to perform any irrevocable actions. 

virtual bool sodl::Engine::holding{\oid) const - Returns true exactly when this engine instance is holding 
because of a scheduled or user induce hold in the simulation. 


184 








virtual void sodl::Engine::init(\oid) - This routine performs some initialization for the sodl::Engine 
instance. This initialization includes calling the init method for each of the process controllers in procList. 

virtual ulong sodU:Engine\:nextMessage(yo\A) - This routine returns the current value of the msgCount 
and then increments it by one for the next message. This value is used in sodU'.Message instances to create 
a unique index for the message handle. 

virtual ulong sodl::Engine::nextProcess(\oid) - This routine allocates space in the process controller list 
(procList) and returns an index number to the calling routine. This is normally called by a process 
controller requesting space in the engine’s process controller list for later registration. 

virtual sodl::ProcessController& sodl::Engine::operator[]{ulong n) - Returns to the calling routine 
procList[n], which is the n* process controller on this sodliiEngine instance. If does not address a valid 
process instance on the local engine, an exception (::Exception::RangeError) is thrown. 

virtual ulong sodl::Engine::processCount(void) const - Returns the number of processes currently under 
control of the engine when called after the simulation starts. Prior to that time, there may be some process 
controllers that have not yet registered with the engine. 

virtual void sodl::Engine::receive(sodl::Message& msg) - Inserts an incoming sodliiMessage pointer 
into evQueue. If the time stamp on the incoming message is less than the current time in the sodlr.Clock 
instance, then the engine instance rolls back to ensure that the incoming message can be in the correct order 
with respect to the other messages in the event queue. 

virtual void sodl::Engine::reg(sodl::ProcessController& pc) - This registers a sodl::ProcessController 
instance with the simulation engine. This involves inserting the process controller into the proper location 
in the procList vector. 

virtual void sodl::Engine::rollback(doub\e t) - Conducts a rollback to time t. It does this by calling the 
rollback routines for each of the process controllers in the procList array; transmitting any members of 
outMessages with a time stamp not strictly less than f, removing all members of the double ended queue 
processedMessages with time stamps not strictly less than t and reinserting them into the evQueue. 


185 







virtual void sodl::Engine::scheduleFC(douhle t, ulong n) - This schedules a fossil collection event in 
fcSched for process controller n at virtual time t. 

virtual void sodl::Engine::scheduleFC(sodl::ScheduleItem i) - This routine schedules a fossil collection 
event at time i.getTimeQ for process controller i.getlndexQ. 

virtual void sodl::Engine::seruilize(std::ostreamSi os) const - This routine is designed to overloads 
sodl::Defs:iserialize{std::ostream&.) to produce output to stream os regarding various aspects of the engine 
state. 

virtual void sodl::Engine::start(\oid) - This routine is called immediately prior to the simulation starting. 
It creates sodliiStartSimulation and sodh:EndSimulation messages and adds the processes under its 
control as destinations. These messages are given their default time stamp value 
(sodl::Clock::getStartTime()=-l for sodlziStartSimulation instances, and sodl::Clock::getEndTimeO = 
10^°’ for sodl::EndSimulaHon instances). These messages are then inserted into the pending message 
queue. 

virtual double sodl::Engine::stepivoid) - This routine processes the next non-revoked message in the 
event queue provided its time stamp is not later than the earliest remaining hold in 
sodl:iEngineList::stand.holdList. It then returns the time stamp of the last message processed, or 
ClockzigetEndTimeO if the event queue was empty. 

virtual void sodl::Engine::transmitisodh:Message& msg) - This routine retains a copy of msg's 
antimessage for potential rollbacks. Msg is then sent to the transmit method of the sodhzEngineStand class 
governing local execution of the simulation system. 

B.2.12. sodl::EngineStand 

The sodlr.EngineStand provides a mechanism for arbitrary distribution of sodliiEngine instances across a 
network. Each node in a distributed simulation, has exactly one sodliiEngineStand instance, and local 
copies of all the sodhiEngine instances that the programmer specifies. Each engine stand controls the 


186 








activities of only those engines that reside on that stand’s node. Messages destined for engines controlled 
by other engine stands must be forwarded over the network to simulation node controlling that engine. 

Parent Classes: public sodl::IdleListener 

Derived Classes: None 

Protected Data Members: 

bool sodl::EngineStand::started - This flag is set to true exactly when the simulation has started. It is 
false prior to that happening. 

double sodl::EngineStand::gvt - Loeal estimate of the Global Virtual Time (GVT). 

std::vector<sodl::Engine> sodl::EngineStand::engineList - This is a list of all of the engines in the 
simulation. Every sodUiEngine instanee is controlled by exactly one sodl::EngineStand instance in the 
distributed simulation. However, all engines are allocated on all engine stands. 

std::priorUy_queue<douhle, std::greater<doub\e> > sodl::EngineStand::holdList - List of holds. If 
holdList is not empty, no engine may continue processing to times after the minimum element in the 
holdList. If the holdList is empty, then there are no holds, and processing may continue unabated. 

Public Data Members: 

static sodh’.EngineStand sodli'.EngineStandv.stand - This is a static instance of the sodliiEngineStand 
class. Each node in a distributed simulation controls exactly one sodh’.EngineStand instance. That 
instanee is sodl::EngineStand::stand. 

sodh’.ViewManager* sodl::EngineSland::vm - Each node in a distributed simulation has exactly one 
sodh’.ViewManager instance associated with each sodhzEngineStand instance. That instance is 
*stand::vm. 

PubUc Constructors: 


187 







sodl::EngineStand::EngineStand(\oid) - This constructor initializes member variables vm to NULL, 
started to false, and gvt to the value returned from a call to sodI::Clock::getStartTimeQ. 

Private Methods: 

virtual void sodl::EngineStand::updateGVTidoub\e t) - This method will update the local estimate of the 
GVT to time t. This update includes performing fossil collection on each of the engines the stand controls. 
The first phase of fossil collection involves polling each locally controlled engine as to the next scheduled 
item in their fossil collection schedule. Each engine is then scheduled at the engine stand level for 
incremental fossil collections; the engine with the earliest scheduled fossil collection event with time stamp 
less than t is permitted to perform that event. That engine is then polled for its new latest fossil collection 
event, which is then scheduled in the engine stand. All locally scheduled fossil collection events with time 
stamp less than t are thus performed in time stamp order. 

The second phase of the fossil collection allows each of the locally controlled engines to perform its gross 
fossil collection (reclaiming memory occupied by processed messages and antimessages generated from 
outbound messages) up to time t. 

virtual void sodl::EngineStand::resizei\i\ong n) - Resizes the number of engines to n. If n is larger than 
the current size of engineList, then additional engines are allocated. Any requests to reduce the size of the 
engine list are ignored. During the process allocation phase of the simulation setup, there is no clear 
indication from the programmer exactly how many simulation engines will actually be needed. During the 
process allocation, as new engines are requested, they are dynamically added to the list of them in the 
engine stand. This method performs that resizing. 

Public Methods: 

virtual void sodl::EngineStand::addHold(douhle t) - This routine adds a hold at time t to the holdList. 

virtual ulong sodl::EngineStand::engineCount{void) - This routine returns the number of engines in the 
engineList vector. 


188 







virtual double sodl::EngineStand:zgetGlobalVT(void) - Returns to the calling routine the value gvt. 

virtual sodl::ViewManager& sodl::EngineStand::getViewManager{\oid) - This routine returns a 
reference to the view manager controlling the engine stand. 

virtual bool sodl::EngineStand::holding(\oid) const - Returns true exactly when all of engines controlled 
by this stand are holding due to the earliest remaining holdList item. 

virtual bool sodl::EngineStand::idle(\oid) - When the view manager has some idle time, it will call 
idle{). When *vm refers to a sodlr.TextViewManager instance, the method is called until this method 
returns false (indicating that it has no more messages to process). When the controlling view manager is a 
sodl’.’.GLUTViewManager instance, this method is called whenever the GLUT sub system has idle time. 

virtual double sodh:EngineStand::nextHold{\oid) - This routine returns the time of the next hold to the 
calling routine. If there are no holds pending, it returns sodl::Clock::getEndTimeO*2.0. 


virtual sodl::Engine& sodl::EngineStand::operator[](viong n) - This method returns engineList[ri\. If 
the simulation has not yet started, and n is outside the range of the array, then the engine list is resized, 
prior to returning the specific engine instance. If the simulation has already started and n is outside of the 
range of the engine list, it throws an ExceptioniiRangeError. 

virtual void sodl::EngineStand::setup(sodli:ViewManager& v) - This method just sets vm variable to 
&v. 

virtual void sodl::EngineStand::start{\oid) - This methods calls the start methods for each of the engines 
in the engineList vector. It also sets to true the start flag. 

virtual void sodl::EngineStand::transmU(sodl::Message& msg) - This routine will forward a message to 
all engines with processes listed in the message destination list. If the engine is not locally controlled, the 
engine stand forwards it to the proper node in the distributed simulation. 


189 







B.2.13. sodl::GLUTViewManager 

This class provides a framework allowing the GL Utility Toolkit (GLUT) to perform two and three- 
dimensional displays of simulation output and minimal support for allowing users to provide inputs to the 
simulation. Use of sodhiGLUTViewManager is specified by using the -dglut option in the SODL parser, 
sp. The sodl::GLUTViewManager makes use of \hcgvni'.'.View class to perform actual 10 operations. 

Parent Classes: public sodhiViewManager 

Derived Classes: None 

Protected Data Members: 

std::vector<gvm::View*> sodl::GLlJTViewManager::viewMap - The collection of GLUT windows 
managed by this sodhiGLUTViewManager instance. Each GLUT window is provided an index, and is 
referenced associatively with that index using the stdiimap template class. 

static sodhiGLUTViewManager* sodhiGLUTViewManageriimanager - This is the master view 
manager for simulation instances using GLUT for the system 10. This static instance is required because 
the callbacks from the various GLUT routines require calls to static methods. By retaining a static pointer, 
those static methods can access the view list to notify individual view instance of 10 events. 

Public Constructors: 

sodhiGLUTViewManageriiGLUTViewManagerisodhildleListenerSi I, int* argc, char*[] argv) - GLUT 
can process command line arguments to specify certain GLUT-specific aspects of the graphics interface. 
This constructor (which receives the values from the main program) passes the command line arguments 
here, and this method then passes them on a routine GLUT uses to parse input parameters, and pare out 
GLUT options specified therein. The parameters GLUT uses will be removed from the command line 
argument list before returning from the constructor. It will also invoke the parent class constructor by 
calling sodhiViewManageriiViewManager(l, argc, argv). 

Public Methods: 


190 







virtual void sodl::GLUTViewManager::activateEntryListener{hool v) - This starts listening for GLUT 
mouse window entry events when v is true, or deactivates listening for GLUT mouse window entry events 
when V if false. 

virtual void sodl::GLUTViewManager::activateKeyboardListener(hool v) - This starts listening for 
GLUT keyboard events when v is true, or deactivates listening for GLUT keyboard events when v if false. 
It activates or deactivates both the key press and key release callback listeners. 

virtual void sodl::GLUTViewManager::activateMouseListener{hoo\ v) - This starts listening for GLUT 
mouse button events when v is true, or deactivates listening for GLUT mouse button events when v if 
false. 

virtual void sodl::GLUTViewManager::activateMotionListener{hool v) - This starts listening for GLUT 
active mouse motion events when v is true, or deactivates listening for GLUT active mouse motion events 
when V if false. 

virtual void sodl::GLUTViewManager::activateOverlayListener{hool v) - This starts listening for GLUT 
overlay events when v is true, or deactivates listening for GLUT overlay events when v if false. 

virtual void sodl::GLUTViewManagen:activatePassiveMotionListener{hoo\ v) - This starts listening for 
GLUT passive mouse motion events when v is true, or deactivates listening for GLUT passive mouse 
motion events when v if false. 

virtual void sodl::GLUTViewManager:iactivateReshapeListener{hool v) - This starts listening for GLUT 
reshape events when v is true, or deactivates listening for GLUT reshape events when v if false. 

virtual void sodl::GLUTViewManager::activateSpecialListener{hool v) - This starts listening for GLUT 
special keyboard events when v is true, or deactivates listening for GLUT special keyboard events when v 
if false. 

virtual void sodl::GLUTViewManager::activateVisibilityListener03oo\ v) - This starts listening for 
GLUT visibility events when v is true, or deactivates listening for GLUT visibility events when v if false. 


191 






virtual void sodl::GLUTViewManager::addView(gym::View& v) - This sets viewMap[v.getWindowO] to 
&v. If necessary, the view map is resized to allow the new view to be added. The gvmiiView instance is 
responsible for creating the GLUT window and retaining the proper value for that window’s index. 

static void sodl::GLUTViewManager::display{void) - Callback function for handling GLUT display 
request events. It calls (*manager)[glutGetWindowQ]^isplayO. 

static void sodl'.:GLUTViewManager::entry(int state) - Callback function for handling GLUT window 
entry and exit events. State is the type of event (entry or exit). It calls 
{^manager) \glutGetWindowQi\.entry{state). 

static void sodl::GLUTViewManager::idlei\oid) - Callback function for handling GLUT idle events, 
when GLUT is not busy handling other events. It makes a call to (*manager).idleListener.idle() allowing 
the simulation to progress. 

static void sodh:GLUTViewManager::keydown{msigped char key, int x, int y) - Callback function for 
handling GLUT key press events. The parameter key contains the key press value, and (x, y) is the screen 
position of the mouse at the time of the keyboard event. It calls 

(*manager)\glutGetWindowO)Jieydown{]key, x, y). 

static void sodl::GLUTViewManager::keyup(unsigned char key, int x, int y) - Callback function for 
handling GLUT key release events. The parameter key contains the value of the released key, and (x, y) is 
the screen position of the mouse at the time of the keyboard event. It calls 
{*manager)[glutGetWindowQi\Jceyup(key, x, y). 

static void sodl::GLUTViewManager::motion(int x, int y) - Callback function for handling GLUT active 
mouse motion events (i.e. with a mouse button depressed), (x, y) is the location of the mouse cursor. It 
calls {*manager)[glutGetWindow()].motion{x, y). 

static void sodl::GLUTViewManager::mouse(mt button, int state, int x, int y) - Callback function for 
handling GLUT mouse events, button is the button number that had the event, state is the button state, and 
(x, y) is the location of the mouse cursor. It calls {*manager)[glutGetWindowO].mouse{button, state, x, y). 


192 







virtual gvm::View8L sodl::GLUTViewManager::operator[](ulong n) - Returns the gvmv.View instance 
given by viewMap[ri\. 

static void sodl::GLUTViewManager::overlay(\oid) - Callback function for handling GLUT overlay 
events. It calls i*manager)[glutGetWindow{)].overlay(). 

static void sodl::GLUTViewManager::passive_motioniint x, int y) - Callback function for handling 
GLUT passive mouse motion events (i.e. with no mouse button pressed), (x, y) is the location of the 
mouse cursor at the time the event occurred. It calls i*manager)[glutGetWindowO]-P^sive_motion(x, y). 

static void sodl::GLUTViewManagerr.reshapeiint width, int height) - Callback function for handling 
GLUT window reshape events. The new width and height are given by the parameters width and height 
respectively. It notifies the gvmv.View instance of the change by calling 

{*manager)[glutGetWindowO\.mouse(width, height). 

static void sodl::GLUTViewManager::specialdown(mt key, int x, int y) - Callback function for handling 
GLUT special key press events, key is the value of the key that was pressed, and (x, y) is the location of the 
mouse cursor at the time of the key press event. It calls (*manager)[glutGetWindow()]jipecialdown(key, 
x,y). 

static void sodl::GLUTViewManager::specialup(int key, int x, int y) - Callback function for handling 
GLUT special key release events, key is the value of the key that was released, and (x, y) is the location of 
the mouse cursor at the time of the key release event. It calls 

(*manager)lglutGetWindowQ].specialup{key, x, y). 

virtual void sodl::GLUTViewManager::start{\oid) - Performs some initialization for starting up GLUT. 
This initialization involves establishing all of the listeners for various mouse, keyboard, and idle events. It 
then calls v.glutMainLoopQ to start the simulation. 

static void sodl::GLUTViewManager::visible{mt vis) - Callback function for handling GLUT window 
visibility change events. It notifies the gvmv.View instance of the change by calling 
(*manager)\glutGetWindowO].tnouse(button, state, x, y). 


193 






B.2.14. sodl::Handle 

Handles are used in this system to reference and identify process and message instances. They are 
composed of two parts, a node and an index. Each process has a unique (in that no other process has the 
same) <node, instance> pair. Similarly, each message has a unique <node, instance> pair. 

Parent Classes; public sodl::Defs 

Derived Classes: None. 

Private Data Members: 

ulong sodl::Handle::node - Node value for the handle, 
ulong sodl::Handle;:index - Index value for the handle. 

Public Constructors: 

sodl::Handle:'.Handle{\i\on% n, ulong i) - Initializes node and index to n and i respectively. 

Protected Methods 

virtual void sodl::Handle::setNode(long n) - Sets the node to n. 
virtual void sodl::Handle::setIndex(long i) - Sets the index to i. 

PubUc Methods: 

virtual long sodl::Handle::getNode{void) const - Returns the node to the calling routine. 

virtual long sodl::Handle::getIndexi\oid) const - Returns the index to the calling routine. 

virtual bool sodl::Handle::isType(ptype t) const - Returns true if this sodlr.Handle instance refers to 
sodh’.Process instance of type t. This is aecomplished by performing the call 
sodl: lEngineStand: :stand[node][index].isType(t). 


194 







B.2.15. sodl::ldleListener 

This is the base (abstract) class used by sodhiViewManager to manage interactions between the user and 
the simulation engine. 

Parent Classes: public sodl::Defs 

Derived Classes; sodl:'.EngineStand 

Public Constructors: 

sodl::IdleListener::IdleListeneri\oid) - This is the default class constructor for the sodh'.IdleListener 
class. It does not perform any special initialization. 

Public Methods: 

virtual void sodl::IdleListener::start(\oid)=(i - This abstract method is meant to be overloaded with 
simulation instance specific initialization routines. 

virtual bool sodl:’.IdleListener:'.idle(void)=0 - This abstract method is meant to be overloaded with 
simulation instance specific instructions that run the simulation system through one iteration. 

B.2.16. sodl::Later 

This class provides an operator for comparing the time stamps of pointers to two messages. It is used in the 
sodl'.'.Engine class to properly order the messages in the event queue. 

Parent Classes: public sodh'.Defs. 

Derived Classes; None 

Public Methods; 

static bool sodl::Later::comp(sodl::Message* a, sodhiMessage* b) - This operator compares the time 
stamps on the two message pointers. It returns true exactly when the time stamp of *a is later than the 
time stamp of *b. In the event that the time stamp of the two messages are the same, the message handles 


195 







are used, first comparing the engine index upon which the message was initially generated, and then the 
message instance number for that originating sodh'.Engine instance. 

virtual bool sodl::Later::operator{) (sodlr.Message* a, sodl::Message* b) - This merely returns the value 
returned by calling comp{a, b). 

B.2.17. sodl;;Message 

This is the base class for all messages. Messages can contain data allowing information to be passed 
between process instances. 

Parent Classes: public sodhiDefs, public sodhzTimeStamp 
Derived Classes: sodly.SystemMessage and all user defined messages. 

Private Data Members: 

double sodl::Message::genTime - Time stamp of this message instance’s creation. 

Protected Data Members: 

sodl::destinationJist sodl::Message::dest - The destination list. Each destination process is listed, 
possibly more than once (in which case, the message will be delivered multiple times to the same process) 
in a compact form that allows rapid discovery of the destination engines and the processes on those engines 
hsted as message recipients. 

sodlr.Handle sodl::Message::me - This is the message identifier. It is used to revoke messages when an 
inconsistent simulation state is encountered. 

bool sodl::Message::preempt - This flag is normally false. If this flag is true, none of the default 
destination processes in the node header generating the message will be added after the node has eompleted 
the process state changes and message formatting. It will instead send only to the destinations in the 
destination list specified at the end of the node execution. The flag is normally set to false if the 
clearDestQ method has been called somewhere in the node body. 


196 







process sodl::Message::source - This is the handle for the message's soxirce process. 

bool sodl::Message::timestampOverride - This is set to true if the time stamp value has been changed 
from inside the node body where the message originates. This is only of concern when a default message 
time stamp value is specified in the node declaration. 

bool sodl::Message::tx - tc is an abbreviation for transmit. This is normally true. 
sodl::ProcessController::transmit(sodl::Message&) will check this value. If it is true, then the message 
will be forwarded to the intended recipients. If it is false, the message is discarded. 

mtype sodl::Message::type - This contains the type information for the message instance. 

Protected Constructors: 

sodl::Message::Message(\ong n, long i, sodlr.mtype t) - This constructor performs the initialization of the 
message by invoking the various constructors for the parent class and member variables. A call to the 
constructor sodl::TimeStamp(-sodl::Clock::getEndTimei), n) initializes the parent class. The call meit, n, 
getEnginei).getNextMessageO) initializes the message handle. The message source is initialized by 
source(n, i). getiTime is initialized to the current simulation time. Flag values tx, preempt, and 
timestampOverride are initialized to true, false and false respectively. 

sodU’,Message::Message{caast process& p, sodhiHandle h, mtype ty, double t)- This constructor is used 
for some sodUiSystemMessages, notably the sodhiAntiMessage to set the various parameters of the 
message instance to the same as another message of some other type. Parent elass initialization is 
performed through a call to the parent constructor, sodl::TimeStamp{t, p.getNodeQ). The message handle 
me is initialized with the copy constructor by setting it to h and type is set to ty. source, the message source 
is also initialized with its copy constructor to p. genTime is initialized to the current simulation time. Flag 
values tx, preempt, and timestampOverride are initialized to true, false and false respectively. 

Public Constructors: 

Protected Methods: 


197 


virtual void sodlv.Messagev.initiyoiA) - This method is intended to be overloaded by a simulation system 
developer to allow initialization of a message’s content prior to being passed as a parameter to the process 
node that will eventually send the message. It does nothing in the sodhiMessage class itself, however. 

virtual sodl::destination_list& sodl::Message::getDest(\oid) - This returns a reference to the message’s 
destination list. 

Public Methods: 

virtual void sodl::Message::addDest (process p) - Inserts the p into the destination list, dest. 

virtual void sodl::Message::addDest (std::vector<process> p) - Inserts into the destination list, dest, all of 
the process instances in p. 

virtual void sodl::Message::clearDesti\oid) - This clears the message destination list, 
sodhidestinationjishidest, and sets the preempt to true. 

virtual sodhiMessage & sodliiMessageiicopy (long n)=0 - This abstract message is supposed to be 
overloaded by derived classes. It returns a copy of the message instance *this and assigns its engine to 
sodliiEngineStandiistand[ri\, which will manage it. This is used primarily when messages are transmitted 
from one sodhiEngine instance to another. 

virtual sodhiMessage & sodliiMessageiicopy (void)=0 - This abstract method is intended to be 
overloaded by derived classes to returns a copy of *this to the calling routine. 

virtual double sodhiMessageiigetGenTimeiyoid^ const - Returns the message generation time stamp, 
genTime, to the calling routine. 

virtual message sodhiMessageiigetID(yoid) const - Returns me to the calling routine, 
virtual ulong sodliiMessageiigetlndexiyoid) const - Returns me.getlndexO to the calling routine, 
virtual ulong sodhiMessageiigetNode{\o\d) const - Returns me.getNodeQ to the calling routine. 


198 







virtual process sodl::Message::getSource(\oid) - Returns source to the calling routine. 

virtual bool sodl::Message::getTX(\oid) - Returns true exactly when the destination list is not empty and 
the tx flag is true. 

virtual sod/::mtype sodl::Messagei:getType(yoid) const - Returns to the calling routine the value 
returned from calling me.getTypeQ. 

virtual bool sodl::Message::isPreempted(void) const - Returns to the calling routine the value in the 
preempt flag. 

virtual bool sodl::Message::isType(sodl::mtype t) const - Returns true if and only if this message 
instance is of type or of sub-type t. 

virtual void sodl::Message::serialize(std::ostream& os) const - Writes to the stream os a textual 
representation of the message instance. 

virtual void sodl::Message::setTX{hool TX) - Sets the value tx to TX. 
virtual void sodl::Message::setPreempted(\)ool p) - Sets preempt to p. 

virtual void sodl::Message:’.setTime{.do\!ii\e^ t) - Sets the value of the message time stamp override flag, 
timestampOverride, to true, indicating that it should not be set to the default value in the node header 
declaration sending this message instance, and calls setTime(t). 

virtual bool sodl::Message::timeOverride(yo\d) - Returns to the calling routine the value in 
TimestampOverride. 

static void sodT.:Message::typeInit{sodl::mtype t) - Performs type data initialization used in ascertaining 
the type of message instance that is transmitted. 


199 







B.2.18. sodl::MessageHandle 

This class serves as an identifier for message instances. It primarily provides a mechanism to distinguish 
between message handles and those for processes, since process handles provide a little more functionality. 

Parent Classes; public sodl::Handle. 

Derived Classes: None 

Public Constructors: 

sodl::MessageHandle::MessageHandle(ulong n, ulong i) - Calls sodl::Handle(n, i). 

B.2.19. sodl::Process 

This is the parent class for all process constructs. It provides the basic functionality associated with all 
processes. 

Parent Classes; public sodl::TimeStamp, public sodl::Defs 
Derived Classes; All user-defined processes. 

Private Data Members: 

bool sodl::Process::collected - This is set to true when the sodl::Process instance has had its 
fossilCollectQ method is called. This allows the instance to be retained after tbe initial fossil collection so 
that its state can be recovered in the event that it is needed in a rollback. 

sodl::ProcessController* sodhiProcess".controller - This is a pointer to the controller governing this 
proeess. 

Protected Data Members: 

process sodl::Process::me - This is a sodlr.Handle instance with handle information about this process 
instance. 


200 







virtual void gvm::SetCubeSize::send(\oid) - This methods actually sets the size attribute of the 
destination gvmiiCube instance. 

B.4.23. gvm:;SetCylinderSize 

The gvm::SetCylinderSize message is intended to set the size attributes of a gvmr.Cylinder instance. 

Parent Classes: puhUc gvm::Message 

Derived Classes: None 

GLdouble gvmr.SetCylinderSize‘.‘.radius - Value to set the radius attribute of the destination 
gvm::Cylinder instance. 

GLdouble gvm::SetCylinderSize::length - Value to set the length attribute of the destination 
gvm::Cylinder instance. 

GLint gvm::SetCylinderSize::sides - Value to set the side count attribute of the destination gvm::Cylinder 
instance. 

GLint gvm::SetCylinderSize::rings - Value to set the ring count attribute of the destination gvm::Cylinder 
instance. 

Public Constructors: 

gvm::SetCylinderSize::SetCylinderSize(gvm::View& v, double t, gvm::objectJndex i, GLdouble ir, 
GLdouble or, GLint s, GLint r) - This constructor calls the parent class constructor gvm::Message{v, t, 
GVM_SetCylinderSize, i) and initializes innerRadius, outerRadius, sides, and rings to ir, or, s, and r 
respectively. 

Public Method: 

virtual void gvm::SetCylinderSize::send(\oid} - This method commits the changes in the various 
attributes of the destination gvm::CyWfider instance. 


300 





static sodh'.Random sodl::Process::rand - Random number generator for all sodl::Process instances. 
sodl::ptypc sodl::Process::type - Contains the type information for the specific process instance. 

Protected Constructors: 

sodl::Process::Processiiiiong n, ulong i, sodliiptype t) - This constructor calls the parent class constructor 
sodi:TimeStampi-Clock::getEndTimeO, n). It also calls the constructor for the process handle me(n, i), 
sets type to t and collected to false. 

Private Methods: 

virtual void sodh'.Process‘.zsetCollected{\ioo\ c) - Sets collected to c. 

virtual bool sodl::Process::isCollected(yoid) - Returns collected to the calling routine. 

Protected Methods: 

virtual void sodl::Process::instanceInit(\oid) - The SODL parser, sp, will overload this function to 
perform instance specific initialization of various data members within the process definition. The 
simulation developer should not overload it. 

Public Methods: 

virtual void sodl::Process:tbackup(void) - During the state saving phase of the Time Warp algorithm, 
when process time stamps are increased, the old state is backed up and a new one is created. Once the new 
state is created with the new time stamp, the backup method for the new process is called. The programmer 
should overload this method in order to make use of this functionality and to manage aspects of the state 
saving that do not fall within the confines of the Time Warp algorithm. 

virtual sodl::Process& sodl::Process::copy(yoid)=0 - This abstract method is intended to be overloaded 
by derived classes so that a copy of *this can be returned to the calling routine. This is normally done for 
state savings purposes in the sodl::ProcessController instances. 


201 






virtual void sodl::Process::fossilCollect{yoid) - This routine is intended to be overloaded by the 
programmer. It is used to perform any irrevocable function required of the process prior to this particular 
process state being reclaimed. 

virtual sodl::ProcessController& sodl::Process::getController{\oid) - Returns to the calling routine 
*controller. 

virtual process sodlwProcess'.'.getlDiyoid) - Returns sodh:Process::me to the calling routine. 

virtual sod/::ptype sodl::Process::getType(\oid) - Returns to the calling routine type. 

virtual void sodl::Process::initi\oid) - Intended to be overloaded by programmer to perform application 
specific initialization. 

static ulong sodh\Processy.nextProcess{\Aong, n) - This is a convenience function returning the index of 
the next process to be added to engine n. It is accomplished by returning to the calling routine 
sodl‘.:EngineStand::stand[n].nextProcessO. 

virtual void sodl::Process::receiver(sodl::Message& m)=0 - This is overloaded by the code generated by 
the SODL parser to process incoming messages. It will compare the actual message type of the reference m 
to the inputs expected by the nodes in active modes. A match occurs when a node accepts messages that 
are of the same type as m or a parent message type of m. When this occurs, the message is passed to the 
proper routine within the SODL parser generated code to handle the message. The process controller 
reference is passed since it is responsible for filtering messages and ensuring they are forwarded to the 
intended destinations. 

virtual void sodl::Process::restorei\oid) - When a rollback occurs, a previous state is restored. That state 
for the process has its restore process called to perform any process specific rollback processing that might 
be required. 

virtual void sodl::Process::serializeistd::ostream& os) const - This method allows a textual 
representation of the process state to be sent to the stream os. 


202 






static void sodl::Process::typeInit(sodl'.:ptype t) - Initialize process type relations. 


B.2.20. sodl::ProcessController 

This class provides the basic functionality of the sodl::ProcessController instances. It is used to manage 
the flow of messages into an individual process. 

Parent Classes; public sodl::Defs 

Derived Classes: None 

Private Data Members: 

std::deque<sodl::Process*> sodl::ProcessControIler::stateQueue - This is the collection of SODL 
process states representing the state of the process at different points of time. Most recent states are at the 
back of the std: :deque, and earlier ones are at the front. Fossil collection is done from the front, while any 
inbound messages are always delivered to the back element. 

Public Constructors: 

sodl::ProcessController;:ProcessController(sodl::Process& p) - Calls constructor id(p.getID{).getNodeO, 
p.getIDO.getIndex(), p.getTypeQ). It then inserts &p into the back of the stateQueue and registers this 
instance with the controlling sodliiEngine instance. 

Protected Methods: 

virtual void sodl::ProcessController::backup{douhle t) - This routine will back up the first state in the 
StateQueue by inserting a copy of it at the back of that data structure. That copy will have its time stamp 
set to time t and its backup method will then be called. This is performed in accordance with the state 
saving phase of the Time Warp algorithm. 

virtual void sodl::ProcessController::displayStateQueue{std::ostream& os) const - This is a routine 
which will send a formatted textual version of all the members of the stateQueue to stream os. 


203 







virtual void sodl::ProcessController::fossilCollect(douh\e i) - This routine performs incremental fossil 
collection. The controller first finds the sodlv.Process instance with time stamp t in stateQueue. If exists, 
this routine will call its sodl::Process::fossilCollectO and set its sodl::Process::collected flag is set to 
indicate that it has been collected. Any elements of the state queue with time stamp values less than t are 
then removed from the queue and the memory they use is reclaimed. The element that was just collected 
remains until this sodliiProcessController instance is tasked with another fossil collection. 

virtual sodl::Engine& sodl::ProcessControllen:getEngine{\oid) - This uses the process handle to 
retrieve a reference of the engine controlling this controller. The actual call is 

EngineStand: :stand[id.getNode ()]. 

virtual void sodl::ProcessController::rollback(double t) - This routine causes a rollback to time t to occur 
for the process this sodh'.ProcessControUer controls. This is accomplished by removing elements from the 
back of the controller’s state queue, stateQueue, until the back element had a time stamp value that is 
strictly less than t. 

Public Methods: 

virtual process sodl::ProcessController::getID(yoid) - Returns to the calling routine stateQueue.backi)- 
>getIDQ. 

virtual void sodl::ProcessControlleri:init(\oid) - Calls the init method for the back element in 
StateQueue. 

virtual void sodl::ProcessController::receive{sodliiMessage& msg) - This routine will perform a backup 
of the back element of stateQueue if the time stamp of that element is strictly less than the time stamp of 
msg. It should not normally happen that the back element of the state queue has a timestamp which is 
strictly greater than that of msg, since the sodlr.Engine that is now transmitting msg should have requested 
an engine-wide rollback to the proper time upon receipt of msg. 

virtual void sodl::ProcessController::serialize{std::ostream& os) const - This routine provides a 
mechanism for providing formatted textual output to stream os. 


204 







virtual void sodl::ProcessController::transmit(sodl::Message& msg) - The back process in the state 
queue may elect, upon receipt of a message, to transmit new messages in response. That sodhiProcess 
instance calls this method in that case, passing all outbound messages singly as the parameter. The first 
thing that is done is to ascertain whether to actually send the message. This is done by doing so only if 
msg.getTXQ returns true. It also checks to see if the time stamp on the outgoing message is strictly greater 
than the current simulation time. If it is not, it could lead to an infinite loop and abnormal termination of 
the simulation run, so it is increased slightly in accordance with Equation 6-1. After this has been 
completed, the message is then forwarded to the eontrolling sodhiEngine instance for further processing. 

B.2.21. sodl::ProcessHandle 

This is primarily a minor extension of the sodl;:Handle elass. Though it does not contain any type 
information, it can be used to obtain type information about the process associated with this handle. 

Parent Classes; public sodl::Handle 

Derived Classes; None 

Public Constructors; 

sodl::ProcessHandle::ProcessHandlei}jiong n, ulong i) - This constructor calls the parent constructor 
sodl::Handle{n, i). 

Public Methods; 

virtual sodl::Defs::ptype sodl::ProcessHandle:zgetType{\oid) const - Returns to the calling routine type. 
Referencing the type information available from the process controller associated with this process handle 
does this. The actual value returned is sodl::EngineStand::stand[node][index].getTypeQ. 

virtual bool sodl::ProcessHandle::isTypeisodl::ptype t) const - This routine will return true if and only 
if the type associated with the process is type t or a sub-type of t. Referencing the type information in the 
process controller associated with this handle does this. The value returned is 
sodU •.EngineStand:‘.stand{node\{index].isTypeii). 


205 








B.2.22. sodl::ProcessMode 

Each mode declared in a SODL process becomes a sodh'.ProcessMode instance in the associated C++ 
class. Prior to a message being delivered to a node, its parent mode must be polled for its activity level. 
This class provides the means for doing this. 

Parent Classes; puhhc sodhiDefs, public sodUxTimeStamp 

Derived Classes: None 

Private Data Members: 

bool sodl::ProcessMode::active - Current state of the mode. 

bool sodl::ProcessMode::newActive - State of the mode after the next time stamp change. 

Public Constructors: 

sodl::ProcessMode::ProcessMode(uiong n) - This constructor initializes both of the member variables 
sodl::ProcessMode::active and sodl::ProcessMode::newActive to true and calls the parent constructor 
sodh : TimeStampi-Clock: xgetEndTimeQ, n). 

Public Methods: 

virtual bool sodl::ProcessMode:'.isActive(void) const - Returns to the calling routine 
sodl : xProcessMode : ‘.active . 

virtual void sodl::ProcessMode::serialize(stdi‘.ostream& os) const - Produces formatted textual output 
representing the state of this sodl::ProcessMode instance to stream os. 

virtual void sodl::ProcessMode::setActive(hool a) - This will set the member variable newActive to a. 
When the time stamp is updated, the this value will be copied to the active member variable. 

virtual void sodl::ProcessMode::setTime(douhlc t) - This will first make an explicit call to the parent 
class version, sodh'.TimeStamp'.'.setTimeit). It then copies the value in newActive to active. 


206 






B.2.23. sodl::ProfileTools 

This class is useful for timing the duration of various activities. It provides a processor-time clock to 
measures elapsed processor time since a reset or instantiation. 


Parent Classes: public sodhiDefs 

Derived Classes; None 

Protected Data Members: 

'.xclockj sodl::ProfileTools::time - Time of instantiation or last reset. 

Public Constructors: 

sodl::ProfileTools:tProfileTools(\oid) - Calls sodl::ProfileTools::resetTimeO- 

Public Methods: 

virtual void sodh’.ProfileToolsxxresetTimeiyoid) - Sets the time to the current time using xiclockO- 

virtual double sodl::ProfileTools::elapsedTime{\oid) - Returns the elapsed processor time (in seconds) 
since time was last set (i.e. ::clockO-time). 

B.2.24. sodl::Random 

This uses the standard C random number generator {zirandQ and ::srand{) for seeding) allowing users to 
uniformly distributed pseudo-random numbers. It is not a particularly divers random number generator, 
and end-users should probably consider replacing it with one that has a more varied and robust set of 
features. 

sodhxRandom has a typedef to sod/;:rand. 

Parent Classes: public sodkiDefs 

Derived Classes: None 


207 







Public Constructors: 


sodl::Random::Random(void) - Initializes the random number stream by calling ::srand((unsigned) 
::timeiNULL)). 

explicit sodl::Random::Random(vdnt seed) - Initializes the random number stream by calling 
::srand(seed). 

Public Methods: 

double sodl::Random::nextDouble(douhle a) - Returns a uniformly distributed double precision floating 
point random number in the range [0, a). 

double sodl::Random::nextDouble{doiihle a, double b) - Returns a uniformly distributed double 
precision floating point random number in the range [a, b). 

int sodl::Random::nextlnteger(int a) - Returns a uniformly distributed random integer in the range [0, a). 

int sodl::Random::nextInteger(int a, int b) - Returns a uniformly distributed random integer in the range 
la, b). 

B.2.25. sodl::Scheduleltem 

The sodh’.Scheduleltem class is used for scheduling fossil collection events, though it could be used for 
other purposes as well. It is based on the std:;pair<douhle, ulong> class, where first represents a time 
stamp, and second is an index of a process or engine that is scheduled. 

Parent Classes: public std::pair<double, ulong>,public sodl::Defs 

Derived classes: None 

Public Constructors: 

sodl::ScheduleItem::ScheduleItemidonhle t, ulong i) - This constructor calls the parent constructor 
std::pair<douhle, ulong> (r, i). 


208 







Public Methods: 


virtual double sodl::ScheduleItem::getTimeiyoid) const - Returns to the calling routine 
std::pair<double, ulong>./irst. 

virtual ulong sodl::ScheduleItem::getIndex(\oid) const - Returns to the calling routine std::pair<douhle, 
vdong>^econd. 

virtual void sodl::ScheduleItem::serialize(std::ostream8i os) const - Produces formatted textual output of 
the sodl::ScheduleItem instance to stream os. 

B.2.26. sodl;:StartSimulation 

A sodlziStartSimulation message is sent to all processes to begin the simulation run. Its time stamp is set 
to Clocky.getStartTimeQ, which defaults to -1.0. This value allows a significant amount of simulation 
initialization prior to time 0. 

Parent Class: public sodhzSystemMessage 

Derived Classes: None 

Public Constructors: 

sodl::StartSimulation::StartSimulation(\oid) - This constructor calls the parent constructor by 
SystemMessage::SystenMessage{0,0^odl::Defs::SMT_StartSimulation). It also sets its time stamp value 
to ClockiigetStartTimeQ. 

Public Methods: 

static void sodl::StartSimulation::typeJnit{sodl::mtype t) - Called to initialize portions of the 
sft/:: vcctor<bool> sodl: :Defs::msgTypes. 


209 






B.2.27. sodl::SystemMessage 

There are a number of system-defined messages that manage various aspects of the SODL run-time system. 
These system-defined messages are all derived from the sodh-.SystemMesage class. 

Parent Class; public sodlv.Message 

Derived Classes; sodlrAntiMessage, sodU'.EndSimulation, sodlr.StartSimulation, sodliiUpdateGVT 

Protected Constructors; 

sodl::SystemMessage::SystemMessage(idong n, ulong i, sodhimtype t) ~ This constructor calls the parent 
class constructor sodl::A/essage(n, i, t). 

sodl::SystemMessage::SystemMessage(const process& p, message h, mtype ty, double t) - This 
constructor calls the parent class constructor sodl::Message(p, h, ty, t). 

Public Methods; 

static void sodl::SystemMessage::typeInit(sodl::mtype i) - Called to initialize portions of the 
std'.:vector<\ioo\> sodl::Defs::msgTypes. 

bool sodl'.:SystemMessage::getTX(yoid) - Overloaded to always return true. 

B.2.28. sodl::TextViewManager 

This is the default view manager for a simulation engine. It is a bare bones manager providing no support 
beyond handling idle events. 

Parent Classes; public sodlr.ViewManager 

Derived Classes; None 

Public Constructors; 


210 







sodl::TextViewManager::TextViewManager(sodl::IdleListener& I, int* argc, char*[] argv) - This 
constructor will invoke the parent class constructor by calling sodl::ViewManager{l, argc, argv). In this 
case, both argc and argv are ignored. They are retained primarily for the benefit of other classes derived 
from sodly.ViewManager. 

Public Methods: 

virtual void sodl::TextViewManager‘.:start(\oid) - Calls idleListener.start{) followed by repeatedly 
calling idleListener.idle{) until it returns false. 

B.2.29. sodl::TimeStamp 

This sodh'.TimeStamp class provides a mechanism for time stamping items within the simulation system. 
These items requiring time stamps are messages, processes, and some other internal classes. Each time 
stamp value is with reference to a specific sodhiEngine instance. For a given process, this engine is the 
one that controls the process. For messages, it is the engine where the message will eventually be 
delivered. Messages also have a generation time that is relative to the engine where the message 
originated. 

Parent Classes: None 

Derived Classes: sodhiClock, sodliiMessage, sodliiProcess, sodly.ProcessMode 

Private Data Members: 

double sodly.TimeStampy.time - Actual time value of the time stamp. 

ulong sodly.TimeStampy.node - Index of the engine to which the time is relative 

Public Constructors: 

sodly.TimeStamp‘.:TimeStamp{Aovib\e t, ulong n) - Sets time to t and node to n. 

Public Methods: 


211 








virtual double sodh:TimeStamp::getTime(void) const 
sodl:: Time Stamp: itime. 


Returns to the calling routine 


virtual sodl::Engine& sodl::TimeStamp::getEngine(void) - Returns to the calling routine 
sodl::EngineStand::stand[node]. 

virtual ulong sodl::TimeStamp::getNode(yoid) const - Returns to the calling routine node. 
virtual void sodl::TimeStamp::setTime(doiihle t) - Sets time to t. 

virtual void sodl::TimeStamp::setEngine(const sodl::Engine& e) - Sets node to e.getNodeQ. 
virtual void sodl::TimeStamp::setEngine(ulong n) - Sets node to n. 

B.2.30. sodl::Trace 

The sodh'.Trace class is used to perform procedure call tracing. It requires a bit of effort to set up, but the 
entire SODL run-time system has instrumentation to trace program execution and log the execution to a file 
if desired. This is done through a stack mechanism. Upon entering a procedure, the programmer calls the 
enter{...) method to log the fact that the entry occurred. Immediately prior to leaving the routine, the 
programmer makes a call to leave(...) with the same parameters as the matching feave(...).Trace will note 
any discrepancies. Programmers can also turn on and off tracing in specific routines without regard to any 
other part of the program. 

We should note that the sodh’.Trace class member data and methods are only defined when the macro 
JTRACE is defined. Programmers wishing to make use of the routines here will need to delimit their calls 
with “#ifdef _7jR4C£” ... “#endif’. 

Parent Class; None 

Derived Classes: sodh’.Defs, gvm::Object, gvm::View. 

Private Data Members; 


212 







static stdy.ostream* sodl::Trace::active - Pointer to the currently active output stream. 

static std::stack<std::pair<hool, std::string> >* sodh:Trace::calls - Stack containing the call trace of the 
program. The calls.top{).first controls where the output is directed, Mev/mll for false, and trace.log for 

true. 

static ulong sodl::Trace::indentCount - The count of the number of pairs in the calls stack that have true 
for the first component of the stack element. This is used for computing how far to indent each line of 
output the sodlwTrace class methods produce. 

static std'.'.ofstream* sodl'.:Trace::null - A pointer to an output file stream whieh dumps the output to 
/dev/null. 

static stdr.ofstream* sodh:Tracey.trace - A pointer to an output file stream directing output to the local 
file trace.log. 

Private Methods: 

static void sodl::Trace::staticInit(\oid) - Allocates the various data structures described above, and sets 
active to direct output to trace.log as the default. It also initializes indentCount to 0. 

Public Methods: 

static void sodl::Trace::enter{hool d, stdy.string s) - This method should be called upon entry into a block 
of code, normally a function or class method. The input parameters d and s are paired and pushed onto the 
top of calls. If the input parameter d is true, active is set to direct output to trace.log and it increments by 2 
indentCount', if d is false, active directs output to /dev/null. The string s is then formatted and sent to the 
active output stream 

static stdy.string sodl::Trace::indenti\oid) - This routine returns a string of spaces indentCount 
characters long. 


213 







static void sodl'.:Trace‘.:leave(boo\ d, std::string s) - This method should be called immediately prior to 
leaving a block of code, typically a function or class method that had a corresponding call to enter{...) 
earlier in the program’s execution. The input parameters, when paired together, should match the top 
element of the call stack, calls. This is checked to ensure that enterQ and leavei...) statements are properly 
paired. The input parameter s is appended to an indented line (as generated in indentQ) and sent to the 
currently active output stream. If d is true, then indentCount is decremented by 2. The top element of the 
calls stack is removed and the new top element is evaluated to determine where the active stream should 
now point... trace.log if calls.topOfirst is true, /dev/null otherwise. 

static std::ostream& sodl::Trace::line(\oid) - This produces a line in the currently active log file which is 
indented and prefaced with “—“. The return stream is the active stream. 

static std::ostream& sodl::Trace::tlog(yoid) - This simply returns * active so that the calling routine may 
provide non-indented text to the currently active stream. 

static void sodl::Trace::stackTrace(std::ostream& os) - This routine makes a copy of the current calls 
stack and dumps that copy to the stream os. 

B.2.31. sodl::UpdateGVT 

The sodU’.UpdateGVT message class is intended to act as a catalyst for computation of the Global Virtual 
Time (GVT). It notifies the various sodU'.EngineStand instances in a distributed simulation to begin 
computing the GVT. 

AS OF THIS WRITING, THIS CLASS IS NOT USED. 

Parent Classes: public SystemMessage 

Derived Classes: None 


214 







B.2.32. sodl::ViewManager 

The view manager is an abstract class declaration, the subclasses of which are intended to manage certain 
aspects of the 10 operations between the user and simulation engine. It requires a sodhildleListener 
instance, which is normally a sodl::EngineStand instance. When a simulation system is produced using 
the SODL system, exactly one sodhiViewManager instance is created on each node in the distributed 
simulation. 

Parent Classes; public sodlv.Defs 

Derived Classes; sodlr.GLUTViewManager, sodlr.TextViewManager. 

Protected Data Members; 

sodhildleListener & sodhiViewManageriiidleListener - During idle times the method idleListenerddleQis 
called, allowing the simulation to process some of the pending messages. 

Public Constructors; 

sodl::ViewManager::ViewManager(sodh:IdleListener& I, int* urge, char** argv)- Sets idleListener to 1. 
The remaining arguments are for sub classes to perform initialization from command line parameters. 

Public Methods; 

virtual void sodl::ViewManager::start(\oid}=:0 - This function is intended to be overloaded by a subclass. 
Its functionality should include at a minimum performing last minute initialization and call the 
idleListener.startO method. It also starts some mechanism whereby idleListener Jdlei) is repeatedly called 
to allow the simulation to proceed. 

B.2.33. SODL run-time system items not associated with a specific class 

Parent Classes; N/A 

Derived Classes; N/A 


215 







Typedefs: 


typedef unsigned char ::byte - Shorthand for unsigned char, 
typedef unsigned int :;uint - Shorthand for unsigned int. 
typedef unsigned long ::ulong - Shorthand for unsigned long. 

typedef sodlr.MessageHandle sod/::message - Shorthand for sodhiMessageHandle class instance as they 
apply to sodl::Message instances. 

typedef sodlr.ProcessHandle sod/::process - Shorthand for sodlr.ProcessHandle class instance as they 
apply to sodh'.Process instances. 

typedef sodl::Defs::MessageType sodhimtype - This typedef is for specifying a shorthand notation for the 
sodl::MiniProcess::MessageType enumerator. 

typedef sodl::ProfileTools sodl: ;profile - Shorthand for a sodl:iProfileTools class instance. 

typedef sodl::Defs::ProcessType sod/::ptype - This typedef is for specifying a shorthand notation for the 
sodl : iMiniProcess: ’.ProcessType enumerator. 

typedef sodU’.Random sodhirsmd - Shorthand for asodliiRandom class instance. 

typedef std::priority_queue<sodl::ScheduleItem, std::vector<sodl: :ScheduleItem>, 

std::greater<sodl;:ScheduleItem> > 5odf::schedule - This provides a convenience declaration for a 
schedule of fossil collection events. 

Functions: 

std::string ::alpha(const bool v) - This returns the string “true” when v is true and “false” when v is false. 
template<class T> T dot(std::valarray<T> x, std::valarray<T> y) - Returns the dot product of x and y. 
int ::main(int argc, char *argv[\) - Start point for the simulation program execution. 


216 







template<dass T> std::vector<T> :;»iafce_vector(ulong s, T v,...) - This routine will create new vector of 
size s with the parameters starting with v. 

template <class T> T ::infljr(T a:, T y) - Returns the maximum value of x and y. Type T must have 
defined the operator 

template <class T> T x, T y) - Returns the minimum value of x and y. Type T must have defined 

the operator 

template<class T> T norm(std‘.:valarray<T> x) - Returns the 2-norm of x (i.e. sqrt{dot(x, x)). 

std::ostream& ::operator«(std::ostream& os, const sodl::Defs& v) - This routine calls \.serialize(ps). 

std::ostream& ::operator«(std::ostream& os, const sodl::Defs* v) - This routine calls {*\).seruilize{os). 

std::ostream& ::operator«(std::ostream&. os, const sodlr, mtype t) - This routine sends to the output 
stream os a string representation of t given by the value sodl::Defs::msgNames[t]. 

std::ostream& ::operator«(std::ostreamSi os, const sodliiptype t) - This routine sends to the output 
stream os a string representation of t given by the value sodl::Defs::procNames[t]. 

std::ostream& :toperator«{std::ostream& os, const sodlz’.Exception::Nonspecific& e) - This routine 
calls ejerialize(os) to produce output related to an exception. 

template <class T, class A> std::ostream& ::operator«{std::ostream& os, const std::deque<T,A>& v) 
- Produces formatted textual output of the components of a stdizdeque class instance. There must be a 
::operator«(std::ostream&, const T&) declared somewhere allowing each component in the container to 
be displayed. 

template <class T, class A> std::ostream& ::operator«{std::ostream& os, const std::list<T,A>& v) - 
Produces formatted textual output of the components of a stdrJist class instance. The operator 
::operator«(std::ostream&, const T&) must be declared somewhere allowing each component in the 
container to be displayed. 


217 






template <class K, class T, class C, class A> std::ostream& ::operator«(std::ostream& os, const 
std::map<K,T,C,A>& v) - Produces formatted textual output of the components of a std::map class 
instance. The operators ::operator«{std::ostream&, const K&) and ::operator«{std::ostream&, const 
T&) must both be declared somewhere allowing each component in the container to be properly displayed. 
The output is sorted according to the sort order specified by the declaration of v. 

template <class K, class T, class C, class A> std::ostream& ::operator«{std::ostream& os, const 
std::multimap<K,T,C,A>& v) - Produces formatted textual output of the components of a std::multimap 
class instance. The operators ::operator«(std::ostream&, const K&) and ::operator«(std::ostream&, 
const T&) must both be declared somewhere allowing each component in the container to be properly 
displayed. The output is sorted according to the sort order specified by the declaration of v. 

template <class T, class C, class A> std::ostream8i '.ioperator«{std::ostream& os, const 

std::multiset<T,C,A>& v) - Produces formatted textual output of the components of a std::multiset class 
instance. The operator ::operator«(std::ostream&, const T&) must be declared somewhere allowing 
each component in the container to be displayed. The output is sorted according to the sort order specified 
by the declaration of v. 

template <class Tl, class T2> std::ostream& ::operator«istd::ostream& os, const std::pair<Tl, T2>& 
v) - Produces formatted textual output of a std::pair class instance. ::operator«istd::ostream&., const 
T1&) and ::operator«(std"ostream&, const T2&) must both be declared allowing each component to be 
properly displayed. 

template <class T, class C, class L> std",ostream& i:operator«{std::ostream& os, const 

std::priority_queue<T,C,L,>& v) - Produces formatted textual output of a std::priority_queue class 
instance. The operator ::operator«(std::ostreatn&, const T&) must be declared somewhere allowing 
each component in the container to be properly displayed. Since there is no iterator for std::priority_queue 
instances, v is copied and items are printed from the top of this copied priority queue prior to their removal. 

template <class T, class C> std::ostream& ::operator«{std:wstream& os, const std::queue<T,C>& v) 
- Produces formatted textual output of the components of a std::stack class instance. The operator 


218 



::operator«(std::ostream&, const T&) must be declared somewhere allowing each component in the 
container to be properly displayed. Since there is no iterator for stdiiqueue instances, v is copied and items 
are printed from the top of this copied queue prior to their removal. 

template <class T, class C, class A> stdi:ostream& ::operator«{std::ostream& os, const 
std::set<T,C,A>& v) - Produces formatted textual output of the components of a stdr.set class instance. 
The operator ::operator«{std::ostream&, const T&) must be declared somewhere allowing each 
component in the container to be displayed. The output is sorted according to the sort order specified by 
the declaration of v. 

template <class T, class C> std::ostream& ::operator«(std:iostream& os, const std::stack<T,C>& v) - 
T) - Produces formatted textual output of the components of a stdr.stack class instance. The operator 
::operator«{std::ostream&, const T&) must be declared somewhere allowing each component in the 
container to be properly displayed. Since there is no iterator for std::stack instances, v is copied and items 
are printed from the top of this copied stack prior to their removal. 

template <class T> std::ostream& ::operator«(std'.:ostream& os, const std::valarray<T >& v) - 
Produces formatted textual output of the components of a std::valarray class instance. The operator 
::operator«istd::ostream&, const T&) must be declared somewhere allowing each component in the 
container to be displayed. 

template <class T, class A> std::ostream& z:operator«{std:iostream& os, const std::vector<T,A.>8i v) 
- Produces formatted textual output of the components of a stdr.vector class instance. The operator 
::operator«istd::ostream&, const T&) must be declared somewhere allowing each component in the 
container to be displayed. 

bool ::operator>{const sodl::Handle& hi, const sodl::Handle& h2) - This routine compares two 
sodhzHandle instances and returns true exactly when hl.getNodeQ > h2.getNode{), or when hl.getNodeQ 
= h2getNodei) and hi.getIndexQ > h2.getlndex(). 


219 






bool ::operator>(const sodl::Message& ml, const sodl::Message& m2) - This routine compares two 
sodU'.Message instances and returns true exactly when ml.getTimeQ > m2.getTimeO, or when 
ml.getTimeO = m2.getTimei) and ml.getIDQ > m2.getID{). 

bool ::operator<(coTist sodl::Handle& hi, const sodl::Handle& h2) - This routine compares two 
sodl'.'.Handle instances and returns true exactly when hLgetNodeQ < h2.getNodeO, or when hl.getNodeQ 
= h2.getNodeQ and hi.getlndexQ < h2.getlndex{). 

bool :loperator<(const sodl::Message& ml, const sodl::Message& m2) - This routine compares two 
sodU'.Message instances and returns true exactly when ml.getTimeQ < m2.getTimeQ, or when 
ml.getTimeQ - m2.getTimeQ and ml.getIDQ < m2.getIDQ. 

bool ::operator>=iconst sodl'.'.Handle &. hi, const sodl'.'.Handle & h2) - This routine compares two 
sodl'.'.Handle instances and returns true exactly when hLgetNodeQ > h2.getNodeQ, or when hi.getNodeQ 
= h2.getNodeQ and hi.getlndexQ > h2.getlndexQ. 

bool ::operator>=(const sodl::Message& ml, const sodl::Message& m2) - This routine compares two 
sodU'.Message instances and returns true exactly when mLgetTimeQ > m2.getTimeQ. or when 
ml.getTimeQ = m2.getTimeQ and ml.getIDQ > ml.getIDQ. 

bool ::opcrafor<=(const sodl'.'.Handle & hi, const sodl'.'.Handle &. h2) - This routine compares two 
sodl'.'.Handle instances and returns true exactly when hl.getNodeQ < h2.getNodeQ, or when hl.getNodeQ 
= h2.getNodeQ and hi.getlndexQ < hl.getIndexQ. 

bool ::opcrator<=(const sodU'.Message &. ml, const sodU'.Message Si m2) - This routine compares two 
sodUiMessage instances and returns true exactly when mLgetTimeQ < m2.getTimeQ, or when 
ml.getTimeQ = m2.getTimeQ and ml.getIDQ < m2.getIDQ. 

bool ::opcrator==(const sodl'.'.Handle Si hi, const sodl'.'.Handle Si h2) - This routine compares two 
sodl'.'.Handle instances and returns true exactly when hLgetNodeQ = h2.getNodeQ and hl.getlndexQ = 
h2.getIndexQ. 


220 





bool ::operfl<or==(const sodl::Message& ml, const sodh:Message& m2) - This routine compares two 
sodh-Message instances and returns true exactly when ml.getTimeQ - ml.getTimeQ and ml.getIDQ = 
m2.getID(). 

bool ::operator!=(const sodl::Handle& hi, const sodl::Handle& h2) - This routine compares two 
sodlr.Handle instances and returns true exactly when hl.getNodeQ ^ h2.getNode{) or hl.getIndexQ 
h2.getlndex(). 

bool ::operator!=iconst sodl:’.Message& ml, const sodl::Message& m2) - This routine compares two 
sodl’.iMessage instances and returns true exactly when mLgetTimeQ ^ m2.getTimeQ and ml.getIDQ ^ 
m2.getID{). 

template<class T> std::vector<T> ::resize_vector(int s, T d, std:tvector<T> v) - This routine will create a 
copy of the std::vector v, resized to size s, and padded with the value d in the case that v needs to grow. 

void ::staticlnit(int* argc, char* argv[\) - Performs static variable initialization particularly associated 
with type information in sodl::Process and sodhzMessage class definitions. It also initializes the view 
manager instance. 

B.3. SODL - GLUT interface 

There is a collection of SODL constructs used with the sodhiGLVTViewManager allowing SODL 
programs to display information to a window that is not located on the local host. 

This interface is implemented as a scene graph. For more detailed information on the notions of a scene 
graph, refer to (Foley 1996). Simulation system developers can have multiple views, each of which 
displays things completely independently. Each view owns a collection of graphics nodes each of which 
having an associated affine transformation. Each of these graphics nodes can themselves have a collection 
of child graphics nodes (again with their associated affine transformation) and so on. Child nodes inherit 
the affine transformation of their parent by which they multiply their own so that all of its child nodes 
inherit the new combined transformation. Additionally, nodes can have a collection of subordinate shapes 
with different properties (color, rendering mode, etc). The aggregate affine transformation associated with 


221 







the owner node is applied to the shapes in order to display them in the proper orientation, scale and location 
within the rendered scene. 


B.3.1. messageiAddNode 

This serves as parent class to the two message:AJ</iVo<f£ derivatives below. It serves little more purpose 
than to act as a type placeholder. 

Parent Message Construct: messagcAddSubordinate 

Derived Message Constructs: messageiAddNodelD, message:AddNode3D 

Receiving Processes: None 

Sending Processes: None 

B.3.2. message:AddNode2D 

This message is used to add multiple process:Node2D instances to a parent pTocess:Node2D or 
process: Vicw^D. Those receiving processes add as subordinate nodes those listed in 

AddSubordinate : :subrdinates . 

Parent Message Construct: messagerAddiVode 

Derived Message Constructs: None 

Receiving Processes: process:Node2D, process; VicH’2D 

Sending Processes: None 

B.3.3. message:AddNode3D 

This message is used to add multiple process:iVode3D instances to a parent processriVodeJD or 
proccss:View3D. Those receiving processes add as subordinate nodes those listed in 

AddSubordinate : isubrdinates. 


222 








Parent Message Construct: laessagewiddNode 
Derived Message Constructs: None 
Receiving Processes: processiNodeSD, process: Vicw3D 
Sending Processes: None 

B.3.4. message:AddShape 

The messagc'AddShape construct serves as parent to messskge:AddShape2D and message’AddShapeSD. 
It serves little more purpose than to act as a type placeholder. 

Parent Message Construct: messagcAddSubordinate 

Derived Message Constructs: messsigeAddShapeZD, messageAddShape3D 

Receiving Processes: None 

Sending Processes: None 

B.3.5. message:AddShape2D 

This message is used to add multiple processiShapeZD instances to a parent processiNodelD. Those 
receiving processes add as subordinate shapes those listed in AddSubordinate::subrdinates. 

Parent Message Construct: messageAddShape 

Derived Message Constructs: None 

Receiving Processes: process:A^ode2D 

Sending Processes: None 


223 






B.3.6. nnessage:AddShape3D 

This message is used to add multiple process:SAa/>tfJD instances to a parent process:Node3D. Those 
receiving processes add as subordinate shapes those listed in AddSubordinater.subrdinates. 

Parent Message Construct: message'AddShape 

Derived Message Constructs: None 

Receiving Processes: process:iV<w/e3D 

Sending Processes: None 

B.3.7. message:AddSubordinate 

The messageAddSubordinate construct provides a common mechanism for adding subordinate process 
references to a parent process. It contains an array of process instances that identify these subordinate 
processes. These messages are sent to processes to have the members listed in subordinates. 

Parent Message Construct: None 

Derived Message Constructs: message^4d</iVodc, messageAddShape, messageAddVertex, and 
message:Register. 

Receiving Processes: None 

Sending Processes: None 

Data Members: 

processisubordinatesU - An array of process handles that are intended to be added to a subordinate list 
managed by the recipient. 

Methods: 

method:add(public; void; processm;) - Adds the process n to back of the subordinates vector. 


224 







method:^e^rX(pubIic; bool;) - An overload of the sodl::Message::getTXQ. It will return true exactly 
when subordinates is not empty and sodl::Message::getTXi) is true. 

method:size(pubUc; ulong;) - Returns subordinates^izeQ to the calling routine. 

B.3.8. messageiAddVertex 

The messagc’AddVertex construct serves as parent to messageAddVertexlD and message’AddVertex3D. 
It serves little more purpose than to act as a type placeholder. 

Parent Message Construct: messageAddSubordinate 

Derived Message Constructs: messageAddVertexlD, messageAddVertex3D 
Receiving Processes: None 
Sending Processes: None 

B.3.9. message;AddVertex2D 

This message is used to add multiple process: Verteair2D instances to a parent ^roeess'.ShapelD. Those 
receiving processes add as subordinate shapes those listed in AddSubordinate::subrdinates. 

Parent Message Construct: messageAddShape 

Derived Message Constructs: None 

Receiving Processes: process:Shape2D 

Sending Processes: None 

B.3.10. message:AddVertex3D 

This message is used to add multiple process: ViertexJD instances to a parent process:Shape3D. Those 
receiving processes add as subordinate shapes those listed mAddSubordinateiisubrdinates. 

Parent Message Construct: messageAddShape 


225 






Derived Message Constructs: None 
Receiving Processes: process:Shape3D 
Sending Processes: None 

B.3.11. message:AddView 

This message is used to inform GUI objects that they have just been added to a view. They normally are 
generated by the view after the GUI object’s parent registered it with the view. 

Parent Message Construct: message:5riVa/ue 

Derived Message Constructs: None 

Receiving Processes: processrCone, process:Cu6e, process:iVode, process:Node2D, processiNodeSD, 
process'.Object, process:Polygon2D, process:Polygon3D, processiShape, processiSphere, 
process: Torus, process:Viertex2D, pTocess:Vertex3D 

Sending Processes: process: V/eH’2D, process; Vich’JD 

B.3.12. message:RefreshDisplay 

This message causes a recipient process: Vich’ instance to refresh its display. 

Parent Message Construct: messagciSetValue 
Derived Message Constructs: None 
Receiving Processes: process; View 
Sending Processes: process:yicH' 


226 






B.3.13. messageiRegister 

Register messages register graphics components with the view that is their parent. Normally a node will 
register its child nodes and shapes. Polygons register their vertices. The objects being registered are stored 
in the AddSubordinateiisubrdinates. 

Parent Message Construct; message’AddSubordinate 

Derived Message Constructs; message:RegisterNode, messageiRegisterShape, messageiRegisterVertex 
Receiving Processes; None 
Sending Processes; None 

Data Members; 

gvm::object_index:index{ (ulong) -1) - Stores object index of the sender with respect to the gvmy.View 
instance that will eventually get the message derived from this construct. 

B.3.14. message;RegisterNode 

This construct acts mainly as the type placeholder and parent construct for the two derived messages. 

Parent Message Construct; messageiRegister 

Derived Message Constructs; messageiRegisterNodelD, message:RegisterNode3D 
Receiving Processes; None 
Sending Processes; None 

B.3.15. message:RegisterNode2D 

This message registers processiNodeZD instances with a process: Vfca'2D. The process handles are listed 
in AddSubordinate: :subrdinates. 

Parent Message Construct; messageiRegisterNode 


227 






Derived Message Constructs; None 
Receiving Processes: process: VicwiD 
Sending Processes: process:Nodc2D 

B.3.16. message:RegisterNode3D 

This message registers process:iV<K/e3D instances with a process: View3D. The process handles are listed 
in AddSubordinate : isubrdinates. 

Parent Message Construct: messageiRegisterNode 

Derived Message Constructs: None 

Receiving Processes: process: Vich’JD 

Sending Processes: process:Node3D 

B.3.17. message:RegisterShape 

This construct acts mainly as the type placeholder and parent construct for the two derived messages. 

Parent Message Construct; messageiRegister 

Derived Message Constructs: message:RegisterShape2D, message:RegisterShape3D 
Receiving Processes: None 
Sending Processes: None 

B.3.18. message:RegisterShape2D 

This message registers processiShapeZD instances with a process:VieH’2D. The process handles are listed 
in AddSubordinate: isubrdinates. 

Parent Message Construct; messageiRegisterShape 


228 






Derived Message Constructs: None 
Receiving Processes: process: View2D 
Sending Processes: process:Node2D 

B.3.19. message:RegisterShapeSD 

This message registers process:Shape3D instances with a process: V/eH'3D. The process handles are listed 
in AddSubordinate : isubrdinates. 

Parent Message Construct: message:RegisterShape 

Derived Message Constructs: None 

Receiving Processes: process: VicH’dD 

Sending Processes: process:Node3D 

B.3.20. message:RegisterVertex 

This construct acts mainly as the type placeholder and parent construct for the two derived messages. 
Parent Message Construct: message:Register 

Derived Message Constructs: messagc:RegisterVertex2D, message:RegisterVertex3D 
Receiving Processes: None 
Sending Processes: None 

B.3.21. message:RegisterVertex2D 

This message registers process: VerteA:2D instances with a process: Vicw2D. The process handles are listed 
in AddSubordinate : isubrdinates. 

Parent Message Construct: messageiRegisterShape 


229 






Derived Message Constructs: None 
Receiving Processes: process: Vicw2D 
Sending Processes: process:Shape2D 

B.3.22. message:RegisterVertexSD 

This message registers process: VcrtcjcJD instances with a process: VfewJD. The process handles are listed 
in AddSubordinate : isubrdinates. 

Parent Message Construct: message:RegisterShape 

Derived Message Constructs: None 

Receiving Processes: process: V/ch’5D 

Sending Processes: process:S/ia/;edD 

B.3.23. message:SelectiveActivate 

This message is delivered to a process and allows the respective gvmr.Object instances controlled by 
different gvmr.View instances to be selectively activated or deactivated. A processiObject instance, upon 
receiving a inessage:SelectiveActivate will send a message:Sefc4crfve to each views[i\ with the active flag 
set to active[i]. 

Parent Message Construct: None 
Derived Message Constructs: None 
Receiving Processes: processiObject 
Sending Processes: None 

Data Members: 


230 






process:v/ews[] - This is the list of process: VicH' instances affected by the change in the active flag. 

bool:acftVe[] - Each element in this array sets the active flag in the associated process:^!^^ for the 
gvmr.Object corresponding to this processiObject. 

Methods: 

method:add(public; void; process:v; booha;) - This calls methods views.push_back(y) and 
active.push_back(a) to insert the view and active flag for that view into their respective vectors. 

method:getVieH'(public; process; ulong::;) - This will return the i* process instance in the view vector 
(i.e. vicwfi]). 

method:gefActivc(public; bool; ulong::;) - This will return the process instance in the active vector (i.e. 
active[i]). 

method:f(ze(public; ulong;) - This method returns the number of elements in the view array (i.e. 
viewMzeQ). This should also be the number of elements in the active array, and will be if method:add is 
used instead of manually adding elements to the arrays. 

B.3.24. message:SetActive 

Upon receipt of this message, a processlOb/ecf instance will set its active flag to that of the aetive variable 
in the message payload. It will then broadcast additional messageiSetActive instances to the views with 
which the processinstance has been registered. 

Parent Message Construct: message:SetDefaultActive 

Derived Message Constructs: None 

Receiving Processes: process’.Object, process: V/cm’ 

Sending Processes: process 


231 








B.3.25. message:SetAffine 

This sets various affine transform values. These allow graphical objects to be moved within a scene. 


Parent Message Construct: message:SetVd/uc 

Derived Message Constructs: mcss&gf.SetAffine2D, message:SetAffine3D 
Receiving Processes: None 
Sending Processes: None 

Data Members: 

GLdouble:cfrRof[] - Specifies a center of rotation. 

GLdouble:ctrScafc[] - Specifies a scaling center. 

GLdouble:rot[] - Specifies a rotation angle about each axis. 

GLdouble:scafc[] - Specifies a scaling factor along each axes. 

GLdouble:trans[] - Specifies a translation factor along each axes. 

Methods: 

method:set(public; void; std::vector<GLdouble>:cr; std::vector<GLdouble>:cs'; 

stdy.vector<GlAovb\e>\r’, std::vector<GLdouble>:j; sf</::vector<GLdouble>:f;) - This sets ctrRot, 
ctrScale, rot, scale, and trans to cr, cs, r, s, and t respectively, using ::resize_yector{ ... ) to copy the 
values and ensure that the size of the vectors remains unchanged. That is, if this message is implemented 
as a messageiSetAffinelD instance, each of the arrays has size 2. The '.'.resize_yector{ ... ) function is 
used to ensure that this size remains fixed after the assignment. 

B.3.26. message:SetAffine2D 

Specializes messagc'.SetAffine for 2D affine transformations. 


232 





Parent Message Construct: messagezSetAffine 
Derived Message Constructs: None 
Receiving Processes: process:iVodc2D, process: VieH'2D 
Sending Processes: process:A^odc2D 

Methods: 

method:irait(public; void;) - Initializes the data members so that the aggregate affine transform is the 
identity. This involves resizing all of the data members to 2 elements with values 0.0 (except 
SetAffineiiscale, which is initialized to <1.0, 1.0>). 

method:se<C/rRotorioii(publlc; void; GLdoublerx; GLdoublety;) - This method sets the value of 
SetAffine:'.ctrRot to ::make_yector(2, x, y). 

method:setCtrji’otation(public; void; std::vcctor<GLdouble>:v;) - This method sets the value of 
SetAffine::ctrRot to -.‘.resize_vector(2, 0.0, v). 

method:sefC^Sca/e(public; void; GLdouble:^; GLdoublery;) - This method sets the value of 

SetAffine::ctrScale to ::make_vector{2, x, y). 

method:se^Cir5ca/e(public; void; s^d:;vector<GLdouble>:v;) - This method sets the value of 

SetAffine-.-.ctrScale to ::resize_vector{2, 0.0, v). 

method(public; void; GLdouble:x; GLdouble:y;) - This method sets the value of 

SetAffine:‘.rot to ::make_vector(2, x, y). 

method:setRotab'o/i(public; void; std::vector<GLdouble>:v;) - This method sets the value of 

SetAffine-.-.rot to ::resize_vector{2, 0.0, v). 

method:set5ca/e(public; void; GLdoublezx; GLdoubleiy;) - This method sets the value of 
SetAffine::scale to ::make_vector(2, x, y). 


233 







method:je^Scflfe(public; void; sfd::vector<GLdouble>:v;) - This method sets the value of 
SetAffine"scale to '.'.resize_vector{2, 1.0, v). 

method:se<7’ranste<ion(public; void; GLdoublerjc; GLdoubleij;) - This method sets the value of 
SetAffine::trans to ::make_vector(2, x, y). 

methodisefTra/isidfion(public; void; std::vector<GLdouble>:v;) - This method sets the value of 
SetAffine'.'.trans to '.'.resize_vector{2, 0.0, v). 

B.3.27. message:SetAffine3D 

Specializes message'.SetAffine for 3D affine transformations. 

Parent Message Construct: message'.SetAffine 
Derived Message Constructs: None 
Receiving Processes: processprocess: VicH'JD 
Sending Processes: ^rocess'.NodeSD 

Methods: 

method:iniY(public; void;) - Initializes the data members so that the aggregate affine transform is the 
identity. This involves resizing all of the data members to 3 elements with values 0.0 (except 
SetAffine'.'.scale, which is initialized to <1.0,1.0, 1.0>). 

method:setCtrRotation(public; void; GLdoubletx; GLdouble:y; GLdoubletz;) - This method sets the 
value of SetAfftneiictrRot to ::make_vector{3, x, y, z). 

method'.setCtrRotationipuhlic; void; std::vector<GLdouble>:v;) - This method sets the value of 
SetAffine::ctrRot to '.'.resize_vector{3, 0.0, v). 

method:sefCP'Sca/e(pubUc; void; GLdoublerx; GLdoublery; GLdoubIe:z;) - This method sets the value 
of SetAffine'.'.ctrScale to ::make_vector(3, x, y, z). 


234 


method:sefC<rScate(public; void; sfd::vector<GLdouble>:v;) - This method sets the value of 
SetAffine::ctrScale to ::resize_vector(3, 0.0, v). 

method:se^J7otob'ora(public; void; GLdoubleu; GLdouble:>’; GLdouble:z;) - This method sets the value 
of SetAffine::rot to :‘.make_vector(3, x, y, z). 

inethod:5:e^/{otob'on(pubIic; void; sfd::vcctor<GLdouble>:v;) - This method sets the value of 
SetAffine::rot to ::resize_yector(3, 0.0, v). 

method:sef5ca/e(public; void; GLdoubler^:; GLdouble:>'; GLdouble:z;) - This method sets the value of 
SetAffineiiscale to ::make_vector(3, x, y, z). 

inethod:s:e^Sca/e(public; void; s^d::vector<GLdouble>:v;) - This method sets the value of 
SetAffineiiscale to ;:resize_vector(3, 1.0, v). 

method:setTranslation{puhlic; void; GLdouble:^:; GLdoublery; GLdoubletz;) - This method sets the 
value of SetAffineiitrans to i:make_vector(3, x, y, z). 

metbodtsf^rran^^ab'onCpublic; void; sfd::vector<GLdouble>:v;) - This method sets the value of 
SetAffineiitrans to iiresize_vector{3, 0.0, v). 

B.3.28. messageiSetColor 

This message is used to set the color of shapes. 

Parent Message Construct: messageVector 
Derived Message Constructs: None 
Receiving Processes: processiShape, process: Vieiv 
Sending Processes: process:Sbapc 

Methods: 


235 






method:5e^(public; void; sfd::vector<GLdouble>:c;) - Sets the value of the vec to ::resize_vector{4, 1.0, 
c). 

method:£et(pubUc; void; GLdouble:r; GLdouble:g; GLdouble:^; GLdoubIe:a;) - Sets the value of vec 
to ..make_vector(A, r, g, b, a). 

method:set(public; void; GLdoubleir; GLdouble:^; GLdouble:^;) - Sets the value of vec to 
::make_vector{4, r, g, b, 1.0). 

method:red(pubIic; GLdouble;) - This routine returns the red color component, vec[0]. 
method:green(pubbc; GLdouble;) - This routine returns the green color component, vec[l]. 
method:6fue(pubIic; GLdouble;) - This routine returns the blue color component, vec [2]. 
method:a/pba(public; GLdouble;) - This routine returns the alpha color component, vec[l]. 

B.3.29. message:SetConeSize 

This message construct is used to set the parameters of a cone. 

Parent Message Construct: messageiSetValue 
Derived Message Constructs: None 
Receiving Processes: process:Cone, process: ViewSD 
Sending Processes: process:Cone 

Data Members: 

GLdouble:Aase - Specifies the radius of the cone base. 

GLdouble:/iei^bt - Specifies the cone height. 

GLint:s/ices - Specifies the number of radial slices into which GLUT should break the cone. 


236 







GLint:stoc/ts - Specifies the number of stacked into which GLUT should break the cone. 

Methods: 

methodzsetfpublic; void; GLdouble:^?; GLdouble:h; GLint:.rZ; GLint:s'{;) - Sets base to b, height to h, 
slices to si, and stacks to st. 

B.3.30. message:SetCubeSize 

This message construct is used to set the size of the cube. 

Parent Message Construct: message:SetValue 
Derived Message Constructs: None 
Receiving Processes: processzCube, process: VieH’JD 
Sending Processes: process:CuAe 

Data Members: 

GLdoubIe:size - New edge length for the cube. 

Methods: 

method:setfpublic; void; GLdouble:.r;) - Sets size to s. 

B.3.31. messageiSetCylinderSize 

Parent Message Construct: messageiSetValue 
Derived Message Constructs: None 
Receiving Processes: processiCylinder, process: Vien’JD 
Sending Processes: processiCylinder 


237 






Data Members: 


GLdouble:ra</ius - Value for the radius of the cylinder. 

GLdoubIe:/e/zgtA - Value for the length of the cylinder. 

GLintzsides - Number of sides per ring. 

GLintin'/igs - Number of radial slices 

Methods: 

method:set(public; void; GLdoubleu'; GLdouble:o; GLintts; GLintrr;) - Sets inner to i, outer to o, 
sides to s, and rings to r. 

B.3.32. message:SetDefaultActive 

This message is used to set the default value of the active flags within a processiObject instance. It does 
not change any of the existing flag values for gvmiiObject instance in any views in which the 
processrOi/cct is already registered. Instead, any future registrations will create the gvm::Object with the 
default activation flag having been set to the new value specified herein. 

Parent Message Construct: message:SetVdfae 

Derived Message Constructs: None 

Receiving Processes: process:Oty'ecf, processrVien’ 

Sending Processes: processrOty'cct 

Data Members: 

bool:active(true) - Used to set the default active flag in the receiving processrOtyect instance. 

Methods: 


238 



method:£e^(public; void; bool:a;) - Sets active to a. 

B.3.33. message:SetLabel 

Each gvm::Object can have a label to assist in debugging. This label can be set through the corresponding 
processiObject instance in the simulation by first sending a message to that processiObject and then by 
forwarding new message:SetLabel instances to the views where the processiObject is registered. 

Parent Message Construct: messageiSetValue 

Derived Message Constructs: None 

Receiving Processes: processiObject, process: Vicm’ 

Sending Processes: processiObject 

Data Members: 

stdiistringilabel - The value of the new label for the object. 

Methods: 

metbod:set(public; void; stdiistringic;) - Set label to c. 
metbod:get(public; stdiistring;) - Return label to the calling routine. 

B.3.34. message:SetMode 

Message to set the rendering mode for shapes. The mode can take on any of the OpenGL rendering modes, 
listed below. One note on this is that the pre-defined 3D shapes (process;Cu6e, process:Cone, etc) use 
GLUT to actually perform the rendering. GLUT only allows solid and wire frame rendering modes for 
these shapes. 

Parent Message Construct: messagerSetVdfue 
Derived Message Constructs: None 


239 






Receiving Processes: processiShape, process: ViewiZ), process: ViewJi? 

Sending Processes: process:S/ia/7e 

Data Members: 

GLenum:gr_mode - Takes on one of the following values GL_POINTS, GL_LINES, 
GL_LINE_STRIP, GL_LINE_LOOP, GL.TRIANGLES, GL_TRIANGLE_STRIP, 
GL_TRIANGLE_FAN, GL_QUADS, GL_QUAD_STRIP, GL_POLYGON. The meaning of these 
values can be found in (Wright 1996) page 172. 

Methods: 

method:set(pubIic; void; GLenum:m;) - Sets mode to m. 

B.3.35. messageiSetPointSize 

This message is sent to a view to set the point size parameter for rendering single vertices in the receiving 
view. It will cause the process: Viciv instance to set the size parameter to the size portion of the payload of 
the message. When a screen is rendered, the process:Vrew instance will call ::glPointSize{size) prior to 
rendering the scene. 

Parent Message Construct: message:5etVa/ue 
Derived Message Constructs: None 
Receiving Processes: process: View 
Sending Processes: None 

Data Members: 

GLfloat:5ize(1.0) - Size parameter for single vertices in the receiving view to be rendered. 

Methods: 


240 






method: set(public; void; GLfloat:^;) - Sets size to s. 


B.3.36. message:SetPosition 

Sets location of the GLUT window. 

Parent Message Construct: None 
Derived Message Constructs: None 
Receiving Processes: process: View 
Sending Processes: None 

Data Members: 

inttr - X coordinate of the window left side. 

inttj; - y coordinate of the window top side. 

Methods: 

method:set(public; void; int:X; int:^;) - Setso: toXand_y to Y. 

B.3.37. message:SetRefresh 

Used to set the simulation time between view refreshes. 

Parent Message Construct: None 
Derived Message Constructs: None 
Receiving Processes: process: VicH’ 

Sending Processes: None 

Data Members: 


241 







Aovib\e'.refreshInterval - Simulation time delta between view refreshes. 

Methods: 

niethod:set(pubIic; void; double:r;) - Sets refreshinterval to r. 

B.3.38. message:SetRotation2D 

This message is used to set the rotation angle (about the Z axis) for a processiNodelD instance. 

Parent Message Construct: mtssage:SetVector2D 

Derived Message Constructs: None 

Receiving Processes: process:Node2D, process:View2D 

Sending Processes: proctss:Node2D 

B.3.39. message;SetRotation3D 

This message is used to set the rotation angles for a process:Node3D instance. 

Parent Message Construct: message: VectorJD 
Derived Message Constructs: None 
Receiving Processes: process:iVode5D, process: Vi'ch'JD 
Sending Processes: pTocess:Node3D 

B.3.40. message;SetRotationCenter2D 

This message is used to set the center of rotation for a process:iVo<fe2D instance. 

Parent Message Construct: mcssage:SetVector2D 
Derived Message Constructs: None 


242 






Receiving Processes: process:A^(M/e2Z), process: VieM’2D 
Sending Processes: process:Node2D 

B.3.41. nnessage:SetRotationCenter3D 

This message is used to set the center of rotation for a process:Node3D instance. 

Parent Message Construct: messstg,e:SetVector3D 

Derived Message Constructs: None 

Receiving Processes: process:iVodc3D, process: Vien’dD 

Sending Processes: process:Node3D 

B.3.42. message:SetScale2D 

This message is used to set the scaling factor for a process:Node2D instance. 

Parent Message Construct: message:SetVector2D 

Derived Message Constructs: None 

Receiving Processes: process:iVodc2D, process: ViieM'2D 

Sending Processes: process;Node2D 

B.3.43. message:SetScale3D 

This message is used to set the scaling factor for a pTocess:Node3D instance. 

Parent Message Construct: message:SetVector3D 

Derived Message Constructs: None 

Receiving Processes: process:lVode5D, process: ViewJD 


243 






Sending Processes: process:Node3D 


B.3.44. message:SetScaleCenter2D 

This message is used to set the scaling center for a processtNode2D instance. 

Parent Message Construct: message:SctVcctor2D 

Derived Message Constructs: None 

Receiving Processes: process:iVode2D, process: View2D 

Sending Processes: process:Node2D 

B.3.45. message:SetScaleCenter3D 

This message is used to set the scaling center for a process:iVode3D instance. 

Parent Message Construct: message:SetVector3D 

Derived Message Constructs: None 

Receiving Processes: process:Node3D, process: ViewSD 

Sending Processes: process:Node3D 

B.3.46. messageiSetSize 

This message requests a change in the size of the GLUT view port window. 
Parent Message Construct: None 
Derived Message Constructs: None 
Receiving Processes: process: VicM’ 

Sending Processes: None 


244 






Data Members; 


mV.width - New width of the view port window. 
mtiheight - New height of the view port window. 

Methods: 

method:set(public; void; int:w; int:/z;) - Sets width to w and height to h. 

B.3.47. messageiSetSphereSize 

Parent Message Construct: messageiSetValue 
Derived Message Constructs: None 
Receiving Processes: processiSphere, process;VicwSD 
Sending Processes: processiSphere 

Data Members: 

GLdoubletrodius - Radius of the sphere, 

GLint:s/ices - Number of radial slices in the sphere. 

GLintistoc&S' - Number of lateral stacks in the sphere. 

Methods: 

method:set(public; void; GLdouble:r; GLint:^/; GLintzst;) - Sets radius to r, slices to si, and stacks to 

St. 

B.3.48. messageiSetTorusSize 

Parent Message Construct: messageiSetValue 


245 









Derived Message Constructs: None 


Receiving Processes: process'.Torus, processiVicwiD 
Sending Processes: processiTorus 

Data Members: 

GLdoubleit/iner - Value for the inner radius of the torus. 

GLdouble:oiiter - Value for the outer radius of the torus. 

GLint:sidcs - Number of sides per ring. 

GLint:rings - Number of radial slices 

Methods: 

method:set(public; void; GLdouble:i; GLdoublero; GLint:.;; GLint:r;) - Sets inner to i, outer to o, 
sides to s, and rings to r. 

B.3.49. message;SetTranslation2D 

This message is used to set the translation for a process:Node2D instance. 

Parent Message Construct: messageiSetVectorZD 
Derived Message Constructs: None 
Receiving Processes: process:iVodc2D, process: VieH’2D 
Sending Processes: process;iVode2D 

B.3.50. message:SetTranslation3D 

This message is used to set the translation for a process:Node3D instance. 


246 



Parent Message Construct: message:SetVector3D 
Derived Message Constructs: None 
Receiving Processes: processzNodeSD, process: Vich'JD 
Sending Processes: process:Node3D 

B.3.51. message:SetValue 

Parent Message Construct: None 

Derived Message Constructs: message :Add View, messagezSetAffine, mcssageiSetConeSize, 
messBgezSetCubeSize, messagezSetDefaultActive, messagezSetMode, messagezSetSphereSize, 
mcssageiSetTorusSize, messagczSetVector 

Receiving Processes: None 

Sending Processes: None 

Data Members: 

gvm::objectJndex’.index( (ulong) (-1) ) - Index of sending graphics object. Used only when the 
destination is a View process. 

B.3.52. message:SetVector 

Base class for messages sending sfd;:vecior<::GLdouble> instances between processes. 

Parent Message Construct: messagezSetValue 

Derived Message Constructs: mcssagciSetColor, messagezSetVectorlD, messagezSetVector3D 
Receiving Processes: None 
Sending Processes: None 


247 






Data Members: 


::GLdouble:vec[] - Vector to send to the destinations. 

Methods: 

method:get(public; std::vcctor<::GLdouble>;) - Return vec to the calling routine. 
method:get(public; ::GLdouble; ulongri;) - Return the i* component of vec to the calling routine. 

B.3.53. message;SetVector2D 

Base class for messages sending two dimensional sfdttvectorctGLdoublo instances between processes. 
Parent Message Construct: message:SctVector 

Derived Message Constructs: messageiSetRotationlD, message:SetRotationCenter2D, 

message:SetScale2D, message:SetScaleCenter2D, message:SetTranslation2D 

Receiving Processes: None 

Sending Processes: None 

Methods: 

method:init(public; void;) - Initializes the SetVectoriivec to <0.0, 0.0> or, if this is instantiated as a 
message:SetScale2D, it is initialized to <1.0,1.0>. 

method;set(public; void; ;:GLdouble:;c; riGLdouble:}/;) - Sets the value of SetVectoriivec to 
::make_vector(2, x, y). 

method:set(public; void; s/d::vector<::GLdouble>:v;) - Set the value of SetVectoriivec to 
iiresize_vector{2, 0.0, v) or, if the message is instantiated as a inessagciSetScale2D, it is set to 
iiresize_yector(2, 1.0, v). 


248 






B.3.54. message:SetVector3D 

Base class for messages sending three dimensional sf£i::vcc<or<::GLdouble> instances between processes. 


Parent Message Construct: messagezSetVector 

Derived Message Constructs: message:SetRotation3D, message:SetRotationCenter3D, 

inessage:SetScale3D, message:SetScaleCenter3D, message'.SetTranslation3D 

Receiving Processes: None 

Sending Processes: None 

Methods: 

method:init(public; void;) - Initializes the SetVectonzvec to <0.0, 0.0, 0.0> or, if this is instantiated as a 
messagezSetScaleSD, it is initialized to <1.0, 1.0, 1.0>. 

niethod:set(public; void; ::GLdouble:Ar; ::GLdouble:>'; ::GLdouble:z;) - Set the value of 
SetVectorzzvec to ::make_vector(3, x, y, z). 

niethod:set(public; void; std:;vector<::GLdouble>:v;) - Set the value of SetVectorzzvec to 
zzresize_vectori3, 0.0, v) or, if the message is instantiated as a messagczSetScale3D, it is set to 
zzresize_vector{3, 1.0, v). 

B.3.55. message;SetVertex2D 

A message to set the location of a processzVertex2D instance. 

Parent Message Construct: message;Setyector2D 
Derived Message Constructs: None 
Receiving Processes: process: Vertex2D, process; VicH'2D 
Sending Processes: process;Verfex2D 


249 







B.3.56. message:SetVertex3D 

A message to set the location of a processrVcrtexJZ) instance. 

Parent Message Construct: message:SctViector3D 
Derived Message Constructs: None 
Receiving Processes: process: VertcxJD, process: 

Sending Processes: process:Vcrtex5D 

B.3.57. process:Cone 

This process construct allows a cone to be viewed within multiple process: VieM'3D instances. It references 
a gvmiiCone instance owned by each gvm::View3D instance with which the process:Cone is registered. 

Parent Process Construct: process:Shape3D 

Derived Process Constructs: None 

Data Members: 

double:ftase(0.0) - Size of the cone base. 

dov.hle:height{0.0) - Size of the cone base. 

int:sZiccs(0) - Number of slices into which the cone is segmented. 

int:stacks(0) - Number of stacks composing the cone. 

Methods: 

method:set(pubIic; void; double:!?; double:/:; int:.r/; int:j'r;) - Sets base to b, height to h, slices to si, and 
stacks to St. 

modezDefault Nodes: 


250 






noAe'.addView[AddView'.in\[SetConeSize'.out\- Upon receiving a request to add view handle, this node 
will report to the new view the current size parameters of the cone. 

node:setSize[SetConeSize:in][SetConeSize:out[\]- Upon receiving a change in any of the size parameters 
for the cone, this node will Sets base to in.base, height to in.height, slices to in.slices, and stacks to 
in.stacks and then report these changes to the views in which this cone has been registered. This will allow 
the associated gvmr.Cone instance in those views to be properly updated. 

B.3.58. process:Cube 

This process construct allows a cube to be viewed within multiple process:VieM'3f) instances. 
agvm::Cube instance owned by each process:ViciriD process in which this cube instance has 

Parent Process Construct: process:Shape3D 

Derived Process Constructs: None 

Data Members: 

double:sizc(0.0) - Length of the cube edges 
modeiDefault Nodes: 

node:addView[AddVie}v:in\[SetCubeSize:out] - Upon receiving a request to add a view handle, this node 
will report to that view the current size parameter of the cube. 

node:setSize[SetCubeSize:in][SetCubeSize:out[]}- Upon receiving a change in any of the size parameter 
for the cube, this node set size to in.size and report to the parent views in which it is registered, those 
changes. This will allow the associated gvmiiCube instance in those views to be properly updated. 


It references 
registered. 


251 







B.3.59. process:Cylinder 

This process construct allows a cylinder to be viewed within multiple process: V/ewJD instances. It 
references a gvm::CyUnder instanee owned by each process:V/ cm'JD process in which this process is 
registered. 

Parent Process Construct: pTocess:Shape3D 

Derived Process Constructs: None 

Data Members: 

double:radiMs(0.0) - Radius of the cylinder. 
double:/cMgt/i(0.0) - Length of the cylinder. 
int:sidcs(0) - Number of sides the cylinder will have. 
int:n'figs(0) - Number of rings the cylinder will have. 

Methods: 

method:set(public; void; double:ra(i; double:/; int:5'; int:r;) - Sets radius to rad, length to /, sides to s, 
rings to r. 

moAeiDefault Nodes: 

xioAe:addView[AddView.iri\[SetCylinderSizewui\ - Upon receiving a request to add a view handle, this 
node will report to that view the current size parameters of the cylinder. 

noAe:setSize[SetCylinderSize‘.iri\[SetCylinderSize’.ou^i\ - Upon receiving a change in any of the 
parameters for the cylinder, this node will report to the process: V/cw instances where this eyUnder is 
registered to update the associated gvi»i::Cy//fw/er instances. 


252 





B.3.60. process:Dodecahedron 

This process construct allows a dodecahedron to be viewed within multiple process: ViewJD instances. It 
references agvm::Dodecahedron instance owned by each process: V/cwJD process in which this process is 
registered. 

Parent Process Construct: process:Shape3D 

Derived Process Constructs; None 

B.3.61. process:Icosahedron 

This process construct allows a dodecahedron to be viewed within multiple process :VieH’5D instances. It 
references a gvmiilcosahedron instance owned by each process: View3D process in which this process is 
registered. 

Parent Process Construct: process:Shape3D 

Derived Process Constructs: None 

B.3.62. process:Node 

Nodes server the purpose of retaining a list of subordinate process:iVodc and processiShape instanees and 
the affine transformation information for both. They have an associate gvm::Node instance directly 
managed in the view processes in which this node is registered. 

Parent Process Construct; processiObject 

Derived Process Constructs; process:Node2D, processtNode3D 

Member Data: 

GLdouble:co/or[4](-1.0) - Default color for all subordinate processes. The default value (-1.0) indicates 
to the associate gvm::Node instances that they are to derive their color from parent gvm::Node instances. 

pTocess:nodeList[] - List of subordinate processzNode instances. 


253 







GLfloat:pfSize(-1.0) - Default point size for all subordinate processes. The default value (-1.0) indicates 
to the associate gvmiiNode instances that they are to derive their point size from parent gvm::Node 
instances. 

process:s/ia/>eLis<[] - List of subordinate processiShape instances. 

Methods: 

method:set_coZor(public; void; GLdouble:red; GLdoubler^reen; GLdoubIe:hlue; GLdoubleraZpAa;) - 
Set color to <red, green, blue, alpha> 

inethod:set_co/or(public; void; std::vcctor<GLdouble>:c;) - Set color to c. 
method:set _point_size(puhl\c; void; GLfloatrp^;) - SetptSize to ps. 
mode:Default nodes: 

node:addView[AddView:in][SetColor:sc, SetPointSizexsps] - Upon notification of the addition of this 
processiNode instance to agviw:;y/cH’ associated with a process:View, this node will set the default color 
and point size settings for the associate gvmiiNode. 

node:setColor[SetColor:in][SetColor:out[]]- Upon a color change request, all of the processrView 
instances v/ith gvm::Node instances associated with this processiNode are notified of the change. 

node:setPomtSize[SetPointSize:in][SetPointSizeiout[]]- Upon a point size change request, all of the 
process:V/cH' instances with gvm::Node instances associated with this processiNode are notified of the 
change. 

B.3.63. process:Node2D 

This process construct further specializes processiNode to govern two-dimensional graphics objects. 

Parent Process Construct: processiNode 

Derived Process Constructs: None 


254 






Data Members: 


double:c<r/?ot[2](0.0) - Center of rotation. 
double:c<rScflfe[2](0.0) - Center for the sealing. 
double:ror[2](0.0) - Rotation angles. 
double:scafe[2](1.0) - Scaling factors. 
double:fraiis[2](0.0) - Translation factors. 
moAt'.Default Nodes: 

noAit'.addNode[AddNode2D\in\[RegisterNode2D'.out\W - This will add subnodes to this process:iV£w/e2D. 
The new subnodes will then need to be registered with all process: Vieit'2D instances with which this 
process:Nodc2D is registered. Sending a message:RegisterNode2D message to those process:View2D 
instances does this. 

noAe'.addShape[AddShape2D'.in\[RegisterShape2D:outW^ - This will add subnodes to this 
process:S/iape2D. The new shapes will then need to be registered with the process:Viea’2D instances 
with which this process:Node2D is registered. Sending a message:RegisterShape2D message to those 
process:View2D instances does this. 

i\oAc’.addView[AddView‘.iri\[RegisterNode2Dim, RegisterShape2D:rs, SetAffine2Disd\ - Upon receipt of 
a messagetAddVieH' instance, this process:iVodc2D needs to register all of its subnodes and shapes with 
that view. The message:RegisterNode2D and messageiRegisterShape2D messages do that. In addition, 
this process:Node2D instance needs to inform the process: Vicm'2D as to the affine transform parameters. 

iioAe:setAffine[SetAffine2D:in][SetAffine2D:out[]] - This node sets the affine transform parameters of 
this process:iV<«fc2D instance. That change is then passed to the process: VieM'2D instances in which this 
process:Node2D is registered in order to update the associate gvm::Node2D affine transform parameters. 


255 






node:setCtrRot[SetRotationCenter2D:in][SetRotationCenter2D:out[J\ - This message sets the rotation 
center of this process:Afodc2Z) instance. That change is then passed to the process: Vicw2D instances in 
which this process:Node2D is registered to update the associate gvm::Node2D rotation center. 

nodc:setCtrScale[SetScaleCenter2D:in][SetScaleCenter2D’.out[]] - This message sets the scaling center 
of this process:iVodc20 instance. That change is then passed to the process:V ich'2£) instances in which 
this pToccss:Node2D is registered to update the associate gviM::iVodc2D scaling center. 

node:setRotation[SetRotation2D:in][SetRotation2D:out[]] - This message sets the rotation angle (in 
radians) of this pTocess:Node2D instance. That change is then passed to the process:Fiew’2D instances in 
which this process:Alo<fc2Z) is registered to update the associate gvm::Node2D rotation value. 

node:setScale[SetScale2D:in][SetScale2D:out[]] - This message sets the scale of this process:A^ode2£) 
instance. That change is then passed to the process:VieM'2iD instances where this process:iVodc2D is 
registered so that the associated gvm::Node2D instances may have their scaling factors updated. 

node:setTranslation[SetTranslation2D:in][SetTransIation2D:out[W - This message sets the translation 
factor of this process:Node2D instance. That change is then passed to the process:VieM>2Z> instances in 
which this process:Node2D is registered to update the associate gvm::Node2D translation factors. 

B.3.64. process;NodeSD 

This process construct further specializes proccsszNode to govern two-dimensional graphics objects. 

Parent Process Construct: process:iVo</c 

Derived Process Constructs: None 

Data Members: 

double:ctr/?ot[3](0.0) - Center of rotation. 
double:c<rScate[3](0.0) - Center for the scaling. 


256 










double:rof[3](0.0) - Rotation angles. 


double:scafe[3](1.0) - Scaling factors. 
double:<rflns[3](0.0) - Translation factors. 
moAt'.Default Nodes: 

noA&:addNode\AddNode3D‘.in\\_RegisterNode3D‘.out\W - This will add subnodes to this process:iVode3D. 
The new subnodes will then need to be registered with all process: VicH’JD instances with which this 
process:Node3D is registered. Sending a message:RegisterNode3D message to those process:View3D 
instances does this. 

noAt'.addShape[AddShape3D:in\[RegisterShape3D:out[W - This will add subnodes to this 
pToctss'.Shape3D. The new shapes will then need to be registered with the process:VicH’3Z) instances 
with which this proce.ss:Node3D is registered. Sending a message:RegisterShape3D message to those 
process:View3D instances does this. 

noAe:addView[AddView’.in\[RegisterNode3D:rn, RegisterShape3D:rs, SetAffine3D:sd\ - Upon receipt of 
a message’AddView instance, this process:Nodc3D needs to register all of its subnodes and shapes with 
that view. The message:RegisterNode3D and message:RegisterShape3D messages do that. In addition, 
this pTocess:Node3D instance needs to inform the process: V/ewJD as to the affine transform parameters. 

node:setAffine[SetAffine3D:in][SetAffine3D:out[\] - This message sets the affine transform parameters of 
this process:iVodc3D instance. That change is then passed to the process: VicwiD instances in which this 
pTocess:Node3D is registered in order to update the associate gvm:iNode3D affine transform parameters. 

node:setCtrRot[SetRotationCenter3D:in][SetRotationCenter3D:out[]] - This message sets the rotation 
center of this process:iVodc3D instance. That change is then passed to the process:VieM’3Z) instances in 
which this process:iVode3D is registered to update the associate gvm:‘.Node3D rotation center. 


257 







noA^zsetCtrScale\SetScaleCenter3Dnn\\SetScaleCenter3D\out\W - This message sets the scaling center 
of this processtiVorfeJD instance. That change is then passed to the processiV/cwJD instances in which 
this process:iVorfeJD is registered to update the associate gvmzzNodeSD scaling center. 

node:setRotaHon[SetRotation3D:in\[SetRotation3D:out[]] - This message sets the rotation angle (in 
radians) of this ]?rocess:Node3D instance. That change is then passed to the process: Vich'3Z) instances in 
which this processis registered to update the associate gvm::Node3D rotation value. 

node:setScale[SetScale3D‘.in][SetScale3D:out[]] - This message sets the scale of this process:iVodc3Z) 
instance. That change is then passed to the process:ViicM'3£) instances where this process:iVo</c3D is 
registered so that the associated gvm::Node3D instances may have their scaling factors updated. 

node:setTranslation[SetTranslation3D:in][SetTranslation3D:out[]] - This message sets the translation 
factor of this process:Node3D instance. That change is then passed to the process:V/ cm'3D instances in 
which this process:iVorfc3D is registered to update the associate gvm::Node3D translation factors. 

B.3.65. process:Object 

This process acts as a base class for various GUI process. Each GUI process can be registered in a variety 
of process: VicH’ instances. Each process: Vich’ provides the pTocesszObject instance with an index value 
that messages to each of those views uses to access the associated gvmr.Object instance. Additionally, 
each processiObject instance can turn itself on and off in each of the views. There is a framework 
allowing all gvm::Object instances associated with this processiObject instance to be independently 
activated. 

Parent Process Construct: None 

Derived Process Constructs: processzNode, processzShape, process:VerteJc 

Data Members: 

bool:dc/4c<ivc(true) - Default active value. 


258 







std::map<process, gvmv.object_index>:indexMap - Map of object/view indices associations. 
modeiDefault Nodes; 

node:addView[AddView:in][SetActive:out, SetLabelzsl] - When a new process:^"!^^ is added, add that 
view and its associated index to indexMap by setting indexMap[in,view] to inindex. The process:V iVh' 
instance is also informed as to the default active status and the process label. 

node’.selectiveActivate[SelectiveActivate:in][SetActivezouAW - Here, we can selectively activate or 
deactivate the associated gvm::Object instances according to the associate made in in.activeMap. Those 
affected process: View instances are notified of the change with the outbound message:SeiAciive. 

TMdcisetActive\SetActive'.in\[SetActive\out[W - This sends inessage:SeiAci/vc instances to all views in 
indexMap reflecting the change in the active status. The result is that all active flags in the associated 
gvm::Object instances in all views in which this object is registered are set to in.active (i.e. they can all be 
turned on or off with this one inbound message). 

node:setDefaultAcHve[SetDefaultActive:in][] - This sets the defActive flag to the inMCtive flag. It only 
sets the active flag, and does not change the view/activity state associations. 

node:setLabel[SetLabel:in][SetLabel:out[]] - This sets the defActive flag to the inactive flag. It only sets 
the active flag, and does not change the view/activity state associations. 

B.3.66. process:Octahedron 

This process construct allows a octahedron to be viewed within multiple process:View3Z) instances. It 
references a gvmr.Octahedron instance owned by each process:ViewJD process in which this process is 
registered. 

Parent Process Construct; process:S/iape5D 

Derived Process Constructs; None 


259 






B.3.67. process:Polygon2D 

This process construct is for general two-dimensional polygons. The rendering mode for this polygon and 
the loeation of the polygon vertices must be consistent with the restrictions provided within OpenGL. The 
most prominent of these restrictions is that if the mode value is GL_POLYGON, then vertiees must form a 
convex polygon. 

Parent Process Construct: process:SAape2D 

Derived Process Constructs: None 

Data Members: 

process:vc/tcA:List[] - List of vertices for this polygon. They will be rendered in the order added to the list. 

Methods: 

modeiDefault Nodes: 

node:addVertex[AddVertex2D:in][RegisterVertex2D:out[]] - When new vertices are added, these 
additions need to be registered with the process:VieM'2D instance that manages the associate 
gvm::Polygon2D instances. 

node:addView[AddView:in][RegisterVertex2Dirv] - Upon reeeipt of the messageL4d(/Viea', this 
process:Polygon2D will register the vertices comprising it with the view that sent the messageView. 

B.3.68. process:Polygon3D 

This proeess eonstruet is for general three-dimensional polygons. The rendering mode for this polygon and 
the loeation of the polygon vertiees must be eonsistent with the restrietions provided within OpenGL. The 
most prominent of these restrictions is that if the mode value is GL_POLYGON, then vertices must form a 
convex polygon. 

Parent Process Construct: process:Shape3D 


260 









Derived Process Constructs: None 


Data Members; 

process:verfexLwt[] - List of vertices for this polygon. They will be rendered in the order added to the list. 

Methods: 

moAeiDefault Nodes: 

noAc:addVertex[AddVertex3D‘.iri\{RegisterVertex3D‘.out\W - When new vertices are added, these 
additions need to be registered with the process: ViewiD instances that manage the associate 
gvm::Polygon3D instances. 

node:addView[AddView:in][RegisterVertex3D:rv] - Upon receipt of the message^4</dV/eH’, this 
process:Polygon3D will register the vertices comprising it with the view that sent the message:AddV/cw. 

B.3.69. processiShape 

This is the parent construct for all of the shapes. Shapes have applied to them affine transforms to position 
them somewhere in the scene. This is then viewed from a certain position. 

Parent Process Construct: processiObject 

Derived Process Constructs: process:Shape2D, pTocess:Shape3D 

Data Members: 

GLdouble:co/or[4](-1.0) - This is the color associated with this shape instance. The default value (-1.0) 
indicates to the associate gvmiiShape instances that they are to derive their color from parent gvmr.Node 
instances. 

GLenum:gr_fnode - Rendering mode for this shape. It takes on one of the following values 
GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP, GL_TRIANGLES, 


261 






GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS, GL_QUAD_STRIP, 
GL_POLYGON. The meaning of these values can be found in (Wright 1996) page 172. 

GLfloat:/7fSize(-1.0) - Point size attribute of this shape. The default value (-1.0) indicates to the associate 
gvmy.Shape instances that they are to derive their point size attribute from parent gvm::Node instances. 

Methods: 

method:set_cofor(public; void; GLdouble:/-; GLdouble:g; GLdoubIe:b; GLdouble:n;) - Sets color to 
::make_vector{4, r, g, b, a). 

method:sct_cofor(public; void; std::vcctor<GLdouble>:c;) - Sets color to ::resize_vector(4, 1.0, c). 
method:5et_/node(public; void; GLenum:m;) - Sets gr_mode to m. 
methodrset _point_size(puhlic; void; GLfloattps;) - Sets ptjsize to ps. 
modeiDefault Nodes: 

node:addView[AddView:iri\[SetColor:sc, SetModeism, SetPointSizeisps] - Upon messager4d<fVieH', this 
process will inform the new process: instance of the shape's color, rendering mode, and point size. 

node:setColor[SetColor:in\[SetColor:out[\\ - When modifying the color of the shape, the change is passed 
to the process: VicH’ instances where this shape is registered so that the associate gvmy.Shape instances can 
have their color parameters similarly altered. 

noAeisetMode\SetMode’.iri\[SetMode’,out{W - When modifying the rendering mode of the shape, the 
change is passed to the process:ViCM' instances where this shape is registered so that the associate 
gvmy.Shape instances can have their rendering mode parameters similarly altered. 

noAe’.setPointSize\SetPointSize’.iri\\SetPointSize’.ou^]\ - When modifying the point size of the shape, the 
change is passed to the process:View instances where this shape is registered so that the associate 
gvmy.Shape instances can have their point size parameters similarly altered. 


262 






B.3.70. process:Shape2D 

This further specializes the process:S/ta/ie construct to allow two dimensional shapes within a 
process: VieM'2D instance. 

Parent Process Construct: processiShape 

Derived Process Constructs: process:Polygon2D 

B.3.71. process:Shape3D 

This further specializes the processzShape construct to allow three dimensional shapes within a 
processrVicwJD instance. 

Parent Process Construct: processtSAape 

Derived Process Constructs: process:Cone, processrCube, processiCylinder, processiDodecahedron, 
process:/co5aAedron, processiOctahedron, processiSphere, processiTetrahedron 

B.3.72. process:Sphere 

This process construct allows a sphere to be viewed within multiple process: ViewJD instances. It 
references a gvmr.Sphere instance owned by each process;ViewJD process in which this process is 
registered. 

Parent Process Construct: process:Shape3D 

Derived Process Constructs: None 

Data Members: 

double:radiMs(0.0) - Sphere radius. 
int:sfices(0) - Number of slices through the sphere. 
int:stacA:5(0) - Number of stacks in the sphere. 


263 






Methods: 


method:set(public; void; double:?-; intisl; int:st;) - Sets radius to r, slices to si, and stacks to st. 
modeiDefault Nodes: 

node:addView[AddView:in][SetSphereSize:out] - Upon receiving a request to add a new process: View 
handle, this node will report to that view the current size parameters of the sphere. 

node:setSize[SetSphereSize:in\[SetSphereSize:outU\ - Upon receiving a change in any of the parameters 
for the sphere, this node will report to the process: View instances where this processiSphere is registered 
to update the associated gvm::Sphere instances. 

B.3.73. process:Tetrahedron 

This process construct allows a tetrahedron to be viewed within multiple process: V/ew3D instances. It 
references a gvmv.Tetrahedron instance owned by each process:VicH>3D process in which this process is 
registered. 

Parent Process Construct: process:Siiqpc3D 

Derived Process Constructs: None 

B.3.74. process'.Torus 

This process construct allows a torus to be viewed within multiple process: View3D instances. It references 
a.gvm::Torus instance owned by each process:V»c»*'3D process in which this process is registered. 

Parent Process Construct: processiShapeSD 

Derived Process Constructs: None 

Data Members: 

double:miier(0.0) - Size of the inner radius. 


264 








double:oiiter(0.0) - Size of the outer radius. 


int:sides(0) - Number of sides the torus will have. 
int:rings(0) - Number of rings the torus will have. 

Methods: 


method:set(public; void; double:z; double:^; int:^; int:r;) - Sets inner to i, outer to o, sides to s, rings to 


mode:Default Nodes: 

node:addView[AddView:in][SetTorusSize:out] - Upon receiving a request to add a view handle, this node 
will report to that view the current size parameters of the torus. 

node:setSize[SetTorusSize:in][SetTorusSize:outU\ - Upon receiving a change in any of the parameters for 
the torus, this node will report to the processtVicM’ instances where this torus is registered to update the 
associated gvin::7’oriis instances. 

B.3.75. process:Vertex 

Parent Process Construct: processiObject 

Derived Process Constructs: process: Vertea:2D, process: 

B.3.76. process:Vertex2D 

This is a two-dimensional vertex. It is normally subordinated to a process:Po/ygon2D. It is associated 
with a gvm::Vertex2D which is manages in the parent process: Vfew2D. 

Parent Process Construct: process: Vertex 

Derived Process Constructs: None 

double:vert[2](0.0) - Vertex location, initialized to <0.0, 0.0> 


265 



modeiDefault Nodes: 


Tiodt:addView[AddView:in][SetVertex2D:out] - reports to the processcV/ews where this vertex is 
registered to update the associated^vin::Vcrtcj:2D instances. 

node:setVertex[SetVertex2D:in\[SetVertex2D‘.outU\ - Sets the vertex location, and reports that new 
location to the process: View2D instances where this vertex is registered so that the associated 
gvm::Vertex2D can properly represent the current state of this vertex. 

B.3.77. process:Vertex3D 

This is a three-dimensional vertex. It is subordinated to a processiPolygonSD. It is associated with a 
gvm::Vertex3D which is manages in the parent process:Vicw3Z). 

Parent Process Construct: process: Vertcjr 

Derived Process Constructs: None 

Data Members: 

double:ve/t[3](0.0) - Vertex location, initialized to <0.0, 0.0, 0.0> 

Methods: 

modeiDefault Nodes: 

node:addView[AddViewiin][SetVertex3D:out] - reports to the process:VieH's instances where this vertex 
is registered to update the associated gvm::Vei1ex3D instances. 

node:setVertex[SetVertex3D:in][SetVertex3D:out[]] - Sets the vertex location, and reports that new 
location to the new process: Vich’JD instances where this vertex is registered so that the associated 
gvm::Vertex3D can properly represent the current state of this vertex. 


266 







B.3.78. process:View 

A process: View construct instance manages a single GLUT window in which can be displayed information 
in any form the programmer wishes. 

Parent Process Construct; None 

Derived Process Constructs: process:VicH'2D, process:ViewJD 

Data Members: 

std::vector<std::vector<gvm::object_index> >:procList - Indices associated with specific processes in the 
simulation system are stored in this structure. The index for some process instance v is stored in 
procList[v.getNodeQ][v.getIndex()]. As new processes request management from a process:^^!^^ 
instance, procList is polled to determine whether the process has already been registered with the view. If 
not, procList is resized if necessary and a unique identifier is placed into the appropriate place in procList. 

douhle:refreshlnterval(Q.01) - Time between consecutive refreshes. 

gvm:: VieM'*:vjcH’(NULL) - This is the actual view that renders the scene. 

Methods: 

method:(ni^(public; void;) - Registers view with the local sodlr.GLUTViewManager instance. 

method:gct(protected; gvm::object_index; process:/?;) - Returns the object index associated with the 
process instance with handle p. If necessary, this routine will increase the size of the procList structure. 
This is 

method:5et(protected; void; process:/?; gvmz:object_index:v;) - This method sets the objeet instance for 
process p to v. procList is resized to accommodate the new process if necessary. 


267 







method:res'tore(public; void;) - This method is called when a rollback restores the state instance receiving 
the call. In this case, it will call the (*view)j'estore(getTimeO) to remove any pending events in view that 
have timestamps later than sodl::TimeStamp::getTimeQ. 

method:/ossi7Co/fecf(public; void;) - This method is called when a fossil collection event occurs for the 
view. In this case, it will call the (*view).fossilCollect(getTimeQ) to process any pending events in view 
that have timestamps with time stamp sodkiTimeStampiigetTimeO. 

mode:Default Nodes: 

node:refresh[RefreshDisplay:in][ReJreshDisplay:oui] - When messageiRefreshDisplay instances are 
received, the node will schedule a gvmr.Refresh event with view a request to update the display for time 
stamp sodl::TimeStamp::getTime(). This event will actually be processed during the fossil collection 
process. The node then sends an output message to schedule another messageiRefreshDisplay event for 
sodh: TimeStampr.getTimeQ+refreshlnterval. 

noAe:setActive[SetActivexin\[] - This node will schedule gvmr.SetActive event to set the active parameter 
of the gvmy.Object instance with index inindex to itiMctive for time stamp sodl::TimeStamp:igetTimeQ. 

iiode:setColor[SetColor:in][] - This node will schedule a gvmr.SetColor event to set the color parameter 
of the gvmiiObject instance with index in.index to inxolor for time stamp sodl::TimeStamp::getTimeO- 

node:setLabel[SetLabel:in][\ - This node will schedule a gvmiiSetLabel event to set the label string 
parameter of the gvm::Object instance with index in.index to in.label for time stamp 
sodlx : TimeStamp : igetTirneQ. 

node:setMode[SetMode:in]U - This node will schedule a gvmxiSetMode event to set the rendering mode 
parameter of the gvmy.Object instance with index in.index to in.grjnode for time stamp 
sodl :: TimeStamp : igetTime (). 

node:setPointSize[SetPointSize:in][] - If in.index references an object, then this node will schedule a 
gvmy.SetPointSize event in view to change the point size parameter of the referenced gvmy.Object instance. 


268 







If the in.index does not refer to an object, then it applies to the default point size value for view. In that 
case, an event for changing view’s point size is scheduled in view. In both cases, the time stamp for these 
scheduled events is sodl::TimeStamp::getTimeO. 

node:setPosition[SetPosition:in]U - This node will schedule gvmiiSetPosition event with view to set the 
position of the GLUT window view controls to <injc, in.y> with time stamp sodl::TimeStamp::getTimeO. 

node:setRefreshInterval[SetRejTesh:in\[] - This node will set refreshlnterval to the value provided in 
inj-efreshlnterval. 

node:seiSizc[SeiSizc:iM][] - This node will schedule a gvmr.SetSize event with view to set the size of the 
GLUT window view controls to <in.width, in.height> with time stamp sodl::TimeStamp::getTimeO. 

node:start[StartSimulation:in][RefreshDisplay:out] - This node schedules the first refresh event to occur 
at time 0.0. All subsequent refresh events will occur at intervals of refreshlnterval. 

B.3.79. process;View2D 

The process:ViieH’20 construct provides more specialized user interactions for two-dimensional data 
representation. 

Parent Process Construct: process: View 

Derived Process Constructs: None 

Methods: 

method:init(public; void;) - This routine will allocate view as a gvm::View2D instance and then call 
Noder.initQ to register that gvwi::View instance with the local instance of the sodlr.GLUTViewManager 
instance. 

inethod:geiGVMrj^/ie(protected; gvm::object_type; sod/::ptype:l;) - This returns the gvmiiobjectjype 
associated with processes of type t. 


269 







moAciDefault Nodes; 


noAe:addNode[AddNode2D:in\{AddView.out\W - Adds a collection of process:Node2i) handles to this 
process: Viiew2D, each of which is directly subordinated to view. It does this by scheduling a 
gvmiiCreateObject to request creation of new gvm::Node2D instances in view for each node 
in.subordinates not previously registered. It then schedules new gvmi’AddNode events with the view for 
actually subordinating all of the nodes in in.subordinates to view. These events are each time stamped for 
sodl::TimeStamp::getTimeO. Any new nodes are informed as to their new index value. 

node:regNode[RegisterNode2D:in][AddView.out[]} - This will register a collection of process:Node2D 
instances and specify their parent process:iVode2D instance (which is the source of the message in). It 
does this by scheduling a gvm::CreateObject event to request creation of a new gvm::Node2D instances in 
view for each node in.subordinates not previously registered. A gvmr^ddNode message is then scheduled 
for each of the processes listed in in.subordinates[], which are added as sub-nodes to the gvm::Node 
instance with index in.index. All of these events are each time stamped for sodl::TimeStamp::getTimeQ. 
Any new nodes are informed as to their new index value. 

node:regShape[RegisterShape2D:in][AddView:out[]] - This will register a collection of 

process:Shape2D instances and specify their parent process:Node2D instance (which is the source of the 
message in). It does this by scheduling a gvm::CreateObject event to request creation of a new 
gvm\‘.Shape2D instances in view for each node in.subordinates not previously registered. A 

gvm’.iAddShape message is then scheduled for each of the processes listed in in.subordinates[\, which are 
added as subordinate shapes to the gvm:iNode instance with index in.index. All of these events are each 
time stamped for sodliiTimeStampiigetTimeQ. Any new nodes are informed as to their new index value. 

node:regVertex[RegisterVertex2D:in][AddView:outU] - This will register a collection of 

process: Vcrtex2D instances and specify their parent pTocessiPolygon2D instance (which is the source of 
the message in). It does this by scheduling a gvmiiCreateObject event to request creation of a new 
gvmiiVertex2D instances in view for each node in.subordinates not previously registered. A 

gvmiiAddVertex message is then scheduled for each of the processes listed in in.subordinates[], which are 


270 










added as subordinate shapes to the gvm::Polygon2D instance with index in.index. All of these events are 
each time stamped for sodl::TimeStamp::getTimeQ. Any new nodes are informed as to their new index 
value. 

node:setAffine[SetAffine2D:in]U - This node schedules a gvm::SetAffine event with view an update for 
all of the affine transformation components in the gvm::Node2D instance with index in.index. The time 
stamp for this scheduled item is sodl::TimeStamp::getTime(). 

iiode:setRotatton[SetRotation2D:in][\ - This node schedules a gvm::SetRotation event with view an 
update for the rotation portion of the affme transformation in the gvm::Node2D instance with index 
in.index. The time stamp for this scheduled item is sodhiTimeStampy.getTimeQ. 

node:setRotationCenter[SetRotationCenter2D:in}[] - This node schedules a gvmr.SetRotationCenter 
event with view an update for the rotational center portion of the affine transformation in the gvm::Node2D 
instance with index in.index. The time stamp for this scheduled item is sodl::TimeStamp:tgetTimeO. 

node:setScalelSetScale2D:in][] - This node schedules a gvmr.SetScale event with view an update for the 
scale portion of the affine transformation in the gvm::Node2D instance with index in.index. The time 
stamp for this scheduled item is sodlv.TimeStampy.getTimeQ. 

node:setScaleCenter[SetScaleCenter2Diin][] - This node schedules a gvmiiSetScaleCetner event with 
view an update for the scaling center portion of the affine transformation in the gvm::Node2D instance with 
index in.index. The time stamp for this scheduled item is sodl::TimeStamp::getTimeO. 

node:setTranslation[SetTranslation2D:in][] - This node schedules agvm::SetTranslation event with view 
an update for the translation portion of the affine transformation in the gvm::Node2D instance with index 
in.index. The time stamp for this scheduled item is sodl::TimeStamp:zgetTime{). 

Taode:setVertex\SetVertex2D:iri\\\ - This node will schedule agvmr.SetVertex event with view a change to 
the gvm::Vertex2D instance with index in.index to the vertex value in.getQ- The time stamp for the 
scheduled event will be sodl::TimeStamp::getTimeQ. 


271 








B.3.80. process:View3D 

The process:ViVw3£) construct provides more specialized user interactions for three-dimensional data 
representation. 

Parent Process Construct; process: V/cw 

Derived Process Constructs: None 

Methods: 

method:irait(public; void;) - This routine will allocate view as a gvm::View3D instance and then call 
Node::init{) to register that gvmr.View instance with the local instance of the sodhiGLVTViewManager 
instance. 

niethod:ge<GVMrype(protected; gvm::objectJype; sodZ::ptype:r;) - This returns the gvmy.objectjype 
associated with processes of type t. 

moAeiDefault Nodes: 

noAe’.addNode[AddNode3Duri\[AddView\outW\ - Adds a collection of process:Node3D handles to this 
processrVimvJD, each of which is directly subordinated to view. It does this by scheduling a 
gvmr.CreateObject to request creation of new gvm::Node3D instances in view for each node 
in.subordinates not previously registered. It then schedules new gvmfAddNode events with the view for 
actually subordinating all of the nodes in in.subordinates to view. These events are each time stamped for 
sodl::TimeStamp::getTimeO. Any new nodes are informed as to their new index value. 

node:regNode[RegisterNode3D:in][AddViewiout[\] - This will register a collection of process:iVode3D 
instances and specify their parent process’.Node3D instance (which is the source of the message in). It 
does this by scheduling a gvm::CreateObject event to request creation of a new gvm::Node3D instances in 
view for each node in.subordinates not previously registered. A gvmiAddNode message is then scheduled 
for each of the processes listed in in.subordinates[], which are added as sub-nodes to the gvmr.Node 


272 









instance with index in.index. All of these events are each time stamped for sodl::TimeStamp::getTimeQ. 
Any new nodes are informed as to their new index value. 

node:regShape[RegisterShape3D:in][AddView:out[]] - This will register a collection of 

process:Shape3D instances and specify their parent processiNodeSD instance (whieh is the source of the 
message in). It does this by seheduling a gvmr.CreateObject event to request creation of a new 
gvm::Shape3D instances in view for each node in.subordinates not previously registered. A 

gvm:\AddShape message is then seheduled for each of the processes listed in in.subordinatesU, whieh are 
added as sub-nodes to the gvm::Node instance with index in.index. All of these events are each time 
stamped for sodl::TimeStamp::getTimeO- Any new nodes are informed as to their new index value. 

node:regVertex[RegisterVertex3D:in][AddViewiout[]\ - This will register a collection of 

processinstances and specify their parent process:Polygon3D instance (whieh is the source of 
the message in). It does this by scheduling a gvmr.CreateObject event to request creation of a new 
gvm::Vertex3D instances in vicH' for each node in.subordinates not previously registered. A 

gvm'.iAddNode message is then scheduled for each of the proeesses listed in in.subordinates[), which are 
added as sub-nodes to the gvm::Node instance with index in.index. All of these events are each time 
stamped for sodh\TimeStamp::getTimeQ. Any new nodes are informed as to their new index value. 

node:setAffine\SetAffine3D’.iri\{\ - This node schedules a gvmy.SetAfJine event with view an update for 
all of the affine transformation components in the gvm::Node3D instance with index in.index. The time 
stamp for this seheduled item is sodliiTimeStampy.getTimei). 

node:setConeSize[SetConeSize:in][\ - This node schedules a gvmy.SetConeSize event with view an event 
with time stamp sodl::TimeStamp::getTimeO to change the parameters of the gvmy.Cone instance with 
identifier in.index to the parameters specified in in. 

iiode:setCubeSize[SetCubeSize:in][] - This node schedules a gvmy.SetSetCubeSize event with view an 
event with time stamp sodl::TimeStamp::getTime{) to change the parameters of the gvmy.Cube instance 
with identifier in.index to the parameters specified in in. 


273 







nodG:setCylinderSize[SetCylinderSize:in][] - This schedules a gvm::SetSetCylinderSize event with view 
an event with time stamp sodl::TimeStamp::getTimeO to change the parameters of the gvm::Cylinder 
instance with identifier in.index to the parameters specified in in. 

node:setRotation[SetRotation3D:in][] - This node schedules a gvm::SetRotation event with view an 
update for the rotation portion of the affine transformation in the gvm::Node3D instance with index 
in.index. The time stamp for this scheduled item is sodl::TimeStamp::getTime{). 

nodt:setRotationCenter[SetRotationCenter3Dtin][] - This node schedules a gvmv.SetRotationCenter 
event with view an update for the rotational center portion of the affine transformation in the gvm::Node3D 
instance with index in.index. The time stamp for this scheduled item is sodl::TimeStamp::getTimeO. 

nod^:setScale[SetScale3D’.in][] - This node schedules a gvmiiSetScale event with view an update for the 
scale portion of the affine transformation in the gvm::Node3D instance with index in.index. The time 
stamp for this scheduled item is sodl::TimeStamp::getTime{). 

node:setScaleCenter[SetScaleCenter3D:in][] - This node schedules a gvmiiSetScaleCenter event with 
view an update for the scaling center portion of the affine transformation in the gvm::Node3D instance with 
index in.index. The time stamp for this scheduled item is sodl::TimeStamp::getTime{). 

nodc:setSphereSize[SetSphereSize:in][] - This node schedules a gvm::SetSphereSize event with view an 
event with time stamp getTimeQ to change the parameters of the gvmr.Sphere instance with identifier 
in.index to the parameters specified in in. 

node:setTorusSize[SetTorusSize:in][] - This node schedules a gvmr.SetTorusSize event with view an 
event with time stamp sodl::TimeStamp::getTime{) to change the parameters of the gvm::Torus instance 
with identifier in.index to the parameters specified in in. 

node:setTranslation[SetTranslation3D:in][] - This node schedules agvmiiSetTranslation event with view 
an update for the translation portion of the affine transformation in the gvm::Node3D instance with index 
in.index. The time stamp for this scheduled item is sodliiTimeStampzigetTimeQ. 


274 









noAe’.setVertex{SetVertex3D'.iri\W - This node will schedule a.gvmr.SetVertex event with view a change to 
the gvm::Vertex3D instance with index in.index to the vertex value in.getQ. The time stamp for the 
scheduled event will be sodhiTimeStampr.getTimeQ. 

B.4. GLUT View Manager (gvm) Classes 

The GLUT View Manager uses the classes below to actually display information to a GLUT window. 
They are owned by a processiVieH’ instance which manages them according to the process hierarchy 
(which process'.Node instances are owned by each other, etc). 

B.4.1. gvm;:AddNode 

This class is derived from the gvmr.Message class and is used to schedule the addition of a subnode to a 
gvm:'.View or gvm::Node instance. 

Parent Classes; public gvm 

Private Data Members; 

gvm::objectJndex gvm:’AddNode::nodeObj - Index of the gvm::Object that is to be added to the node list 
of the destination. 

Public Constructors; 

gvm:'AddNode:'AddNode(gvm::View& v, double t, gvm::object_index o) - This constructor is used to 
add the gvm::Node instance with index o as a subnode to v. This addition will occur at time t. 

gvm:'AddNode:'AddNode{gvm::View& v, double t, gvm::object_index d, gvm::object_index o) - This 
constructor is used to add the gvm::Node instance with index o as a subnode to the gvmiiNode instance 
with index d. This addition will occur at time t. 

Public Methods; 


275 






virtual void gvm'.'AddNode'.xsendiydid^ - This method is called when the message is to actually be 
delivered (when the underlying simulation engine is performing incremental fossil collection for time 
gvm::Message::getTimeQ. In this case, it actually establishes the parent/subordinate relation specified in 
the constructor used in creating this instance. 

B.4.2. gvm::AdclShape 

Parent Classes: public gvmr.Message 

Private Data Members: 

gvm::objectJndex gvm:xAddShape::shapeObj - This is the index of the gvmr.Object instance (it should 
actually be a gvmixShape instance) that is to be added as a subordinate shape to the destination. 

Public Constructors: 

gvm:'AddShape:'AddShape(gvm::View& v, double t, gvmxxobjectjndex d, gvmwobjectjndex o) - This 
constructor is used to add the gvm::Shape instance with index o as a subordinate shape to the gvmr.Node 
instance with index d. This addition will occur at time t. 

Public Methods: 

virtual void gvm:’jiddShape::send(yoid) - This method is called when the message is to actually be 
delivered (when the underlying simulation engine is performing incremental fossil collection for time 
gvm::Message::getTimeO. In this case, it actually establishes the parent/subordinate relation specified in 
the constructor used in creating this instance. 

B.4.3. gvm:: Add Vertex 

Parent Classes: public gvmiiMessage 

Private Data Members: 


276 






gvm:iobject_index gvmiiAddVertexv.vertOhj - This is the index of the gvm'.'.Vertex instance that will be 
added as a subordinate of the destination. 

Public Constructors: 

gvm‘.‘AddVertexyAddVertex{gvm::View& v, double i, gvmv.objectJndex d, gvm::object_index o) - This 
constractor is used to add the gvmy.Vector instance with index o as a subordinate vertex to the 
gvm::Polygon2D or gvm::Polygon3D instance with index d. This addition will occur at time /. 

Public Methods: 

virtual void gvm:AddVertex::sendi\oid) - This method is called when the message is to actually be 
delivered (when the underlying simulation engine is performing incremental fossil collection for time 
gvm::Message::getTimeO. In this case, it actually establishes the parent/subordinate relation specified in 
the constructor used in creating this instance. 

B.4.4. gvm:;Cone 

This provides a means of displaying a cone in a GLUT window. The ::glutSolidCone(base, height, slices, 
stacks) and ::glutWireConeibase, height, slices, stacks) routines are called to render the cone. 

Parent Class: public gv/n::SAape5I> 

Derived Classes: None 

Protected Data Members: 

GLdouble gvm::Cone::base - Radius of the cone base. 

GLdouble gvm::Cone::height - Height of the cone. 

GLint gvm::Cone::slices - Number of radial slices in the cone slices. 

GLint gvm::Cone::stacks - Number of lateral stacks for the cone. 


277 






Public Constructors; 


gvm:'.Cone'.:Cone{gvm'.:View3D& v, ulong i) - This constructor calls the parent constructor 
gvm::Shape3D{y, GVM_Cone, 0 and initializes base and height both to 1.0 and slices and stacks both to 
10 . 

Public Methods: 

virtual void gvm::Cone::display(void) - This method is used to display a cone using 
::glutSolidCone{base, height, slices, stacks) if mode is GL_POLYGON or is or ::glutWireConeibase, 
height, slices, stacks) otherwise. 

virtual bool gvm::Cone::isType(gvm::object_type t) - Returns true exactly when t=GVM_Cone or 
gvm;:Shape3D::isType(t) returns true. 

virtual void gviM;:Cone::sct(GLdouble b, GLdouble h, GLint si, GLint st) - Sets base to b, height to h, 
slices to si, and stacks to s. 

virtual void gvi«::Co«e::setBase(GLdouble b) - Sets base to b. 
virtual void gvm::Cone::setHeight(Glidouhle h) - Sets height to h. 
virtual void gvm::Cone::setSlices(GL,mt s) - Sets slices to s. 
virtual void gvm::Cone::setStacksiGLint s) - Sets stacks to s. 

B.4.5. gvm::CreateObject 

This elass is used to schedule the creation of a new gvmiiObject instance within a gvmv.View instance. 
The creation will occur at time gvm::Message::getTimeO. 

Parent Classes: public gvm::Message 

Private Data Members: 


278 








gvm::object_type gvm::CreateObjecti:objType - Type of object to create 


Public Constructors: 

gvm::CreateObject::CreateObject(gvm::View& v, double t, gvmv.objectjndex d, gvmy.objectjype d) - 
This constructor is used for scheduling the creation of a gvm::Object instance of type o with index d at time 
t. 

Public Methods: 

virtual void gvm'.'.CreateObject‘.'.send(yo\d) - This method actually allocates the gvm::Object instance 
and inserts it into the owning view’s list of objects. 

B.4.6. gvm;:Cube 

This provides a means of displaying a cube in a GLUT window. The ::glutSolidCube(size) and 
::glutWireCube(size) routines are called to render the cube. 

Parent Class: public gviR::S/iapc3Z) 

Derived Classes: None 

Protected Data Members: 

GLdouble gvm::Cube::size - Edge length for the cube. 

Public Constructors: 

gvm::Cube::Cube{gvm::View3D& v, ulong i) - This constructor calls the parent constructor 
gvm::Shape3Div, GVM_Cube, i) and initializes size to 1.0. 

Public Methods: 

virtual void gvm::Cube::display(\oid) - Display a cube using ::glutSolidCube{size) if mode is 
GL_POLYGON or is or ::glutWireCube{size) otherwise. 


279 







virtual bool gvm::Cube::isType(gvm::object_type t) - Returns true exactly when t=GVM_Cube or 
gvm::Shape3D::isType{t) returns true. 

virtual void gvm::Cube::set (GLdouble s) - Sets size to s. 

B.4.7. gvm::Cylinder 

The gvmr.Cylinder class provides a means of displaying a cylinder in a GLUT window. Unlike the other 
solids displayed here, there is no GLUT routine to display a cylinder, so the author wrote one from scratch. 
It supports rendering modes GL_POINTS, GL_LINES, GL_LINE_STRIP, and GL_TRIANGLES. If the 
rendering mode is set to any other mode, it is treated as GL_TRIANGLES. 

Parent Class: publicgvm::Shape3D 

Derived Classes: None 

Protected Data Members: 

GLdouble gvm::Cylinder::radius - Radius of the cylinder, 

GLint gvm::Cylinder::nsides - Number of sides around a ring. 

GLdouble gvm::Cylinder::length - Length of the cylinder. 

GLint gvm::Cylinder::rings - Number of rings around the cylinder. 

std::vector<std::vector<std::vector<Ghdoublc> > > gvm::Cylinder::vertices - Hold the vertex values so 
they do not need to be computed every time. They are changed any time the cylinder parameters are 
changed. 

Public Constructors: 

gvm::CyUnder::Cylinder(gvm::View3D& v, ulong i) - This constructor calls the parent class constructor 
gvm::Shape3D{v, GVM_Cylinder, i) and initializes radius, length, nsides and rings to 1.0, 1.0, 10 and 10 
respectively. 


280 









Public Methods: 


virtual void gvm:'.Cylinder‘.'.display(yo\A) - Display the cylinder in the currently active GLUT window by 
rendering the points listed in vertices in the proper order, given the rendering mode. 

virtual bool gvm::Cylinder::isType(gvm::object_Jype t) - This routine returns to the calling routine true if 
f=GVM_Cylinder or gvm::Shape3D::isType(t) returns true. 

virtual void gvm::Cylinder::set(GLdouhle rad, GLdouble I, GLint n, GLint r) - Sets radius, length, 
nsides and rings to rad, I, n and r respectively. 

virtual void gvm::Cylinder::setRadius(GL,douhlc rad) - Sets radius to rad. 
virtual void gvm::Cylinder::setNSides(GL,int n) - Sets nsides to n. 
virtual yoxdgvmy.Cylinderv.setLengthiGGdoviile 1) - Sets length to 1. 
virtual void gvm::Cylinder::setRings(GLint r) ~ Sets rings to r. 

B.4.8. gvmiiOodecahedron 

This provides a means of displaying a dodecahedron in a GLUT window. The iiglutSolidDodecahedronQ 
and ::glutWireDodecahedronO routines are called to render the dodecahedron. 

Parent Class: public gv/n::SAape3D 

Derived Classes: None 

Public Constructors: 

gvmiiDodecahedron:’.Dodecahedron (gvm::View3D& v, ulong i) - This constructor calls the parent 
constructor gvin::SAapc3D(v, GVM_Dodecahedron, i). 

Public Methods: 


281 







virtual void gvm::Dodecahedron::display(\o\d) - Display a dodecahedron using 
y.glutSolidDodecahedronQ if mode is GL_POLYGON or "glutWireDodecahedronO otherwise. 

virtual bool gvm::Dodecahedron::isType(gvm::objectJype l) - Returns true exactly when 
t=GVM_Dodecahedron or gvm::Shape3Dz:isType(t) returns true. 

B.4.9. gvm::lcosahedron 

This provides a means of displaying an icosahedron in a GLUT window. The ::glutSolidIcosahedronQ 
and ::glutWireIcosahedron{) routines are called to render the icosahedron. 

Parent Class; public gvm::Shape3D 

Derived Classes: None 

Public Constructors: 

gvm::Icosahedron::Icosahedron (gvm::View3D& v, ulong 0 - This constructor calls the parent 
constructor gv/n::S/tape5D(v, GVMJcosahedron, i)- 

Public Methods; 

virtual void gvm::Icosahedron::display(yoid) - Display a dodecahedron using iiglutSolidIcosahedronQ 
if mode is GL_POLYGON or y.glutWirelcosahedronO otherwise. 

virtual bool gvm::Icosahedron::isType(gvm::object_type t) - Returns true exactly when 
t:=GVM_Icosahedron or gvm::Shape3Dz:isType{t) returns true. 

B.4.10. gvm::Message 

This is the parent class for all of the messages. These messages are scheduled to occur at some time. They 
are processed dining the fossil collection phase of the simulation and are intended to provide a mechanism 
to buffer change requests to the scene graph in the graphics system. 

Parent Class: public sodh'.Trace 


282 








Derived Classes; gvm-.iAddNode, gvmrAddShape, gvm'.iAddVertex, gvm::CreateObject, gvm:'.Refresh, 
gvm::SetActive, gvm::SetColor, gvm::SetConeSize, gvmi'.SetCubeSize, gvm::SetCylinderSize, 


gvm::SetLabel, gvm::SetMode, gvm::SetPointSize, gvm::SetPosition, gvm::SetPosition, 
gvm::SetRotation, gvm::SetRotationCenter, gvm::SetScale, gvm::SetScaleCenter, gvm::SetSize, 
gvm::SetSphereSize, gvmiiSetTorusSize, gvm::SetTranslation, gvm::SetVertex 

Private Data Members; 

gvm::View& gvm::Message::view - This is a reference to owning view. 

double gvm::Message::time ~ This is the message timestamp 

gvmi'.messagejype gvm::Message::type - This is the message type 

gvmi'.objectjndex gvm:'.Message::dest - The index of the message destination object. 

ulong gvm::Message::msgIndex - A unique identifier for each message instance associated with a 
particular gviw::VicH’ instance. 

Public Constructors; 

gvm::Message::Message(gvm::View& v, double t, gvmizmessagejtype ty, gvmz'.objectjndex i) - This 
constructor initializes view to v, time to t, type to ty and dest to i. 

Public Methods; 

virtual gvm‘.:object_index gvm::Message::getDestivoid) const - This method returns dest to the calling 
routine. 

virtual double gvm::Message::getTimeivoid) const - This method returns time to the calling routine 

virtual gvm::message_type gvm::Message::getType{\oid) const - This method returns type to the calling 
routine. 


283 







virtual gvm::View& gvm::Message::getView(\oid) - This method returns view to the calling routine. 

virtual void gvm::Message::send(\oid) - Derived classes overload this method to perform the specific 
functions associated with delivering the message. 

virtual void gvm::Message::setIndex(ulong i) - This method sets msgindex to i. 
virtual \ilonggvm::Message::getIndex(void) - Return msgindex to the calling routine. 

B.4.11. gvm::Node 

Parent Class; public 

Derived Classes: gvm::Node2D, gvm::Node3D 

Protected Enumerators: 

enum gvm::Node::nodeJTags{NFjColor, NF_PointSi 2 e, NF_LAST} - These flags are used to index an 
array of bool values associated with various flag values. 

Protected Data Members: 

std::vcctor<GLdouble> gvm::Message::color - When /?cgsINr_Color] is set to true, then the current 
drawing color is saved and the color specified in this data member is used instead. When the subordinate 
objects are finished being rendered, the original color is restored for additional processing. 

std::vector<GLdouble> gvm::Node::ctrRot - Specifies the center of rotation for this rendering node. 

sh/;:vcctor<GLdouble> gvm::Node::ctrScale - Specifies the center of scaling for this rendering node. 

std::vector<hool> gvm::Message::flags - This array contains flags for either using the local values for 
color and point size or to use the default values in place when the display method is called. When the flag 
is set to true, then the local value is used for all subordinates. Otherwise, the current value in effect at the 
calling of the display method is used. 


284 







GLfloat gvm::Message::pt_size - When /Zags[NF_PointSize] is set to true, then the current point size 
parameter is saved and the size specified in this data member is used instead. When the subordinate objects 
are finished being rendered, the original point size parameter is restored for additional processing. 

bool gvm::Node::running - For detecting preventing infinite loops. This gets set to true when rendering 
for this node starts, and false when it's done. If it is asked to render itself while true, the request is ignored 
without performing any rendering. 

s/rf::vcctor<GLdouble> gvm::Node::rot - Specifies the rotation angle for this rendering node. 

std::vector<Gljdouhle> gvm::Node::scale - Specifies the scaling factors for this rendering node. 

std::vector<gvm::Object*> gvm::Node::subordinateList - This acts as a list of subordinate components. 
It mixes both subordinate nodes and shapes in the same list. 

s<rf:;vector<GLdouble> gvm::Node::trans - Specifies the translation factors for this rendering node. 

Protected Constructors; 

gvm::Node::Node{gvm::View& v, gvm::object_type t, gvmiiobjectjindex i) - This constructor calls the 
parent constructor gvm'.'.Objectiy, t, i) and initializes the other data members. Each of the affine 
transformation components are initialized to arrays of size two or three, for t G'VM_Node2D and 
GVM_Node3D respectively, at the origin (except scale, which is at <1, 1> or <1,1,1> as appropriate). 
Members running and pt_size are initialized to false and 1.0 respectively. Arrays color and flags are set to 
<-l,-l,-l,-l> and <false, false, false> respectively. 

Public Constructors: 

Public Methods: 

virtual void gvm::Node::addObject(gvm::object_index i) - This routine adds the pointer associated with 
the reference gvm::Object::getViewQ[i] to subordinateList. 


285 









virtual void gvmr.Noder^isplayiyoid) - If yfags[NF_Color] is true then the current drawing color is 
saved and reset to color. Likewise, if yiags[NF_PointSize] is true, the current point size parameter is 
saved and the default point size is set to pt_size. It then renders the scene from this node to all of its 
subordinate objects. The previous drawing color and point size are then restored prior to returning to the 
calling routine. 

virtual bool gvm::Node::isType{gvm::objectJype t) - This method returns true when either 
t=GVM_Node OTgvm::Object::isType(t) returns true. 

virtual void gvm::Node::setColor(GLdoublc r, GLdouble g, GLdouble b) - Sets data member color to 
::make_vectoriA, r, g, b, 1.0). If r, g, and b are all in the range [0.0, 1.0] then_/Iflgs[NF_Color] is set to 
true; false otherwise. 

virtual void gvm::Node::setColor(Gl,douhle r, GLdouble g, GLdouble b, GLdouble a) - Sets data 
member color to -.intake_vectori4, r, g, b, a). If r, g, b and a axe all in the range [0.0, 1.0] then 
yZags[NF_Color] is set to true; false otherwise. 

virtual void gvm::Nodei:setColor(std:ivector<Gl,douhlc> c) - This method sets color to 
:iresize_vector(4, 1.0, c). If all of the elements of c are in the range [0.0, 1.0], then/?ags[Nr_Color] is set 
to true; false otherwise. 

virtual void gvm::Node::setPointSizeiGl,float s) — This method sets pt_size to s. If j> 0.0 then 
yZdgs[Nr_PointSize] is set to true; false otherwise. 

virtual void gvmiiNodeiisetRotationiconsi std;;Fector<GLdouble> & v) - Sets data member rot to 
-.-.resize_yector(rot.size(), 0.0, v). 

virtual void gvm::Node::setRotationCenter{const sfd::vector<GLdouble> & v) - Sets data member 
ctrRot to -.-.resize_vector(ctrRotj:izeQ, 0.0, v). 

virtual void gvm::Node::setScaleCenter{const sto;:vector<GLdouble> & v) - Sets data member ctrScale 
to -.-.resize_vector{ctrScaleMze{), 0.0, v). 


286 







virtual void gvm::Node'.\setScaleicoin.st std::vtfctor<GLdouble> & v) - Sets data member scale to 
::resize_vector{scale.size(), 1.0, v). 

virtual void gvm::Node::setTranslation(const sfd::vector<GLdouble> & v) - Sets date member trans to 
'.'.resize _vector(trans.sizeO, 1.0, v). 

B.4.12. gvm::Node2D 

This specializes the node to perform two-dimensional affine transformations. 

Parent Class: public gviw::A^odc 

Derived Classes: None 

Public Constructors: 

gvm::Node2D::Node2D(gvm::View2D& v, ulong i) - This constructor calls the parent constructor 
gvm::Node(y, GVM_Node2D, i). 

Public Methods: 

virtual void gvm::Node2D::addNode(gvm::objectjindex n) - Add an existing node, given by 
gvm::Object::getView0[n] instance to the list of subordinate objects. 

virtual void gvm::Node2D::addShape(gvm::object_index s) - Add an existing shape, given by 
gvm::Object::getView0[n] instance to the list of subordinate objects. 

virtual void gvm::Node2D::display(\oid) - Display routine for this node. It performs the node’s affine 
transformations and then calls the parent version gvm::Node::displayO to actually display the subordinate 
objects. 

virtual bool gvm::Node2D::isType(gvm::object_type t) - Returns true exactly when t=GVM_Node2D or 
gvm::Node::isType{t) returns true. 


287 







virtual void gvm'.'.Node2D::setRotation{GLAo\xh\t z) - Sets the data member gvm::Node::rot to 
::make_vector(2, z, 0 . 0 ). 

virtual void gvm'.'.Node2D\:setRotationCenteriGLAovh\e. x, GLdouble y) - Sets the data member 
gvm::Node::ctrRot to :imake_vector{2, x, 3 ;). 

virtual void gvm::Node2D::setScale{GLAouhle x, GLdouble 3 ;) - Sets the data member 
gvmv.Node'.'.scale to '.'.make_vector(2,x,y). 

virtual void gvm'.'.lSlode2D'.:setScaleCenter{GLAaa\Ae x, GLdouble y) - Sets the data member 
gvm::Node::ctrScale to ::make_vector(2,x,y). 

virtual void gvm::Node2D;:setTranslation(Gl.Aouh\e x, GLdouble 3 ') - Sets the data member 
gvm::Node::trans to ::make_vector(2, x, y). 

B.4.13. gvm::Node3D 

This specializes the node to perform three-dimensional affine transformations. 

Parent Class: public gvin::A^ode 

Derived Classes: None 

Public Constructors: 

gvm::Node3D::Node3D{gvm::Vie}v3D& v, ulong i) - This constructor calls the parent constructor 
gvm::Node(y, GVM_Node3D, i). 

Public Methods: 

virtual void gvm::Node3D::addNode(gvm::object_index n) - Add an existing node, given by 
gvm::Object::getView{)[ri\ instance to the list of subordinate objects. 

virtual void gvm::Node3D::addShape(gvm::object_index s) - Add an existing shape, given by 
gvm::Object::getView()[n] instance to the list of subordinate objects. 


288 







virtual void gvm::Node3D::display(\oid) - Display routine for this node. It performs the node’s affine 
transformations and then calls the parent version gvm:iNode::displayO to actually display the subordinate 
objects. 

virtual bool gvm::Node3D::isType(gvm::objectJype l) - Returns true exactly when t=GVM_Node3D or 
gvm::Node::isType{l) returns true. 

virtual void gvm::Node3D::setRotation(GL,douh\e z) - Sets the data member gvm::Node::rot to 
:’.make_vector(2, z, 0.0). 

virtual void gvm::Node3D::setRotationCenter(GlAouh\e x, GLdouble y, GLdouble z) - Sets the data 
member gvm::Node::ctrRot to t:make_vector(3, x, y, z). 

virtual void gvm::Node3D::setScale{Gljdo\ih\e x, GLdouble y, GLdouble z) - Sets the data member 
gvm::Node::scale to •.:make_vector(3, x, y, z). 

virtual void gvm'.\Node3D:'.setScaleCenter{GLdovib\e x, GLdouble y, GLdouble z) - Sets the data 
mQvnbexgvm:\Node:'.ctrScale to ::make_vector(3,x,y,z). 

virtual void gvm::Node3D::setTranslation(GLdouhle x, GLdouble y, GLdouble z) - Sets the data 
member gvm::Node::trans to :'.make_yector{3, x, y, z). 

B.4.14. gvm::Object 

This is the base class for all of the gvmii classes displayed in gvmr.View instances. It provides some basic 
mechanisms for displaying information to the gvmxiView instances. It contains many of the methods for 
setting data members within derived classes. This allows a certain level of abstraction that is useful for 
delivering buffered messages to gvm::Object instances that are not of any predefined type. If a message 
does something to a gvmv.Object instance that does not make any sense, the instance will allow the call, but 
it will be ignored, and a warning message will be delivered to stdiiout. 

Parent Class; pubUc sodlwTrace 


289 









Derived Classes; gvmiiNode, gvmr.Shape, gvm'.'.Vertex 

Private Data Members: 

gvm::View* gvm::Object::view - Pointer to xhc. gvm'.iView instance to which this gvm::Object instance is 
subordinate. 

gvm’.zobjectjiandle gvm::Object"handle - This is a unique identifier associated with this specific 
gvmr.Object instance. 

std'.istring gvm'.’.Object'.'.label - A settable label for identification purposes. 

Protected Data Members: 

bool gvm::Object::active - This is set to true exactly when this gvm::Object instance is active. When set 
to true, it will enable the object to be displayed; when false, the code in the display method is to be 
ignored. 

Public Constructors: 

gvm::Object::Object(gvm::View& v, gvm::object_type t, gvm::object_index i) - This constructor 
initializes view to v, handle to it, i), and active and label to true and “none” respectively. 

Public Methods: 

virtual void gvm::Object::addNode(gvm:iobject_index) - A placeholder for some derived classes to 
overload. 

virtual void gvm::Object::addShape(gvmi:object_index) ~ A placeholder for some derived classes to 
overload. 

virtual void gvm::Object::addVertex(gvm::object_index) - A placeholder for some derived classes to 
overload. 


290 







virtual void gvm:'.Objecty.hegin{\o\il^ - A placeholder for some derived classes to overload. 


virtual void gvm::Object::display{yoid) - This method should be overloaded to perform output specific to 
the derived class instance. 

virtual void gvm::Object::end(\oid) - A placeholder for some derived classes to overload. 

virtual gvmv.objectjiandle gvm::Object’.:getHandle(\oid) - Return handle to the calling routine. 

virtual gvm::object_index gvm:'.Object‘.:getIndex{yo\d^ - Return handle second to the calling routine. 

virtual std::string gvm::Object::getLabel(yoid) - Return label to the calling routine. 

virtual gvm::object_type gvm::Object::getType(yoid) - Return handle.first to the calling routine. 

virtual gvm::View& gvm::Object::getView(yQid) - Returns view to the calling routine. 

virtual bool gvm::Object::isAcHve{yoid) const - Return active to the calling routine. 

virtual bool gvm::Object::isType(gvm::object_type t) - Return true exactly when t=GVM_Object. 

virtual void gv/n::Oty'cct::set(GLdouble) - A placeholder for some derived classes to overload. 

virtual void gvfn::Oty'ect::sct(GLdouble, GLdouble) - A placeholder for some derived classes to 
overload. 

virtual void gvm::Object::set(GlAouhlc, GLdouble, GLdouble) - A placeholder for some derived 
classes to overload. 

virtual void gvm::06ject::set(GLdouble, GLdouble, GLint, GLint) - A placeholder for some derived 
classes to overload. 

virtual void gvm::Object::setiGLdouhle, GLint, GLint) - A placeholder for some derived classes to 
overload. 


291 







virtual void gvmv.ObjecU'.seticonsl $td;:vector<GLdouble>&) - A placeholder for some derived classes 
to overload. 

virtual void gvm\‘.Object‘.’.setActive(hoo\ a) - Sets the active flag to a. 

virtual void gvm::Object::setBase{GL,do\ihle) - A placeholder for some derived classes to overload. 

virtual void gvm::Object::setColor(GLdouh\e, GLdouble, GLdouble) - A placeholder for some derived 
classes to overload. 

virtual void gvm::Object::setColoriGl-Aouhle, GLdouble, GLdouble, GLdouble) - A placeholder for 
some derived classes to overload. 

virtual void gvm::Object::setColor(stdi:vector<GLdouhie>) - A placeholder for some derived classes to 
overload. 

virtual void gvm‘.:Object::setHeight(Ghdouhle) - A placeholder for some derived elasses to overload. 

virtual void gvm::Object::setInnerRadius{GLdouhle) - A placeholder for some derived classes to 
overload. 

virtual void gvm::Object::setLabel(std::string s) - Set label to s. 

virtual void gvm::Object::setMode(GL,enu.m m) - A placeholder for some derived classes to overload. 

virtual void gvm::Object::setNSides(Ghint) - A placeholder for some derived classes to overload. 

virtual void gvm::Object::setOuterRadius(GL,douh\e) - A placeholder for some derived classes to 
overload. 

virtual void gvm::Object::setPointSizeiGL,Roat) - A placeholder for some derived classes to overload, 
virtual void gvm::Object::setRadius{GlAouble) - A placeholder for some derived elasses to overload, 
virtual void gvm::Object::setRings(GL,int) - A placeholder for some derived classes to overload. 


292 








virtual void gvmv.Objectv.setRotationiGLAaahXfi) - A placeholder for some derived classes to overload. 

virtual void gvm:iObject‘,‘.setRotation{GLAo\ib\^, GLdouble, GLdouble) - A place holder for some 
derived classes to overload. 

virtual void gvm::Object::setRotation(const std::vcctor<GLdouble>&) - A place holder for some 
derived classes to overload. 

virtual void gvm::Object::setRotationCenteriGLdoub\e, GLdouble) - A placeholder for some derived 
classes to overload. 

virtual void gvm::Object::setRotationCenter(Gl.douhle, GLdouble, GLdouble) - A place holder for 
some derived classes to overload. 

virtual void gvm::Object::setRotationCenter{const s/d::vector<GLdouble>&) - A place holder for some 
derived classes to overload. 

virtual void gvm::Object::setScale(GL,doah\e, GLdouble) - A place holder for some derived classes to 
overload. 

virtual void gvm::Object::setScaleiGLidouhle, GLdouble, GLdouble) - A place holder for some derived 
classes to overload. 

virtual void gvm::Object::setScale(const std::vec#or<GLdouble>&) - A place holder for some derived 
classes to overload. 

virtual void gvm::Object::setScaleCenter(GLdouh\e, GLdouble) - A place holder for some derived 
classes to overload. 

virtual void gvm::Object::setScaleCenter{GL,douhle, GLdouble, GLdouble) - A place holder for some 
derived classes to overload. 


293 







virtual void gvm::Object::setScaleCenter(const std::vector<GLdouhle>&) - A place holder for some 
derived classes to overload. 

virtual void gvm::Object::setSlices(GLint) - A place holder for some derived classes to overload. 

virtual void gvm::Object::setStacks(Gljint) - A place holder for some derived classes to overload. 

virtual void gvm::Object::setTranslation(GL,douh\e, GLdouble) - A place holder for some derived 
classes to overload. 

virtual void gvm::Object::setTranslation(Gljdoublc, GLdouble, GLdouble) - A place holder for some 
derived classes to overload. 

virtual void gvm::Object::setTranslation(const std::vccfor<GLdouble>&) - A place holder for some 
derived classes to overload. 

virtual \oid gvm::Object::setView(gvmi:View& v) - Sets view to v. 

B.4.15. gvm::Octahedron 

This provides a means of displaying an octahedron in a GLUT window. The r.glutSolidOctahedronQ and 
::glutWireOctahedron() routines are called to render the octahedron. 

Parent Class; public gvm::SAapr5D 

Derived Classes: None 

Public Constructors: 

gvm::Octahedron::Octahedron(gvm::View3D& v, ulong i) - This constructor calls the parent constructor 
gvm::Shape3D(v, GVM_Octahedron, i). 

Public Methods: 


294 








virtual void gvm::Octahedron::display(void) - This method displays an octahedron using 
r.glutSolidOctahedronO if fnode=GL_POLYGON or y.glutWireOctahedronQ otherwise. 

virtual bool gvm::Octahedron::isType(gvm::object_type t) - This routine returns to the calling routine 
true if t=GVM_Octahedron or gvm::Shape3D:iisType(t) returns true. 


B.4.16. gvm::Polygon2D 

This class is used for displaying groups of two-dimensional vertices to the parent gvm:iView2D window. 
When mode is GL_POLYGON, the vertices need to form a convex polygon. 

Parent Class: publicgvm::Shape2D 

Derived Classes: None 

Protected Methods: 

std::vector<gvm::Object*> gvm::Polygon2D::vertList - List of vertices to display to the parent view. 

Public Constructors: 

gvm::Polygon2D::Polygon2D(gvmi:View2D& v, ulong i) - This constructor calls the parent constructor 
gvm::Shape2D(v, GVM_Polygon2D, i). 

Public Methods: 

virtual void gvm::Polygon2D::display(\oid) - Display this two-dimensional polygon to the currendy 
active GLUT window. 

virtual void gvm::Polygon2D::addVertex(gvm::objectJndex i) - This method will add 
&gvm::Object::getView()[i] to the back of vertList. 


295 









B.4.17. gvm::Polygon3D 

This class is used for displaying groups of three-dimensional vertices to the parent gvmy.View3D window. 
When mode is GL_POLYGON, the vertices need to form a convex polygon. 

Parent Class: public gvfM::S/iapc3D 

Derived Classes: None 

Protected Methods: 

std::vector<gvm::Object*> gvm:‘.Polygon3D::vertList - List of vertices to display to the parent view. 

Public Constructors: 

gvm::Polygon3D::Polygon3D(gvm::View3D& v, ulong i) - This constructor calls the parent constructor 
gvm:'.Shape3D(y, GVM_Polygon3D, i). 

Public Methods: 

virtual void gvm::Polygon3D::display{\oid) - Display this two-dimensional polygon to the currently 
active GLUT window. 

virtual void gvm::Polygon3D’.:addVertex(gvm::object_index i) - This method will add 
&gvm::Object::getViewQ[i] to the back of vertList. 

B.4.18. gvm::Refresh 

The gvmiiReJresh message is used to schedule a screen refresh. Once it has been scheduled, the refresh is 
actually performed during fossil collection of the owning process: V/cw instance. 

Parent Classes: public gvm::Message 

Derived Classes: None 

Public Constructors: 


296 







gvm::Refresh::Refresh(gvm::View& v, double t) - This class constructor calls the parent constructor 
gvm::Message(v, t, GVM_Refresh, (ulong) -1). 

Public Methods: 

virtual void gvm::Message::send(yoid) - This method sets the refresh flag in view so that the next fossil 
collection event will force a screen refresh. 


B.4.19. gvm::SetActive 

The gvmy.SetActive message sets the active flag for the destination gvmy.Object instance, turning it either 
on or off within view. 

Parent Classes: public gvmr.Message 

Derived classes: None 

Private Data Members: 

bool gvm::SetActive::acHve - This contains the value for the active flag in the destination object. 

Public Constructors: 

gvm::SetActive::SetActive(gvm::View& v, double t, gvm::object_index d, bool a) - Class constructor 
which calls parent constructor gvm::Message(y, t, GVM_SetActive, i) and initializes active to a. 

Public Methods: 

virtual void gvm::SetActive::sendi\oid) - Sets the active flag of getViewQldest] to active. 

B.4.20. gvm::SetColor 

The gvmiiSetColor message is intended to change the color attribute of the destination gvmr.Object 
instance, either ngvm::Shape or gvmr.Node instance. 

Parent Classes: public gvm::Message 


297 






Derived Classes: None 


Private Data Members: 

s<d::vector<GLdouble> gvm::SetColor::color - Color to set the destination object. 

Public Constructors: 

gvm::SetColor::SetColor(gvm::View& v, double /, gvnt"object_index i, std::vcctor<GLdouble> c) - 
This constructor initializes color to c and calls the parent constructor gvm::Message(v, t, GVM_SetColor, 
0 - 

Public Methods: 

virtual void gvm::SetColor::send(void) - This method sets the color attribute of the destination object to 
color. 

B.4.21. gvm::SetConeSize 

The gvm::SetConeSize message is intended to change the cone size attributes of the destination gvmiiCone 
instance. 

Parent Classes: public gvm::Message 

Derived Classes: None 

Private Data Members: 

GLdouble gvm::SetConeSize::base ~ Size to set the base attribute of the destination gvmr.Cone instance. 

GLdouble gvm::SetConeSize::height - Size to set the height attribute of the destination gvm::Cone 
instance. 

GLint gvm::SetConeSize::slices - Number of radial slices composing the destination gvm:: Cone instance. 


298 







GLint gvm::SetConeSize::stacks - Number of lateral slices composing the destination gvmr.Cone 
instance. 

Public Constructors: 

gvm::SetConeSize::SetConeSize(gvmi’.View& v, double t, gvmr.objectjndex i, GLdouble b, GLdouble 
h, GLint si, GLint jf) - This constructor calls the parent constructor gvm::Message{v, t, 
GVM_SetConeSize, 0 and initializes base to b, height to h, slices to si and stacks to st. 

Public Methods: 

virtual void gvm::SetConeSize::sendi\oid) - This method will update the parameters of the destination 
gvm::Cone instance to the parameters in the payload of this message. 

B.4.22. gvm::SetCubeSize 

The gvmr.SetCubeSize message is sent to gvm::Cube instances to change the size of the cube edges. 

Parent Classes: public sodliiMessage 

Derived Classes: None 

Private Data Members: 

GLdouble gvm::SetCubeSize::cube_size - New size attribute for the destination gvmizCube instance. 

Public Constructors: 

gvm::SetCubeSize::SetCubeSize(gvm’.:View& v, double t, gvmiiobjectjlndex i, GLdouble s) - This 
constructor initializes cube_size to s and calls the parent constructor gvmz'.Messagely, t, 
GVM_SetCubeSize, i). 

Public Methods: 


299 







virtual void gvm::SetCubeSize'.‘.send{\o\di) - This methods actually sets the size attribute of the 
destination gv/n::C«A^ instance. 

B.4.23. gvm::SetCylinderSize 

gvm’.:SetCylinderSize message is intended to set the size attributes of a gvmr.Cylinder instance. 

Parent Classes: public gvmi'.Message 

Derived Classes; None 

GLdouble gvm::SetCylinderSize::radius - Value to set the radius attribute of the destination 
gvmr.Cylinder instance. 

GLdouble gvm::SetCyUnderSize::length - Value to set the length attribute of the destination 
gvmr.Cylinder instance. 

GLint gvm::SetCylinderSize::sides - Value to set the side count attribute of the destination gvmr.Cylinder 
instance. 

GLint gvm::SetCylinderSize::rings - Value to set the ring count attribute of the destination gvmv.Cylinder 
instance. 

Public Constructors: 

gvm::SetCylinderSize::SetCylinderSize{gvm::View& v, double t, gvmv.objectjndex i, GLdouble ir, 
GLdouble or, GLint s, GLint r) - This constructor calls the parent class constructor gvm::Message{v, t, 
GVM_SetCylinderSize, i) and initializes innerRadius, outerRadius, sides, and rings to ir, or, s, and r 
respectively. 

Public Method: 

virtual void gvm::SetCylinderSize::send(\oid) - This method commits the changes in the various 
attributes of the destination gvmy.Cylinder instance. 


300 







B.4.24. gvm::SetLabel 

The gvmv.SetLabel message is used to set the label attribute of the destination 
Parent Classes: public 

Derived Classes: None 

Private Data Members: 

stdv.string gvm::SetLabel::label - Value to set the label attribute of the destination gvm::Object instance. 

Public Constructors: 

gvm::SetLabel::SetLabel(gvm::View& v, double t, gvm::object_index i, std::string 1) - This constructor 
calls the parent constructor gvm::Message(c, t, GVM_SetLabel, i) and initializes label to 1. 

Public Methods: 

virtual void gvm::SetLabel::send(\oid) - This method actually sets the label attribute of the destination 
object. 

B.4.25. gvm::SetMocle 

The gvm::SetMode message is used to set the mode attribute of some gvm::Object instances. 

Parent Classes: public gv/R::Mmage 

Derived Classes: None 

Private Data Members: 

GLenum gvm::SetMode::mode - Value to set the mode attribute of the destination object. 

Public Constructors: 


301 






gvm::SetMode::SetMode(gvm::View& v, double t, gvmiwbjectjndex i, GLenum m) - This constructor 
calls the parent constructor gvm::Message{v, t, GVM_SetMode, i) and initialize mode to m. 

Public Methods: 

virtual void gvm'.:SetMode:‘.send{yoiA) - This method sets the mode parameter of the destination 
gvm::Object instance. 

B.4.26. gvm::SetPointSize 

The gvm::SetPointSize message is used to set the point size attribute within destination gvmiiObject 
instances. 

Parent Classes: public gvmy.Message 

Derived Classes: None 

Private Data Members: 

double gvm::SetPointSize::point_size - Value to set the point size attribute in the destination gvmv.Object 
instance or gvmv.View instance, as appropriate. 

Public Constructors: 

gvm::SetPointSize::SetPointSize(gvm::View& v, double t, double ps) - This constructor calls the parent 
constructor gvm::Message{v, t, GVM_SetPointSize, (ulong) -1) and initializes point_size to ps. This 
constructor is used when the intended destination of the message is view. 

gvm::SetPointSize::SetPointSize(gvm::View& v, double t, gvmiwbjectjndex i, double ps) - This 
constructor initializes point_size to ps and calls the parent constructor gvmy.Message{v, t, 
GVM_SetPointSize, i). This constructor is used when the intended destination is the gvmr.Object instance 
with identifier i. 

Public Methods: 


302 






virtual void gvmy.SetPointSizev.send{\ovSL) - This method actually commits the change to the point size 
attribute of the destination gvmy.Object OTgvmitView instance. 

B.4.27. gvm::SetPosition 

The gvmr.SetPosition message is intended to set the position of the GLUT window. 

Parent Classes: public gvm::Message 

Derived Classes: None 

Private Data Members: 

GLint gvm:'.SetPosition::x - New X location of the GLUT window. 

GLint gvm::SetPosition::y - New Y location of the GLUT window. 

Public Class Constructors: 

gvm::SetPosition::SetPosiHon(gvm::View& v, double t, GLint X, GLint L) - This constructor initializes 
X and y to X and Y respectively and calls the parent constructor gvmy.Message(v, t, GVM_SetPosition, 
(ulong) -1). 

Public Methods: 

virtual void gvm::SetPosUion::sendivoid) - This method will set the window position of the destination 
gvmv.View instance to <x,y>. 

B.4.28. gvnn::SetRotation 

Thegvm::SetRotation message is intended to set the rotation attribute ofagvmiiNode instance. 

Parent Classes: public gvin::Mcssage 

Derived Classes: None 


303 







Private Data Members: 


strf::vector<GLdouble> gvm::SetRotation::rot - Value to set the rotation attribute of the destination 
gvm::Node instance. 

Public Constructors: 

gvm::SetRotation::SetRotation(gvm::View& v, double t, gvm::object_index i, std::vecfor<GLdouble> r) 
- This constructor initializes rot to r and calls the parent class constructor gvm::Message{v, t, 
GVM_SetRotation, i). 

Public Methods: 

virtual void gvm::SetRotation::send(yoid) - This method actually sets the rotation attribute of the 
destination gv»i::Vodc instance. 

B.4.29. gvm::SetRotationCenter 

The gvmy.SetRotationCenter message is intended to set the center of rotation attribute of a gvm::Node 
instance. 

Parent Classes: public gvm::Message 

Derived Classes: None 

Private Data Members: 

std::vector<GLdouble> gvm::SetRotationCenter::ctrRot - Value to set the rotation center attribute of the 
destination gvm::Node instance. 

Public Constructors: 

gvm::SetRotationCenter::SetRotationCenter(gvm::View& v, double t, gvm::object_index i, 
s/d::vector<GLdouble> c) - This constructor initializes ctrRot to c and calls the parent class constructor 
gvm::Message{v, t, GVM_SetRotationCenter, i). 


304 









Public Methods: 


virtual void gvm::SetRotationCenter:'.send{\oid) - This method actually sets the center of rotation 
attribute of the destination gviwrriVode instance. 

B.4.30. gvm::SetScale 

The gvmiiSetScale message is intended to set the scale attribute of agvmiiNode instance. 

Parent Classes: puhhcgvmiiMessage 

Derived Classes: None 

Private Data Members: 

st</::vcctor<GLdouble> gvmr.SetScaleiiscale - Value to set the scale attribute of the destination 
gvmy.Node instance. 

Public Constructors: 

gvm::SetScale::SetScale(gvm::View& v, double t, gvmxwbjectjndex i, st<f::vcctor<GLdouble> s) - This 
constructor initializes scale to s and calls the parent class constructor gvm::Message{y, t, GVM_SetScale, 
0 . 

Public Methods: 

virtual void gvmy.SetScaley.sendlyoid) - This method actually sets the scale attribute of the destination 
gvm::Node instance. 

B.4.31. gvm::SetScaleCenter 

The gvm::SetScaleCenter message is intended to set the center of scaling attribute of a gvmy.Node 
instance. 

Parent Classes: public gvmy.Message 


305 






Derived Classes: None 


Private Data Members: 

s<d::vector<GLdouble> gvm::SetScaleCenter::ctrScale - Value to set the scaling center attribute of the 
destination gviii::iVode instance. 

Public Constructors: 

gvm::SetScaleCenter::SetScaleCenter(gvm:iView& v, double t, gvm:'.object_index i, 
strf::vecfor<GLdouble> c) - This constructor initializes ctrScale to c and calls the parent class constructor 
gvm\'.Message{y, 1, GVM_SetScaleCenter, i). 

Public Methods: 

virtual void gvm::SetScaleCenter::send(\oid) - This method actually sets the center of scaling attribute of 
the destination gvm::Node instance. 

B.4.32. gvm:;SetSize 

The gvm::SetSize message is intended to set the GLUT viewport size of the destination gvmr.View 
instance. 

Parent Classes: public gv/n::Mes5age 

Private Data Members: 

GLint gvm::SetSize::width - Value to set the width attribute of the destination instance. 

GLint gvm::SetSize:'.height - Value to set the height attribute of the destination gvm::View instance. 

Public Constructors: 


306 






gvm::SetSize::SetSize(gvm::View& v, double t, GLint w, GLint h) - This constructor calls the parent 
constructor gvm::Message(v, t, GVM_SetSize, (ulong) -1) and initializes width and height to w and h 
respectively. 

Public Methods; 

virtual void gvmz’.SetSize‘.zsend{yoiA) - This method commits the GLUT viewport size changes for the 
intended gvm:: View instance. 

B.4.33. gvm::SetSphereSize 

The gvmv.SetSphereSize message is intended to set the size attributes of a gvmiiSphere instance. 

Parent Classes: public 

Derived Classes: None 

GLdouble gvm::SetSphereSize::radius - Value to set the radius attribute of the destination gvmizSphere 
instance. 

GLint gvm::SetSphereSize::slices - Value to set the slices attribute of the destination gvmziSphere 
instance. 

GLint gvmz’.SetSphereSize•.’.stacks - Value to set the stacks attribute of the destination gvmiiSphere 
instance. 

Public Constructors: 

gvm::SetSphereSize::SetSphereSize(gvm::View& v, double t, gvmwobjectjndex i, GLdouble r, GLint 
si, GLint Jl) - This constructor calls the parent constructor gvm::Message{v, t, GVM_SetSphereSize, i) 
and initializes radius, slices and stacks to r, si, and st respectively. 

Public Methods: 


307 






virtual void gvm::SetSphereSize::send(\oid) - This method commits the changes to the attributes of the 
destination gvm “Sphere instance. 

B.4.34. gvm::SetTorusSize 

The gvm::SetTorusSize message is intended to set the size attributes of a gvm::Torus instance. 

Parent Classes: public gvmr.Message 

Derived Classes: None 

GLdouble gvm::SetTorusSize::innerRadius - Value to set the inner radius attribute of the destination 
gvmiiTorus instance. 

GLdouble gvm::SetTorusSize::outerRadius - Value to set the outer radius attribute of the destination 
gvmiiTorus instance. 

GLint gvm::SetTorusSize::sides - Value to set the side count attribute of the destination gvmr.Torus 
instance. 

GLint gvm::SetTorusSize‘.:rings - Value to set the ring count attribute of the destination gvmr.Torus 
instance. 

Public Constructors: 

gvm::SetTorusSize::SetTorusSize(gvmi:View& v, double t, gvmr.objectjndex i, GLdouble ir, 
GLdouble or, GLint s, GLint r) - This constructor calls the parent class constructor gvm::Message(v, t, 
GVM_SetTorusSize, i) and initializes innerRadius, outerRadius, sides, and rings to ir, or, s, and r 
respectively. 

Public Method: 

virtual void gvm::SetTorusSize::send{\oid) - This method commits the changes in the various attributes 
of the destination gvm::Torus instance. 


308 






B.4.35. gvm::SetTranslation 

The gvmr.SetTranslation message is intended to set the translation attribute of a.gvm::Node instance. 
Parent Classes: public gvm::Message 

Derived Classes: None 

Private Data Members: 

s<rf::vector<GLdouble> gvm::SetTranslation::trans - Value to set the translation attribute of the 
destination gvm::Node instance. 

Public Constructors: 

gvm::SetTranslation::SetTranslation(gvm::View& v, double t, gvmv.objectjndex i, 
std::vector<GLdouhlc> r) - This constructor initializes trans to r and calls the parent class constructor 
gvm::Message(v, t, GVM_SetTranslation, i). 

Public Methods: 

virtual void gvm::SetTranslation::send(yo\A) - This method actually sets the translation attribute of the 
destination gvm "Node instance. 

B.4.36. gvm::SetVertex 

The gvm::SetVertex message is intended to set the location of the vertex associated with a gvmziVertex 
instance. 

Parent Classes: public gvin::Afessage 

Derived Classes: None 

Private Data Members: 


309 







sfrf::vector<GLdouble> gvm::SetVertexi:vert - Value to set the location attribute of the destination 
gvmy.Vertex instance. 

Public Constructors: 

gvm::SetVertex::SetVertex(gvm::View& v, double t, gvmv.objectjndex i, std::vector<GLdouble> v) - 
This constructor initializes vert to v and calls the parent class constructor gvm::Message(v, t, 
GVM_SetVertex, i). 

Public Methods: 

virtual void gvm::SetVertex::send(\oid) - This method actually sets the location attribute of the 
destination ^viw:: Vertex instance. 

B.4.37. gvm::Shape 

This class provides basic support for displaying two and three-dimensional shapes to a GLUT window. It 
provides support only for rendering color, and nothing for lighting, shading or texturing. 

Parent Class: public gvmv.Object 

Derived Classes: gvm::Shape2D, gvm::Shape3D 

Protected Enumerators: 

enum gvm::Shape::shape_Jlags{SF_Color, SF_PointSize, SF_LAST} - This enumerator acts as in index 
in the flags array. 

Protected Data Members: 

s<rf::vecfor<GLdouble> gvm::Shape::color - Vector containing the color components of the shape 
instance. This is used only when_/Iags[SF_Color] is true. 

GLenum gvm:iShape::mode - Rendering mode for this shape. It takes on one of the following values 
GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP, GL_TRIANGLES, 


310 






GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS, GL_QUAD_STRIP, 
GL_POLYGON. The meaning of these values can be found in (Wright 1996) page 172. 

GLfloat gvm:iShape::pt_size - Point size to use when rendering using GL_POINTS. This is only used 
when/2ags[SF_PointSize] is true. 

std::vector<hool> gvm::Shape::flags - The components of this array are used to determine whether the 
default values for drawing color or point size are to be used when rendering the shape. 

Public Constructors: 

gvm::Shape::Shape(gvm::View& v, gvm::object_type t, gvm::object_index i) - This constructor calls 
parent constructor gvm::Object(v, t, i), and constructors color{4, -l.O), flags(SFJLAST, false), and sets 
mode and ptjize to GL_POINTS and 1.0 respectively. 

Public Methods; 

virtual void gvm::Shape::begini\oid) - Begin rendering this shape. It saves the default drawing color and 
point size sets the local values for them if the proper elements of flags are set. It then calls 
: :glBegin {mode ). 

virtual void gvm::Shape::end{void) - End rendering this shape. It calls r.glEndQ and restores the default 
drawing color and point size in the event that the local values were used instead. 

virtual bool gvm::Shape::isType(gvm::object_type t) - This routine returns to the calling routine true if 
t=GVM_Shape or gvm::Object::isType{t) returns true. 

virtual \oidgvm::Shape::setColor{std::vector<GLdouh\e> c) - Sets color to ::resize_vector{4, 1.0, c). If 
all of the components of c are in the range [0,1] then^gs[SF_Color] is set to true; false otherwise. 

virtual void gvm::Shape::setColor{Gljdouhle r, GLdouble g, GLdouble b) - Sets color to 
::make_vector{4, r, g, b, 1.0). If r, g, and b are all in the range [0, 1] then/Iags[SF_Color] is set to true; 
false otherwise. 


311 







virtual void gvm::Shape'.:setColor(GLAo\i[i\t r, GLdouble g, GLdouble b, GLdouble a) - Sets color to 
::make_vector{4, r, g, b, a). If r, g, b, and a are all in the range [0, 1] then/lflgs[SF_Color] is set to true; 
false otherwise. 

virtual void gvm::Shape::setMode(GTL.enum m) - Sets mode to m. 
virtual void gvm::Shape::setPointSize(GluRoat ps) - Set pt_size to ps. 

B.4.38. gvm::Shape2D 

This is the parent class for all two-dimensional shapes. It serves primarily as a placeholder for various data 
structures, and has no additional functionality beyond that defined in gvm::Shape. 

Parent Class: public gvmr.Shape 

Derived Classes: gvm::Polygon2D 

Protected Constructors: 

gvm::Shape2D::Shape2D(gvm::View2D& v, gvm::objectJype t, gvm::objectJndex i) - This constructor 
calls the parent constructor gvm::Shape(y, t, i). 

Public Methods: 

virtual bool gvm::Shape2D::isType(gvmi:object_type t) - This routine returns to the calling routine true 
if t=GVM_Shape2D or gvm::Shape::isType{t) returns true. 

B.4.39. gvm::Shape3D 

This is the parent class for all three-dimensional shapes. It serves primarily as a placeholder for various 
data structures, and has no additional functionality beyond gvm::Shape. 

Parent Class: public 


312 






Derived Classes: gymv.Cone, gvmr.Cube, gvm::Cylinder, gvm::Dodecahedron, gvmrJcosahedron, 
gvm::Octahedron, gvm::Polygon3D, gvm::Sphere, gvmuTetrahedron, gvm::Torus 

Protected Constructors: 

gvm::Shape3D::Shape3D(gvm::View3D& v, gvm::object_type t, gvm::object_index i) - This constructor 
calls the parent constructor gvm::Shape(v, t, i). 

Public Methods: 

virtual bool gvm::Shape3D::isType(gvm::objectJype t) - This routine returns to the calling routine true 
if t=GVM_Shape3D or gvm::Shape::isType(t) returns true. 

B.4.40. gvm::Sphere 

This provides a means of displaying a sphere in a GLUT window. The ::glutSolidSphere(radius, slices, 
stacks) and ::glutWireOctahedron(radius, slices, stacks) routines are called to render the sphere. 

Parent Class: public gvm::Shape3D 

Derived Classes: None 

Protected Data Members: 

GLdouble gvm::Sphere::radius - Radius of the sphere. 

GLint gvm::Sphere::slices - Number of radial slices into which to break the sphere. 

Ghint gvm::Sphere::stacks - Number of lateral stacks into which to break the sphere. 

Public Constructors: 

gvm::Sphere::Sphere(gvm::View3D& v, ulong i) - This constructor calls the parent class constructor 
gvm::Shape3D(v, GVM_Sphere, i) and initializes radius, slices and stacks to 1.0, 10 and 10 respectively. 

Public Methods: 


313 






virtual void gvm:iSphere::display{yo\A) - Display sphere using ::glutSolidSphere(radius, slices, stacks) 
if mode is GL_POLYGON or ::glutWireSphere(radius, slices, stacks) otherwise. 

virtual bool gvm::Sphere::isType(gvm::object_type t) - This routine returns to the calling routine true if 
t=GVM_Sphere or gvm::Shape3D::isType(t) returns true. 

virtual void gvm::Sp/iere::sct(GLdouble r, GLint si, GLint st) - Set radius, slices and stacks to r, si, and 

St. 

virtual void gvmy.Sphere:\setRadius{GLAo\ib\e r) - Set radius to r. 
virtual void gvm::Sphere::setSlices(Gljint s) - Set slices to s. 
virtual void gvm::Sphere::setStacks(GL,int - Set stacks to s. 

B.4.41. gvm:Tetrahedron 

This provides a means of displaying a tetrahedron in a GLUT window. The ::glutSolidTetrahedronQ and 
::glutWireTetrahedron() routines are called to render the tetrahedron. 

Parent Class: public gvin::S/iape3D 

Derived Classes: None 

Public Constructors: 

gvm::Tetrahedron::Tetrahedron(gvmiiView3D& v, ulong i) - This class constructor calls the parent 
constructor gvin::SAape3D(v, GVM_Tetrahedron, /). 

Public Methods: 

virtual void gvm::Tetrahedron::display(yoid) - Display a tetrahedron using iiglutSolidTetrahedronQ if 
mode is GL_POLYGON or ::glutWireTetrahedronO otherwise. 


314 







virtual bool gvm::Tetrahedron::isType(gvm::object_type t) - This routine returns to the ealling routine 
true if t=GVM_Tetrahedron or gvm::Shape3D::isType(t) returns true. 

B.4.42. gvm::Torus 

The gvmv.Torus class provides a means of displaying a torus in a GLUT window. The 
::glutSolidTorus(innerRadius, outerRadius, nsides, rings) and ::glutWireTorus(inner, outer, nsides, 
rings) routines are called to render the torus. 

Parent Class: public gvf«::S/iapcJD 

Derived Classes; None 

Protected Data Members: 

GLdouble gvm::Torus::innerRadius - Inner radius of the torus. 

GLint gvm::Torus::nsides - Number of sides around a ring. 

GLdouble gvm::Torus::outerRadius - Outer radius of the torus. 

GLint gvm::Torust:rings - Number of rings around the torus. 

Public Constructors: 

gvm::Torus::Torus{gvm::View3D& v, ulong i) - This constructor calls the parent class constructor 
gvm::Shape3Div, GVM_Torus, i) and initializes innerRadius, outerRadius, nsides and rings to 1.0, 2.0, 
10 and 10 respectively. 

Public Methods: 

virtual void gym::Torus".display{yo\d^ - Display a torus using either of the routines 
::glutSolidTorus{innerRadius, outerRadius, nsides, rings) if mode is GL_POLYGON or 
::glutWireTorus(innerRadius, outerRadius, nsides, rings) otherwise. 


315 







virtual bool gvm’.'.Torus::isType(gvm::object_type i) - This routine returns to the calling routine true if 
t=GVM_Torus or gvm::Shape3D::isType(t) returns true. 

virtual void gvm::Torus::set(GLdouble i, GLdouble o, GLint n, GLint r) - Sets innerRadius, 
outerRadius, nsides and rings to i, o, n and r respectively. 

virtual yoidgvm::Torus::setInnerRadius(GL,douh\e i) - Sets innerRadius to i. 

virtual void gvm:'.Torus'.:setNSides{G\Ant n) - Sets nsides to n. 

virtual void gvni:'.Torus‘.:setOuterRadius(GGdonhle o) - Sets outerRadius to o. 

virtual void gvm::Torus::setRingsiGlumt r) - Sets rings to r. 

B.4.43. gvm::Vertex 

gvmy.Veriex provides the basic functionality for generic vertices. Derived classes deal with vertices in 
specific vector spaces, notably 2 and 3-spaces. 

Parent Class: public gvmr.Object 

Derived Classes; gvm::Vertex2D, gvm::Vertex3D 

Protected Data Members: 

std::vector<GLdouble> gvm::Vertex::loc - Location of the vertex. 

Protected Constructors; 

gvm::Vertex::Vertex(gvm::View& v, gvmr.objectjype t, gvm::object_index i) - This constructor calls the 
parent constructor gvmz’.Objectiy, t, i) and initializes loc to either <0.0, 0.0> in the case *this is a 
gvm::Vertex2D instance or <0.0, 0.0, 0.0> when it is agvm::Vertex3D. 

Public Methods: 


316 






virtual void gvm'.iVertex\'.set{consi std::vector<GLdouble>& v) - This method sets loc to 
'.•.resize_vector(loc.size{), 0.0, v). 

virtual bool gvm::Vertex::isType(gvm::object_type t) - This routine returns to the ealling routine true if 
r=GVM_Vertex or gvm::Object::isType(t) returns true. 

B.4.44. gvm::Vertex2D 

gvm::Vertex2D provides specializes gvm::Vertex to perform two-dimensional vertex operations. 

Parent Class: public gvm::Vertex 

Derived Classes: None 

Public Constructors: 

gvm::Vertex2D::Vertex2D(gvm'.'.View2D& v, ulong j) - This constructor calls the parent class constructor 
gvm::Vertexiv, GVM_Vertex2D, i). 

Public Methods: 

virtual void gvm::Vertex2D::display{void) - This routine displays the vertex at its specified location, < 
gvm::Vertex::loc[0], gvm:: Vertex'. :/oc[ 1]> 

virtual bool gvm::Vertex2D::isType{gvm'.:object_type t) - This routine returns to the calling routine true 
if r=GVM_Vertex2D or gvm::Vertex::isType{t) returns true. 

virtual void gvm::Vertex2D::set(Ghdouhle x, GLdouble y) - This method sets the vector 
gvm::Vertex::loc to '.:make_yector{2,x,y). 

B.4.45. gvm::Vertex3D 

gvm::Vertex3D provides specializes gvmiiVertex to perform three-dimensional vertex operations. 

Parent Class: public gvm "Vertex 


317 








Derived Classes: None 

Public Constructors: 

gvm::Vertex3D::Vertex3D(gvm::View3D& v, ulong i) - This constructor calls the parent class constructor 
gvm::Vertex(v, GVM_Vertex3D, 0- 

Public Methods: 

virtual void gvm::Vertex3D::display(\oid) - This routine displays the vertex at its specified location, < 
gvm::Vertex::loc[0],gvm'.'.Vertexy.loc[\\ ,gv»i::Viertcjc::/oc[2]> 

virtual bool gvm::Vertex3D::isType(gvm::object_type t) - This routine returns to the calling routine true 
if t=GVM_Vertex3D or gvm::Vertex::isType{t) returns true. 

virtual void gvm:\Vertex3D:-.set{GlAo\ih\c x, GLdouble y, GLdouble z) - This method sets the vector 
gvm::Vertex::loc to ::make_vector(3,x,y,z)- 

B.4.46. gvm::View 

The gvmr.View class is the parent for the two classes specifically intended for two and three dimensional 
graphical output, gvm::View2D andgvmitView3D respectively. 

Parent Classes: public sod/::Trace 

Derived Classes: gvm::View2D, gvm:iView3D 

Protected Data Members: 

float gvm::View::aspect - This is the current aspect ratio of the output display. It is updated any time there 
is a change in the size of the viewport window. 

std::vector<hool> gvm::View::buttonState - This vector is used to keep track of the mouse button states. 
When button xxx is pressed, baWoiiStete[GLUT_xxc_BUTTON] is true, where xxx is one of {LEFT, 
MIDDLE, RIGHT}. 


318 







bool gvm::View::isVisible - This value is true when the window is visible, false otherwise. 

std::vector<mnt> gvni::View::mouseLoc - This vector is used to track the current mouse position. When 
the mouse is at GLUT window location <x, y>, then mouseLoc[0]=x and mouseLoc[l]=y. 

std::list<gvm::Message*> gvm::View::msgList - This is the pending message list. It is always sorted by 
time stamp. Messages with earlier timestamps are at the front of the list, and later ones are at the back. 

ulong gvm::View::nextMessage - This is a counter for the number of messages that have been created for 
the view. Each new message is given a unique handle, part of which is the instance number it derived from 
the value of the nextMessage data member. As new messages are added, the nextMessage field is 
incremented. 

gvm::object_index gvm::View::nextObject - This is a counter for the number of objeets that have been 
created for the view. Each new object is given a unique handle, part of which is the instance number it 
derived from the value of the nextObject data member. As new objects are added, the nextObject field is 
incremented. 

std::vector<gvm::Object*> gvm::View::nodeList - This is the list of root nodes for the gvmiiView 
instance. These are polled during the display phase, and each one is displayed in turn. 

std::vector<gvm::Object*> gvm::View::objectList - This is the master list of all of the objects in the view. 
As each new object obj is created, a pointer to it occupies objectListlobj.getlndexQ]. 

GLfloat gvm::View::ptSize - This is the view’s default point size. Any subordinate objects in the view’s 
scene graph that do not explicitly override the point size will use this default value. 

bool gvm::View::rejresh - This flag is set to true when there has been a request to refresh the display. 
The actual screen refresh occurs during the fossil collection phase. 

bool gvm::View::sceneChange - This is set to true when some component of the scene has changed its 
state. 


319 






stdr.string gvm::View::time - This is the time stamp of the eurrent seene. 

std'.'.string gvm::View::title - This acts as a title for the GLUT window containing the scene. 

ulong gvm::View::window - This is the GLUT window number for this view. When a GLUT window is 
created, it is assigned a unique identifier, which we retain in window. 

std'.ivector<hoo\> gvmv.View.-.zoom - This is a flag for determining whether or not to zoom into or away 
from a scene. When the “+’ or “=’ keys are pressed, zoom[G\ is set to true. When or is pressed, 
zoom[\\ is set to true. 

Protected Constructors; 

gvmv.Viewv.Viewiyoid) - This is the default class constructor for the gvmiiView class. It initializes 
nextObject, nextMessage,ptSize, sceneChange and refresh to 0, 0, 1.0, false and true respectively. It also 
calls the initializers buttonState(3, false), mouseLoc(2, 0) and windowiglutCreateWindowi^'%oA\ 
Display")). 

Public Methods; 

virtual void gvm::View::addNode(gvm’.:objectjindex i) - This routine adds to the back of the nodeList 
array, objectList[i\. 

virtual void gvin::VieH’::6cgf/i(void)=0 - This abstract method is used to set up any view-dependent 
settings related to view position, orientation, and the like. 

virtual gvm::object_index gvm::View::createObject(douhle t, gvmiiobjectjtype type) - This method 
scheduled the creation of a new object of type type for time t. 

virtual void gvm::View::createObject(gvm::object_handle /z)=0 - This abstract method is meant to have 
derived classes actually create an object with the handle specified. Once created, a pointer to it is inserted 
at objectList[h.first]. 


320 







virtual void gymy.ViewwdisplayiyoiA) - If isDisplayQ returns true, then this method will call the begin 
method, set the default point size and perform some boiler-plate OpenGL routines. After that, it calls the 
display method for each of the gvm::Node instance pointed to in the nodeList. Afterwards, the end method 
is called. Normally, the GLUT run-time system will inform the controlling sodhiGLUTViewManager 
instance that a display update needs to occur, which in turn calls this method. It is unwise to directly call 
the method, since there is some additional processing that occurs outside of the method that needs to be 
performed first. Requests for a redisplay of the screen can be made by a call to iiglutPostRedisplayi). 

virtual void gvm:iView::end(\oid) - This method performs some cleanup after the scene graph is 
displayed. 

virtual void gvm::View::entry(int e) - This method is called from with the controlling 
sodli'.GLUTViewManager whenever the mouse either enters or leaves the window associated with this 
view. Parameter e takes on either of the values GLUT_ENTERED if the mouse curser entered the 
window, or GLUT_LEFT if the mouse left the window. 

virtual void gvm'.:Viewy.fossilCollect{AovM& t) - The owning processrEiew instance calls this method 
during incremental fossil collection to time t. This fossil collection routine updates the scene graph, 
processing any messages with a time stamp equal to t. There should be no messages with a time stamp less 
then t, since those messages should have been processed during an earlier fossil collection cycle. 

virtual stdiistring gvmiiViewy.getTimeiyaiA) const - This routine returns time to the calling routine 

virtual std::stringgvm::View::getTitle(yoid) const - This routine returns title to the calling routine 

virtual vdonggvm’.',Viewy.getWindow{\oid^ const - This routine returns window to the calling routine. 

virtual bool gvm'.:View’.’.isDisplay{yoidi)=ti - This abstract method returns true exactly when the derived 
class instance actually implementing this method has determined that it needs to redraw its scene graph. 

virtual void gvmr.View’.’.keydownQaytt key, int x, int y) - When the GLUT run-time system detects a key 
press event for the GLUT window associated with this gvm::View instance, the controlling 


321 






sodlr.GLUTViewManager is notified, which in turn calls this method to notify the view. Parameter key is 
the ASCII value of the key that was pressed, and the mouse position at the time of the key press is given by 

virtual void gvmv.Viewv.keyupfhyte^ key, int jc, int y) - When the GLUT run-time system detects a key 
release event for the GLUT window associated with this gvmr.View instance, the controlling 
sodl::GLUTViewManager is notified, which in turn calls this method to notify the view. Parameter key is 
the ASCII value of the key that was released, and the mouse position at the time of the key press is given 
by <x, y>. 

virtual void gvm::View::motion(mt x, int y) - When the GLUT run-time system detects an active mouse 
motion event (i.e. one where at least one mouse button is pressed while the mouse is moved) in the GLUT 
window associated with this gvmr.View instance, the controlling sodlr.GLUTViewManager is notified. 
This in turn calls this method to notify the view. The position of the mouse is given by <x, y>. 

virtual void gvm::Viewr.mouseimt button, int state, int x, int y) - When the GLUT run-time system 
detects an active mouse button event (i.e. one where at least one mouse button changes its state) in the 
GLUT window associated with this gvmr.View instance, the controlling sodlr.GLUTViewManager is 
notified. This in turn calls this method to notify the view. Parameter button refers to the button that was 
actually had the event: GLUT_LEFT_BUTTON for the left mouse button, GLUT_MIDDLE_BUTTON 
for the middle button or GLUT_RIGHT_BUTTON for the right button. The state parameter describes the 
new mouse button position: GLUT_UP for a button release; GLUT_DOWN for a button press. The 
position of the mouse at the time of the button event is given by <x, y>. 

virtual gvm::Object& gvmr.Viewr.operator[]{gvmr.object_index i) - This routine returns *objectList[i] to 
the calling routine. In the event that i is out of range, this routine will throw an Exception::RangeError. 

virtual void gvm::View::overlay(void) - When the GLUT run-time system detects an overlay event in the 
GLUT window associated with this gvmr.View instance, the controlling sodlr.GLUTViewManager is 
notified, which in turn calls this method to notify the view. 


322 






virtual void gvm::View::passive_motion(mt x,inty) - When the GLUT run-time system detects a passive 
mouse motion event (i.e. one where no mouse button is pressed while the mouse is moved) in the GLUT 
window associated with this gvmr.View instance, the controlling sodh'.GLUTViewManager is notified, 
which in turn calls this method to notify the view. The position of the mouse is given by <x, y>. 

virtual void gvmv.Viewwresdisplayiyoid) - This method requests that the GLUT engine redisplay the 
scene associated with this view by calling glutSetWindow(window) followed by glutPostRedisplayO- 

virtual void gvm::View::reshape(mt width, int height) - When the GLUT run-time system detects a 
reshape event to size <width, height> in the GLUT window associated with this gvmr.View instance, the 
controlling sodliiGLUTViewManager is notified, which in turn calls this method to notify the view. In 
this case, OpenGL needs to be informed of the change in the viewport parameters, and aspect needs to be 
updated to reflect the new window aspect ratio. 

virtual void gvm::View:‘.restore(_do\ih\e. t) - When the owning process:View instance receives a rollback 
request to time t, it calls this method. Sinee the new t is the new time stamp for this gvmr.View instance, 
all message previously scheduled for times at or after t are now invalid, and must be removed from 
messageList. This method accomplishes this removing them from the front of messageList. 

virtual void gvm::View::schedule(gvm::Message* msg) - Messages are scheduled for later processing 
with this method. The new message inserted at the back of msgList. By virtue of the Time Warp 
algorithm, {*msg).getTimeO is never less than (*messageListJ>ackO).getTime{). Thus, the messages 
appear in msgList in ascending time stamp order from the earliest at the front to the latest in the back. 

virtual void gvm::View::setPointSizeiGLiRoa.tps) - This routine sets ptSize to ps. 

virtual void gvm::View::setPositionimt x, int y) - This routine sets the position of the GLUT window to 
<ac, y>. 

virtual void gvm::View::setRefresh(hool r) - This routine sets the refresh flag to r. 

virtual void gvm::Vie}v::setSceneChange{hool s) - This routine sets the sceneChange flag to s. 


323 








virtual void gvm::View::setSize(int w, int h) - This routine sets the size of the GLUT window to <w, h>. 
In this case, the GLUT run-time system will notify the sodhiGLUTViewManager controlling this view of 
the change, which will in turn call the reshape method described above. 

virtual void gvm::View::setTitle{std::string str) - This routine will set title to str and reset the GLUT 
window and icon titles to str. 

virtual void gvm::View::specialdown(int key, int x, int y) - When the GLUT run-time system detects a 
special key press events for the GLUT window associated with this gvmiiView instance, the controlling 
sodlr.GLUTViewManager is notified, which in turn calls this method to notify the view. Parameter key 
takes on one of the following values: GLUT_KEY_F1, GLUT_KEY_F2, GLUT_KEY_F3, 
GLUT_KEY_F4, GLUT_KEY_F5, GLUT_KEY_F6, GLUT_KEY_F7, GLUT_KEY_F8, 
GLUT_KEY_F9, GLUT_KEY_F10, GLUT_KEY_F11, GLUT_KEY_F12, GLUT_KEY_LEFT, 
GLUT_KEY_UP, GLUT_KEY_RIGHT, GLUT_KEY_DOWN, GLUT_KEY_PAGE_UP, 
GLUT_KEY_PAGE_DOWN, GLUT_KEY_HOME, GLUT_KEY_END, or GLUT_KEY_INSERT. 
The mouse position at the time of the key press is given by •cc, y>. 

virtual void gvm'.:View::special{mt key, int x, int y) - When the GLUT run-time system detects a special 
key release event for the GLUT window associated with this gvmtiView instance, the controlling 
sodhiGLUTViewManager is notified, which in turn calls this method to notify the view. Parameter key 
takes on one of the listed in the description of specialdown. The mouse position at the time of the key 
release is given by <x, y>. 

virtual void gvmy.View::visible^nt vw) - When the GLUT run-time system detects a visibility event (i.e. 
one in which the window either becomes visible or hidden) for the GLUT window associated with this 
gvmv.View instance, the controlling sodhiGLUTViewManager is notified, which in turn calls this method 
to notify the view. Parameter vis takes on one of the values GLUT_NOT_VISIBLE or GLUT_VISIBLE. 


324 







B.4.47. gvm::View2D 

The gvm::View2D class expands somewhat the functionality of the gvmiiView class above. This extension 
is mainly in the form of allowing only certain types of objects to be added to gvni::View::objectList, and 
how it handles input events from the user. 

Parent Classes: public gvm::View 

Derived Classes: None 

Private Data Members: 

float gvm:'.View2D::transX - This holds the translation factor in the X direction for viewing the scene. 

float gvm::View2D::transY- This holds the translation factor in the Y direction for viewing the scene. 

float gvm::View2D::sdepth - This is used to position the view at different distances “above” the view, 
allowing it to be uniformly scaled. 

float gvm::View2D::zNear - This is the near clipping plane, 
float gvm::View2D::zFar - This is the far clipping plane 

Public Constructors: 

gvm::View2D::View2D(yoid) - This class constructor initializes transX, transY, sdepth, zNear, zFar to 
0.0, 0.0, 80.0, 1.0 and 1000.0 respectively. 

Public Methods: 

virtual void gvm::View2D::addNode(gvm::object_index n) - This routine will add the gvm::Node2D 
instance with index n to the back of gvmv.Viewy.nodeList. 

virtual void gvm::View2D’,:begin{yold^ - This method performs some scene initialization that is useful in 
specifying how to display the scene graph. Among other things, it positions the viewer in a useful location. 


325 







In addition, if zoo/n[0] is set, the view zooms into the scene, and when zoom[\] is set, it zooms away from 
the scene. 

virtual void gvnf.:View2D::createObject (gvmv.objectjiandle h) - This method will create a gvm::Object 
instance of type h.first and with index Lsecond. Object types are limited to GVM_Nade2D, 
GVM_Polygon2D, and GVM_Vertex2D. Once created, a pointer to the new object is entered into 
gvm:: View::objectList[hj5econd]. 

virtual void gvm::View2D::end(void) - This method performs some eleanup actions in the view, notably 
changing the display buffers to display what had just been drawn. 

virtual bool gvm::View2D::isDisplay(\oid) - This method returns true exactly when the isVisible, 
refresh, and sceneChange methods return true. This will enable the scene to be updated on the screen 
only when there is a change in the scene graph and when there has been a request to change the display. 

virtual void gvm’.'.View2D\'.motionimi x, int y) - This method changes the view parameters to allow the 
user to interact with the scene. When the left mouse button is pressed, the scene is proportionally 
translated in plane with the mouse motion. When the middle button is pressed, the scene is scaled 
proportionally with the y-displacement of the mouse. 

B.4.48. gvm::View3D 

The gvm\‘.View3D class expands somewhat the functionality of the gvm::View class above. This extension 
is mainly in the form of allowing only certain types of objects to be added to gvm::View::objectList, and 
how it handles input events from the user. 

Parent Classes: public gvmr.View 

Derived Classes; None 

Private Data Members: 

std::valarray< GLdouble > gvm::View3D::pos - Position of the view point. 


326 







std::valarray< GLdouble > gvm::View3D:tori - Orientation of the view. 

GLdouble gvm::View3D::scale - Zooming factor of the scale. 

GLdouble gvmi:View3D::zNear - Near clipping plane. 

GLdouble gvm::View3D::zFar - Far clipping plane. 

Public Constructors: 

gvm::View3D::View3D(\oid) - This constructor initializes pos, ori, scale, zNear and zFar to <0.0, 0.0, - 
80.0>, <90.0, 0.0, -45.0>, 1.0, 1.0 and 1000.0 respectively. 

Public Methods: 

virtual void gvm:\View3D::addNode(svm::object_index i) - This routine will add the gvm::Node3D 
instance with index n to the back of gvm::View::nodeList. 

virtual void gvm::View3D::begini\oid) - This method performs some scene initialization that is useful in 
specifying how to display the scene graph. Among other things, it positions the viewer in a useful location. 
In addition, if zoowi[0] is set, the view zooms into the scene, and when zoom[Y\ is set, it zooms away from 
the scene. 

virtual yoid gvm::View3D::createObject(gvm::object_handle h) - This method will create a gvmr.Object 
instance of type h.first and with index h,second. Object types are limited to GVM_Cone, GVM_Cube, 
GVM_Cylindcr, GVM_Dodecahedron, GVMJcosahcdron, GVM_Node3D, GVM_Octahedron, 
GVM_Polygon3D, GVM_Sphere, GVM_Tetrahcdron, GVM_Torus and GVM_Vertex3D. Once 
created, a pointer to the new object is entered into gvmy.Vi€W.wbjectList[h,second]. 

virtual void gvm’.:View3D’.idisplay{yo\d) - This method performs some setup for displaying the 3D 
information in the GLUT window associated with this instance. If zooot[0] is set, the view zooms into the 
scene, and when zoom\X\ is set, it zooms away from the scene. 


327 








virtual void gvm'.'.ViewSD'.'.endiyoid) - This method performs some cleanup actions in the view, notably 
changing the display buffers to display what had just been drawn. 

virtual bool gvm::View3D::isDisplay(yoid) - This method returns true exactly when the 
gvm::View::isVisible, gvm::View::refresh, and gvm::View::sceneChange methods return true. This will 
enable the scene to be updated on the screen only when there is a change in the scene graph and when there 
has been a request to change the display. 

virtual void gvm::View3D::motion(mt x, int y) - This method is called when an active mouse event 
occurs within the GLUT window associated with this gvm::View3D instance. When the left mouse button 
is pressed, the scene rotates about the screen x-axis proportional to mouse displacement in the y-direction 
and rotation about the y-axis is performed proportionally to mouse displacement in the x-direction. When 
the middle button is pressed, the scene is scaled proportionally with the y-displacement of the mouse. 

B.4.49. GVM Definitions not associated with a specific class 

Enumerators: 

enum gv»i::message_0'pe{GVM_AddNode, GVM_AddShape, GVM_Add Vertex, 

GVM_CreateObject, GVM_Refresb, GVM_SetActive, GVM_SetColor, GVM_SetConeSize, 
GVM_SetCubeSize, GVM_SetCylinderSize, GVM_SetLabel, GVM_SetMode, GVM_SetPointSize, 
GVM_SetPosition, GVM_SetRotation, GVM_SetRotationCenter, GVM_SetScale, 

GVM_SetScaleCenter, GVM_SetSize, GVM_SetSphereSize, GVM_SetTorusSize, 

GVM_SetTranslation, GVM_SetVertex, GVM_LAST_MESSAGE} - Specifies the various 

message types associated with the GVM. There are a number of user messages that can be used for figures 
not defined here. These type labels take on the form GVM_UserMessageOOO, ... GVM_UserMessage255. 

enum gvm::objectjtype{G\M_Cone, GVM_Cube, GVM_Cylinder, GVM_Dodecahedron, 
GVMJcosahedron, GVM_Node, GVM_Node2D, GVM_Node3D, GVM_Object, GVM_Octahedron, 
GVM_Polygon2D, GVM_Polygon3D, GVM_Shape, GVM_Shape2D, GVM_Shape3D, GVM_Sphere, 
GVM_Tetrahedron, GVM_Torus, GVM_Vertex, GVM_Vertex2D, GVM_Vertex3D, ..., 


328 








GVM_LAST_OBJECT} - Specifies the various object types associated with the GVM. User defined 
processes can make use of pre-specified labels. These type labels take on the form GVM_UserProcessOOO, 
... GVM_UserProcess255. 

Type Definitions: 

typedef unsigned long gvmwobjectjndex - Unique identifier for a gvmiiObject instance assigned to a 
specific gvmziView instance. 

typedef std::pair<gvm::object_type, gvmz:object_index> gvm::object_handle - A compact form for 
specifying both agvm::Object instance’s type and identifier. 

Functions: 

std'.istring gvm::typeName(gvm::message_type t) - Returns a string representation of the message type t. 

std::ostream&. gvm::operator«{std::ostream& os, const gvmr.messagejype t) - Sends the string 
representation of t to os. 

std::string gvm::typeName(gvm::objectJype t) - Returns a string representation of the object type t. 

std::ostream& gvm::operator«{std::ostream& os, const gvm::object_type t) - Sends the string 
representation of t to os. 


329 








330 






Appendix C. Sample Code Listings 

These code samples those used to produce the demonstrations packaged with the SODL distribution. In 
some cases they have been modified slightly to remove debugging code and for formatting purposes. In all 
cases, the essential functionality of the code has been maintained. 


C.1. Battle 


C.1.1. Add Environment.msg 

{import message {SetValue} } 

{message:AddEnvironment(SetValue);} 

C.1.2. AddTrack.msg 

{import message {TrackMotionEvent} } 
{message:AddTrack(TrackMotionEvent);} 


C.1.3. AdjustFormation.msg 

{import message {MoveTo} } 
{import gvm {gvmTank} } 


/* 

This message is used to set the structure, position, orientation and 
spacing of a platoon formation. The platoon will form up on the lead tank 
which will be in the center of the formation, and will be at the location 
specified in pos. All of the tanks will face direction ori with distance 
between adjacent tanks norm(ori). The left flank will be spread along 
a line emanating at angle left to the left of the lead tank. When left is 
0.0, the left side of the formation will be abreast the lead tank. 

Positive values of left will rotate the line forward; negative values will 
rotate the line back. The same case holds for the right flank. 

There are some predefined formations: 

LINE_ABREAST => left = 0.0, right = 0.0 

V_FORMATION => left = -PI/8, right = -Pl/8 

FORWARD_SWEEP => left = PI/8, right = PI/8 

COLUMN => left = PI/2, right = -PI/2 


message:AdjustFormation(MoveTo) 

{ 

double:left(0.0); 
double:right(0.0) ; 

method:setOri(public; void; double:x; 

{ ori[0]=x; ori(l]=y; ori[2]=0.0; ) 
method:setOri(public; void; double:x; 
{ ori[0]=x; ori[l]=y; ori[2]=z; ) 


// message:AdjustFormation(MoveTo) 
// Left flank angle 
// Right flank angle 

double:y;) // Orientation vector vals 
// Set the orientation valarray 
double:y; double:z;) // Orientation 

// Set the orientation valarray 


method:setFlank(public; void; double:!; double:r;) 

{ left=l; right=r; } // Set the formation parameters 

method:getLeft(public; double;) { return left; } // Get left flank angle 


method:getRight(public; double;) { return right; } // Get right flank angle 
method:setForm(public; void; ulong:form;) // Use a standard formation 


switch (form) 
{ 


331 





case LINE_ABREAST: left = right = 0.0; break; 
case V_FORMATION: left = right = -PI/8.0; break; 
case FORWARD_SWEEP: left = right = PI/8.0; break; 
case COLUMN: left = PI/2.0; right = -PI/2.0; break; 

default: left=right=0.0; // Default is line abreast 

} 

) 

} // message:AdjustFormation(MoveTo) 


C.1.4. Attack.msg 

{ 

message:Attack 
{ 

ulong:track; // Track to attack 

method:set(public; void; ulong:t;) { track=t; } // Set the track index 

method:get(public; ulong;) { return track; ) // Get the track index 

} // message:Attack 

} 


C.1.5. Battle.proc 

{import process {BattleView, NodeSD, RedCompany, BlueCompany, Ground, 
Environment) > 

{import message {AddNodeSD, AddShape3D, StartSimulation, SetFormation, 
SetEnvironment, SetRefresh) } 

{import spt {sptEnvironmentObject, sptNewtonianMotion, sptLinearMotion, 
sptAngularMotion) } 

{debug false) 

{ 

process:Battle 
{ 

BattleView:view; 

Node3D:root; 

Ground:ground; 

BlueCompany:blue; 

RedCompany:red; 

Environment:environment; 


// process:Battle 
// Main view for the battle 
// Root node for the view 
// Play region 
// Blue force 
// Red force 

// Environment in which simulation exists 


mode:Default 


// mode:Default 

node:start_sim[StartSimulation:strt] // Bootstrapping message 

[AddNode3D:an=>(view;), // Add a node to the main view 

AddShape3D:as=>(root;), // Add the shape to the root node 
SetEnvironment:se=>(blue; red; ground;), 

SetRefresh:sr=>(view; red; blue;)] 

{ // node:start_sim[StartSimulation][ 

EngineStand::stand.addHold(1.0); // Set a hold at ti 

an.add(root); // Add the root node to th 

as.add(ground); // Add the ground to th 

se.setNode(root); // Tell the forces of the roo 

se.SetEnvironment(environment); // Set the envir 

sr.set(0.5); // Refres 


); // Set a hold at time 0.0 

// Add the root node to the view 
// Add the ground to the node 
// Tell the forces of the root node 
; // Set the environment 

// Refresh rate 
// node:start_sim[StartSimulation][ ... ] 

// mode:Default 
// process:Battle 


C.1.6. BattleView.proc 

{import process {View3D, Tank, CommandPost, NewtonianMotion, Munition, 
Ground) } 

{import message {SetTankState, SetNewtonianMotion, Destroyed, Explosion) ) 
{import gvm {gvmBattleView, gvmNewtonianMotion, gvmCommandPost, gvmTank, 

gvmSetTankState, gvmMunition, gvmGround, gvmSetNewtonianMotion, 
gvmGrid, gvmSetActive, gvmExplosion) ) 


332 






{debug false) 


process:BattleView(View3D) 

{ 

method:init(public; void;) 

{ 

view = new gvm::BattleView; 
View::init(); 
view->setSize(1020,1063) ; 

} 


// process:BattleView(View3D) 

// method;init(public; void;) 

// Create a new view 
// Call the parent class initializer 
// Set the size of the window 
// method:init(public; void;) 


method:getGVMType(protected; gvm::object_type; ptype:t;) 

{ // method:getGVMType(protected; gvm;:object_type; ptype;) 

switch(t) // Which one is it? 

( 

case SPT_Tanlc: return gvm: :GVM_Tank; 
case SPT_CommandPost: return gvm;:GVM_CommandPost; 
case SPT_Munition: return gvm::GVM_Munition; 
case SPT_Ground: return gvm::GVM_Ground; 
default: return View3D::getGVMType(t); 

} // switch(t) 

} // method:getGVMType(protected; gvm::object_type; ptype;) 

mode:Default 

( // mode:Default 

node: setTanl<State [SetTankstate:in] [] 

( // node:setTankState[SetTankState:in][] 

view->schedule(new gvm::SetTankState(*view,getTime(),in.index, 

in.az,in.azRate,in.azStart,in.azStop, 
in.el, in.elRate,in.elstart,in.elStop)); 

} // node:setTankState[SetTankState:in][] 


node:setNewtonianMotion[SetNewtonianMotion:in][] 

{ // node:setNewtonianMotion[SetNewtonianMotion:in][] 

view->schedule(new gvm::SetNewtonianMotion(*view,getTime(), 
in. index, in.getO)); 

} // node:setNewtonianMotion[SetNewtonianMotion:in][] 


} 


node:destroyed[Destroyed:in][] 

{ // node:destroyed[Destroyed:in][] 

view->schedule (new gvm: :SetActive (*view, getTimeO, in.index, false)); 

} // node:destroyed[Destroyed:in][] 

node:explosion[Explosion:in][] 

{ // node:destroyed[Destroyed:in][] 

view->schedule(new gvm::Explosion(*view,getTime(),in.index,in.get())); 

} // node:destroyed[Destroyed:in][] 

// mode:Default 
// process:BattleView(View3D) 


C.1.7. BlueCompany.proc 

[import process [Company] } 

(import message [StartSimulation, SetColor, SetFormation, SetLinearPosition, 
MoveFormation] } 

[import spt {sptEnvironmentObject] } 

[import gvm {gvmTank} } 

[import [<math.h>} } 


process:BlueCompany(Company) 

{ 

method:init(public; void;) 

{ 

Company::init(); 
view->setTitle("Blue Tactical View" 
force = BLUE; 

view->setPosition(1032,574); 


II process:BlueCompany(Company) 

// method:init(public; void;) 
Initialize the parent construct first 
; // Blue view title 

// This has force designator "BLUE" 
II Set the position of the window 


333 






view->setSize(563, 516); 


// Set the size 
// method;init(public; void;) 


mode:startup 

( 

node:start[StartSimulation;in] 

[SetColor:sc=>(platoons; cp;) 

SetFormation:sf[]=>(platoons[@];) , 

SetLinearPosition:slp=>(cp;), 

MoveFormation;smove=>(platoons[3] ; 

MoveFormationimf[]=>(platoons[@];):(@<3 ? 10.0 : 10.0+0*5.0)] 

{ // node:start[StartSimulation:in][ ... ] 

sc.set(0.0, 0.0, 1.0, 1.0); // Set the color of subordinates to blue 

double r = sqrt(5000.0); // Range between tanks 

for (uint i=0; i<platoons.size(); ++i) // Loop over the platoons 


// mode:startup 
// Upon startup 
// Set the color of the objects 
, // Platoon formation 

// Command post position 
r):(10.0), 


double X = 8500+r*((double) i); 
sf.push_back(me); 
sf[i].setPos(x,x); 
sf[i].setOri(-r, -r); // 

sf[i].setForm(LINE_ABREAST); 
if (i==0) 


// Location 
// Create a new message 
// Set the platoon position 
Set the platoon orientation & spacing 
// Use line abreast formation 


{ 

smove.setPos(x,x); // Move this platoon up slightly 

smove.setOri(-r, -r) ; 

smove.setForm(LINE_ABREAST); 

} 


} 


mf .push_back(me); 
if (i<3) 

{ 

X = -9500+r*((double) i); 
mf[i].setPos(x,x); 
mf[i].setOri(-r, -r); 
mf[i].setForm(LINE_ABREAST); 

} 

else 


// Create a new move formation message 
// If any of the first three platoons 


// Destination location 
// Set the platoon position 
// Set the platoon orientation & spacing 
// Use line abreast formation 
// if (i<3) 
// if {i>=3) 


{ 

double a = 0.125*PI; // Angle to place tanks relative diagonal 

double s = sin(a); // Sin of angle from diagonal 

double h = 3500.0*sqrt(2.0+(s-2.0)*s); // distance from CP 

double theta = 1.25+PI + (i==3 ? -a : a); // Angle for this platoon 
mf[i].setPos{9500+h*cos(theta), 9500+h*sin(theta)); 
mf[i].setOri(-r, -r); // Set the platoon orientation & spacing 

mf[i].setForm{V_FORMATION); // Use line abreast formation 

} // else from if (i<3) 

} // for (uint i=0; i<sf.size(); ++i) 

sip.set(9500.0, 9500.0, 0.0); // Set the position of the command post 

// node:start[StartSimulation:in][ ... ] 

// mode:startup 
// process;BlueCompany(Company) 


C.1.8. ChangeTrack.msg 

{import message {TrackMotionEvent] ] 
[message:ChangeTrack(TrackMotionEvent);} 


C.1.9. CommandPost.proc 

[import process [SensorTrack] } 

[import message [AddShape3D, SetColor, UnitSetup, RegisterEnvironmentObject] } 
[import spt [sptEnvironmentObject] } 

{ 

process;CommandPost(SensorTrack) 

{ // process:CommandPost(SensorTrack) 

mode:Default 

[ // mode:Default 

node:unitSetup[UnitSetup:in] // Setting the environment 

[RegisterEnvironmentObject;out=>(in.environment;), // Reg 


334 






} 


AddShape3D:as=>{in.getNode();) , // Ensure display 

SetColor:sc=>(me;)] // Proper color 

II node:unitSetup[HnitSetup:in][SetEnvironment:se] 
out.setForce(force = in.getForce{)); // Set the force component 

out.setRadius(radius = in.getRadius()); // Set the sensor radius 

out.setMotion(nm); II Set the newtonian motion parameters 

as.add(me); II Add this as a subordinat to the node 

if (force==BLUE) sc.set(0.0, 0.0, 1.0); // Set the proper color 

else if (force==RED) sc.set(1.0, 0.0, 0.0); 
else sc.set(1.0, 1.0, 1.0); 

// node:unitSetup[UnitSetup:in][SetEnvironment:se] 

// mode:Default 
// process:CommandPost(SensorTrack) 


C.1.10. Company.proc 

(import process (Platoon, CommandPost, Environment) } 

(import message (SetEnvironment, UnitSetup, AddTrack, ChangeTrack, LoseTrack, 
SetNewtonianMotion, RefreshDisplay, StartSimulation, 
SetRefresh, Destroyed) ) 

(import spt {sptNewtonianMotion) ) 

(import gvm (gvmTacticalView, gvmTacticalGrid, gvmTrack, gvmAddTrack, 
gvmChangeTrack, gvmDeleteTrack, gvmRefresh) } 

(import ("GLUTViewManager.h", <GL/glut.h>, <math.h>} } 

(debug false) 


process:Company 
{ 

Platoon:platoons[5]; 
bool:active[5](true); 

CommandPost:cp; 

process:environment; // 

ulong:force(NEUTRAL); 

double:tankSensorRange(2000.0); 

double:cpSensorRange(5000.0); 

ulong:trackCount[]; 

spt::NewtonianMotion:tracks(]; 

ulong:trackForce[]; 

gvm;:TacticalView*:view; 

double:refreshinterval(0.01); 


// process:Company 
// The platoons composing this company 
// All of the platoons are active 
// Command post for the company 
Environment in which the simulation exists 

// Force flag 
// Default tank sensor range 
// Default command post sensor range 
// Number of platoons tracking each object 
// Actual objects being tracked 
// Force association of the tracks 
// Tactical view for the company 
// Time between consecutive refreshes 


method;init(public; void;) 

( // method:init(public; void;) 

view = new gvm::TacticalView; // Create a new view 

dynamic_cast<sodl::GLUTViewManagerS>(*EngineStand::stand.vm). 
addview(view); 

} // method:init(public; void;) 


method:restore(public; void;) 
{ 

view->restore(getTime()); 

) 


// method:restore(public; void;) 

// Rollback the view to time t 
// method:restore(public; void;) 


method:fossilCollect(public; void;) 
{ 

view->fossilCollect(getTime()); 

) 


// method:fossilCollect(public; void;) 
// Fossil collect the view up to time t 
// method:fossilCollect(public; void;) 


mode:startup 


node:start[StartSimulation:in][RefreshDisplay:out=>(me;);(0.0)] (} 


node:unitSetup[SetEnvironment:in] // Setting the environment 

[DnitSetup:pl_us=>(platoons;) , II Setup the platoons 

UnitSetup:cp_us=>(cp;)] // Setup the command post 

( // node:SetEnvironment[SetEnvironment:in][SetEnvironment:se] 

environment=in.getEnvironment0; // Set subordinate environs 

pl_us.set(force, tankSensorRange, environment, in.getNode()); 


335 







cp_us.set(force, cpSensorRange, environment, in.getNode()); 

} // node:setEnvironment[SetEnvironment:in][SetEnvironment:se] 


} 


mode:run 
{ 

node:setNewtonianMotion[SetNewtonianMotion:in][] 

{ // node:setNewtonianMotion[SetNewtonianMotion:in][] 


if (tracks.size() <= in.index) 

{ 

tracks.resize(in.index+1) ; 
trackCount.resize(in.index+1, 0); 
trackForce.resize(in.index+1, 0); 

} 

tracks[in.index] = in.nm; 
if (trackCount[in.index]==0) 


// If we need to increase storage 

// Resize the track array 
// Resize the counter 
// Resize the counter 
// if (tracks.size() <= in.index) 
// Save the motion paramter 

// Is this a new friendly track? 


> 


double rad=in.getSource().getType()==SPT_CommandPost ? cpSensorRange 

: tankSensorRange; 

view->schedule(new gvm::AddTrack(*view, getTime(), in.index, 
in.nm, rad, force)); 

trackCount[in.index] =1; // Increment the number of trackers 

trackForce[in.index]=force; // Set track identity 

} // if (trackCount[in.index]==0) 

else // If we've seen this one before 


{ 

} 


view->schedule (new gvm: iChangeTrack(*view, getTimeO, in.index, 

in.nm)); 

// node:setNewtonianMotion[SetNewtonianMotion:in][] 


node:setRefreshinterval[SetRefresh:in)[] 

{ refreshinterval = in.refreshinterval; } 


node:refresh[RefreshDisplay:in] 

[RefreshDisplay:out=>(me;):(in.getTime()+refreshinterval)] 

{ // node:refresh[RefreshDisplay:in][RefreshDisplay:out) 

view->schedule(new gvm::Refresh(*view,getTime())); // Schedule refresh 

} // node:refresh[RefreshDisplay:in][RefreshDisplay:out] 

node:addTrack[AddTrack:in][] 

{ // node:addTrack[AddTrack:in][] 

if (tracks.size() <= in.getTrack()) // If we need to increase storage 

{ 

tracks.resize(in.getTrack0+1); // Resize the track array 

trackCount.resize(in.getTrack0+1, 0); // Resize the counter 

trackForce.resize(in.getTrack0+1, 0); // Resize the counter 

} // if (tracks.size() <= in.getTrack()) 

if (trackCount[in.getTrack()]==0) 

view->schedule(new gvm::AddTrack(*view, getTimeO, in.getTrack(), 
in.motion, -I, in.getForce())); 

tracks[in.getTrack()] = in.motion; // Save the motion paramter 

trackCount[in.getTrack()]++; // Increment the number of trackers 

trackForce[in.getTrack0]=in.getForce0; // Set track identity 

} // node:addTrack[AddTrack:in] [ ] 


node:changeTrack[ChangeTrack:in][] 

{ // node:changeTrack[ChangeTrack:in][] 

if (tracks[in.getTrack()].getStartTime() != in.motion.getStartTime()) 

{ 

view->schedule(new gvm::ChangeTrack(*view,getTime(),in.getTrack(), 
in.motion)); 

tracks[in.getTrack0] = in.motion; // Save the motion paramter 

} 

} // node:changeTrack[ChangeTrack:in][] 

node:loseTrack[LoseTrack:in][] 


336 






—trackCount[in.getTrack()]; 


// node:loseTrack[LoseTrack:in)[] 
// Reduce the number of trackers by 1 


if (trackCount[in.getTrack()]==0) 

view->schedule(new gvm::DeleteTrack(*view,getTime(), 

in.getTrack0)); 

} // node:loseTrack[LoseTrack:in][] 

node:destroyed[Destroyedrin][] // Upon notification of a unit's loss 

{ // node:destroyed[Destroyed:in][] 

if (in. getSource {) . getType () == SPT_CoinmandPost) // If CP destroyed 

{ 

std::cout << me « " lost." « std::endl; // This team lost 

exit(O); // Don't do this at home 

} // if (in.getSource{).getType() == SPT_CommandPost) 

else // The notification should have come from a platoon 

{ 

if (in.index == ((ulong) -1)) // If the platoon was lost 

{ 

bool a = false; // Accumulator to determine company status 

for (ulong i=0; i<platoons.size(); ++i) // Loop over all platoons 

{ 

if (in.getSource0==platoons[i]) // If this platoon was it 

active[i]=false; // It is now inactive 

a = a II active[i]; // Accumulate activity 

} // for (ulong i=0; i<platoons.size(); ++i) 

if (!a) // If all of the platoons are destroyed 

std::cout « me << " surrenders." « std::endl; // This team lost 
exit(O); // Don't do this at home 

} 

} // if (in.index == ((ulong) -1)) 

else // if (in.index != ((ulong) -1)) 

{ 

trackCount[in.index]=((ulong) -1); // No longer tracking tank 

view->schedule(new gvm::DeleteTrack(*view,getTime(),in.index)); 

} 

) // else from if (in.getSource.getType()==SPT_CommandPost) 

} // node:destroyed[Destroyed:in][] 

// mode:startup 
// process:Company 


C.1.11. Destroyed.msg 

{import message (SetValue) } 

{ message:Destroyed(SetValue); } 

C.1.12. Environmnent.proc 

{import message {RegisterEnvironmentObject, AddEnvironment, AddTrack, 

LoseTrack, ChangeTrack, SetNewtonianMotion, ScheduleAddTrack, 
ScheduleLoseTrack, Explosion, Impact, Hit, Destroyed} } 

{import spt {sptLinearMotion, sptLinearMotion, sptEnvironmentObject, sptDefs} } 
{import std {<map>} } 

{import {<math.h>} } 


process:Environment 

{ 

spt::EnvironmentObject:motion[]; 
process:obj ects [ ]; 
std::set<ulong>:tracks[]; 
bool:active[]; 


// process:Environment 
// Object motion parameters 
II Associated process handles 
// List of sensor/track associations 
// Active list of sensor/tracks 


method:trackTimes(public; std::vector<double>; spt::EnvironmentObjectS:s; 

spt::EnvironmentObject&:t;) 

{ // method:trackTimes(public; std::vector<double>; LinearMotion; ... ) 

double time = getTime(); II Get the current time 

spt::vertex p = t.Ip(time)-s.Ip(time); // Relative position 

spt::vertex v = t.Iv(time)-s.Iv(time); // Relative velocity 


337 







std::vector<double> rv; // Return the detect/loss times 

double a = dot(v,v); // Get the polynomial coefficients 

double b = 2*dot(p,v); 

double c = dot(p,p)-s.rad()*s.rad(); 

double disc = b*b-4*a*c; 

if (a > 0.0 && disc > 0.0) // If there are more than one real roots 

{ 

double tl = time+(-b+sqrt(disc))/(2*a); 
double t2 = timet{-b-sqrt(disc))/(2*a) ; 

rv.push_back(min(tl, t2)); // Entry time into sensor range 

rv.push_bac)c (max (tl, t2)); // Exit time from sensor range 

p = t.Ip(tl)-s.Ip(tl); 
p = t.Ip(t2)-s.Ip{t2); 

} // if (a > 0.0, && disc > 0.0) 

else if (a==0.0 c<0.0) // If rel vel is 0 & track in range of sensor 

{ // Track has always been and will alway be in sensor range 

rv.push_back(-Clock:rgetEndTime0); // A long time ago 

rv.push_back(2.0*Clock::getEndTime0); // Forever 

} // else if {a==0.0 && c<0.0) 


// Entry time into sensor range 
// Exit time from sensor range 


return rv; 

} // raethodrtrackTimes(public; 


: vector<double>; 


// Return the times 
LinearMotion; ... ) 


method:configure(public; void; std::vector<AddTrack>&;at; 

ulong:sensor; ulong:track;) 

{ // method:configure (public; void; std:: vector<AddTrack>Si: at; ... ) 

at.push_back(me); // Generate new message 

at.back().addDest(objects[sensor)); // The message goes to sensor 

at.back().setMotion(motion[track]); // Set track motion parameters 

at.back().set(track, motion[track].iff()); // Set identifier info 

} // method:configure(public; void; std::vector<AddTrack>s:at; ... ) 

method:configure(public; void; std::vector<LoseTrack>s:It; 

ulong:sensor; ulong:track;) 

{ // method:configure(public; void; std::vector<LoseTrack>S:at; ... ) 

lt.push_back(me); // Generate new message 

It.back0.addDest(objects[sensor]); // The message goes to sensor 

It.back().set(track, motion[track].iff()); // Set track id info 

} // method:configure(public; void; std::vector<LoseTrack>&:at; ... ) 


method:configure(public; void; 

{ // method:configure(public; 

at.push_back(me); 
at.back().addDest(me); 
at.back().setTime(t); 
at.back().set(sensor, track), 
} // method:configure(public; 


std::vector<ScheduleAddTrack>&:at; double:t; 
ulong:sensor; ulong:track;) 

void; std::vector<ScheduleAddTraok>&:at;...) 

// Generate new message 
// The message comes to me 
// With time stamp t 
// Set sensor and the track indices 
void; std: : vector<ScheduleAddTrack>6i: at; . . .) 


method:configure(public; void; std::vector<ScheduleLoseTrack>s:It; 

double:t; ulong:sensor; ulong:track;) 

{ // method:configure(public; void; std;:vector<ScheduleLoseTrack>&:lt;...) 
It.push_back(me); // Generate new message 

It.back().addDest(me); // The message comes to me 

It.back().setTime(t); // With time stamp t 

It.back().set(sensor, track); // Set the sensor and the track indices 

} // method:configure(public; void; std::vector<ScheduleLoseTrack>&:It;...) 

method:testDetect(public; void; ulong:sensor; ulong:track; 

std:;vector<ScheduleAddTrack>&:sat; 
std::vector<ScheduleLoseTrack>&:sit; 
std::vector<AddTrack>s:at; 
std::vector<LoseTrack>&:It;) 

{ // method:testDetect(public; void; ...) 

std;:vector<double> t = trackTimes(motion[sensor], motion[track]); 
double mt=min(motion[track].getStopTime0, motion[sensor].getStopTime()); 

if (active[track] active[sensor]) 


if (t.emptyO && // If sensor never sees track 

tracks[track].find(sensor) != tracks[track].end()) // If known 


338 






{ 

configure(It, sensor, track); // It is no longer known 

} // if (t.empty!) &S ... ) 

else if (It.empty!)) // If there are detection times 

{ 

if !t[0]<getTime!) &S t[1]>getTime!)) // Is track currently in range 

( 

if (tracks[track].find(sensor)==tracks[track].end0) // Not known? 

{ 

tracks[track].insert(sensor); // Associate track with sensor 

configure(at, sensor, track); // Configure sensor notification 
} // if (tracks[track].find(sensor)==tracks[track].end 0) 

if (t[l]<rat) // Should we schedule a LoseTrack? 


} 


{ 

configure(sit,t[1],sensor,track); // Configure lose track message 
} // if (t[l]<mt) 

} // if (t[0]<getTime() && t[1]>getTime()) 

else if (t[0]>getTime() £& t[0]<mt) // If entry will occur 

{ 

configure(sat, t[0], sensor, track); // Configure a new message 

if (t[l]<mt) // If the exit will occur 


{ 

configure(sit, t[l], sensor, track); // Configure a new message 
} // if (t[l]<mt) 

} // else if (t[0]>getTime() && t[0]<mt) 

// if (loldTimes.empty 0) 
// if (active[track] && active[sensor]) 
// methodrtestDetect(public; void; ...) 


method;symetricDetect(public; void; ulong:sl; ulong:s2; 

std::vector<ScheduleAddTrack>&:sat; 
std::vector<ScheduleLoseTrack>s:sit; 
std::vector<AddTrack>&:at; 
std::vector<LoseTrack>&:lt;) 

{ // method:symetricDetect(public; void; ...) 

std::vector<double> t = trackTimes(motion[sl], motion[s2]); 
double mt=min(motion[si].getStopTime (), motion[s2].getStopTime()); 


if 

{ 


(active[si] active[s2]) 
if (t.empty!) && 

tracks[s2].find(si) != tracks[s2].end()) 


// If si never sees s2 
// If known 


configure(It, si, s2); 
configure(It, s2, si); 

} 

else if (It.emptyO) 

{ 


// It is no longer known 
// It is no longer known 
// if (t.empty!) && ... ) 
// If there are detection times 


if 

( 


(t[0]<getTime() && t[1]>getTime()) // Is track currently in range 

.f (tracks[sl] .find(s2)==tracks[sl] .end() ) II and not known 


{ 


tracks[s2].insert(si); 
tracks[si].insert(s2); 
configure(at, si, s2); 
configure(at, s2, si); 

} 

if (t[l]<mt) 

{ 


// Associate s2 with si 
// Associate si with s2 
// Configure si notification 
II Configure s2 notification 
// if (tracks[si].find(s2)==tracks[si] .end 0) 
II Should we schedule a LoseTrack? 


configure(slt,t[l],sl,s2); 
configure(sit,t[1],s2,si); 


} 

} 

els€ 


// Configure lose track message 
// Configure lose track message 
II if (t[1]<mt) 
// if (t[0]<getTime() && t[1]>getTime()) 


if (t[0]>getTime() && t[0]<mt) 


// If entry will occur 


configure(sat, t[0], si, s2); 
configure(sat, t[0], s2, si); 
if (t[1]<mt) 

{ 

configure(sit, t[l], si, s2); 
configure(sit, t[l], s2, si); 


II Configure a new message 
II Configure a new message 
II If the exit will occur 

// Configure a new message 
// Configure a new message 


339 


// if (t[l]<mt) 
// else if (t[0]>getTime() && t[0]<mt) 
// if (!oldTimes.empty 0) 
// if {active[sl] active[2]) 
// method:symetricDetect(public; void; ...) 


method:canSense(public; bool; ulong:i;) // Index of sensing object 

{ II method:canSense(public; bool; ulong:i;) 

return (motion[i].iff() == RED || motion[i].iff{) == BLUE) && active[i]; 

) // method:canSense(public; bool; ulong:i;) 


method:test(public; void; std::string:s;) 

{ 

} 


method:genTrackMessages(public; void; ulong:changed; // Changed obj index 

std::vector<ScheduleAddTrack>S:sat; 
std::vector<ScheduleLoseTrack>&:sit; 
std::vector<AddTrack>&:at; 
std::vector<LoseTrack>&:it;) 

{ II method::genTrackMessages(public; void; ulong:changed; ... ) 

ulong force = motion[changed].iff(); // Get the force 

if (canSense(changed)) // If changed object can have a sensor 

for (ulong i=0; Kmotion.size (); ++i) // Loop over the other objects 

{ 

double iforce = motion[i].iff(); // Get next object's force 

if ( canSense(i) SS force!=iforce ) // Is this sensible 

{ // If forces are opposed to each other 

if (motion[i].rad() == motion[changed].rad()) // Do both at once? 

symetricDetect(i, changed, sat, sit, at. It); 
else // If the sensor radii differ between objects 

( 

testDetect(i,changed,sat,sit,at,It); // Check with i as sensor 

testDetect(changed,i,sat,sit,at,It); // Check with changed sensor 
} // else from if (motion[i].rad() == motion[changed].rad()) 

} // if (canSense(i) && force!=iforce) 

else if (iforce != force) // If motion[i] has no sensing capability 


{ 

testDetect(changed, i, sat,sit,at. It); // Check with motion[changed] 

} 

} // for (ulong i=0; i<motion.size(); ++i) 

} // if (canSense(changed)) 

else if (active[changed]) // If changed is not a sensing object 

for (ulong i=0; i<motion.size(); ++i) // Loop over everything else 

if (canSense(i)) II Is it a sensor? 

testDetect(i,changed,sat,sit,at,It); // Test with changed as track 

} // method; .-genTrackMessages (public; void; ulong: changed; ... ) 


method:inRange(public; bool; ulong:sensor; ulong:track;) 

{ // method;inRange(public; bool; ulong:sensor; ulong:track;) 

double adjTime = getTimeO + 0.0001; // Get time slightly ahead 

double d = motion[sensor].rad()^motion[sensor].rad(); // Square of range 

spt::vertex rp = motion[track].Ip(adjTime)- motion[sensor].Ip(adjTime); 
return (dot(rp, rp) <= d); // Is it really in range? 

} // method:inRange(public; bool; ulong:sensor; ulong:track;) 


mode:Default 

{ // mode:Default 

node:registerObject[RegisterEnvironmentObject:in] II Request both 

[AddEnvironment:out=>(in.getSource();), // Confirm 

ScheduleAddTrack:sat[], // Pending detections 

ScheduleLoseTrack:slt[], II Pending track losses 

AddTrack:at[], II Add track notifications 

LoseTrack:It [] ] II Lose track notifications 

{ // node:registerObject[RegisterEnvironmentObject][AddEnvironment] 

out.index = motion.size(); // Report back to source its index 

motion.push_back(spt::EnvironmentObject(in.getForce() , 

in.getRadius0)); // new s_t 

static_cast<spt:;NewtonianMotion&>(motion.back())=in.getMotion(); 
objects.push_back(in.getSource0); // Save the process handle 


340 


tracks.push_back{std::set<ulong>()); // Create a new one 

tracks-back{).clear(); // Clear the set 

active.push_back(true); // Track is currently active 

genTrackMessages(motion.size 0-1, sat, sit, at. It); // Update messages 
// node:registerObject[RegisterEnvironmentObject][AddEnvironment] 


node:setNewtonianMotion[SetNewtonianMotion:in] 

[ScheduleAddTrack:sat[], 
ScheduleLoseTrack:sit[], 
AddTrackiat[], 

LoseTrack:It[], 


// Something is moving 
// Schedule detects 
// Scheduled loses 
// Current detects 
// Current detects 


ChangeTrack:ct] // Change the track direction 

// node:setNewtonianMotion[SetNewtonianMotion:in][ ... ] 
static_cast<spt::NewtonianMotionS>(motion[in.index])=in.get(); 
genTrackMessages(in.index,sat,sit,at,It); // Update things 

std::set<ulong>::iterator i; // Index for sensors knowing about track 
for (i=tracks[in.index].begin 0; i!=tracks[in.index].end(); ++i) 

ct.addDest(objects[*i]); // Notify the sensors tracking the track 

ct.setMotion(motion[in.index]); // Set motion parameters 

ct. set (in. index, motion [in. index] .if f 0) ; // Set track ID params 

// node:setNewtonianMotion[SetNewtonianMotion:in][ ... ] 


node:addTrack[ScheduleAddTrack:in] 

[AddTrack:out=>(objects[in.getSensor()];)] 

{ // node:addTrack[AddTrack:in][AddTrack:out=>(in.getSensor0] 

ulong track = in.getTrack(); // Get the track index 

ulong sensor = in.getSensor(); // Get the sensor index 

bool ir = inRange(sensor, track) active[sensor] s& active[track]; 
bool im = tracks[track].find(sensor) == tracks[track].end(); 
if (ir && im) // If in range S not alreay known to sensor 

{ 

out.setMotion(motion[track]); // Set track motion paramters 

out.set(track, motion[track].iff()); // Set identifier info 

tracks[track].insert(sensor); // Register the sensor 

} // if (inRange(sensor, track) s& ... ) 

else out.setTX(false); // Don't send the message 

} // node:addTrack[AddTrack:in][AddTrack:out=>(in.getSensor0] 

node:loseTrack[ScheduleLoseTrack:in] 

[LoseTrack:out=>(objects[in.getSensor()];)] 

( // node:loseTrack[LoseTrack:in][LoseTrack:out=>(in.getSensor()] 

ulong track = in.getTrack(); // Get the track index 

ulong sensor = in.getSensor(); // Get the sensor index 

bool ir = inRange(sensor, track) &S active[sensor] s& active[track]; 
bool im = tracks[track].find(sensor) == tracks[track].end(); 
if (!ir && !im) // If out of range & known to sensor 

{ 

out.set(track, motion[track].iff()); // Format message payload 

tracks[track].erase(sensor); // Remove sensor from tracker list 

} // if (inRange(sensor, track) &s ... ) 

else out.setTX(false); // Don't send the message 

} // node:loseTrack[LoseTrack:in][LoseTrack:out=>(in.getSensor()] 

node:explosion[Explosion:in] 

[Impact:out[],Hit:h=>(in.getSource() ;) ] 

{ // node:explosion[Explosion:in][Impact:out[] , Hit:h] 

for (ulong i=0; ikmotion.size(); ++i) // Loop over all of the objects 

{ 

spt::vertex diff = in.get()-motion[i].Ip(getTime()); 

if (dot(diff, diff)<9.0) // If they were within 3m of impact 

{ 

out.push_back(me); // Add message to inform the target it was hit 

out.back().addDest(objects[i]); // Add object as a destination 

h.add(i); // Set the track index of object hit 

} // if (dot(diff, diff)<9.0) 

} // for (ulong i=0; i<motion.size(); ++i) 

} // node:explosion[Explosion:in][Impact:out[], Hit:h] 


node:destroyed[Destroyed:in] 
[LoseTrack:out] 


// Something was destroyed 
// A bunch of track losses 


341 






} 

} 


{ // node:destroyed[Destroyed:in][LoseTrack:out[]] 

if (active[in.index]) // If the object is already inactive 

{ 

active[in.index] = false; // Object can no longer sense 

out.set(in.index, motion[in.index].iff()); // Set the track index 

std::set<ulong>::iterator i; // Used to loop over sensors of track 
for (i=tracks[in.index].begin 0; i!=tracks[in.index].end(); ++i) 

out.addDest(objects[*i]); // Inform the sensors 

tracks[in.index].clear 0; // Delete the set of tracks 

} 

else out.setTX(false); // Don't transmit output message 

} // node:destroyed[Destroyed:in][LoseTrack:out[]] 

// mode:Default 
// process:Environment 


C.1.13. Explosion.msg 

(import message [SetValue] ] 

(import spt [sptDefs] } 

{ 

message:Explosion(SetValue) 

( // message:Explosion 

spt::vertex:pos(0.0, 3); // Initial position of the munition 

method:set(public; void; spt::vertex:p;) ( pos=p; } 
method:get(public; spt:;vertex;) { return pos; } 

} // message:Explosion 


C.1.14. Fire.msg 

[import spt [sptDefs] } 


message:Fire 
[ 

double:muzzle_velocity; 
double:azimuth; 
double:elevation; 
spt::vertex:pos(0.0, 3); 
spt::vertex:vel(0.0, 3); 


// message:Fire 
// Muzzle velocity of the munition 
// Azimuth of the projectile motion (in radians) 
// Elevation of the projectile motion (in radians) 
// Initial position of the munition 
// Velocity vector of firing platform 


} 


method:set(public; void; double:mv; 

double:a; 
double:e; 
spt::vertex:p; 
spt::vertex:v;) 
[ // method:set(public; void; 

muzzle_velocity = mv; 
azimuth = a; 
elevation = e; 
pos = p; 
vel = v; 

} // method:set(public; void; 


// Muzzle velocity 
// Azimuth 
// Elevation 
// Position 
// Velocity 

double; double; double; doubled;) 

// Save the muzzle velocity 
// Save the azimuth 
// Save the elevation 
II Save the position 
// Save the velocity 
double; double; double; doubled;) 

// message:Fire 


C.1.15. FormationMove.msg 

(message:FormationMove;} 

C.1.16. Ground.proc 

[import process [NewtonianMotion] } 
(process:Ground(NewtonianMotion);} 


342 






C.1.17. Hit.msg 

{import std {<vector>} } 


message:Hit 
{ 

ulong:track [ ]; 
method:add(public; 
method:get(public; 

} 

} 


// message:Hit 

// Track number of object, -1 if nothing was hit 
void; ulong:t;) { track.push_back(t); } // Add the track 
std::vector<ulong>;) { return track; ) // Report tracks 

// message:Hit 


C.1.18. HoldPosition.msg 

{message:HoldPosition;} 

C.1.19. Impact.msg 

{message:Impact;} 

C.1.20. LoseTrack.msg 

{import message {TrackEvent} } 

{message:LoseTrack(TrackEvent);) 

C.1.21. MoveFormation.msg 

{import message {AdjustFormation} ) 
{message:MoveFormation(AdjustFormation);) 


C.1.22. MoveTo.msg 

{import spt {sptDefs} } 


message:MoveTo 

{ // message:MoveTo 

spt::vertex:pos(0.0, 3); 
spt::vertex:ori(0.0, 3); 


method:fixOri(protected; spt::vertex; spt::vertex:o;) 

{ // method:fixOri(protected; spt::vertex; spt::vertex:o;) 

for (ulong i=0; i<3; ++i) // Loop over the axes 


o[i] = fmod(o[i], PI*2.0); 
if (o[i]<0.0) o[il+=PI*2.0; 


} 

return o; 


// Adjust angular pos 
// Make certain it's > 0 
// for {i=0; i<3; ++i) 
// Return the adjusted orientation array 
// method:fixOri(protected; spt::vertex; spt::vertex:o;) 


method:setPos(public; 
method:setPos(public; 
{ pos[0]=x; pos[l]=y; 
method:setPos(public; 
{ pos(01=x; pos[l]=y; 


void; spt::vertex:v;) { pos = v; ) 
void; double:x; double:y; double:z;) 
pos(2]=z; } 

void; double:x; double:y;) 
pos[21=0.0; ) 


method:setOri(public; 
method:setOri(public; 
{ ori[0]=x; ori[l]=y; 


void; spt::vertex:v;) { ori = fixOri(v); ) 
void; double:x; double:y; double:z;) 
ori[2]=z; ori=fixOri(ori); } 


method:set(public; void; spt;:vertex:p; spt::vertex:o;) 
{ pos=p; ori=fixOri(o); } 


method:getPos(public; spt::vertex;) { return pos; } 
method:getOri(public; spt::vertex;) { return ori; } 

} // message:MoveTo 

} 


343 






C.1.23. MovementComplete.msg 

{messageiMovementComplete; ) 

C.1.24. Munition.proc 

{import process {NewtonianMotion} ) 

{import message {Fire, Impact, SetNewtonianMotion, Explosion, 

StartSimulation, SetEnvironment, Hit, AddShapeSD} } 
{import {<math.h>} } 


process:Munition(NewtonianMotion) 
{ 

process:environment; 
processiparent; 
process:grNode; 


// process:Munition(NewtonianMotion) 
II Environment in which the munition exists 

// Parent process 
// Graphics node for display purposes 


method:getImpactTime(public; double; double:v; doublera;) 

{ // method:getImpactTime(public; double; double:v; double:a;) 

return -2.0*v/a; II Return the value to the calling routine 

} // method;getImpactTime(public; double; double:v; double:a;) 


mode;Default 


node:start[StartSimulation:in][] 
{ 

fired.setActive(false); 


// mode:Default 

// node:start[StartSimulation:in][] 
// Turn the "fired" mode off 
// node:start[StartSimulation:in][] 
// mode:Default 


mode:waiting 

{ // mode:waiting 

node:setEnvironment[SetEnvironment:in][] 

{ // node:setEnvironment[SetEnvironment:in][] 

environment = in.getEnvironment(); // Environment in which this exists 

grNode = in.getNode(); // Graphics node displaying the munition 

parent = in.getSource(); // Save the parent process handle 

} // node:setEnvironment[SetEnvironment:in][] 

node:fire[Fire:f] // Request to fire the munition 

[Impact:imp=>(me;)/ // Schedule the impact 

AddShape3D:as=>(grNode;), // Start rendering the munition 

SetNewtonianMotion:out[]] // Set motion parameters 

{ // node:fire[Fire:f][Impact,AddShapeSDSetNewtonianMotion] 

spt::vertex v(0.0, 3), a(0.0, 3), p(f.pos); 

v[0] = f.muzzle_velocity*cos(f.elevation)*cos(f.azimuth)+f.vel[0]; 

V[1] = f.muzzle_velocity*cos(f.elevation)*sin(f.azimuth)+f.vel[1]; 
v[2] = f.muzzle_velocity*sin(f.elevation)+f.vel[2]; 
a[0] = a[l] = 0.0; 

a[2] = -0.9; // Acceleration due to gravity 

imp.setTime(getTime () tgetlmpactTime(v[2],-9. 8 )); II Impact time 

nm.setLM(p, v, a, getTimeO, imp.getTime()); // Set motion parameters 


notify(out); 

waiting.setActive(false); 
fired.setActive(true) ; 


// Inform interested parties about this 
// We're no longer waiting 
// We are now firing 


as.add(me); // Add this process as a subordinate to the rendering node 
// node:fire[Fire:f][Impact,AddShapeSDSetNewtonianMotion] 

// mode:waiting 


mode:fired 
{ 

node:impact[Impact:in] 

[Explosion:explode[], 
SetNewtonianMotion:out[]] 


// mode:fired 
// We impacted the environment 
II An explosion occurred 
// Stop motion 


double t = getTimeO; 
spt::vertex p(nm.lp(t)); 


// node:impact[Impact:in][Explosion:explode] 
// Get the time of the impact 
II Get current position 


344 







nm.lv(0.0, 0.0, 0.0, t); 
nm.la(0.0, 0.0, 0.0, t); 
notify(out); 


// Set to stop at current position 
// Set to stop at current position 
// Inform interested parties about this 


std::map<process, gvm::object_index>::iterator i; // For loop index 

for {i=views.begin 0; i!=views.end(); ++i) // Loop over index map 


explode.push_back(me); 
explode.back().addDest(i->first); 
explode.back().index = i->second; 
explode.back().set(p); 


// Allocate a new message 
II Add this view as a destination 
// Specify the index 
// Specify the position 


} II for (i=views.begin 0; i!=views.end{); ++i) 

explode.push_back(me); // Allocate a new message 

explode.back().addDest(environment); // Add environ as dest 

explode.back().set(p); // Specify the position 

} // node:impact[Impact:in][Explosion:explode] 


node:hit[Hit:in][Hit:out=>(parent;)] 

{ n 

out.track = in.track; 
fired.setActive(false); 


)] // Notify parent what we hit 

// node:hit[Hit:in][Hit:out=>(parent;)] 
// Set the target data 
// No more adventures 
// node:hit[Hit:in][Hit:out=>(parent;)] 

// mode:fired 
// process:Munition(NewtonianMotion) 


C.1.25. NewtonianMotion.proc 


[import process [ShapeSD] } 

[import message [SetNewtonianMotion, SetLinearPosition, SetLinearVelocity, 

SetLinearAcceleration, SetAngularPosition, SetAngularVelocity, 
SetAngularAcceleration, Addview] } 

[import [<math.h>} } 

[import std [<valarray>} } 

[import spt [sptNewtonianMotion, sptLinearMotion, sptAngularMotion) ) 


process:NewtonianMotion(ShapeSD) 

[ 

spt::NewtonianMotion:nm; 


// process:NewtonianMotion(ShapeSD) 
// Parameters of Newtonian Motion 


method:notify (public; void; std::vector<SetNewtonianMotion>S(: out;) 

[ // method:notify(public; void; std::vector<SetNewtonianMotion>s:out;) 

std::map<process, gvm::object_index>:;iterator i; // For loop index 

for (i=views.begin 0; i!=views.end(); ++i) II Loop over index map 


out.push_back(me); 
out.back{).addDest{i->first); 
out.back().index = i->second; 
out.back().set(nm); 


II Allocate a new message 
// Add this view as a destination 
// Specify the index 
// Specify the Linear Motion paramters 


} // for (i=views.begin0; i!=views.end(); ++i) 

} // method:notify(public; void; std::vector<SetNewtonianMotion>&:out;) 

mode:Default 

[ // mode:Default 

node:addView[AddView:in] 

[SetNewtonianMotion:snm=>(in.getSource();)] 

[ // Node:addview[Addview:in][...] 

snm.set(nm); // Set the motion parameters 

snm.index = in.index; // Get the index 

} // Node:addview[Addview:in][...] 

node : setLinear Posit ion [SetLinearPosition: in] 

[SetNewtonianMotion:out[]] 

[ // node:SetLinearPosition[SetLinearPosition:in][ ... ] 

nm.Ip(in.get(), getTimeO); // Copy the linear position contents 

notify(out); // Notify the views, et al 

} // node:SetLinearPosition[SetLinearPosition:in)[ ... ] 


node:SetLinearVelocity[SetLinearVelocity:in] 


345 








[SetNewtonianMotion:out[]] 

{ // node:setLinearVelocity[SetLinearVelocity:in][ ... ] 

nm.Iv(in.get(), getTime()); // Copy the linear velocity contents 

notify{out); // Notify the views, et al 

} // node:setLinearVelocity[SetLinearVelocity:in][ ... ] 

node:setLinearAcceleration[SetLinearAcceleration:in] 

[SetNewtonianMotion:out[]] 

{ // node:setLinearAcceleration[SetLinearAcceleration:in][ ... ] 

nm.la(in.get{), getTimeO); // Copy the linear acceleration contents 
notify(out); // Notify the views, et al 

} // node:setLinearAcceleration[SetLinearAcceleration:in][ ... ] 

node:setAngularPosition[SetAngularPosition:in] 

[SetNewtonianMotion:out[]] 

{ // node:setAngularPosition[SetAngularPosition:in][ ... ] 

nrti. ap (in. get () , getTimeO); II Copy the angular position contents 

notify(out); // Notify the views, et al 

} // node:setAngularPosition[SetAngularPosition:in][ ... ] 

node:setAngularVelocity[SetAngularVelocity:in] 

[SetNewtonianMotion:out[]] 

( // node:setAngularVelocity[SetAngularVelocity:in][ ... ] 

nm.av(in.get(), getTimeO); // Copy the angular velocity contents 

notify(out); // Notify the views, et al 

} // node:setAngularVelocity[SetAngularVelocity:in][ ... ] 

node:setAngularAcceleration[SetAngularAcceleration:in] 

[SetNewtonianMotion:out[]] 

{ // node:setAngularAcceleration[SetAngularAcceleration:in][ ... ] 

nm.aa(in.get 0, getTimeO); // Copy the angular acceleration contents 
notify(out); // Notify the views, et al 

} // node:setAngularAcceleration[SetAngularAcceleration:in][ ... ] 


node:SetNewtonianMotion[SetNewtonianMotion:in] 

[SetNewtonianMotion:out[]] 

{ // node:SetNewtonianMotion[SetNewtonianMotion:in][ ... ] 

nm = in.get 0; // Copy the newtonian motion parameters 

notify(out); // Generate notification messages 

} // node:setAngularAcceleration[SetAngularAcceleration:in][ ... ] 

// mode:Default 
// process:NewtonianMotion(ShapeSD) 


C.1.26. Platoon.proc 

[import process [Tank] } 

(import message [AddShapeSD, SetFormation, StartSimulation, Destroyed, 
SetLinearPosition, SetAngularPosition, UnitSetup, 
MoveFormation, MovementComplete, MoveTo, UnitSetup, 
AddTrack, ChangeTrack, LoseTrack, SetNewtonianMotion) } 
{import spt {sptEnvironmentObject} } 

[import std {<valarray>} } 

[import [<math.h>} } 


process:Platoon 
[ 

Tank:tanks [5]; 
bool:active[5](true); 
bool:complete[5](true); 
process:environment; 
process:parent; 
spt::vertex:pos(3); 
spt::vertex:ori(3) ; 
spt::vertex:destPos(3) ; 
spt::vertex:destOri(3) ; 
double:destLeft; 
double:destRight; 
double:startTime(-1.0) ; 
double:left(0.0) ; 


// process:Platoon 
// The tanks in the platoon 
// Which of the tanks are still alive 
// Phase complete flags for each tank 
// Environment in which the simulation occurs 
// Parent unit (company) 
// Current position 
// Current orientation 
II Destination position 
II Destination orientation 
// Destination line of left flank 
// Destination line of right flank 
II Time we start moving 
// Left flank line 


346 






double:right(0.0); 

ulong: traclcCount [ ] ; 

spt::NewtonianMotion:tracks[]; 

ulong:force(UNKNOWN); 


// Right flank line 
II Number of members tracking track 
// List of all known tracks 
// Which force is this platoon a member of 


method:init(public; void;) 

{ 

turn_to_dest.setActive(false) ; 
move_to_dest.setActive(false) ; 
turn_to_heading.setActive(false); 

} 


// methodrinit(public; void;) 
// Turning to the destination 
// We're not moving to destination yet 
II Not turning to final heading yet 
// method:init(public; void;) 


method:phaseDone(public; bool; process:p;) 

{ II method:phaseDone(public; bool; processrp;) 

bool done=true; // Are we done yet? 

for (uint i=0; i<tanks.size(); ++i) // Loop over the tanks 


if (p==tanks[i]) complete[i]=true; // Is this the one? 

done &= (complete[i] || !active[i]); // Are we done yet 

} // for (uint i=0; i<tanks.size(); ++i) 

return done; // Return the value to the calling routine 

// method;phaseDone(public; bool; process;p;) 


method:getOrientation(protected; spt::vertex; spt::vertex:dO;) // Dest ori 

{ // method:getOrientation 

double theta = atan2(dO[l], dO[0]); 
spt::vertex rv(0.0, 3); 


// Get the angle 
// Resulting orientation 


rv[2) = theta; 
return rv; 


} 


method:getPosition(protected; spt::vertex 
spt::vertex:dP; 
spt::vertex:dO; 
double:1; 
double:r; 
ulong:i;) 


// Direction to look 
// Return the orientation 
// method:getOrientation 


{ 


double c = ((double) i)-((double) 
double h = norm(dO); 
double theta = atan2(dO[l], dO[0]); 
double act=0; 
double ast=0; 
spt::vertex rv(0.0, 3); 


// Destination position 
// Destination orientation 
// Formation left leg 
// Formation right leg 
// Index 
// method:getPosition 
(tanks.size 0-1))/2.0; // Rel. location 
// Get orientation magnitude 
// Get the angle 
// Abreast factor in x 
// Abreast factor in y 
// Resize positions 


double leg = 0.5*PI+(c>0 ? -1 : r); 
act=(c==0 ? 0.0 : h*cos(theta+leg)) ; 
ast={c==0 ? 0.0 : h*sin(theta+leg)); 

rv[0]=dP[0]+c*act ; 
rv[1]=dP[1]+c*ast ; 


// Direction 
// V leg factor in x 
// V leg factor in y 

II Set the position 


return rv; 

} 


// Return the position to the calling routine 

II method:getPosition 


mode:startup 

{ II mode:startup 

node:unitSetup[OnitSetup:in] // Setting the environment 

[OnitSetup:se=>(tanks;), // Establish environment 

AddShape3D;out=>(in.getNode{);)1 // Add tanks to node 

{ // node:setEnvironment[UnitSetup:in][SetEnvironment:se...] 

environment = se.environment = in.environment; II Set the environment 

force = in.getForce(); II This is our force 

parent = in.getSource(); // Set the parent process 

se.set(force,in.getRadius(),environment,in.getNode0); // Setup tanks 

for (uint i=0; i<tanks.size{); ++i) // Loop over all of the tanks 

out.add(tanks[i]); // Add each one to the scene 

startup.setActive(false); // 

} // node:setEnvironment[UnitSetup;in][SetEnvironment:se...] 


347 






} 


mode:run 
{ 

node;setFormation[SetFormation:in] // Orders from above 

[SetLinearPosition:sip[5]=>(tanks[@];), // Set tank pos 

SetAngularPosition:sap[5]=>(tanks[@];)] // Orient tanks 
{ // node:setFormation[SetFormation:in][ ... ] 

left = in.getLeft(); // Get formation left flank 

right = in.getRight(); // Get formation right flank 

pos = in.getPos(); // This is now the current position 

ori = in.getOri(); // This is now the current orientation 


for (uint i=0; i<tanks.size(); ++i) 


// Loop over the tanks 


sip[i].set(getPosition(pos, ori, left, right, i)); // Get position 

sap[i].set(getOrientation(ori)); // Get the orientation 

} // for (uint i=0; i<tanks.size(); ++i) 

} // node:setFormation[SetFormation:in][ ... ] 

node:moveFormation[MoveFormation:in] // Request to move the formation 

[MoveTo: out [5] => (tanks [(?];) ] // Move the tanks 

{ // nodermoveFormation[MoveFormation:in][MoveTo:out[5]] 

destLeft = in.getLeft(); // Destination formation left flank 

destRight = in.getRight(); // Destination formation left flank 

destOri = in.getOri(); // Destination orientation 

destPos = in.getPos(); // Destination position 

if (startTime>0) pos += 40.0*(getTime()-startTime)*ori; // If moving 
spt::vertex rp = destPos-pos; // Relative position 

StartTime = -1; // We are not moving any more 

double theta = atan2(rp[l], rp[0]); // Relative bearing to destination 
double dist = norm(ori); // Distance between adjacent units 


ori[0] = dist*cos(theta); 
ori[l] = dist*sin(theta); 
ori[2] = 0.0; 


II Modify the orientation 


for (uint i=0; i<tanks.size(); ++i) 


// Loop over the tanks 


out[i].setPos(getPosition(pos, ori, left, right, i)); 
out[i].setOri(getOrientation(ori)); 

} // for (uint i=0; i<tanks.size(); ++i) 

turn_to_dest.setActive(true); // Turning to the destination 

move_to_dest.setActive(false); // We're not moving to destination yet 

turn_to_heading.setActive(false); // Not turning to final heading yet 

for (uint i=0; i<complete.size(); ++i) complete[i]=false; 

} // node:moveFormation[MoveFormation:in] [MoveToiout[5] 1 

node:setNewtonianMotion[SetNewtonianMotion:in] 

[SetNewtonianMotion:out=>(parent;)] 

{ // node:setNewtonianMotion]SetNewtonianMotion][SetNewtonianMotion] 

out.set(in.nm); // Set the newtonian motion paramters of output message 
out.index = in.index; II Set the index value, too 


if (tracks.size() <= in.index) II If we need to increase storage 

{ 

tracks.resize(in.index+1); // Resize the track array 

trackCount.resize(in.index+1, 0); // Resize the counter 

} // if (tracks.size() <= in.index) 

tracks[in.index] = in.nm; // Save the motion paramter 

// node:setNewtonianMotion]SetNewtonianMotion][SetNewtonianMotion] 


node:addTrack[AddTrackiin] [AddTrack:out=>(parent;) ] 

{ // node:addTrack[AddTrack:in][AddTrack:out] 

if (tracks.size() <= in.getTrack()) // If we need to increase storage 


tracks.resize(in.getTrack()+1) ; 
trackCount.resize(in.getTrack0+1, 0); 


// Resize the track array 
// Resize the counter 


348 






// if (tracks.size() <= in.getTrack()) 


if (trackCount[in.getTrack()]++>0) 
out.setTX(false); 
else 


// If this is not a new track 
// Don't bother informing parent 
// If the track is new 


tracks[in.getTrack0 ] = in.getMotion() ; // Save the motion paramter 

out. setMotion(in.getMotion()) ; // Notify as to object motion 

out.set(in.getTrack0, in.getForce()); // Provide force tracking 

} // else from if (trackCount[in.getTrack()]>1) 

// node:addTrack[AddTrack:in][AddTrackiout] 


node:changeTrack[ChangeTrack:in][ChangeTrack:out=>(parent;)] 

{ II node:changeTrack[ChangeTrack:in][ChangeTrackiout] 

ulong t = in.getTrack0; // Get the track index 


if (in.getMotion().getStartTime() 
out.setTX(false) ; 
else 
{ 

tracks[t] = in.getMotion(); 
out.setMotion(in.getMotion ()); 
out.set(t,in.getForce()); 

} // else from if 


=tracks[t].getStartTime0) // Known? 

//We already informed the parent 
// If this is a new update 


etMotionO; // Save the motion paramter 

.getMotion0); // Notify as to object motion 

ForceO); // Provide force tracking 

// else from if (in.getMotion().getStartTime == ... ) 
// node:changeTrack(ChangeTrack:in][ChangeTrack:out] 


node:loseTrack[LoseTrack:in][LoseTrack:out=>(parent;)] 

{ // node:loseTrack[LoseTrack:in][LoseTrack:out] 

if (—trackCount[in.getTrack()]>0) // If there are tracks remaining 

out.setTX(false); // Don't notify parent that track is lost 

else // If this was the last one observing the track 

out.set(in.getTrack0,in.getForce0); // Provide force info 

} // node:loseTrack[LoseTrack:in][LoseTrackiout] 

node;destroyed[Destroyed:in] 

[Destroyed:out[]=>(parent;)] 

{ // node:destroyed[Destroyed:in][Destroyed:out[]] 

bool a = false; // Accumulator for testing state of platoon 


out.push_back(me); 

out.back 0.index = in.index; 


// Inform the company of the tank loss 
// Tell company which one it was 


for (ulong i=0; i<tanks.size(); ++i) 


// Loop over tanks in platoon 


if (tanks[i]==in.getSource0) // If the ith tank just got destroyed 


active[i]=false; 


a = a I I active[i]; 


// This is no longer part of the platoon 

// Accumulate to see if we're still alive 
// for (i=0; i<tanks.size(); ++i) 


if (!a) 

{ 

out.push_back(me); 
run.setActive(false) 


// If the platoon is destroyed 


out.push_back(me); // Inform company if platoon destroyed 

run.setActive(false); // This platoon is no longer active 

turn_to_dest.setActive(false); // Turning to the destination 

move_to_dest.setActive(false); // We're not moving to destination yet 
turn_to_heading.setActive(false); // Not turning to final heading yet 

} 

} // node:destroyed[Destroyed:in][Destroyed:out[]] 

} // mode:startup 

mode:turn_to_dest 

{ // mode:turn_to_dest 

node:movementComplete[MovementComplete:in] // One is done 

[MoveTo:out[5]=>(tanks[@];)] // Next movement phase 

{ // node:movementComplete[MovementComplete:in][MoveTo[5]:out] 

if (phaseDone(in.getSource0)) // If we can go to the next phase 

{ 

for (uint i=0; i<out.size(); ++i) // Loop over the output messages 


349 







out[i].setPos(getPosition(destPos, ori, destLeft, destRight, i)) ; 
out[i].setOri(getOrientation(ori)) ; 

complete[i]=false; // Not done with next pha: 


complete[i]=false; // Not done with next phase 

} // for (int i=0; i<out.size(); ++i) 

turn_to_dest.setActive(false); // No longer turning to destination 

move_to_dest.setActive(true); // Start moving to destination 

turn_to_heading.setActive(false); // Not turning to final heading 

startTime = getTimeO; // Get the current time 

} // if (phaseDone(in.getSource()) 

else II if we're not yet done turing to final heading 

for (uint i=0; i<out.size(); ++i) out[i].setTX(false) ; // Don't send 

// node:movementComplete[MovementComplete:in][MoveTo[5]=>(tanks[@];)] 

// mode:turn to dest 


mode:move_to_dest 

{ // mode:move_to_dest 

node:movementComplete[MovementComplete:in] // One is done 

[MoveTo:out[5]=>(tanks[@];)] // Next movement phase 

{ // node:movementComplete[MovementComplete:in][MoveTo[5]:out] 

if (phaseDone (in. getSource 0) ) // If we can go to the next phase 


pos = destPos; // We’re at our final dest 

StartTime = -1; // Not moving any more 

left = destLeft; // We are now in the final formation 

right = destRight; // We are now in the final formation 

for (uint i=0; i<out.size(); ++i) // Loop over the output messages 

{ 

out[i].setPos(getPosition(pos, destOri, left, right, i)); 

out[i].setOri(getOrientation(destOri)) ; 

complete[i]=false; // Not done with i 


complete[i]=false; // Not done with next phase 

) // for (int i=0; i<out.size(); ++i) 

turn_to_dest.setActive(false); // No longer turning to destination 

move_to_dest.setActive(false); // Not moving to destination 

turn_to_heading.setActive(true); // Start turning to final heading 

} // if (phaseDone(in.getSource0) 

else // if we're not yet done turing to final heading 

for (uint i=0; i<out.size(); ++i) out[i].setTX(false); // Don't send 

} // node;movementComplete[MovementComplete:in][MoveTo[5]=>(tanks[@];)] 

// mode:move to dest 


mode:turn_to_heading 

( // mode:turn_to_heading 

node:movementComplete[MovementComplete:in] // One is done 

[MovementComplete:out=>(parent;)] // All done 

{ // node:movementComplete[MovementComplete:in][MovementComplete:out] 

if (phaseDone(in.getSource0)) // If we can go to the next phase 

{ 

ori = destOri; // We're at our destination orientation 

StartTime = -1.0; // Get the current time 

turn_to_dest.setActive(false); // No longer turning to destination 

move_to_dest.setActive(false); // Not moving to destination 

turn_to_heading.setActive(false); // Not turning to final heading 

for (uint i=0; ikcomplete.size(); ++i) complete[i]=false; 

} // if (phaseDone(in.getSource0) 

else // if we're not yet done turing to final heading 

out.setTX(false); // Don't send the final confirmation 

} // node:movementComplete[MovementComplete:in][MoveTo[5]=>(tanks[@];)] 

} II mode:turn_to_heading 

II process: Platoon 


C.1.27. RedCompany.proc 

(import process (Company) } 

(import message (StartSimulation, SetColor, SetFormation, SetLinearPosition, 
MoveFormation, UnitSetup) } 

(import spt (sptEnvironmentObject) } 

(import gvm (gvmTank) } 

( 

process rRedCompany(Company) 


350 






method:init(public; void;) 

( 

Company::init0; // 

view->setTitle{"Red Tactical View") 
force = RED; 

view->setPosition(1032,27) ; 
view->setSize(563, 516); 


// process:RedCorapany(Company) 

// method:init(public; void;) 
Initialize the parent construct first 

// Red view 

// This has force designator "RED" 
// Set the position of the window 
// Set the size 
// method;init(public; void;) 


mode:startup 

{ // mode:startup 

node:start[StartSimulation:in] // Upon startup 

[SetColor:sc=>(platoons; cp;), // Set the color of the objects 
SetFormation:sf[]=>(platoons[@];), // Set formation params 

SetLinearPosition:slp=>(cp;), // Command post position 

MoveFormation:mf[]=>(platoons[@];):(10.0+0*5.0)] 

{ // node:start[StartSimulation:in][ ... ] 

sc.set(1.0, 0.0, 0.0, 1.0); II Set the color of subordinates to red 

double r = sqrt (5000.0) ; // Range between tanlcs 

double d = sqrt(2000000.0); II Range between destinations 

for (uint i=0; i<platoons.size(); ++i) // Loop over the platoons 

{ 


double X = -8500-r*{(double) 

sf .push_bac)c(me) ; 

sf[i].setPos(x,X); 

sf[i].setOri(r,r); 

sf[i].setForm(V_FORMATION); 


i) ; // Location 

// Add a new message 
// Set the tank position 
// Set the platoon orientation & spacing 
// Use V formation 


} 


x=d*(((double) i)-((double) (platoons.size ()-1))/2.0); // Dest loc 

mf.push_back(me); // Add a new message 

mf [i].setPos(X, -x); // Set the destination 

mf [i].setOri(r,r); // Set the orientation 

mf[i].setForm(V_FORMATION); // Use the V formation 

} // for (uint i=0; Kplatoons. size () ; ++i) 

sip.set(-9500.0, -9600.0, 0.0); // Set the position of the command post 

// node:start[StartSimulation:in][ ... ] 

// mode:startup 
// process:RedCompany(Company) 


C.1.28. RegisterEnvironmentObject.msg 

[import spt {sptNewtonianMotion, sptLinearMotion, sptAngularMotion] } 


message:RegisterEnvironmentObject 

{ // message:RegisterEnvironmentObject 

double:r; // Effective radius of the sensor 

ulong:f; // Which force does track belong to NEUTRAL (0), RED (1), BLUE (2) 
spt::NewtonianMotion:motion; // Motion paramters 

method:setRadius(public; void; double:R;) [ r=R; } // Set the sensor radius 
method:setForce(public; void; ulong:F;) { f = F; } // Set the force value 

method:getMotion(public; spt::NewtonianMotion;) [ return motion; } 

method:getForce(public; ulong;) [ return f; } // Return the force index 

method:getRadius(public; double;) { return r; } // Return the sensor radius 
method:setMotion(public; void; spt::NewtonianMotionS:m;) [ motion=m; } 

} // message:RegisterEnvironmentObject 

} 

C.1.29. ScheduleAddTrack.msg 

{import message {ScheduleTrackEvent) } 

{message:ScheduleAddTrack(ScheduleTrackEvent);} 


351 






C.1.30. ScheduleLoseTrack.msg 

{import message {ScheduleTrackEvent} } 

(message:ScheduleLoseTrack(ScheduleTrackEvent);} 


C.1.31. ScheduleTrackEvent.msg 

{ 

message:ScheduleTrackEvent 
{ 

ulong:sensor; 
ulong:track; 

method:set(public; void; ulong:s; ulongrt;) { sensor=s; track=t; } 
method:getSensor(public; ulong;) { return sensor; } 
method:getTrack(public; ulong;) { return track; } 

} 


C.1.32. SensorTrack.proc 


{import process (NewtonianMotion) } 

{import message {AddTrack, ChangeTrack, LoseTrack, AddEnvironment, 

SetEnvironment, SetNewtonianMotion, Impact, Destroyed) 
{import spt {sptEnvironmentObject, sptNewtonianMotion) ) 

{import std {<vector>} } 


{ 


process:SensorTrack(NewtonianMotion) 

{ 

ulong:envindex((ulong) (-1)); 

process:environment; 

process:parent; 

double:radius(2000.0); 

ulong;force(NEUTRAL); // 

spt::NewtonianMotion:tracks[]; 

std::set<ulong>:active; 


// process:SensorTrack(NewtonianMotion) 
// Index within the environment 
// Environment process 
// Parent object to report back to 
// Sensor Radius 
Initially neutral, until we know better 
// Collection of known tracks 
// Collection of active tracks 


method:isActive(public; bool; ulong:i;) 

{ return active.find(i)!=active.end(); 1 


// Is track i active? 


method:notify(public; void; std:;vector<SetNewtonianMotion>&:out;) 


{ // method;notify(public; void; 

NewtonianMotion::notify(out); 
if (envindex != ((ulong) -1)) // 

{ 

out.push_back(me); 
out.back().addDest(environment); 
out.back().addDest(parent); 
out.back().index = envindex; 
out.back().set(nm); 


} 


// method:notify(public; void; 


std::vector<SetNewtonianMotion>&:out;) 

// Call the parent version 
If we have registered with environment 

// Allocate a new message 
// Add environment as dest 
// Add environment as dest 
// Specify the index 
// Specify the Linear Motion paramters 
// if (envindex != ((ulong) -1)) 
std::vector<SetNewtonianMotion>&:out; ) 


mode:Default 

{ // mode:Default 

node:addEnvironment[AddEnvironment:in] 

[SetNewtonianMotion:out=>(parent;)] 

{ // Node:addEnvironment[AddEnvironment:in][...] 

envindex = in.index; // Save the environment index 

out.index = envindex; // Notify parent of new index 

out.set(nm); // Set the motion parameters 

) // Node:addEnvironment[AddEnvironment:in] [... ] 


node:setEnvironment[SetEnvironment:in][] 

{ // node:setEnvironment[SetEnvironment:in][] 

environment = in.environment; // Environment in which this exists 

parent = in.getSource(); // Save the parent process handle 

} // node:setEnvironment[SetEnvironment:in][] 


node:addTrack[AddTrack:in][AddTrack:out=>(parent;)] 

{ // node:addTrack[AddTrack:in][AddTrack:out] 


352 






if (tracks.size() <= in.getTrackO) 
tracks.resize(in.getTrack{)+1) ; 


// If tracks isn't big enough 
// Resize the track list 


tracks [in.getTrack 0 1 = in.getMotionO ; // Save the motion paramter 

active.insert(in.getTrack()); // Add an active element 

out.setMotion(in.getMotion()); // Notify as to object motion 

out.set(in.getTrack0, in.getForce()); // Provide force tracking 

} II node:addTrack[AddTrack:in][AddTrack:out] 

node:changeTrack[ChangeTrack:in][ChangeTrack:out=>(parent;)] 

{ II node:changeTrack[ChangeTrack:in][ChangeTrack:out] 

tracks[in.getTrack0] = in.getMotionO; // Save the motion paramter 

out. setMotion (in.getMotion 0) ; // Notify as to object motion 

out.set(in.getTrack(), in.getForce()); // Provide force tracking 

} // node:changeTrack[ChangeTrack:in][ChangeTrack;out] 

node:loseTrack[LoseTrack:in][LoseTrack;out=>(parent;)] 

{ II node:loseTrack[LoseTrack:in][LoseTrack:out] 

active . erase (in. getTrack 0) ; // Track is no longer active 

out.set(in.getTrack{),in.getForce0); // Provide force information 

} // node:loseTrack[LoseTrack;in][LoseTrack:out] 

node:impact[Impact:in] // We've been hit 

[Destroyed:out[], II Notify processes of our destruction 

LoseTrack:lt[]=>(parent;), // Lost all the tracks 

SetNewtonianMotion:snm[]] // Notify of new newtonian motion 

{ // node:impact[Impact:in][Destroyed:out[]] 

nm.la(0.0, 0.0, 0.0, getTimeO); // Stop linear acceleration 

nm.lv(0.0, 0.0, 0.0, getTimeO); // Stop linear motion 

nm.aa(0.0, 0.0, 0.0, getTimeO); // Stop angular acceleration 

nm.av(0.0, 0.0, 0.0, getTimeO); // Stop angular motion 

notify(snm); // Notify views/environments about new newtonian motion 


notify(snm); // Notify views/environments about new newtonian motion 

std::map<process, gvm::object_index>::iterator i; // For loop index 

for (i=views.begin0; i!=views.end(); ++i) // Loop over index map 

{ 

out.push_back(me); // Allocate a new message 

out.backO.addDest(i->first); // Add this view as a destination 

out.back().index = i->second; // Specify the index 

} // for (i=views.beginO; i!=views.end(); ++i) 

if (envindex != ((ulong) -1)) II If we have registered with environment 


out.push_back(me); 
out.back().addDest(environment); 
out.back().addDest(parent); 
out.back().index = envindex; 


// Allocate a new message 
// Add environment as dest 
// Add environment as dest 
II Specify the index 
// if (envindex != ((ulong) -1)) 


std::set<ulong>::iterator t; // Index for active tracks 

for (t=active.begin 0; t!=active.end(); ++t) II Loop over active tracks 
{ 

lt.push_back(me); // Create a new LoseTrack message 

It.back() .set (*t, (force==RED ? BLUE : RED)); // tell of lost tracks 
} // for (t=active.begin 0; t!=active.end(); ++t) 

// node:impact[Impact:in][Destroyed:out[]] 

II mode:Default 
// process:SensorTrack(NewtonianMotion) 


C.1.33. SetAngularAcceleration.msg 

[import message [SetMotion] } 

[message:SetAngularAcceleration(SetMotion) ; 


C.1.34. SetAngularPosition.msg 

[import message [SetMotion) ) 

[message:SetAngularPosition(SetMotion) ;} 


353 







C.1.35. SetAngularVelocity.msg 

{import message (SetMotion) } 

{message:SetAngularVelocity(SetMotion);) 

C.1.36. SetEnvironment.msg 

{ 

message:SetEnvironment 

{ // message:SetEnvironment 

process:environment; // Reference to the environment process 

process:renderNode; // Root node in the scene graph 

method:SetEnvironment(public; void; process:e;) ( environment=e; } 
method:setNode(public; void; process:n;) { renderNode=n; } 

method:getEnvironment(public; process;) { return environment; } 
method:getNode(public; process;) { return renderNode; } 

} // message:SetEnvironment 


C.1.37. SetFormation.msg 

(import message (AdjustFormation) } 
{message:SetFormation(AdjustFormation);) 


C.1.38. SetLinearAcceleration.msg 

{import message (SetMotion) ) 

(message:SetLinearAcceleration(SetMotion);} 

C.1.39. SetLinearPosition.msg 

(import message (SetMotion) } 

(message:SetLinearPosition(SetMotion);} 


C.1.40. SetLinearVelocity.msg 

(import message (SetMotion) } 

(message:SetLinearVelocity(SetMotion);} 


C.1.41. SetMotion.msg 

(import message (SetValue) } 
(import spt (sptDefs) ) 


message:SetMotion(SetValue) 

{ // message:SetMotion(SetValue) 

double:t(getTime()); // Effective time of the motion paramters 

spt::vertex:v(0.0, 3); // Where the vector is stored 

method:set(public; void; double:x; double:y; double:z;) 

( v[0]=x; v[l]=y; v[2]=z; ) 


) 


method:set(public; 
method:setT(public; 
method:get(public; 
method:get(public; 
method:getT(public; 


void; spt::vertex:V;) { v = V; } 
void; double:T;) { t = T; } 
spt::vertex;) { return v; } 
double; ulong:i;) { return (i<v.size() 
double;) { return t; } 


// Set the time 
// Get value 
v[i] : 0.0); ) 
II Get the time 
II message:SetMotion(SetValue) 


C.1.42. SetNewtonianMotion.msg 

(import message (SetValue) ) 

(import spt {sptNewtonianMotion, sptLinearMotion, sptAngularMotion) ) 

( 

message:SetNewtonianMotion(SetValue) 


354 







spt: :NewtonianMotion:nm; 


// message:SetNewtonianMotion(SetValue) 
// Motion paramters 


method:set(public; void; spt::NewtonianMotion&:n;) { nm=n; } 
method:get(public; spt::NewtonianMotion;) { return nm; } 

} // message;SetLinearVelocity(SetValue) 

} 

C.1.43. SetTankState.msg 

{import message {SetValue} } 


{ 

message:SetTankState(SetValue) 
{ 

double:az(0.0); 
double:el(0.0); 
double:azRate(0.0); 
double:elRate(0.0); 
double:azStart(0.0); 
double:elstart(0.0) ; 
double:azStop(0.0) ; 
double:elStop(0.0); 


II message:SetTankState(SetValue) 
// Gun azimuth 
// Gun elevation 
// Azimuth slew rate 
// Elevation slew rate 
// Azimuth slew start time 
// Elevation slew start time 
// Azimuth slew start time 
// Elevation slew start time 


method:setAzimuth(public; void; double:a; double:r; double:s0; double:sl;) 

{ az=a; azRate=r; azStart=s0; azStop=sl; } 
method:setElevation(public; void; double:e; double:r; double:s0; 

double:si;) 

{ el=e; elRate=r; elStart=s0; elStop=sl; } 

method;getAzimuth(public; double;) { return az; } 
method:getAzimuthRate(public; double;) { return azRate; } 
method:getAzimuthStart(public; double;) { return azStart; } 
method;getAzimuthStop(public; double;) { return azStop; } 
method:getElevation(public; double;) { return el; } 
method:getElevationRate(public; double;) { return elRate; ) 
method:getElevationStart(public; double;) { return elStart; } 
method;getElevationStop(public; double;) ( return elStop; } 

} // message:SetTankState(SetValue) 

} 


C.1.44. Stop.msg 

(message:Stop;} 

C.1.45. StopAzimuthSlew.msg 

{import message {StopSlew} } 

{message:StopAzimuthSlew;} 

C.1.46. StopElevationSlew.msg 

{import message {StopSlew} } 

{message:StopElevationSlew;} 

C.1.47. StopSlew.msg 

{message;StopSlew;} 

C.1.48. Tank.proc 

{import process {Vehicle, Munition} } 

{import message {SetTankState, AddView, UnitSetup, RegisterEnvironmentObject, 
SetEnvironment, AddTrack, ChangeTrack, LoseTrack, SetColor, 
Fire, Attack, StopAzimuthSlew, StopElevationSlew, Hit, 
Impact} } 

{import spt {sptEnvironmentObject, sptNewtonianMotion, sptLinearMotion, 
sptAngularMotion} } 

{import std {<map>, <queue>} } 

{import {<math.h>} } 


355 





process :Tank; (Vehicle) 

{ 

Munition:rounds[50]; 
double:azMaxRate(PI/4.0); 
double:elMaxRate{PI/8.0); 
ulong:round {0); 
double :inv (5000.0) ; 

double:az(0.0); 
double:el (0.0); 
double:azRate (0.0); 
double:elRate(0.0); 
double:azStart (0.0); 
double:elstart{0.0) ; 
double : azStop (2.0*Cloclc: tgetEndTime () ) ; 
double:elStop(2.0*Clock::getEndTime()); 
std::queue<ulong>:targets; 

method:init(public; void;) 

{ 

maxVel = 40.0; 
maxRot = 0.5*PI; 

Vehicle::init {) ; 
attack.setActive(false); 

} 


// process:Tank(Vehicle) 
// We have fifty of them we can fire off 
// Azimuth slew rate 
// Elevation slew rate 
// Next round to use 
// Set muzzle velocity to 5000 m/s 

// Gun azimuth 
// Gun elevation 
// Azimuth slew rate 
// Elevation slew rate 
// Azimuth slew start time 
// Elevation slew start time 
// Azimuth slew stop time 
// Elevation slew stop time 
// Targets to shoot 


// method:init(public; void;) 
// Set maximum velocity to 40 m/s 
// Set max rotation angle to PI/2 
// Call parent version 
// Nothing to attack right now 
// method:init(public; void;) 


method:setGunPos(public; void; double:a; double:e;) { az=a; el=e; } 

// method:angle returns the radian angle value of t in the range {-PI, PI] 
method:angle(public; double; double:t;) 

{ // method:angle(public; double; double:t;) 

t = fmod(t, 2.0*PI); // Get in range [0,2*PI) or (-2*PI, 0] 


if (t<=-PI) t+=2.0*PI; 
else if (t>=PI) t-=2.0*PI; 
return t; 


// Get in range (-2*PI, PI) 
// Get in range (-PI, PI] 
// Return the value 
// method:angle(public; double; double:!;) 


// method:bearing returns the radian angle value of t in the range [0, 2.0*PI) 
method:bearing(public; double; double:!;) 

{ // method:bearing(public; double; double:!;) 

t = fmod(t, 2.0*PI); if (t<0.0) t+=2.0*PI; // Get in range [0, 2*PI) 

return t; // Return the value 

) // method:bearing(public; double; double:!;) 

method:getAzimuth(public; double; double:!;) // Effective time of azimuth 
{ // method:getAzimuth(public; void; double:!;) 

double dt = min(azStop, t)-azStart; // Get the elapsed time 

return angle(az+azRate*dt) ; // Get the current bearing 

} // method:getAzimuth(public; void; double:!;) 


method:getElevation(public; double; double:!;) // Effective elevation time 
{ // method:getElevation(public; void; double:!;) 

double dt = min{elStop, t)-elStart; // Get the elapsed time 

return angle(el+elRate*dt); // Get the current elecation 

} // method:getElevation(public; void; double:!;) 


method:updateGunPos(public; void; double:!;) // Update to current position 
{ // method:updateGunPos(public; void; double:!;) 


az = getAzimuth(t) ; 
el = getElevation(t) ; 
if (azStop < t) // If we haven' 
{ 

azStop = 2.0*Clock::getEndTime( 
azRate = 0.0; 

} 


// Update the azimuth to time t 
// Update the elevation to time t 
actually arrived at the stop time, yet 

; // Specify end time of current motion 

// Stop motion 
// if (azStop < t) 


if (elStop < t) // If we haven't actually arrived at the stop time, yet 
{ 


356 





elStop = 2.0*Clock::getEndTime{); // Specify end time of current motion 
elRate = 0.0; // Stop motion 

// if (elStop < t) 


azStart = elStart = t; 

) 


II Specify time t as the new start time 
// method:updateGunPos(public; void; double:t;) 


method:turnGunTo(public; void; double:a; double:e;) 

{ II method:turnGunTo(public; void; double:a; double:e) 

updateGunPos(getTime()); // Get the current gun position 

azRate = angle(a-az) < 0 ? -azMaxRate : azMaxRate; // Turn direction 

elRate = angle(e-el) < 0 ? -elMaxRate : elMaxRate; // Turn direction 

azStop = getTime 0 +(angle (a-az)/azRate) ; // Get azimuth slew stop time 

elStop = getTime 0 +(angle (e-el)/elRate) ; // Get elevation slew stop time 

} II method;turnGunTo(public; void; double:a; double:e) 


method:stopAzimuth(public; void; double:!;) // Stop azimuth at time t 

{ // method:StopAzimuth(public; void; double:!;) 

az = getAzimuth(t); // Update the gun azimuth 

azRate =0.0; // Stop azimuth slewing 

azStart = t; // New affective time 

azStop = 2.0*Cloc]c: : getEndTime 0 ; // Get the stop time 

} // method:StopAzimuth(public; void; double:!;) 


method:stopElevation(public; void; double:!;) // Stop elevation at time t 
{ // method:StopElevation(public; void; double:!;) 

el = getElevation(t); // Update the gun elevation 

elRate =0.0; // Stop elevation slewing 

elStart = t; // New affective time 

elStop = 2.0*Clock::getEndTime0; // Get the stop time 

} // method:StopElevation(public; void; double:!;) 


method:notify(public; void; std::vector<SetTankState>s:out;) 

{ // method:notify(public; void; std::vector<SetTankState>&:out;) 

std::map<process, gvm::object_index>::iterator i; // For loop index 

for (i=views.begin0; i!=views.end(); ++i) // Loop over index map 

{ 

out.push_back(me); // Allocate a new message 

out.back 0 .addDest(i->first); // Add this view as a destination 

out.back 0 .index = i->second; // Specify the index 

out.backO .setAzimuth(az,azRate,azStart,azStop); // Specify azimuth 

out.back().setElevation(el,elRate,elStart,elStop); // Specify elevation 
} // for (i=views.begin0; i!=views.end(); ++i) 

} // method:notify(public; void; std::vector<SetTankState>s:out;) 


method:getPos(public; spt::vertex; spt::vertex:p; spt::vertex:v; 

spt::vertex:a; double:t;) 

{ return p+(v+0.5*t*a)*t; } // Position affected by velocity & acceleration 

method:getPos(public; spt::vertex; spt::vertex:p; spt::vertex:v; double:!;) 

( return p+(t*v); } // Position affected only by velocity 

method:getPos(public; spt::vertex; double:a; double:e; double:!;) 

{ // method:getPos(public;spt::vertex; ... ) 

spt::vertex pos(0.0, 3); 
spt::vertex acc(0.0, 3); 
spt::vertex vel(0.0, 3); 

vel[0] = mv*cos(e)*cos(a); 
vel[l] = mv*cos(e)*sin(a); 
vel[2] = rav*sin(e); 

acc[0] = 0.0; 
acc[l] = 0.0; 
acc[2] = -9.8; 


return getPos(pos, vel, acc, t); 

} II method:getPos(public;spt::vertex; ... ) 

method:turnTime(public; double; double:a; double:e;) 

{ return max(fabs(angle(a)/azMaxRate), fabs(angle(e)/elMaxRate)); } 


357 







method:getIntercept(public; void; spt:ivertex:p; spt;;vertex&:sin;) 

{ // method:getIntercept(public; void; spt::vertex:p; ...) 

double r = norm(p); // Range to target 

sln[0] = angle(atan2(p[1], p[0])); // Get the gun's azimuth 

sln[l] = 0.5*asin {9.8*r/(rtiv*mv) ) ; // Get the gun's elevation 

sln[2] = mv*sin(sin[1])/4.9; // Get the travel time 

) // method:getIntercept(public; void; spt::vertex:p; ...) 


method:rotate(public; spt::vertex; spt::vertexS:v; double:theta;) 


} 


// method:rotate(public 
double ct=cos(theta), st=sin(theta) 
spt::vertex rv(0.0,3); 
rv[0]=ct*v[0]-st*v[l] ; 
rv[l]=st*v[0]+ct*v[l] ; 
return rv; 


spt::vertex; spt::vertex; double;) 
// Get transformation coefficients 
// Return value valarray 

// Get the new 'x' component 
// Get the new 'y' component 
// Return the valarray to the calling routine 


// method:rotate(public; spt::vertex; spt;:vertex; double;) 


method:aim(public; void; ulong;track; double:err;) // Aim at trade 

{ // method:aim(public; void; ulong;track; double:err;) 

double tt=0; // Turn time to target 

double t = getTime(); // Convenience for the current time 

double a = getAzimuth(t), e = getElevation(t); // Get current gun state 

double bearing = nm.ap(t)[2]; // Get the current tanle bearing 

spt::vertex rp=trac)e5 (track) .Ip (t)-nm.Ip (t); // Rel target pos 

spt:;vertex rv=tracks[track].Iv(t)-nm.Iv(t); // Rel target vel 

rp = rotate(rp,-bearing); // Rotate to reflect a relative bearing 

rv = rotate(rv,-bearing); // Rotate this to relative bearing, too 

spt::vertex diff(0.0,3); // Vector between impact and target 

spt::vertex sin(0.0,3); // Place to hold gun firing solution 

ulong c=0; // Counter to avoid infinite loops 

t = 0; // Start with current time as a reference 

do // Iteratively get solutions until within acceptable margin of error 

( 

getintercept(getPos(rp,rv,sin[2]+tt), sin); // Solution to position 

tt = turnTime(sin[0]-a, sln[l]-e); // Calculate turn time 

diff=getPos(rp,rv,sin[2]+tt)-getPos(sin[0],sin[1],sin[2]); // Imp diff 

} // do 

while (norm(diff)>err && ++c<10); // Loop until solution, or divergence 

turnGunTo(sln[0], sln[l]); // Start turning the gun to where it belongs 
} // method:aim(public; void; ulong:track; double:err;) 

mode:Default 

( // mode:Default 

node: addview[AddView:in][SetTankState:out=>(in.getSource();) ] 

{ // node:addview[Addview:in][SetTankState:out] 

out.setAzimuth(az, azRate, azStart, azStop); // Specify gun azimuth 

out.setElevation(el, elRate, elStart, elStop); // Specify gun elevation 
out.index = in.index; // Set the index value 

} // node:addView[AddView:in] [SetTankState:out] 


node:unitSetup[OnitSetup:in] // Setting the environment 

[SetEnvironment:se=>(rounds;), // Establish env. 

SetColor:sc=>(me;), // Set this unit's color 

RegisterEnvironmentObject:rst=>(in.environment;)] // Reg 

( // node:unitSetup[OnitSetup:in][SetEnvironment:se] 

se.SetEnvironment(in.getEnvironment0); // Set the environment 

se.setNode(in.getNode()); // Set the rendering node 

rst.setForce(force = in.getForce()); // Set the force component 

rst.setRadius(radius = in.getRadius()); // Set the sensor radius 

rst.setMotion(nm); // Set the newtonian motion parameters 

if (force==RED) sc.setd.O, 0.0, 0.0); // Set this unit's color 

else if (force==BLOE) sc.set (0.0, 0.0, 1.0); // Red or Blue 

} // node:unitSetup[OnitSetup:in][SetEnvironment:se] 

node:addTrack[AddTrack:in] // Upon notification of a new track 

[Attack:out=>(me;)] // Attack the new track 

{ // node:addTrack[AddTrack:in][Attack:out] 

attack.setActive(true); // Start the attack sequence 

out.setTX(targets.empty 0); // Don't bother if we're already attacking 


358 







targets.push(in.getTrack{) ) ; 

} 


// 

// node:addTrack[AddTrack:in][Attack:out] 


node:impact[Impact:in][] 

{ 

Default.setActive(false); 
attack.setActive(false) ; 
turn_to_dest.setActive(false) ; 
move_to_dest.setActive(false) ; 
turn_to_heading.setActive(false); 

} 

} 


// node:impact[Impact:in][] 


// node:impact[Impact:in][] 
// mode:Default 


mode:attack 

{ // mode:attack 

node:attack[Attack:in] // Upon notification of a new track 

[StopAzimuthSlew:sas=>(me;):(azStop), // Slew azimuth 

StopElevationSlew:ses=>(me;):(elStop) , // Slew elevation 

SetTankState:sts[]] II Notify views of new tank state 

{ II node:addTrack[AddTrack:in][StopAzimuthSlew, StopElevationSlew, ... ] 
if (round<rounds.size()) // If we have some tank rounds left 


aim(targets.front(),0.01); // Start moving gun to aim at target 

notify(sts); // Notify the views that the gun parameters have changed 
} //if (round<rounds.size()) 

else // If there are no more tank rounds left 

( 

sas.setTX(false); // Don't bother realigning the gun 

ses.setTX(false); // Or changing its elevation 

attack.setActive(false); // Turn the attack mode off 

} // else from if (round<rounds.size ()) 

} // node:addTrack[AddTrack:in][StopAzimuthSlew, StopElevationSlew, ... ] 


node:StopAzimuth[StopAzimuthSlew:in] // When the azimuth stops slewing 
[Fire:f, // Fire the gun if we're all done aiming it 

SetTankState:sts[]] // Notify views of new tank state 

[ // node:StopAzimuth[StopAzimuthSlew:in][Fire:f, SetTankState:sts[]] 

double t = getTimeO; // Get the current time 

if (t == azStop) // If this message is for current slew 

{ 

StopAzimuth(t); // Stop slewing the azimuth 

notify(sts); // Notify the views that the azimuth has stopped 

if (elStop>Clock::getEndTime ()) // If the elevation slew stopped 

{ 

f.set(mv,az+nm.ap(t)[2],el,nm.Ip(t),nm.Iv(t)); // Set firing params 
f.addDest(rounds[round++]); // Tell it to the next tank round 

} // if (elStop>Clock::getEndTime0) 

else f.setTX(false); // Don't fire until elevation slew is complete 

} // if (getTimeO == azStop) 

} // node:StopAzimuth[StopAzimuthSlew:in][Fire:f, SetTankState:sts[]] 


node:StopElevation[StopElevationSlew:in] // When elevation stops slewing 
[Fire:f, // Fire the gun if we're all done aiming it 
SetTankState:sts[]] // Notify views of new tank state 

( // node:StopElevation[StopElevationSlew:in][Fire:f, SetTankState:sts[]] 

double t = getTimeO; // Get the current time 

if (t == elStop) //If this message is for current slew 

{ 

StopElevation(t); // Stop slewing the elevation 

notify(sts); // Notify the views that the elevation has stopped 

if (azStop>Clock::getEndTimeO) // If the elevation slew stopped 

{ 

f.set(mv,az+nm.ap(t)[2],el,nm.lp(t),nm.lv{t)); // Set firing params 
f.addDest(rounds[round++]); // Tell it to the next tank round 

} // if (elStop>Clock::getEndTime0) 

else f.setTX(false); // Don't fire until elevation slew is complete 

} // if (getTime() == elStop) 

} // node:StopElevation[StopElevationSlew:in][Fire:f,SetTankState:sts[]] 


node:changeTrack[ChangeTrack:in][Attack:out] 

{ // node:changeTrack[ChangeTrack:in][Attack:out] 


359 








if (in.getTrack() == targets.front()) // If this is the target... 

out.set(in.getTrack{)); // Reaim and start again 

else // If it's not the one we're aiming at 

out.setTX(false); // Don't change anything 

} II node:changeTrack[ChangeTrack;in][Attack:out] 


} 


node:hit[Hit:in] 

[Attack:out] 

{ 

std::set<ulong> hits; 

for (ulong i=0; i<in.track.size() 
hits.insert(in.track[i]); 

while {!targets.empty() ss 

(hits.find(targets.front() 
active.find(targets.front())^ 
targets.pop(); 

if (!targets.empty 0) 

out.set(targets.front()); 
else 
{ 

out.setTX(false) ; 
attack.setActive(false); 

} II else from if ([target; 


// node:hit[Hit:in][Attack:out] 
// Sorted hits 

++i) // Loop over the hits 

II Put them in the set to sort them 

// While targets remain 
^hits.endO I j // but they were hit 
=active.end{))) // or lost 

// Pop the target from the queue 

// If targets remain 
// Attack the next one 
// If no targets remain 

// Don't send the attack message 
// Turn off the attack mode 
.empty()) out.set(targets.front())) 
// node:hit[Hit:in][Attack:out] 
// mode:attack 
// process:Tank(Vehicle) 


C.1.49. TrackEvent.msg 

{ 

message:TrackEvent 

{ // message:TrackEvent 

ulong:force(0); // Force identifier for the track 

ulong:track( ((ulong) -1) ); // Index of track being detected by sensor 

method:set(public; void; ulong:t; ulong:f;) { track=t; force=f; } 
method:setTrack(public; void; ulong:t;) { track=t; } 
method:setForce(public; void; ulong:f;) { force=f; ) 
method:getForce(public; ulong;) { return force; ) 
method:getTrack(public; ulong;) { return track; ) 

} // message:TrackEvent 


C.1.50. TrackMotionEvent.msg 

(import message (TrackEvent) } 

(import spt (sptNewtonianMotion) } 

{ 

message:TrackMotionEvent(TrackEvent) 

( // message:TrackMotionEvent(TrackEvent) 

spt::NewtonianMotion:motion; // Motion parameters of the track 

method:setMotion(public; void; spt::NewtonianMotion:m;) 

( motion=m; motion.setStopTime(2.0*Clock::getEndTime()); } 
method:getMotion(public; spt::NewtonianMotion;) ( return motion; ) 

) // message:TrackMotionEvent(TrackEvent) 

} 


C.1.51. UnitSetup.msg 

(import message (SetEnvironment) } 


message:UnitSetup(SetEnvironment) 
{ 


360 







ulong:force; II Force identifier for the destination object 

double:radius; II Radius of detection for destination unit 

method:set{public; void; ulongif; double:r; process:e; process:n;) 

{ force=f; radius=r; environment=e; renderNode=n; } 

method:setForce(public; void; ulong:f;) { force=f; } 
method:setRadius(public; void; double:r;) { radius=r; } 

method:getForce(public; ulong;) { return force; } 
method:getRadius(public; double;) { return radius; } 


} 


C.1.52. Vehicle.proc 

{import process {SensorTrack) ) 

{import message {MoveTo, Stop, MovementComplete, HoldPosition, 


SetNewtonianMotion} ) 
{import std {<valarray>} } 

{import spt {sptDefs} } 

{import {<math.h>} } 

{ 

process:Vehicle(SensorTrack) 

{ 

double:maxVel; 
double:maxRot; 
spt::vertex:destPos(3); 
spt::vertex:destOri(3); 
double:orderTime; // Last order time, 

method:init(public; void;) 

{ 

turn_to_dest.setActive(false); 
move_to_dest.setActive(false); 
turn_to_heading.setActive(false); 

} 


// process:Vehicle(SensorTrack) 
// Max vehicle velocity 
// Max vehicle rotation rate 
// Destination position 
// Destination orientation 
to ignore obsolete movement messages 


// method:init(public; void;) 
// Now turning to destination 
// Not yet moving to destination 
// Not turning to final heading yet 
// method:init(public; void;) 


method:halt(protected; void; double:st; double:et;) 

{ // method:halt(protected; void; double:st; double:et;) 

spt::vertex 2(0.0, 3); // z for zero 

nm.set(nm.lp(st), z, z, nm.ap(st), z, z, st, et); 

turn_to_dest. setActive (false); // Now turning to destination 

move_to_dest.setActive(false); // Not yet moving to destination 

turn_to_heading.setActive(false); // Not turning to final heading yet 

} // method:halt(protected; void; double:st; double:et;) 


method:turn(protected; void; double:r; double:st; double:et;) // Rotation 
( // method:turn(protected; void; double:r; double:st; double:et;) 

spt:;vertex z(0.0, 3); // z for zero 

spt::vertex tr(0.0, 3); // Turn rate 

tr[2]=r; // Set the turn rate 

nm.set(nm.Ip(st) , z, z,nm.ap(st),tr,z,st,et); 

} // method:turn(protected; void; double:r;) 


method:forward(protected; void; double:r; double:st; double:et;) // Rate 
{ // method:forward(protected; void; double:r;) 

spt::vertex co(nm.ap(st)); // Get current orientation 

spt::vertex 2(0.0, 3); // z for zero 

spt::vertex v{0.0, 3); // Velocity vector 

v[0] = r*cos (CO [2]); // Get the velocity in the x direction 

v[l] = r*sin(co[2]); // Get the velocity in the y direction 


nm.set(nm.Ip(st), v, z, co, z, z, st, et); 

turn_to_dest.setActive(false); // No need to turn to destination 

move_to_dest.setActive(true); // Moving to destination 

turn_to_heading.setActive(false); // Not turning to final heading 

} // method:forward(protected; void; double:r;) 


mode:Default 


361 







// mode:Default 


nodeiholdPosition[HoldPositioniin] 

[SetNewtonianMotion:out[]] 

{ // node:holdPosition[HoldPositiontin][SetNewtonianMotion:out[]] 

halt(getTime(), 2.0*Clock:tgetEndTime()); // Stop all motion 

notify(out); // Generate the output messages 

} // nodetholdPosition[HoldPosition:in][SetNewtonianMotion:out[]] 


nodetmoveTo[MoveTo:in] // Receive order to move to a position/ori 

[Stop:st=>(me;), // Stop turning 

SetNewtonianMotion:out[], // Inform views & environment 

MovementComplete:mc=>(parent;)] // Movement is complete 

{ // nodetmoveTo[MoveTo:in][StopTurn:st,SetNewtonianMotion:out[]] 


destPos = in.getPosO; 
destOri = in.getOriO; 
spt::vertex cp(nm.Ip(getTime())) 
spt::vertex co(nm.ap(getTime())) 
spt::vertex rp(destPos-cp); 
double dist=norm(rp); 


// Save the destination position 
// Save the destination orientation 
; // Get current position 

; // Get current orientation 

// Relative position 
// Get relative distance to destination 


double theta = dist>0 ? atan2(rp[l], rp[0]) 

: co[2]; // Direction to dest if not there 
if (theta<0.0) theta += PI*2.0; // All positive 
double rb=spt::AngularMotion::angDiff(theta,co[2]); // Get rel bearing 
double rh=spt::AngularMotion::angDiff(destOri[2],co[2]); // Rel heading 
orderTime = getTime(); // Reacting to current order 
me.setTX(false); // Don't transmit, by default 


if (dist==0.0 && rh==0.0) 


// Already there? 


me.setTX(true) ; // Inform parent we're done 

St.setTX(false) ; // Don't transmit stop message 

halt(getTime0 , 2.0*Clock::getEndTime()); // Stop all motion 

} // If we're at the destination 

else if (dist==0.0) // If all we need to do is turn 

( 

double stop = orderTime+fabs(rh/maxRot); // Get stop time for motion 


double rate = (rh<0.0 
st.setTime(stop); 
turn(rate, orderTime, stop); 
turn_to_dest.setActive(false); 
move_to_dest.setActive(false); 
turn_to_heading.setActive(true); 


maxRot : maxRot); 


} 


(rb==0.0) 


// Get proper turn rate 
// Get the rotation time 
// Set the turning parameters 
// Done turning to destination 
// Done moving to destination 
// Turning to final heading yet 
// else if (dist==0.0) 
// If pointed in the right direction 


else if 

{ 

double stop = orderTime+fabs(dist/maxVel); // Get motion stop time 

St .setTime(stop); // Get the rotation time 

forward(maxVel, orderTime, stop); // Move forward at the max velocity 
} // If (dist != 0.0) 

else // If we need to turn to the destination 

( 

double stop = orderTime+fabs(rb/maxRot); // Get stop time for motion 

maxRot : maxRot); 


double rate = (rb<0.0 
St.setTime(stop); 
turn(rate, orderTime, stop); 
turn_to_dest.setActive(true); 
move_to_dest.setActive(false); 
turn_to_heading.setActive(false) 


} 

notify(out); 


// Get proper turn rate 
// Get the rotation time 
// Set the turning parameters 
II Now turning to destination 
// Not yet moving to destination 
// Not turning to final heading yet 


} 


// Generate the output messages 
// node:moveTo[MoveTo:in][Stop:st,SetNewtonianMotion:out[]] 

// mode:Default 


mode:turn_to_dest 

{ // mode:turn_to_dest 

node:stop[Stop:in] // Receive order to move to a position/ori 

[Stop:st=>(me;), // Stop turning 

SetNewtonianMotion:out[], // Inform views & environment 

MovementComplete:mc=>(parent;)] // Movement is complete 

{ // node:stop[Stop:in][Stop,SetNewtonianMotion,MovementComplete] 

me .setTX(false); // Probably not done yet 


362 







if (in.getGenTime ()==orderTiirie) 

{ 

spt:;vertex cp(nm.Ip(getTime())) 
spt:;vertex co(nm.ap(getTime())) 
spt::vertex rp(destPos-cp); 
double dist=norm(rp); 
orderTime = getTime(); 
double stop = orderTime+fabs(dis 
St.setTime(stop); 
forward(maxVel, orderTime, stop) 
notify(out); 

} 

else II If the order was 

St.setTX(false); 

// node:stop[Stop:in][Stop 


n It this is something we obey 

; // Get current position 

; // Get current ori 

// Relative position 
// Distance to destination 
// Reacting to current order 
t/maxVel); // Get motion stop time 

// Get the rotation time 
; // Move forward at the max velocity 
// Generate the output messages 
II if (in.getGenTime0==orderTime) 
from something we implicitly revoked 

,SetNewtonianMotion,MovementComplete] 
// mode:turn to dest 


mode:move_to_dest 

{ // mode:move_to_dest 

node:stop[Stop:in] // Receive order to move to a position/ori 

[Stop:st=>(me;), // Stop turning 

SetNewtonianMotion:out[], // Inform views & environment 

MovementComplete:mc=>(parent;)] // Movement is complete 

{ // node:stop[Stop:in][Stop,SetNewtonianMotion,MovementComplete] 

me.setTX(false); // Probably not done yet 

if (in.getGenTime()==orderTime) // If this is something we obey 

{ 

spt::vertex co(nm.ap(getTime())); // Get current ori 

double rh=spt::AngularMotion::angDiff(destOri[2],co[2]); // Rel hdng 

orderTime = getTime(); // Reacting to current order 


if (rh != 0.0) 

[ 

double stop = orderTime+fabs(rh/maxRot); 
double rate = (rh<0.0 ? -maxRot : maxRot) 
St.setTime{stop); 

turn(rate, orderTime, stop); // 

turn_to_dest.setActive(false); // D 

move_to_dest.setActive(false); // 

turn_to_heading.setActive(true); // Tu 

} 

else // If already 


// Some rotation? 


(rh/maxRot); // Get motion stop time 

Rot : maxRot); // Get proper turn rate 
// Get the rotation time 
// Set the turning parameters 
); // Done turning to destination 

); // Done moving to destination 

ue); // Turning to final heading yet 

// else if (rh != 0.0) 
// If already in position s orientation 


me.setTX(true); 
St.setTX(false); 


// Inform parent we're done 
// Don't transmit stop message 


halt(getTime{), 2.0*Clock::getEndTime(); 


II Stop all motion 


notify(out); // Generate the output messages 

) // if (in.getGenTime0==orderTime) 

else // If the order was from something we implicitly revoked 

st.setTX(false); 

// node:stop[Stop:in][Stop,SetNewtonianMotion,MovementComplete] 

// mode:move to dest 


mode:turn_to_heading 

{ II mode:turn_to_heading 

node:stop[Stop:in] // Receive order to move to a position/ori 

[SetNewtonianMotion:out[], // Inform views & environment 

MovementComplete:mc=>(parent;)] II Movement is complete 

[ // node:stop[Stop:in][SetNewtonianMotion,MovementComplete] 

if (in.getGenTime()==orderTime) // If this is something we obey 

{ 

halt(getTime0, 2.0*Clock::getEndTime()); // Stop all motion 

notify(out); // Generate the output messages 

} // if (in.getGenTime0==orderTime) 

else // If the order was from something we implicitly revoked 

me.setTX(false); 

} // node:stop[Stop:in][SetNewtonianMotion,MovementComplete] 

} // mode:turn_to_heading 

// process:Vehicle(SensorTrack) 


363 







C.1.53. gvm/gvmAddTrack.h 

/////////////////////////////////////////////////////////////////////////////// 
// gvmAddTrack.h - Class declaration for the gvm::AddTrack class 
/////////////////////////////////////////////////////////////////////////////// 

#ifndef ADDTRACK_H_INCLUDED 
♦define ADDTRACK_H_INCLUDED 

♦include "gvmChangeTrack.h" 

♦include "gvmObject.h" 

♦define GVM_AddTrack GVM_UserMessage003 
namespace gvm 

{ // namespace gvm 

class View; // Forward declaration of the gvm::View class 

class AddTrack : public ChangeTrack 

{ 

private: 

double radius; 
ulong force; 

public: 

AddTrack(Views, double, object_index, const spt::NewtonianMotion&, 
GLdouble, ulong); 

virtual void send(void); // Deliver the message payload 

// class AddTrack : public Message 
// namespace gvm 

♦endif 



// class AddTrack : public ChangeTrack 

// Radius of tank 
// Set the track iff value 


C. 1.54. gvm/gvmAddTrack.cxx 

/////////////////////////////////////////////////////////////////////////////// 
// gvmAddTrack.cxx - Class method definitions for the gvm::AddTrack 
// class 

/////////////////////////////////////////////////////////////////////////////// 

♦include "gvmTacticalView.h" 

♦include "gvmAddTrack.h" 

♦include "gvmView.h" 

namespace gvm 

{ // namespace gvm 

AddTrack::AddTrack(Views v, 
double t, 
object_index i, 

const spt::NewtonianMotion& nm, 
double r, 
ulong f) 

: ChangeTrack(v, t, GVM_AddTrack, i, nm), radius(r), force(f) 

{ // AddTrack::AddTrack(Views,double,object_index,spt::NewtonianMotion, ...) 

} // AddTrack::AddTrack(Views,double,object_index,spt;:NewtonianMotion, ...) 

void AddTrack::send(void) 

{ // void AddTrack::send(void) 

gvm::TacticalViewS v = dynamic_cast<gvm::TacticalViewS>(getView()); 

V.addTrack(getDest(), nm, radius, force); 

} // void AddTrack::send(void) 

} II namespace gvm 

C.1.55. gvm/gvmBattleView.h 

/////////////////////////////////////////////////////////////////////////////// 
// gvmBattleView.h - Defines the gvm::BattleView class in which all gvm::Object 
// instances are viewed. 

/////////////////////////////////////////////////////////////////////////////// 


364 








#ifndef GVMBATTLEVIEW_H_INCLaDED 
#define GVMBATTLEVIEW_H_INCLUDED 

#include <string> 
iinclude <iostream> 

#include "gvmViewSD.h" 

#include "gvmTank.h" 

#include "spt/sptDefs.h" 


namespace gvm 
{ 

class BattleView : public View3D 

{ 

protected: 

spt::vertex vnv; 
spt::vertex vup; 
spt::vertex vrv; 
double forward, up, right; 
double speed; 

public: 

BattleView(void); 


// namespace gvm 

// class BattleView : public View 

// View normal vector 
// View up vector 
// Right side of view 
// Current speed in these directions 
// Speed of motion when in motion 


// Class constructor 


virtual void createObject(object_handle); 


virtual void updateTravel(void); 
virtual void begin(void); 
virtual bool isDisplay(void); 
virtual void keydown(byte,int,int); 
virtual void keyup(byte,int,int); 
virtual void motion(int, int); 


// Schedule object creation 




// Update camera motion parameters 
// Start rendering the scene graph 
// Should we update the display? 
// Key press event callback 
// Key release event callback 
// Active mouse motion callback 
// class BattleView : public View 
// namespace gvm 


#endif 


C.1.56. gvm/gvmBattleView.cxx 

/////////////////////////////////////////////////////////////////////////////// 
// gvmBattleView.cxx - Class member and static definitions of the 
// gvm::BattleView class. 

/////////////////////////////////////////////////////////////////////////////// 

#include "Exception.h" 

#include "gvmTank.h" 

#include "gvmCommandPost.h" 

#include "gvmBattleView.h" 

#include "gvmMunition.h" 

#include "gvmGround.h" 


namespace gvm 
{ 

BattleView::BattleView(void) 

: vnv(3), vup(3), vrv(3), forward(0.0) 

{ 

zFar = 40000.0; 

pos[0] = 0.0; pos[l] = 0.0; pos[2] 
ori[0] = 0.0; ori[l] = 0.0; ori[2] 
setTitle("BattleView"); 
setSceneChange(true) ; 
redisplay() ; 

} 


// namespace gvm 
II Window for the view 
up(O.O), right(O.O), speed(l.O) 

// BattleView::BattleView(void) 
// Set the far clipping plane to 40,000m 
= 2000 . 0 ; 

= 0.0; 

// Need to refresh display 
// Redisplay the environment 
II BattleView::BattleView(void) 


void BattleView::createObject(object_handle h) 

{ // void BattleView:;createObject(object_handle) 

if (h.second >= objectList.size()) // Is this lined up properly 

throw Exception::Nonspecific("Object count mis-alignment."); 

switch (h.first) // Which object should we create? 

{ 

case GVM_Tank: // A new Tank instance requested 


365 







objectList[h.second] = 
break; 

case GVM_CoininandPost: 
objectList[h.second] = 
break; 

case GVM_Munition: 

objectList(h.second] = 
break; 

case GVM_Ground: 

objectList[h.second] = 
break; 

default: 

View3D::createObj ect(h) 
break; 



new Tank(*this, h.second); 

// case GVM_Tank: 

// A new Command Post instance requested 
new CommandPost(*this, h.second); 

// case GVM_CommandPost: 

II A new Munition instance requested 
new Munition (*this, li.second); 

// case GVM_Munition: 

// A new Ground instance requested 
new Ground (*this, li.second); 

// case GVM_Ground: 

// None of the above 
; II Call the parent class version 

// default 
// switch (t) 

// void BattleView::createObject(object_handle) 


void BattleView::updateTravel(void) 

{ II void BattleView:rupdateTravel(void) 

up = (up<0) ? up=-speed : (up>0) ? up=speed : 0.0; 
right = (right<0) ? right=-speed : {right>0) ? right=speed : 0.0; 
forward = (forward<0) ? forward=-speed : (forward>0) ? forward=speed : 0.0; 
} // void BattleView::updateTravel(void) 


void BattleView::begin(void) 

( 

pos += forward*vnv+right*vrv+up*vup; 
if (pos[0]>10000.0) pos[0] = 10000.0; 
if (pos[0]<-10000.0) pos[0] = -10000.0; 
if (pos[1]>10000.0) pos[l] = 10000.0; 
if (pos[l]<-10000.0) pos[ll = -10000.0; 
if (pos[2]<100.0) pos[2] = 100.0; 
if (zoom[0]) scale *= 200M_FACT0R; 
if (zoom[l]) scale /= ZOOM_FACTOR; 
glMatrixMode(GL_PROJECTION); 
glLoadldentity(); 

gluPerspective(50.0, aspect, zNear, zFar), 
glMatrixMode(GL_MODELVIEW); 


// void BattleView::begin(void) 
// Get the current position 
// Don't go out of the play box 


// Don't go below ground 
// Zoom in if desired 
// Zoom out if desired 
// Establish a projection view 
// Load the identity matrix 
II Establish perspecive 
// Extablish MODELVIEW 
// Load another identity matrix 
// How to blend 


glLoadldentity() 

glBlendFunc{GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 
gluLookAt(pos[0], pos[l], pos[2], 

pos[0]+vnv[0], pos[1]+vnv[l], pos[2]+vnv[2], 

vup[0], vup[l], vup[2]); // Setup the camera viewing paramters 

glScaled(scale, scale, scale); // Zoom back aways 

// void BattleView::begin(void) 


bool BattleView::isDisplay(void) 
{ 

return (isVisible && refresh); 

} 


// bool BattleView::isDisplay(void) 
// Should we redisplay 
// bool BattleView::isDisplay(void) 


void BattleView::keydown(byte key, 
{ 

glutSetWindow(window); 
switch (key) 

{ 

case 'W : 
case 'w': 

forward = speed; 
break; 
case 'S': 
case 's': 

forward = -speed; 
break; 
case 'A': 
case 'a': 

right = speed; 
break; 
case 'D': 
case 'd': 

right = -speed; 


int X, int y) 

// void BattleView:;keydown(byte,int,int) 

II Set the window 
II Which key was pressed 

// Applies to either 'w' or shift-'w' 

II We're moving forward 

// Applies to either 's' or shift-'s' 

// We're moving backward 

// Applies to either 'a' or shift-'A' 

II We're moving left 

// Applies to either 'a' or shift-'A' 

// We're moving right 


366 






break; 
case 'Q': 
case 'q': 
up = speed; 
break; 


case 

’E’ 




// 

Applies 

case 

' e' 






up 

= -speed; 





break; 






case 

'1' 






case 

' ! ' 

speed 

= 

1.0; 

updateTravel(); 

break; 

case 

'2 ' 






case 


speed 

= 

2.0; 

updateTravel() ; 

break; 

case 

'3' 






case 

'#' 

speed 

= 

4.0; 

updateTravel() ; 

break; 

case 

' 4 ' 






case 

'$' 

speed 


8.0; 

updateTravel() ; 

break; 

case 

'5' 






case 

' % ' 

speed 

= 

16.0 

; updateTravel() ; 

break; 

case 

'6' 






case 

, A I 

speed 

= 

32.0 

; updateTravel() ; 

break; 

case 

'7' 






case 

' & ' 

speed 


64.0 

; updateTravel() ; 

break; 

case 

'8' 






case 

' * I 

speed 

= 

128. 

0; updateTravel() 

; break 

case 

'9' 






case 

' (' 

speed 


256. 

0; updateTravel() 

; break 

case 

'O' 






case 

') ' 

speed 

= 

512. 

0; updateTravel() 

; break 

default : 






View3D 

: keydown 

key, 

X, y) ; 



// Applies to either 'a' or shift-'A' 
// We're moving up 
either 'e' or shift-'e' 
// We're moving up 


setSceneChange(true); 
redisplay(); 


// Call parent version 
// switch (key) 
// Need to refresh display 
// Redisplay the environment 
// void BattleView::keydown(byte,int,int) 


void BattleView::keyup(byte key, int x, int y) 


{ 


glutSetWindow(window) ; 
switch (key) 

{ 


// void BattleView::keyup(byte,int,int) 
// Set the window 
// Which key was pressed 


case 

'W' 


// 

Applies to either 'w' 

or shift-'w' 

case 

'w' 





case 

'S' 


// 

Applies to either 's' 

or shift-'s' 

case 

' s' 





forward = 0.0; 


// No longermoving 

forward/back 

break; 





case 

'A' 


// 

Applies to either 'a' 

or shift-'A' 

case 

'a' 





case 

'D' 


// 

Applies to either 'a' 

or shift-'A' 

case 

'd' 





right 

o 

o 


// No longer moving right/left 

break; 





case 

'Q' 


// 

Applies to either 'a' 

or shift-'A' 

case 

'q' 





case 

'E' 


// 

Applies to either 'e' 

or shift-'e' 

case 

' e' 





up 

= 0 

0; 


// No longer moving up.down 


break; 
default: 

View3D::keyup(key, x, y); 

} 

setSceneChange(true); 
redisplay(); 


void BattleView::motion(int x, int y) 


{ 


glutSetWindow(window); 
if (buttonState[GLUT_LEFT_BUTTON]) 
{ 


// Call parent version 
// switch (key) 
// Need to refresh display 
// Redisplay the environment 
// void BattleView::keyup(byte,int,int) 


// void BattleView::motion(int, int) 
// Set the window 
// If the left button is down 


367 











ori[l] += (float) (mouseLoc[1]-y)*0.125; // Change rotation about X 
ori[2] += (float)(mouseLoc[0] -X) *0.125; // Change rotation about Z 
if (ori[1]>90.0) ori[l]=90.0; // Don't pitch too high or too low 
if (ori[l]<-90.0) ori[l]=-90.0; 

} // if (buttonState[GLUT LEFT BUTTON]) 


if (buttonState[GLOT_RIGHT_BUTTON]) 

ori[0] += (float)(x-mouseLoc[0])*0.125; 


// If the right button is down 
// Change rotation about Z 


if (buttonState[GLUT MIDDLE BUTTON]) 


// If the middle button is down 


float dy = ((float) (mouseLoc[1]-y))*0.2; 
scale *= pow(ZOOM_FACTOR, dy); 


))*0.2; // Get the difference 

// Get the scaling factor 
// if (buttonState[GLUT MIDDLE BUTTON]) 


mouseLoc[0] = x; 
mouseLoc[1] = y; 


// Get the x component 
// Get the y component 


GLdouble roll=ori[0]*PI/180.0; 
GLdouble pitch=ori[1]*PI/180.0; 
GLdouble yaw=ori[2]*PI/180.0; 


// Get the roll in radians 
// Get the pitch in radians 
// Get the yaw in radians 


GLdouble cx=cos(roll), cy=cos(pitch), cz=cos(yaw); 

GLdouble sx=sin(roll), sy=sin(pitch), sz=sin(yaw); 

vnv[0] = cz*cy; vnv[l] = sz*cy; vnv[2] = -sy; 

vrv[0] = cz*sy*sx-sz*cx; vrv[ll = sz*sy*sx+cz*cx; vrv[2] = cy*sx; 

vup[0] = cz*sy*cx+sz*sx; vup(l] = sz*sy*cx-cz*sx; vup[2] = cy*cx; 


setSceneChange(true); 
redisplay(); 


// Need to refresh display 
// Redisplay the environment 
// void BattleView:imotion(int, int) 


C.1.57. gvm/gvmChangeTrack.h 


/////////////////////////////////////////////////////////////////////////////// 
// gvmChangeTracl<.h - Class declaration for the gvm: :ChangeTrack class 
/////////////////////////////////////////////////////////////////////////////// 

#ifndef CHANGETRACK_H_INCLUDED 
#define CHANGETRACK_H_INCLUDED 

#include "gvmSetNewtonianMotion.h" 

#include "gvmObject.h" 

#include "spt/sptNewtonianMotion.h" 

#define GVM_ChangeTrack GVM_UserMessage002 


namespace gvm 
{ 

class View; 


// namespace gvm 
// Forward declaration of the gvm::View class 


class ChangeTrack ; public SetNewtonianMotion 

{ // class ChangeTrack : public SetNewtonianMotion 

public: 

ChangeTrack(Views, double, object_index, const spt:iNewtonianMotions); 
ChangeTrack(Views, double, message_type, object_index, 
const spt::NewtonianMotions); 


virtual void send(void); 


// Deliver the message payload 
// class ChangeTrack : public Message 
// namespace gvm 


#endif 


C.1.58. gvm/gvmChangeTrack.cxx 


/////////////////////////////////////////////////////////////////////////////// 
// gvmChangeTrack.cxx - Class method definitions for the gvm::ChangeTrack 
// class 


368 







/////////////////////////////////////////////////////////////////////////////// 


#include "gvmTacticalView.h" 

#include "gvmChangeTrack.h" 

#include "gvmView.h" 

namespace gvm 

{ // namespace gvm 

ChangeTrack::ChangeTrack(Views v, 

double t, 
object_index i, 

const spt:iNewtonianMotionS nm) 

: SetNewtonianMotion(v, t, GVM_ChangeTrack, i, nm) 

( // ChangeTrack::ChangeTrack(Views,double,object_index,NewtonianMotion,...) 

} // ChangeTrack;:ChangeTrack(Views,double,object_index, NewtonianMotion, ...) 

ChangeTrack:;ChangeTrack(Views v, 

double t, 
message_type ty, 
object_index i, 

const spt::NewtonianMotionS nm) 

: SetNewtonianMotion (V, t, ty, i, nm) 

{ // ChangeTrack::ChangeTrack(Views,double,message_type,Object_index,...) 

} // ChangeTrack::ChangeTrack(Views,double,message_type,object_index,...) 

void ChangeTrack::send(void) 

{ // void ChangeTrack::send(void) 

gvm::TacticalViews v = dynamic_cast<gvm::TacticalViews>(getView()); 

V. ChangeTrack(getDest 0 , nm) ; 

} // void ChangeTrack::send(void) 

} // namespace gvm 


C.1.59. gvm/gvmCommandPost.h 

/////////////////////////////////////////////////////////////////////////////// 
// gvmCommandPost.h - This draws a command post on the screen 
/////////////////////////////////////////////////////////////////////////////// 

#ifndef GVMCOMMANDPOST_H_INCLUDED 
♦define GVMCOMMANDPOST_H_INCLUDED 

♦include "gvmNewtonianMotion.h" 

♦include "gvmCube.h" 

♦define GVM_CommandPost GVM_UserObject002 

namespace gvm 
{ 

class ViewlD; 

class CommandPost : public NewtonianMotion 
{ 

protected: 

Cube body; // Command post 

public: 

CommandPost(gvm::View3DS, ulong); // Class constructor 

virtual void display(void); // Display the command post 

virtual bool isType(object_type); II Check if this is of type t 

}; // class CommandPost : public NewtonianMotion 

} 

♦endif 


C.1.60. gvm/gvmCommandPost.cxx 

/////////////////////////////////////////////////////////////////////////////// 
// gvmCommandPost.cxx - Method definitions for the CommandPost class 
/////////////////////////////////////////////////////////////////////////////// 


369 







iinclude <iostream> 

#include <GL/glut.h> 

#include "gvmCoramandPost.h" 

#include "gvmViewSD.h" 

namespace gvm 

{ 

CommandPost::CommandPost(gvm::View3DS v, ulong i) 

: NewtonianMotion (v, GVM_CoinmandPost, i) , body(v, i) 

{ II CoiranandPost: iCommandPost (gvm: : ViewSDS, ulong) 

mode = GL_LINES; // Set the mode to polygon 

body.set(20.0); 
body.setMode(GL_LINES); 

} II CommandPost: :CoitimandPost (gvm: :View3D&, ulong) 


void CommandPost::display(void) 

{ 

begin(); 
glPushMatrix() ; 

glTranslated(0.0, 0.0, 10.0); 
glScaled(5.0, 4.0, 1.0); 
body.display(); 
glPushMatrix (); 

glTranslated{0.0, 0.0, 11.0); 
glScaled(1.1, 1.1, 0.1); // 

body.display(); 
glPopMatrix(); 
glPopMatrix(); 
end () ; 


// void CommandPost::display(void) 


// Put the CP above ground 
// Main portion of the building 
// Display the main building portion 

// Move the roof on top 
Malce it thinner, and overhanging building 
// Display the roof 


// void CommandPost::display(void) 


bool CommandPost::isType(object_type c) 

{ // bool CommandPost::isType(object_type) 

return (c==GVM_CommandPost II NewtonianMotion::isType(c)); 

} // bool CommandPost::isType(object_type) 

} // namespace gvm 


C.1.61. gvm/gvmDelete!rack.h 

/////////////////////////////////////////////////////////////////////////////// 
// gvmDeleteTrac)c.h - Class declaration for the gvm: :DeleteTrac): class 
/////////////////////////////////////////////////////////////////////////////// 

#ifnd6f DELETETRACK_H_INCLUDED 
#define DELETETRACK_H_INCLUDED 

#include "gvmMessage.h" 

#include "gvmObject.h" 

#define GVM_DeleteTrack GVM_UserMessage004 
namespace gvm 

{ // namespace gvm 

class View; // Forward declaration of the gvm::View class 

class DeleteTrack : public Message 

{ // class DeleteTrack : public Message 

public: 

DeleteTrack(Views, double, object_index); 

virtual void send(void); // Deliver the message payload 

}; // class DeleteTrack : public Message 

} // namespace gvm 

tendif 


C.1.62. gvm/gvmDeleteTrack.cxx 

/////////////////////////////////////////////////////////////////////////////// 

// gvmDeleteTrack.cxx - Class method definitions for the gvm::DeleteTrack 


370 







// class 

/////////////////////////////////////////////////////////////////////////////// 


#include "gvmTacticalView.h" 

♦include "gvmDeleteTrack.h" 

♦include "gvmView.h" 

namespace gvm 

{ // namespace gvm 

DeleteTrack::DeleteTrack(Views v, 

double t, 
object_index i) 

: Message (V, t, GVM_DeleteTrack, i) 

{ // DeleteTrack::DeleteTrack(Views,double,object_index) 

} // DeleteTrack::DeleteTrack(Views,double,object_index) 

void DeleteTrack::send(void) 

{ // void DeleteTrack::send(void) 

gvm::TacticalViewS v = dynamic_cast<gvm::TacticalViewS>(getView()); 

V. deleteTrack(getDest() ) ; 

} // void DeleteTrack::send(void) 

} // namespace gvm 


C.1.63. gvm/gvmExplosion.h 

/////////////////////////////////////////////////////////////////////////////// 
/! gvmExplosion.h - Class declaration for the gvm::Explosion class 
/////////////////////////////////////////////////////////////////////////////// 


♦ifndef EXPLOSION_H_INCLUDED 
♦define EXPLOSION_H_INCLUDED 

♦ include "gvitiMessage. h" 

♦include "gvmObject.h" 

♦include "spt/sptDefs.h" 

♦define GVM_Explosion GVM_UserMessage005 


namespace gvm 

{ // namespace gvm 

class View; // Forward declaration of the gvm::View class 

class Explosion : public Message 

{ // class Explosion : public Message 

protected: 

spt::vertex pos; // Location of the explosion 


public: 

Explosion(Views, double, object_index, spt::vertex); 


virtual void send(void); 

}; 

} 


// Deliver the message payload 
// class Explosion : public Message 
// namespace gvm 


♦endif 


C.1.64. gvm/gvmExplosion.cxx 

/////////////////////////////////////////////////////////////////////////////// 
// gvmExplosion.cxx - Class method definitions for the gvm::Explosion class 
/////////////////////////////////////////////////////////////////////////////// 


♦include "gvmExplosion.h" 
♦include "gvmView.h" 

♦include "gvmMunition.h" 

♦ifdef _TRACE 

♦define EXPLOSION_TRACE false 
♦endif 


namespace gvm 


371 








{ 


// namespace gvm 

Explosion:rExplosion(Views v, double t, object_index i, spt::vertex p) 

: Message(v, t, GVM_Explosion, i), pos(p) 

{ // Explosion::Explosion(Views,double,object_index,spt:ivertex) 

} // Explosion::Explosion(Views,double,object_index,spt::vertex) 

void Explosion::send(void) 

{ // void Explosion::send(void) 

Munitions mun = dynamic_cast<MunitionS>(getView()[getDest()]); 
mun.explode(pos); // The munition has exploded 

} // void Explosion::send(void) 

} // namespace gvm 


C.1.65. gvm/gvmGrid.h 

/////////////////////////////////////////////////////////////////////////////// 
// gvmGrid.h - This draws an m x n grid of size width x height centered at 
// ( 0 , 0 ) 

/////////////////////////////////////////////////////////////////////////////// 

#ifndef GVMGRID_H_INCLODED 
#define GVMGRID_H_INCLODED 

♦include "gvmShapeSD.h" 

♦define GVM_Grid GVM_UserObject004 

namespace gvm 

{ 

class View3D; 

class Grid : public Shape3D 

{ 

protected: 

ulong m, n; // Number of squares along x, y axis respectively 

double width, height; // Width and height of the grid 

public: 

Grid(View3DS,object_type,ulong,ulong,ulong,double,double); 

virtual void display(void); // Display the command post 

virtual bool isType(object_type); // Check if this is of type t 

}; // class Grid : public NewtonianMotion 

} 

♦endif 


C.1.66. gvm/gvmGrid.cxx 

/////////////////////////////////////////////////////////////////////////////// 
// gvmGrid.cxx - Method definitions for the Grid class 
/////////////////////////////////////////////////////////////////////////////// 

♦include <iostream> 

♦include <GL/glut.h> 

♦include "gvmGrid.h" 

♦include "gvmView3D.h" 

namespace gvm 
{ 

Grid::Grid(gvm::View3Di v, object_type t, ulong i, 
ulong M, ulong N, double W, double H) 

: Shape3D(v,t,i), m(M), n(N), width(W), height{H) 

{ // Grid::Grid(View3D&,object_type,ulong,ulong,ulong,double, double) 

} // Grid::Grid(ViewSDS,object_type,ulong,ulong,ulong,double, double) 

void Grid::display(void) 


372 






double dx = width/((double) m); 
double dy = height/((double) n); 
double lx = width/2.0; 
double ly = height/2.0; 

begin{); 

glPushAttrib(GL_CURRENT_BIT); 
glColor-3d(0.0, 1.0, 0.0, 0.2); 
glBegin(GL_LINES); 

for(double x=-lx; x<=lx; x+=dx) 

{ 

glVertex2d(x, -ly); 
glVertex2d(x, ly); 

} 

for(double y=-ly; y<=ly; y+=dy) 
{ 

glVertex2d{-lx, y); 
glVertex2d( lx, y); 

} 

glEnd(); 
glPopRttrib(); 
end{); 

} 


// void Grid::display(void) 
// Distance between adjacet x lines 
// Distance between adjacet y lines 
// Limits of travel in x direction 
// Limits of travel in y direction 


// Get the current point size 
// Set color to translucent green 


// Restore the color 
// void Grid::display(void) 


bool Grid::isType{object_type c) 

{ // bool Grid::isType(object_type) 

return (c==GVM_Grid || ShapeSD::isType(c)); // Return results 

} // bool Grid;:isType(object_type) 

} // namespace gvm 


C.1.67. gvm/gvmGround.h 

/////////////////////////////////////////////////////////////////////////////// 

// gvmGround.h - This draws the ground 

/////////////////////////////////////////////////////////////////////////////// 

#ifndef GVMGRODND_H_INCLUDED 
#define GVMGRODND_H_INCLUDED 

#include "gvmGrid.h" 

#define GVM_Ground GVM_UserObject005 

namespace gvm 

{ 

class View3D; 

class Ground : public Grid 

{ 

public: 

Ground(View3D&, ulong); // Class constructor 

virtual bool isType(object_type); // Check if this is of type t 

}; // class Ground : public Grid 

} 

#endif 


C.1.68. gvm/gvmGround.cxx 

/////////////////////////////////////////////////////////////////////////////// 
// gvmGround.cxx - Method definitions for the Ground class 
/////////////////////////////////////////////////////////////////////////////// 

#include <iostream> 

#include <GL/glut.h> 

#include "gvmGround.h" 
finclude "gvmView.h" 


373 







namespace gvm 
{ 


Ground::Ground{View3DS v, ulong i) 

: Grid(v,GVM_Ground,1,20,20,20000.0,20000.0) 

{ // Ground::Ground(View3DS, ulong) 

mode = GL_LINES; // Set the mode to polygon 

} // Ground::Ground(View3D&, ulong) 


bool Ground::isType(object_type c) 

{ // bool Ground::isType(Object_type) 

return (c==GVM_Ground || Grid::isType(c)); // Return results 

} II bool Ground::isType(Object_type) 

} // namespace gvm 


C.1.69. gvm/gvmMunition.h 

/////////////////////////////////////////////////////////////////////////////// 
// gvmMunition.h - This draws a tanjc round on the screen 
/////////////////////////////////////////////////////////////////////////////// 

#ifndef GVMMUNITION_H_INCLUDED 
#define GVMMUNITI0N_H_1NCLUDED 

#include "gvmNewtonianMotion.h" 

#include "spt/sptLinearMotion.h" 

#include "Random.h" 


#define GVM_Munition GVM_OserObject003 

namespace gvm 

{ 

class View3D; 


class Munition : public NewtonianMotion 


protected: 

std::vector<spt::LinearMotion> fragments; 
bool exploding; 
sodl::Random rnd; 
double eTime; 


// Motion of fragments 
// Are we exploding yet? 
// Random number generator 
// Explosion time 


public: 

Munition(gvm::View3D&, ulong); 


// Class constructor 


virtual void display(void); 
virtual bool isType(object_type); 
virtual void explode(spt::vertex); 


// Display the command post 
// Checlc if this is of type t 
// Explode the munition 


} 


}; 


// class Munition : public NewtonianMotion 


#endif 


C.1.70. gvm/gvmMunition.cxx 

/////////////////////////////////////////////////////////////////////////////// 
// gvmMunition.cxx - Method definitions for the Munition class 
/////////////////////////////////////////////////////////////////////////////// 

#include <iostream> 

#include <GL/glut.h> 

#include "gvmMunition.h" 

#include "gvmView3D.h" 

#define FRAGMENT_COaNT 500 

namespace gvm 
{ 

Munition::Munition(gvm::View3D& v, ulong i) 

: NewtonianMotion(v,GVM_Munition,i), fragments(FRAGMENT_COUNT) , 
exploding(false), eTime(0.0) 


374 








mode = GL LINES; 


// Munition::Munition(gvm::View3D&, ulong) 
// Set the mode to polygon 
// Munition::Munition(gvm::View3D&, ulong) 


void Munition::display(void) 

{ 

glPushAttrib{GL_POINT_BIT) ; 
glPushAttrib(GL_CURRENT_BIT); 
glPointSize(3.0); 


glColor3d(1.0, 1.0, 1.0); 


// void Munition::display(void) 
// Save the current point size 
// Save the current drawing color 
// Set the point size 

// Set the color of the projectile to white 


if ((exploding) // If we are on the initial flight to the target 

{ 

glColorSd(1.0, 1.0, 1.0); // Set the color of the projectile to white 

begin(); 

glBegin(GL_POINTS) ; // Want to do points 

glVertex3d(0.0, 0.0, 0.0); // Well, one of them any way 

glEndO; // That's it 

end(); // All done here 

} // if ((exploding) 

else // If we're doing the explosion now 

{ 

double t=getview().getTime(); // Get the current time 

double dt = t-eTime; // Difference between current & explosion times 


// Set the color of the projectile to white 

// Want to do points 
: // Well, one of them any way 

// That's it 
// All done here 
//if ((exploding) 
// If we're doing the explosion now 


if (dt >= 5.0) setActive(false); // Tu 

double alpha = 1.0-(dt/5.0); // Specify al 

glEnable(GL_BLEND); 

glColor4d(1.0, 1.0, 1.0, alpha); 

glBegin(GL_POINTS); 

for (ulong i=0; i<fragments.size(); ++i) 

{ 

while (t>fragments[i].getStopTime()) 

{ 

double stop = fragments[i].getStopTime(); 


// Turn off after 5 sim-seconds 
// Specify alpha component of fragments 
// Enable alpha blending 
// Set new color 
// Draw the particles 
(); ++i) // Loop over all fragments 


// If we're doing a bounce 
// Get current stop time 


spt::vertex p = fragments[i].Ip(stop); // Get position at stop time 
spt::vertex v = fragments[i].Iv(stop); // Get velocity at stop time 


spt::vertex a = fragments[i].la(stop); 

V *= 0.9; 
v[2] = -v[2); 

double newStop = stop-2.0*v[2]/a[2]; 
fragments[i].setLM(p, v, a, stop, newStop); 


// Get acc at stop time 
//Energy loss 
// Bounce 
// Get next bounce time 
// Set next leg 


} // if (t>fragments[i].getStopTime0) 

spt::vertex pos = fragments[i].Ip(t); // Get fragment current pos 

glvertex3d(pos[0], pos[l], pos[2]); // Draw the fragment 

} // for (ulong i=0; i<fragments.size(); ++i) 

glEndO; // glBegin (GL_POLYGON) 

glDisable(GL_BLEND); // Disable alpha blending 

} // else from if ((exploding) 

glPopAttrib(); // Restore the color 

glPopAttrib(); // Restore the point attributes 

// void Munition::display(void) 


void Munition::explode(spt::vertex p) 

{ 

spt::vertex dv{0.0, 3) ; 
spt::vertex a(0.0, 3); 
a[2] = -9.8; 


// void Munition: : explode (spt:: vertex) 
// Delta from the main velocity vector 

// Acceleration 
// Due to gravity 


exploding = true; // We are now exploding; 

eTime = getview().getTime(); // Save the explosion time 

for (ulong i=0; i<fragments.size(); ++i) // Loop over all of the fragments 

{ 

double theta = rnd.nextDouble(0, 2.0*PI); 
double phi = rnd.nextDouble(0.0, PI); 
double rad = rnd.nextDouble(0, 20.0); 
dv[0] = rad*cos(phi)*cos(theta); 
dv[l] = rad*cos(phi)*sin(theta); 
dv[2) = rad*sin(phi)+5.0; 
double stop = eTime-2.0*(dv[2])/a[2]; 
fragments[i].setLM(p, dv, a, eTime, stop); 


375 







} 


// for (ulong i=0; i<fragments.size{); ++i) 
// void Munition::explode{spt::vertex) 


} 


} 


bool Munition::isType(object_type c) 

{ // bool Munition::isType(object_type) 

return (c==GVM_Munition || NewtonianMotion::isType(c)); // Return results 
} // bool Munition::isType(object_type) 

// namespace gvm 


C.1.71. gvm/gvmNewtonianMotion.h 

/////////////////////////////////////////////////////////////////////////////// 
// gvmNewtonianMotion.h - This draws a command post on the screen 
/////////////////////////////////////////////////////////////////////////////// 

#ifnde f GVMNEWTONIANMOTION_H_INCLUDED 
#de fine GVMNEWTONIANMOT10N_H_1NCLDDED 

#include "gvmShapeSD.h" 

#include "spt/sptNewtonianMotion.h" 

tdefine GVM_NewtonianMotion GVM_UserObjectOOO 

namespace gvm 

{ 

class View3D; 

class NewtonianMotion : public ShapeSD 

{ 

protected: 

NewtonianMotion(gvm::View3D&, object_type, ulong); // Class constructor 
spt::NewtonianMotion nm; // Newtonian motion parameters 

public: 

virtual void setNM(const spt::NewtonianMotions); // Update parameters 

virtual void begin(void); // Begin displaying the object 

virtual void end(void); // We're all finished with the object 

virtual bool isType(object_type); // Check if this is of type t 

}; // class NewtonianMotion : public ShapeSD 

} 

#endif 


C.1.72. gvm/gvmNewtonianMotion.cxx 

/////////////////////////////////////////////////////////////////////////////// 
// gvmNewtonianMotion.cxx - Method definitions for the NewtonianMotion class 
/////////////////////////////////////////////////////////////////////////////// 

tinclude <GL/glut.h> 

#include "gvmNewtonianMotion.h" 

#include "gvmView3D.h" 

#include <math.h> 

#include "spt/sptDefs.h" 

namespace gvm 

{ 

NewtonianMotion::NewtonianMotion(gvm::View3D& v, object_type t, ulong i) 

: ShapeSD(v, t, i) 

{ // NewtonianMotion::NewtonianMotion(gvm::View3D&, ulong) 

} // NewtonianMotion: :NewtonianMotion{gvm: :View3D6i, ulong) 

void NewtonianMotion::begin(void) 

{ // void NewtonianMotion::begin(void) 

double time = getview().getTime(); // Get current time 

spt::vertex pos = nm.lp(time); II Get current position 

spt::vertex rot = 180.0*nm.ap(time)/PI; // Get orientation 

glPushMatrix(); // Push the current matrix 

glTranslated(pos [0], pos[l], pos[2]); // Move to the proper position 

glRotated(rot[0], 1.0, 0.0, 0.0); // Roll 


376 








glRotated(rot[1], 0.0, 1.0, 0.0); // Pitch 

glRotated(rot[2], 0.0, 0.0, 1.0); // Yaw 

ShapeSD::begin(); // Call the parent version of the begin method 

} // void NewtonianMotion::begin(void) 


void NewtonianMotion::end(void) 


ShapeSD::end(); 
glPopMatrix(); 


} 


// void NewtonianMotion::end(void) 
II Call the parent version of the end method 

// Pop the matrix 
// void NewtonianMotion::end(void) 


void NewtonianMotionsetNM(const spt::NewtonianMotion& n) 

{ // void NewtonianMotion::setNM(const spt::NewtonianMotion&) 

nm=n; // Set the newtonin motion paramters 

} // void NewtonianMotion: : setNM (const spt: :NewtonianMotionSi) 

bool NewtonianMotion::isType(object_type c) 

{ // bool NewtonianMotion::isType(Object_type) 

return (c==GVM_NewtonianMotion || ShapeSD::isType(c)); // Return results 

} II bool NewtonianMotion::isType(object_type) 

} // namespace gvm 


C.1.73. gvm/gvmSetNewtonianMotion.h 


/////////////////////////////////////////////////////////////////////////////// 
II gvmSetNewtonianMotion.h - Class declaration for the 
II gvm::SetNewtonianMotion class 

/////////////////////////////////////////////////////////////////////////////// 

#ifndef SETNEWTONIANPOSITION_H_INCLODED 
#define SETNEWTONIANPOSITION_H_INCLODED 

♦include "gvmMessage.h" 

♦include "gvmObject.h" 

♦include "spt/sptNewtonianMotion.h" 

♦define GVM_SetNewtonianMotion GVM_UserMessage001 


namespace gvm 
( 

class View; 


// namespace gvm 
II Forward declaration of the gvm::View class 


class SetNewtonianMotion : public Message 

{ // class SetNewtonianMotion : public Message 

protected: 

spt::NewtonianMotion nra; // Netonian motion parameters 

public: 

SetNewtonianMotion(Views, double, object_index, 
const spt::NewtonianMotionS); 

SetNewtonianMotion(Views, double, message_type, object_index, 
const spt::NewtonianMotionS); 


virtual void send(void), 


}; 


II Deliver the message payload 
// class SetNewtonianMotion : public SetMotion 

U namespace gvm 


♦endif 


C.1.74. gvm/gvmSetNewtonianMotion.cxx 

/////////////////////////////////////////////////////////////////////////////// 
// gvmSetNewtonianMotion.cxx - Class method definitions for the 
// gvm::SetNewtonianMotion class 

/////////////////////////////////////////////////////////////////////////////// 

♦include "gvmSetNewtonianMotion.h" 

♦include "gvmView.h" 

♦include "gvmNewtonianMotion.h" 

namespace gvm 


377 







// namespace gvm 


SetNewtonianMotion::SetNewtonianMotion(Views v, 

double t, 
object_index i, 

const spt::NewtonianMotions n) 

: Message (v, t, GVM_SetNewtonianMotion, i) , nin(n) 

{ // SetNewtonianMotion::SetNewtonianMotion(Views, double, ... ) 

} // SetNewtonianMotion::SetNewtonianMotion(Views, double, ... ) 

SetNewtonianMotion:;SetNewtonianMotion(Views v, 

double t, 
message_type ty, 
object_index i, 

const spt::NewtonianMotions n) 

: Message (V, t, ty, i), nm(n) 

{ // SetNewtonianMotion::SetNewtonianMotion(Views, double, ... ) 

} // SetNewtonianMotion::SetNewtonianMotion(Views, double, ... ) 

void SetNewtonianMotion::send(void) 

{ // void SetNewtonianMotion::send(void) 

NewtonianMotion* m=dynamic_cast<NewtonianMotion*>(SgetView()[getDest()]); 
if (m!=NtJLL) m->setNM (nm) ; // Set destination Newtonian motion parameters 

} // void SetNewtonianMotion::send(void) 

} // namespace gvm 


C.1.75. gvm/gvmSetTankState.h 

/////////////////////////////////////////////////////////////////////////////// 
// gvmSetTanlcState.h - Class declaration for the gvm::SetTankState class 
/////////////////////////////////////////////////////////////////////////////// 

#ifndef SETTANKSTATE_H_INCLUDED 
#define SETTANKSTATE__H_INCLUDED 

#include "gvmMessage.h" 

♦include "gvmObject.h" 

♦define GVM_SetTankState GVM_UserMessageOOO 
namespace gvm 

{ // namespace gvm 

class View; // Forward declaration of the gvm::View class 

class SetTankState : public Message 
{ 

private: 
double az; 
double azRate; 
double azStart; 
double azStop; 
double el; 
double elRate; 
double elStart; 
double elStop; 

public: 

SetTankState(Views, double, object_index, double, double, double, double, 
double, double, double, double); 

virtual void send(void); // Deliver the message payload 

}; // class SetTankState : public Message 

} // namespace gvm 

♦endif 


// class SetTankState : public Message 

// Gun azimuth 
// Azimuth slew rate 
// Azimuth slew start time 
// Azimuth slew start time 
// Gun elevation 
// Elevation slew rate 
// Elevation slew start time 
// Elevation slew start time 


C.1.76. gvm/gvmSetTankState.cxx 

/////////////////////////////////////////////////////////////////////////////// 
// gvmSetTankState.cxx - Class method definitions for the gvm::SetTankState 
// class 

/////////////////////////////////////////////////////////////////////////////// 


378 







#include "gvmSetTankState.h" 

#include "gvmView.h" 

#include "gvmTank.h" 

#ifdef _TRACE 

#define SETTANKSTATE_TRACE false 
#endif 

namespace gvm 

{ // namespace gvm 

SetTankState::SetTankState(Views v, 

double t, 
object_index i, 
double a, 
double ar, 
double asO, 
double asl, 
double e, 
double er, 
double esO, 
double esl) 

: Message(v, t, GVM_SetTankState, i), az(a), azRate(ar), azStart(asO), 
azStop{asl), el(e), elRate(er), elstart(esO), elStop(esl) 

{ // SetTankState::SetTankState(Views, double, object_index, GLdouble, ... ) 

} // SetTankState::SetTankState(Views, double, object_index, GLdouble, ... ) 

void SetTankState::send(void) 

{ // void SetTankState::send(void) 

gvm;:Tank st = dynamic_cast<gvm: :Tanks> (getViewO [getDest () ]) ; 
t.setTank(az, azRate, azStart, azStop, el, elRate, elStart, elStop); 

} // void SetTankState::send(void) 

} // namespace gvm 

C.1.77. gvm/gvmTacticalGrid.h 

/////////////////////////////////////////////////////////////////////////////// 
// gvitiTacticalGrid.h - This draws the ground 
/////////////////////////////////////////////////////////////////////////////// 

#ifnde f GVMTACTICALGRID_H_INCLUDED 

#define gvmtacticalgrid_h_included 

tinclude "gvmGrid.h" 

#define GVM_TacticalGrid GVM_UserObject006 

namespace gvm 

{ 

class ViewlD; 

class TacticalGrid : public Grid 

( 

public: 

TacticalGrid(ViewSDS, ulong) ; // Class constructor 

virtual bool isType(object_type); // Check if this is of type t 

); // class TacticalGrid ; public Grid 

} 

#endif 

C.1.78. gvm/gvmTacticalGrid.cxx 

/////////////////////////////////////////////////////////////////////////////// 
// gvmTacticalGrid.cxx - Method definitions for the TacticalGrid class 
/////////////////////////////////////////////////////////////////////////////// 

#include <iostream> 

#include <GL/glut.h> 
tinclude "gvmTacticalGrid.h" 


379 






#include "gvmView.h 


namespace gvm 
{ 

TacticalGrid::TacticalGrid(View3D& v, ulong i) 

: Grid(v,GVM_TacticalGrid,i,20,20,20000.0,20000.0) 

{ II TacticalGrid::TacticalGrid(View3DS, ulong) 


mode = GL LINES; 


// Set the mode to polygon 


} 


II TacticalGrid::TacticalGrid{View3D&, ulong) 


bool TacticalGrid::isType(object_type c) 

{ II bool TacticalGrid::isType(Object_type) 

return (c==GVM_TacticalGrid I I Grid::isType(c)); // Return results 

) // bool TacticalGrid::isType(object_type) 

} // namespace gvm 


C.1.79. gvm/gvmTacticalView.h 

/////////////////////////////////////////////////////////////////////////////// 
// gvmTacticalView.h - Defines the gvm::TacticalView class in which all 
// gvm::Object instances are viewed. 

/////////////////////////////////////////////////////////////////////////////// 

#ifnde f GVMTACTICALVIEW_H_INCLODED 
#define GVMTACTICALVIEW_H_INCLODED 

#include "spt/sptEnvironmentObject.h" 

#include "gvmViewSD.h" 

#include "gvmTrack.h" 
finclude <vector> 


namespace gvm 


class TacticalGrid; 


// namespace gvm 


class Tacticalview : public View3D 

{ // class Tacticalview : public View3D 

protected: 

GLint width, height; // Width and height of the tactical view port 

TacticalGrid *grid; // Refrence grid 

std::vector<Track> trackList; // List of the tracks 


public: 

Tacticalview(void); 


// Class constructor 


virtual void reshape(int, int); 


II Callback for window resizing 


virtual void createObject(object_handle); // Schedule object creation 

virtual void begin(void); // Start rendering the scene graph 

virtual void display(void); // Display the tracks 

virtual void end(void); // Stop rendering the scene graph 

virtual bool isDisplay(void); // Should we update the display? 

virtual void addTrack(object_index, spt::NewtonianMotion, double, ulong); 
virtual void changeTrack(object_index, spt::NewtonianMotion); 
virtual void deleteTrack(object_index); 

// class Tacticalview : public View3D 
// namespace gvm 


#endif 


C.1.80. gvm/gvmTacticalView.cxx 

/////////////////////////////////////////////////////////////////////////////// 
// gvmTacticalView.cxx - Class member and static definitions of the 
// gvm::Tacticalview class. 

/////////////////////////////////////////////////////////////////////////////// 

#ifdef _TRACE 

#define TACTICALVIEW_TRACE false 
#endif 


380 






finclude "Exception.h" 
#include "gvinTacticalView.h" 
#include "gvmTacticalGrid.h" 
#include "EngineStand.h" 


namespace gvm 
{ 

TacticalView::TacticalView(void) 

{ 

setSceneChange(true); 
redisplay(); 

GLint vp[4]; 

glGetIntegerv{GL_VIEWPORT, vp); 
widtli = vp[2]; laeight = vp[3]; // Get 

grid = new TacticalGrid(*this, 0); 
grid->setColor(0.0, 0.0, 1.0, 1.0); 


// namespace gvm 
// Window for ttie view 
// TacticalView::TacticalView(void) 
// Need to refresli display 
// Redisplay the environment 
// Viewport parameters 
// Get the window viewport params 
the height and width of the viewport 
// Add the grid 
// Set the grid color 


for (ulong i=0; i<100; ++i) // Allocate an initial 100 trades 

{ 

trac]cList .push_bacl<; (gvm: :Trac)c (*this, i, -1.0, NEUTRAL) ) ; 

tracjcList .bade (). setActive {false) ; // Set the active flag 

} // while (tradcList. size {) <=i) 

} // TacticalView::TacticalView(void) 


void TacticalView::reshape(int w, int h) 

{ // void TacticalView::reshape(int, int) 

View3D::reshape(w,h); 
width = w; height = h; 
redisplay(); 

} // void TacticalView::reshape(int, int) 


void TacticalView::createObject(object_handle h) 

{ // void TacticalView::createObject(object_handle) 

} // void TacticalView::createObject(object_handle) 


void TacticalView::begin(void) 

glutSetWindow(window); 
glMatrixMode(GL_PROJECTION) ; 
glLoadldentity(); 
gluPerspective(90.0, aspect, 
glMatrixMode(GL_MODELVIEW); 
glLoadldentity{); 
glBlendFunc(GL_SRC_ALPHA, GL 
glRotated(-90.0, 0.0, 0.0, 1. 
glRotated(180.0, 0.0, 1.0, 0, 
glTranslated(0.0, 0.0, 10500. 

} 


// void TacticalView::begin(void) 
// Set the window 
// Establish a projection view 
// Load the identity matrix 
9000.0, 11000.0); // Establish perspecive 

// Extablish MODELVIEW 
// Load another identity matrix 
_ONE_MINOS_SRC_ALPHA); // How to blend 

0); // Get it corectly positioned 

0); // Get it corectly positioned 

0); // Position the view 

// void TacticalView::begin(void) 


void TacticalView::display(void) 

{ // void TacticalView::display(void) 

if (isDisplayO || sodl:: EngineStand: : stand, holding {) ) // Update the scene? 
( 

begin 0; // Let derived classes do what they need 

GLboolean smooth = glIsEnabled(GL_POINT_SMOOTH); // Setting to restore 

if ((smooth) glEnable(GL_POINT_SMOOTH); // Create smooth points 

glPointSize(ptSize); // Set the point size 


for (ulong i=0; KtraclcList. size (); ++i) traclcList [i] . displaySensor () ; 
grid->display(); // Display the grid 

for (ulong i=0; i<traclcList.size () ; ++i) traclcList[i].displayTraclc(); 

setRefresh(sodl::EngineStand::stand.holding{)); // Set the refresh value 

setSceneChange(false); // The scene has not changed 

if ((smooth) glDisable(GL_POINT_SMOOTH); // Create smooth points 

end(); // Let derived classes do the cleanup 

} // if (isDisplay) 

} // void TacticalView::display(void) 


void TacticalView::end(void) 

{ // void TacticalView::end(void) 


381 







glutSwapBuffers(); // Swap the buffers 

glClear(GL_COLOR_BOFFER_BIT 1 GL_DEPTH_BDFFER_BIT); // Reset the buffer 

} // void TacticalView::end(void) 


void TacticalView: : addTraclc {object_index i, // Track ID # 

spt::NewtonianMotion nm, // Motion parameters 

double r, // Sensor radius 

ulong f) // Force indicator 

{ // void TacticalView::addTrack(object_index,spt:zNewtonianMotion,...) 

trackList[i].setTrack(r, f); // Set the track parameters 

trackList[i].setNM(nm); // Set the motion parameters 

trackList[i].setActive(true); // Turn the track on 

} II void TacticalView::addTrack(object_index,spt:zNewtonianMotion,...) 


void TacticalView::changeTrack(object_index i, // Track ID # 

spt:zNewtonianMotion nm) // Motion parameters 
{ // void TacticalView:zchangeTrack(object_index,spt:ZNewtonianMotion) 

if (trackList[i].isActive()) trackList[i].setNM(nm); // If it's active, set 
} // void TacticalView::changeTrack{object_index,spt:zNewtonianMotion) 


void TacticalView::deleteTrack{object_index i) // Track ID # 

( // void TacticalView:zdeleteTrack(object_index) 

trackList[i].setActive(false); // Set the track parameters 

} // void TacticalView::deleteTrack(object_index) 


bool TacticalView::isDisplay(void) 

{ 

return (isVisible &s refresh); 

} 


// bool TacticalView::isDisplay(void) 
// Should we redisplay 
// bool TacticalView: .'isDisplay (void) 


C.1.81. gvm/gvmTank.h 

/////////////////////////////////////////////////////////////////////////////// 
II gvmTank.h - This draws a tank on the screen 
/////////////////////////////////////////////////////////////////////////////// 

#ifndef GVMTANK_H_INCLUDED 
♦define GVMTANK_H_INCLUDED 

♦include "gvmNewtonianMotion.h" 

♦include "gvmCube.h" 

♦include "gvmCylinder.h" 

♦include "gvmSphere.h" 

♦define GVM_Tank GVM_UserObjectOOl 
♦define LINE_ABREAST 0 
♦define V_FORMATION 1 
♦define FORWARD_SWEEP 2 
♦define COLUMN 3 


namespace gvm 

{ 

class View3D; 

class Tank : public NewtonianMotion 

{ 

protected: 
double az; 
double azRate; 
double azStart; 
double azStop; 
double el; 
double elRate; 
double elStart; 
double elStop; 

Cylinder gun; 

Sphere turret; 

Cube body; 


II Gun azimuth 
// Azimuth slew rate 
// Azimuth slew start time 
// Azimuth slew start time 
// Gun elevation 
II Elevation slew rate 
// Elevation slew start time 
// Elevation slew start time 

// Tank gun 
II Tank turret 
// Tank body 


382 






public: 

Tank(gvm::View3DS, ulong); 


// Class constructor 


virtual double getAzimuth(double); // Get the current azimuth 

virtual double getElevation(double); // Get the current elevation 

virtual void display(void); // Display the tank 

virtual void setTank(double,double,double,double,double,double,double, 
double); 

virtual bool isType(object_type); // Check if this is of type t 

// class Tank : public NewtonianMotion 


#endif 


C.1.82. gvm/gvmTank.cxx 


/////////////////////////////////////////////////////////////////////////////// 
// gvmTank.cxx - Method definitions for the Tank class 
/////////////////////////////////////////////////////////////////////////////// 

#include <iostream> 

#include <GL/glut.h> 

#include "gvmTank.h" 

#include "gvmViewSD.h" 

double deg{double r) { return 180.0*r/PI; } 
double rad(double d) { return PI*d/180.0; } 


namespace gvm 

{ 

Tank::Tank(gvm::View3D4 v, ulong i) 

: NewtonianMotion(v,GVM_Tank,i), a2(0.0), azRate(O.O), azStart(0.0), 
azStop(O.O), el(O.O), elRate(O.O), elStart(0.0) , elStop(O.O), gun(v,i), 
turret(v,i), body{v,i) 


mode = GL_LINES; 

gun.set (0.5, 15.0, 10, 10); 

gun.setMode(GL_LINES); 

turret.set(1.5, 10, 10); 

turret.setMode(GL_LINES); 

body.set(1.0); 

body.setMode(GL_LINES); 


void Tank::display(void) 

{ 

double t = getview().getTime(); 

begin{); 
glPushMatrix(); 

glTranslated(0.0, 0.0, 1.0); 
glPushMatrix(); 

glScaled(15.0, 10, 1.0); 
body.display{); 
glPopMatrix(); 

glPushMatrix(); 

glScaleddO.O, 7.5, 2.0); 
body.display(); 
glPopMatrix(); 


// Tank::Tank(gvm::View3D&, ulong) 
// Set the mode to polygon 


// Tank::Tank(gvm::View3D&, ulong) 


// void Tank::display(void) 
// Get current time for display purposes 

II Perform the setup 

// Move tank above the ground 

// Outer, thinner portion of tank body 
// Display the body 


// Inner, fatter portion of the tank body 
II Display that part 


glPushMatrix(); 

glTranslated(2.0, 0.0, 2.25); // Move turret assy forward from center 

glRotated(deg(getAzimuth(t)),0.0,0.0,1.0); // Set turret azimuth 

glRotated(90.0-deg(getElevation(t)),0.0,1.0,0.0); // Set gun elevation 

turret.display 0; // Display the turret portion 

glPushMatrix(); 

glTranslated(0.0, 0.0, 7.5); // Change the center of the gun 

gun.display 0; // Display the gun 

glPushMatrix(); 

glTranslated(0.0, 0.0, 7.5); // Final embelishment on end of gun 


383 







glScaled(1.0, 1.5, 0.05); II Slightly wider than high 

gun.display 0; // Redisplay the cylinder 

glPopMatrix(); 
glPopMatrix(); 
glPopMatrix{); 
glPopMatrix() ; 
end () ; 

} // void Tank::display(void) 


double Tank::getAzimuth(double t) 

{ 

double dt = min(azStop, t)-azStart; 
double rv = fmod(az+azRate*dt, 2.0*PI); 
return (rv < 0.0 ? rv+2.0*PI : rv); 


// Effective time of azimuth 
// double Tank::getAzimuth(double) 
// Get the elapsed time 
// Get the raw azimuth position 
// Return in range [0, 2*PI) 
// double Tank::getAzimuth(double) 


double Tank::getElevation(double t) // Effective time of elevation 

{ // double Tank::getElevation(double) 

double dt = min(elStop, t)-elStart; // Get the elapsed time 

double rv = fmod{el+elRate*dt, 2.0*PI); // Get the raw elevation position 

return (rv < 0.0 ? rv+2.0*PI : rv); // Return in range [0, 2*PI) 

} // double Tank::getElevation(double) 


void Tank::setTank(double a, double ar, double asO, double asl, 

double e, double er, double esO, double esl) 

{ // void Tank::setTanl(double,double,double,double,double,double, ... ) 

az = a; azRate = ar; azStart = asO; azStop = asl; 

el = e; elRate = er; elstart = esO; elStop = esl; 

getview().setSceneChange(true); // Don't need to refresh again 

} II void Tank::setTanl(double,double,double,double,double,double, ... ) 


} 


bool Tank::isType(object_type c) 

{ // bool Tank::isType(object_type) 

return (c==GVM_Tank || NewtonianMotion::isType(c)); // Return results 

} // bool Tank::isType(object_type) 

// namespace gvm 


C.1.83. gvm/gvmTrack.h 

/////////////////////////////////////////////////////////////////////////////// 
// gvmTrack.h - This draws a tank on the screen 
/////////////////////////////////////////////////////////////////////////////// 

#ifndef GVMTRACK_H_INCLODED 
ttdefine GVMTRACK_H_INCLUDED 

#include "gvmNewtonianMotion.h" 

#define GVM_Track GVM_UserObjectOO? 

namespace gvm 
{ 

class View3D; 

class Track : public NewtonianMotion 
{ 

protected: 

double radius; 
ulong force; 

static std::vector<std::pair<double, double> > disc; 
public: 

Track(gvm::View3D&, ulong, double, ulong); // Class constructor 

virtual void displayTrack(void); // Display the track 

virtual void displaySensor(void); II Display sensor range information 

virtual void setTrack(double, ulong); // Set the track parameters 

virtual bool isType(object_type); // Check if this is of type t 

}; II class Track : public NewtonianMotion 

} 


II Sensor radius 
// Force indicator 
II Disc elements 


384 






#endif 


C. 1.84. gvm/gvmTrack.cxx 

/////////////////////////////////////////////////////////////////////////////// 
// gvmTrack.cxx - Method definitions for the Track class 
/////////////////////////////////////////////////////////////////////////////// 

#include <iostream> 

#include <GL/glut.h> 

#include "gvmTrack.h" 

#include "gvmViewSD.h" 

#include "spt/sptEnvironmentObject.h" 

♦define DISC SIZE 64 


namespace gvm 

{ 

std::vector<std::pair<double, double> > Track::disc; 

Track::Track(gvm::View3D& v, ulong i, double r, ulong f) 

: NewtonianMotion(v,GVM_Track,i), radius(r), force(f) 

{ // Track::Track(gvm::View3D&, ulong) 

mode = GL_LINES; // Set the mode to polygon 

if (disc.empty0) // If the disc components have not yet been initialized 

( 

double dt = 2.0*PI/((double) DISC_SIZE); // Delta theta 

for (double theta=0; theta<2.0*PI; theta+=dt) // loop over disc 

disc.push_back(make_pair(cos(theta), sin(theta))); // Get each point 

} // if (disc.empty0) 

if (force == BLUE) setColor(0.0, 0.0, 1.0, 1.0); 
else if (force == RED) setColor(1.0, 0.0, 0.0, 1.0); 
else setColor(1.0, 1.0, 1.0, 1.0); 

} // Track::Track{gvm;:View3Di, ulong) 

void Track::displayTrack(void) 

if (isActive()) 

{ 

begin(); 

glBegin(GL_QUADS); 

glVertex2d(200.0, 0.0); 
glVertex2d(0.0, -50.0); 
glVertex2d(-50.0, 0.0); 
glVertex2d(0.0, 50.0); 
glEnd(); 
end(); 

} 

} 

void Track::displaySensor(void) 

{ 

if (isActive 0 &.& radius>0.0) 

{ 

ulong i; // For loop index variable 

GLdouble dred = (force == BLUE ? 0.0 : 0.5); 

GLdouble dgreen = (force == RED I I force == BLUE ? 0.0 : 0.5); 

GLdouble dblue = (force == RED ? 0.0 : 0.5); 


// void Track::displayTrack(void) 
// If this is an active track 

// Perform the setup 


// Stop displaying this track 
// if (isActive()) 
// void Track::displayTrack(void) 


// void Track::displaySensor(void) 
// If this is an active track 


} 


begin(); 

glEnable(GL_BLEND); 

glColorid(dred, dgreen, dblue, 0.4) 
glBegin(GL_POLYGON); 
for(i=0; Kdisc. size () ; ++i) 

glVertex2d(radius*disc[i].first, 
glEnd(); 

glDisable(GL_BLEND); 
end () ; 


// Perform the setup 
II Enable alpha blending 
; // Set new color 

// Draw the filled portion 
// Loop over the points in the disc 
radius*disc[i].second); 

// glBegin(GL_POLYGON) 
// Disable alpha blending 
// Stop displaying this track 
// if (isActive()) 
// void Track::displaySensor(void) 


385 






void Track::setTrack(double r, ulong f) 

{ // void Track::setTrack(double, ulong) 

radius = r; 
force = f; 

if (force == BLUE) setColor(0.0, 0.0, 1.0, 1.0); 
else if (force == RED) setColor(1.0, 0.0, 0.0, 1.0); 
else setColor(1.0, 1.0, 1.0, 1.0); 

} // void Track::setTrack(double, ulong) 


} 


bool Track::isType(Object_type c) 

{ // bool Track::isType(object_type) 

return (c==GVM_Track || NewtonianMotion::isType(c)); // Return results 

) // bool Track::isType(object_type) 

// namespace gvm 


C.1.85. spt/sptAngularMotion.h 

{import process {NewtonianMotion) } 

{import message {AddTrack, ChangeTrack, LoseTrack, AddEnvironment, 

SetEnvironment, SetNewtonianMotion, Impact, Destroyed) } 
{import spt {sptEnvironmentObject, sptNewtonianMotion} } 

{import std {<vector>} } 


{ 


process:SensorTrack(NewtonianMotion) 

{ 

ulong:envindex((ulong) (-1)); 

process:environment; 

process:parent; 

double:radius(2000.0); 

ulong:force(NEUTRAL); // 

spt::NewtonianMotion:tracks[]; 

std::set<ulong>:active; 


// process:SensorTrack(NewtonianMotion) 
// Index within the environment 
// Environment process 
// Parent object to report back to 
// Sensor Radius 
Initially neutral, until we know better 
// Collection of known tracks 
// Collection of active tracks 


method:isActive(public; bool; ulong:i;) // Is track i active? 

{ return active.find(i)!=active.end(); } 


method;notify(public; void; std::vector<SetNewtonianMotion>s:out;) 

{ // method:notify(public; void; std::vector<SetNewtonianMotion>&:out;) 

NewtonianMotion::notify(out); // Call the parent version 

if (envindex != ((ulong) -1)) // If we have registered with environment 

{ 

out.push_back(me); // Allocate a new message 

out.back().addDest(environment); // Add environment as dest 

out.back().addDest(parent); // Add environment as dest 

out.back().index = envindex; // Specify the index 

out.back().set(nm); // Specify the Linear Motion paramters 

} // if (envindex != ((ulong) -1)) 

} // method:notify(public; void; std::vector<SetNewtonianMotion>&:out;) 


mode:Default 

{ // mode:Default 

node:addEnvironment{AddEnvironment:in] 

{SetNewtonianMotion:out=>(parent;)] 

{ // Node:addEnvironment[AddEnvironment:in][...] 

envindex = in.index; // Save the environment index 

out.index = envindex; // Notify parent of new index 

out.set(nm); // Set the motion parameters 

} // Node:addEnvironment[AddEnvironment:in][...] 


node:setEnvironment[SetEnvironment:in][] 

{ // node:setEnvironment[SetEnvironment:in][] 

environment = in.environment; // Environment in which this exists 

parent = in.getSource(); // Save the parent process handle 

} // node:setEnvironment[SetEnvironment:in][] 


node:addTrack[AddTrack:in][AddTrack:out=>(parent;)] 

{ // node:addTrack[AddTrack:in][AddTrack:out] 

if (tracks.size() <= in.getTrackO) // If tracks isn't big enough 


386 







tracks.resize(in,getTrack{)+!); 


// Resize the track list 


tracks[in.getTrack0] = in.getMotion(); // Save the motion paramter 

active.insert(in.getTrack()); // Add an active element 

out.setMotion(in.getMotion0); // Notify as to object motion 

out.set(in.getTrack0, in.getForce()); // Provide force tracking 

} // node:addTrack[AddTrack:in][AddTrack:out] 

node:changeTrack[ChangeTrack:in][ChangeTrack:out=>(parent;)] 

{ // node:changeTrack[ChangeTrack:in][ChangeTrack:out] 

tracks[in.getTrack0] = in.getMotion(); // Save the motion paramter 

out.setMotion(in.getMotion0); // Notify as to object motion 

out.set(in.getTrack0, in.getForce{)); // Provide force tracking 

] n node:changeTrack[ChangeTrack:in][ChangeTrack:out] 

node:loseTrack[LoseTrack:in][LoseTrack:out=>(parent;)] 

{ // node:loseTrack[LoseTrack:in][LoseTrack:out] 

active . erase (in. getTrack 0) ; // Track is no longer active 

out.set(in.getTrack0,in.getForce0); // Provide force information 

} // node:loseTrack[LoseTrack:in][LoseTrack:out] 

node:impact[Impact:in] // We've been hit 

[Destroyed:out[], // Notify processes of our destruction 

LoseTrack:It[]=>(parent;), // Lost all the tracks 

SetNewtonianMotion:snm[]] // Notify of new newtonian motion 

{ // node:impact[Impact:in][Destroyed:out[ 1 1 

nm.la(0.0, 0.0, 0.0, getTime()); // Stop linear acceleration 

nm.lv(0.0, 0.0, 0.0, getTime()); // Stop linear motion 

nm.aa(0.0, 0.0, 0.0, getTimeO); // Stop angular acceleration 

nm.av(0.0, 0.0, 0.0, getTimeO); // Stop angular motion 

notify(snm); // Notify views/environments about new newtonian motion 

std::map<process, gvm::object_index>::iterator i; // For loop index 

for (i=views.begin(); i!=views.end(); ++i) // Loop over index map 

( 

out.push_back(me); // Allocate a new message 

out.back().addDest(i->first); // Add this view as a destination 

out.back().index = i->second; // Specify the index 

} // for (i=views.begin0; i!=views.end(); ++i) 

if (envindex != ((ulong) -1)) // If we have registered with environment 


out.push_back(me); 
out.back().addDest(environment); 
out.back().addDest(parent); 
out.back().index = envindex; 


II Allocate a new message 
// Add environment as dest 
// Add environment as dest 
// Specify the index 
// if (envindex != ((ulong) -1)) 


std::set<ulong>::iterator t; // Index for active tracks 

for (t=active.begin0; t!=active.end(); ++t) // Loop over active tracks 
{ 

It.push_back(me); // Create a new LoseTrack message 

It.back().set(*t, (force==RED ? BLUE : RED)); // tell of lost tracks 
} // for (tractive.begin 0; t!=active.end(); ++t) 

// node:impact[Impact:in][Destroyed:out[]] 

// mode:Default 
// process:SensorTrack(NewtonianMotion) 


C.1.86. spt/sptAngularMotion.cxx 


/////////////////////////////////////////////////////////////////////////////// 
// sptAngularMotion.cxx - Class definition for the spt::AngularMotion class 
// used to track objects. 

/////////////////////////////////////////////////////////////////////////////// 

#include "sptAngularMotion.h" 

#define END TIME le307 


namespace spt 


387 







// namespace spt 

AngularMotion::AngularMotion(void) 

: p(0.0, 3), v(0.0, 3), a(0.0, 3), start{0.0), stop(END_TIME) 

{ // AngularMotion::AngularMotion(void) 

} II AngularMotion::AngularMotion(void) 

void AngularMotion::setAM(const vertexS P, // New acceleration 

const vertexs V, // New velocity 

const vertexs A, // New position 

double t) // Effective time of the position 

{ // void AngularMotion::setAM(const vertexs, ...) 

update(t); // Set the new effective time 

p=fixOri(P); // Set the new position 

v=V; // Set the new velocity 

a=A; // Set the new acceleration 

} // void AngularMotion::setAM(const vertexs, ...) 


void AngularMotion::setAM(const vertexs P, 

const vertexs V, 


update(1,u); 
p=fixOri (P) ,■ 
v=V; 
a=A; 


const vertexs P, // New acceleration 

const vertexs V, // New velocity 

const vertexs A, // New position 

double 1, // Effective time of the position 

double u) // End time of motion 

// void AngularMotion::setAM(const vertexs, ...) 

// Set the new effective time 
// Set the new position 
// Set the new velocity 
// Set the new acceleration 
// void AngularMotion::setAM(const vertexs, ...) 


void AngularMotion::ap(const vertexs P, double 1) 

{ // void AngularMotion::ap(const vertexs, double) 

update(1); // Set the new effective time 

p=fixOri(P); // Set the new position 

} // void AngularMotion::ap(const vertexs, double) 

void AngularMotion::ap(const vertexs P, double 1, double u) 

{ // void AngularMotion::ap(const vertexs, double, double) 

update(1,u); // Set the new effective time 

p=fixOri(P); // Set the new position 

} // void AngularMotion::ap(const vertexs, double, double) 

void AngularMotion::av(const vertexs V, double 1) 

{ // void AngularMotion::av(const vertexs, double) 

updated); // Set the new effective time 

v=V; // Set the new velocity 

} // void AngularMotion::av(const vertexs, double) 

void AngularMotion::av(const vertexs V, double 1, double u) 

{ // void AngularMotion::av(const vertexs, double, double) 

updated,u); // Set the new effective time 

v=V; // Set the new velocity 

} // void AngularMotion::av(const vertexs, double, double) 

void AngularMotion::aa(const vertexs A, double 1) 

{ // void AngularMotion::aa(const vertexs, double) 

updated); // Set the new effective time 

a=A; // Set the new acceleration 

} // void AngularMotion::aa(const vertexs, double) 

void AngularMotion::aa(const vertexs A, double 1, double u) 

{ // void AngularMotion::aa(const vertexs, double, double) 

updated,u); // Set the new effective time 

a=A; // Set the new acceleration 

} // void AngularMotion::aa(const vertexs, double, double) 


void AngularMotion::ap(double x, double y, double z, double 1) 

{ // void AngularMotion::ap(double, double, double, double) 

updated); // Set the new effective time 

p[0]=x; p[l]=y; p[2]=z; // Set the new position 

p=fixOri(p); // Ensure that things are in the proper ranges 

} // void AngularMotion::ap(double, double, double, double) 


388 







void AngularMotion::ap(double x, double y, double z, double 1, double u) 

{ // void AngularMotion:tap(double, double, double, double, double) 

update(1,u); // Set the new effective time 

p[0]=x; p[l]=y; p[2]=2; // Set the new position 

p=fixOri(p); // Ensure that things are in the proper ranges 

} // void AngularMotion::ap(double, double, double, double, double) 

void AngularMotion::av(double x, double y, double z, double 1) 

{ // void AngularMotion::av{double, double, double, double) 

updated); // Set the new effective time 

v[0]=x; v[l]=y; v[2]=z; // Set the new velocity 

} // void AngularMotion::av(double, double, double, double) 

void AngularMotion::av(double x, double y, double z, double 1, double u) 

{ II void AngularMotion::av(double, double, double, double, double) 

update(l,u); // Set the new effective time 

v[0]=x; v[l]=y; v[2]=2; // Set the new velocity 

} // void AngularMotion::av(double, double, double, double, double) 

void AngularMotion::aa(double x, double y, double z, double 1) 

{ // void AngularMotion::aa(double, double, double, double) 

updated); // Set the new effective time 

a[0]=x; a[l]=y; a[2]=z; // Set the new position 

} // void AngularMotion::aa(double, double, double, double) 

void AngularMotion::aa(double x, double y, double z, double 1, double u) 

{ // void AngularMotion::aa(double, double, double, double, double) 

update(l,u); // Set the new effective time 

a[0]=x; a[l]=y; a[2]=z; // Set the new position 

) // void AngularMotion::aa(double, double, double, double, double) 

vertex AngularMotion::ap(double t) 

{ // vertex AngularMotion::ap(double) 

double dt = (min(t,stop)-start); // Get the time difference 

return fixOri(vertex{p+(v+0.5*a*dt)*dt)); // Get position 

} // vertex AngularMotion::ap(double) 


vertex AngularMotion::av(double t) 

{ 

double dt = (min(t,stop)-start); 
return vertex{v+a*dt) ; 

} 

vertex AngularMotion::aa(double t) 

{ 

return a; 


// vertex AngularMotion::av(double) 
// Get the time difference 
// Get the current velocity 
// vertex AngularMotion::av(double) 


// vertex AngularMotion::aa(double) 
// Return the current acceleration 
// vertex AngularMotion::aa(double) 


void AngularMotion::update(double 1) 
{ 


// Update to new time, 1 
// void AngularMotion::update(double) 


if {1>END_TIME) // If the start time is beyond the end time 

{ 

std::cerr « "AngularMotion::update(" « 1 « ")" « std::endl; 

std::cerr « "Start time reduced to " « END_TIME « std::endl; 

1 = END_TIME; 

} // if (1>END_TIME) 

p = ap(l); // Get new position 

v = av(l); // Get the new velocity vector 

start =1; // Set the new effective time 

stop = END_TIME; // Set the end time of this leg of movement 

} // void AngularMotion::update(double) 

void AngularMotion::update(double 1, double u) // Update to new time, 1 

{ // void AngularMotion::update(double, double) 

if (l>u) // If start time is is beyond the end time 


std::cerr << "AngularMotion::update(" « 1 « ", " « u « ")" 
« std::endl; 

std::cerr « "Start time reduced to " « u « std::endl; 

1 = u; 


389 






p = ap{1) ; 
V = av( 1 ); 
start = 1; 
stop = u; 


// if (l>u) 
// Get new position 
// Get the new velocity vector 
// Set the new effective time 
// Set the end time of this leg of movement 
II void AngularMotion::update(double, double) 


double AngularMotion::getStopTime(void) 

f II double AngularMotion::getStopTime(void) 

return stop; II Return the value to the calling routine 

} II double AngularMotion::getStopTime(void) 


double AngularMotion::getStartTime(void) 

{ II double AngularMotion::getStartTime(void) 

return start; // Return the value to the calling routine 

} // double AngularMotion::getStartTime(void) 

void AngularMotion::setStopTime(double t) // Update stop time 

{ // void AngularMotion::setStopTime(double) 

stop = t; // Set new stop time 

} II void AngularMotion::setStopTime(double) 

double AngularMotion::angDiff(double tl, double t2) 

{ // double AngularMotion::angDiff(double, double) 

double rv = fixOri(tl)-fixOri(t2); // Get difference 

if (rv<=-PI) rv+=2.0*PI; // Correct to one side 

else if (rv>=PI) rv-=2.0*PI; // Correct to the other 

return rv; // Return the value to the calling routine 

} // double AngularMotion::angDiff(double, double) 

double AngularMotion::fixOri(double a) // Angle to fix 

{ // double AngularMotion::fixOri(double) 

a = fmod(a, 2.0*PI); if (a<0.0) a+=2.0*PI; // Get in range [0, 2*PI) 

return a; // Return the fixed value 

} // double AngularMotion::fixOri(double) 


vertex AngularMotion::fixOri(vertex o) 


// vertex AngularMotion::fixOri(vertex) 


for (ulong i=0; i<o.size(); ++i) o[i]=fixOri(o[i]); 


// Fix each one 


return o; 


// Return the adjusted orientation array 
// vertex AngularMotion::fixOri(vertex) 

// namespace spt 


C.1.87. spt/sptDefs.h 

/////////////////////////////////////////////////////////////////////////////// 
// sptDefs.h - Some spt namespace definitions 
/////////////////////////////////////////////////////////////////////////////// 

#ifndef SPTDEFS_H_INCLnDED 
#define SPTDEFS H INCLUDED 


#include <valarray> 

namespace spt 
{ 

typedef std::valarray<double> vertex; 

} 

#endif 


// namespace spt 
// TO malce life a little easier 
// namespace spt 


C.1.88. spt/spt/Defs.cxx 

////////////////////////////////////////////////////////////////////////////// 
// sptDefs.cxx - Some simple definitions 
/////////////////////////////////////////////////////////////////////////////// 

#include "sptDefs.h" 


namespace spt 
{ 


// namespace spt 


390 









} // namespace spt 

C.1.89. spt/sptEnvironmentObject.h 

/////////////////////////////////////////////////////////////////////////////// 
// sptEnvironmentObject.h - Class declaration for the spt:;EnvironmentObject 
// class used to sense and track objects within an 

// environment. It's part of namespace spt (short for 

// 'support'). 

/////////////////////////////////////////////////////////////////////////////// 

#ifndef SPTENVIRONMENTOBJECT_H_INCLUDED 
#define SPTENVIRONMENTOBJECT_H_INCLODED 

#include "sptNewtonianMotion.h" 

#include <string> 

idefine UNKNOWN 0 
#define NEUTRAL 1 
#define RED 2 
♦define BLUE 3 


namespace spt 
{ 

class EnvironmentObject 
{ 

protected: 
ulong force; 
double radius; 


// namespace spt 

public NewtonianMotion 

II class EnvironmentObject : public NewtonianMotion 

// Force to which this track belongs 
// Radius of sensing capability 


public: 

EnvironmentObject(void); 
EnvironmentObject(ulong, double); 
virtual double rad(void); 
virtual ulong iff(void); 


// Default class constructor 
// Class constructor 
II Get the sensor radius 
II Friend/Foe identifier 


// class EnvironmentObject 


public NewtonianMotion 
// namespace spt 


extern std::string forcestring(ulong); 


#endif 


C.1.90. spt/sptEnvironmentObject.cxx 

/////////////////////////////////////////////////////////////////////////////// 
// sptEnvironmentObject.cxx - Class definition for the spt::EnvironmentObject 
// class used to sense objects. 

/////////////////////////////////////////////////////////////////////////////// 

♦ include "sptEnvironmentObj ect.h" 

namespace spt 

{ // namespace spt 

EnvironmentObject::EnvironmentObject(void) 

: force(UNKNOWN), radius(O.O) 

{ // EnvironmentObject::EnvironmentObject(void) 

} // EnvironmentObject::EnvironmentObject(void) 

EnvironmentObject::EnvironmentObject(ulong f, double r) 

: force(f), radius(r) 

{ // EnvironmentObject::EnvironmentObject(ulong, double) 

} // EnvironmentObject::EnvironmentObject(ulong, double) 

double EnvironmentObject::rad(void) 

{ 

return radius; 

} 

ulong EnvironmentObject::iff(void) 

{ // ulong EnvironmentObject::iff(void) 

return force; // Return the force identifier 


// double EnvironmentObject::rad(void) 
// Return the sensor radius 
// double EnvironmentObject::rad(void) 


391 








// ulong EnvironmentObject::iff(void) 
// namespace spt 


std::string forcestring(ulong i) 

{ return i==l ? "NEUTRAL" : i==2 ? "RED" : i==3 ? "BLUE" 


"UNKNOWN" 


C.1.91. spt/sptLinearMotion.h 


/////////////////////////////////////////////////////////////////////////////// 
// sptLinearMotion.h - Class declaration for the spt::LinearMotion class used 
// to move objects in a simulation. It's part of the spt 

// (short for 'support') namespace. 

/////////////////////////////////////////////////////////////////////////////// 

#ifndef SPTLINEARMOTION_H_INCLUDED 
#define SPTLINEARMOTION_H_INCLODED 

#include "sptDefs.h" 
tinclude "Trace.h" 


namespace spt 
{ 

class LinearMotion 
{ 

protected: 
vertex p; 
vertex v; 
vertex a; 
double start; 
double stop; 


public sodl::Trace 

II class LinearMotion 


// namespace spt 
public sodl::Trace 


// Linear position vector 
// Linear velocity vector 
// Linear acceleration vector 
II Effective time of the linear motion paramters 

// Boundary of movement 


public: 

LinearMotion(void) ; 


// Default class constructor 


virtual 

virtual 

virtual 

virtual 

virtual 

virtual 

virtual 

virtual 

virtual 

virtual 

virtual 

virtual 

virtual 

virtual 


setLM(const vertexs, const vertexs, 
setLM(const vertexs, const vertexs, 
double); 

Ip(const vertexs, double); 

Iv(const vertexs, double); 
la(const vertexs, double); 

Ip(const vertexs, double, double); 
lv(const vertexs, double, double); 
la(const vertexs, double, double); 
Ip(double, double, double, double); 
lv(double, double, double, double); 
la(double, double, double, double); 
Ip(double, double, double, double, 
lv(double, double, double, double, 
la(double, double, double, double. 


const vertexs, double); 
const vertexs, double. 


// Set pos 
// Set vel 
// Set acc 
// Set pos 
// Set vel 
// Set acc 
// Set pos 
// Set vel 
// Set acc 
// Set pos 
// Set vel 
// Set acc 


double) ; 
double) ; 
double) ; 


virtual vertex Ip(double); 
virtual vertex Iv(double); 
virtual vertex la(double); 
virtual void update(double); 
virtual void update(double, double); 
virtual double getStopTime(void); 
virtual double getStartTime(void); 
virtual void setStopTime(double); 

// class 


// Get the position 
II Get the velocity 
// Get the acceleration 
// Update motion to time t 
// Update motion to time t 
II Get the stop time 
// Get the start time 
// Set the stop time 
LinearMotion : public sodl::Trace 
// namespace spt 


#endif 


C.1.92. spt/sptLinearMotion.cxx 

////////////////////////////////////////////////////////////////////////////// 
// sptLinearMotion.cxx - Class definition for the spt::LinearMotion class used 
// to trac)c objects. 

/////////////////////////////////////////////////////////////////////////////// 

#include "sptLinearMotion.h" 


392 







#define END_TIME 2e307 
namespace spt 

{ // namespace spt 

LinearMotion::LinearMotion(void) 

: p(0.0, 3), v(0.0, 3), a(0.0, 3), start(O.O), stop(END_TIME) 

{ // LinearMotion;:LinearMotion(void) 

) // LinearMotion;:LinearMotion(void) 


void LinearMotion;;setLM(const vertexs P, 

const vertexs V, 
const vertexs A, 
double 1) 


// New acceleration 
// New velocity 
// New position 
// Effective time of the position 


update(1); 

P=P; 

v=V; 


// void LinearMotion;;setLM{const vertexs, ...) 

// Set the new effective time 
// Set the new position 
// Set the new velocity 
// Set the new acceleration 
// void LinearMotion:;setLM(const vertexs, ...) 


void LinearMotion::setLM(const vertexs P, 

const vertexs V, 
const vertexs A, 
double 1, 
double u) 


update(1,u); 

p=P; 

v=V; 

a=A; 


vertexs P, // New acceleration 

vertexs V, // New velocity 

vertexs A, // New position 

e 1, // Effective time of the position 

e u) // Upper bound of time in motion 

// void LinearMotion::setLM(const vertexs, ...) 

// Set the new effective time 
// Set the new position 

// Set the new velocity 

// Set the new acceleration 

// void LinearMotion::setLM(const vertexs, ...) 


void LinearMotion::Ip(double x, double y, double z, double 1) 

{ // void LinearMotion::Ip(double, double, double, double) 

updated); // Set the new effective time 

p[0]=x; p[l]=y; p[2]=z; // Set the new position 

} // void LinearMotion::Ip(double, double, double, double) 

void LinearMotion::Ip(double x, double y, double z, double 1, double u) 

{ // void LinearMotion::Ip(double, double, double, double, double) 

update(l,u); // Set the new effective time 

p[0]=x; p[l]=y; p[2]=z; // Set the new position 

} // void LinearMotion::Ip(double, double, double, double, double) 

void LinearMotion::Iv(double x, double y, double z, double 1) 

{ // void LinearMotion::lv(double, double, double, double) 

updated); // Set the new effective time 

v[0]=x; v[l]=y; v[2]=z; // Set the new velocity 

) // void LinearMotion::lv{double, double, double, double) 

void LinearMotion::Iv(double x, double y, double z, double 1, double u) 

{ // void LinearMotion::lv(double, double, double, double, double) 

update(l,u); // Set the new effective time 

v[0]=x; v[l]=y; v[2]=z; // Set the new velocity 

} // void LinearMotion::lv(double, double, double, double, double) 

void LinearMotion::1a(double x, double y, double z, double 1) 

{ // void LinearMotion::la(double, double, double, double) 

update(1); // Set the new effective time 

a[0]=x; a[l]=y; a[2]=z; // Set the new position 

) // void LinearMotion::1a(double, double, double, double) 

void LinearMotion::1a(double x, double y, double z, double 1, double u) 

{ // void LinearMotion::1a(double, double, double, double, double) 

update(l,u); // Set the new effective time 

a[0]=x; a[l]=y; a[2]=z; // Set the new position 

} // void LinearMotion::la(double, double, double, double, double) 


void LinearMotion::Ip(const vertexs P, double 1) 

{ // void LinearMotion::Ip(const vertexs, double) 


393 







update(1); 
p=P; 


// Set the new effective time 
// Set the new position 
// void LinearMotion::Ip{const vertexS, double) 


void LinearMotion:;Ip(const vertexS P, double 1, double u) 

{ // void LinearMotion::Ip(const vertexS, double, double) 

update(1,u); // Set the new effective time 

p=P; // Set the new position 

} // void LinearMotion::Ip(const vertexS, double, double) 

void LinearMotion::Iv(const vertexS V, double 1) 

{ // void LinearMotion: :lv(const vertexS,, double) 

update(1); // Set the new effective time 

v=V; // Set the new velocity 

} // void LinearMotion::Iv(const vertexS, double) 

void LinearMotion::Iv(const vertexS V, double 1, double u) 

{ // void LinearMotion::lv(const vertexS, double, double) 

update(1,u); // Set the new effective time 

v=V; // Set the new velocity 

} II void LinearMotion::lv(const vertexS, double, double) 

void LinearMotion::la(const vertexS A, double 1) 

{ // void LinearMotion::1a(const vertexS, double) 

update(1); // Set the new effective time 

a=A; // Set the new acceleration 

} // void LinearMotion::1a(const vertexS, double) 


void LinearMotion::1a(const vertexS A, double 1, double u) 


update(1,u); 
a=A; 


// void LinearMotion::1a(const vertexS, double, double) 

// Set the new effective time 
// Set the new acceleration 
// void LinearMotion::la(const vertexS, double, double) 


vertex LinearMotion::Ip(double t) 

{ 

double dt = min(t,stop)-start; 
return vertex(p+(v+0.5*a*dt)*dt); 

} 

vertex LinearMotion::lv{double t) 

{ 

double dt = min(t,stop)-start; 
return vertex(v+a*dt); 


// vertex LinearMotion::Ip(double) 
// Get the time difference 
// Get current position 
// vertex LinearMotion::Ip(double) 


// vertex LinearMotion::Iv(double) 
// Get the time difference 
// Get the current velocity 
// vertex LinearMotion::lv{double) 


vertex LinearMotion::la(double t) 

{ 

return a; 

} 


// vertex LinearMotion::1a(double) 
// Return the current acceleration 
// vertex LinearMotion::la(double) 


void LinearMotion::update(double 1) 

{ 


// Update to new time, 1 
// void LinearMotion::update(double) 


if {1>END_TIME) // If 

{ 

std::cerr « "LinearMotion::update( 
std::cerr « "Start time reduced to 
1 = END TIME; 


//If the start time is beyond the end time 


1 « ")" « std::endl; 
« END TIME « std::endl; 


p = Ipd) ; 

V = lv(l) ; 
start = 1; 
stop = END_TIME; 


// if (1>END_TIME) 
// Get new position 
// Get the new velocity vector 
// Set the new effective time 
// Set the end time of this leg of movement 
// void LinearMotion::update(double) 


void LinearMotion::update(double 1, double u) // Update to new time, 1 

{ // void LinearMotion::update(double, double) 

if (l>u) //If start time is is beyond the end time 

{ 

std::cerr << "LinearMotion::update(" « 1 « ", " « u << ")" 

« std::endl; 


394 







std::cerr « "Start time reduced to " « u « std::endl; 
1 = u; 


} 

P = ip(i) ; 
V = lv{l) ; 
start = 1; 
stop = u; 


// if (l>u) 
// Get new position 
// Get the new velocity vector 
// Set the new effective time 
// Set the end time of this leg of movement 
// void LinearMotion::update(double, double) 


double LinearMotion::getStopTime(void) 

{ // double LinearMotion::getStopTime(void) 

return stop; // Return the value to the calling routine 

} // double LinearMotion::getStopTime(void) 


double LinearMotion::getStartTime(void) 

{ // double LinearMotion::getStartTime(void) 

return start; // Return the value to the calling routine 

} // double LinearMotion::getStartTime(void) 


void LinearMotion::setStopTime(double t) // Update stop time 

{ // void LinearMotion::setStopTime(double) 

stop = t; // Set new stop time 

} // void LinearMotion::setStopTime(double) 

// namespace spt 


C.1.93. spt/sptNewtonianMotion.h 

/////////////////////////////////////////////////////////////////////////////// 
II sptNewtonianMotion.h - Class declaration for the spt::NewtonianMotion class 
// used to move objects in a simulation. It's part of 

// the spt (short for 'support') namespace. 

/////////////////////////////////////////////////////////////////////////////// 


#ifndef SPTNEWTONIANMOTION_H_INCLUDED 
#define SPTNEWTONIANMOTION_H_INCLODED 

♦include "sptAngularMotion.h" 

♦include "sptLinearMotion.h" 


namespace spt 

{ // namespace spt 

class NewtonianMotion : public AngularMotion, public LinearMotion 
{ // class NewtonianMotion : public AngularMotion, public LinearMotion 

public: 

NewtonianMotion(void); // Default class constructor 


virtual 

void 

set(const 
const 

vertexS, 
vertexs. 

const 

const 

vertexs, 

vertexs. 

const 

const 

vertexs, 

vertexs. 

double) ; 

virtual 

void 

set (const 
const 

vertexS, 

vertexs, 

const 

const 

vertexs, 

vertexs. 

const 

const 

vertexs, 

vertexs. 

double. 


double); 


virtual void update(double); 
virtual void update(double, double); 
virtual double getStopTime(void); 
virtual double getStartTime(void); 
virtual void setStopTime(double); 


// Update motion params to time t 
II Update motion params to time t 
// Get the stop time 
// Get the start time 
II Set the stop time 


// class NewtonianMotion 


public AngularMotion, public LinearMotion 

// namespace spt 


♦endif 


C.1.94. spt/sptNewtonianMotion.cxx 

/////////////////////////////////////////////////////////////////////////////// 
// sptNewtonianMotion.cxx - Class definition for the spt::NewtonianMotion 
// class used to track objects. 

/////////////////////////////////////////////////////////////////////////////// 

♦include "sptNewtonianMotion.h" 


395 








// namespace spt 


namespace spt 

{ 

NewtonianMotion::NewtonianMotion(void) 

{ // NewtonianMotion::NewtonianMotion(void) 

} // NewtonianMotion::NewtonianMotion(void) 


LP, 

LV, 


void NewtonianMotion::set(const vertexS 

const vertexs 
const vertexs LA, 
const vertexs AP 
const vertexs AV, 
const vertexs AA, 
double 1) 


{ 


// New lin acc 
// New lin vel 
// New lin pos 
// New ang acc 
// New ang vel 
// New ang pos 

// Effective time of the position 
// void NewtonianMotion::set(const vertexs, ...) 

// Update to the current motion parameters 
// Set the new position 

// Set the new velocity 

// Set the new acceleration 

// Set the new angular position 

// Set the new angular acceleration 
// Set the new angular velocity 


update(1); 

LinearMotion::p=LP; 
LinearMotion::v=LV; 
LinearMotion::a=LA; 
AngularMotion::p=fixOri(AP); 
AngularMotion::a=AA; 
AngularMotion: :v=AV;. 


// void NewtonianMotion::set(const vertexs, ...) 


void NewtonianMotion::set(const vertexs LP, 

const vertexs LV, 
const vertexs LA, 
const vertexs AP, 
const vertexs AV, 
const vertexs AA, 
double 1, double u) 


// void 

update(1,u); 

LinearMotion::p=LP; 
LinearMotion::v=LV; 
LinearMotion::a=LA; 
AngularMotion::p=fixOri(AP) 
AngularMotion::a=AA; 
AngularMotion::v=AV; 


// New lin acc 
// New lin vel 
// New lin pos 
// New ang acc 
// New ang vel 
// New ang pos 

// Effective motion times 
NewtonianMotion::set(const vertexs, ...) 

// Update to the current motion parameters 
// Set the new position 

// Set the new velocity 

// Set the new acceleration 

// Set the new angular position 

// Set the new angular acceleration 
// Set the new angular velocity 


// void NewtonianMotion::set(const vertexs, ...) 


void NewtonianMotion::update(double 1) // Update to new time, t 

( // void NewtonianMotion::update(double) 

LinearMotion::update(1); // Set new effective time for linear motion 

AngularMotion::update(1); // Set new effective time for angular motion 

} // void NewtonianMotion::update(double) 


void NewtonianMotion::update(double 1, double u) II Update to new time, 1 

{ // void NewtonianMotion::update(double) 

LinearMotion::update(1,u); // Set new effective time for linear motion 

AngularMotion::update(l,u); II Set new effective time for angular motion 
} // void NewtonianMotion::update(double) 


double NewtonianMotion::getStopTime(void) 

{ // double NewtonianMotion::getStopTime(void) 

return min(LinearMotion::getStopTime(), AngularMotion::getStopTime()); 

} // double NewtonianMotion::getStopTime(void) 


double NewtonianMotion::getStartTime(void) 

{ // double NewtonianMotion::getStartTime(void) 

return max(LinearMotion::getStartTime(), AngularMotion::getStartTime()) ; 

} // double NewtonianMotion::getStartTime(void) 


void NewtonianMotion::setStopTime(double t) // Update stop time 

{ // void NewtonianMotion::setStopTime(double) 

LinearMotion::setStopTime(t); // Set new stop time for linear motion 

AngularMotion::setStopTime(t); // Set new stop time for angular motion 

} // void NewtonianMotion::setStopTime(double) 

) // namespace spt 


396 







C.2. Bouncel 


C.2.1. bounce.proc 

{import process {View3D, Node3D, Cube, Polygon3D, particle} } 
{import message {start, gr_update, AddNode3D, AddShapeSD, 

SetColor, SetMode, SetPosition, SetSize, 
SetCubeSize, SetRefresh, set_system, SetPointSize, 
StartSimulation) } 

{import {<stdlib.h>, <time.h>, <GL/glut.h>} } 

{import std {<iostream>}} 


process:bounce 
{ 

double:interval(0.025) ; 
particle:b[200] ; 

View3D:view; 

Node3D:systemNode; 
Polygon3D:system; 

Cube:cube; 


// Interval between updates 
// Collection of bouncing particles 
// Display to this view 
// Node for the display 
// Place where the points are stored 
// Cube for surrounding the collection of points 


mode:Default 

{ 

node:start_sim[StartSimulation:strt] 

[start:s=>(b;):(0.0), 
set_system:setSystem=>(b;), 
AddNode3D:an=>(view;), 

AddShape3D:as=>(systemNode;), 

SetColor:scSystem=>(system;), 

SetColor:scCube=>(cube;), 

SetMode:smSystem=>(system;), 

SetMode:smCube=>(cube;), 

SetPosition:sp=>(view;):(0.0), 

SetSize:ss=>(view;):(0.0), 

SetRefresh:sr=>(view;), 

SetCubeSize:scs=>(cube;), 
gr_update;up=>(me;b;):(interval-le-9), 
SetPointSize:sps=>(view; 


// mode;Default 
// Initial Start message 
// Transmit start to particles 
// Set parent system 
// Add system node to view 
, // Add shape to node 

// Set system color 
// Set cube color 
// Set system mode 
// Set cube mode 
// Set view position 
// Set view size 
// Set refresh rate 
// Set cube size 
// Update #1 
// Set point size 


{ 


// node:start_sim[StartSimulation:strt][start:s. 


] 


sr. set(interval); 
setSystem.system = system; 
an.add(systemNode); 

as.add(system); 
as.add(cube); 

scSystem.set(0.0, 1.0, 1.0) 
scCube.set(0.0, 1.0, 0.0); 
smSystem.gr_mode 
sp.set(50, 50); 

ss. set(800,600); 
scs.size = 20.0; 
sps.size = 3.0; 


// Set the refresh interval 
// Set the system for the particles 
// Main node to add to the view 
// System of particles to add to node 
// Cube exterior to the particle system 
// Set system color to cyan 
// Set cube color to green 
= smCube.gr_mode = GL_POINTS; // Rendering mode 

// Set the position of the view 
// Set the size of the window 
// Set the size of the cube 
II Specify the point size 
// node:start sim[StartSimulation:strt][start;s, ... ] 


} 


node:update[gr_update:in] 

[gr_update:out=>(me; 


{ } 


b;):(getTime()tinterval)] 


// mode:Default 
// process:bounce 


C.2.2. gr update.msg 

{message:gr_update;} 

C.2.3. hit.msg 

{ 

message;hit 


397 








{ 

int:axis; 

} 

} 


// message:hit 
// Axis associated with the hit event 

// message:hit 


C.2.4. particle.proc 

{import message {hit, start, gr_update, set_system, SetVertex3D, 
AddVertex3D) } 

{import process {Node3D, Vertex3D) } 

{import std {<vector>} } 

{import {"Exception.h"} } 


process:particle 
{ 

Vertex3D:vrt; 
double:pos[3]; 
double:vel[3] ; 
double:nextTime[3]; 
double:time(0.0); 

sodl:;Defs::MessageType:lm(SMT_LAST) ; 


// Screen vertex 
// Position vector 
// Velocity vector 
// Next impact times for each axis 
// Time for the last velocity change 
// Last message type 


method:init(public; void;) 

{ // method:init (public; void;) 


for (uint i=0; i<3; ++i) 

{ 

pos[i] = random.nextDouble(-10.0, 
vel[i] = random.nextDouble(-10.0, 
setNextHitTime(i); // 

} 

} 


// Loop over each of the coordinates 

10.0); // Assign position 

10.0); // Assign position 

Set time for next impact along axis i 
// for (int i=0; i<3; ++i) 
// method:init(public; void;) 


method:setNextHitTime(private; void; int:i;) 

{ // method:SetNextHitTime(private; void; int:i;) 

if (vel[i]<0.0) nextTimefi] = time-(10.0+pos[i])/vel[i]; 
else if (vel[i]>0.0) nextTime[i) = time+(10.0-pos[i])/vel[i] ; 
else nextTime[i]=getEngine0.getClock0.getEndTime0; // vel[i]==0 

} // method:SetNextHitTime(private; void; int:i;) 


method:move(private; void;) 

{ // method:move(private; 

double dt = getTime()-time; 

for (uint i=0; i<3; ++i) pos[i] += dt*vel[i]; 
time = getTime(); 

} // method:move(private; 


void; double time) 
// Delta time 
// Update position 
// Update the time 
void; double time) 


method:getMinAxis(private; int;) 

{ // method:getMinAxis(private; int;) 

int axis = 0; // Initialize the axis value 


for (uint i=l; i<nextTime.size(); ++i) // Loop over the axes 

if (nextTime[axis]>nextTime[i]) axis=i; // Get min time 

return axis; // Return that axis value 

) // method:getMinAxis(private; int;) 


mode:Default 

{ // mode:Default 

node:setSystem[set_system:in][AddVertex3D:av=>(in.system;)] 

{ // node:setSystem[set_system:in][AddVertex3D:out] 

Im = in.getType(); 

av.add(vrt); // Add the vertex to the system 

} // node:setSystem[set_system:in][AddVertex3D:out] 


node:start_sim[start:s] 

[hit:out=>(me;) 

{ 

Im = s.getType(); 

out.axis = getMinAxis(); 


(nextTime[out.axis])] 

// node:start_sim[start:s][hit:out=>(me;)] 

// Axis for the impact 
// node:start sim[start:s][hit:out=>(me;)] 


398 






node;update[gr_update:in][SetVertex3D:out=>(vrt;)] 

( // node:update[gr_update:in][SetVertexSD:out] 

Im = in.getType{); 

move(); // Move the particle to the current position 

out.set(pos); // Update the vertex position 

} // node:update[gr_update:in][SetVertex3D:out] 

node:change[hit:in] 

[hit:out=>(me;):(nextTime[out.axis])] 

{ // node:change[hit:in][hit:out=>(me;)] 

Im = in.getType(); 

move(); // Move the particle to the current position 

vel[in.axis] = -vel[in.axis]; // Change the velocity 

setNextHitTime(in.axis); II Set next hit time for specified axis 

out.axis = getMinAxis(); // Axis for the impact 

} // nodeichange[hit:in][hit:out=>(me;)] 

} // mode:Default 

// process:particle 


C.2.5. set_system.msg 


message:set_system 
{ 

process:system; 

} 


// message:set_system 
// System to which particles will be added 
// message:set_system 


C.2.6. start.msg 

{ message:start; } 

C.3. Bounce2 

C.3.1. bounce.proc 

(import process {bounce_view, Node3D, Cube, Polygon3D, particle) ) 
(import message (AddNode3D, AddShape3D, SetColor, SetMode, SetSize, 
SetPosition, SetCubeSize, SetRefresh, SetPointSize, 
StartSimulation, AddVertex3D) ) 

(import {<stdlib.h>, <time.h>, <GL/glut.h>) ) 

(import std {<iostream>}) 


process:bounce 


particle;b[2000]; 
bounce_view:view; 
Node3D:systemNode; 
Polygon3D:system; 
Cube:cube; 


// Collection of bouncing particles 
// Display to this view 
II Node for the display 
// Place where the points are stored 
II Cube for surrounding the collection of points 


mode:Default 

( 

node:start_sim[StartSimulation:strt] 

[AddNodeSD:an=>(view;) , 
AddShapeSD:as=>(systemNode;) 
AddVertex3D:av=>(system;), 
SetColor:scSystem=>(system;) 
SetColor:scCube=>(cube;), 
SetMode:smSystem=>(system;), 
SetMode:smCube=>(cube;), 
SetPosition:sp=>(view;), 
SetSize:ss=>(view;), 
SetRefresh:sr=>(view;), 
SetCubeSize:scs=>(cube;), 
SetPointSize:sps=>(view;)] 


II mode:Default 
// Initial start message 
// Add system node to view 
// Add shape to node 
// Add vert to system 
// Set system color 
// Set cube color 
// Set the system mode 
// Set the cube mode 
// Set the view position 
// Set the view size 
// Set the refresh rate 
II Set the cube size 
II Set the point size 


// node:start sim[StartSimulation:strt][start:s, ... 


399 







} 


an.add(systemNode); 
as.add(system); 
as.add(cube); 
for (uint i=0; i<b.size{ 
scSystem.set(0.0, 1.0, 1 
scCube.set(0.0, 1.0, 0.0 
smSystem.gr_mode=smCube. 
sp.set(50, 50); 
ss.set(800,600); 
sr.set (0.025) ; 
scs.size = 20.0; 
sps.size = 3.0; 

II node 


// Main node to add to the view 
// System of particles to add to node 
// Cube exterior to the particle system 
); ++i) av.add(b[i]); // Add particles 

.0); // Set system color to cyan 

); // Set cube color to green 

gr_mode=GL_POINTS; // Rendering mode 

// Set the position of the view 
II Set the size of the window 
// Set the refresh interval 
// Set the size of the cube 
// Specify the point size 
:start_sim[StartSimulation:strt][start:s, ... ] 

// mode:Default 
// processrbounce 


C.3.2. bounce_view.proc 

{import process {View3D} } 

(import message {set_motion} } 

(import gvm (gvmBounceView, gvmParticle, gvmSetMotion} } 


process:bounce_view(View3D) 

{ 

method:init(public; void;) 

{ // method:init(public; void;) 

view = new gvm::BounceView; // Create a new view 

dynamic_cast<GLUTViewManagers.>{*EngineStand: :stand.vm) . 
addview(view) ; 

} // method:init(public; void;) 


method:getGVMType(protected; gvm::object_type; ptype:t;) 

{ // method:getGVMType(protected; gvm::object_type; ptype;) 

switch(t) // Which one is it? 

{ 

case SPT_partiole: return gvm::GVM_Particle; 
default: return View3D::getGVMType(t); 

} // switch(t) 

} // method:getGVMType(protected; gvm::object_type; ptype;) 


} 


} 


mode:Default 


{ 


} 


// mode:Default 


node:setMotion[set_motion:in)[] 

{ // node:setMotion[set_motion:in][] 

view->schedule(new gvm::SetMotion(*view, getTimeO, in.index, 

in .getT () , in. getP () , in.getVO, 
in.getA())); 

} // node:setMotion[set_motion:in][] 

II mode:Default 


C.3.3. hit.msg 

{ 

message:hit 

( // message:hit 

int:axis; II Axis associated with the hit event 

} II message:hit 

} 


C.3.4. particle.proc 

(import message (hit, set_motion, SetVertex3D, StartSimulation, 
AddVertex3D, AddView] } 

(import process {Vertex3D} } 

(import std (<vector>} } 

(import {"Exception.h"} } 


400 






process:particle(VertexSD) 
{ 

double:vel[3]; 
double:acc[3]; 
double:nextTime[3]; 
double:time(0.0); 


// Velocity vector 
// Acceleration vector 
// Next impact times for each axis 
// Time for the last velocity change 


method;init(public; void;) 

{ // method:init(public; void;) 

for (uint i=0; i<3; ++i) // Loop over each of the coordinates 


} 

} 


pos[i] = random.nextDouble(-10.0, 10.0); // Assign position 
vel[i] = random.nextDouble(-10.0, 10.0); // Assign velocity 
acc[i] = (i==2) ? -9.8 : 0.0; // Initialize acceleration 
setNextHitTime(i); // Set time for next impact along axis i 

// for (int i=0; i<3; ++i) 
// method:init(public; void;) 


method:setNextHitTime(private; void; int:i;) 

{ // method:setNextHitTime(private; void; int:i;) 

static double et = getEngine().getClock().getEndTime(); 
static double s = 10.0; 


if (acc[i] != 0.0) // If there is non-zero acceleration 

{ 

double a = acc[i]/2.0; 
double a2 = acc[i]; 
double b = vel[i]; 
double bs = b*b; 
double c = pos[i]; 
double pd = bs-4.0*(c-s)*a; 
double nd = bs-4.0*(c+s)*a; 
double psr = (pd>=0.0) ? sqrt(pd) : -1.0; 
double nsr = (nd>=0.0) ? sqrt(nd) : -1.0; 
double pt = (pd>0.0) ? min((-b+psr)/a2, (-b-psr)/a2) 

double nt = (nd>0.0) ? max((-b+nsr)/a2, (-b-nsr)/a2) 

if (pt<0.0 nt<0.0) 

throw Exception::Nonspecific("Bounce fault"); 
else if (pt<0.0 II nt<0.0) nextTime[i] = time+max(pt, nt); 
else nextTime[i]=time+min(pt, nt); // It should hit one wall 

} // if (acc[i] != 0.0) 

else if (vel[i]<0.0) nextTime[i] = time-(10.0+pos[i])/vel[i]; 
else if (vel[i]>0.0) nextTime[i] = time+(10.0-pos[i])/vel[i]; 
else nextTime[i]=getEngine0.getClock0.getEndTime0; // vel[i)==0 

// method:setNextHitTime(private; void; int:i;) 


// Discriminant 1 
// Discriminant 2 


et; 

et; 


method:move(private; void;) 

{ 

double dt = getTime()-time; 

double ddt = dt*dt; 

for (uint i=0; i<3; ++i) 

{ 

pos[i] += dt*vel[i]+acc[i]*ddt*0.5 
vel[i] += dt*acc[i]; 

} 

time = getTime(); 


// method:move(private; void; double time) 

// Delta time 
// Get the delta time 
// Loop over the axes 

// Get the current position 
// Get the current velocity 
// for (uint i=0; i<3; ++i) 
// Update the time 
// method:move(private; void; double time) 


method:getMinAxis(private; int;) 
{ 

int axis = 0; 


// method:getMinAxis(private; int;) 
// Initialize the axis value 


for (uint i=l; i<nextTime.size(); ++i) 
if (nextTime[axis]>nextTime [i]) axis=i; 
return axis; 


// Loop over the axes 
// Get min time 
// Return that axis value 


// method:getMinAxis(private; int;) 


mode:Default 

{ // mode:Default 


401 






node:start[StartSimulation:s] 

[hit:out=>(me;):(nextTime[out.axis])] 

{ // node:start[StartSimulation:s][hit:out=>(me;)] 

out. axis = getMinAxis () ; // Axis for the impact 

} // node:start[StartSimulation:s][hit:out=>(me;)] 

node:addView[AddView:in] [set_motion:out=>(in.getSource 0;)] 

( // node:addView[AddView:in][set_motion:out=>(in.getSource();)] 

out.set(time,pos,vel,acc); // Set parameters in new view 

out.index = in.index; // Set the index value, too 

} // node:addView[AddView:in][set_motion:out=>(in.getSource();)] 

node:change[hit:in] 

[hit:out=>(me;):(nextTime[out.axis]) , 
set_motion:sm[]] 

{ II node:change[hit:in][hit:out=>(me;)] 

move(); // Move the particle to the current position 

vel[in.axis] = -vel[in.axis]; // Change the velocity 

setNextHitTime(in.axis); // Set next hit time for specified axis 

out.axis = getMinAxis0; // Axis for the impact 


std::map<process, gvm::object_index>:riterator i; 
for(i=views.begin 0; i!=views.end(); ++i) / 

{ 

sm.push_back(me); 

sm.back().addDest(i->first); // Ac 

sm.back 0.index = i->second; 

sm.back().set(getTime(),pos,vel,acc); 


r // For index 

// Loop over index map 


// Make new msg 

st); // Add a destination to it 

ond; // Specify the index 

pos, vel, acc); // Specify motion 

// for (i=views.begin0; i!=views.end{); ++i) 
// node:change[hit:in][hit:out=>(me;)] 

// mode:Default 
// process:particle 


C.3.5. set_motion.msg 

(import message (SetValue] 
(import std (<vector>} } 


message:set_motion(SetValue) 
{ 

double:t; 
double:a[3]; 
double:p[3]; 
double:v[3]; 


// message:set_motion(SetValue) 
// Effective time of these settings 
// Acceleration to set 
// Position to set 
// Velocity to set 


method:set(public; void; double:T; 

std::vector<double>:P; 
std::vector<double>:V; 


// Effective time 
II Position at T 
// Velocity at T 


t=T; 

p=resi 2 e 

vector(3, 

0.0, 

P) 

v=resize 

vector(3, 

0.0, 

V) 

a=resize 

vector(3, 

0.0, 

A) 


std::vector<double>:A;)// Acceleration at T 

II method:set(public; void; double!; ... ) 
// Set the effective time 
P); // Set the position at that time 

V); // Set the velocity at that time 

A) ; // Set the acceleration at that time 

II method:set(public; void; double!; ... ) 


method:get!(public; double;) { return t; } // Ge 
method:getP(public; std::vector<double>;) ( return p; } 
method:getV(public; std::vector<double>;) { return v; } 
method:getA(public; std:;vector<double>;) { return a; } 


// Get the effective time 
return p; } // Get pos 
return v; } // Get vel 
return a; } II Get acc 
// message:set_motion(SetValue) 


C.3.6. gvm/gvmBounceView.h 

#ifndef GVMBOONCEVIEW_H_INCLUDED 
#define GVMBOUNCEVIEW_H_INCLODED 

tinclude "gvmView3D.h" 


402 






// namespace gvm 


namespace gvm 
{ 

class BounceView : public View3D 
{ // class BounceView : public ViewSD 

public: 

BounceView(void); // Class constructor 


virtual bool isDisplay(void); II Should we update the display? 

virtual void createObject(object_handle); // Create object to view 

}; II class BounceView : public ViewSD 

} // namespace gvm 

#endif 


C.3.7. gvm/gvmBounceView.cxx 

#include <math.h> 

#include "Exception.h" 

#include "gvmBounceView.h" 

♦include "gvmParticle.h" 

namespace gvm 
{ 

BounceView;:BounceView(void) 

{ 

setTitle("BounceView"); 

} 

bool BounceView;:isDisplay(void) 

{ 

return (isVisible && refresh); 

} 


II namespace gvm 
II BounceView;;BounceView(void) 
II BounceView;;BounceView(void) 

II bool BounceView;;isDisplay(void) 
II Should we redisplay 
// bool BounceView;;isDisplay(void) 


void BounceView;;createObject(object_handle h) 

{ // void BounceView;;createObject(object_handle) 

if (h.second >= objectList.size()) II Is this lined up properly 

throw Exception;;Nonspecific("Object count mis-alignment."); 


switch (h.first) II Which, object should we create? 

{ 

case GVM_Particle; // A new Particle instance requested 

objectList[h.second] = new Particle(*this, h.second); 

break; // case GVM_Particle; 

default; II None of the above 

ViewSD;;createObject(h); // Create a default object 

break; II default 

} II switch (t) 

II void BounceView;;createObject(object_type, object_index) 

II namespace gvm 


C.3.8. gvm/gvmParticle.h 

iifndef GVMPARTICLE_H_INCLUDED 
♦define GVMPARTICLE_H_INCLUDED 

♦include <vector> 

♦include "gvmVertexSD.h" 

♦define GVM_Particle GVM_UserObjectOOO 

namespace gvm 
{ 

class BounceView; 

class Particle ; public VertexSD 
{ 

protected; 
double t; 

std;;vector<double> pos; 
std;;vector<double> vel; 
std;;vector<double> acc; 


403 






protected; 

Particle(gvm::BounceViewS,object_type,ulong); // Class constructor 

public: 

Particle(gvm:;BounceViews,ulong); // Class constructor 


#endif 


virtual void setMotion(double, const std::vector<double>&, 

const std::vector<double>S, 
const std::vector<double>&); 

virtual void display(void); // Display the component 

virtual bool isType(object_type); // Check if this is of type t 

// class Particle 
// namespace gvm 


C.3.9. gvm/gvmParticle.cxx 

#include <iostream> 

#include "gvmParticle. h"' 
#include "gvmBounceView.h" 


namespace gvm 

{ 

Particle::Particle(gvm::BounceViews v, object_type t, ulong i) 

: Vertex3D(v, t, i), t(O.O), pos(3,0.0), vel(3,0.0), acc(3,0.0) 

{ // Particle::Particle(gvm::BounceViews, object_type, 

} // Particle::Particle(gvm::BounceViews, object_type, 


ulong) 

ulong) 


Particle::Particle(gvm; :BounceViews v, ulong i) 

: Vertex3D(v,GVM_Particle,i), t(O.O), pos(3,0.0), vel(3,0.0), 
acc(3,0.0) 

{ // Particle::Particle(gvm::BounceViews, ulong) 

} // Particle::Particle(gvm::BounceViews, ulong) 


void Particle: 


t=T; 

pos=P; 

vel=V; 

acc=A; 


;setMotion(double T, 

const std::vector<double>s P, 
const std::vector<double>s V, 
const std::vector<double>s A) 

// void Particle 
// Set the effective time 

// Set the position of the particle at 

// Set the velocity of the particle at 

// Set the acceleration of the particle at 

// void Particle 


: :set(double, ... ) 
for the parameters 
the effective time 
the effective time 
the effective time 
: : set(double, ... ) 


void Particle:;display(void) 

{ 

double dt = getView().getTime()-t; 
for (uint i=0; i<3; ++i) 

loc[i]=pos[i]+(vel[i]+0.5*acc[i]*dt)*dt; 
Vertex3D::display(); 

} 


// void Particle::display(void) 
// Getdelta since last update 
// Loop over each coordinate 
// Get new point location 
// Display the vertex 
// void Particle::display(void) 


bool Particle::isType(object_type c) 

{ // bool Particle::isType(object_type) 

return (c==GVM_Particle || Vertex3D::isType(c)); // Return results 

} // bool Particle::isType(object_type) 

} // namespace gvm 


C.3.10. gvm/gvmSetMotion.h 

#ifndef SETMOTION_H_INCLUDED 
#define SETMOTION_H_INCLDDED 

tinclude "gvmMessage.h" 

#include "gvmObject.h" 

#define GVM_SetMotion GVM_UserMessage000 
namespace gvm 


( 


404 







{ 

class View; 


// namespace gvm 
// Forward declaration of the gvm::View class 


class SetMotion : public Message 

{ // class SetMotion : public Message 


private: 
double t; 

std::vector<GLdouble> p; 
std::vector<GLdouble> v; 
std::vector<GLdouble> a; 


// Effective time stamp of the motion parameters 

// Position at time t 

// Velocity at time t 

// Acceleration at time t 


public: 

SetMotion(Views, double, object_index, double, 

std::vector<GLdouble>, std::vector<GLdouble>, 
std::v6ctor<GLdouble>); 


virtual void send(void); 

}; 

} 

#endif 


// Deliver the message payload 
// class SetMotion : public Message 
// namespace gvm 


C.3.11. gvm/gvmSetMotion.cxx 

#include "Exception.h" 

#include "gvmParticle.h" 

#include "gvmSetMotion.h" 
tinclude "gvmView.h" 


namespace gvm 

{ 

SetMotion::SetMotion(Views v, 
double t, 
object_index i, 
double T, 


// namespace gvm 
// Parent (owning) view of this message 
// Time to process the message 
// Destination object 
// Effective time of motion parameters 


std::vector<GLdouble> P, // Position at time T 

std::vector<GLdouble> V, // Velocity at time T 

std::vector<GLdouble> A) // Acceleration at T 

: Message (V, t, GVM_SetMotion, i), t(T), p(P), v(V), a(A) 

{ // SetMotion::SetMotion(Views, double, object_index, ... ) 

} // SetMotion::SetMotion(Views, double, object_index, ... ) 


// void SetMotion::send(void) 


void SetMotion::send(void) 

{ 

gvm::Particle *part = 

dynamic_cast<gvm::Particle*>(sgetView()[getDest()]); 
if (part==NULL) 

throw Exception::BadCast("gvm::Object", "gvm::Particle"); 
part->setMotion(t,p,V,a); // Set motion parameters of destination 

} // void SetMotion::send(void) 

} // namespace gvm 


C.4. Brigadel 


C.4.1. battalion.proc 

{import process {unit, company) } 

{import message {StartSimulation, SetColor) } 


process:battalion(unit) 

{ 

company:subordinates[4] ; 


// process:battalion(unit) 
II Subordinate objects 


method:init(protected; void;) 

{ 

unit::init(); 
subs = subordinates; 

sub_labels .push_bac): (" Alpha Company"); 
sub_labels .push_bac): (" Bravo Company"); 


// method:init(protected; void;) 
// Call parent class version 
// Copy the subordinate handles 


405 







} 


// method:init(protected; void;) 


sub_labels .push_bacl< (" Charlie Company"); 
sub_labels .push_bacl< (" Delta Company"); 


} 

) 

C.4.2. brigade.proc 

{import process {unit, battalion, View2D, Polygon2D, Vertex2D} } 

{import message {order, set_parent, StartSimulation, SetColor, 

AddVertex2D, SetVertex2D, AddNode2D, SetMode, SetSize, 
SetPosition, SetRefresh} } 

{import std {<string>, <iostream>) } 


process:brigade(unit) 

{ 

battalion:subordinates[4] ; 
View2D:view; 

Polygon2D:rect; 

Vertex2D:vert[4]; 


// Subordinate battalions 
// View where stuff is seen 
// A rectangle everbody will reference 
// The vertices of the rectangle 


method:init(protected; void;) 

{ 

unit::init(); 
subs = subordinates; 

sub_labels .push_bacl< (" 1st Battalion"); 
sub_labels.push_back(" 2nd Battalion"); 
sub_labels.push_back(" 3rd Battalion"); 
sub_labels.push_back(" 4th Battalion"); 


// method:init(protected; void;) 
// Call parent class version 
// Copy the subordinate handles 


// method:init(protected; void;) 


method:getScale(public; std::vector<double>;) 

{ return make_vector(2, 1.0, 1.0); ) 

method:getTranslation(public; std::vector<double>;) 
{ return make_vector(2, 0.0, 0.0); ) 


mode:start 
{ 

node:start[StartSimulation;strt] 

[order:out=>(me;):(0.0), 
set_parent:sp=>(me;), 
AddVertex2D:av=>(rect;), 
SetVertex2D:sv[4]=>(vert[@];), 
AddNode2D:an=>(view;), 

SetMode:sm=>(rect;), 

SetSize:ss=>(view;), 
SetPosition:spos=>(view;), 
SetRefresh:srfsh=>(view;)] 

{ // node; 

sp.instance = 0; 
sp.label = "Brigade"; 
sp.parent_node = unit_node; 
sp.rect = rect; 


// mode:start 
// Initial message 
// Start sim at time 0 
// Set subordinate parent handle 
// Add vertex loc to shape 
// Set vertex locations 
// Add the node to the view 
// Set the rectangle mode 
// Set window size 
// Set window position 
// Set refresh interval 
start[StartSimulation:strt][...] 
// Set the instance number 
// Set this instance label 
// Set the unit handle 
// Set the rectangle handle 


sm.set(GL_QDADS) 


// Filled rectangle 


out.data = 5.0+random.nextDouble(5.0) ; 


// Generate data value 


an.add(unit node); 


// Add brigade's node as root node in view 


sv[0].set(-50.0, -0.5); 
sv[l].set( 50.0, -0.5); 
sv[2].set( 50.0, 0.5); 

sv[3].set(-50.0, 0.5); 

for (int i=0; i<4; ++i) 
av.add{vert [i]) ; 


// First vertex 
// Second vertex 
// Third vertex 
// Fourth vertex 
// Loop over the vertices 
// Specify the vertices 


ss.set(800,600); 
spos.set(100,100); 


// Set the viewport size to 800x600 
// And positioned at (100,100) 


406 







srfsh.set(0.1) ; 


// Set it to refresh every 0.5 time unit 
II node:start[StartSimulation:strt][...] 

// mode:start 


} 

} 


} 


} 


C.4.3. company.proc 

{import process {unit, platoon) ) 

{import std {<string>, <iostreain>) } 

{import message {StartSimulation, SetColor} } 


process:company(unit) 

{ 

platoon:subordinates[4]; 


} 

} 


method:init(protected; void;) 
{ 


unit::init(); 
subs = subordinates; 
sub_labels .push_bac)<; ( 
sub_labels.push_back( 
sub_labels .push_bacl< { 
sub_labels.push_back{ 


1st Platoon") 
2nd Platoon") 
3rd Platoon") 
4th Platoon") 


// process:company(unit) 


// method:init(protected; void;) 
// Call parent class version 
// Copy the subordinate handles 
// Subordinate label values 


// method:init(protected; void;) 

// process:company(unit) 


C.4.4. order.msg 

{ 

message:order 
{ 

double:data(0.0); 

} 

} 


C.4.5. platoon.proc 

{import process {unit, squad) > 

{import std {<string>, <vector>, <iostream>} ) 
{import message {StartSimulation, SetColor) ) 


process:platoon(unit) 

{ // process:platoon(unit) 

squad:subordinates[4]; 


} 


method:init(protected; void;) 

{ 

unit::init(); 
subs = subordinates; 
sub_labels.push_back{" Squad 
sub_labels.push_back{" Squad 
sub_labels.push_back(" Squad 
sub_labels.push_back(" Squad 

} 


1 "); 
2 "); 
3”) ; 
4") ; 


// method:init(protected; void;) 
// Call parent class version 
// Copy the subordinate handles 
II Subordinate label values 


// method:init(protected; void;) 

// process:platoon(unit) 


C.4.6. report.msg 

{ 

message:report 
{ 

long:instance(0); 

} 

} 


407 








C.4.7. set_parent.msg 

{import std {<string>} } 


{ 

message:set_parent 
{ 

int:instance; 
std::string:label; 
process:parent_node; 
process:rect; 

} 


// message:set_parent 
II Subordinate instance number of the destination 
// Label of the destination 
// Node for the parent 
II Polygon for the figure to use 
// message:set_parent 


C.4.8. soldier.proc 

{import process {unit} } 

{import message {order, report, SetColor, StartSimulation} } 
{import std {<string>, <iostreara>} ) 

{ 

process:soldier(unit) 

{ 

method:init(protected; void;) 

{ 

unit::init(); 
subs.push_back(me); 

} 


// method:init(protected; void;) 
// Call parent class version 
II Copy the subordinate handles 
II method:init(protected; void;) 


method:fossilCollect(protected; void;) 

{ // method:statusReport(protected; void; sim:reports:in;) 

if (getTimeO >= 0.0) // If the timestamp is not negative 

{ 

if (waiting_for_orders.isActive0) // If waiting for orders 

{ 

std::cout << getXimeO « << std::endl; 

std::cout << getLabelO « " following orders: " 

<< sub_times[0] « std::endl << std::endl; 

} II if (waiting_for_orders.isActive()) 

else // If it's anything else 

unit::fossilCollect0; // Let the parent class take care of it 

} // if (getTimeO >= 0.0) 

} // method:statusReport(protected; void; sim:reports:in;) 

method:getScale(public; std::vector<double>;) 

{ return make_vector(2, 1.0, 1.0); } 


method:getTranslation(public; std::vector<double>;) 

{ return make_vector(2, 0.0, -(2.0+1.5*((float) instance))); } 

mode:waiting_for_orders 

{ // mode:waiting_for_orders 

node:receive[order:ord)[report:out=>(me;):(sub_times[0])] 

{ // node:receive[order:ord][order:out[4]] 

sub_times.push_back(getTime()+random.nextDouble(ord.data)); 

} // node:receive[order:ord)[order:out[4]] 

} // mode;waiting_for_orders 

) II process:soldier(unit) 

} 


C.4.9. squad.proc 

{import process {unit, soldier} } 

{import message {StartSimulation, SetColor} } 
{import std {<string>, <iostream>} } 


process:squad(unit) 

{ // process:squad(unit) 

soldier:subordinates[10] ; 


408 







method:init(protected; void;) 


} 


unit::init{); 
subs = subordinates; 
sub_labels .push_bacl< (" 
sub_labels.push_back{" 
sub_labels.push_back(" 
sub_labels.push_back{" 
sub_labels.push_back(" 
sub_labels.push_back (" 
sub_labels.push_back(" 
sub_labels.push_back {" 
sub_labels.push_back(" 
sub_labels.push_back{" 


Leader"); 
Radioman"); 

Heavy gunner 1"); 
Heavy gunner 2"); 
Corpsman"); 
Demolitionist"); 
Infantryman 1"); 
Infantryman 2"); 
Infantryman 3"); 
Infantryman 4"); 


II method;init(protected; void;) 
// Call parent class version 
// Copy the subordinates 
// Subordinate label values 


// method:init(protected; void;) 

// process:squad(unit) 


C.4.10. unit.proc 

(import process {Node2D} } 

(import message (report, order, set_parent, SetColor, AddShape2D, 

AddNode2D, SetLabel, SetAffine2D, StartSimulation) ) 
(import std (<vector>, <iostream>, <string>} } 

(import (<stdlib.h>, <time.h>} } 


process:unit 
{ 

int:instance; 

int:units_done(0); // 

process:parent; 

process:subs [ ]; 

std::string:label ("") ; 

std::string:sub_labels[]; 

double:sub_times[I; 

Node2D:unit node; 


// Subordinate instance of this unit 
s the entire unit done with it's task? 

// Parent unit 
// Handles to subordinate units 
// Label for this unit 
// Labels for subordinate units 
// Timestamp for subordinate units 
// Graphics node for this unit 


method:setLabel(protected; void; std::string:l;) { label = 1; } 
method:getLabel(protected; std::string;) ( return label; } 


method:fossilCollect(protected; void;) 

{ // method:fossilCollect(protected; void;) 

char ch; 


if (getTimeO >-0.0) II If the timestamp is not negative 

{ 

if (waiting_for_orders.isActive()) // Unit waiting for orders? 

{ 

std::cout « getTimeO « « std::endl; 

std::cout « getLabelO << " issuing orders:" « std::endl; 
for (int i=0; i<subs.size(); ++i) 

std::cout << "\t" « sub_labels[i] « " — " « sub_times[i] 

<< std::endl; 

} II if (waiting_for_orders.isActive ()) 

else if (working.isActive() S& units_done==subs.size()) 

( 

std::cout « getTimeO « « std::endl; 

std:;cout << getLabelO « " reports objective achieved." 

<< std::endl « std;:endl; 

} // else if (working.isActive 0) 

} II if (getTimeO >= 0.0) 

) // method:fossilCollect(protected; void;) 

method:getScale(public; std::vector<double>;) 

( return make_vector(2, 0.20, 1.0); } 

method:getTranslation(public; std::vector<double>;) 

{ return make_vector(2, 150.0*((double) (instance-1.5)), -1.5); } 

mode:start 


409 







{ // mode:Default 

node:startSimulation[StartSimulationrin] 

[SetColor:sc=>(unit_node;)] 

{ sc.setd.O, 0.0, 0.0, 1.0); ) 


node:setParent[set_par6nt:in] 


// Upon reciept of set_parent 


[set_parent:out[]=>(subs[@];), 
AddShape2D:as=>(unit_node;) , 
AddNode2D:an=>(in.parent_node;), 
SetAffine2D:sat=>{unit_node;) , 
SetLabel:sl=>(unit node;)] 


// Send subordinates 
// Add in.rect to node 
// Add to parent 
// Set transform 
// Set tine unit's label 


// node:setParent[set_parent:in][set_parent:out[]] 


parent=in.getSource(); 
instance = in.instance; 
setLabel(in.label); 
si.set(in.label); 


// Set tire parent liandle 
// Set subordinate instance number 
// Set this instance label 
// Label value to set for the unit node 


as.add(in.rect); 
an.add(unit node); 


// Add shape as a subordinate to the node 
// Add unit_node as subordinate to parent 


sat.setScale(getScale()); 

sat.setTranslation(getTranslation()); 


// Set the node scaling factor 
// Set node translation 


if (getTypeO != SPT_soldier) 

{ 

for (int i=0; i<subs.size(); 


++i) 


// If not a soldier 
// Loop over output messages 




} 


out .push_bac)<; (me) ; 
out .bac)c 0 .instance = i; 
out .bac)c () ,rect = in.rect; 
out.baclc() ,parent_node = unit_node; 
out.bacli (). label = label+sub_labels [i] ; II Set label number 

// for (int i=0; i<subs.size(); ++i) 
// if (getTypeO != SPT_soldier) 


// Create a new output message 
// Set the instance number 
// Pass the info along 
// Pass this along, too 


start.setActive(false); // Deactivate the start mode 

waiting_for_orders.setActive(true); // Actvt waiting_for_orders 

an.setTX(getType() != SPT_brigade); // To avoid a loop 

} // node:setParent[set_parent:in][set_parentrout[]] 

// mode:Default 


mode:waiting_for_orders 

{ // mode:waiting_for_orders 

node:startSimulation[StartSimulation:in][] 

{ waiting_for_orders.setActive(false); } 

node:receive[order;ord] 

[order:out[]=>(subs[@];):(sub_times[@] ) , 

SetColor:sc=>(unit_node;)] 

{ // node:receive(order:ord][order:out[4]] 

sc.setll.O, 1.0, 0.0); // Set the color to yellow 


) 


if (getTypeO != SPT_soldier) 

{ 

for (int i=0; i<subs.size(); ++i) // Loop over subordinates 

{ 

sub_times .push_back (getTime 0+randoin.nextDouble (ord. data) ) ; 
out.push_bac]c (me) ; // Create a new order 

out .bac]c () . data=ord.data+random.nextDouble (ord. data) 12.0; 

} II for (int i=0; Ksubs . size () ; ++i) 

} II if (getTypeO != SPT_soldier) 

waiting_for_orders.setActive(false); II No longer waiting 

wor):ing. setActive (true) ; // We are now worlcing 

// node:receive[order:ord][order:out[4]] 
// mode:waiting_for_orders 


410 






C.5. Brigade2 


C.5.1. battalion.proc 

(import process (unit, company} } 


process:battalion(unit) 

{ 

company:subordinates[4] ; 


// processibattalion(unit) 
// Subordinate objects 


} 


method:init(protected; void;) 

{ 


} 


unit::init(); 
subs = subordinates; 
sub_labels.push_back(" 
sub_labels .push_baclc (" 
sub_labels .push_baclc (" 
sub_labels .push_bac)c (" 


Alpha Company"); 
Bravo Company"); 
Charlie Company"); 
Delta Company"); 


// method:init(protected; void;) 
// Call parent class version 
// Copy the subordinate handles 


// method:init(protected; void;) 


C.5.2. brigade.proc 

(import process (unit, battalion) } 

(import message (order, set_parent, StartSimulation) } 
(import std (<string>, <iostream>) ) 


process;brigade(unit) 

{ 

battalion:subordinates[4]; 


method:init(protected; void;) 

{ 


} 


unit:;init(); 
sub_count = 4; 
subs = subordinates; 
sub_labels.push_back{" 
sub_labels.push_back(" 
sub_labels.push_back(" 
sub_labels.push_back{" 


1st Battalion"); 
2nd Battalion"); 
3rd Battalion"); 
4th Battalion"); 


// method:init(protected; void;) 
// Call parent class version 
// We have four subordinates 
// Copy the subordinate handles 


// method:init(protected; void;) 


mode:start 
( 

node:start[StartSimulation:strt] 

[order:out=>(me;):(0.0), 
set_parent:sp=>(me;)] 

( // 

sp.instance = 0; 
sp.label = "Brigade"; 
out.data = 5.0+random.nextDouble( 
} // 

} 

} 

} 


// mode;start 
// Initial message from engine 
// Actually start sim at time 0 
// Set parent for subordinates 
node:start[StartSimulation:strt][...] 
// Set the instance number 
// Set this instance label 
i.O); // Generate data value 

node:start[StartSimulation:strt][...] 

// mode:start 


C.5.3. company.proc 

(import process (unit, platoon) } 
(import std (<string>, <iostream>} } 


process:company(unit) 

( II process:company(unit) 

platoon:subordinates[4] ; 


411 







method:init(protected; void;) 

{ 

unit::init (); 
subs = subordinates; 

sub_labels.push_back(" 1st Platoon"); 
sub_labels .push_baclc (" 2nd Platoon"); 
sub_labels .push_bacl<; (" 3rd Platoon"); 
sub_labels .push_bacl<; (" 4 th Platoon"); 

} 

} 

) 

C.5.4. order.msg 

{ 

message:order 
{ 

double:data(0.0); 

} 

} 

C.5.5. platoon.proc 

{import process {unit, squad) ) 

{import std {<string>, <vector>, <iostream>} ) 

{ 

process:platoon(unit) 

{ // process:platoon(unit) 

squad:subordinates[4]; 

method:init(protected; void;) 

{ 

unit::init {); 
subs = subordinates; 
sub_labels .push_bacl< (" Squad 1"); 
sub_labels .push_bacl< (" Squad 2"); 
sub_labels .push_bac)c {" Squad 3"); 
sub_labels .push_bacli {" Squad 4") ; 

} 

} 

} 

C.5.6. report.msg 

{ 

message;report 
{ 

long:instance(0); 

} 

} 

C.5.7. set_parent.msg 

{import std {<string>} } 

{ 

message:set_parent 
{ 

int:instance; 
std::string:label; 

} 


C.5.8. soldier.proc 

{import process {unit} } 

{import message {order, report) } 
{import std {<string>, <iostream>) } 


II message:set_parent 
// Subordinate instance number of the destination 
II Label of the destination 
// message:set_parent 


// method:init(protected; void;) 
// Call parent class version 
// Copy the subordinate handles 
// Subordinate label values 


II method:init(protected; void;) 

// process:platoon(unit) 


// method:init(protected; void;) 
// Call parent class version 
// Copy the subordinate handles 
// Subordinate label values 


II method:init(protected; void;) 

// process:company(unit) 


412 







process:soldier(unit) 

{ 

method:init(protected; void;) 
{ 

unit::init(); 
sub_count = 1; 
subs .push_bacl<; (me) ; 


// method:init(protected; void;) 
// Call parent class version 
// Soldier has no subordinates 
II Copy the subordinate handles 
// method:init(protected; void;) 


method:fossilCollect(protected; void;) 

{ n method:statusReport(protected; void; sim:reports:in;) 

if (getTime() >= 0.0) // If the timestamp is not negative 

{ 

if (waiting_for_orders.isftctive()) // If unit waiting for orders 

{ 

std::cout « getTimeO « ":” « std::endl; 
std:;cout « getLabelO << " following orders: " 

« sub_times[0] « std::endl « std::endl; 

} II if (waiting_for_orders.isActive()) 

else // If it's anything else 

unit::fossilCollect0; // Let parent class take care of it 

} // if (getTimeO >= 0.0) 

} // method:statusReport(protected; void; sim:reports:in;) 

mode:waiting_for_orders 

{ // mode:waiting_for_orders 

node:receive[order:ord] 

[report:out=>(me;):(sub_times[0])] 

{ // node:receive[order:ord][order:out[4]] 

sub_times.push_back(getTime0trandom.nextDouble(ord.data)); 
subs_done.push_back(false); // The subordinates are not done 

} // node:receive[order:ord][order:out[4]] 

} // mode:waiting_for_orders 

// process:soldier(unit) 


C.5.9. squad.proc 


(import process (unit, soldier) } 
(import std (<string>, <iostream>} } 


process:squad(unit) 


soldier:subordinates[10]; 


// process:squad(unit) 


method:init(protected; 

( 

unit::init(); 
subs = subordinates; 
sub_labels.push_back 
sub_labels.push_back 
sub_labels.push_back 
sub_labels.push_back 
sub_labels.push_back 
sub_labels.push_back 
sub_labels.push_back 
sub_labels.push_back 
sub_labels.push_back 
sub_labels.push_back 


void;) 


(" Leader"); 

(" Radioman"); 

{" Heavy gunner 1") ; 
(" Heavy gunner 2"); 
(" Corpsman"); 

{" Demolitionist") ; 
(" Infantryman 1"); 
(" Infantryman 2"); 
(" Infantryman 3"); 
(" Infantryman 4"); 


// method:init(protected; void;) 
// Call parent class version 
// Copy the subordinates 
// Subordinate label values 


// method:init(protected; void;) 

// process:squad(unit) 


C.5.10. unit.proc 

(import message (report, order, set_parent, StartSimulation) } 
(import std (<vector>, <iostream>, <string>} } 

(import (<stdlib.h>, <time.h>) } 


413 






process:unit 
{ 

int;instance; 

int:sub_count; 

bool:unit_done(false); 

bool:subs_done[]; 

process:parent; 

process:subs[]; 

std::string:label ( "") ; 

std::string:sub_labels[]; 

double:sub times[]; 


// Subordinate instance of this unit 
// # of subordniates 
// Is the entire unit done with it's task? 
// Subordinates reporting that they're done 

// Parent unit 
// Handles to subordinate units 
// Label for this unit 
// Labels for subordinate units 
// Timestamp for subordinate units 


method:setLabel(protected; void; std::string:1;) { label = 1; } 
method:getLabel(protected; std::string;) { return label; } 


method:fossilCollect(protected; void;) 

{ // method:fossilCollect(protected; void;) 

if (getTimeO >= 0.0) // If the timestamp is not negative 


if (waiting_for_orders.isActive()) // If unit waiting for orders 

3td::cout « getTimeO << << std::endl; 

std::cout << getLabelO « " issuing orders:" « std::endl; 

for (int i=0; i<sub_count; ++i) 

std::cout << "\t" « sub_labels[i] « " — " « sub_times[i] 

« std::endl; 

} // if (waiting_for_orders.isActive0) 

else if (working.isActive() s& unit_done) // If unit is done 

{ 

std::cout << getTimeO << ":" << std::endl; 

std::cout « getLabelO « " reports objective achieved." 

<< std::endl << std::endl; 

} // else if (working.isActive0) 

} // if (getTimeO >= 0.0) 

} // method:fossilCollect(protected; void;) 


mode:start 

{ // mode:Default 

node:setParent[set_parent:in] 

[set_parent:out(]=>(subs[@];)] 

{ // node:setParent[set_parent:in][set_parent:out[]] 

parent=in.getSource0; // Set the parent handle 

instance = in.instance; // Set the subordinate instance number 

setLabel(in.label); // Set this instance label 


if (getTypeO != SPT_soldier) 

{ 

for (int i=0; i<sub_count; ++i) 
( 

out.push_back(me); 

out.back().instance = i; 

out.back 0 .label = label+sub_ 

) 

} 


// If not a soldier 

// Loop over output messages 

// Create a new output message 
// Set the instance number 
labels[i]; // Set label number 

// for (int i=0; i<sub_count; ++i) 
// if (getTypeO != SPT_soldier) 


start.setActive(false); // Deactivate the start mode 

waiting_for_orders.setActive(true); II Actvt waiting_for_orders 


} 


// node:setParent[set_parent:in][set_parent:out[]] 

// mode;Default 


mode:waiting_for_orders 

{ // mode;waiting_for_orders 

node;startSimulation[StartSimulation:in][] 

{ waiting_for_orders.setActive(false); } 

node:receive[order:ord] 

[order:out[]=>(subs[@];):(sub_times[@])] 

{ // node:receive[order:ord][order:out[4]] 

if (getTypeO != SPT_soldier) 


414 






for (int i=0; i<sub count; 


// Loop over the subordinates 


sub_times.push_back(getTime{)trandom.nextDouble(ord.data)); 
subs_done.push_back(false); // Subordinates are not done 

out.push_back(me); // Create a new order 

out.back().data=ord.data+random.nextDouble(ord.data)/2.0; 

) // for (int i=0; i<sub_count; ++i) 

} // for (int i=0; i<sub_count; ++i) 

waiting_for_orders.setActive(false); // Not waiting for orders 

working.setActive(true); // We are now working 

// node:receive[order;ord][order:out[4]] 
// mode:waiting_for_orders 


mode:working 
{ 

node:startSimulation[StartSimulationiin][] 
{ working.setActive(false) ; ) 


// mode:working 


node:status[report:in][report:out=>(parent;)] 

{ // node:status[report:in][report:out] 

unit_done = true; // Initialize the unit_done flag 


out.instance = instance; 
subs done[in.instance] = true; 


// Specify the instance 
// This subordinate is done 


for (int i=0; i<subs_done.size(); ++i) 
unit done = unit done && subs done[i]; 


// Loop over subordinates 
// Accumulate the value 


out.setTX(unit_done && parent!=me); // Report to the parent? 

} // node:status[report:in][report:out] 

} // mode:working 

} // process:unit 


C.6. Hierarchy 


C.6.1. generic.msg 

{ 

message:generic 

{ 

ulong:index(0); 

method:set(public; void; ulong:i;) ( index = i; } 
method:get(public; ulong;) { return index; } 

} 


C.6.2. hierarchy.proc 

(import process {View2D, Polygon2D, Node2D, Vertex2D} } 

{import message {SetVertex2D, AddNode2D, AddShape2D, StartSimulation, 
AddVertex2D, SetScale2D, SetTranslation2D, SetColor, 

SetLabel, SetMode, generic, SetRefresh, SetSize} } 

{import {<GL/glut.h>} } 

{ 

process:hierarchy 

{ // process:hierarchy 

View2D:view; 

Node2D:nodes[511]; 

Polygon2D:rect; 

Vertex2D:vert[4]; 

bool:completed[]; 

method:init(public; void;) { completed.resize(nodes.size (), false) ; } 
mode:Default 


415 






{ 

node:start[StartSimulation:in] 

[SetVertex2D:out_sv[4]=>(vert[@];), 

AddNode2D:out_an[]=>( (@==0 ? view 
AddShape2D:out_as=>(nodes;) , 

SetScale2D:out_ss=>(nodes;) , 

SetTranslation2D:out_st[2] , 

AddVertex2D:out_av=>(rect;) , 

SetMode:out_sm=>(rect;) , 

SetRefresh:out_sr=>(view;) , 

SetSize:out_size=>(view;), 
generic:out=>(me;):(0.0)] 

{ 

out_sv[0].set(-100.0, -0.5); 
out_sv[1].set( 100.0, -0.5); 
out_sv[2].set( 100.0, 0.5); 

out_sv[3].set(-100,0, 0.5); 

out_an.push_bacl<; (me) ; 
out_an.back().add(nodes[0]); 

for (int i=0; (i+1)*2<nodes.size(); ++i) 

( 

out_an.push_back(me); 
out_an.back().add(nodes[i*2+l]) ; 
out_an.back().add(nodes[i*2+2]); 

} 

out_as.add(rect); 

out_ss.set(0.5, 1.0); 

out_st[0].set( -150, -1.5); out_st[1].set( 150, -1.5); 

for (int i=l; i<nodes.size(); ++i) 
out_st[i%2].addDest(nodes[i]); 

out_av.add(vert[0]); out_av.add(vert[1]); 
out_av.add(vert[2]); out_av.add(vert[3]); 

out_sr.set(0.5); 

out_size.set(1000, 600); 

out_sm.set(GL_QUADS); 

} 

node:run[generic:in] 

[generic:out=> (me;) : (getTime 0+1.0) , 

SetColor:out_sc=>(nodes[in.get()];)] 

( // node:run[generic:in][generic:out=>(me;)] 


// mode:Default 

: nodes[@-l]); ), 


ulong index = in.getO; 
ulong left = index*2+l; 
ulong right = index*2+2; 
ulong parent = (index-1)/2; 

if (right > nodes.sizeO || 

(completed[left] &S completed[right])) 

{ 

std::cout « "parent in.getO = " « in.getO « std::endl; 

completed[index] = true; 

out.set(parent); 
out.setTX(index!=0) ; 

out_sc.set(0.0, 1.0, 0.0, 1.0); 

} 

else if (!completed[left]) 

{ 


416 






std::cout << 


left in.getO 


« in.getO « std::endl; 


out.set(left); 

out_sc.set(0.0, 0.0, 1.0, 1.0); 

} 

else if (!completed[right]) 

{ 

std::cout << " right in.getO = " « in.getO « std::endl; 
out.set(right); 

out_sc.set(0.0, 1.0, 1.0, 1.0); 

} 

// node:run[generic:in][generic:out=>(me;)] 

// mode:Default 
// process:hierarchy 


C.7. Ping 

C.7.1. Generic.msg 

( message:Generic; } 

C.7.2. Ping.proc 

{import std {<iostream>} } 


[import message [Generic, StartSimulation] 
[import process [Pong] } 


process:Ping 

{ 

int:count(-1); 
Pong:pong; 


// process:Ping 
II Count of the number of messages received 
II Something to send a message to 


method:init(public; void;) { std::cout.precision(16); } 
method:fossilCollect(public; void;) 

{ // method:fossilCollect(public; void;) 

if (getTimeO >= 0) 

std::cout << "Ping (" « count << ") at time " « getTimeO 
« std::endl; 

} // method:fossilCollect(public; void;) 

mode:start 

[ // mode:start 

node:start[StartSimulation:strt] 

[Generic:out=>(me;):(0.0)] 

[ // node:start[StartSimulation:nm][Generic:out=>(pong;)] 

start.setActive(false); II Deactivate the start mode 

} // node:start[StartSimulation:nm][Generic:out=>(pong;)] 

} II mode:start 


mode:run 

{ 

node:ponger[Generic:in][Generic:out=>(pong;) [ 
[ out.setTX( ++count < 20 ); } 


// mode:run 


II mode:run 
// process:Ping 


C.7.3. Pong.proc 

[import message [Generic] } 
[import std {<iostream>} } 


process:Pong 


417 







int:count {-!) ; 


// process:Pong 
// How many times have we received a message 


method:fossilCollect(public; void;) 

{ // method:fossilCollect(public; void;) 

if (getTimeO >= 0) 

std::cout « "Pong (" « count « ") at time " « getTime() 

« std::endl; 

} // method:fossilCollect(public; void;) 


mode:Default 

{ 

node:pinger[Generic:in][Generic:out=>(in.getSource();)] 
{ out.setTX(++count<20) ; } 


// mode:Default 


// mode:Default 
// process:Pong 


C.8. Relayl 

C.8.1. generic.msg 

{ message:generic; } 

C.8.2. reflector.proc 

{import message {generic} } 


process:reflector 


int:count(-1) ; 


// process:reflector 
// Count of the number of messages received 


method:init(public; void;) { std::cout.precision(15); } 
method:fossilCollect(public; void;) 

( // method:fossilCollect(public; void;) 

if (count % 10000 == 0) // Every 10000 messages 

std::cout « count << " on <" « me.getNodeO « ", " 

« me.getindex() << "> at time " << getTimeO 
« std::endl; // Display the message to std::cout 

} // method:fossilCollect(public; void;) 

mode:Default 

{ // mode:Default 

node:reflect[generic:in] // Upon reciept of a generic input msg 

[generic:out=>(in.getSource0;)] II Reflect one bade 

{ count++; } 

} II mode:Default 

} II process:reflector 


C.8.3. relay.proc 


{import message {generic, StartSimulation} } 
{import process {reflector} } 


process:relay(reflector) 


reflector:r:1; 


// Something to send a message to 


mode:Default 

{ 

node:start[StartSimulation:strt] 

[generic:out=>(r;):(0.0)] { } 


II mode:Default 


// mode:Default 


418 






C.9. Relay2 


C.9.1. generic.msg 

{ message:generic; } 


C.9.2. reflector.proc 

{import message {generic, set_partner} } 


process:reflector 

{ // process:reflector 

int:count(-1); // Count of the number of messages received 


double:tl; 
double:t2; 
process:partner; 

method:fossilCollect(public; void;) 
{ 

if (count >= 0) 

{ 

std::cout << count << " on " « 
« std::endl; 

} 

} 


// Timestamp of output message 1 
// Timestamp of output message 2 
// Remote partner 


// method:fossilCollect(public; void;) 
// Is this valid to do at this point? 

me « " at time " « getTime() 

//if (count >= 0) 
// method:fossilCollect(public; void;) 


mode:Default 

{ // mode:Default 

node:setPartner[set_partner:in][] { partner = in.getSource(); } 


> 


node:reflect[generic:in] // Upon reciept of a generic input msg 

[generic:outl=>(partner;):(tl), // Reflect one back 

generic:out2=>(me;):(t2)] // Send something back here 

{ // node:reflect[generic][generic, logmsg] 

tl = getTime0trandom.nextDouble(10.0); // Get output timestamp 

t2 = getTime0trandom.nextDouble(10.0); // Get output timestamp 

counttt; // Log the reflection 

} // node:reflect[generic][generic, logmsg] 

// mode:Default 
// process:reflector 


C.9.3. relay.proc 

{import message {generic, set_partner, StartSimulation] } 

{import process {reflector} } 

{ 

process:relay(reflector) 

{ 

reflector:r:1; // Something to send a message to 

mode:Default 

{ // mode:Default 

node:start[StartSimulation:strt] 

[generic:out=>(r;):(0.0), 
set_partner:sp=>(r;):(-0.9)] 

{partner=r;} 

} // mode:Default 

} 

} 


C.9.4. SetPartner.msg 

{message:set_partner;) 


419 







C.10. Relays 


C.10.1. child.proc 

{import message {generic, setup) } 


process:child 
{ 

long:count(-1); 
ulong:cc; 
ulong:ec; 
process:dest; 
double:ts; 


// process:child 
II Count of the number of messages received 
// Number of children 
// Number of engines 
// Destination process 
// Outgoing message timestamp 


method:fossilCollect(public; void;) 

{ II method:fossilCollect(public; void;) 

if (count % 200 == 0) 

std::cout « count « ” on " « me « " at time " « getTime () 

« std::endl; 

} // method:fossilCollect(public; void;) 


mode:start 

{ 

node:relay[setup:in][] 

{ 

cc = in.getChildCount(); 
ec = in.getEngineCount(); 
start.setActive{false); 


// mode:start 

II node:relay[setup:in][] 
// Number of children 
// Number of engines 
// Turn off the start mode 
// node:relay[setup:in][] 
// mode:start 


mode:run 

{ // mode:run 

node:relay[generic:in] 

[generic:out=>(dest;):(ts)] 

{ // node:relay[generic:in][generic:out] 

ulong di = random.nextinteger(cc); // Destination index 

dest = process(di%(ec-1)+1, di/ec); // Set destination processid 

ts = getTime0+random.nextDouble(1.0); // Timestamp 

count++; // Log the reflection 

} // node:relay[generic:in][generic;out] 

} // mode:run 

// process:child 


C.10.2. generic.msg 

{ message:generic; } 

C.10.3. relay.proc 

[import message {StartSimulation, generic, setup) ) 
{import process {child) ) 


process:relay 

{ 

child:children[1000] :I3%100+1; 


mode:Default 

{ // mode:Default 

node:start[StartSimulation:strt] 

[setup:s=>(children;), 
generic:out=>(children;):(0.0)] 

{ II node:start[StartSimulation][setup, generic] 

s . set(children.size(), EngineStand::stand.engineCount()-1); 

) // node:start[StartSimulation][setup, generic) 

) // mode:Default 


420 







} 

} 

C.10.4. setup.msg 

{ 

message:setup 
{ 

ulong:childCount; 
ulong:engineCount; 

method:set(public; void; ulongtc; ulong:e;) 

{childCount=c; engineCount=e;) 
method:getChildCount(public; ulong;) { return childCount; } 
methodigetEngineCount(public; ulong;) { return engineCount; } 

) 


C.11. Relay4 


C.11.1. child.proc 

{import message {generic, setup, subscribe, unsubscribe) } 


process:child 
{ 

long:count(-1); 

ulong:ct; 

long:di[3]; 

double:ts[3]; 

process:subscriptions[]; 


// process:child 
// Count of the number of messages received 
// Number of children 
// Destination index 
// Outgoing message timestamp 
// Subscription process handles 


method:fossilCollect(public; void;) 

{ // method:fossilCollect(public; void;) 

if (count%100 == 0) // For the run phase 

std::cout « count « " on " << me « ” at time " « getTime () 

« std::endl; 

} // method:fossilCollect(public; void;) 

mode:start 

{ // mode:start 

node:relay[setup:in] 

[subscribe:sub=>(subscriptions[di[1]];):(ts(1])] 

{ // node:relay[setup:in][Subscribe:sub] 

subscriptions = in.getO; // Get the subscription handles 

ct = ((ulong) subscriptions.size 0); // Number of subscriptions 

di[1]=random.nextInteger(ct); // Determine subscription to join 

ts[l] = -0.9; // Timestamp for the subscription event 

start.setActive(false); // Turn off the start mode 

} // node:relay[setup:in][Subscribe:sub] 

} // mode:start 

mode:run 

{ // mode:run 

node:relay[generic:in] // Generic inbound message 

[generic:out=>(subscriptions[di[0]];):(ts[0]), 
subscribe:sub=>(subscriptions[di[1]];):(ts[1]) , 
unsubscribe:unsub=>(subscriptions[di[2]];):(ts[2])] 

{ II node: relay [generic: in] [ ... ] 


for (long i=0; i<3; ++i) 

{ 

di[i] = random.nextinteger(ct); 

ts[i]=getTime()trandom.nextDouble(1.0); 

} 

count++; 


// Loop over the messages 

// Get the destination 
// Get timestamp 
// for (i=0; i<3; ++i) 
// Log the reflection 
// node:relay[generic:in] [ ... ] 
// mode:run 
// process:child 


421 







C.11.2. generic.msg 

{ message:generic; } 

C.11.3. relay.proc 

{import message {StartSimulation, generic, setup} } 
{import process {child, subscription} } 


process:relay 

{ 

subscription:sub[2]; 
child:children[4]:@; 


// process:relay 
// Subscription instances for the program 

// Child processes 


mode:Default 

{ // mode:Default 

node:start[StartSimulation:strt] 

[setup:s=>(children;), 
generic:out=>(sub;):(0.0)] 

{ II node:start[StartSimulation][setup, generic] 

s.set(sub); // Set the subscription 

} // node:start[StartSimulation][setup, generic] 

} // mode:Default 

// process:relay 


C.11.4. setup.msg 

[import std {<vector>} } 

{ 

message:setup 

{ // message:setup 

process:sub[]; // Subscription reference 

method:set(public; void; process:s[];) { sub = s; } 
method;get(public; std::vector<process>;) { return sub; } 

} // message:setup 


C.11.5. subscribe.msg 

{message:subscribe;} 


C.11.6. subscription.proc 

{import message {generic, subscribe, unsubscribe} } 
{import std {<set>} } 


process:subscription 
{ 

std::set<process>:subscribers; 


II process:subscription 
// All of the subscription 


mode:Default 
{ 

node:subscribe[subscribe:in][] 

{ subscribers.insert(in.getSource() 


// mode:Default 


node:unsubscribe[unsubscribe:in][) 

{ subscribers.erase(in.getSource( 


)); } 


node 

{ 


: forward[generic:in] [generic:out[]} 


// Forward generic msg 


out.push_back(in) ; 
out.back().clearDeSt(); 
std::set<process>::iterator 
for (i=subscribers.begin 0; 
out.back().addDest(*i); 


// node:forward[generic:in][generic:out[]] 
// Copy the input message 
// Clear the destination list 
i; // subscribers iterator 

i!=subscribers.end(); ++i) 

// Change the destination list 
// node:forward[generic:in][generic:out[]] 


422 






} 

} 

} 

C. 11.7. unsubscribe.msg 

{message:unsubscribe;} 

C.12. Relays 

C.12.1. base.proc 

{import message {generic} } 

{import std {<string>} } 

{ 

process:base 
( 

long:count(-1); 
std::string:label; 

method:init(public; void;) { std::cout.precision(15); } 

method:fossilCollect(public; void;) 

{ // method:fossilCollect(public; void;) 

if (count%1000 == 0) 

std::cout « label « ": (" « count « ") @ " << getTimeO 
« std::endl; 

} // method:fossilCollect(public; void;) 

mode:Default 

{ 

node:routine[generic:in][1 { ++count; } 

} 

} 

> 

C.12.2. generic.msg 

{ message:generic; } 

C.12.3. relay.proc 

{import message {generic} } 

{import process {base, sinjc} } 

{ 

process:relay(base) 

{ 

sin]c: s :me . getNode ()+1; // Sinlc for the message stream 

method:init(public; void;) { base::init(); label = " relay"; ) 
mode:Default 

{ // mode:Default 

node:run[generic:in][generic:out=>(s;)] {} 

} // mode:Default 

} 

1 

C.12.4. sink.proc 

{import process {base} ] 

{ 

process: sin): (base) 

{ // process : sinlc (base) 

method:init(public; void;) { base::init(); label = " sink"; } 

} // process:sink(base) 


// mode:Default 
// Increment counter 
// mode:Default 
// process:base 


// process:base 
// Counter 

// Label for this base instance 


// mode:Default 
// process:subscription 


423 






C.12.5. source.proc 

{import message {StartSimulation, generic} } 

{import process {relay, base} } 

{ 

process:source (base) 

{ // process:source(base) 

relay:r:me.getNode()+1; // Relay process 

method:init(public; void;) { base::init() ; label = "source"; } 


mode;Default 

{ // mode;Default 

node:start[StartSimulation:s][generic:out=>(me; r;):(0.0)} {} 

node:run[generic:in][generic:out=>(me; r;)] {} 

} // mode:Default 

} // process:source(base) 

} 


C.13. Relays 


C.13.1. base.proc 

{import message {generic} } 
{import std {<string>} } 


// process:base 
// Counter 

// Label for this base instance 


process:base 
{ 

long:count(-1); 
std::string:label; 


method:fossilCollect(public; void;) 

{ // method:fossilCollect(public; void;) 

if (getTimeO >= 0) 

{ 

ulong p = std::cout.precision 0; 
std::cout.precision(12); 

std:;cout « label « ": (" « count « ") @ " « getTimeO 
« std::endl; 
std::cout.precision(p) ; 

} 

} // method:fossilCollect(public; void;) 


method:round(public; double; double:t;) 

{ return floor(t*10.0+0.5)/lO.0; } 

mode:Default 

{ 

node:routine[generic:in][] { ++count;} 

} 

} 

} 


// mode:Default 
// Increment counter 
// mode:Default 
// process:base 


C.13.2. generic.msg 

{ message:generic; } 

C.13.3. relay.proc 

{import message {generic, StartSimulation} } 
{import process {base, sin}:} } 

{ 

process:relay(base) 

{ 


424 






sink:s:l; // Sink for the message stream 

ulong:ct(0); // Counter 

method:init(public; void;) { base::init(); label = ” relay"; } 

mode:Default 

{ // mode:Default 

node:start[StartSimulation:in][generic;out=>(me;s;):(0.0)] { } 


node:run[generic:in] 

[generic: out=> (me;) : (round (getTime 0+0.1))] 

{ // node:run[generic:in][generic:out=>(me;)] 

if (++ct%10==0) out.addDest(s); // Do we send it to s, too 

} // node:run[generic:in][generic:out=>(me;)] 

} // mode:Default 

} 

} 


C.13.4. sink.proc 

[import message [generic] } 

[import process [base] } 

[import [<math.h>} } 

[ 

process:sink(base) 

[ // process:sink(base) 

double:It(-1.0); 

method:init(public; void;) ( base::init(); label=" sink”; } 
mode:Default 

[ // mode:Default 

node:run[generic:in] [generic:out=>(me;):(round(getTime ()+1.0))] 

[ out.setTX(fabs(getTime0-It)>0.1); lt=getTime(); ] 

} // mode:Default 

} // process:sink(base) 

} 

C.14. Ring1 

C.14.1. Generic.msg 

[import message [ReportValue] } 

(message:Generic(ReportValue);) 

C.14.2. Reportlndex.msg 

[import message [ReportValue] } 

[ message:Reportindex(ReportValue); } 

C.14.3. ReportSize.msg 

(import message [ReportValue) } 

[message:ReportSize(ReportValue);} 

C.14.4. ReportValue.msg 

[ 

message:ReportValue 

[ 

ulong:value( ((ulong) -1) ); 
method:set(public; void; ulong:v;) ( value 
method:get(public; ulong;) ( return value; 

} 


// message:ReportValue 
// Size of the subscription 
= v; ] // Set the value 

} // Return the value 

// message:ReportValue 


425 






C.14.5. Ring.proc 

{import process {RingMember, Subscription) } 

{import message {Generic, StartSimulation, Setup, ReportSize) } 


process:Ring 
{ 

RingMember:ring[10]; 

Subscription:sub; 

mode:Default 
{ 

node:start[StartSimulation:strt] 

[Setup:s=>(ring;), 

ReportSize:r=>(sub;):(-0.5), 
Generic:out=>(ring[0);):(0.0) 


// process:Ring 
// Ring of elements 
// Subscription list 


s.set(sub) 


// mode:Default 
// Start the simulation 
// Broadcast a setup message 
// Report size to members 
// Start the ring 
// node: start [StartSimulation: strt] [ ... ] 
// Set the subscription parameter 
// node: start [StartSimulation: strt ] [ ... ] 

// mode:Default 
// process:Ring 


C.14.6. RingMember.proc 

[import message {Generic, Setup, ReportSize, Reportindex, Subscribe) ) 


process:RingMember 
{ 

process:sub; 
long:count(-1); 
long:next(0); 
long:index(0); 


// process:RingMember 
// Handle to the subscription process 
// A simple counter 
// Index of next instance 
// Index of this instance 


method:init(protected; void;) { std::cout.precision(16); ) 
method;fossilCollect(public; void;) 

{ // method:fossilCollect(public; void;) 

if (getTimeO >= 0) 

std: :cout « "RingMember)" « index« "] : (" << count 
« ") at time " « getTimeO « std::endl; 

) // method:fossilCollect(public; void;) 


mode:Default 

{ // mode:Default 

node:setup[Setup;in][Subscribe:out=>(sub;)] ( sub=in.get(); ) 
node:setindex[ReportIndex:in][] { index=in.get{); ) 
node;setNext[ReportSize:in][] { next = (index+1) % in.getO; ) 


node:run[Generic:in][Generic:out => 

{ 

out.set(next); // Set 

out.setTX(count++<10); 

} 

} 

} 

} 


(sub;) ] 

// node;run[Generic:in][Generic:out] 
the index of the eventual destination 
// Do we continue transmitting? 
// node:run[Generic:in][Generic:out] 
// mode:Default 
// process;RingMember 


C.14.7. Setup.msg 

{ 

message:Setup 
{ 

process:sub; 
method:set(public; 
method:get(public; 

} 


// message:Setup 
// Subscription reference to use 
void; process:p;) { sub=p; ) // Set the sub 

process;) { return sub; ) // Return the sub 

// message:Setup 


426 






C.14.8. Subscribe.msg 

{message;Subscribe;} 

C.14.9. Subscription.proc 

{import message {Subscribe, ReportSize, Reportindex, Generic) } 


process:Subscription 

( // process:Subscription 

process:subscribers[]; // List of subscribers 


mode:Default 

{ // mode:Default 

node:subscribe[Subscribe:in][Reportindex:out=>(in.getSource{);)] 

{ // node:subscribe[Subscribe:in][Reportindex:out] 

out.set(subscribers.size()); // Index value to report back 

subscribers.push_back(in.getSource0); // Add the subscriber 

} II node:subscribe[Subscribe:in][Reportindex:out] 


} 


node:reports!ze[ReportSize:in][ReportSize:out=>(subscribers;)] 

{ // node;reportSize[ReportSize:in][ReportSize:out] 

out.set(subscribers.size 0); // Set size of output message 

} // node:reportSize[ReportSize:in][ReportSize:out] 


node:forward[Generic:in][Generic:out] 

{ // node:forward[Generic:in][Generic:out] 

if (in.getO < subscribers.size()) // If a valid value 

out.addDest(subscribers[in.get()]); // Forward only to dest 

else // If the value is not valid 

out.addDest(subscribers); // Broadcast to all subscribers 

out. set (in. get 0); // Report the proper index 

} // node:forward[Generic:in][Generic:out] 

// mode:Default 
// process:Subscription 


C.15. Ring2 


C.15.1. Generic.msg 

{import message {ReportValue} ) 
{message:Generic(ReportValue);} 


C.15.2. Reportindex.msg 

{import message {ReportValue) } 

{ message:Reportlndex(ReportValue); ) 


C.15.3. ReportSize.msg 

{import message {ReportValue) ) 

{message:ReportSize(ReportValue);} 

C.15.4. ReportValue.msg 

{ 

message:ReportValue 

{ II message:ReportValue 

ulong:value( ((ulong) -1) ); // Size of the subscription 

method:set(public; void; ulong:v;) { value = v; } // Set the value 

method:get(public; ulong;) { return value; } // Return the value 

} // message:ReportValue 

} 


C.15.5. Ring.proc 

{import process {RingMember, Subscription) ) 


427 






{import message {Generic, StartSimulation, Setup, ReportSize} } 


process:Ring 

{ 

RingMember:ring[10]; 
Subscription:sub; 

mode:Default 


node:start(StartSimulation:strt] 

[Setup:s=>(ring;), 

ReportSize:r=>(sub;):(-0.5), 
Generic:out=>(sub;):(0.0)] 


{ 


s . set(sub); 


// process:Ring 
// Ring of elements 
// Subscription list 


// mode:Default 
// Start tire simulation 
// Broadcast a setup message 
// Report size to members 
// Start tire ring 
// node: start [StartSimulation: strt] [ ... ] 
// Set tlie subscription parameter 
// node: start [StartSimulation: strt] [ ... ] 

// mode:Default 
// process:Ring 


C.15.6. RingMember.proc 

[import message {Generic, Setup, ReportSize, Reportindex, Subscribe} } 


process:RingMember 

{ 

process:sub; 
long:count(-1) ; 
long:next(0); 
long:index(0) ; 


// process:RingMember 
// Handle to the subscription process 
// A simple counter 
// Index of next instance 
// Index of this instance 


method:init(public; void;) { std::cout.precision(16); } 


method:fossilCollect(public; void;) 

{ // method:fossilCollect(public; void;) 

if (getTimeO >= 0) 

std::cout << "RingMember[" << index« "]: (" « count 
« ") at time " « getTimeO « std::endl; 

} // method:fossilCollect(public; void;) 


mode:Default 

{ // mode:Default 

node:setup[Setup:in][Subscribe:out=>(sub;)] { sub=in.get(); ) 
node:setindex[Reportindex:in][] { index=in.get{); } 
node:setNext[ReportSize:in)[] { next = (index+1) % in.getO; } 


node:run[Generic:in][Generic:out => 

{ 

out.set(next); // Set 

out.setTX{count++<10); 

} 

} 

} 

} 


(sub;) ] 

// node:run[Generic:in][Generic:out] 
the index of the eventual destination 
// Do we continue transmitting? 
// node:run[Generic:in][Generic:out] 
// mode:Default 
// process:RingMember 


C.15.7. Setup.msg 


message:Setup 

{ 

process:sub; 
method:set(public; 
method:get(public; 

} 

} 


// 

void; process:p;) { sub=p; 
process;) { return sub; } 


II message:Setup 
Subscription reference to use 
} // Set the sub 

// Return the sub 
II message:Setup 


428 






C.15.8. Subscribe.msg 

{message:Subscribe;} 

C.15.9. Subscription.proc 

{import message {Subscribe, ReportSize, Reportindex, Generic} } 


process:Subscription 
{ 

process:subscribers[]; 


// process:Subscription 
// List of subscribers 


mode:Default 

{ // mode:Default 

node:subscribe[Subscribe:in][Reportindex:out=>(in.getSource();)] 

{ // node:subscribe[Subscribe:in][Reportindex:out] 

out.set(subscribers.size()); // Index value to report back 

subscribers.push_back(in.getSource0); // Add the subscriber 

} II node:subscribe[Subscribe:in][Reportindex:out] 

node:reportSize[ReportSize:in][ReportSize:out=>(subscribers;)] 

{ // node:reportSize[ReportSize;in][ReportSize:out] 

out.set(subscribers.size 0); // Set size of output message 

} II node:reportSize[ReportSize:in][ReportSize:out] 


node:forward[Generic:in][Generic:out] 

{ // node:forward[Generic:in][Generic:out] 


if (in.get 0 < subscribers.size()) 
out.addDest(subscribers[in.get()]) 
else 

out.addDest(subscribers); 
out.set(in.get()); 


// If a valid value 
// Forward only to dest 
// If the value is not valid 
// Broadcast to all subscribers 
// Report the proper index 
// node:forward[Generic;in][Generic:out) 

// mode:Default 
// process:Subscription 


C.16. Simplel 


C.16.1. Generic.msg 

{message:Generic;} 


C.16.2. Simple.proc 

{import std {<iostream>} ] 

{import message {Generic, StartSimulation] ] 


// process:Simple 
II Hit counter 


process:Simple 

{ 

int:count(-1); 


method:init(public; void;) { std::cout.precision(15); ] 
method:fossilCollect(public; void;) 

{ II method:fossilCollect(public; void;) 

if (getTimeO >= 0) II If this is a good time to proceed 

{ 

std::cout « me « ": "; 

if (getTimeO == 0) std::cout « "starting"; 

else std::cout « count « " @ time " « getTimeO; 

std::cout « std::endl; 

} // if (getTimeO >= 0) 

} II method:fossilCollect(public; void;) 


mode:start 


429 






{ // mode:start 

node:proc[StartSimulation:strt][Generic:om=>(me;):(0.0)] 

{ // node:proc[StartSimulation:strt][Generic:om=>(me;)] 

start.setActive(false); // Turn the start mode off 

} // node:proc[StartSimulation:strt][Generic:om=>(me;)] 

} // mode:start 


} 

} 


mode:run 


{ 


} 


// mode:run 


node:proc[Generic:im][Generic:om=>(me;)] 

{ // node:proc[Generic:im][Generic:om=>(me;)] 

om.setTX(++count < 100); // Do we transmit? 

} // node:proc[Generic:im][Generic:om=>(me;)] 

// mode:run 
// process:Simple 


C.17. Simple2 

C.17.1. Generic.msg 

(message:Generic;} 

C.17.2. Simple.proc 

[import message (StartSimulation, Generic) ) 

(import std (<iostream>) } 

( 

process:Simple 

( // process:Simple 

long:count(-1); // Counter 

method:init(public; void;) ( std::cout.precision(16); } 

method:fossilCollect(public; void;) 

( // method:fossilCollect(public; void;) 

if (count % 10000 == 0) 

std: :cout « "Simple (" « count « ") at time ” « getTimeO 
« std::endl; 

} // method:fossilCollect(public; void;) 

mode:start 
( 

node:proc[StartSimulation:in][Generic:out=>(me;):(0.0)] 

( start.setActive(false); } 

} 

mode:run 
{ 

node:proc[Generic:in][Generic:out=>(me;)] 

( count++; } 

} 

} 

} 

C.18. SimpleS 

C.18.1. Child.proc 

(import message (SetParent, Generic) ) 

{ 

process:Child 
{ 

process:parent; 
long:count(-1); 


// process:Child 
// Handle to the parent process 
// Counter 


// mode:run 

// Loop 
// mode:run 
// process:Simple 


// mode:start 

// mode:start 


430 






method:init(public; void;) { std::cout.precision(16); } 


method:fossilCollect(public; void;) 

{ // method:fossilCollect(public; void;) 

if (run.isActive() && count % 10000==0) // Every 10000 messages 

std::cout « " Child (" « count « ") at time " « getTimeO 

<< std::endl; // Display the status 

) II method:fossilCollect(public; void;) 


mode:start 
{ 

node:setParent[SetParent:in][] 
{ 

parent = in.getSource (); 
start.setActive(false); 

} 

} 


// mode:start 

// node:setParent[SetParent:in][] 
// Set the parent reference 
// Turn this mode off 
// node:setParent[SetParent:in][] 
// mode:start 


mode:run 

{ // mode:Default 

node:bounce[Generic:in][Generic:out=>(parent;)] { count++; } 

} // mode:Default 

} // process:Child 

} 


C.18.2. Generic.msg 

{message:Generic;} 

C.18.3. SetParent.msg 

{message:SetParent;} 


C.18.4. Simple.proc 

{import message {StartSimulation, Generic, SetParent] ) 
{import process {Child} } 

{import std {<iostream>} } 


{ 

process:Simple 

{ 

long:count(-1); 

Child:child; 

method:init(public; void;) { std::cout.precision(16); 


// process:Simple 
// Counter 
// Child process 


method:fossilCollect(public; void;) 

{ // method:fossilCollect(public; void;) 

if (run.isActive() &S count % 10000==0) II Every 10000 messages 

std::cout << "Simple {" « count « ") at time " « getTimeO 

« std::endl; // Display the status 

} // method:fossilCollect(public; void;) 


mode:start 

{ II mode:Default 

node:start[StartSimulation:strt] 

[Generic:out=>(child;):(0.0), SetParent:sp=>(child;)] 

{ // node:start[StartSimulation:strt][Generic:out,SetParent:sp] 

start.setActive(false); // Deactivate the start mode 

) // node:start[StartSimulation:strt][Genericiout,SetParent:sp] 

} // mode:Default 


mode:run 

{ // mode:Default 

node:bounce[Generic:in][Generic:out=>(child;)] { count++; } 

} II mode:Default 

} // process:Simple 

} 


431 






432 








Appendix D. References 


(Atkinson 1989) 
(Bauer 1992) 
(Bellenot 1990) 
(Boeing 2001) 

(Biyant 1977) 

(Chandy 1979) 

(Chandy 1981) 

(Concepcion 1990) 

(Cormen 1990) 
(DMSO 2001) 
(DODI 1996) 

(D’Souza 1994) 

(Fabbri 1999) 

(Fishwick 1995) 
(Foley 1996) 
(Fujimoto 1990) 
(Fujimoto 1993) 


K. Atkinson, An Introduction to Numerical Analysis, New York, 

John Wiley & Sons 

H. Bauer, C. Sporrer, 1992, “Distributed Logic Simulation and an Approach to 
Asynchronous GVT-Calculation”, Proceedings 6''' Workshop on Parallel and 
Distributed Simulation, vol 24, pp 205-206, 

S. Bellenot, 1990 “Global virtual time algorithms”. Proceedings of the 
Multiconference on Distributed Simulation, 22 (1), January, pp. 122-127. 

The Boeing Company, 2001, online product description of digital design 
methodologies for the Boeing 777 family of commercial airliners 
httD://www.boeing.com/commercial/777familv/cdfacts.html 

R. Bryant, 1977, Simulation of Packet Communication Architecture Computer 
Systems. MIT-LCS-TR-188, Massachusetts Institute of Technology, Cambridge, 
MA 

K. Chandy, J. Misra, J., 1979, “Distributed Simulation: A Case Study in Design 
and Verification of Distributed Programs”, IEEE Transactions on Software 
Engineering, Vol. SE-5, No 5, September, pp. 440-452 

K. Chandy, J. Misra, J., 1981, “Asynchronous Distributed Simulation via a 
Sequence of Parallel Computations”, Communications of the ACM, 24:4, 198-205 

A. Concepcion, S. Kelly, 1990, “Computing Global Virtual Time Using the Multi- 
Level Token Passing Algorithm”, Advances in Parallel and Distributed Simulation, 
vol 23, pp 63-70 

T. Cormen, C. Leiserson, R. Rivest, Introduction to Algorithms, MIT-Press, 
McGraw Hill, Cambridge, MA 

Defense Modeling and Simulation Organization, 2001, online statement of mission 
and purpose http ://www.dmso.mil/index.Dhp?Dage= 133 

Department of Defense Instruction (DODI) 5000.61,29 April 1996, DoD Modeling 
and Simulation (M&S) Verification, Validation, and Accreditation (W&A), 

L. D’Souza, X. Fan, P. A. Wilsey, 1994, "pGVT: An algorithm for accurate GVT 
estimation". Proceedings of the Workshop on Parallel and Distributed 
Simulation, Edinburgh Scotland, pp 102-109 

A. Fabbri, 1999, “GVT and Scheduling in Space Time Memory Based 
Techniques” Proceedings 13"’ Workshop on Parallel and Distributed Simulation, 
Atlanta, GA, pp. 54-61 

P. Fishwick, 1995, Simulation Model Design and Execution; Building Digital 
Worlds, Englewood Cliffs, NJ, Prentice-Hall 

J. Foley, et al, 1996 Computer Graphics: Principles and Practice in C: 2/e, 
Reading, MA Addison Wesley Longman 

R. Fujimoto, 1990 , “Parallel Discrete Event Simulation”, Communications of the 
ACM, Vol. 33 (10), October 

R. Fujimoto, 1993, “Parallel Discrete Event Simulation: Will the Field Survive?” 
OSRA Journal on Computing, 5(3):213-230 


433 








(Fujimoto 1997) 

(Gropp 1998) 

(Gropp 1999a) 

(Gropp 1999b) 
(Hockney 1988) 
(Hungerford 1974) 
(Isaacson 1966) 

(Jefferson 1982) 

(Jefferson 1985a) 

(Jefferson 1985b) 

(Jefferson 1987) 
(Josuttis 1999) 

(Kuhl 1999) 

(Lin 1989) 

(LLNL 1998) 
(Mansuripur 1997) 


R. Fujimoto, M. Hybinette, 1997, “Computing Global Virtual Time in Shared- 
Memory Multiprocessors”, ACM Transactions on Modeling and Computer 
Simulation, Vol. 7, No. 4, October, pp 425-446 

W. Gropp, et al, 1998, MPJ - The Complete Reference, Volume 2, The MPI 
Extensions, Cambridge, MA, The MIT Press 

W. Gropp, E. Lusk, A. Skjellum, 1999, Using MPI: Portable Parallel 
Programming with the Message-Passing Interface, 2""^ Edition, Cambridge, MA, 
The MIT Press 

W. Gropp, E. Lusk, A. SVjQWvm, \999, Using MPI-2: Advanced Features of the 
Message-Passing Interface, Cambridge, MA, The MIT Press 

R. Hockney, Eastwood James, 1988, Computer Simulation Using Particles, 
Bristol, Adam Hilger 

T. Hungerford, 1974, Algebra, New York, Springer Verlag Graduate Texts in 
Mathematics, pp 7-9 

E. Isaacson, H. Keller, Herbert. \966 Analysis of Numerical Methods,VleviYoTV, 
Dover Publications, Inc. 

D. Jefferson, H. Sowizral, 1982, Fast Concurrent Simulation Using the Time 
Warp Mechanism, Part I: Local Control, Technical Report N-1906-AF, RAND 
Corporation, Santa Monica, CA 

D. Jefferson, 1985, “Virtual Time”, ACM Transactions on Programming 
Languages and Systems, 7 (3), July 1985, pp. 3-7 

D. Jefferson, H. Sowizral, 1985, “Fast concurrent simulation using the Time Warp 
mechanism”. Proceedings of the Conference on Distributed Simulation, volxune 
15(2), San Diego, CA, January, 63-69 

D. Jefferson, et al, 1987, “The Time Warp Operating System.” //'* Symposium on 
Operating Systems Principles 21,5, November, 77-93 

N. Josuttis, 1999, The C++ Standard Library; A Tutorial and Reference, Reading, 
MA, Addison Wesley Longman 

F. Kuhl, R. Weatherly, J. Dahmann, 1999, Creating Computer Simulation 
Systems: An introduction to the High Level Architecture, Upper Saddle River, NJ, 
Prentice Hall PTR 

Y. Lin, E. Lazowska, 1989, Determining the global virtual time in distributed 
simulation. Technical Report 90-01-02, Department of Computer Science, 
University of Washington, Seattle, WA 

Lawrence Livermore National Laboratory, 2001, online JCATS Executive 
Summary, http://www.llnl.gov/nai/group/JCATSExecSummarv.htm 

M. Mansuripur, 1997, “The Ronchi Test”, Optics & Photonics News, July, pp 42- 
46 


(MathWorks 2001) 


The MathWorks, 2001, online product Literature for Simulink 4, available at 
http://www.mathworks.com/products/simulink/ 


(Mattera 1993) 


F. Mattem,1993, “Efficient Algorithms for Distributed Snapshots and Global 
Virtual Time Approximation”, Journal of Parallel and Distributed Computing, vol 
18, pp 423-434 


434 







(Morrison 1991) 
(Priess 1990) 

(Press 1992) 
(Reiher 1990a) 

(Reiher 1990b) 
(Reiher 1990c) 
(Reiher 1991a) 

(Reiher 1991b) 

(Reiher 1992) 

(Rosenbloom 1994) 

(STRICOM 1999) 

(Wonnacott 1996) 
(Wright 1996) 

(Xiao 1995) 


F. Morrison, 1991, The Art of Modeling Dynamic Systems; Forecasting for Chaos, 
Randomness, & Determinism, New York, John Wiley & Sons 

B. Priess,!. MacIntyre, 1990, YADDES — Yet Another Distributed Discrete Event 
Simulator: User Manual, Department of Electrical and Computer Engineering, 
University of Waterloo, Waterloo, Ontario, Canada 

W. Press et al. 1992 Numerical Recipes in C; The Art of Scientific Computing, 2”‘’ 
Edition, Cambridge University Press 

P. Reiher, D. Jefferson, 1990, “Virtual Time Based Dynamic Load Management 
in the Time Warp Operating System”, Transactions if the Society for Computer 
Simulation, Vol 7(2), June 

P. Reiher, F. Wieland, P. Hontalas, 1990, “Providing Determinism in the Time 
Warp Operating System - Costs, Benefits, and Implications”, Proceedings of the 
IEEE Workshop on Experimental Distributed Systems, October 

P. Reiher, 1990, “Parallel Simulation Using the Time Warp Operating System”, 
Proceedings of the 1990 Winter Simulation Conference, December 

P. Reiher, S. Bellenot, D. Jefferson, 1991, “Temporal Decomposition of 
Simulations Under the Time Warp Operating System”, Proceedings of the 1991 
Principles of Distributed Simulation Conference 

P. Reiher, S. Bellenot, D. Jefferson, 1991 “Debugging the Time Warp Operating 
System and Its Applications”, Proceedings of the Symposium in Experiences with 
Distributed and Multiprocessor Systems II, March 

P. Reiher, 1992, “Experiences With Optimistic Synchronization for Distributed 
Operating Systems”, Proceedings of the Third Symposium on Experiences with 
Distributed and Multiprocessor Systems, March 

P. Rosenbloom, et al, 1994. “Intelligent automated agents for tactical air 
simulation: a progress report”. Proceedings of the Fourth Conference on Computer 
Generated Forces and Behavioral Representation. Orlando, FL. 

United States Army Simulation, Training and Instrumentation Command, 1999, 
Advanced Distributed Simulation Technology II: ModSAF 5.0 Functional 
Description Document, ADST-n-CDRL-MODSAF5.0-9800327 

P. Wonnacott, 1996, Run-time Support for Parallel Discrete Event Simulation 
Languages, Ph.D. Dissertation, University of Exeter, Faculty of Science 

R. Wright, M. Sweet, 1996, OpenGL Superbible, Waite Group Press 

Z. Xiao, F. Gomes, B. Unger, 1995, “A Fast Asynchronous GVT Algorithm for 
Shared Memory Multiprocessor Architectures”, Proceedings of The 1995 
Workshop on Parallel and Distributed Simulation, IEEE Computer Society 


435