1 /* obj.c -- xspringies object (masses, springs) handling
2 * Copyright (C) 1991,1992 Douglas M. DeCarlo
3 *
4 * This file is part of XSpringies, a mass and spring simulation system for X
5 *
6 * XSpringies is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 1, or (at your option)
9 * any later version.
10 *
11 * XSpringies is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with XSpringies; see the file COPYING. If not, write to
18 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 */
21
22 #include "defs.h"
23 #include "obj.h"
24
25 #define MPROXIMITY 8.0
26 #define SPROXIMITY 8.0
27
28 /* Object globals */
29 mass *masses;
30 spring *springs;
31 int num_mass, num_spring;
32 static int num_mass_alloc, num_spring_alloc;
33 int fake_mass, fake_spring;
34 static mass *masses_save = NULL;
35 static spring *springs_save = NULL;
36 static int num_mass_saved, num_mass_savedalloc, num_spring_saved, num_spring_savedalloc;
37
38 /* init_objects: create an initial set of masses and
39 springs to work with
40 */
init_objects()41 void init_objects()
42 {
43 /* Create initial objects */
44 num_mass = 0;
45 num_mass_alloc = ALLOC_SIZE * 2;
46 masses = (mass *)xmalloc(sizeof(mass) * num_mass_alloc);
47
48 num_spring = 0;
49 num_spring_alloc = ALLOC_SIZE;
50 springs = (spring *)xmalloc(sizeof(spring) * num_spring_alloc);
51
52 fake_mass = create_mass();
53 masses[fake_mass].status = S_FIXED;
54 fake_spring = create_spring();
55 springs[fake_spring].status = 0;
56
57 add_massparent(fake_mass, fake_spring);
58 springs[fake_spring].m1 = fake_mass;
59 }
60
attach_fake_spring(tomass)61 void attach_fake_spring(tomass)
62 int tomass;
63 {
64 add_massparent(fake_mass, fake_spring);
65 springs[fake_spring].m2 = tomass;
66 springs[fake_spring].status |= S_ALIVE;
67 springs[fake_spring].ks = mst.cur_ks;
68 springs[fake_spring].kd = mst.cur_kd;
69 }
70
kill_fake_spring()71 void kill_fake_spring()
72 {
73 springs[fake_spring].status &= ~S_ALIVE;
74 }
75
move_fake_mass(mx,my)76 void move_fake_mass(mx, my)
77 int mx, my;
78 {
79 masses[fake_mass].x = mx;
80 masses[fake_mass].y = my;
81 }
82
83 /* create_mass: return the index for a new mass,
84 possibly allocating more space if necesary
85 */
create_mass()86 int create_mass()
87 {
88 if (num_mass >= num_mass_alloc) {
89 /* Allocate more masses */
90 num_mass_alloc += ALLOC_SIZE;
91 masses = (mass *)xrealloc(masses, sizeof(mass) * num_mass_alloc);
92 }
93
94 /* Set parameters for new mass */
95 masses[num_mass].x = masses[num_mass].y = 0.0;
96 masses[num_mass].vx = masses[num_mass].vy = 0.0;
97 masses[num_mass].ax = masses[num_mass].ay = 0.0;
98 masses[num_mass].mass = masses[num_mass].elastic = 0.0;
99 masses[num_mass].radius = masses[num_mass].num_pars = 0;
100 masses[num_mass].pars = NULL;
101 masses[num_mass].status = S_ALIVE;
102
103 /* Return next unused mass */
104 return num_mass++;
105 }
106
107 /* create_spring: return the index for a new spring,
108 possibly allocating more space if necesary
109 */
create_spring()110 int create_spring()
111 {
112 if (num_spring >= num_spring_alloc) {
113 /* Allocate more springs */
114 num_spring_alloc += ALLOC_SIZE;
115 springs = (spring *)xrealloc(springs, sizeof(spring) * num_spring_alloc);
116 }
117
118 /* Set parameters for new spring */
119 springs[num_spring].ks = springs[num_spring].kd = 0.0;
120 springs[num_spring].restlen = 0.0;
121 springs[num_spring].m1 = springs[num_spring].m2 = 0;
122 springs[num_spring].status = S_ALIVE;
123
124 /* Return next unused spring */
125 return num_spring++;
126 }
127
add_massparent(which,parent)128 void add_massparent(which, parent)
129 int which, parent;
130 {
131 int len = masses[which].num_pars++;
132
133 masses[which].pars = (int *)xrealloc(masses[which].pars, (len + 1) * sizeof(int));
134
135 masses[which].pars[len] = parent;
136 }
137
del_massparent(which,parent)138 void del_massparent(which, parent)
139 int which, parent;
140 {
141 int i;
142
143 if (masses[which].status & S_ALIVE) {
144 for (i = 0; i < masses[which].num_pars; i++) {
145 if (masses[which].pars[i] == parent) {
146 if (i == masses[which].num_pars - 1) {
147 masses[which].num_pars--;
148 } else {
149 masses[which].pars[i] = masses[which].pars[--masses[which].num_pars];
150 }
151 return;
152 }
153 }
154 }
155 }
156
157 /* delete_spring: delete a particular spring
158 */
delete_spring(which)159 void delete_spring(which)
160 int which;
161 {
162 if (springs[which].status & S_ALIVE) {
163 springs[which].status = 0;
164 del_massparent(springs[which].m1, which);
165 del_massparent(springs[which].m2, which);
166 }
167 }
168
169 /* delete_mass: delete a particular mass, and all springs
170 directly attached to it
171 */
delete_mass(which)172 void delete_mass(which)
173 int which;
174 {
175 int i;
176
177 if (masses[which].status & S_ALIVE) {
178 masses[which].status = 0;
179
180 /* Delete all springs connected to it */
181 for (i = 0; i < masses[which].num_pars; i++) {
182 delete_spring(masses[which].pars[i]);
183 }
184 }
185
186 if (which == mst.center_id)
187 mst.center_id = -1;
188 }
189
190 /* delete_selected: delete all objects which
191 are currently selected
192 */
delete_selected()193 void delete_selected()
194 {
195 int i;
196
197 for (i = 0; i < num_mass; i++) {
198 if (masses[i].status & S_SELECTED) {
199 delete_mass(i);
200 }
201 }
202
203 for (i = 0; i < num_spring; i++) {
204 if (springs[i].status & S_SELECTED) {
205 delete_spring(i);
206 }
207 }
208 }
209
delete_all()210 void delete_all()
211 {
212 int i;
213
214 for (i = 0; i < num_mass; i++) {
215 free(masses[i].pars);
216 }
217 free(masses);
218 num_mass = num_mass_alloc = 0;
219 free(springs);
220 num_spring = num_spring_alloc = 0;
221 mst.center_id = -1;
222 }
223
reconnect_masses()224 void reconnect_masses()
225 {
226 int i;
227
228 for (i = 0; i < num_mass; i++) {
229 masses[i].num_pars = 0;
230 masses[i].pars = NULL;
231 }
232
233 for (i = 0; i < num_spring; i++) {
234 add_massparent(springs[i].m1, i);
235 add_massparent(springs[i].m2, i);
236 }
237 }
238
restore_state()239 void restore_state()
240 {
241 delete_all();
242
243 if (masses_save != NULL) {
244 num_mass = num_mass_saved;
245 num_mass_alloc = num_mass_savedalloc;
246 num_spring = num_spring_saved;
247 num_spring_alloc = num_spring_savedalloc;
248
249 masses = (mass *)xmalloc(sizeof(mass) * num_mass_alloc);
250 memcpy(masses, masses_save, sizeof(mass) * num_mass_alloc);
251 springs = (spring *)xmalloc(sizeof(spring) * num_spring_alloc);
252 memcpy(springs, springs_save, sizeof(spring) * num_spring_alloc);
253
254 reconnect_masses();
255 }
256 }
257
save_state()258 void save_state()
259 {
260 masses_save = (mass *)xmalloc(sizeof(mass) * num_mass_alloc);
261 memcpy(masses_save, masses, sizeof(mass) * num_mass_alloc);
262 num_mass_saved = num_mass;
263 num_mass_savedalloc = num_mass_alloc;
264
265 springs_save = (spring *)xmalloc(sizeof(spring) * num_spring_alloc);
266 memcpy(springs_save, springs, sizeof(spring) * num_spring_alloc);
267 num_spring_saved = num_spring;
268 num_spring_savedalloc = num_spring_alloc;
269 }
270
271 /* nearest_object: Find the nearest spring or mass to the position
272 (x,y), or return -1 if none are close. Set is_mass accordingly
273 */
nearest_object(x,y,is_mass)274 int nearest_object(x, y, is_mass)
275 int x, y;
276 boolean *is_mass;
277 {
278 int i, closest = -1;
279 double dist, min_dist = MPROXIMITY * MPROXIMITY, rating, min_rating = draw_wid * draw_ht;
280 boolean masses_only = *is_mass;
281
282 *is_mass = TRUE;
283
284 if (masses_only)
285 min_dist = min_dist * 36;
286
287 /* Find closest mass */
288 for (i = 0; i < num_mass; i++) {
289 if (masses[i].status & S_ALIVE) {
290 if ((dist = SQR(masses[i].x - (double)x) + SQR(masses[i].y - (double)y) - (double)SQR(masses[i].radius)) < min_dist) {
291 rating = SQR(masses[i].x - (double)x) + SQR(masses[i].y - (double)y);
292 if (rating < min_rating) {
293 min_dist = dist;
294 min_rating = rating;
295 closest = i;
296 }
297 }
298 }
299 }
300
301 if (closest != -1)
302 return closest;
303
304 if (masses_only)
305 return -1;
306
307 *is_mass = TRUE;
308
309 min_dist = SPROXIMITY;
310
311 /* Find closest spring */
312 for (i = 0; i < num_spring; i++) {
313 double x1, x2, y1, y2;
314
315 if (springs[i].status & S_ALIVE) {
316 x1 = masses[springs[i].m1].x;
317 y1 = masses[springs[i].m1].y;
318 x2 = masses[springs[i].m2].x;
319 y2 = masses[springs[i].m2].y;
320
321 if (x > MIN(x1, x2) - SPROXIMITY && x < MAX(x1, x2) + SPROXIMITY &&
322 y > MIN(y1, y2) - SPROXIMITY && y < MAX(y1, y2) + SPROXIMITY) {
323 double a1, b1, c1, dAB, d;
324
325 a1 = y2 - y1;
326 b1 = x1 - x2;
327 c1 = y1 * x2 - y2 * x1;
328 dAB = sqrt((double)(a1*a1 + b1*b1));
329 d = (x * a1 + y * b1 + c1) / dAB;
330
331 dist = ABS(d);
332
333 if (dist < min_dist) {
334 min_dist = dist;
335 closest = i;
336 *is_mass = FALSE;
337 }
338 }
339 }
340 }
341
342 return closest;
343 }
344
eval_selection()345 void eval_selection()
346 {
347 int i;
348 double sel_mass, sel_elas, sel_ks, sel_kd;
349 boolean sel_fix;
350 boolean found = FALSE, changed = FALSE;
351 boolean mass_same, elas_same, ks_same, kd_same, fix_same;
352
353 for (i = 0; i < num_mass; i++) {
354 if (masses[i].status & S_SELECTED) {
355 if (found) {
356 if (mass_same && masses[i].mass != sel_mass) {
357 mass_same = FALSE;
358 }
359 if (elas_same && masses[i].elastic != sel_elas) {
360 elas_same = FALSE;
361 }
362 if (fix_same && (masses[i].status & S_FIXED)) {
363 fix_same = FALSE;
364 }
365 } else {
366 found = TRUE;
367 sel_mass = masses[i].mass;
368 mass_same = TRUE;
369 sel_elas = masses[i].elastic;
370 elas_same = TRUE;
371 sel_fix = (masses[i].status & S_FIXED);
372 fix_same = TRUE;
373 }
374 }
375 }
376
377 if (found) {
378 if (mass_same && sel_mass != mst.cur_mass) {
379 mst.cur_mass = sel_mass;
380 changed = TRUE;
381 }
382 if (elas_same && sel_elas != mst.cur_rest) {
383 mst.cur_rest = sel_elas;
384 changed = TRUE;
385 }
386 if (fix_same && sel_fix != mst.fix_mass) {
387 mst.fix_mass = sel_fix;
388 changed = TRUE;
389 }
390 }
391
392 found = FALSE;
393 for (i = 0; i < num_spring; i++) {
394 if (springs[i].status & S_SELECTED) {
395 if (found) {
396 if (ks_same && springs[i].ks != sel_ks) {
397 ks_same = FALSE;
398 }
399 if (ks_same && springs[i].ks != sel_ks) {
400 ks_same = FALSE;
401 }
402 } else {
403 found = TRUE;
404 sel_ks = springs[i].ks;
405 ks_same = TRUE;
406 sel_kd = springs[i].kd;
407 kd_same = TRUE;
408 }
409 }
410 }
411
412 if (found) {
413 if (ks_same && sel_ks != mst.cur_ks) {
414 mst.cur_ks = sel_ks;
415 changed = TRUE;
416 }
417 if (kd_same && sel_kd != mst.cur_kd) {
418 mst.cur_kd = sel_kd;
419 changed = TRUE;
420 }
421 }
422
423 if (changed) {
424 redraw_widgets(FALSE);
425 }
426 }
427
anything_selected()428 boolean anything_selected()
429 {
430 int i;
431
432 for (i = 0; i < num_mass; i++) {
433 if (masses[i].status & S_SELECTED)
434 return TRUE;
435 }
436 for (i = 0; i < num_spring; i++) {
437 if (springs[i].status & S_SELECTED)
438 return TRUE;
439 }
440 return FALSE;
441 }
442
select_object(selection,is_mass,shifted)443 void select_object(selection, is_mass, shifted)
444 int selection;
445 boolean is_mass, shifted;
446 {
447 if (is_mass) {
448 if (shifted) {
449 masses[selection].status ^= S_SELECTED;
450 } else {
451 masses[selection].status |= S_SELECTED;
452 }
453 } else {
454 if (shifted) {
455 springs[selection].status ^= S_SELECTED;
456 } else {
457 springs[selection].status |= S_SELECTED;
458 }
459 }
460 }
461
select_objects(ulx,uly,lrx,lry,shifted)462 void select_objects(ulx, uly, lrx, lry, shifted)
463 int ulx, uly, lrx, lry;
464 boolean shifted;
465 {
466 int i;
467
468 for (i = 0; i < num_mass; i++) {
469 if (masses[i].status & S_ALIVE) {
470 if (ulx <= masses[i].x && masses[i].x <= lrx && uly <= masses[i].y && masses[i].y <= lry) {
471 select_object(i, TRUE, FALSE);
472 }
473 }
474 }
475
476 for (i = 0; i < num_spring; i++) {
477 if (springs[i].status & S_ALIVE) {
478 int m1, m2;
479
480 m1 = springs[i].m1;
481 m2 = springs[i].m2;
482
483 if (ulx <= masses[m1].x && masses[m1].x <= lrx && uly <= masses[m1].y && masses[m1].y <= lry &&
484 ulx <= masses[m2].x && masses[m2].x <= lrx && uly <= masses[m2].y && masses[m2].y <= lry) {
485 select_object(i, FALSE, FALSE);
486 }
487 }
488 }
489 }
490
unselect_all()491 void unselect_all()
492 {
493 int i;
494
495 for (i = 0; i < num_mass; i++) {
496 if (masses[i].status & S_SELECTED) {
497 masses[i].status &= ~S_SELECTED;
498 }
499 }
500
501 for (i = 0; i < num_spring; i++) {
502 if (springs[i].status & S_SELECTED) {
503 springs[i].status &= ~S_SELECTED;
504 }
505 }
506 }
507
select_all()508 void select_all()
509 {
510 int i;
511
512 for (i = 0; i < num_mass; i++) {
513 if (masses[i].status & S_ALIVE) {
514 masses[i].status |= S_SELECTED;
515 }
516 }
517
518 for (i = 0; i < num_spring; i++) {
519 if (springs[i].status & S_ALIVE) {
520 springs[i].status |= S_SELECTED;
521 }
522 }
523 }
524
duplicate_selected()525 void duplicate_selected()
526 {
527 int i, j, *mapfrom, *mapto, num_map, num_map_alloc, spring_start;
528 int which;
529
530 spring_start = num_spring;
531
532 num_map = 0;
533 num_map_alloc = ALLOC_SIZE;
534 mapfrom = (int *)xmalloc(sizeof(int) * num_map_alloc);
535 mapto = (int *)xmalloc(sizeof(int) * num_map_alloc);
536
537 for (i = 0; i < num_mass; i++) {
538 if (masses[i].status & S_SELECTED) {
539 if (num_map >= num_map_alloc) {
540 num_map_alloc += ALLOC_SIZE;
541 mapfrom = (int *)xrealloc(mapfrom, sizeof(int) * num_map_alloc);
542 mapto = (int *)xrealloc(mapto, sizeof(int) * num_map_alloc);
543 }
544
545 which = create_mass();
546 mapto[num_map] = which;
547 mapfrom[num_map] = i;
548 num_map++;
549 masses[which] = masses[i];
550 masses[which].status &= ~S_SELECTED;
551 masses[which].num_pars = 0;
552 masses[which].pars = NULL;
553 }
554 }
555
556 for (i = 0; i < spring_start; i++) {
557 if (springs[i].status & S_SELECTED) {
558 boolean m1done, m2done;
559
560 m1done = m2done = FALSE;
561
562 which = create_spring();
563 springs[which] = springs[i];
564 springs[which].status &= ~S_SELECTED;
565
566 for (j = 0; (!m1done || !m2done) && j < num_map; j++) {
567 if (!m1done && springs[which].m1 == mapfrom[j]) {
568 springs[which].m1 = mapto[j];
569 add_massparent(mapto[j], which);
570 m1done = TRUE;
571 }
572 if (!m2done && springs[which].m2 == mapfrom[j]) {
573 springs[which].m2 = mapto[j];
574 add_massparent(mapto[j], which);
575 m2done = TRUE;
576 }
577 }
578 if (!m1done && !m2done) {
579 /* delete spring that isn't connected to anyone */
580 delete_spring(which);
581 }
582 }
583 }
584
585 free(mapfrom);
586 free(mapto);
587 }
588
translate_selobj(dx,dy)589 void translate_selobj(dx, dy)
590 int dx, dy;
591 {
592 int i;
593
594 for (i = 0; i < num_mass; i++) {
595 if (masses[i].status & S_SELECTED) {
596 masses[i].x += dx;
597 masses[i].y += dy;
598 }
599 }
600 }
601
changevel_selobj(vx,vy,relative)602 void changevel_selobj(vx, vy, relative)
603 int vx, vy;
604 boolean relative;
605 {
606 int i;
607
608 for (i = 0; i < num_mass; i++) {
609 if (masses[i].status & S_SELECTED) {
610 if (relative) {
611 masses[i].vx += vx;
612 masses[i].vy += vy;
613 } else {
614 masses[i].vx = vx;
615 masses[i].vy = vy;
616 }
617 }
618 }
619 }
620
tempfixed_obj(store)621 void tempfixed_obj(store)
622 boolean store;
623 {
624 int i;
625
626 for (i = 0; i < num_mass; i++) {
627 if (masses[i].status & S_SELECTED) {
628 if (store) {
629 masses[i].status &= ~S_TEMPFIXED;
630 if (!(masses[i].status & S_FIXED)) {
631 masses[i].status |= (S_TEMPFIXED | S_FIXED);
632 }
633 } else {
634 if (masses[i].status & S_TEMPFIXED) {
635 masses[i].status &= ~S_FIXED;
636 }
637 }
638 }
639 }
640 }
641
set_sel_restlen()642 void set_sel_restlen()
643 {
644 int i;
645 double dx, dy;
646
647 for (i = 0; i < num_spring; i++) {
648 if (springs[i].status & S_SELECTED) {
649 dx = masses[springs[i].m1].x - masses[springs[i].m2].x;
650 dy = masses[springs[i].m1].y - masses[springs[i].m2].y;
651 springs[i].restlen = sqrt(dx * dx + dy * dy);
652 }
653 }
654 }
655
set_center()656 void set_center()
657 {
658 int i, cent = -1;
659
660 for (i = 0; i < num_mass; i++) {
661 if (masses[i].status & S_SELECTED) {
662 if (cent != -1)
663 return;
664
665 cent = i;
666 }
667 }
668
669 mst.center_id = cent;
670 }
671