cosmetix
[b2ld.git] / xmain.d
blob222694803f89582f5affd84ec8a31dbe67c3db12
1 /*
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
13 module xmain;
15 import arsd.simpledisplay;
16 import arsd.color;
17 import arsd.image;
19 import iv.glbinds.utils;
20 import iv.vmath;
22 import b2dlite;
24 enum show_times = false;
27 // ////////////////////////////////////////////////////////////////////////// //
28 enum GWidth = 800;
29 enum GHeight = 600;
32 // ////////////////////////////////////////////////////////////////////////// //
33 __gshared BodyContact[] contacts;
36 // ////////////////////////////////////////////////////////////////////////// //
37 // random number in range [-1,1]
38 public VFloat rnd () {
39 pragma(inline, true);
40 import std.random : uniform;
41 return cast(VFloat)uniform!"[]"(-VFloatNum!1, VFloatNum!1);
44 public VFloat rnd (VFloat lo, VFloat hi) {
45 pragma(inline, true);
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();
68 Vec2[] vtx;
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));
73 vtx ~= v;
75 bb.set(vtx, 50);
76 bomb = bb;
77 //bomb = PolyBody.Box(Vec2(VFloatNum!1, VFloatNum!1), VFloatNum!50);
78 bomb.friction = VFloatNum!(0.2);
79 //world ~= bomb;
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);
85 world ~= bomb;
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);
96 if (booody is bomb) {
97 glColor3f(VFloatNum!(0.4), VFloatNum!(0.9), VFloatNum!(0.4));
98 } else {
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);
107 glEnd();
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));
126 glBegin(GL_LINES);
127 glVertex2f(x1.x, x1.y);
128 glVertex2f(p1.x, p1.y);
129 glVertex2f(x2.x, x2.y);
130 glVertex2f(p2.x, p2.y);
131 glEnd();
135 // ////////////////////////////////////////////////////////////////////////// //
136 // demos
137 enum BoxW = VFloatNum!100;
138 enum BoxH = VFloatNum!20;
140 struct DemoInfo { string dsc; }
143 // single box
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*/);
147 world ~= b;
149 if (auto b = PolyBody.Box(Vec2(VFloatNum!1, VFloatNum!1), VFloatNum!200)) {
150 b.position.set(VFloatNum!0, VFloatNum!4);
151 world ~= b;
156 // a simple pendulum
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;
162 world ~= b1;
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;
168 world ~= b2;
170 if (auto j = new Joint()) {
171 j.set(b1, b2, Vec2(VFloatNum!0, VFloatNum!11));
172 world ~= j;
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*/);
181 world ~= b;
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);
187 world ~= b;
190 if (auto b = PolyBody.Box(Vec2(VFloatNum!(0.25), VFloatNum!1), VFloat.max)) {
191 b.position.set(VFloatNum!(5.25), VFloatNum!(9.5));
192 world ~= b;
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);
198 world ~= b;
201 if (auto b = PolyBody.Box(Vec2(VFloatNum!(0.25), VFloatNum!1), VFloat.max)) {
202 b.position.set(-VFloatNum!(5.25), VFloatNum!(5.5));
203 world ~= b;
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);
209 world ~= b;
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);
217 world ~= b;
223 // a vertical stack
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;
229 world ~= b;
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);
237 world ~= b;
243 // a pyramid
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;
249 world ~= b;
252 Vec2 x = Vec2(-VFloatNum!6, VFloatNum!(0.75));
253 Vec2 y;
255 for (int idx = 0; idx < 12; ++idx) {
256 y = x;
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);
260 b.position = y;
261 world ~= b;
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);
271 // a teeter
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*/);
275 world ~= b1;
277 BodyBase b2 = PolyBody.Box(Vec2(VFloatNum!12, VFloatNum!(0.25)), VFloatNum!100);
278 b2.position.set(VFloatNum!0, VFloatNum!1);
279 world ~= b2;
281 BodyBase b3 = PolyBody.Box(Vec2(VFloatNum!(0.5), VFloatNum!(0.5)), VFloatNum!25);
282 b3.position.set(-VFloatNum!5, VFloatNum!2);
283 world ~= b3;
285 BodyBase b4 = PolyBody.Box(Vec2(VFloatNum!(0.5), VFloatNum!(0.5)), VFloatNum!25);
286 b4.position.set(-VFloatNum!(5.5), VFloatNum!2);
287 world ~= b4;
289 BodyBase b5 = PolyBody.Box(Vec2(VFloatNum!1, VFloatNum!1), VFloatNum!100);
290 b5.position.set(VFloatNum!(5.5), VFloatNum!15);
291 world ~= b5;
293 if (auto j = new Joint()) {
294 j.set(b1, b2, Vec2(VFloatNum!0, VFloatNum!1));
295 world ~= j;
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;
308 world ~= b;
311 enum numPlanks = 15;
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);
318 world ~= b;
321 // tuning
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;
331 // spring stifness
332 VFloat k = mass*omega*omega;
334 // magic formulas
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;
343 world ~= j;
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;
350 world ~= j;
355 // dominos
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*/);
359 world ~= b1;
361 if (auto b = PolyBody.Box(Vec2(VFloatNum!12, VFloatNum!(0.5)), VFloat.max)) {
362 b.position.set(-VFloatNum!(1.5), VFloatNum!10);
363 world ~= b;
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);
370 world ~= b;
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);
377 world ~= b;
380 BodyBase b2 = PolyBody.Box(Vec2(VFloatNum!(0.5), VFloatNum!3), VFloat.max);
381 b2.position.set(-VFloatNum!7, VFloatNum!4);
382 world ~= b2;
384 BodyBase b3 = PolyBody.Box(Vec2(VFloatNum!12, VFloatNum!(0.25)), VFloatNum!20);
385 b3.position.set(-VFloatNum!(0.9), VFloatNum!1);
386 world ~= b3;
388 if (auto j = new Joint()) {
389 j.set(b1, b3, Vec2(-VFloatNum!2, VFloatNum!1));
390 world ~= j;
393 BodyBase b4 = PolyBody.Box(Vec2(VFloatNum!(0.5), VFloatNum!(0.5)), VFloatNum!10);
394 b4.position.set(-VFloatNum!10, VFloatNum!15);
395 world ~= b4;
397 if (auto j = new Joint()) {
398 j.set(b2, b4, Vec2(-VFloatNum!7, VFloatNum!15));
399 world ~= j;
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);
405 world ~= b5;
407 if (auto j = new Joint()) {
408 j.set(b1, b5, Vec2(VFloatNum!6, VFloatNum!(2.6)));
409 world ~= j;
412 // box cap
413 BodyBase b6 = PolyBody.Box(Vec2(VFloatNum!2, VFloatNum!(0.2)), VFloatNum!10);
414 b6.position.set(VFloatNum!6, VFloatNum!(3.6));
415 world ~= b6;
417 if (auto j = new Joint()) {
418 j.set(b5, b6, Vec2(VFloatNum!7, VFloatNum!(3.5)));
419 world ~= j;
424 // a multi-pendulum
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;
432 world ~= b1;
434 VFloat mass = VFloatNum!10;
436 // tuning
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;
446 // spring stiffness
447 VFloat k = mass*omega*omega;
449 // magic formulas
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);
459 b.position = x;
460 b.rotation = VFloatNum!0;
461 world ~= b;
463 if (auto j = new Joint()) {
464 j.set(b1, b, Vec2(idx, y));
465 j.softness = softnesss;
466 j.biasFactor = biasFactorr;
467 world ~= j;
470 b1 = b;
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) {
483 import std.traits;
484 alias sms = getSymbolsByUDA!(mixin(__MODULE__), DemoInfo);
485 foreach (immutable idx, const memb; sms) {
486 if (index == idx) {
487 w.title = getUDAs!(memb, DemoInfo)[0].dsc;
488 world.clear();
489 bomb = null;
490 memb();
491 demoIndex = index;
492 return true;
495 return false;
499 // ////////////////////////////////////////////////////////////////////////// //
500 void drawWorld () {
501 glMatrixMode(GL_MODELVIEW);
502 glLoadIdentity();
503 glTranslatef(VFloatNum!0, -VFloatNum!7, -VFloatNum!25);
505 // draw world
506 foreach (BodyBase b; world.bodies) b.drawBody();
507 foreach (Joint j; world.joints) j.drawJoint();
509 // draw contacts
511 import iv.glbinds;
512 glPointSize(VFloatNum!4);
513 glColor3f(VFloatNum!1, VFloatNum!0, VFloatNum!0);
514 glBegin(GL_POINTS);
515 foreach (const ref cxy; contacts) glVertex2f(cxy.position.x, cxy.position.y);
516 glEnd();
517 glPointSize(VFloatNum!1);
520 version(none) {
521 // draw BVH
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);
529 glEnd();
535 // ////////////////////////////////////////////////////////////////////////// //
536 void simulationStep (SimpleWindow sdwin) {
537 static double accumulator = 0;
539 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
542 char[128] buf;
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);
560 glLoadIdentity();
561 glTranslatef(VFloatNum!0, -VFloatNum!7, -VFloatNum!25);
563 contacts.length = 0;
564 contacts.assumeSafeAppend;
566 static if (show_times) {
567 import core.time;
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 // ////////////////////////////////////////////////////////////////////////// //
580 void showOpts () {
581 import std.stdio;
582 writeln("simulation options:");
583 writeln(" accumutate impulses: ", World.accumulateImpulses);
584 writeln(" position correction: ", World.positionCorrection);
585 writeln(" warm starting : ", World.warmStarting);
589 // ////////////////////////////////////////////////////////////////////////// //
590 void main () {
591 //setOpenGLContextVersion(3, 2); // up to GLSL 150
592 //openGLContextCompatible = false;
594 b2dlDrawContactsCB = delegate (in ref BodyContact ctx) {
595 contacts ~= 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);
605 drawWorld();
608 sdwin.visibleForTheFirstTime = delegate () {
609 showOpts();
610 setupDemo(0, sdwin);
611 sdwin.setAsCurrentOpenGlContext(); // make this window active
612 // init matrices
614 glMatrixMode(GL_PROJECTION);
615 glLoadIdentity();
616 glOrtho(0, GWidth, GHeight, 0, -1, 1);
617 glMatrixMode(GL_MODELVIEW);
618 glLoadIdentity();
620 glViewport(0, 0, GWidth, GHeight);
621 glMatrixMode(GL_PROJECTION);
622 glLoadIdentity();
623 oglPerspective(45.0, cast(VFloat)GWidth/cast(VFloat)GHeight, 0.1, 100.0);
624 sdwin.redrawOpenGlScene();
627 sdwin.eventLoop(1000/60,
628 delegate () {
629 if (sdwin.closed || world is null) return;
630 if (!paused) {
631 doOneStep = false;
632 if (slowmo) {
633 if (--slowmocount < 0) {
634 simulationStep(sdwin);
635 slowmocount = 10;
637 } else {
638 simulationStep(sdwin);
639 slowmocount = 0;
641 } else if (doOneStep) {
642 doOneStep = false;
643 simulationStep(sdwin);
646 delegate (KeyEvent event) {
647 if (sdwin.closed) return;
648 if (!event.pressed) return;
649 switch (event.key) {
650 case Key.Escape: sdwin.close(); break;
651 default:
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; }