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