arbiter is a struct now
[b2ld.git] / xmain.d
blob01bd67f761476fdfec44685a2f042a3040f524e7
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 bool optShowTimes = false;
25 bool optDrawBVH = false;
28 // ////////////////////////////////////////////////////////////////////////// //
29 enum GWidth = 800;
30 enum GHeight = 600;
33 // ////////////////////////////////////////////////////////////////////////// //
34 __gshared BodyContact[] contacts;
37 // ////////////////////////////////////////////////////////////////////////// //
38 // random number in range [-1,1]
39 public VFloat rnd () {
40 pragma(inline, true);
41 import std.random : uniform;
42 return cast(VFloat)uniform!"[]"(-VFloatNum!1, VFloatNum!1);
45 public VFloat rnd (VFloat lo, VFloat hi) {
46 pragma(inline, true);
47 import std.random : uniform;
48 return cast(VFloat)uniform!"[]"(lo, hi);
52 // ////////////////////////////////////////////////////////////////////////// //
53 __gshared BodyBase bomb = null;
55 __gshared VFloat timeStep = VFloatNum!1/VFloatNum!60;
56 __gshared int iterations = 10;
57 __gshared Vec2 gravity = Vec2(VFloatNum!0, -VFloatNum!10);
59 __gshared int demoIndex = 0;
61 __gshared World world;
62 shared static this () { world = new World(gravity, iterations); }
65 // ////////////////////////////////////////////////////////////////////////// //
66 void launchBomb (SimpleWindow sdwin) {
67 if (true/*bomb is null*/) {
68 auto bb = new PolyBody();
69 Vec2[] vtx;
70 foreach (int ang; 0..10) {
71 import std.math : cos, sin;
72 import std.random : uniform;
73 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));
74 vtx ~= v;
76 bb.set(vtx, 50);
77 bomb = bb;
78 //bomb = PolyBody.Box(Vec2(VFloatNum!1, VFloatNum!1), VFloatNum!50);
79 bomb.friction = VFloatNum!(0.2);
80 //world ~= bomb;
82 bomb.position.set(rnd(-VFloatNum!15, VFloatNum!15), VFloatNum!15);
83 bomb.rotation = rnd(-VFloatNum!(1.5), VFloatNum!(1.5));
84 bomb.velocity = bomb.position*(-VFloatNum!(1.5));
85 bomb.angularVelocity = rnd(-VFloatNum!20, VFloatNum!20);
86 world ~= bomb;
87 import std.format : format;
88 sdwin.title = "bodies: %s".format(world.bodies.length);
92 // ////////////////////////////////////////////////////////////////////////// //
93 void drawBody (BodyBase bodyb) {
94 if (auto booody = cast(PolyBody)bodyb) {
95 auto rmt = Mat22(booody.rotation);
97 if (booody is bomb) {
98 glColor3f(VFloatNum!(0.4), VFloatNum!(0.9), VFloatNum!(0.4));
99 } else {
100 glColor3f(VFloatNum!(0.8), VFloatNum!(0.8), VFloatNum!(0.9));
103 glBegin(GL_LINE_LOOP);
104 foreach (const ref vx; booody.verts) {
105 auto vp = booody.position+rmt*vx;
106 glVertex2f(vp.x, vp.y);
108 glEnd();
113 void drawJoint (Joint joint) {
114 auto b1 = joint.body0;
115 auto b2 = joint.body1;
117 auto R1 = Mat22(b1.rotation);
118 auto R2 = Mat22(b2.rotation);
120 Vec2 x1 = b1.position;
121 Vec2 p1 = x1+R1*joint.localAnchor1;
123 Vec2 x2 = b2.position;
124 Vec2 p2 = x2+R2*joint.localAnchor2;
126 glColor3f(VFloatNum!(0.5), VFloatNum!(0.5), VFloatNum!(0.8));
127 glBegin(GL_LINES);
128 glVertex2f(x1.x, x1.y);
129 glVertex2f(p1.x, p1.y);
130 glVertex2f(x2.x, x2.y);
131 glVertex2f(p2.x, p2.y);
132 glEnd();
136 // ////////////////////////////////////////////////////////////////////////// //
137 // demos
138 enum BoxW = VFloatNum!100;
139 enum BoxH = VFloatNum!20;
141 struct DemoInfo { string dsc; }
144 // single box
145 @DemoInfo("A Single Box") void demo1 () {
146 if (auto b = PolyBody.Box(Vec2(BoxW, BoxH), VFloat.max)) {
147 b.position.set(VFloatNum!0, -VFloatNum!(0.5)*BoxH/*b.width.y*/);
148 world ~= b;
150 if (auto b = PolyBody.Box(Vec2(VFloatNum!1, VFloatNum!1), VFloatNum!200)) {
151 b.position.set(VFloatNum!0, VFloatNum!4);
152 world ~= b;
157 // a simple pendulum
158 @DemoInfo("Simple Pendulum") void demo2 () {
159 auto b1 = PolyBody.Box(Vec2(BoxW, BoxH), VFloat.max);
160 b1.friction = VFloatNum!(0.2);
161 b1.position.set(VFloatNum!0, -VFloatNum!(0.5)*BoxH/*b1.width.y*/);
162 b1.rotation = VFloatNum!0;
163 world ~= b1;
165 auto b2 = PolyBody.Box(Vec2(VFloatNum!1, VFloatNum!1), VFloatNum!100);
166 b2.friction = VFloatNum!(0.2);
167 b2.position.set(VFloatNum!9, VFloatNum!11);
168 b2.rotation = VFloatNum!0;
169 world ~= b2;
171 if (auto j = new Joint()) {
172 j.set(b1, b2, Vec2(VFloatNum!0, VFloatNum!11));
173 world ~= j;
178 // varying friction coefficients
179 @DemoInfo("Varying Friction Coefficients") void demo3 () {
180 if (auto b = PolyBody.Box(Vec2(BoxW, BoxH), VFloat.max)) {
181 b.position.set(VFloatNum!0, -VFloatNum!(0.5)*BoxH/*b.width.y*/);
182 world ~= b;
185 if (auto b = PolyBody.Box(Vec2(VFloatNum!13, VFloatNum!(0.25)), VFloat.max)) {
186 b.position.set(-VFloatNum!2, VFloatNum!11);
187 b.rotation = -VFloatNum!(0.25);
188 world ~= b;
191 if (auto b = PolyBody.Box(Vec2(VFloatNum!(0.25), VFloatNum!1), VFloat.max)) {
192 b.position.set(VFloatNum!(5.25), VFloatNum!(9.5));
193 world ~= b;
196 if (auto b = PolyBody.Box(Vec2(VFloatNum!13, VFloatNum!(0.25)), VFloat.max)) {
197 b.position.set(VFloatNum!2, VFloatNum!7);
198 b.rotation = VFloatNum!(0.25);
199 world ~= b;
202 if (auto b = PolyBody.Box(Vec2(VFloatNum!(0.25), VFloatNum!1), VFloat.max)) {
203 b.position.set(-VFloatNum!(5.25), VFloatNum!(5.5));
204 world ~= b;
207 if (auto b = PolyBody.Box(Vec2(VFloatNum!13, VFloatNum!(0.25)), VFloat.max)) {
208 b.position.set(-VFloatNum!2, VFloatNum!3);
209 b.rotation = -VFloatNum!(0.25);
210 world ~= b;
213 static immutable VFloat[5] frictions = [VFloatNum!(0.75), VFloatNum!(0.5), VFloatNum!(0.35), VFloatNum!(0.1), VFloatNum!0];
214 for (int idx = 0; idx < 5; ++idx) {
215 if (auto b = PolyBody.Box(Vec2(VFloatNum!(0.5), VFloatNum!(0.5)), VFloatNum!25)) {
216 b.friction = frictions[idx];
217 b.position.set(-VFloatNum!(7.5)+VFloatNum!2*idx, VFloatNum!14);
218 world ~= b;
224 // a vertical stack
225 @DemoInfo("Randomized Stacking") void demo4 () {
226 if (auto b = PolyBody.Box(Vec2(BoxW, BoxH), VFloat.max)) {
227 b.friction = VFloatNum!(0.2);
228 b.position.set(VFloatNum!0, -VFloatNum!(0.5)*BoxH/*b.width.y*/);
229 b.rotation = VFloatNum!0;
230 world ~= b;
233 for (int idx = 0; idx < 10; ++idx) {
234 if (auto b = PolyBody.Box(Vec2(VFloatNum!1, VFloatNum!1), VFloatNum!1)) {
235 b.friction = VFloatNum!(0.2);
236 VFloat x = rnd(-VFloatNum!(0.1), VFloatNum!(0.1));
237 b.position.set(x, VFloatNum!(0.51)+VFloatNum!(1.05)*idx);
238 world ~= b;
244 // a pyramid
245 @DemoInfo("Pyramid Stacking") void demo5 () {
246 if (auto b = PolyBody.Box(Vec2(BoxW, BoxH), VFloat.max)) {
247 b.friction = VFloatNum!(0.2);
248 b.position.set(VFloatNum!0, -VFloatNum!(0.5)*BoxH/*b.width.y*/);
249 b.rotation = VFloatNum!0;
250 world ~= b;
253 Vec2 x = Vec2(-VFloatNum!6, VFloatNum!(0.75));
254 Vec2 y;
256 for (int idx = 0; idx < 12; ++idx) {
257 y = x;
258 for (int j = idx; j < 12; ++j) {
259 if (auto b = PolyBody.Box(Vec2(VFloatNum!1, VFloatNum!1), VFloatNum!10)) {
260 b.friction = VFloatNum!(0.2);
261 b.position = y;
262 world ~= b;
264 y += Vec2(VFloatNum!(1.125), VFloatNum!0);
266 //x += Vec2(VFloatNum!(0.5625), VFloatNum!(1.125));
267 x += Vec2(VFloatNum!(0.5625), VFloatNum!2);
272 // a teeter
273 @DemoInfo("A Teeter") void demo6 () {
274 BodyBase b1 = PolyBody.Box(Vec2(BoxW, BoxH), VFloat.max);
275 b1.position.set(VFloatNum!0, -VFloatNum!(0.5)*BoxH/*b1.width.y*/);
276 world ~= b1;
278 BodyBase b2 = PolyBody.Box(Vec2(VFloatNum!12, VFloatNum!(0.25)), VFloatNum!100);
279 b2.position.set(VFloatNum!0, VFloatNum!1);
280 world ~= b2;
282 BodyBase b3 = PolyBody.Box(Vec2(VFloatNum!(0.5), VFloatNum!(0.5)), VFloatNum!25);
283 b3.position.set(-VFloatNum!5, VFloatNum!2);
284 world ~= b3;
286 BodyBase b4 = PolyBody.Box(Vec2(VFloatNum!(0.5), VFloatNum!(0.5)), VFloatNum!25);
287 b4.position.set(-VFloatNum!(5.5), VFloatNum!2);
288 world ~= b4;
290 BodyBase b5 = PolyBody.Box(Vec2(VFloatNum!1, VFloatNum!1), VFloatNum!100);
291 b5.position.set(VFloatNum!(5.5), VFloatNum!15);
292 world ~= b5;
294 if (auto j = new Joint()) {
295 j.set(b1, b2, Vec2(VFloatNum!0, VFloatNum!1));
296 world ~= j;
301 // a suspension bridge
302 @DemoInfo("A Suspension Bridge") void demo7 () {
303 import std.math : PI;
305 if (auto b = PolyBody.Box(Vec2(BoxW, BoxH), VFloat.max)) {
306 b.friction = VFloatNum!(0.2);
307 b.position.set(VFloatNum!0, -VFloatNum!(0.5)*BoxH/*b.width.y*/);
308 b.rotation = VFloatNum!0;
309 world ~= b;
312 enum numPlanks = 15;
313 VFloat mass = VFloatNum!50;
315 for (int idx = 0; idx < numPlanks; ++idx) {
316 auto b = PolyBody.Box(Vec2(VFloatNum!1, VFloatNum!(0.25)), mass);
317 b.friction = VFloatNum!(0.2);
318 b.position.set(-VFloatNum!(8.5)+VFloatNum!(1.25)*idx, VFloatNum!5);
319 world ~= b;
322 // tuning
323 VFloat frequencyHz = VFloatNum!2;
324 VFloat dampingRatio = VFloatNum!(0.7);
326 // frequency in radians
327 VFloat omega = VFloatNum!2*PI*frequencyHz;
329 // damping coefficient
330 VFloat d = VFloatNum!2*mass*dampingRatio*omega;
332 // spring stifness
333 VFloat k = mass*omega*omega;
335 // magic formulas
336 VFloat softnesss = VFloatNum!1/(d+timeStep*k);
337 VFloat biasFactorr = timeStep*k/(d+timeStep*k);
339 for (int idx = 0; idx < numPlanks; ++idx) {
340 auto j = new Joint();
341 j.set(world.bodies[idx], world.bodies[idx+1], Vec2(-VFloatNum!(9.125)+VFloatNum!(1.25)*idx, VFloatNum!5));
342 j.softness = softnesss;
343 j.biasFactor = biasFactorr;
344 world ~= j;
347 if (auto j = new Joint()) {
348 j.set(world.bodies[numPlanks], world.bodies[0], Vec2(-VFloatNum!(9.125)+VFloatNum!(1.25)*numPlanks, VFloatNum!5));
349 j.softness = softnesss;
350 j.biasFactor = biasFactorr;
351 world ~= j;
356 // dominos
357 @DemoInfo("Dominos") void demo8 () {
358 BodyBase b1 = PolyBody.Box(Vec2(BoxW, BoxH), VFloat.max);
359 b1.position.set(VFloatNum!0, -VFloatNum!(0.5)*BoxH/*b1.width.y*/);
360 world ~= b1;
362 if (auto b = PolyBody.Box(Vec2(VFloatNum!12, VFloatNum!(0.5)), VFloat.max)) {
363 b.position.set(-VFloatNum!(1.5), VFloatNum!10);
364 world ~= b;
367 for (int idx = 0; idx < 10; ++idx) {
368 if (auto b = PolyBody.Box(Vec2(VFloatNum!(0.2), VFloatNum!2), VFloatNum!100)) {
369 b.position.set(-VFloatNum!6+VFloatNum!1*idx, VFloatNum!(11.125));
370 b.friction = VFloatNum!(0.1);
371 world ~= b;
375 if (auto b = PolyBody.Box(Vec2(VFloatNum!14, VFloatNum!(0.5)), VFloat.max)) {
376 b.position.set(VFloatNum!1, VFloatNum!6);
377 b.rotation = VFloatNum!(0.3);
378 world ~= b;
381 BodyBase b2 = PolyBody.Box(Vec2(VFloatNum!(0.5), VFloatNum!3), VFloat.max);
382 b2.position.set(-VFloatNum!7, VFloatNum!4);
383 world ~= b2;
385 BodyBase b3 = PolyBody.Box(Vec2(VFloatNum!12, VFloatNum!(0.25)), VFloatNum!20);
386 b3.position.set(-VFloatNum!(0.9), VFloatNum!1);
387 world ~= b3;
389 if (auto j = new Joint()) {
390 j.set(b1, b3, Vec2(-VFloatNum!2, VFloatNum!1));
391 world ~= j;
394 BodyBase b4 = PolyBody.Box(Vec2(VFloatNum!(0.5), VFloatNum!(0.5)), VFloatNum!10);
395 b4.position.set(-VFloatNum!10, VFloatNum!15);
396 world ~= b4;
398 if (auto j = new Joint()) {
399 j.set(b2, b4, Vec2(-VFloatNum!7, VFloatNum!15));
400 world ~= j;
403 BodyBase b5 = PolyBody.Box(Vec2(VFloatNum!2, VFloatNum!2), VFloatNum!12);
404 b5.position.set(VFloatNum!6, VFloatNum!(2.5));
405 b5.friction = VFloatNum!(0.1);
406 world ~= b5;
408 if (auto j = new Joint()) {
409 j.set(b1, b5, Vec2(VFloatNum!6, VFloatNum!(2.6)));
410 world ~= j;
413 // box cap
414 BodyBase b6 = PolyBody.Box(Vec2(VFloatNum!2, VFloatNum!(0.2)), VFloatNum!10);
415 b6.position.set(VFloatNum!6, VFloatNum!(3.6));
416 world ~= b6;
418 if (auto j = new Joint()) {
419 j.set(b5, b6, Vec2(VFloatNum!7, VFloatNum!(3.5)));
420 world ~= j;
425 // a multi-pendulum
426 @DemoInfo("Multi-pendulum") void demo9 () {
427 import std.math : PI;
429 BodyBase b1 = PolyBody.Box(Vec2(BoxW, BoxH), VFloat.max);
430 b1.friction = VFloatNum!(0.2);
431 b1.position.set(VFloatNum!0, -VFloatNum!(0.5)*BoxH/*b1.width.y*/);
432 b1.rotation = VFloatNum!0;
433 world ~= b1;
435 VFloat mass = VFloatNum!10;
437 // tuning
438 VFloat frequencyHz = VFloatNum!4;
439 VFloat dampingRatio = VFloatNum!(0.7);
441 // frequency in radians
442 VFloat omega = VFloatNum!2*PI*frequencyHz;
444 // damping coefficient
445 VFloat d = VFloatNum!2*mass*dampingRatio*omega;
447 // spring stiffness
448 VFloat k = mass*omega*omega;
450 // magic formulas
451 VFloat softnesss = VFloatNum!1/(d+timeStep*k);
452 VFloat biasFactorr = timeStep*k/(d+timeStep*k);
454 const VFloat y = VFloatNum!12;
456 for (int idx = 0; idx < 15; ++idx) {
457 Vec2 x = Vec2(VFloatNum!(0.5)+idx, y);
458 auto b = PolyBody.Box(Vec2(VFloatNum!(0.75), VFloatNum!(0.25)), mass);
459 b.friction = VFloatNum!(0.2);
460 b.position = x;
461 b.rotation = VFloatNum!0;
462 world ~= b;
464 if (auto j = new Joint()) {
465 j.set(b1, b, Vec2(idx, y));
466 j.softness = softnesss;
467 j.biasFactor = biasFactorr;
468 world ~= j;
471 b1 = b;
476 // ////////////////////////////////////////////////////////////////////////// //
477 __gshared bool paused = false;
478 __gshared bool doOneStep = false;
479 __gshared bool slowmo = false;
480 __gshared int slowmocount = 0;
483 bool setupDemo (int index, SimpleWindow w) {
484 import std.traits;
485 alias sms = getSymbolsByUDA!(mixin(__MODULE__), DemoInfo);
486 foreach (immutable idx, const memb; sms) {
487 if (index == idx) {
488 w.title = getUDAs!(memb, DemoInfo)[0].dsc;
489 world.clear();
490 bomb = null;
491 memb();
492 demoIndex = index;
493 return true;
496 return false;
500 // ////////////////////////////////////////////////////////////////////////// //
501 void drawWorld () {
502 glMatrixMode(GL_MODELVIEW);
503 glLoadIdentity();
504 glTranslatef(VFloatNum!0, -VFloatNum!7, -VFloatNum!25);
506 // draw world
507 foreach (BodyBase b; world.bodies) b.drawBody();
508 foreach (Joint j; world.joints) j.drawJoint();
510 // draw contacts
512 import iv.glbinds;
513 glPointSize(VFloatNum!4);
514 glColor3f(VFloatNum!1, VFloatNum!0, VFloatNum!0);
515 glBegin(GL_POINTS);
516 foreach (const ref cxy; contacts) glVertex2f(cxy.position.x, cxy.position.y);
517 glEnd();
518 glPointSize(VFloatNum!1);
521 if (optDrawBVH) {
522 // draw BVH
523 world.drawBVH((Vec2 min, Vec2 max) {
524 glColor3f(VFloatNum!1, VFloatNum!1, VFloatNum!0);
525 glBegin(GL_LINE_LOOP);
526 glVertex2f(min.x, min.y);
527 glVertex2f(max.x, min.y);
528 glVertex2f(max.x, max.y);
529 glVertex2f(min.x, max.y);
530 glEnd();
536 // ////////////////////////////////////////////////////////////////////////// //
537 void simulationStep (SimpleWindow sdwin) {
538 static double accumulator = 0;
540 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
543 char[128] buf;
545 snprintf(buf, sizeof(buf), "Demo %d: %s", demoIndex+1, demos[demoIndex].name);
546 DrawText(5, 15, buf);
547 DrawText(5, 45, "Keys: 1-9 Demos, Space to Launch the Bomb");
549 static char buffer[512];
550 sprintf(buffer, "(A)ccumulation %s", (World::accumulateImpulses ? "ON" : "OFF"));
551 DrawText(5, 75, buffer);
553 sprintf(buffer, "(P)osition Correction %s", (World::positionCorrection ? "ON" : "OFF"));
554 DrawText(5, 105, buffer);
556 sprintf(buffer, "(W)arm Starting %s", (World::warmStarting ? "ON" : "OFF"));
557 DrawText(5, 135, buffer);
560 glMatrixMode(GL_MODELVIEW);
561 glLoadIdentity();
562 glTranslatef(VFloatNum!0, -VFloatNum!7, -VFloatNum!25);
564 contacts.length = 0;
565 contacts.assumeSafeAppend;
567 import core.time;
568 MonoTime stt;
569 if (optShowTimes) stt = MonoTime.currTime;
570 world.step(timeStep);
571 if (optShowTimes) {
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; }
670 if (ch == 'T') { optShowTimes = !optShowTimes; return; }
671 if (ch == 'A') { optDrawBVH = !optDrawBVH; return; }