BRL-CAD
metaball.c
Go to the documentation of this file.
1 /* M E T A B A L L . C
2  * BRL-CAD
3  *
4  * Copyright (c) 1985-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 /** @addtogroup primitives */
21 /** @{ */
22 /** @file primitives/metaball/metaball.c
23  *
24  * Intersect a ray with a metaball implicit surface.
25  *
26  * Algorithm:
27  *
28  * The heart of it is the set of point evaluation functions. These are
29  * different for each "type" of blobby object (metaballs, blinn blobs,
30  * and iso-potential contours). All are simple summation formulas at
31  * the moment.
32  *
33  * Plot merely draws sphere type objects around the control points,
34  * with size related to each points 'strength' and the primitives
35  * threshold.
36  *
37  * Ray-tracing is incredibly hackish. The ray is walked in a fairly
38  * coarse matter until the point evaluation crosses the threshold
39  * value, then a basic binary search is done to refine the
40  * approximated hit point.
41  *
42  * THIS PRIMITIVE IS INCOMPLETE AND SHOULD BE CONSIDERED EXPERIMENTAL.
43  *
44  */
45 /** @} */
46 
47 #include "common.h"
48 
49 #include <stddef.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <math.h>
54 #include "bnetwork.h"
55 
56 #include "bu/cv.h"
57 #include "vmath.h"
58 #include "db.h"
59 #include "nmg.h"
60 #include "rtgeom.h"
61 #include "raytrace.h"
62 #include "nurb.h"
63 #include "wdb.h"
64 
65 #include "metaball.h"
66 
67 #define SQ(a) ((a)*(a))
68 
69 #define PLOT_THE_BIG_BOUNDING_SPHERE 0
70 
71 const char *metaballnames[] =
72 {
73  "Metaball",
74  "Isopotential",
75  "Blob",
76  NULL
77 };
78 
79 
81 {
82  int i = 0;
83  while (metaballnames[i++])
84  if (!bu_strncmp(metaballnames[i], name, 4))
85  return i;
86  return -1;
87 }
88 
89 
90 const char *rt_metaball_lookup_type_name(const int id)
91 {
92  return metaballnames[id];
93 }
94 
95 
96 /* compute the bounding sphere for a metaball cluster. center is
97  * filled, and the radius is returned.
98  */
99 fastf_t
100 rt_metaball_get_bounding_sphere(point_t *center, fastf_t threshold, struct rt_metaball_internal *mb)
101 {
102  struct wdb_metaballpt *mbpt, *mbpt2;
103  point_t min, max, d;
104  fastf_t r = 0.0, dist, mag;
105  int i;
106  struct bu_list *points;
107 
108  points = &mb->metaball_ctrl_head;
109 
110  /* find a bounding box for the POINTS (NOT the surface) */
111  VSETALL(min, +INFINITY);
112  VSETALL(max, -INFINITY);
113  for (BU_LIST_FOR(mbpt, wdb_metaballpt, points))
114  for (i = 0; i < 3; i++) {
115  if (mbpt->coord[i] < min[i])
116  min[i] = mbpt->coord[i];
117  if (mbpt->coord[i] > max[i])
118  max[i] = mbpt->coord[i];
119  }
120  /* return -1 if no points are defined. */
121  if (ZERO(min[X] - INFINITY) || min[X] > INFINITY)
122  return -1;
123 
124  /* compute the center of the generated box, call that the center */
125  VADD2(*center, min, max);
126  VSCALE(*center, *center, 0.5);
127 
128  /* start looking for the radius... */
129  for (BU_LIST_FOR(mbpt, wdb_metaballpt, points)) {
130  VSUB2(d, mbpt->coord, *center);
131  /* since the surface is where threshold=fldstr/mag,
132  mag=fldstr/threshold, so make that the initial value */
133  dist = MAGNITUDE(d) + mbpt->fldstr/threshold;
134  /* and add all the contribution */
135  for (BU_LIST_FOR(mbpt2, wdb_metaballpt, points))
136  if (mbpt2 != mbpt) {
137  fastf_t additive = 0.0;
138  VSUB2(d, mbpt2->coord, mbpt->coord);
139  mag = MAGNITUDE(d) + dist;
140 
141  switch (mb->method) {
142  case METABALL_METABALL:
143  break;
144  case METABALL_ISOPOTENTIAL:
145  additive = fabs(mbpt2->fldstr) * mbpt2->fldstr / mag;
146  break;
147  case METABALL_BLOB:
148  additive = 1.0/exp((mbpt2->sweat / (mbpt2->fldstr * mbpt2->fldstr)) * mag * mag - mbpt2->sweat);
149  break;
150  }
151 
152  dist += additive;
153  }
154  /* then see if this is a 'defining' point */
155  if (dist > r)
156  r = dist;
157  }
158  return r;
159 }
160 
161 /**
162  * Calculate a bounding RPP around a metaball
163  */
164 int
165 rt_metaball_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *UNUSED(tol))
166 {
167  struct rt_metaball_internal *mb;
168  point_t center;
169  fastf_t radius;
170  mb = (struct rt_metaball_internal *)ip->idb_ptr;
171  RT_METABALL_CK_MAGIC(mb);
172  VSETALL(center, 0);
173 
174  radius = rt_metaball_get_bounding_sphere(&center, mb->threshold, mb);
175 
176  VSET((*min), center[X] - radius, center[Y] - radius, center[Z] - radius);
177  VSET((*max), center[X] + radius, center[Y] + radius, center[Z] + radius);
178  return 0;
179 }
180 
181 
182 /**
183  * prep and build bounding volumes... unfortunately, generating the
184  * bounding sphere is too 'loose' (I think) and O(n^2).
185  */
186 int
187 rt_metaball_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
188 {
189  struct rt_metaball_internal *mb, *nmb;
190  struct wdb_metaballpt *mbpt, *nmbpt;
191  fastf_t minfstr = +INFINITY;
192 
193  if (rtip) RT_CK_RTI(rtip);
194 
195  mb = (struct rt_metaball_internal *)ip->idb_ptr;
196  RT_METABALL_CK_MAGIC(mb);
197 
198  /* generate a copy of the metaball */
199  BU_ALLOC(nmb, struct rt_metaball_internal);
200  nmb->magic = RT_METABALL_INTERNAL_MAGIC;
201  BU_LIST_INIT(&nmb->metaball_ctrl_head);
202  nmb->threshold = mb->threshold;
203  nmb->method = mb->method;
204 
205  /* and copy the list of control points */
206  for (BU_LIST_FOR(mbpt, wdb_metaballpt, &mb->metaball_ctrl_head)) {
207  BU_ALLOC(nmbpt, struct wdb_metaballpt);
208  nmbpt->fldstr = mbpt->fldstr;
209  if (mbpt->fldstr < minfstr)
210  minfstr = mbpt->fldstr;
211  nmbpt->sweat = mbpt->sweat;
212  VMOVE(nmbpt->coord, mbpt->coord);
213  BU_LIST_INSERT(&nmb->metaball_ctrl_head, &nmbpt->l);
214  }
215 
216  /* find the bounding sphere */
217  stp->st_aradius = rt_metaball_get_bounding_sphere(&stp->st_center, mb->threshold, mb);
218  stp->st_bradius = stp->st_aradius * 1.01;
219 
220  /* XXX magic numbers, increase if scalloping is observed. :(*/
221  nmb->initstep = minfstr / 2.0;
222  if (nmb->initstep < (stp->st_aradius / 200.0))
223  nmb->initstep = (stp->st_aradius / 200.0);
224  else if (nmb->initstep > (stp->st_aradius / 10.0))
225  nmb->initstep = (stp->st_aradius / 10.0);
226  nmb->finalstep = /*stp->st_aradius * */minfstr / 1e5;
227 
228  /* generate a bounding box around the sphere...
229  * XXX this can be optimized greatly to reduce the BSP presence... */
230  if (rt_metaball_bbox(ip, &(stp->st_min), &(stp->st_max), &rtip->rti_tol)) return 1;
231  stp->st_specific = (void *)nmb;
232  return 0;
233 }
234 
235 
236 void
237 rt_metaball_print(register const struct soltab *stp)
238 {
239  int metaball_count = 0;
240  struct rt_metaball_internal *mb;
241  struct wdb_metaballpt *mbpt;
242 
243  mb = (struct rt_metaball_internal *)stp->st_specific;
244  RT_METABALL_CK_MAGIC(mb);
245  for (BU_LIST_FOR(mbpt, wdb_metaballpt, &mb->metaball_ctrl_head)) ++metaball_count;
246  bu_log("Metaball with %d points and a threshold of %g (%s rendering)\n", metaball_count, mb->threshold, rt_metaball_lookup_type_name(mb->method));
247  metaball_count = 0;
248  for (BU_LIST_FOR(mbpt, wdb_metaballpt, &mb->metaball_ctrl_head))
249  bu_log("\t%d: %g field strength at (%g, %g, %g) and 'goo' of %g\n", ++metaball_count, mbpt->fldstr, V3ARGS(mbpt->coord), mbpt->sweat);
250  return;
251 }
252 
253 
254 void
255 rt_metaballpt_print(const struct wdb_metaballpt *metaball, double mm2local)
256 {
257  point_t p1;
258 
259  bu_log("Metaball Point:\n");
260  VSCALE(p1, metaball->coord, mm2local);
261  bu_log("\tat (%g %g %g)\n", V3ARGS(p1));
262  bu_log("\tfield strength = %g\n", metaball->fldstr*mm2local);
263  return;
264 }
265 
266 
267 fastf_t
268 rt_metaball_point_value_metaball(const point_t *p, const struct bu_list *points)
269 {
270  if (!p || !points) bu_bomb("oops");
271 
272  bu_log("ERROR: rt_metaball_point_value_metaball() is not implemented");
273 
274  /* Makes the compiler happy */
275  return 0.0;
276 }
277 
278 
279 fastf_t
280 rt_metaball_point_value_iso(const point_t *p, const struct bu_list *points)
281 {
282  struct wdb_metaballpt *mbpt;
283  fastf_t ret = 0.0;
284  point_t v;
285 
286  for (BU_LIST_FOR(mbpt, wdb_metaballpt, points)) {
287  VSUB2(v, mbpt->coord, *p);
288  ret += fabs(mbpt->fldstr) * mbpt->fldstr / MAGSQ(v); /* f/r^2 */
289  }
290  return ret;
291 }
292 
293 
294 fastf_t
295 rt_metaball_point_value_blob(const point_t *p, const struct bu_list *points)
296 {
297  struct wdb_metaballpt *mbpt;
298  fastf_t ret = 0.0;
299  point_t v;
300 
301  for (BU_LIST_FOR(mbpt, wdb_metaballpt, points)) {
302  /* TODO: test if sweat is sufficient enough that r=0 returns a positive value? */
303  /* TODO: test to see if negative contribution needs to be wiped out? */
304  VSUB2(v, mbpt->coord, *p);
305  ret += 1.0 / exp((mbpt->sweat/(mbpt->fldstr*mbpt->fldstr)) * MAGSQ(v) - mbpt->sweat);
306  }
307  return ret;
308 }
309 
310 
311 /* main point evaluation function, to be exposed to the ugly outside
312  * world.
313  */
314 fastf_t
315 rt_metaball_point_value(const point_t *p, const struct rt_metaball_internal *mb)
316 {
317  RT_METABALL_CK_MAGIC(mb);
318  switch (mb->method) {
319  case METABALL_METABALL:
320  return rt_metaball_point_value_metaball(p, &mb->metaball_ctrl_head);
321  case METABALL_ISOPOTENTIAL:
322  return rt_metaball_point_value_iso(p, &mb->metaball_ctrl_head);
323  case METABALL_BLOB:
324  return rt_metaball_point_value_blob(p, &mb->metaball_ctrl_head);
325  default:
326  break;
327  }
328  return 0;
329 }
330 
331 
332 /* returns true if the point is in/on the unscaled metaball. */
333 int
334 rt_metaball_point_inside(const point_t *p, const struct rt_metaball_internal *mb)
335 {
336  return rt_metaball_point_value(p, mb) >= mb->threshold;
337 }
338 
339 
340 /*
341  * Solve the surface intersection of mb with an accuracy of finalstep given that
342  * one of the two points (a and b) are inside and the other is outside.
343  */
344 int
345 rt_metaball_find_intersection(point_t *intersect, const struct rt_metaball_internal *mb, const point_t *a, const point_t *b, fastf_t step, const fastf_t finalstep)
346 {
347  point_t mid;
348  const point_t *midp = (const point_t *)&mid;
349 
350  VADD2(mid, *a, *b);
351  VSCALE(mid, mid, 0.5);
352 
353  if (finalstep > step) {
354  VMOVE(*intersect, mid); /* should this be the midpoint between a and b? */
355  return 0;
356  }
357 
358  /* should probably make a or b necessarily inside, to eliminate one point
359  * computation? */
360  return rt_metaball_find_intersection(intersect, mb, midp, (rt_metaball_point_inside(a, mb) == rt_metaball_point_inside(midp, mb)) ?b:a , step/2.0, finalstep);
361 }
362 
363 
364 int
365 rt_metaball_shot(struct soltab *stp, register struct xray *rp, struct application *ap, struct seg *seghead)
366 {
367  struct rt_metaball_internal *mb = (struct rt_metaball_internal *)stp->st_specific;
368  struct seg *segp = NULL;
369  int retval = 0;
370  fastf_t step, distleft;
371  point_t p, inc;
372  const point_t *cp = (const point_t *)&p;
373 
374  /* switching behavior to retain old code for performance and correctness
375  * comparisons. */
376 #define SHOOTALGO 3
377 
378 #if SHOOTALGO == 2
379  int fhin = 1;
380 #endif
381 
382  step = mb->initstep;
383  distleft = (rp->r_max-rp->r_min) + step * 3.0;
384 
385  VMOVE(p, rp->r_pt);
386  VSCALE(inc, rp->r_dir, step); /* assume it's normalized and we want to creep at step */
387 
388  /* walk back out of the solid */
389  while (rt_metaball_point_value(cp, mb) >= mb->threshold) {
390 #if SHOOTALGO == 2
391  fhin = -1;
392 #endif
393  distleft += step;
394  VSUB2(p, p, inc);
395  }
396 
397 #if SHOOTALGO == 2
398  /* we hit, but not as fine-grained as we want. So back up one step,
399  * cut the step size in half and start over...
400  */
401  {
402  int mb_stat = 0, segsleft = abs(ap->a_onehit);
403  point_t delta;
404 
405 #define STEPBACK { distleft += step; VSUB2(p, p, inc); step *= .5; VSCALE(inc, inc, .5); }
406 #define STEPIN(x) { \
407  --segsleft; \
408  ++retval; \
409  VSUB2(delta, p, rp->r_pt); \
410  segp->seg_##x.hit_dist = fhin * MAGNITUDE(delta); \
411  segp->seg_##x.hit_surfno = 0; }
412  while (mb_stat == 0 && distleft >= -0) {
413  int in;
414 
415  distleft -= step;
416  VADD2(p, p, inc);
417  in = rt_metaball_point_value(cp, mb) > mb->threshold;
418  if (mb_stat == 1)
419  if ( !in )
420  if (step<=mb->finalstep) {
421  STEPIN(out)
422  step = mb->initstep;
423  mb_stat = 0;
424  if (ap->a_onehit != 0 || segsleft <= 0)
425  return retval;
426  } else
427  STEPBACK
428  else
429  if ( in )
430  if (step<=mb->finalstep) {
431  RT_GET_SEG(segp, ap->a_resource);
432  segp->seg_stp = stp;
433  STEPIN(in)
434  fhin = 1;
435  BU_LIST_INSERT(&(seghead->l), &(segp->l));
436  /* reset the ray-walk stuff */
437  mb_stat = 1;
438  VADD2(p, p, inc); /* set p to a point inside */
439  step = mb->initstep;
440  } else
441  STEPBACK
442  }
443  }
444 #undef STEPBACK
445 #undef STEPIN
446 #elif SHOOTALGO == 3
447  {
448  int mb_stat = 0, segsleft = abs(ap->a_onehit);
449  point_t lastpoint;
450 
451  while (distleft >= 0.0 || mb_stat == 1) {
452  /* advance to the next point */
453  distleft -= step;
454  VMOVE(lastpoint, p);
455  VADD2(p, p, inc);
456  if (mb_stat == 1) {
457  if (rt_metaball_point_value(cp, mb) < mb->threshold) {
458  point_t intersect, delta;
459  const point_t *pA = (const point_t *)&lastpoint;
460  const point_t *pB = (const point_t *)&p;
461  rt_metaball_find_intersection(&intersect, mb, pA, pB, step, mb->finalstep);
462  VMOVE(segp->seg_out.hit_point, intersect);
463  --segsleft;
464  ++retval;
465  VSUB2(delta, intersect, rp->r_pt);
466  segp->seg_out.hit_dist = MAGNITUDE(delta);
467  segp->seg_out.hit_surfno = 0;
468  mb_stat = 0;
469  if (ap->a_onehit != 0 && segsleft <= 0)
470  return retval;
471  }
472  } else {
473  if (rt_metaball_point_value(cp, mb) > mb->threshold) {
474  point_t intersect, delta;
475  const point_t *pA = (const point_t *)&lastpoint;
476  const point_t *pB = (const point_t *)&p;
477  rt_metaball_find_intersection(&intersect, mb, pA, pB, step, mb->finalstep);
478  RT_GET_SEG(segp, ap->a_resource);
479  segp->seg_stp = stp;
480  --segsleft;
481  ++retval;
482  VMOVE(segp->seg_in.hit_point, intersect);
483  VSUB2(delta, intersect, rp->r_pt);
484  segp->seg_in.hit_dist = MAGNITUDE(delta);
485  segp->seg_in.hit_surfno = 0;
486  BU_LIST_INSERT(&(seghead->l), &(segp->l));
487 
488  mb_stat = 1;
489  step = mb->initstep;
490  }
491  }
492  }
493  }
494 
495 #else
496 # error "pick a valid algo."
497 #endif
498 
499  return retval;
500 }
501 
502 inline void
503 rt_metaball_norm_internal(vect_t *n, point_t *p, struct rt_metaball_internal *mb)
504 {
505  struct wdb_metaballpt *mbpt;
506  vect_t v;
507  fastf_t a;
508 
509  VSETALL(*n, 0.0);
510 
511  switch (mb->method) {
512  case METABALL_METABALL: bu_log("Sorry, strict metaballs are not yet implemented\n");
513  break;
514  case METABALL_ISOPOTENTIAL:
515  for (BU_LIST_FOR(mbpt, wdb_metaballpt, &mb->metaball_ctrl_head)) {
516  VSUB2(v, *p, mbpt->coord);
517  a = MAGSQ(v);
518  VJOIN1(*n, *n, fabs(mbpt->fldstr)*mbpt->fldstr / (SQ(a)), v); /* f/r^4 */
519  }
520  break;
521  case METABALL_BLOB:
522  for (BU_LIST_FOR(mbpt, wdb_metaballpt, &mb->metaball_ctrl_head)) {
523  VSUB2(v, *p, mbpt->coord);
524  a = MAGSQ(v);
525  VJOIN1(*n, *n, 2.0*mbpt->sweat/SQ(mbpt->fldstr)*exp(mbpt->sweat*(1-(a/SQ(mbpt->fldstr)))) , v);
526  }
527  break;
528  default: bu_log("unknown metaball method\n"); break;
529  }
530  VUNITIZE(*n);
531 }
532 
533 /**
534  * Given ONE ray distance, return the normal and entry/exit point.
535  */
536 void
537 rt_metaball_norm(register struct hit *hitp, struct soltab *stp, register struct xray *rp)
538 {
539  if (rp) RT_CK_RAY(rp); /* unused. */
540  rt_metaball_norm_internal(&(hitp->hit_normal), &(hitp->hit_point), (struct rt_metaball_internal *)(stp->st_specific));
541  return;
542 }
543 
544 
545 /**
546  * Return the curvature of the metaball.
547  */
548 void
549 rt_metaball_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp)
550 {
551  struct rt_metaball_internal *metaball = (struct rt_metaball_internal *)stp->st_specific;
552 
553  if (!metaball || !cvp) return;
554  if (hitp) RT_CK_HIT(hitp);
555 
556  bu_log("ERROR: rt_metaball_curve() is not implemented\n");
557  return;
558 }
559 
560 
561 /**
562  * For a hit on the surface of an METABALL, return the (u, v)
563  * coordinates of the hit point, 0 <= u, v <= 1.
564  *
565  * u = azimuth
566  * v = elevation
567  */
568 void
569 rt_metaball_uv(struct application *ap, struct soltab *stp, struct hit *hitp, struct uvcoord *uvp)
570 {
571  struct rt_metaball_internal *metaball = (struct rt_metaball_internal *)stp->st_specific;
572  vect_t work, pprime;
573  fastf_t r;
574 
575  if (ap) RT_CK_APPLICATION(ap);
576  if (stp) RT_CK_SOLTAB(stp);
577  if (hitp) RT_CK_HIT(hitp);
578  if (!uvp) return;
579  if (!metaball) return;
580 
581  /* stuff stolen from sph */
582  VSUB2(work, hitp->hit_point, stp->st_center);
583  VSCALE(pprime, work, 1.0/MAGNITUDE(work));
584  /* Assert that pprime has unit length */
585 
586  /* U is azimuth, atan() range: -pi to +pi */
587  uvp->uv_u = bn_atan2(pprime[Y], pprime[X]) * M_1_2PI;
588  if (uvp->uv_u < 0)
589  uvp->uv_u += 1.0;
590  /*
591  * V is elevation, atan() range: -pi/2 to +pi/2, because sqrt()
592  * ensures that X parameter is always >0
593  */
594  uvp->uv_v = bn_atan2(pprime[Z],
595  sqrt(pprime[X] * pprime[X] + pprime[Y] * pprime[Y])) * M_1_2PI;
596 
597  /* approximation: r / (circumference, 2 * pi * aradius) */
598  r = ap->a_rbeam + ap->a_diverge * hitp->hit_dist;
599  uvp->uv_du = uvp->uv_dv =
600  M_1_2PI * r / stp->st_aradius;
601  return;
602 }
603 
604 
605 void
606 rt_metaball_free(register struct soltab *stp)
607 {
608  struct rt_metaball_internal *metaball = (struct rt_metaball_internal *)stp->st_specific;
609 
610  bu_free((char *)metaball, "rt_metaball_internal");
611 }
612 
613 
614 int
615 rt_metaball_class(const struct soltab *stp, const fastf_t *min, const fastf_t *max, const struct bn_tol *tol)
616 {
617  if (stp) RT_CK_SOLTAB(stp);
618  if (tol) BN_CK_TOL(tol);
619  if (!min) return 0;
620  if (!max) return 0;
621 
622  return 0;
623 }
624 
625 
626 void
627 rt_metaball_plot_sph(struct bu_list *vhead, point_t *center, fastf_t radius)
628 {
629  fastf_t top[16*3], middle[16*3], bottom[16*3];
630  point_t a, b, c;
631  int i;
632 
633  BU_CK_LIST_HEAD(vhead);
634 
635  VSET(a, radius, 0, 0);
636  VSET(b, 0, radius, 0);
637  VSET(c, 0, 0, radius);
638  rt_ell_16pts(top, *center, a, b);
639  rt_ell_16pts(bottom, *center, b, c);
640  rt_ell_16pts(middle, *center, a, c);
641 
642  RT_ADD_VLIST(vhead, &top[15*ELEMENTS_PER_VECT], BN_VLIST_LINE_MOVE);
643  for (i = 0; i < 16; i++) RT_ADD_VLIST(vhead, &top[i*ELEMENTS_PER_VECT], BN_VLIST_LINE_DRAW);
644  RT_ADD_VLIST(vhead, &bottom[15*ELEMENTS_PER_VECT], BN_VLIST_LINE_MOVE);
645  for (i = 0; i < 16; i++) RT_ADD_VLIST(vhead, &bottom[i*ELEMENTS_PER_VECT], BN_VLIST_LINE_DRAW);
646  RT_ADD_VLIST(vhead, &middle[15*ELEMENTS_PER_VECT], BN_VLIST_LINE_MOVE);
647  for (i = 0; i < 16; i++) RT_ADD_VLIST(vhead, &middle[i*ELEMENTS_PER_VECT], BN_VLIST_LINE_DRAW);
648 }
649 
650 
651 int
652 rt_metaball_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct rt_tess_tol *UNUSED(ttol), const struct bn_tol *UNUSED(tol), const struct rt_view_info *UNUSED(info))
653 {
654  struct rt_metaball_internal *mb;
655  struct wdb_metaballpt *mbpt;
656  point_t bsc;
657  fastf_t rad;
658 
659  BU_CK_LIST_HEAD(vhead);
660  RT_CK_DB_INTERNAL(ip);
661  mb = (struct rt_metaball_internal *)ip->idb_ptr;
662  RT_METABALL_CK_MAGIC(mb);
663  rad = rt_metaball_get_bounding_sphere(&bsc, mb->threshold, mb);
664  /* cope with the case where 0 points are defined. */
665  if (rad<0)
666  return 0;
667 #if PLOT_THE_BIG_BOUNDING_SPHERE
668  rt_metaball_plot_sph(vhead, &bsc, rad);
669 #endif
670  for (BU_LIST_FOR(mbpt, wdb_metaballpt, &mb->metaball_ctrl_head))
671  rt_metaball_plot_sph(vhead, &mbpt->coord, mbpt->fldstr / mb->threshold);
672  return 0;
673 }
674 
675 /**
676  * Import an metaball/sphere from the database format to the internal
677  * structure. Apply modeling transformations as well.
678  */
679 int
680 rt_metaball_import5(struct rt_db_internal *ip, const struct bu_external *ep, register const fastf_t *mat, const struct db_i *dbip)
681 {
682  struct wdb_metaballpt *mbpt;
683  struct rt_metaball_internal *mb;
684  int metaball_count = 0, i;
685 
686  /* must be double for import and export */
687  double *buf;
688 
689  if (dbip) RT_CK_DBI(dbip);
690 
691  BU_CK_EXTERNAL(ep);
692  metaball_count = ntohl(*(uint32_t *)ep->ext_buf);
693  buf = (double *)bu_malloc((metaball_count*5+1)*SIZEOF_NETWORK_DOUBLE, "rt_metaball_import5: buf");
694  bu_cv_ntohd((unsigned char *)buf, (unsigned char *)ep->ext_buf+2*SIZEOF_NETWORK_LONG, metaball_count*5+1);
695 
696  RT_CK_DB_INTERNAL(ip);
697  ip->idb_major_type = DB5_MAJORTYPE_BRLCAD;
698  ip->idb_type = ID_METABALL;
699  ip->idb_meth = &OBJ[ID_METABALL];
700  BU_ALLOC(ip->idb_ptr, struct rt_metaball_internal);
701 
702  mb = (struct rt_metaball_internal *)ip->idb_ptr;
703  mb->magic = RT_METABALL_INTERNAL_MAGIC;
704  mb->method = ntohl(*(uint32_t *)(ep->ext_buf + SIZEOF_NETWORK_LONG));
705  mb->threshold = buf[0];
706 
707  BU_LIST_INIT(&mb->metaball_ctrl_head);
708  if (mat == NULL) mat = bn_mat_identity;
709  for (i = 1; i <= metaball_count * 5; i += 5) {
710  /* Apply modeling transformations */
711  BU_GET(mbpt, struct wdb_metaballpt);
712  mbpt->l.magic = WDB_METABALLPT_MAGIC;
713  MAT4X3PNT(mbpt->coord, mat, &buf[i]);
714  mbpt->fldstr = buf[i+3] / mat[15];
715  mbpt->sweat = buf[i+4];
716  BU_LIST_INSERT(&mb->metaball_ctrl_head, &mbpt->l);
717  }
718 
719  bu_free((void *)buf, "rt_metaball_import5: buf");
720  return 0; /* OK */
721 }
722 
723 
724 /**
725  * storage is something like
726  * long numpoints
727  * long method
728  * fastf_t threshold
729  * fastf_t X1 (start point)
730  * fastf_t Y1
731  * fastf_t Z1
732  * fastf_t fldstr1
733  * fastf_t sweat1 (end point)
734  * fastf_t X2 (start point)
735  * ...
736  */
737 int
738 rt_metaball_export5(struct bu_external *ep, const struct rt_db_internal *ip, double local2mm, const struct db_i *dbip)
739 {
740  struct rt_metaball_internal *mb;
741  struct wdb_metaballpt *mbpt;
742  int metaball_count = 0, i = 1;
743  /* must be double for import and export */
744  double *buf = NULL;
745 
746  if (dbip) RT_CK_DBI(dbip);
747 
748  RT_CK_DB_INTERNAL(ip);
749  if (ip->idb_type != ID_METABALL)
750  return -1;
751  mb = (struct rt_metaball_internal *)ip->idb_ptr;
752  RT_METABALL_CK_MAGIC(mb);
753  if (mb->metaball_ctrl_head.magic == 0) return -1;
754 
755  /* Count number of points */
756  for (BU_LIST_FOR(mbpt, wdb_metaballpt, &mb->metaball_ctrl_head)) metaball_count++;
757 
758  BU_CK_EXTERNAL(ep);
759  ep->ext_nbytes = SIZEOF_NETWORK_DOUBLE*(1+5*metaball_count) + 2*SIZEOF_NETWORK_LONG;
760  ep->ext_buf = (uint8_t *)bu_malloc(ep->ext_nbytes, "metaball external");
761  if (ep->ext_buf == NULL)
762  bu_bomb("Failed to allocate DB space!\n");
763  *(uint32_t *)ep->ext_buf = htonl(metaball_count);
764  *(uint32_t *)(ep->ext_buf + SIZEOF_NETWORK_LONG) = htonl(mb->method);
765 
766  /* pack the point data */
767  buf = (double *)bu_malloc((metaball_count*5+1)*SIZEOF_NETWORK_DOUBLE, "rt_metaball_export5: buf");
768  buf[0] = mb->threshold;
769  for (BU_LIST_FOR(mbpt, wdb_metaballpt, &mb->metaball_ctrl_head), i+=5) {
770  VSCALE(&buf[i], mbpt->coord, local2mm);
771  buf[i+3] = mbpt->fldstr * local2mm;
772  buf[i+4] = mbpt->sweat;
773  }
774  bu_cv_htond((unsigned char *)ep->ext_buf + 2*SIZEOF_NETWORK_LONG, (unsigned char *)buf, 1 + 5 * metaball_count);
775  bu_free(buf, "rt_metaball_export5: buf");
776  return 0;
777 }
778 
779 
780 /**
781  * Make human-readable formatted presentation of this solid. First
782  * line describes type of solid. Additional lines are indented one
783  * tab, and give parameter values.
784  */
785 int
786 rt_metaball_describe(struct bu_vls *str, const struct rt_db_internal *ip, int verbose, double UNUSED(mm2local))
787 {
788  int metaball_count = 0;
789  char buf[BUFSIZ];
790  struct rt_metaball_internal *mb;
791  struct wdb_metaballpt *mbpt;
792 
793  RT_CK_DB_INTERNAL(ip);
794  mb = (struct rt_metaball_internal *)ip->idb_ptr;
795  RT_METABALL_CK_MAGIC(mb);
796  for (BU_LIST_FOR(mbpt, wdb_metaballpt, &mb->metaball_ctrl_head)) metaball_count++;
797 
798  snprintf(buf, BUFSIZ, "Metaball with %d points and a threshold of %g (%s rendering)\n", metaball_count, mb->threshold, rt_metaball_lookup_type_name(mb->method));
799  bu_vls_strcat(str, buf);
800 
801  if (!verbose)
802  return 0;
803 
804  metaball_count = 0;
805  for (BU_LIST_FOR(mbpt, wdb_metaballpt, &mb->metaball_ctrl_head)) {
806  switch (mb->method) {
807  case METABALL_ISOPOTENTIAL:
808  snprintf(buf, BUFSIZ, "\t%d: %g field strength at (%g, %g, %g)\n",
809  ++metaball_count, mbpt->fldstr, V3ARGS(mbpt->coord));
810  break;
811  case METABALL_METABALL:
812  case METABALL_BLOB:
813  snprintf(buf, BUFSIZ, "\t%d: %g field strength at (%g, %g, %g) and blobbiness factor of %g\n",
814  ++metaball_count, mbpt->fldstr, V3ARGS(mbpt->coord), mbpt->sweat);
815  break;
816  default:
817  bu_bomb("Bad metaball method"); /* asplode */
818  }
819  bu_vls_strcat(str, buf);
820  }
821  return 0;
822 }
823 
824 
825 /**
826  * Free the storage associated with the rt_db_internal version of this
827  * solid. This only effects the in-memory copy.
828  */
829 void
831 {
832  register struct rt_metaball_internal *metaball;
833  register struct wdb_metaballpt *mbpt;
834 
835  RT_CK_DB_INTERNAL(ip);
836 
837  metaball = (struct rt_metaball_internal*)ip->idb_ptr;
838  RT_METABALL_CK_MAGIC(metaball);
839 
840  if (metaball->metaball_ctrl_head.magic != 0)
841  while (BU_LIST_WHILE(mbpt, wdb_metaballpt, &metaball->metaball_ctrl_head)) {
842  BU_LIST_DEQUEUE(&(mbpt->l));
843  BU_PUT(mbpt, struct wdb_metaballpt);
844  }
845  bu_free(ip->idb_ptr, "metaball ifree");
846  ip->idb_ptr = ((void *)0);
847 }
848 
849 
850 /**
851  * Add a single point to an existing metaball.
852  */
853 int
854 rt_metaball_add_point (struct rt_metaball_internal *mb, const point_t *loc, const fastf_t fldstr, const fastf_t goo)
855 {
856  struct wdb_metaballpt *mbpt;
857 
858  BU_GET(mbpt, struct wdb_metaballpt);
859  mbpt->l.magic = WDB_METABALLPT_MAGIC;
860  VMOVE(mbpt->coord, *loc);
861  mbpt->fldstr = fldstr;
862  mbpt->sweat = goo;
863  BU_LIST_INSERT(&mb->metaball_ctrl_head, &mbpt->l);
864 
865  return 0;
866 }
867 
868 
869 int
870 rt_metaball_params(struct pc_pc_set *UNUSED(ps), const struct rt_db_internal *ip)
871 {
872  if (ip) RT_CK_DB_INTERNAL(ip);
873 
874  return 0; /* OK */
875 }
876 
877 /**
878  * db get/g2asc
879  */
880 int
881 rt_metaball_get(struct bu_vls *logstr, const struct rt_db_internal *intern, const char *UNUSED(attr))
882 {
883  struct rt_metaball_internal *mb = (struct rt_metaball_internal *)intern->idb_ptr;
884  struct wdb_metaballpt *mbpt = NULL;
885  int first = 1;
886 
887  RT_METABALL_CK_MAGIC(mb);
888 
889  /* write crap in */
890  bu_vls_printf(logstr, "metaball %d %.25G {", mb->method, mb->threshold);
891  for (BU_LIST_FOR(mbpt, wdb_metaballpt, &mb->metaball_ctrl_head))
892  if (first) {
893  first = 0;
894  bu_vls_printf(logstr, "{%.25G %.25G %.25G %.25G %.25G}",
895  V3ARGS(mbpt->coord), mbpt->fldstr, mbpt->sweat);
896  } else
897  bu_vls_printf(logstr, " {%.25G %.25G %.25G %.25G %.25G}",
898  V3ARGS(mbpt->coord), mbpt->fldstr, mbpt->sweat);
899  bu_vls_printf(logstr, "}");
900 
901  return 0;
902 }
903 
904 /**
905  * used for db put/asc2g
906  */
907 int
908 rt_metaball_adjust(struct bu_vls *logstr, struct rt_db_internal *intern, int argc, const char **argv)
909 {
910  struct rt_metaball_internal *mb;
911  const char *pts;
912  const char *pend;
913  double thresh;
914 
915  if (argc != 3) {
916  bu_vls_printf(logstr, "Invalid number of arguments: %d\n", argc);
917  return BRLCAD_ERROR;
918  }
919 
920  RT_CK_DB_INTERNAL(intern);
921  mb = (struct rt_metaball_internal *)intern->idb_ptr;
922  RT_METABALL_CK_MAGIC(mb);
923 
924  if ( strlen(*argv) != 1 || (**argv < '0' || **argv > '2') ) {
925  bu_vls_printf(logstr, "Invalid method type, must be one of 0, 1, or 2.");
926  return BRLCAD_ERROR;
927  }
928  mb->method = *argv[0] - '0';
929  sscanf(argv[1], "%lG", &thresh);
930  mb->threshold = thresh;
931  BU_LIST_INIT(&mb->metaball_ctrl_head);
932 
933  pts = argv[2];
934  pend = pts + strlen(pts);
935 
936  while (1) {
937  int len;
938  double xyz[3];
939  double fldstr, goo;
940  point_t loc;
941  const point_t *locp = (const point_t *)&loc;
942 
943  while ( pts < pend && *pts != '{' ) ++pts;
944  if (pts >= pend) break;
945  len = sscanf(pts, "{%lG %lG %lG %lG %lG}", &xyz[0], &xyz[1], &xyz[2], &fldstr, &goo);
946  VMOVE(loc, xyz);
947 
948  if (len == EOF) break;
949  if (len != 5) {
950  bu_vls_printf(logstr, "Failed to parse point information: \"%s\"", pts);
951  return BRLCAD_ERROR;
952  }
953  pts++;
954  if (rt_metaball_add_point (mb, locp, fldstr, goo)) {
955  bu_vls_printf(logstr, "Failure adding point: {%f %f %f %f %f}", V3ARGS(loc), fldstr, goo);
956  return BRLCAD_ERROR;
957  }
958  }
959 
960  return BRLCAD_OK;
961 }
962 
963 
964 /*
965  * Local Variables:
966  * mode: C
967  * tab-width: 8
968  * indent-tabs-mode: t
969  * c-file-style: "stroustrup"
970  * End:
971  * ex: shiftwidth=4 tabstop=8
972  */
const char * rt_metaball_lookup_type_name(const int id)
Definition: metaball.c:90
#define BU_LIST_FOR(p, structure, hp)
Definition: list.h:365
Definition: raytrace.h:800
void bu_log(const char *,...) _BU_ATTR_PRINTF12
Definition: log.c:176
#define BU_LIST_INSERT(old, new)
Definition: list.h:183
#define SIZEOF_NETWORK_DOUBLE
Definition: cv.h:48
Definition: list.h:118
#define RT_CK_APPLICATION(_p)
Definition: raytrace.h:1675
#define RT_CK_RTI(_p)
Definition: raytrace.h:1833
const mat_t bn_mat_identity
Matrix and vector functionality.
Definition: mat.c:46
fastf_t uv_u
Range 0..1.
Definition: raytrace.h:341
int rt_metaball_get(struct bu_vls *logstr, const struct rt_db_internal *intern, const char *attr)
Definition: metaball.c:881
void rt_metaball_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp)
Definition: metaball.c:549
Definition: clone.c:90
void bu_vls_strcat(struct bu_vls *vp, const char *s)
Definition: vls.c:368
#define VSET(a, b, c, d)
Definition: color.c:53
#define VSETALL(a, s)
Definition: color.c:54
Definition: raytrace.h:215
#define SIZEOF_NETWORK_LONG
Definition: cv.h:46
Definition: pc.h:108
Definition: raytrace.h:368
fastf_t rt_metaball_point_value_metaball(const point_t *p, const struct bu_list *points)
Definition: metaball.c:268
int rt_metaball_point_inside(const point_t *p, const struct rt_metaball_internal *mb)
Definition: metaball.c:334
Definition: raytrace.h:248
fastf_t st_aradius
Radius of APPROXIMATING sphere.
Definition: raytrace.h:433
Header file for the BRL-CAD common definitions.
#define RT_CK_RAY(_p)
Definition: raytrace.h:224
void rt_metaball_uv(struct application *ap, struct soltab *stp, struct hit *hitp, struct uvcoord *uvp)
Definition: metaball.c:569
struct resource * a_resource
dynamic memory resources
Definition: raytrace.h:1591
#define WDB_METABALLPT_MAGIC
Definition: magic.h:213
void bu_cv_htond(unsigned char *out, const unsigned char *in, size_t count)
int a_onehit
flag to stop on first hit
Definition: raytrace.h:1586
#define SQ(a)
Definition: metaball.c:67
struct bu_list l
Definition: raytrace.h:369
void * bu_malloc(size_t siz, const char *str)
Definition: malloc.c:314
#define ID_METABALL
Metaball.
Definition: raytrace.h:508
if(share_geom)
Definition: nmg_mod.c:3829
int idb_major_type
Definition: raytrace.h:192
int bu_strncmp(const char *string1, const char *string2, size_t n)
Definition: str.c:191
Definition: color.c:49
int rt_metaball_adjust(struct bu_vls *logstr, struct rt_db_internal *intern, int argc, const char **argv)
Definition: metaball.c:908
int rt_metaball_shot(struct soltab *stp, register struct xray *rp, struct application *ap, struct seg *seghead)
Definition: metaball.c:365
#define RT_ADD_VLIST(hd, pnt, draw)
Definition: raytrace.h:1865
#define RT_CK_DB_INTERNAL(_p)
Definition: raytrace.h:207
#define BU_ALLOC(_ptr, _type)
Definition: malloc.h:223
fastf_t st_bradius
Radius of BOUNDING sphere.
Definition: raytrace.h:434
#define RT_CK_HIT(_p)
Definition: raytrace.h:259
#define BN_VLIST_LINE_MOVE
Definition: vlist.h:82
fastf_t a_diverge
slope of beam divergence/mm
Definition: raytrace.h:1600
const struct rt_functab * idb_meth
for ft_ifree(), etc.
Definition: raytrace.h:194
fastf_t uv_dv
delta in v
Definition: raytrace.h:344
#define V3ARGS(a)
Definition: color.c:56
#define BRLCAD_OK
Definition: defines.h:71
uint8_t * ext_buf
Definition: parse.h:216
static void top()
void rt_metaball_norm_internal(vect_t *n, point_t *p, struct rt_metaball_internal *mb)
Definition: metaball.c:503
#define BU_GET(_ptr, _type)
Definition: malloc.h:201
point_t hit_point
DEPRECATED: Intersection point, use VJOIN1 hit_dist.
Definition: raytrace.h:251
point_t st_max
max X, Y, Z of bounding RPP
Definition: raytrace.h:438
int rt_metaball_describe(struct bu_vls *str, const struct rt_db_internal *ip, int verbose, double mm2local)
Definition: metaball.c:786
#define BN_VLIST_LINE_DRAW
Definition: vlist.h:83
fastf_t r_max
exit dist from bounding sphere
Definition: raytrace.h:221
void rt_metaball_free(register struct soltab *stp)
Definition: metaball.c:606
#define UNUSED(parameter)
Definition: common.h:239
#define BU_PUT(_ptr, _type)
Definition: malloc.h:215
void rt_metaball_print(register const struct soltab *stp)
Definition: metaball.c:237
goto out
Definition: nmg_mod.c:3846
const char * metaballnames[]
Definition: metaball.c:71
Support for uniform tolerances.
Definition: tol.h:71
#define BN_CK_TOL(_p)
Definition: tol.h:82
int rt_metaball_find_intersection(point_t *intersect, const struct rt_metaball_internal *mb, const point_t *a, const point_t *b, fastf_t step, const fastf_t finalstep)
Definition: metaball.c:345
double bn_atan2(double x, double y)
Definition: mat.c:104
#define BU_LIST_WHILE(p, structure, hp)
Definition: list.h:410
int rt_metaball_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
Definition: metaball.c:187
fastf_t rt_metaball_get_bounding_sphere(point_t *center, fastf_t threshold, struct rt_metaball_internal *mb)
Definition: metaball.c:100
vect_t r_dir
Direction of ray (UNIT Length)
Definition: raytrace.h:219
int rt_metaball_lookup_type_id(const char *name)
Definition: metaball.c:80
struct bn_tol rti_tol
Math tolerances for this model.
Definition: raytrace.h:1765
int rt_metaball_class(const struct soltab *stp, const fastf_t *min, const fastf_t *max, const struct bn_tol *tol)
Definition: metaball.c:615
#define RT_CK_DBI(_p)
Definition: raytrace.h:829
#define RT_GET_SEG(p, res)
Definition: raytrace.h:379
fastf_t r_min
entry dist to bounding sphere
Definition: raytrace.h:220
void rt_metaball_plot_sph(struct bu_list *vhead, point_t *center, fastf_t radius)
Definition: metaball.c:627
#define ZERO(val)
Definition: units.c:38
#define BU_LIST_INIT(_hp)
Definition: list.h:148
void * idb_ptr
Definition: raytrace.h:195
point_t r_pt
Point at which ray starts.
Definition: raytrace.h:218
point_t st_min
min X, Y, Z of bounding RPP
Definition: raytrace.h:437
void bu_cv_ntohd(unsigned char *out, const unsigned char *in, size_t count)
const struct rt_functab OBJ[]
Definition: table.c:159
int rt_metaball_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *tol)
Definition: metaball.c:165
int rt_metaball_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct rt_tess_tol *ttol, const struct bn_tol *tol, const struct rt_view_info *info)
Definition: metaball.c:652
#define RT_CK_SOLTAB(_p)
Definition: raytrace.h:453
fastf_t rt_metaball_point_value_blob(const point_t *p, const struct bu_list *points)
Definition: metaball.c:295
void rt_metaball_norm(register struct hit *hitp, struct soltab *stp, register struct xray *rp)
Definition: metaball.c:537
void bu_vls_printf(struct bu_vls *vls, const char *fmt,...) _BU_ATTR_PRINTF23
Definition: vls.c:694
void * st_specific
-> ID-specific (private) struct
Definition: raytrace.h:435
fastf_t uv_du
delta in u
Definition: raytrace.h:343
int rt_metaball_params(struct pc_pc_set *ps, const struct rt_db_internal *ip)
Definition: metaball.c:870
fastf_t rt_metaball_point_value(const point_t *p, const struct rt_metaball_internal *mb)
Definition: metaball.c:315
int rt_metaball_export5(struct bu_external *ep, const struct rt_db_internal *ip, double local2mm, const struct db_i *dbip)
Definition: metaball.c:738
Definition: color.c:51
void bu_free(void *ptr, const char *str)
Definition: malloc.c:328
vect_t hit_normal
DEPRECATED: Surface Normal at hit_point, use RT_HIT_NORMAL.
Definition: raytrace.h:252
fastf_t a_rbeam
initial beam radius (mm)
Definition: raytrace.h:1599
#define BU_CK_LIST_HEAD(_p)
Definition: list.h:142
#define BU_CK_EXTERNAL(_p)
Definition: parse.h:224
void rt_metaball_ifree(struct rt_db_internal *ip)
Definition: metaball.c:830
void rt_ell_16pts(fastf_t *ov, fastf_t *V, fastf_t *A, fastf_t *B)
Definition: ell.c:606
#define BU_LIST_DEQUEUE(cur)
Definition: list.h:209
size_t ext_nbytes
Definition: parse.h:210
fastf_t hit_dist
dist from r_pt to hit_point
Definition: raytrace.h:250
HIDDEN void verbose(struct human_data_t *dude)
Definition: human.c:2008
Definition: vls.h:56
#define BRLCAD_ERROR
Definition: defines.h:72
void bu_bomb(const char *str) _BU_ATTR_NORETURN
Definition: bomb.c:91
HIDDEN const point_t delta
Definition: sh_prj.c:618
double fastf_t
Definition: defines.h:300
fastf_t rt_metaball_point_value_iso(const point_t *p, const struct bu_list *points)
Definition: metaball.c:280
#define RT_METABALL_INTERNAL_MAGIC
Definition: magic.h:100
void rt_metaballpt_print(const struct wdb_metaballpt *metaball, double mm2local)
Definition: metaball.c:255
Definition: color.c:50
int rt_metaball_add_point(struct rt_metaball_internal *mb, const point_t *loc, const fastf_t fldstr, const fastf_t goo)
Definition: metaball.c:854
fastf_t uv_v
Range 0..1.
Definition: raytrace.h:342
int rt_metaball_import5(struct rt_db_internal *ip, const struct bu_external *ep, register const fastf_t *mat, const struct db_i *dbip)
Definition: metaball.c:680
point_t st_center
Centroid of solid.
Definition: raytrace.h:432