2 * Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
4 * Permission to use, copy, modify, distribute and sell this software
5 * and its documentation for any purpose is hereby granted without fee,
6 * provided that the above copyright notice appear in all copies.
7 * Erin Catto makes no representations about the suitability
8 * of this software for any purpose.
9 * It is provided "as is" without express or implied warranty.
11 * Changes by Ketmar // Invisible Vector
15 import arsd
.simpledisplay
;
19 import iv
.glbinds
.utils
;
24 enum show_times
= false;
27 // ////////////////////////////////////////////////////////////////////////// //
32 // ////////////////////////////////////////////////////////////////////////// //
33 __gshared BodyContact
[] contacts
;
36 // ////////////////////////////////////////////////////////////////////////// //
37 // random number in range [-1,1]
38 public VFloat
rnd () {
40 import std
.random
: uniform
;
41 return cast(VFloat
)uniform
!"[]"(-VFloatNum
!1, VFloatNum
!1);
44 public VFloat
rnd (VFloat lo
, VFloat hi
) {
46 import std
.random
: uniform
;
47 return cast(VFloat
)uniform
!"[]"(lo
, hi
);
51 // ////////////////////////////////////////////////////////////////////////// //
52 __gshared BodyBase bomb
= null;
54 __gshared VFloat timeStep
= VFloatNum
!1/VFloatNum
!60;
55 __gshared
int iterations
= 10;
56 __gshared Vec2 gravity
= Vec2(VFloatNum
!0, -VFloatNum
!10);
58 __gshared
int demoIndex
= 0;
60 __gshared World world
;
61 shared static this () { world
= new World(gravity
, iterations
); }
64 // ////////////////////////////////////////////////////////////////////////// //
65 void launchBomb (SimpleWindow sdwin
) {
66 if (true/*bomb is null*/) {
67 auto bb
= new PolyBody();
69 foreach (int ang
; 0..10) {
70 import std
.math
: cos
, sin
;
71 import std
.random
: uniform
;
72 Vec2 v
= Vec2(0.6*cos(deg2rad(360/10*ang
))*uniform
!"[]"(0.7, 1.3), 0.6*sin(deg2rad(360/10*ang
))*uniform
!"[]"(0.7, 1.3));
77 //bomb = PolyBody.Box(Vec2(VFloatNum!1, VFloatNum!1), VFloatNum!50);
78 bomb
.friction
= VFloatNum
!(0.2);
81 bomb
.position
.set(rnd(-VFloatNum
!15, VFloatNum
!15), VFloatNum
!15);
82 bomb
.rotation
= rnd(-VFloatNum
!(1.5), VFloatNum
!(1.5));
83 bomb
.velocity
= bomb
.position
*(-VFloatNum
!(1.5));
84 bomb
.angularVelocity
= rnd(-VFloatNum
!20, VFloatNum
!20);
86 import std
.format
: format
;
87 sdwin
.title
= "bodies: %s".format(world
.bodies
.length
);
91 // ////////////////////////////////////////////////////////////////////////// //
92 void drawBody (BodyBase bodyb
) {
93 if (auto booody
= cast(PolyBody
)bodyb
) {
94 auto rmt
= Mat22(booody
.rotation
);
97 glColor3f(VFloatNum
!(0.4), VFloatNum
!(0.9), VFloatNum
!(0.4));
99 glColor3f(VFloatNum
!(0.8), VFloatNum
!(0.8), VFloatNum
!(0.9));
102 glBegin(GL_LINE_LOOP
);
103 foreach (const ref vx
; booody
.verts
) {
104 auto vp
= booody
.position
+rmt
*vx
;
105 glVertex2f(vp
.x
, vp
.y
);
112 void drawJoint (Joint joint
) {
113 auto b1
= joint
.body0
;
114 auto b2
= joint
.body1
;
116 auto R1
= Mat22(b1
.rotation
);
117 auto R2
= Mat22(b2
.rotation
);
119 Vec2 x1
= b1
.position
;
120 Vec2 p1
= x1
+R1
*joint
.localAnchor1
;
122 Vec2 x2
= b2
.position
;
123 Vec2 p2
= x2
+R2
*joint
.localAnchor2
;
125 glColor3f(VFloatNum
!(0.5), VFloatNum
!(0.5), VFloatNum
!(0.8));
127 glVertex2f(x1
.x
, x1
.y
);
128 glVertex2f(p1
.x
, p1
.y
);
129 glVertex2f(x2
.x
, x2
.y
);
130 glVertex2f(p2
.x
, p2
.y
);
135 // ////////////////////////////////////////////////////////////////////////// //
137 enum BoxW
= VFloatNum
!100;
138 enum BoxH
= VFloatNum
!20;
140 struct DemoInfo
{ string dsc
; }
144 @DemoInfo("A Single Box") void demo1 () {
145 if (auto b
= PolyBody
.Box(Vec2(BoxW
, BoxH
), VFloat
.max
)) {
146 b
.position
.set(VFloatNum
!0, -VFloatNum
!(0.5)*BoxH
/*b.width.y*/);
149 if (auto b
= PolyBody
.Box(Vec2(VFloatNum
!1, VFloatNum
!1), VFloatNum
!200)) {
150 b
.position
.set(VFloatNum
!0, VFloatNum
!4);
157 @DemoInfo("Simple Pendulum") void demo2 () {
158 auto b1
= PolyBody
.Box(Vec2(BoxW
, BoxH
), VFloat
.max
);
159 b1
.friction
= VFloatNum
!(0.2);
160 b1
.position
.set(VFloatNum
!0, -VFloatNum
!(0.5)*BoxH
/*b1.width.y*/);
161 b1
.rotation
= VFloatNum
!0;
164 auto b2
= PolyBody
.Box(Vec2(VFloatNum
!1, VFloatNum
!1), VFloatNum
!100);
165 b2
.friction
= VFloatNum
!(0.2);
166 b2
.position
.set(VFloatNum
!9, VFloatNum
!11);
167 b2
.rotation
= VFloatNum
!0;
170 if (auto j
= new Joint()) {
171 j
.set(b1
, b2
, Vec2(VFloatNum
!0, VFloatNum
!11));
177 // varying friction coefficients
178 @DemoInfo("Varying Friction Coefficients") void demo3 () {
179 if (auto b
= PolyBody
.Box(Vec2(BoxW
, BoxH
), VFloat
.max
)) {
180 b
.position
.set(VFloatNum
!0, -VFloatNum
!(0.5)*BoxH
/*b.width.y*/);
184 if (auto b
= PolyBody
.Box(Vec2(VFloatNum
!13, VFloatNum
!(0.25)), VFloat
.max
)) {
185 b
.position
.set(-VFloatNum
!2, VFloatNum
!11);
186 b
.rotation
= -VFloatNum
!(0.25);
190 if (auto b
= PolyBody
.Box(Vec2(VFloatNum
!(0.25), VFloatNum
!1), VFloat
.max
)) {
191 b
.position
.set(VFloatNum
!(5.25), VFloatNum
!(9.5));
195 if (auto b
= PolyBody
.Box(Vec2(VFloatNum
!13, VFloatNum
!(0.25)), VFloat
.max
)) {
196 b
.position
.set(VFloatNum
!2, VFloatNum
!7);
197 b
.rotation
= VFloatNum
!(0.25);
201 if (auto b
= PolyBody
.Box(Vec2(VFloatNum
!(0.25), VFloatNum
!1), VFloat
.max
)) {
202 b
.position
.set(-VFloatNum
!(5.25), VFloatNum
!(5.5));
206 if (auto b
= PolyBody
.Box(Vec2(VFloatNum
!13, VFloatNum
!(0.25)), VFloat
.max
)) {
207 b
.position
.set(-VFloatNum
!2, VFloatNum
!3);
208 b
.rotation
= -VFloatNum
!(0.25);
212 static immutable VFloat
[5] frictions
= [VFloatNum
!(0.75), VFloatNum
!(0.5), VFloatNum
!(0.35), VFloatNum
!(0.1), VFloatNum
!0];
213 for (int idx
= 0; idx
< 5; ++idx
) {
214 if (auto b
= PolyBody
.Box(Vec2(VFloatNum
!(0.5), VFloatNum
!(0.5)), VFloatNum
!25)) {
215 b
.friction
= frictions
[idx
];
216 b
.position
.set(-VFloatNum
!(7.5)+VFloatNum
!2*idx
, VFloatNum
!14);
224 @DemoInfo("Randomized Stacking") void demo4 () {
225 if (auto b
= PolyBody
.Box(Vec2(BoxW
, BoxH
), VFloat
.max
)) {
226 b
.friction
= VFloatNum
!(0.2);
227 b
.position
.set(VFloatNum
!0, -VFloatNum
!(0.5)*BoxH
/*b.width.y*/);
228 b
.rotation
= VFloatNum
!0;
232 for (int idx
= 0; idx
< 10; ++idx
) {
233 if (auto b
= PolyBody
.Box(Vec2(VFloatNum
!1, VFloatNum
!1), VFloatNum
!1)) {
234 b
.friction
= VFloatNum
!(0.2);
235 VFloat x
= rnd(-VFloatNum
!(0.1), VFloatNum
!(0.1));
236 b
.position
.set(x
, VFloatNum
!(0.51)+VFloatNum
!(1.05)*idx
);
244 @DemoInfo("Pyramid Stacking") void demo5 () {
245 if (auto b
= PolyBody
.Box(Vec2(BoxW
, BoxH
), VFloat
.max
)) {
246 b
.friction
= VFloatNum
!(0.2);
247 b
.position
.set(VFloatNum
!0, -VFloatNum
!(0.5)*BoxH
/*b.width.y*/);
248 b
.rotation
= VFloatNum
!0;
252 Vec2 x
= Vec2(-VFloatNum
!6, VFloatNum
!(0.75));
255 for (int idx
= 0; idx
< 12; ++idx
) {
257 for (int j
= idx
; j
< 12; ++j
) {
258 if (auto b
= PolyBody
.Box(Vec2(VFloatNum
!1, VFloatNum
!1), VFloatNum
!10)) {
259 b
.friction
= VFloatNum
!(0.2);
263 y
+= Vec2(VFloatNum
!(1.125), VFloatNum
!0);
265 //x += Vec2(VFloatNum!(0.5625), VFloatNum!(1.125));
266 x
+= Vec2(VFloatNum
!(0.5625), VFloatNum
!2);
272 @DemoInfo("A Teeter") void demo6 () {
273 BodyBase b1
= PolyBody
.Box(Vec2(BoxW
, BoxH
), VFloat
.max
);
274 b1
.position
.set(VFloatNum
!0, -VFloatNum
!(0.5)*BoxH
/*b1.width.y*/);
277 BodyBase b2
= PolyBody
.Box(Vec2(VFloatNum
!12, VFloatNum
!(0.25)), VFloatNum
!100);
278 b2
.position
.set(VFloatNum
!0, VFloatNum
!1);
281 BodyBase b3
= PolyBody
.Box(Vec2(VFloatNum
!(0.5), VFloatNum
!(0.5)), VFloatNum
!25);
282 b3
.position
.set(-VFloatNum
!5, VFloatNum
!2);
285 BodyBase b4
= PolyBody
.Box(Vec2(VFloatNum
!(0.5), VFloatNum
!(0.5)), VFloatNum
!25);
286 b4
.position
.set(-VFloatNum
!(5.5), VFloatNum
!2);
289 BodyBase b5
= PolyBody
.Box(Vec2(VFloatNum
!1, VFloatNum
!1), VFloatNum
!100);
290 b5
.position
.set(VFloatNum
!(5.5), VFloatNum
!15);
293 if (auto j
= new Joint()) {
294 j
.set(b1
, b2
, Vec2(VFloatNum
!0, VFloatNum
!1));
300 // a suspension bridge
301 @DemoInfo("A Suspension Bridge") void demo7 () {
302 import std
.math
: PI
;
304 if (auto b
= PolyBody
.Box(Vec2(BoxW
, BoxH
), VFloat
.max
)) {
305 b
.friction
= VFloatNum
!(0.2);
306 b
.position
.set(VFloatNum
!0, -VFloatNum
!(0.5)*BoxH
/*b.width.y*/);
307 b
.rotation
= VFloatNum
!0;
312 VFloat mass
= VFloatNum
!50;
314 for (int idx
= 0; idx
< numPlanks
; ++idx
) {
315 auto b
= PolyBody
.Box(Vec2(VFloatNum
!1, VFloatNum
!(0.25)), mass
);
316 b
.friction
= VFloatNum
!(0.2);
317 b
.position
.set(-VFloatNum
!(8.5)+VFloatNum
!(1.25)*idx
, VFloatNum
!5);
322 VFloat frequencyHz
= VFloatNum
!2;
323 VFloat dampingRatio
= VFloatNum
!(0.7);
325 // frequency in radians
326 VFloat omega
= VFloatNum
!2*PI
*frequencyHz
;
328 // damping coefficient
329 VFloat d
= VFloatNum
!2*mass
*dampingRatio
*omega
;
332 VFloat k
= mass
*omega
*omega
;
335 VFloat softnesss
= VFloatNum
!1/(d
+timeStep
*k
);
336 VFloat biasFactorr
= timeStep
*k
/(d
+timeStep
*k
);
338 for (int idx
= 0; idx
< numPlanks
; ++idx
) {
339 auto j
= new Joint();
340 j
.set(world
.bodies
[idx
], world
.bodies
[idx
+1], Vec2(-VFloatNum
!(9.125)+VFloatNum
!(1.25)*idx
, VFloatNum
!5));
341 j
.softness
= softnesss
;
342 j
.biasFactor
= biasFactorr
;
346 if (auto j
= new Joint()) {
347 j
.set(world
.bodies
[numPlanks
], world
.bodies
[0], Vec2(-VFloatNum
!(9.125)+VFloatNum
!(1.25)*numPlanks
, VFloatNum
!5));
348 j
.softness
= softnesss
;
349 j
.biasFactor
= biasFactorr
;
356 @DemoInfo("Dominos") void demo8 () {
357 BodyBase b1
= PolyBody
.Box(Vec2(BoxW
, BoxH
), VFloat
.max
);
358 b1
.position
.set(VFloatNum
!0, -VFloatNum
!(0.5)*BoxH
/*b1.width.y*/);
361 if (auto b
= PolyBody
.Box(Vec2(VFloatNum
!12, VFloatNum
!(0.5)), VFloat
.max
)) {
362 b
.position
.set(-VFloatNum
!(1.5), VFloatNum
!10);
366 for (int idx
= 0; idx
< 10; ++idx
) {
367 if (auto b
= PolyBody
.Box(Vec2(VFloatNum
!(0.2), VFloatNum
!2), VFloatNum
!100)) {
368 b
.position
.set(-VFloatNum
!6+VFloatNum
!1*idx
, VFloatNum
!(11.125));
369 b
.friction
= VFloatNum
!(0.1);
374 if (auto b
= PolyBody
.Box(Vec2(VFloatNum
!14, VFloatNum
!(0.5)), VFloat
.max
)) {
375 b
.position
.set(VFloatNum
!1, VFloatNum
!6);
376 b
.rotation
= VFloatNum
!(0.3);
380 BodyBase b2
= PolyBody
.Box(Vec2(VFloatNum
!(0.5), VFloatNum
!3), VFloat
.max
);
381 b2
.position
.set(-VFloatNum
!7, VFloatNum
!4);
384 BodyBase b3
= PolyBody
.Box(Vec2(VFloatNum
!12, VFloatNum
!(0.25)), VFloatNum
!20);
385 b3
.position
.set(-VFloatNum
!(0.9), VFloatNum
!1);
388 if (auto j
= new Joint()) {
389 j
.set(b1
, b3
, Vec2(-VFloatNum
!2, VFloatNum
!1));
393 BodyBase b4
= PolyBody
.Box(Vec2(VFloatNum
!(0.5), VFloatNum
!(0.5)), VFloatNum
!10);
394 b4
.position
.set(-VFloatNum
!10, VFloatNum
!15);
397 if (auto j
= new Joint()) {
398 j
.set(b2
, b4
, Vec2(-VFloatNum
!7, VFloatNum
!15));
402 BodyBase b5
= PolyBody
.Box(Vec2(VFloatNum
!2, VFloatNum
!2), VFloatNum
!12);
403 b5
.position
.set(VFloatNum
!6, VFloatNum
!(2.5));
404 b5
.friction
= VFloatNum
!(0.1);
407 if (auto j
= new Joint()) {
408 j
.set(b1
, b5
, Vec2(VFloatNum
!6, VFloatNum
!(2.6)));
413 BodyBase b6
= PolyBody
.Box(Vec2(VFloatNum
!2, VFloatNum
!(0.2)), VFloatNum
!10);
414 b6
.position
.set(VFloatNum
!6, VFloatNum
!(3.6));
417 if (auto j
= new Joint()) {
418 j
.set(b5
, b6
, Vec2(VFloatNum
!7, VFloatNum
!(3.5)));
425 @DemoInfo("Multi-pendulum") void demo9 () {
426 import std
.math
: PI
;
428 BodyBase b1
= PolyBody
.Box(Vec2(BoxW
, BoxH
), VFloat
.max
);
429 b1
.friction
= VFloatNum
!(0.2);
430 b1
.position
.set(VFloatNum
!0, -VFloatNum
!(0.5)*BoxH
/*b1.width.y*/);
431 b1
.rotation
= VFloatNum
!0;
434 VFloat mass
= VFloatNum
!10;
437 VFloat frequencyHz
= VFloatNum
!4;
438 VFloat dampingRatio
= VFloatNum
!(0.7);
440 // frequency in radians
441 VFloat omega
= VFloatNum
!2*PI
*frequencyHz
;
443 // damping coefficient
444 VFloat d
= VFloatNum
!2*mass
*dampingRatio
*omega
;
447 VFloat k
= mass
*omega
*omega
;
450 VFloat softnesss
= VFloatNum
!1/(d
+timeStep
*k
);
451 VFloat biasFactorr
= timeStep
*k
/(d
+timeStep
*k
);
453 const VFloat y
= VFloatNum
!12;
455 for (int idx
= 0; idx
< 15; ++idx
) {
456 Vec2 x
= Vec2(VFloatNum
!(0.5)+idx
, y
);
457 auto b
= PolyBody
.Box(Vec2(VFloatNum
!(0.75), VFloatNum
!(0.25)), mass
);
458 b
.friction
= VFloatNum
!(0.2);
460 b
.rotation
= VFloatNum
!0;
463 if (auto j
= new Joint()) {
464 j
.set(b1
, b
, Vec2(idx
, y
));
465 j
.softness
= softnesss
;
466 j
.biasFactor
= biasFactorr
;
475 // ////////////////////////////////////////////////////////////////////////// //
476 __gshared
bool paused
= false;
477 __gshared
bool doOneStep
= false;
478 __gshared
bool slowmo
= false;
479 __gshared
int slowmocount
= 0;
482 bool setupDemo (int index
, SimpleWindow w
) {
484 alias sms
= getSymbolsByUDA
!(mixin(__MODULE__
), DemoInfo
);
485 foreach (immutable idx
, const memb
; sms
) {
487 w
.title
= getUDAs
!(memb
, DemoInfo
)[0].dsc
;
499 // ////////////////////////////////////////////////////////////////////////// //
501 glMatrixMode(GL_MODELVIEW
);
503 glTranslatef(VFloatNum
!0, -VFloatNum
!7, -VFloatNum
!25);
506 foreach (BodyBase b
; world
.bodies
) b
.drawBody();
507 foreach (Joint j
; world
.joints
) j
.drawJoint();
512 glPointSize(VFloatNum
!4);
513 glColor3f(VFloatNum
!1, VFloatNum
!0, VFloatNum
!0);
515 foreach (const ref cxy
; contacts
) glVertex2f(cxy
.position
.x
, cxy
.position
.y
);
517 glPointSize(VFloatNum
!1);
522 world
.drawBVH((Vec2 min
, Vec2 max
) {
523 glColor3f(VFloatNum
!1, VFloatNum
!1, VFloatNum
!0);
524 glBegin(GL_LINE_LOOP
);
525 glVertex2f(min
.x
, min
.y
);
526 glVertex2f(max
.x
, min
.y
);
527 glVertex2f(max
.x
, max
.y
);
528 glVertex2f(min
.x
, max
.y
);
535 // ////////////////////////////////////////////////////////////////////////// //
536 void simulationStep (SimpleWindow sdwin
) {
537 static double accumulator
= 0;
539 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT
);
544 snprintf(buf, sizeof(buf), "Demo %d: %s", demoIndex+1, demos[demoIndex].name);
545 DrawText(5, 15, buf);
546 DrawText(5, 45, "Keys: 1-9 Demos, Space to Launch the Bomb");
548 static char buffer[512];
549 sprintf(buffer, "(A)ccumulation %s", (World::accumulateImpulses ? "ON" : "OFF"));
550 DrawText(5, 75, buffer);
552 sprintf(buffer, "(P)osition Correction %s", (World::positionCorrection ? "ON" : "OFF"));
553 DrawText(5, 105, buffer);
555 sprintf(buffer, "(W)arm Starting %s", (World::warmStarting ? "ON" : "OFF"));
556 DrawText(5, 135, buffer);
559 glMatrixMode(GL_MODELVIEW
);
561 glTranslatef(VFloatNum
!0, -VFloatNum
!7, -VFloatNum
!25);
564 contacts
.assumeSafeAppend
;
566 static if (show_times
) {
568 auto stt
= MonoTime
.currTime
;
570 world
.step(timeStep
);
571 static if (show_times
) {
572 auto tm
= (MonoTime
.currTime
-stt
).total
!"msecs";
573 { import core
.stdc
.stdio
; printf("step: %d msecs\n", cast(int)tm
); }
575 sdwin
.redrawOpenGlSceneNow();
579 // ////////////////////////////////////////////////////////////////////////// //
582 writeln("simulation options:");
583 writeln(" accumutate impulses: ", World
.accumulateImpulses
);
584 writeln(" position correction: ", World
.positionCorrection
);
585 writeln(" warm starting : ", World
.warmStarting
);
589 // ////////////////////////////////////////////////////////////////////////// //
591 //setOpenGLContextVersion(3, 2); // up to GLSL 150
592 //openGLContextCompatible = false;
594 b2dlDrawContactsCB
= delegate (in ref BodyContact ctx
) {
598 auto sdwin
= new SimpleWindow(GWidth
, GHeight
, "Box2DLite Physics", OpenGlOptions
.yes
, Resizablity
.fixedSize
);
599 //sdwin.hideCursor();
601 //sdwin.closeQuery = delegate () { concmd("quit"); };
603 sdwin
.redrawOpenGlScene
= delegate () {
604 glClear(GL_COLOR_BUFFER_BIT
);
608 sdwin
.visibleForTheFirstTime
= delegate () {
611 sdwin
.setAsCurrentOpenGlContext(); // make this window active
614 glMatrixMode(GL_PROJECTION);
616 glOrtho(0, GWidth, GHeight, 0, -1, 1);
617 glMatrixMode(GL_MODELVIEW);
620 glViewport(0, 0, GWidth
, GHeight
);
621 glMatrixMode(GL_PROJECTION
);
623 oglPerspective(45.0, cast(VFloat
)GWidth
/cast(VFloat
)GHeight
, 0.1, 100.0);
624 sdwin
.redrawOpenGlScene();
627 sdwin
.eventLoop(1000/60,
629 if (sdwin
.closed || world
is null) return;
633 if (--slowmocount
< 0) {
634 simulationStep(sdwin
);
638 simulationStep(sdwin
);
641 } else if (doOneStep
) {
643 simulationStep(sdwin
);
646 delegate (KeyEvent event
) {
647 if (sdwin
.closed
) return;
648 if (!event
.pressed
) return;
650 case Key
.Escape
: sdwin
.close(); break;
654 delegate (MouseEvent event
) {
656 delegate (dchar ch
) {
657 if (ch
== 'q') { sdwin
.close(); return; }
658 if (ch
== 'r') { setupDemo(demoIndex
, sdwin
); return; }
659 if (ch
== ' ') { paused
= !paused
; return; }
660 if (ch
== 's') { slowmo
= !slowmo
; return; }
661 if (ch
>= '1' && ch
<= '9') { setupDemo(ch
-'1', sdwin
); return; }
662 if (ch
== '0') { setupDemo(10, sdwin
); return; }
663 if (ch
== 'a') { World
.accumulateImpulses
= !World
.accumulateImpulses
; showOpts(); return; }
664 if (ch
== 'p') { World
.positionCorrection
= !World
.positionCorrection
; showOpts(); return; }
665 if (ch
== 'w') { World
.warmStarting
= !World
.warmStarting
; showOpts(); return; }
666 if (ch
== '\n' || ch
== '\r') { launchBomb(sdwin
); return; }
667 if (ch
== '+') { setupDemo(demoIndex
+1, sdwin
); return; }
668 if (ch
== '-') { if (demoIndex
> 0) setupDemo(demoIndex
-1, sdwin
); return; }
669 if (ch
== 'z') { doOneStep
= true; return; }