11 Cursor crosscursor = {
13 {0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0,
14 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF,
15 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0xC0,
16 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, },
17 {0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
18 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x7F, 0xFE,
19 0x7F, 0xFE, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
20 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00, }
25 {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF,
26 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF,
27 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF,
28 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8, },
29 {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84,
30 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE,
31 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
32 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00, }
37 0x01, 0x80, 0x03, 0xC0, 0x07, 0xE0, 0x07, 0xe0,
38 0x07, 0xe0, 0x07, 0xe0, 0x03, 0xc0, 0x0F, 0xF0,
39 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8,
40 0x0F, 0xF0, 0x1F, 0xF8, 0x3F, 0xFC, 0x3F, 0xFC,
42 0x01, 0x80, 0x03, 0xC0, 0x07, 0xE0, 0x04, 0x20,
43 0x04, 0x20, 0x06, 0x60, 0x02, 0x40, 0x0C, 0x30,
44 0x10, 0x08, 0x14, 0x08, 0x14, 0x28, 0x12, 0x28,
45 0x0A, 0x50, 0x16, 0x68, 0x20, 0x04, 0x3F, 0xFC,
71 int showv, showa, throttle, paused;
112 r = nrand(nelem(cols));
114 cols[r].i = allocimage(display, Rect(0,0,1,1), screen->chan, 1, cols[r].c);
125 sysfatal("invalid pause value %d:", p);
128 if(pid != -1 && pid != id)
137 if(!paused || pid != id)
150 static char buf[1024];
152 snprint(buf, sizeof(buf), "Number of bodies: %d", glxy.l);
153 p = addpt(screen->r.min, (Point){5, 3});
154 string(screen, p, display->white, ZP, font, buf);
156 snprint(buf, sizeof(buf), "Avg. calculations per body: %g", avgcalcs);
157 p = addpt(p, (Point){0, font->height});
158 string(screen, p, display->white, ZP, font, buf);
160 snprint(buf, sizeof(buf), "Max depth of quad tree: %d", quaddepth);
161 p = addpt(p, (Point){0, font->height});
162 string(screen, p, display->white, ZP, font, buf);
172 draw(screen, screen->r, display->black, 0, ZP);
173 for(b = glxy.a; b < glxy.a + glxy.l; b++) {
174 pos.x = b->x / scale + orig.x;
175 pos.y = b->y / scale + orig.y;
177 fillellipse(screen, pos, s, s, b->col, ZP);
181 if(va.x != 0 || va.y != 0)
182 line(screen, pos, addpt(pos, va), Enddisc, Endarrow, 0, b->col, ZP);
185 va.x = b->a.x/scale*50;
186 va.y = b->a.y/scale*50;
187 if(va.x != 0 || va.y != 0)
188 line(screen, pos, addpt(pos, va), Enddisc, Endarrow, 0, b->col, ZP);
192 flushimage(display, 1);
201 pos.x = b->x / scale + orig.x;
202 pos.y = b->y / scale + orig.y;
203 d = subpt(mc->xy, pos);
205 b->size = h == 0 ? scale : h*scale;
206 b->mass = b->size*b->size*b->size;
214 pos.x = b->x / scale + orig.x;
215 pos.y = b->y / scale + orig.y;
216 d = subpt(mc->xy, pos);
217 b->v.x = (double)d.x*scale/10;
218 b->v.y = (double)d.y*scale/10;
224 b->x = (mc->xy.x - orig.x) * scale;
225 b->y = (mc->xy.y - orig.y) * scale;
286 if(!(mc->buttons & 1))
290 else if(mc->buttons == 5)
299 orig.x += gc.x / scale;
300 orig.y += gc.y / scale;
304 getinput(char *info, char *sug)
306 static char buf[1024];
307 static Channel *rchan;
312 rchan = chancreate(sizeof(Rune), 20);
315 strecpy(buf, buf+1024, sug);
320 r = enter(info, buf, sizeof(buf), mc, &kc, nil);
323 sysfatal("save: could not get filename: %r");
327 sysfatal("getinput: could not save input: %r");
336 setcursor(mc, &crosscursor);
342 off = subpt(mc->xy, oldp);
345 orig = addpt(orig, off);
349 setcursor(mc, cursor);
358 setcursor(mc, &zoomcursor);
366 d = subpt(mc->xy, z);
367 f = tanh((double)d.y/200) + 1;
374 setcursor(mc, cursor);
381 orig = divpt(subpt(screen->r.max, screen->r.min), 2);
382 orig = addpt(orig, screen->r.min);
395 switch(menuhit(3, mc, &menu, nil)) {
400 s = getinput("Enter file:", file);
401 if(s == nil || *s == '\0')
405 fd = create(file, OWRITE, 0666);
407 sysfatal("domenu: could not create file %s: %r", file);
412 s = getinput("Enter file:", file);
413 if(s == nil || *s == '\0')
417 fd = open(file, OREAD);
419 sysfatal("domenu: could not open file %s: %r", file);
424 s = getinput("Speed multiplier:", nil);
425 if(s == nil || *s == '\0')
435 s = getinput("Gravity multiplier:", nil);
436 if(s == nil || *s == '\0')
455 threadsetname("mouse");
458 switch(mc->buttons) {
475 threadsetname("resize");
477 recv(mc->resizec, nil);
479 if(getwindow(display, Refnone) < 0)
480 sysfatal("resize failed: %r");
492 threadsetname("keyboard");
493 realkc = initkeyboard(nil);
495 sysfatal("kbdthread: could not initkeyboard: %r");
522 cursor = &pausecursor;
525 setcursor(mc, cursor);
531 /* verlet barnes-hut */
538 threadsetname("simulate");
551 STATS(quaddepth = 0;)
552 for(b = glxy.a; b < glxy.a + glxy.l; b++) {
553 if(quadins(b, LIM) == -1) {
560 for(b = glxy.a; b < glxy.a + glxy.l; b++) {
563 b->newa.x = b->newa.y = 0;
565 quadcalc(b, space, LIM);
566 STATS(avgcalcs += calcs;)
568 STATS(avgcalcs /= glxy.l;)
570 for(b = glxy.a; b < glxy.a + glxy.l; b++) {
571 b->x += dt*b->v.x + dt²*b->a.x/2;
572 b->y += dt*b->v.y + dt²*b->a.y/2;
573 b->v.x += dt*(b->a.x + b->newa.x)/2;
574 b->v.y += dt*(b->a.y + b->newa.y)/2;
585 fprint(2, "Usage: %s [-t throttle] [-G gravity] [-ε smooth] [-i] [file]\n", argv0);
586 threadexitsall("usage");
590 threadmain(int argc, char **argv)
600 throttle = strtol(EARGF(usage()), nil, 0);
603 G = strtod(EARGF(usage()), nil);
606 ε = strtod(EARGF(usage()), nil);
616 fmtinstall('B', Bfmt);
621 file = strdup(argv[0]);
623 sysfatal("threadmain: could not save file name: %r");
625 if(open(file, OREAD) != 0)
626 sysfatal("threadmain: could not open file: %r");
629 if(initdraw(nil, nil, "Galaxy") < 0)
630 sysfatal("initdraw failed: %r");
631 if(mc = initmouse(nil, screen), mc == nil)
632 sysfatal("initmouse failed: %r");
635 orig = divpt(subpt(screen->r.max, screen->r.min), 2);
636 orig = addpt(orig, screen->r.min);
642 threadcreate(mousethread, nil, STK);
643 threadcreate(resizethread, nil, STK);
644 threadcreate(kbdthread, nil, STK);
645 proccreate(simulate, nil, STK);