/* scene.c
Copyright (C) 2005,2006,2007 Eugene K. Ressler, Jr.
This file is part of Sketch, a small, simple system for making
3d drawings with LaTeX and the PSTricks or TikZ package.
Sketch is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
Sketch is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Sketch; see the file COPYING.txt. If not, see
http://www.gnu.org/copyleft */
#include <stdio.h>
#include <math.h>
#include "scene.h"
#include "emit.h"
DECLARE_DYNAMIC_2D_ARRAY_FUNCS (POINT_LIST_3D, POINT_3D, FLOAT,
point_list_3d, v, n_pts, NO_OTHER_INIT)
DECLARE_DYNAMIC_2D_ARRAY_FUNCS (TRANSFORM_LIST, TRANSFORM, FLOAT,
transform_list, xf, n_xfs, NO_OTHER_INIT)
// this must match the definition of OBJECT_TYPE
char *object_type_str[] = {
"base",
"tag",
"option list",
"scalar",
"point",
"vector",
"transform",
"dots",
"line",
"curve",
"polygon",
"special",
"sweep",
"repeat",
"compound",
};
#define LAY_IN 0
#define LAY_OVER 1
#define LAY_UNDER -1
int lay_val (OPTS * opts, int lay_default)
{
char *val = opt_val (opts, "lay");
if (!val)
return lay_default;
if (strcmp (val, "over") == 0)
return LAY_OVER;
else if (strcmp (val, "under") == 0)
return LAY_UNDER;
else if (strcmp (val, "in") == 0)
return LAY_IN;
else
{
warn (no_line, "lay=%s has been ignored", val);
return lay_default;
}
}
OBJECT *
new_tag_def (void)
{
TAG_DEF *r = safe_malloc (sizeof *r);
r->tag = O_TAG_DEF;
r->sibling = NULL;
return (OBJECT *) r;
}
OBJECT *
new_opts_def (char *opts_str, SRC_LINE line)
{
OPTS_DEF *r = safe_malloc (sizeof *r);
r->tag = O_OPTS_DEF;
r->sibling = NULL;
r->opts = new_opts (opts_str, line);
return (OBJECT *) r;
}
OBJECT *
new_scalar_def (FLOAT val)
{
SCALAR_DEF *r = safe_malloc (sizeof *r);
r->tag = O_SCALAR_DEF;
r->sibling = NULL;
r->val = val;
return (OBJECT *) r;
}
OBJECT *
new_point_def (POINT_3D p)
{
POINT_DEF *r = safe_malloc (sizeof *r);
r->tag = O_POINT_DEF;
r->sibling = NULL;
copy_pt_3d (r->p, p);
return (OBJECT *) r;
}
OBJECT *
new_vector_def (VECTOR_3D v)
{
VECTOR_DEF *r = safe_malloc (sizeof *r);
r->tag = O_VECTOR_DEF;
r->sibling = NULL;
copy_vec_3d (r->v, v);
return (OBJECT *) r;
}
OBJECT *
new_transform_def (TRANSFORM xf)
{
TRANSFORM_DEF *r = safe_malloc (sizeof *r);
r->tag = O_TRANSFORM_DEF;
r->sibling = NULL;
copy_transform (r->xf, xf);
return (OBJECT *) r;
}
void
translate_points (POINT_LIST_3D * dst, OBJECT * src_obj)
{
POINT_DEF *sibling, *src = (POINT_DEF *) src_obj;
while (src)
{
copy_pt_3d (pushed_point_list_3d_v (dst), src->p);
sibling = (POINT_DEF *) src->sibling;
safe_free (src);
src = sibling;
}
}
DOTS_OBJECT *
raw_dots (OPTS * opts)
{
DOTS_OBJECT *r = safe_malloc (sizeof *r);
r->tag = O_DOTS;
r->sibling = NULL;
r->opts = opts;
init_point_list_3d (r->pts);
return r;
}
OBJECT *
new_dots (OPTS * opts, OBJECT * pts)
{
DOTS_OBJECT *r = raw_dots (opts);
translate_points (r->pts, pts);
return (OBJECT *) r;
}
OBJECT *
copy_dots (OBJECT * obj)
{
DOTS_OBJECT *org = (DOTS_OBJECT *) obj, *r = raw_dots (org->opts);
copy_point_list_3d (r->pts, org->pts);
return (OBJECT *) r;
}
LINE_OBJECT *
raw_line (OPTS * opts)
{
LINE_OBJECT *r = safe_malloc (sizeof *r);
r->tag = O_LINE;
r->sibling = NULL;
r->opts = opts;
init_point_list_3d (r->pts);
return r;
}
OBJECT *
new_line (OPTS * opts, OBJECT * pts)
{
LINE_OBJECT *r = raw_line (opts);
translate_points (r->pts, pts);
return (OBJECT *) r;
}
OBJECT *
copy_line (OBJECT * obj)
{
LINE_OBJECT *org = (LINE_OBJECT *) obj, *r = raw_line (org->opts);
copy_point_list_3d (r->pts, org->pts);
return (OBJECT *) r;
}
CURVE_OBJECT *
raw_curve (OPTS * opts)
{
CURVE_OBJECT *r = safe_malloc (sizeof *r);
r->tag = O_CURVE;
r->sibling = NULL;
r->opts = opts;
init_point_list_3d (r->pts);
return r;
}
OBJECT *
new_curve (OPTS * opts, OBJECT * pts)
{
CURVE_OBJECT *r = raw_curve (opts);
translate_points (r->pts, pts);
return (OBJECT *) r;
}
OBJECT *
copy_curve (OBJECT * obj)
{
CURVE_OBJECT *org = (CURVE_OBJECT *) obj, *r = raw_curve (org->opts);
copy_point_list_3d (r->pts, org->pts);
return (OBJECT *) r;
}
POLYGON_OBJECT *
raw_polygon (OPTS * opts)
{
POLYGON_OBJECT *r = safe_malloc (sizeof *r);
r->tag = O_POLYGON;
r->sibling = NULL;
r->opts = opts;
init_point_list_3d (r->pts);
r->border_p = 0;
return r;
}
OBJECT *
new_polygon (OPTS * opts, OBJECT * pts)
{
POLYGON_OBJECT *r = raw_polygon (opts);
translate_points (r->pts, pts);
return (OBJECT *) r;
}
OBJECT *
copy_polygon (OBJECT * obj)
{
POLYGON_OBJECT *org = (POLYGON_OBJECT *) obj, *r = raw_polygon (org->opts);
copy_point_list_3d (r->pts, org->pts);
return (OBJECT *) r;
}
static SPECIAL_OBJECT *
raw_special (OPTS * opts)
{
SPECIAL_OBJECT *r = safe_malloc (sizeof *r);
r->tag = O_SPECIAL;
r->sibling = NULL;
r->code = NULL;
r->opts = opts;
init_point_list_3d (r->pts);
return r;
}
OBJECT *
new_special (char *code, OPTS * opts, OBJECT * pts, SRC_LINE line)
{
SPECIAL_OBJECT *r = raw_special (opts);
r->code = code;
translate_points (r->pts, pts);
// syntax check
process_special (NULL, r, line);
return (OBJECT *) r;
}
OBJECT *
copy_special (OBJECT * obj)
{
SPECIAL_OBJECT *org = (SPECIAL_OBJECT *) obj, *r = raw_special (org->opts);
copy_point_list_3d (r->pts, org->pts);
r->code = safe_strdup (org->code);
return (OBJECT *) r;
}
SWEEP_OBJECT *
raw_sweep (OPTS * opts)
{
SWEEP_OBJECT *r = safe_malloc (sizeof *r);
r->tag = O_SWEEP;
r->sibling = NULL;
r->n_slices = 0;
r->closed_p = 0;
init_transform_list (r->xforms);
r->opts = opts;
r->swept = NULL;
return r;
}
void
translate_transforms (TRANSFORM_LIST * dst, OBJECT * src_obj)
{
TRANSFORM_DEF *sibling, *src = (TRANSFORM_DEF *) src_obj;
while (src)
{
copy_transform (pushed_transform_list_xf (dst), src->xf);
sibling = (TRANSFORM_DEF *) src->sibling;
safe_free (src);
src = sibling;
}
}
OBJECT *
new_sweep (OPTS * opts, int n_slices, int closed_p, OBJECT * xfs,
OBJECT * swept)
{
SWEEP_OBJECT *r = raw_sweep (opts);
r->n_slices = n_slices;
r->closed_p = closed_p;
translate_transforms (r->xforms, xfs);
r->swept = swept;
return (OBJECT *) r;
}
// this is a shallow copy
OBJECT *
copy_sweep (OBJECT * obj)
{
SWEEP_OBJECT *org = (SWEEP_OBJECT *) obj, *r = raw_sweep (org->opts);
r->n_slices = org->n_slices;
r->closed_p = org->closed_p;
copy_transform_list (r->xforms, org->xforms);
r->swept = org->swept;
return (OBJECT *) r;
}
REPEAT_OBJECT *
raw_repeat (void)
{
REPEAT_OBJECT *r = safe_malloc (sizeof *r);
r->tag = O_REPEAT;
r->sibling = NULL;
r->n = 0;
init_transform_list (r->xforms);
r->repeated = NULL;
return r;
}
OBJECT *
new_repeat (int n, OBJECT * xfs, OBJECT * repeated)
{
REPEAT_OBJECT *r = raw_repeat ();
r->n = n;
translate_transforms (r->xforms, xfs);
r->repeated = repeated;
return (OBJECT *) r;
}
OBJECT *
copy_repeat (OBJECT * obj)
{
REPEAT_OBJECT *org = (REPEAT_OBJECT *) obj, *r = raw_repeat ();
r->n = org->n;
copy_transform_list (r->xforms, org->xforms);
r->repeated = org->repeated; // shallow copy
return (OBJECT *) r;
}
OBJECT *
new_compound (TRANSFORM xform, OBJECT * child)
{
COMPOUND_OBJECT *r = safe_malloc (sizeof *r);
r->tag = O_COMPOUND;
r->sibling = NULL;
copy_transform (r->xform, xform);
r->child = child;
return (OBJECT *) r;
}
// this is a shallow copy
OBJECT *
copy_compound (OBJECT * obj)
{
COMPOUND_OBJECT *org = (COMPOUND_OBJECT *) obj;
return new_compound (org->xform, org->child);
}
typedef OBJECT *(*COPY_FUNC) (OBJECT *);
static COPY_FUNC copy_tbl[] = {
NULL, // O_BASE
NULL, // O_TAG_DEF
NULL, // O_OPTS_DEF
NULL, // O_SCALAR_DEF
NULL, // O_POINT_DEF
NULL, // O_VECTOR_DEF
NULL, // O_TRANSFORM_DEF
copy_dots,
copy_line,
copy_curve,
copy_polygon,
copy_special,
copy_sweep,
copy_repeat,
copy_compound,
};
OBJECT *
copy_drawable (OBJECT * obj)
{
OBJECT *r = NULL;
while (obj)
{
if (copy_tbl[obj->tag])
{
OBJECT *copy = (*copy_tbl[obj->tag]) (obj);
copy->sibling = r;
r = copy;
}
else
{
die (no_line, "copy_drawable: attempt to copy non-drawable %s",
object_type_str[obj->tag]);
}
obj = obj->sibling;
}
return sibling_reverse (r);
}
OBJECT *
sibling_reverse (OBJECT * obj)
{
OBJECT *p, *q, *t;
// pop from p and push onto q until p is empty
p = obj;
q = NULL;
while (p)
{
t = p;
p = p->sibling; // pop
t->sibling = q;
q = t; // push
}
return q;
}
OBJECT *
object_from_expr (EXPR_VAL * val)
{
switch (val->tag)
{
case E_FLOAT:
return new_scalar_def (val->val.flt);
case E_POINT:
return new_point_def (val->val.pt);
case E_VECTOR:
return new_vector_def (val->val.vec);
case E_TRANSFORM:
return new_transform_def (val->val.xf);
default:
die (no_line, "object_from_expr: unknown value tag %d", val->tag);
}
return NULL; // never occurs
}
void
transform_points (POINT_LIST_3D * dst_pts, TRANSFORM xf,
POINT_LIST_3D * src_pts)
{
int i;
setup_point_list_3d (dst_pts, src_pts->n_pts);
for (i = 0; i < src_pts->n_pts; i++)
transform_pt_3d (dst_pts->v[i], xf, src_pts->v[i]);
dst_pts->n_pts = src_pts->n_pts;
}
static void
fill_transform_accum (TRANSFORM_LIST * accum, TRANSFORM_LIST * inc)
{
int i;
setup_transform_list (accum, inc->n_xfs);
accum->n_xfs = inc->n_xfs;
for (i = 0; i < inc->n_xfs; i++)
set_ident (accum->xf[i]);
}
static void
advance_transform_accum (TRANSFORM_LIST * accum, TRANSFORM_LIST * inc)
{
int i;
for (i = 0; i < accum->n_xfs; i++)
compose (accum->xf[i], accum->xf[i], inc->xf[i]);
}
static void
compose_transform_accum (TRANSFORM xf, TRANSFORM_LIST * accum,
TRANSFORM model_view_xf)
{
int i;
if (accum->n_xfs <= 0)
die (no_line, "zero size accumulator");
copy_transform (xf, accum->xf[0]);
// left-multiply because accumulator is in "then" order
for (i = 1; i < accum->n_xfs; i++)
compose (xf, accum->xf[i], xf);
if (model_view_xf)
compose (xf, model_view_xf, xf);
}
OBJECT *
flat_dots (OBJECT * obj, TRANSFORM xf)
{
DOTS_OBJECT *s = (DOTS_OBJECT *) obj, *dots = raw_dots (s->opts);
transform_points (dots->pts, xf, s->pts);
return (OBJECT *) dots;
}
OBJECT *
flat_line (OBJECT * obj, TRANSFORM xf)
{
LINE_OBJECT *s = (LINE_OBJECT *) obj, *line = raw_line (s->opts);
check_opts (s->opts, OPT_INTERNAL | OPT_LINE,
"unknown line option %s=%s will be ignored",
global_env->output_language, no_line);
transform_points (line->pts, xf, s->pts);
return (OBJECT *) line;
}
OBJECT *
flat_curve (OBJECT * obj, TRANSFORM xf)
{
CURVE_OBJECT *s = (CURVE_OBJECT *) obj, *curve = raw_curve (s->opts);
transform_points (curve->pts, xf, s->pts);
return (OBJECT *) curve;
}
OBJECT *
flat_polygon (OBJECT * obj, TRANSFORM xf)
{
POLYGON_OBJECT *s = (POLYGON_OBJECT *) obj,
*polygon = raw_polygon (s->opts);
check_opts (s->opts, OPT_INTERNAL | OPT_POLYGON | OPT_LINE,
"unknown polygon option %s=%s will be ignored",
global_env->output_language, no_line);
transform_points (polygon->pts, xf, s->pts);
return (OBJECT *) polygon;
}
OBJECT *
flat_special (OBJECT * obj, TRANSFORM xf)
{
SPECIAL_OBJECT *s = (SPECIAL_OBJECT *) obj,
*special = raw_special (s->opts);
special->code = safe_strdup (s->code);
transform_points (special->pts, xf, s->pts);
return (OBJECT *) special;
}
#define MAX_WARP 1e-5
// return -1 if no split is necessary
// return 0 if best spilt is on the 0--2 line
// return 1 if best split is on the 1--3 line
static int
best_triangle_split (POINT_3D v0, POINT_3D v1, POINT_3D v2, POINT_3D v3)
{
VECTOR_3D n, d0, d1, e, e_max;
FLOAT e_len_sqr, e_max_len_sqr, warp;
sub_vecs_3d (d0, v2, v0);
sub_vecs_3d (d1, v3, v1);
cross (n, d0, d1);
// if the cross product is zero length, the polygon is degenerate and can
// be considered flat; no need to traingulate
if (!find_unit_vec_3d (n, n))
return -1;
// find the edge of maximum length; probably not necessary
sub_vecs_3d (e_max, v1, v0);
e_max_len_sqr = dot_3d (e_max, e_max);
sub_vecs_3d (e, v2, v1);
e_len_sqr = dot_3d (e, e);
if (e_len_sqr > e_max_len_sqr)
{
e_max_len_sqr = e_len_sqr;
copy_vec_3d (e_max, e);
}
sub_vecs_3d (e, v3, v2);
e_len_sqr = dot_3d (e, e);
if (e_len_sqr > e_max_len_sqr)
{
e_max_len_sqr = e_len_sqr;
copy_vec_3d (e_max, e);
}
sub_vecs_3d (e, v0, v3);
e_len_sqr = dot_3d (e, e);
if (e_len_sqr > e_max_len_sqr)
{
e_max_len_sqr = e_len_sqr;
copy_vec_3d (e_max, e);
}
// flat if projection of edge on normal is small, else split on shortest diagonal
warp = dot_3d (e_max, n);
return
-MAX_WARP <= warp && warp <= MAX_WARP ? -1 :
dot_3d (d0, d0) < dot_3d (d1, d1) ? 0 : 1;
}
// add triangular or quadrilateral faces to object list depending on flatness
static void
make_faces (OBJECT ** r, OPTS * opts, TRANSFORM xf,
POINT_3D v0, POINT_3D v1, POINT_3D v2, POINT_3D v3, int split_p)
{
POLYGON_OBJECT *new_polygon;
if (!split_p)
goto no_split;
switch (best_triangle_split (v0, v1, v2, v3))
{
case -1:
no_split:
new_polygon = raw_polygon (opts);
setup_point_list_3d (new_polygon->pts, 4);
transform_pt_3d (new_polygon->pts->v[0], xf, v0);
transform_pt_3d (new_polygon->pts->v[1], xf, v1);
transform_pt_3d (new_polygon->pts->v[2], xf, v2);
transform_pt_3d (new_polygon->pts->v[3], xf, v3);
new_polygon->pts->n_pts = 4;
new_polygon->sibling = *r;
*r = (OBJECT *) new_polygon;
break;
case 0:
new_polygon = raw_polygon (opts);
setup_point_list_3d (new_polygon->pts, 3);
transform_pt_3d (new_polygon->pts->v[0], xf, v0);
transform_pt_3d (new_polygon->pts->v[1], xf, v1);
transform_pt_3d (new_polygon->pts->v[2], xf, v2);
new_polygon->pts->n_pts = 3;
new_polygon->sibling = *r;
*r = (OBJECT *) new_polygon;
new_polygon = raw_polygon (opts);
setup_point_list_3d (new_polygon->pts, 3);
transform_pt_3d (new_polygon->pts->v[0], xf, v2);
transform_pt_3d (new_polygon->pts->v[1], xf, v3);
transform_pt_3d (new_polygon->pts->v[2], xf, v0);
new_polygon->pts->n_pts = 3;
new_polygon->sibling = *r;
*r = (OBJECT *) new_polygon;
break;
case 1:
new_polygon = raw_polygon (opts);
setup_point_list_3d (new_polygon->pts, 3);
transform_pt_3d (new_polygon->pts->v[0], xf, v1);
transform_pt_3d (new_polygon->pts->v[1], xf, v2);
transform_pt_3d (new_polygon->pts->v[2], xf, v3);
new_polygon->pts->n_pts = 3;
new_polygon->sibling = *r;
*r = (OBJECT *) new_polygon;
new_polygon = raw_polygon (opts);
setup_point_list_3d (new_polygon->pts, 3);
transform_pt_3d (new_polygon->pts->v[0], xf, v3);
transform_pt_3d (new_polygon->pts->v[1], xf, v0);
transform_pt_3d (new_polygon->pts->v[2], xf, v1);
new_polygon->pts->n_pts = 3;
new_polygon->sibling = *r;
*r = (OBJECT *) new_polygon;
break;
}
}
OBJECT *
flat_sweep (OBJECT * obj, TRANSFORM xf)
{
int i, j, jj, split_p;
POINT_LIST_3D *a, *b, *t;
OBJECT *swept, *r;
TRANSFORM sweep_xf;
POINT_LIST_3D pts_1[1], pts_2[1];
TRANSFORM_LIST sweep_accum[1];
SWEEP_OBJECT *s = (SWEEP_OBJECT *) obj;
init_point_list_3d (pts_1);
init_point_list_3d (pts_2);
init_transform_list (sweep_accum);
split_p = bool_opt_p (s->opts, "split", 1)
&& bool_opt_p (global_env->opts, "split", 1);
r = NULL;
#define ADD_TO_OUTPUT(O) do { \
(O)->sibling = r; \
r = (OBJECT*)(O); \
} while (0)
// handle definitions first; a point becomes a single line or a polygon
if (s->swept->tag == O_POINT_DEF)
{
fill_transform_accum (sweep_accum, s->xforms);
for (swept = s->swept; swept; swept = swept->sibling)
{
POINT_DEF *pd = (POINT_DEF *) swept;
if (s->closed_p)
{
POLYGON_OBJECT *polygon = raw_polygon (s->opts);
for (i = 0; i < s->n_slices; i++)
{
compose_transform_accum (sweep_xf, sweep_accum, xf);
transform_pt_3d (pushed_point_list_3d_v (polygon->pts),
sweep_xf, pd->p);
advance_transform_accum (sweep_accum, s->xforms);
}
ADD_TO_OUTPUT (polygon);
}
else
{
LINE_OBJECT *line = raw_line (s->opts);
for (i = 0; i < s->n_slices + 1; i++)
{
compose_transform_accum (sweep_xf, sweep_accum, xf);
transform_pt_3d (pushed_point_list_3d_v (line->pts),
sweep_xf, pd->p);
advance_transform_accum (sweep_accum, s->xforms);
}
ADD_TO_OUTPUT (line);
}
}
}
else
{
// it's drawable; recursively flatten swept object in its own coordinates
for (swept = flat_scene (s->swept, NULL); swept; swept = swept->sibling)
{
// refill with identity for each swept object
fill_transform_accum (sweep_accum, s->xforms);
// now the different flavors of sweep depend on what's being swept and
// the setting of the closure tag
if (swept->tag == O_LINE)
{
// a line becomes a surface represented by a sequence of 4-sided polygons
LINE_OBJECT *line = (LINE_OBJECT *) swept;
// a is the trail buffer and b the lead
a = pts_1;
b = pts_2;
copy_point_list_3d (a, line->pts);
if (s->closed_p)
{
POLYGON_OBJECT *e1 = raw_polygon (s->opts);
POLYGON_OBJECT *e2 = raw_polygon (s->opts);
OPTS *face_opts = line->opts ? line->opts : s->opts;
// set up in advance; e1 is filled in forward, e2 in reverse
setup_point_list_3d (e1->pts, s->n_slices);
e1->pts->n_pts = s->n_slices;
setup_point_list_3d (e2->pts, s->n_slices);
e2->pts->n_pts = s->n_slices;
for (i = 0; i < s->n_slices - 1; i++)
{
advance_transform_accum (sweep_accum, s->xforms);
compose_transform_accum (sweep_xf, sweep_accum, 0); // apply mv transform in make_faces
transform_points (b, sweep_xf, line->pts);
// copy first and last points for 'end caps'
transform_pt_3d (e1->pts->v[i], xf, b->v[b->n_pts - 1]);
transform_pt_3d (e2->pts->v[s->n_slices - 1 - i],
xf, b->v[0]);
for (jj = 0, j = 1; j < a->n_pts; jj = j++)
make_faces (&r, face_opts, xf, b->v[jj],
b->v[j], a->v[j], a->v[jj], split_p);
t = a;
a = b;
b = t; // swap a and b for next pass
}
// closure: add last point of original line. first to ends, then as faces
transform_pt_3d (e1->pts->v[i], xf,
line->pts->v[line->pts->n_pts - 1]);
transform_pt_3d (e2->pts->v[0], xf, line->pts->v[0]);
for (jj = 0, j = 1; j < a->n_pts; jj = j++)
make_faces (&r, face_opts, xf, line->pts->v[jj],
line->pts->v[j], a->v[j], a->v[jj], split_p);
// add ends to output
ADD_TO_OUTPUT (e1);
ADD_TO_OUTPUT (e2);
}
else
{
for (i = 0; i < s->n_slices; i++)
{
advance_transform_accum (sweep_accum, s->xforms);
compose_transform_accum (sweep_xf, sweep_accum, 0);
transform_points (b, sweep_xf, line->pts);
for (jj = 0, j = 1; j < a->n_pts; jj = j++)
make_faces (&r, s->opts, xf, b->v[jj], b->v[j],
a->v[j], a->v[jj], split_p);
t = a;
a = b;
b = t; // swap a and b for next pass
}
}
}
else if (swept->tag == O_POLYGON)
{
// a polygon becomes a surface represented by a sequence of 4-sided polygons (with "end caps")
POLYGON_OBJECT *new_polygon, *polygon =
(POLYGON_OBJECT *) swept;
OPTS *end_opts = polygon->opts ? polygon->opts : s->opts;
if (s->closed_p)
warn (no_line,
"closure tag on polygon sweep ignored (sorry, no line number)");
a = pts_1;
b = pts_2;
copy_point_list_3d (a, polygon->pts);
// initial end cap
new_polygon = raw_polygon (end_opts);
transform_points (new_polygon->pts, xf, a);
ADD_TO_OUTPUT (new_polygon);
for (i = 0; i < s->n_slices; i++)
{
advance_transform_accum (sweep_accum, s->xforms);
compose_transform_accum (sweep_xf, sweep_accum, 0);
transform_points (b, sweep_xf, polygon->pts);
for (jj = a->n_pts - 1, j = 0; j < a->n_pts; jj = j++)
make_faces (&r, s->opts, xf, b->v[jj], b->v[j],
a->v[j], a->v[jj], split_p);
t = a;
a = b;
b = t; // swap a and b for next pass
}
// final end cap is copy of a (note reverse point order)
new_polygon = raw_polygon (end_opts);
reverse_copy_point_list_3d (new_polygon->pts, a);
transform_points (new_polygon->pts, xf, new_polygon->pts);
ADD_TO_OUTPUT (new_polygon);
}
else
{
warn (no_line,
"cannot sweep a %s; object ignored (sorry, no line number)",
object_type_str[swept->tag]);
}
}
}
clear_point_list_3d (pts_1);
clear_point_list_3d (pts_2);
clear_transform_list (sweep_accum);
return r;
#undef ADD_TO_OUTPUT
}
// forward declaration
static OBJECT *rev_transformed_flat_scene (OBJECT * obj, TRANSFORM xf);
OBJECT *
flat_repeat (OBJECT * obj, TRANSFORM xf)
{
int i;
REPEAT_OBJECT *s = (REPEAT_OBJECT *) obj;
OBJECT *flat_repeated, *r;
TRANSFORM_LIST repeat_accum[1];
TRANSFORM child_xf;
init_transform_list (repeat_accum);
if (s->n <= 0)
return NULL;
// recursively flatten repeated object in its own coordinates
flat_repeated = flat_scene (s->repeated, NULL);
fill_transform_accum (repeat_accum, s->xforms);
r = NULL;
for (i = 0; i < s->n; i++)
{
compose_transform_accum (child_xf, repeat_accum, xf);
r = cat_objects (rev_transformed_flat_scene
(flat_repeated, child_xf), r);
advance_transform_accum (repeat_accum, s->xforms);
}
// flat_repeated is a memory leak
return r;
}
OBJECT *
flat_compound (OBJECT * obj, TRANSFORM xf)
{
COMPOUND_OBJECT *compound = (COMPOUND_OBJECT *) obj;
TRANSFORM child_xf;
compose (child_xf, xf, compound->xform);
return rev_transformed_flat_scene (compound->child, child_xf);
}
typedef OBJECT *(*FLATTEN_FUNC) (OBJECT *, TRANSFORM);
static FLATTEN_FUNC flatten_tbl[] = {
NULL, // O_BASE
NULL, // O_TAG_DEF
NULL, // O_OPTS_DEF
NULL, // O_SCALAR_DEF
NULL, // O_POINT_DEF
NULL, // O_VECTOR_DEF
NULL, // O_TRANSFORM_DEF
flat_dots,
flat_line,
flat_curve,
flat_polygon,
flat_special,
flat_sweep,
flat_repeat,
flat_compound,
};
OBJECT *
cat_objects (OBJECT * lft, OBJECT * rgt)
{
OBJECT *p;
if (!lft)
return rgt;
for (p = lft; p->sibling; p = p->sibling)
/* skip */ ;
p->sibling = rgt;
return lft;
}
static OBJECT *
rev_transformed_flat_scene (OBJECT * obj, TRANSFORM xf)
{
OBJECT *r = NULL;
while (obj)
{
// flatten the object
if (flatten_tbl[obj->tag] == NULL)
die (no_line, "rev_transformed_flat_scene: bad tag %d", obj->tag);
// join scene sibling lists
r = cat_objects ((*flatten_tbl[obj->tag]) (obj, xf), r);
// on to next object
obj = obj->sibling;
}
return r;
}
// call with null env omits camera transformation
OBJECT *
flat_scene (OBJECT * obj, GLOBAL_ENV * env)
{
FLOAT *camera = env
&& global_env_is_set_p (env, GE_CAMERA) ? env->camera : identity;
return sibling_reverse (rev_transformed_flat_scene (obj, camera));
}
// ---- overlay/underlay/depth sort flag --------------------------------------
static int
dots_lay_val (OBJECT * obj)
{
return lay_val (((DOTS_OBJECT *) obj)->opts, LAY_IN);
}
static int
line_lay_val (OBJECT * obj)
{
return lay_val (((LINE_OBJECT *) obj)->opts, LAY_IN);
}
static int
curve_lay_val (OBJECT * obj)
{
return lay_val (((CURVE_OBJECT *) obj)->opts, LAY_IN);
}
static int
polygon_lay_val (OBJECT * obj)
{
return lay_val (((POLYGON_OBJECT *) obj)->opts, LAY_IN);
}
static int
special_lay_val (OBJECT * obj)
{
return lay_val (((SPECIAL_OBJECT *) obj)->opts, LAY_OVER);
}
typedef int (*LAY_VAL_FUNC) (OBJECT *);
static LAY_VAL_FUNC lay_val_tbl[] = {
NULL, // O_BASE
NULL, // O_TAG_DEF
NULL, // O_OPTS_DEF
NULL, // O_SCALAR_DEF
NULL, // O_POINT_DEF
NULL, // O_VECTOR_DEF
NULL, // O_TRANSFORM_DEF
dots_lay_val,
line_lay_val,
curve_lay_val,
polygon_lay_val,
special_lay_val,
NULL, // O_SWEEP (flattened)
NULL, // O_REPEAT (flattened)
NULL, // O_COMPOUND (flattened)
};
int
object_lay_val (OBJECT * obj)
{
if (!lay_val_tbl[obj->tag])
die (no_line, "bad tag in object_lay_val");
return (*lay_val_tbl[obj->tag]) (obj);
}
// ---- binary space partition ------------------------------------------------
static void
add_dots_object_to_bsp_pass_1 (OBJECT * obj, BSP_TREE * bsp)
{
}
static void
add_line_object_to_bsp_pass_1 (OBJECT * obj, BSP_TREE * bsp)
{
}
static void
add_curve_object_to_bsp_pass_1 (OBJECT * obj, BSP_TREE * bsp)
{
}
static void
add_polygon_object_to_bsp_pass_1 (OBJECT * obj, BSP_TREE * bsp)
{
int i;
POLYGON_3D *polygon;
POLYGON_OBJECT *polygon_obj = (POLYGON_OBJECT *) obj;
PLANE plane[1];
// copy point list to new polygon
polygon = new_polygon_3d (polygon_obj->pts->n_pts);
polygon->n_sides = polygon_obj->pts->n_pts;
for (i = 0; i < polygon->n_sides; i++)
copy_pt_3d (polygon->v[i], polygon_obj->pts->v[i]);
find_polygon_plane (plane, polygon);
// backface elimination
// put the new polygon in the tree
if (plane->n[Z] >= -FLOAT_EPS ||
!bool_opt_p (polygon_obj->opts, "cull", 1) ||
!bool_opt_p (global_env->opts, "cull", 1))
{
add_polygon_to_bsp (bsp, polygon, obj);
}
else
{
delete_polygon_3d (polygon);
}
}
static void
add_special_object_to_bsp_pass_1 (OBJECT * obj, BSP_TREE * bsp)
{
}
typedef void (*BSP_INSERT_FUNC) (OBJECT *, BSP_TREE *);
static BSP_INSERT_FUNC insert_in_bsp_pass_1_tbl[] = {
NULL, // O_BASE
NULL, // O_TAG_DEF
NULL, // O_OPTS_DEF
NULL, // O_SCALAR_DEF
NULL, // O_POINT_DEF
NULL, // O_VECTOR_DEF
NULL, // O_TRANSFORM_DEF
add_dots_object_to_bsp_pass_1,
add_line_object_to_bsp_pass_1,
add_curve_object_to_bsp_pass_1,
add_polygon_object_to_bsp_pass_1,
add_special_object_to_bsp_pass_1,
NULL, // O_SWEEP (flattened)
NULL, // O_REPEAT (flattened)
NULL, // O_COMPOUND (flattened)
};
static void
add_dots_object_to_bsp_pass_2 (OBJECT * obj, BSP_TREE * bsp)
{
int i;
DOTS_OBJECT *dots_obj = (DOTS_OBJECT *) obj;
// insert each dot as a polyline with only one vertex
for (i = 0; i < dots_obj->pts->n_pts; i++)
{
POLYLINE_3D *dot = new_polyline_3d (1);
dot->n_vertices = 1;
copy_pt_3d (dot->v[0], dots_obj->pts->v[i]);
add_polyline_to_bsp (bsp, dot, obj);
}
}
static void
add_line_object_to_bsp_pass_2 (OBJECT * obj, BSP_TREE * bsp)
{
int i;
POLYLINE_3D *polyline;
LINE_OBJECT *line_obj = (LINE_OBJECT *) obj;
// copy point list to new polyline
polyline = new_polyline_3d (line_obj->pts->n_pts);
polyline->n_vertices = line_obj->pts->n_pts;
for (i = 0; i < line_obj->pts->n_pts; i++)
copy_pt_3d (polyline->v[i], line_obj->pts->v[i]);
// fprintf(stderr, "adding to bsp [%p(%d)]\n", line_obj->opts, line_obj->opts->list->n_elts); // DEBUG
// put the new polyline in the tree
add_polyline_to_bsp (bsp, polyline, obj);
}
static void
add_curve_object_to_bsp_pass_2 (OBJECT * obj, BSP_TREE * bsp)
{
}
static void
add_polygon_object_to_bsp_pass_2 (OBJECT * obj, BSP_TREE * bsp)
{
}
static void
add_special_object_to_bsp_pass_2 (OBJECT * obj, BSP_TREE * bsp)
{
SPECIAL_OBJECT *special_obj = (SPECIAL_OBJECT *) obj;
POLYLINE_3D *special = new_polyline_3d (1);
special->n_vertices = 1;
copy_pt_3d (special->v[0], special_obj->pts->v[0]);
add_polyline_to_bsp (bsp, special, obj);
}
static BSP_INSERT_FUNC insert_in_bsp_pass_2_tbl[] = {
NULL, // O_BASE
NULL, // O_TAG_DEF
NULL, // O_OPTS_DEF
NULL, // O_SCALAR_DEF
NULL, // O_POINT_DEF
NULL, // O_VECTOR_DEF
NULL, // O_TRANSFORM_DEF
add_dots_object_to_bsp_pass_2,
add_line_object_to_bsp_pass_2,
add_curve_object_to_bsp_pass_2,
add_polygon_object_to_bsp_pass_2,
add_special_object_to_bsp_pass_2,
NULL, // O_SWEEP (flattened)
NULL, // O_REPEAT (flattened)
NULL, // O_COMPOUND (flattened)
};
// table functions are called in
// OBJECT *hsr_scene_with_bsp(OBJECT *scene);
static void
get_dots_from_polyline (OBJECT * src, OBJECT ** output,
BSP_POLYLINE_NODE * polyline_node)
{
DOTS_OBJECT *dots_src = (DOTS_OBJECT *) src;
DOTS_OBJECT *new_obj = raw_dots (dots_src->opts);
copy_point_list_3d (new_obj->pts,
(POINT_LIST_3D *) polyline_node->polyline);
new_obj->sibling = *output;
*output = (OBJECT *) new_obj;
}
static void
get_line_from_polyline (OBJECT * src, OBJECT ** output,
BSP_POLYLINE_NODE * polyline_node)
{
LINE_OBJECT *line_src = (LINE_OBJECT *) src;
LINE_OBJECT *new_obj = raw_line (copy_line_opts (line_src->opts,
polyline_node->first_p,
polyline_node->last_p,
global_env->
output_language));
copy_point_list_3d (new_obj->pts,
(POINT_LIST_3D *) polyline_node->polyline);
new_obj->sibling = *output;
*output = (OBJECT *) new_obj;
}
static void
get_curve_from_polyline (OBJECT * src, OBJECT ** output,
BSP_POLYLINE_NODE * polyline_node)
{
}
static void
get_polygon_border_from_polyline (OBJECT * src,
OBJECT ** output,
BSP_POLYLINE_NODE * polyline_node)
{
// no longer used
POLYGON_OBJECT *polygon_src = (POLYGON_OBJECT *) src;
LINE_OBJECT *new_obj = raw_line (copy_opts (polygon_src->opts, OPT_LINE,
global_env->output_language));
copy_point_list_3d (new_obj->pts,
(POINT_LIST_3D *) polyline_node->polyline);
new_obj->sibling = *output;
*output = (OBJECT *) new_obj;
}
static void
get_special_from_polyline (OBJECT * src, OBJECT ** output,
BSP_POLYLINE_NODE * polyline_node)
{
SPECIAL_OBJECT *special_src = (SPECIAL_OBJECT *) src;
SPECIAL_OBJECT *new_obj =
raw_special (copy_opts (special_src->opts, OPT_INTERNAL,
global_env->output_language));
copy_point_list_3d (new_obj->pts, special_src->pts); // go back to original special since we didn't split
new_obj->code = safe_strdup (special_src->code);
new_obj->sibling = *output;
*output = (OBJECT *) new_obj;
}
typedef void (*GET_OBJ_FROM_POLYLINE_FUNC) (OBJECT * src, OBJECT ** output,
BSP_POLYLINE_NODE *
polyline_node);
static GET_OBJ_FROM_POLYLINE_FUNC get_obj_from_polyline_tbl[] = {
NULL, // O_BASE
NULL, // O_TAG_DEF
NULL, // O_OPTS_DEF
NULL, // O_SCALAR_DEF
NULL, // O_POINT_DEF
NULL, // O_VECTOR_DEF
NULL, // O_TRANSFORM_DEF
get_dots_from_polyline,
get_line_from_polyline,
get_curve_from_polyline,
get_polygon_border_from_polyline,
get_special_from_polyline,
NULL, // O_SWEEP (flattened)
NULL, // O_REPEAT (flattened)
NULL, // O_COMPOUND (flattened)
};
void
get_objects_from_bsp_node (BSP_NODE * bsp, void *env)
{
int i, j, k, broken_border_p;
OBJECT **output = (OBJECT **) env;
if (bsp == NULL)
return;
if (bsp->tag == BSP_POLYGON)
{
OPTS *opts;
LINE_OBJECT *new_line_obj = NULL;
BSP_POLYGON_NODE *polygon_node = (BSP_POLYGON_NODE *) bsp;
POLYGON_OBJECT *src = bsp->attr, *new_polygon_obj;
broken_border_p = 0;
for (i = 0; i < polygon_node->polygon->n_sides; i++)
{
if (!polygon_node->polygon_attr->elt[i].border_p)
{
broken_border_p = 1;
break;
}
}
if (broken_border_p)
{
// add these options if user didn't specify them
opts =
copy_opts (src->opts, OPT_POLYGON, global_env->output_language);
add_no_edges_default_opt (&opts, global_env->output_language);
add_solid_white_default_opt (&opts, global_env->output_language);
new_polygon_obj = raw_polygon (opts);
copy_point_list_3d (new_polygon_obj->pts,
(POINT_LIST_3D *) polygon_node->polygon);
new_polygon_obj->sibling = *output;
*output = (OBJECT *) new_polygon_obj;
// create the border from edges that did not result from splitting
//
// find a break in the border if there is one
for (j = polygon_node->polygon->n_sides - 1, i = 0;
i < polygon_node->polygon->n_sides; j = i++)
{
if (!polygon_node->polygon_attr->elt[j].border_p)
break;
}
if (i == polygon_node->polygon->n_sides)
i = 0;
// j->i is now a border edge, which is what we want
for (k = 0;
k < polygon_node->polygon->n_sides;
j = i, i = (i + 1) % polygon_node->polygon->n_sides, k++)
{
if (polygon_node->polygon_attr->elt[j].border_p)
{
if (new_line_obj == NULL)
{
opts =
copy_opts (src->opts, OPT_LINE,
global_env->output_language);
new_line_obj = raw_line (opts);
copy_pt_3d (pushed_point_list_3d_v
(new_line_obj->pts),
polygon_node->polygon->v[j]);
}
copy_pt_3d (pushed_point_list_3d_v (new_line_obj->pts),
polygon_node->polygon->v[i]);
}
else if (new_line_obj)
{
new_line_obj->sibling = *output;
*output = (OBJECT *) new_line_obj;
new_line_obj = NULL;
}
}
if (new_line_obj)
{
new_line_obj->sibling = *output;
*output = (OBJECT *) new_line_obj;
new_line_obj = NULL;
}
}
else
{
opts =
copy_opts (src->opts, OPT_POLYGON | OPT_LINE,
global_env->output_language);
add_solid_white_default_opt (&opts, global_env->output_language);
new_polygon_obj = raw_polygon (opts);
new_polygon_obj->border_p = 1;
copy_point_list_3d (new_polygon_obj->pts,
(POINT_LIST_3D *) polygon_node->polygon);
new_polygon_obj->sibling = *output;
*output = (OBJECT *) new_polygon_obj;
}
}
else
{ // BSP_POLYLINE
OBJECT *src = bsp->attr;
(*get_obj_from_polyline_tbl[src->tag]) (src, output,
(BSP_POLYLINE_NODE *) bsp);
}
}
OBJECT *
hsr_scene_with_bsp (OBJECT * scene)
{
OBJECT *p, *t, *underlay, *overlay, *sorted;
BSP_TREE bsp;
// two passes are needed to serve the bsp requirement
// that polylines be inserted after all polygons are
// already there
// also take care of underlays and ovelays in the first pass
bsp = NULL;
underlay = overlay = sorted = NULL;
for (p = scene; p; p = p->sibling)
{
switch (object_lay_val (p))
{
case LAY_UNDER:
t = copy_drawable (p);
t->sibling = underlay;
underlay = t;
break;
case LAY_IN:
(*insert_in_bsp_pass_1_tbl[p->tag]) (p, &bsp);
break;
case LAY_OVER:
t = copy_drawable (p);
t->sibling = overlay;
overlay = t;
break;
default:
die (no_line, "bad lay value in hsr_scene_with_bsp");
break;
}
}
for (p = scene; p; p = p->sibling)
if (object_lay_val (p) == LAY_IN)
(*insert_in_bsp_pass_2_tbl[p->tag]) (p, &bsp);
traverse_bsp (bsp, get_objects_from_bsp_node, &sorted);
sorted = sibling_reverse (sorted);
sorted = cat_objects (underlay, sorted);
sorted = cat_objects (sorted, overlay);
return sorted;
}
// ---- depth sort ------------------------------------------------------------
static void
add_dots_object_to_sort (OBJECT * obj, BSP_TREE * bsp)
{
int i;
DOTS_OBJECT *dots_obj = (DOTS_OBJECT *) obj;
POLYLINE_3D dot[1];
init_polyline_3d (dot);
setup_polyline_3d (dot, 1);
dot->n_vertices = 1;
// insert each dot as a polyline with only one vertex
for (i = 0; i < dots_obj->pts->n_pts; i++)
{
copy_pt_3d (dot->v[0], dots_obj->pts->v[i]);
add_polyline_to_sort (bsp, dot, obj);
}
clear_polyline_3d (dot);
}
static void
add_line_object_to_sort (OBJECT * obj, BSP_TREE * bsp)
{
LINE_OBJECT *line_obj = (LINE_OBJECT *) obj;
// DANGER: assumes point list in polyline object is congruent to a geometry.h polyline
add_polyline_to_sort (bsp, (POLYLINE_3D *) line_obj->pts, obj);
}
static void
add_curve_object_to_sort (OBJECT * obj, BSP_TREE * bsp)
{
}
static void
add_polygon_object_to_sort (OBJECT * obj, BSP_TREE * bsp)
{
POLYGON_OBJECT *polygon_obj = (POLYGON_OBJECT *) obj;
PLANE plane[1];
// backface elimination
// put the new polygon in the tree
find_polygon_plane (plane, (POLYGON_3D *) polygon_obj->pts);
if (plane->n[Z] >= -FLOAT_EPS ||
!bool_opt_p (polygon_obj->opts, "cull", 1) ||
!bool_opt_p (global_env->opts, "cull", 1))
add_polygon_to_sort (bsp, (POLYGON_3D *) polygon_obj->pts, obj);
}
static void
add_special_object_to_sort (OBJECT * obj, BSP_TREE * bsp)
{
SPECIAL_OBJECT *special_obj = (SPECIAL_OBJECT *) obj;
POLYLINE_3D *special = new_polyline_3d (1);
special->n_vertices = 1;
copy_pt_3d (special->v[0], special_obj->pts->v[0]);
add_polyline_to_sort (bsp, special, obj);
}
typedef void (*ADD_TO_DS_FUNC) (OBJECT *, BSP_TREE *);
static ADD_TO_DS_FUNC add_to_sort_tbl[] = {
NULL, // O_BASE
NULL, // O_TAG_DEF
NULL, // O_OPTS_DEF
NULL, // O_SCALAR_DEF
NULL, // O_POINT_DEF
NULL, // O_VECTOR_DEF
NULL, // O_TRANSFORM_DEF
add_dots_object_to_sort,
add_line_object_to_sort,
add_curve_object_to_sort,
add_polygon_object_to_sort,
add_special_object_to_sort,
NULL, // O_SWEEP (flattened)
NULL, // O_REPEAT (flattened)
NULL, // O_COMPOUND (flattened)
};
OBJECT *
hsr_scene_with_depth_sort (OBJECT * scene)
{
OBJECT *p, *t, *underlay, *overlay, *sorted;
BSP_TREE bsp;
// two passes are needed to serve the bsp requirement
// that polylines be inserted after all polygons are
// already there
// also take care of underlays and ovelays in the first pass
bsp = NULL;
underlay = overlay = sorted = NULL;
for (p = scene; p; p = p->sibling)
{
switch (object_lay_val (p))
{
case LAY_UNDER:
t = copy_drawable (p);
t->sibling = underlay;
underlay = t;
break;
case LAY_IN:
(*add_to_sort_tbl[p->tag]) (p, &bsp);
break;
case LAY_OVER:
t = copy_drawable (p);
t->sibling = overlay;
overlay = t;
break;
default:
die (no_line, "bad lay value in hsr_scene_with_bsp");
break;
}
}
sort_by_depth (&bsp);
traverse_depth_sort (bsp, get_objects_from_bsp_node, &sorted);
sorted = sibling_reverse (sorted);
sorted = cat_objects (underlay, sorted);
sorted = cat_objects (sorted, overlay);
return sorted;
}
// ---- extent finding --------------------------------------------------------
static void
get_extent_of_points (POINT_LIST_3D * pts, BOX_3D * e)
{
int i;
for (i = 0; i < pts->n_pts; i++)
fold_min_max_pt_3d (e, pts->v[i]);
}
static void
get_extent_of_dots (OBJECT * obj, BOX_3D * e)
{
DOTS_OBJECT *dots = (DOTS_OBJECT *) obj;
get_extent_of_points (dots->pts, e);
}
static void
get_extent_of_line (OBJECT * obj, BOX_3D * e)
{
LINE_OBJECT *line = (LINE_OBJECT *) obj;
get_extent_of_points (line->pts, e);
}
static void
get_extent_of_curve (OBJECT * obj, BOX_3D * e)
{
CURVE_OBJECT *curve = (CURVE_OBJECT *) obj;
get_extent_of_points (curve->pts, e);
}
static void
get_extent_of_polygon (OBJECT * obj, BOX_3D * e)
{
POLYGON_OBJECT *polygon = (POLYGON_OBJECT *) obj;
get_extent_of_points (polygon->pts, e);
}
static void
get_extent_of_special (OBJECT * obj, BOX_3D * e)
{
SPECIAL_OBJECT *special = (SPECIAL_OBJECT *) obj;
fold_min_max_pt_3d (e, special->pts->v[0]);
}
typedef void (*EXTENT_FUNC) (OBJECT *, BOX_3D *);
static EXTENT_FUNC extent_tbl[] = {
NULL, // O_BASE
NULL, // O_TAG_DEF
NULL, // O_OPTS_DEF
NULL, // O_SCALAR_DEF
NULL, // O_POINT_DEF
NULL, // O_VECTOR_DEF
NULL, // O_TRANSFORM_DEF
get_extent_of_dots,
get_extent_of_line,
get_extent_of_curve,
get_extent_of_polygon,
get_extent_of_special,
NULL, // O_SWEEP (flattened)
NULL, // O_REPEAT (flattened)
NULL, // O_COMPOUND (flattened)
};
void
get_extent (OBJECT * obj, BOX_3D * e, int *n_obj)
{
if (obj)
{
int n = 0;
init_box_3d(e);
while (obj)
{
if (extent_tbl[obj->tag] == NULL)
die (no_line, "get_extent: bad tag %d", obj->tag);
(*extent_tbl[obj->tag]) (obj, e);
obj = obj->sibling;
++n;
}
*n_obj = n;
}
else
{
// reasonable empty box
e->min[X] = e->min[Y] = e->min[Z] = 0;
e->max[X] = e->max[Y] = e->max[Z] = 1;
*n_obj = 0;
}
}
int
xy_overlap_p (OBJECT * obj, BOX_3D * e)
{
BOX_3D e_obj[1];
init_box_3d(e_obj);
(*extent_tbl[obj->tag]) (obj, e_obj);
return
!(e_obj->max[X] < e->min[X] ||
e_obj->min[X] > e->max[X] ||
e_obj->max[Y] < e->min[Y] ||
e_obj->min[Y] > e->max[Y]);
}