Compare commits
1
Commits
annotrt
...
nonuniform
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b1f81178f |
@@ -4542,6 +4542,9 @@ The “press” command simulates the pressing of a button. All of these button
|
||||
* sedit – (deprecated) Enter primitive edit mode.
|
||||
* srot – Enter solid (i.e., primitive) rotate mode (must be in primitive edit mode).
|
||||
* sscale – Enter solid (i.e., primitive) scale mode (must be in primitive edit mode).
|
||||
* sxscale – Enter solid scale (x-direction only) mode (must be in primitive edit mode).
|
||||
* syscale – Enter solid scale (y-direction only) mode (must be in primitive edit mode).
|
||||
* szscale – Enter solid scale (z-direction only) mode (must be in primitive edit mode).
|
||||
* sxy – Enter solid (i.e., primitive) translate mode (must be in primitive edit mode).
|
||||
* zoomin – Zoom in, synonym for zoom 2.
|
||||
* zoomout – Zoom out, synonym for zoom 0.5.
|
||||
|
||||
@@ -46,6 +46,10 @@ generation capabilities. The classic behavior (and the behavior users
|
||||
will see if calling without specifying a subcommand) is a report
|
||||
summarizing geometric information about the specified solid(s). This
|
||||
behavior may also be explicitly invoked by the *summarize* subcommand.
|
||||
If a primitive advertises a metric but cannot calculate it, the summary
|
||||
reports *COULD NOT DETERMINE* for that value and the command returns an
|
||||
error. Other available metrics and summaries for subsequent objects are
|
||||
still reported.
|
||||
|
||||
The *inside* subcommand will take an object and the x y z coordinates
|
||||
of a point, and report whether the point is inside or outside the
|
||||
|
||||
@@ -98,7 +98,7 @@ Raytrace validation mode (*--raytrace*) cross-checks surface area and volume est
|
||||
* **Crofton SA/volume** – Cauchy-Crofton estimate obtained by firing random chord rays against the CSG raytracer.
|
||||
* **Analytic SA/volume** – Closed-form result from the primitive's function table (ft_surf_area / ft_volume), when available.
|
||||
+
|
||||
If analytic values are not available (e.g. BoT, BREP) the Crofton result is recorded informatively without a pass/fail verdict.
|
||||
If a primitive does not implement an analytic value, the Crofton result is recorded informatively without a pass/fail verdict. If an implemented analytic callback reports an error, the object fails with *raytrace_analytic_failed*; a calculation failure is not treated as an unavailable reference.
|
||||
|
||||
5. **Comb validation.** For each comb whose direct children all passed, two independent estimates are compared:
|
||||
* **CSG Crofton SA/volume** – ray-sampled against the original CSG boolean tree.
|
||||
@@ -115,7 +115,7 @@ Raytrace validation mode (*--raytrace*) cross-checks surface area and volume est
|
||||
Results are reported in three categories:
|
||||
|
||||
* *Passed* – Both estimates agree within *--raytrace-tol*.
|
||||
* *Failed* (raytrace_mismatch) – The two estimates disagree beyond *--raytrace-tol*. The output includes SA and volume error percentages and the reason for failure.
|
||||
* *Failed* – Either the two estimates disagree beyond *--raytrace-tol* (*raytrace_mismatch*) or an implemented analytic metric could not be calculated (*raytrace_analytic_failed*). Mismatch output includes SA and volume error percentages; analytic failures identify the failed query.
|
||||
* *Skipped* – The object could not be validated. Reasons include:
|
||||
- *contains_unbounded_primitive* – a halfspace is present in the subtree.
|
||||
- *child_validation_failed* – a child object failed or was skipped.
|
||||
@@ -132,6 +132,8 @@ Raytrace validation uses 2 000 Crofton rays per object by default. For a databa
|
||||
|
||||
A *raytrace_mismatch* on a **leaf primitive** suggests a bug in the raytrace shot routine for that primitive type (the Crofton ray count is sufficient for basic primitives at 10% tolerance).
|
||||
|
||||
A *raytrace_analytic_failed* result means the primitive supplies an analytic metric callback, but the callback rejected the specific object or encountered an internal error. Fix that failure before interpreting comparisons for the object or any parent combination.
|
||||
|
||||
A *raytrace_mismatch* on a **comb** when all children passed suggests either:
|
||||
* A coplanar-face artifact (see *COPLANAR INTERFACE PATTERNS* below).
|
||||
* A problem with boolean evaluation producing incorrect geometry.
|
||||
|
||||
@@ -48,6 +48,9 @@ Simulates the pressing of a button. All of these button actions can be run direc
|
||||
* sedit -- (deprecated) Enters primitive edit mode.
|
||||
* srot -- Enters solid (i.e., primitive) rotate mode (must be in primitive edit mode).
|
||||
* sscale -- Enters solid (i.e., primitive) scale mode (must be in primitive edit mode).
|
||||
* sxscale -- Enters solid scale (x-direction only) mode (must be in primitive edit mode).
|
||||
* syscale -- Enters solid scale (y-direction only) mode (must be in primitive edit mode).
|
||||
* szscale -- Enters solid scale (z-direction only) mode (must be in primitive edit mode).
|
||||
* sxy -- Enters solid (i.e., primitive) translate mode (must be in primitive edit mode).
|
||||
* zoomin -- Zooms in, synonym for zoom 2.
|
||||
* zoomout -- Zooms out, synonym for zoom 0.5.
|
||||
|
||||
+10
-10
@@ -166,15 +166,15 @@ struct db_full_path;
|
||||
// NOT the view plane.
|
||||
//
|
||||
// Note that the underlying solids do not always support directly expressing
|
||||
// the shapes that can be created with these opts using comb instances (for
|
||||
// example, a TOR scaled in the X direction) and the edit will be rejected in
|
||||
// those cases. Just because a comb instance of a solid can be stretched, that
|
||||
// does not mean the solid itself can support that shape. Consequently, it is
|
||||
// recommended that the _X, _Y and _Z versions of the scale operations be used
|
||||
// sparingly if at all. Even if a comb instance's tree is able to support
|
||||
// rendering such an operation at the time the original edit is made, a
|
||||
// subsequent editing of that tree could add new geometry and ultimately result
|
||||
// in an inability to apply the matrix successfully to all leaf solids.
|
||||
// the shapes that can be created with these opts using comb instances, and the
|
||||
// edit will be rejected in those cases. Just because a comb instance of a
|
||||
// solid can be stretched, that does not mean the solid itself can support that
|
||||
// shape. Consequently, it is recommended that the _X, _Y and _Z versions of
|
||||
// the scale operations be used sparingly if at all. Even if a comb instance's
|
||||
// tree is able to support rendering such an operation at the time the original
|
||||
// edit is made, a subsequent editing of that tree could add new geometry and
|
||||
// ultimately result in an inability to apply the matrix successfully to all
|
||||
// leaf solids.
|
||||
#define RT_MATRIX_EDIT_SCALE 10
|
||||
#define RT_MATRIX_EDIT_SCALE_X 11
|
||||
#define RT_MATRIX_EDIT_SCALE_Y 12
|
||||
@@ -247,7 +247,7 @@ struct rt_edit {
|
||||
//
|
||||
// Options:
|
||||
// RT_PARAMS_EDIT_TRANS – free translate
|
||||
// RT_PARAMS_EDIT_SCALE – uniform or axis scale
|
||||
// RT_PARAMS_EDIT_SCALE – uniform primitive-parameter scale
|
||||
// RT_PARAMS_EDIT_ROT – rotation
|
||||
// RT_PARAMS_EDIT_PICK – geometric pick (e.g. click to select a vertex)
|
||||
//
|
||||
|
||||
@@ -398,6 +398,9 @@ RT_EXPORT extern void rt_crofton_result_free(
|
||||
* superell, ETO/TOR spindle) should call this function directly with
|
||||
* appropriate params. Callers that already have a prepared rt_i should
|
||||
* call rt_crofton_shoot() instead.
|
||||
*
|
||||
* On failure, each requested output is set to -1, following the metric
|
||||
* functab convention.
|
||||
*/
|
||||
RT_EXPORT extern void rt_crofton_sample(fastf_t *area, fastf_t *vol,
|
||||
const struct rt_db_internal *ip,
|
||||
|
||||
@@ -230,6 +230,11 @@ struct rt_functab {
|
||||
const struct bn_tol *);
|
||||
#define RTFUNCTAB_FUNC_BBOX_CAST(_func) ((int (*)(struct rt_db_internal *, point_t *, point_t *, const struct bn_tol *))((void (*)(void))_func))
|
||||
|
||||
/**
|
||||
* Calculate primitive metrics. These callbacks predate status return
|
||||
* values; callers initialize each scalar, or every centroid coordinate,
|
||||
* to -1 and use NEAR_EQUAL or VNEAR_EQUAL to detect a failure.
|
||||
*/
|
||||
void (*ft_volume)(fastf_t * /*vol*/, const struct rt_db_internal * /*ip*/);
|
||||
#define RTFUNCTAB_FUNC_VOLUME_CAST(_func) ((void (*)(fastf_t *, const struct rt_db_internal *))((void (*)(void))_func))
|
||||
|
||||
|
||||
+9
-1
@@ -70,6 +70,14 @@ struct soltab {
|
||||
long st_bit; /**< @brief solids bit vector index (const) */
|
||||
struct bu_ptbl st_regions; /**< @brief ptrs to regions using this solid (const) */
|
||||
matp_t st_matp; /**< @brief solid coords to model space, NULL=identity */
|
||||
matp_t st_nu_matp; /**< @brief attr body coords to model space, NULL=identity */
|
||||
matp_t st_nu_inv_matp; /**< @brief model coords to attr body space, NULL=identity */
|
||||
matp_t st_nu_norm_matp;/**< @brief normal transform for attr body normals */
|
||||
point_t st_nu_body_center; /**< @brief body-space centroid before nonuniform bounds transform */
|
||||
point_t st_nu_body_min; /**< @brief body-space min before nonuniform bounds transform */
|
||||
point_t st_nu_body_max; /**< @brief body-space max before nonuniform bounds transform */
|
||||
fastf_t st_nu_body_aradius;/**< @brief body-space approximate radius before nonuniform bounds transform */
|
||||
fastf_t st_nu_body_bradius;/**< @brief body-space bounding radius before nonuniform bounds transform */
|
||||
struct db_full_path st_path; /**< @brief path from region to leaf */
|
||||
/* Experimental stuff for accelerating "pieces" of solids */
|
||||
long st_npieces; /**< @brief # pieces used by this solid */
|
||||
@@ -77,7 +85,7 @@ struct soltab {
|
||||
struct bound_rpp * st_piece_rpps; /**< @brief bounding RPP of each piece of this solid */
|
||||
};
|
||||
|
||||
#define RT_SOLTAB_INIT_ZERO {BU_LIST_INIT_ZERO, BU_LIST_INIT_ZERO, NULL, NULL, 0, 0, VINIT_ZERO, 0.0, 0.0, NULL, NULL, VINIT_ZERO, VINIT_ZERO, 0, BU_PTBL_INIT_ZERO, NULL, DB_FULL_PATH_INIT_ZERO, 0, 0, NULL}
|
||||
#define RT_SOLTAB_INIT_ZERO {BU_LIST_INIT_ZERO, BU_LIST_INIT_ZERO, NULL, NULL, 0, 0, VINIT_ZERO, 0.0, 0.0, NULL, NULL, VINIT_ZERO, VINIT_ZERO, 0, BU_PTBL_INIT_ZERO, NULL, NULL, NULL, NULL, VINIT_ZERO, VINIT_ZERO, VINIT_ZERO, 0.0, 0.0, DB_FULL_PATH_INIT_ZERO, 0, 0, NULL}
|
||||
|
||||
#define st_name st_dp->d_namep
|
||||
#define RT_SOLTAB_NULL ((struct soltab *)0)
|
||||
|
||||
@@ -29,6 +29,13 @@ if(TARGET libged)
|
||||
brlcad_regression_test(regress-ged_facetize "regress_facetize;ged-facetize;ged_exec" EXEC regress_facetize)
|
||||
distclean("${CMAKE_CURRENT_BINARY_DIR}/regress-ged_facetize.log")
|
||||
|
||||
brlcad_addexec(regress_nonuniform nonuniform.cpp "libged;libwdb;librt;libbu" TEST_USESDATA)
|
||||
if(TARGET regress_nonuniform)
|
||||
add_dependencies(regress_nonuniform ged_plugins)
|
||||
endif(TARGET regress_nonuniform)
|
||||
brlcad_regression_test(regress-ged_nonuniform "regress_nonuniform;ged-facetize;ged_exec" EXEC regress_nonuniform)
|
||||
distclean("${CMAKE_CURRENT_BINARY_DIR}/regress-ged_nonuniform.log")
|
||||
|
||||
# lint --raytrace regression: Crofton vs analytic / BoT validation pass.
|
||||
# Exercises the raytrace validation pipeline in lint/raytrace.cpp:
|
||||
brlcad_addexec(regress_lint_raytrace regress_lint_raytrace.cpp "libged;libwdb;librt;libbu" TEST_USESDATA)
|
||||
@@ -42,6 +49,7 @@ endif(TARGET libged)
|
||||
cmakefiles(
|
||||
CMakeLists.txt
|
||||
mater.c
|
||||
nonuniform.cpp
|
||||
regdef.c
|
||||
push.cpp
|
||||
regress_facetize.cpp
|
||||
@@ -55,6 +63,7 @@ cmakefiles(
|
||||
regress-ged_regdef.cmake.in
|
||||
regress-ged_push.cmake.in
|
||||
regress-ged_facetize.cmake.in
|
||||
regress-ged_nonuniform.cmake.in
|
||||
regress-ged_lint_raytrace.cmake.in
|
||||
rpush_tests.g
|
||||
xpush_tests.g
|
||||
|
||||
@@ -0,0 +1,897 @@
|
||||
/* N O N U N I F O R M . C P P
|
||||
* BRL-CAD
|
||||
*
|
||||
* Copyright (c) 2026 United States Government as represented by
|
||||
* the U.S. Army Research Laboratory.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* version 2.1 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this file; see the file named COPYING for more
|
||||
* information.
|
||||
*/
|
||||
/** @file nonuniform.cpp
|
||||
*
|
||||
* Integration coverage for v5 matrix:nonuniform preservation. For each
|
||||
* scalar-radius primitive, build an axis-aligned and an oblique baseline,
|
||||
* facetize it, then npush -x transformed CSG and BoT wrapper instances
|
||||
* through X, Y, Z, and stacked XYZ scale combinations.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "bu/app.h"
|
||||
#include "bu/env.h"
|
||||
#include "bu/file.h"
|
||||
#include "bu/malloc.h"
|
||||
#include "bu/str.h"
|
||||
#include "bu/vls.h"
|
||||
#include "vmath.h"
|
||||
#include "raytrace.h"
|
||||
#include "rt/geom.h"
|
||||
#include "rt/primitives/bot.h"
|
||||
#include "nmg.h"
|
||||
#include "wdb.h"
|
||||
#include "ged.h"
|
||||
|
||||
#define NONUNIFORM_ATTR "matrix:nonuniform"
|
||||
#define VOL_TOL_CSG 0.03
|
||||
#define VOL_TOL_BOT 0.03
|
||||
#define RATIO_TOL 0.05
|
||||
#define SA_TOL_CSG_TESS 0.18
|
||||
#define SA_TOL_BOT_TESS 0.08
|
||||
#define NORMAL_DOT_TOL 0.995
|
||||
|
||||
struct frame {
|
||||
point_t o;
|
||||
vect_t x;
|
||||
vect_t y;
|
||||
vect_t z;
|
||||
};
|
||||
|
||||
struct prim_case {
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct scale_case {
|
||||
const char *name;
|
||||
fastf_t sx;
|
||||
fastf_t sy;
|
||||
fastf_t sz;
|
||||
};
|
||||
|
||||
static const prim_case prims[] = {
|
||||
{"tor"},
|
||||
{"eto"},
|
||||
{"epa"},
|
||||
{"ehy"},
|
||||
{"rpc"},
|
||||
{"rhc"},
|
||||
{"particle"},
|
||||
{"hyp"},
|
||||
{"cline"},
|
||||
{"pipe"},
|
||||
{"metaball"}
|
||||
};
|
||||
|
||||
static const scale_case scales[] = {
|
||||
{"x", 1.7, 1.0, 1.0},
|
||||
{"y", 1.0, 0.65, 1.0},
|
||||
{"z", 1.0, 1.0, 1.3},
|
||||
{"xy", 1.7, 0.65, 1.0},
|
||||
{"xz", 1.7, 1.0, 1.3},
|
||||
{"yz", 1.0, 0.65, 1.3},
|
||||
{"xyz", 1.7, 0.65, 1.3}
|
||||
};
|
||||
|
||||
static void
|
||||
make_frame(frame *f, bool oblique)
|
||||
{
|
||||
if (!oblique) {
|
||||
VSET(f->o, 0.0, 0.0, 0.0);
|
||||
VSET(f->x, 1.0, 0.0, 0.0);
|
||||
VSET(f->y, 0.0, 1.0, 0.0);
|
||||
VSET(f->z, 0.0, 0.0, 1.0);
|
||||
return;
|
||||
}
|
||||
|
||||
vect_t up;
|
||||
VSET(f->o, 100.0, -40.0, 25.0);
|
||||
VSET(f->z, 0.35, 0.45, 0.82);
|
||||
VUNITIZE(f->z);
|
||||
VSET(up, 0.0, 0.0, 1.0);
|
||||
VCROSS(f->x, up, f->z);
|
||||
VUNITIZE(f->x);
|
||||
VCROSS(f->y, f->z, f->x);
|
||||
VUNITIZE(f->y);
|
||||
}
|
||||
|
||||
static void
|
||||
local_pnt(point_t out, const frame *f, fastf_t lx, fastf_t ly, fastf_t lz)
|
||||
{
|
||||
VJOIN3(out, f->o, lx, f->x, ly, f->y, lz, f->z);
|
||||
}
|
||||
|
||||
static void
|
||||
local_vec(vect_t out, const frame *f, fastf_t lx, fastf_t ly, fastf_t lz)
|
||||
{
|
||||
vect_t zero;
|
||||
VSETALL(zero, 0.0);
|
||||
VJOIN3(out, zero, lx, f->x, ly, f->y, lz, f->z);
|
||||
}
|
||||
|
||||
static int
|
||||
write_pipe(struct rt_wdb *wdbp, const char *name, const frame *f)
|
||||
{
|
||||
struct bu_list head;
|
||||
point_t p0, p1, p2;
|
||||
|
||||
local_pnt(p0, f, -4.0, 0.0, 0.0);
|
||||
local_pnt(p1, f, 0.0, 0.0, 10.0);
|
||||
local_pnt(p2, f, 5.0, 0.0, 15.0);
|
||||
|
||||
mk_pipe_init(&head);
|
||||
mk_add_pipe_pnt(&head, p0, 4.0, 1.0, 5.0);
|
||||
mk_add_pipe_pnt(&head, p1, 4.0, 1.0, 5.0);
|
||||
mk_add_pipe_pnt(&head, p2, 4.0, 1.0, 5.0);
|
||||
int ret = mk_pipe(wdbp, name, &head);
|
||||
mk_pipe_free(&head);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
write_metaball(struct rt_wdb *wdbp, const char *name, const frame *f)
|
||||
{
|
||||
point_t p0, p1, p2;
|
||||
local_pnt(p0, f, -2.0, 0.0, 0.0);
|
||||
local_pnt(p1, f, 2.0, 0.0, 0.0);
|
||||
local_pnt(p2, f, 0.0, 2.0, 1.0);
|
||||
|
||||
fastf_t mb0[5] = {p0[X], p0[Y], p0[Z], 3.0, 0.0};
|
||||
fastf_t mb1[5] = {p1[X], p1[Y], p1[Z], 3.0, 0.0};
|
||||
fastf_t mb2[5] = {p2[X], p2[Y], p2[Z], 2.5, 0.0};
|
||||
const fastf_t *verts[3] = {mb0, mb1, mb2};
|
||||
|
||||
return mk_metaball(wdbp, name, 3, METABALL_ISOPOTENTIAL, 1.0, verts);
|
||||
}
|
||||
|
||||
static int
|
||||
write_primitive(struct rt_wdb *wdbp, const prim_case &pc, const char *name, const frame *f)
|
||||
{
|
||||
point_t v;
|
||||
vect_t h, b, a, n, c;
|
||||
|
||||
local_pnt(v, f, 0.0, 0.0, 0.0);
|
||||
local_vec(h, f, 0.0, 0.0, 12.0);
|
||||
local_vec(b, f, 0.0, 6.0, 0.0);
|
||||
local_vec(a, f, 4.0, 0.0, 0.0);
|
||||
local_vec(n, f, 0.0, 0.0, 1.0);
|
||||
local_vec(c, f, 3.0, 0.0, 0.0);
|
||||
|
||||
if (BU_STR_EQUAL(pc.name, "tor"))
|
||||
return mk_tor(wdbp, name, v, n, 6.0, 1.5);
|
||||
if (BU_STR_EQUAL(pc.name, "eto"))
|
||||
return mk_eto(wdbp, name, v, n, c, 6.0, 1.2);
|
||||
if (BU_STR_EQUAL(pc.name, "epa"))
|
||||
return mk_epa(wdbp, name, v, h, f->x, 4.0, 2.5);
|
||||
if (BU_STR_EQUAL(pc.name, "ehy"))
|
||||
return mk_ehy(wdbp, name, v, h, f->x, 4.0, 2.5, 16.0);
|
||||
if (BU_STR_EQUAL(pc.name, "rpc"))
|
||||
return mk_rpc(wdbp, name, v, h, b, 2.5);
|
||||
if (BU_STR_EQUAL(pc.name, "rhc"))
|
||||
return mk_rhc(wdbp, name, v, h, b, 2.5, 5.0);
|
||||
if (BU_STR_EQUAL(pc.name, "particle"))
|
||||
return mk_particle(wdbp, name, v, h, 2.0, 3.0);
|
||||
if (BU_STR_EQUAL(pc.name, "hyp"))
|
||||
return mk_hyp(wdbp, name, v, h, a, 2.5, 0.45);
|
||||
if (BU_STR_EQUAL(pc.name, "cline"))
|
||||
return mk_cline(wdbp, name, v, h, 2.0, 0.5);
|
||||
if (BU_STR_EQUAL(pc.name, "pipe"))
|
||||
return write_pipe(wdbp, name, f);
|
||||
if (BU_STR_EQUAL(pc.name, "metaball"))
|
||||
return write_metaball(wdbp, name, f);
|
||||
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
static int
|
||||
run_ged_cmd(struct ged *gedp, std::vector<const char *> av, const char *ctx)
|
||||
{
|
||||
int ret = ged_exec(gedp, (int)av.size(), av.data());
|
||||
if (ret != BRLCAD_OK) {
|
||||
const char *log = bu_vls_cstr(gedp->ged_result_str);
|
||||
bu_log("[nonuniform] %s failed", ctx);
|
||||
if (log && log[0])
|
||||
bu_log(": %s", log);
|
||||
bu_log("\n");
|
||||
}
|
||||
bu_vls_trunc(gedp->ged_result_str, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
copy_obj(struct ged *gedp, const char *src, const char *dst)
|
||||
{
|
||||
std::vector<const char *> av = {"copy", src, dst};
|
||||
return run_ged_cmd(gedp, av, "copy");
|
||||
}
|
||||
|
||||
static int
|
||||
facetize_obj(struct ged *gedp, const char *src, const char *dst)
|
||||
{
|
||||
std::vector<const char *> av = {"facetize", src, dst};
|
||||
return run_ged_cmd(gedp, av, "facetize");
|
||||
}
|
||||
|
||||
static void
|
||||
scale_mat(mat_t mat, const scale_case &sc)
|
||||
{
|
||||
MAT_IDN(mat);
|
||||
mat[0] = sc.sx;
|
||||
mat[5] = sc.sy;
|
||||
mat[10] = sc.sz;
|
||||
}
|
||||
|
||||
static int
|
||||
write_wrapper(struct ged *gedp, const char *comb, const char *leaf, mat_t mat)
|
||||
{
|
||||
struct rt_wdb *wdbp = wdb_dbopen(gedp->dbip, RT_WDB_TYPE_DB_DEFAULT);
|
||||
if (!wdbp)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
struct wmember wm;
|
||||
BU_LIST_INIT(&wm.l);
|
||||
mk_addmember(leaf, &wm.l, mat, WMOP_UNION);
|
||||
return mk_lcomb(wdbp, comb, &wm, 0, NULL, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
npush_x(struct ged *gedp, const char *comb)
|
||||
{
|
||||
std::vector<const char *> av = {"npush", "-x", comb};
|
||||
return run_ged_cmd(gedp, av, "npush -x");
|
||||
}
|
||||
|
||||
static int
|
||||
object_volume(struct db_i *dbip, const char *name, fastf_t *vol)
|
||||
{
|
||||
struct directory *dp = db_lookup(dbip, name, LOOKUP_QUIET);
|
||||
if (!dp)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
struct rt_db_internal intern;
|
||||
RT_DB_INTERNAL_INIT(&intern);
|
||||
if (rt_db_get_internal(&intern, dp, dbip, NULL) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
const struct rt_functab *ft = intern.idb_meth;
|
||||
*vol = -1.0;
|
||||
if (ft && ft->ft_volume)
|
||||
ft->ft_volume(vol, &intern);
|
||||
rt_db_free_internal(&intern);
|
||||
if (!std::isfinite(*vol) || *vol <= 0.0 ||
|
||||
NEAR_EQUAL(*vol, -1.0, SMALL_FASTF))
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
return BRLCAD_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
object_surface_area(struct db_i *dbip, const char *name, fastf_t *area)
|
||||
{
|
||||
struct directory *dp = db_lookup(dbip, name, LOOKUP_QUIET);
|
||||
if (!dp)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
struct rt_db_internal intern;
|
||||
RT_DB_INTERNAL_INIT(&intern);
|
||||
if (rt_db_get_internal(&intern, dp, dbip, NULL) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
const struct rt_functab *ft = intern.idb_meth;
|
||||
*area = -1.0;
|
||||
if (ft && ft->ft_surf_area)
|
||||
ft->ft_surf_area(area, &intern);
|
||||
rt_db_free_internal(&intern);
|
||||
if (!std::isfinite(*area) || *area <= 0.0 ||
|
||||
NEAR_EQUAL(*area, -1.0, SMALL_FASTF))
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
return BRLCAD_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
object_centroid(struct db_i *dbip, const char *name, point_t *centroid, bool *available)
|
||||
{
|
||||
struct directory *dp = db_lookup(dbip, name, LOOKUP_QUIET);
|
||||
if (!dp)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
struct rt_db_internal intern;
|
||||
RT_DB_INTERNAL_INIT(&intern);
|
||||
if (rt_db_get_internal(&intern, dp, dbip, NULL) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
point_t error_centroid;
|
||||
VSETALL(*centroid, -1.0);
|
||||
VSETALL(error_centroid, -1.0);
|
||||
*available = intern.idb_meth && intern.idb_meth->ft_centroid;
|
||||
if (*available)
|
||||
intern.idb_meth->ft_centroid(centroid, &intern);
|
||||
rt_db_free_internal(&intern);
|
||||
|
||||
if (*available && (!std::isfinite((*centroid)[X]) ||
|
||||
!std::isfinite((*centroid)[Y]) || !std::isfinite((*centroid)[Z]) ||
|
||||
VNEAR_EQUAL(*centroid, error_centroid, SMALL_FASTF)))
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
return BRLCAD_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
object_tess_surface_area(struct db_i *dbip, const char *name, fastf_t *area)
|
||||
{
|
||||
struct directory *dp = db_lookup(dbip, name, LOOKUP_QUIET);
|
||||
if (!dp)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
struct rt_db_internal intern;
|
||||
RT_DB_INTERNAL_INIT(&intern);
|
||||
if (rt_db_get_internal(&intern, dp, dbip, NULL) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
struct bg_tess_tol ttol = BG_TESS_TOL_INIT_ZERO;
|
||||
struct bn_tol tol = BN_TOL_INIT_TOL;
|
||||
ttol.abs = 0.05;
|
||||
ttol.rel = 0.01;
|
||||
ttol.norm = 0.0;
|
||||
|
||||
struct model *m = nmg_mm();
|
||||
struct nmgregion *r = NULL;
|
||||
int ret = rt_obj_tess(&r, m, &intern, &ttol, &tol);
|
||||
if (ret == 0 && r)
|
||||
*area = nmg_region_area(r);
|
||||
nmg_km(m);
|
||||
rt_db_free_internal(&intern);
|
||||
|
||||
if (ret < 0 || !std::isfinite(*area) || *area <= 0.0)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
return BRLCAD_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
object_bbox(struct db_i *dbip, const char *name, point_t bmin, point_t bmax)
|
||||
{
|
||||
struct directory *dp = db_lookup(dbip, name, LOOKUP_QUIET);
|
||||
if (!dp)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
if (rt_bound_internal(dbip, dp, bmin, bmax) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
return BRLCAD_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
has_nonuniform_attr(struct db_i *dbip, const char *name)
|
||||
{
|
||||
struct directory *dp = db_lookup(dbip, name, LOOKUP_QUIET);
|
||||
if (!dp)
|
||||
return 0;
|
||||
|
||||
struct rt_db_internal intern;
|
||||
RT_DB_INTERNAL_INIT(&intern);
|
||||
if (rt_db_get_internal(&intern, dp, dbip, NULL) < 0)
|
||||
return 0;
|
||||
|
||||
const char *attr = bu_avs_get(&intern.idb_avs, NONUNIFORM_ATTR);
|
||||
int ret = (attr && attr[0]);
|
||||
rt_db_free_internal(&intern);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct normal_sample {
|
||||
int hit;
|
||||
vect_t normal;
|
||||
};
|
||||
|
||||
static int
|
||||
normal_hit_fn(struct application *ap, struct partition *PartHeadp, struct seg *UNUSED(segp))
|
||||
{
|
||||
normal_sample *sample = (normal_sample *)ap->a_uptr;
|
||||
struct partition *pp = PartHeadp->pt_forw;
|
||||
vect_t normal;
|
||||
|
||||
if (pp == PartHeadp)
|
||||
return 0;
|
||||
|
||||
RT_HIT_NORMAL(normal, pp->pt_inhit, pp->pt_inseg->seg_stp, NULL, pp->pt_inflip);
|
||||
VMOVE(sample->normal, normal);
|
||||
sample->hit = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
normal_miss_fn(struct application *ap)
|
||||
{
|
||||
(void)ap;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
object_normal(struct db_i *dbip, const char *name, const point_t origin, const vect_t dir, vect_t normal)
|
||||
{
|
||||
struct rt_i *rtip = rt_i_create(dbip);
|
||||
if (!rtip)
|
||||
return BRLCAD_ERROR;
|
||||
if (rt_gettree(rtip, name) < 0) {
|
||||
rt_i_destroy(rtip);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
rt_prep_parallel(rtip, 1);
|
||||
|
||||
normal_sample sample;
|
||||
sample.hit = 0;
|
||||
VSETALL(sample.normal, 0.0);
|
||||
struct application ap;
|
||||
RT_APPLICATION_INIT(&ap);
|
||||
ap.a_rt_i = rtip;
|
||||
VMOVE(ap.a_ray.r_pt, origin);
|
||||
VMOVE(ap.a_ray.r_dir, dir);
|
||||
ap.a_onehit = 1;
|
||||
ap.a_uptr = &sample;
|
||||
ap.a_hit = normal_hit_fn;
|
||||
ap.a_miss = normal_miss_fn;
|
||||
|
||||
rt_shootray(&ap);
|
||||
rt_i_destroy(rtip);
|
||||
|
||||
if (!sample.hit)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
VMOVE(normal, sample.normal);
|
||||
return BRLCAD_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
check_scaled_volume(const char *label, fastf_t base, fastf_t got, fastf_t det, fastf_t tol)
|
||||
{
|
||||
fastf_t expect = base * det;
|
||||
fastf_t rel = std::fabs((got - expect) / expect);
|
||||
if (rel > tol) {
|
||||
bu_log("[nonuniform] %s volume mismatch: got %.17g expected %.17g det %.17g rel %.6g tol %.6g\n",
|
||||
label, got, expect, det, rel, tol);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
return BRLCAD_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
check_no_nonuniform_attr(struct db_i *dbip, const char *name, const char *label)
|
||||
{
|
||||
if (has_nonuniform_attr(dbip, name)) {
|
||||
bu_log("[nonuniform] %s unexpectedly has %s\n", label, NONUNIFORM_ATTR);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
return BRLCAD_OK;
|
||||
}
|
||||
|
||||
static fastf_t
|
||||
csg_volume_tol_for_tag(const char *tag)
|
||||
{
|
||||
if (BU_STR_EQUAL(tag, "cline_axis") || BU_STR_EQUAL(tag, "cline_oblique") ||
|
||||
BU_STR_EQUAL(tag, "metaball_axis") || BU_STR_EQUAL(tag, "metaball_oblique"))
|
||||
return 0.10;
|
||||
|
||||
return VOL_TOL_CSG;
|
||||
}
|
||||
|
||||
static fastf_t
|
||||
ratio_tol_for_tag(const char *tag)
|
||||
{
|
||||
if (BU_STR_EQUAL(tag, "cline_axis") || BU_STR_EQUAL(tag, "cline_oblique") ||
|
||||
BU_STR_EQUAL(tag, "metaball_axis") || BU_STR_EQUAL(tag, "metaball_oblique"))
|
||||
return 0.12;
|
||||
|
||||
return RATIO_TOL;
|
||||
}
|
||||
|
||||
static int
|
||||
run_attr_minimality_case(struct ged *gedp, const char *base_csg, fastf_t base_csg_vol, const char *tag)
|
||||
{
|
||||
char uniform_leaf[128], uniform_comb[128], scaled_leaf[128], scaled_comb[128], inverse_comb[128], label[192];
|
||||
fastf_t csg_vol_tol = csg_volume_tol_for_tag(tag);
|
||||
|
||||
std::snprintf(uniform_leaf, sizeof(uniform_leaf), "%s_uniform_csg.s", tag);
|
||||
std::snprintf(uniform_comb, sizeof(uniform_comb), "%s_uniform_csg.c", tag);
|
||||
if (copy_obj(gedp, base_csg, uniform_leaf) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
mat_t uniform_mat;
|
||||
MAT_IDN(uniform_mat);
|
||||
uniform_mat[15] = 1.0 / 1.25;
|
||||
if (write_wrapper(gedp, uniform_comb, uniform_leaf, uniform_mat) != BRLCAD_OK ||
|
||||
npush_x(gedp, uniform_comb) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
std::snprintf(label, sizeof(label), "%s/uniform", tag);
|
||||
if (check_no_nonuniform_attr(gedp->dbip, uniform_leaf, label) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
fastf_t uniform_vol = -1.0;
|
||||
if (object_volume(gedp->dbip, uniform_leaf, &uniform_vol) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
if (check_scaled_volume(label, base_csg_vol, uniform_vol, 1.25 * 1.25 * 1.25, csg_vol_tol) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
std::snprintf(scaled_leaf, sizeof(scaled_leaf), "%s_cancel_csg.s", tag);
|
||||
std::snprintf(scaled_comb, sizeof(scaled_comb), "%s_cancel_scale_csg.c", tag);
|
||||
std::snprintf(inverse_comb, sizeof(inverse_comb), "%s_cancel_inverse_csg.c", tag);
|
||||
if (copy_obj(gedp, base_csg, scaled_leaf) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
scale_case xscale = {"x", 1.7, 1.0, 1.0};
|
||||
mat_t xmat;
|
||||
scale_mat(xmat, xscale);
|
||||
if (write_wrapper(gedp, scaled_comb, scaled_leaf, xmat) != BRLCAD_OK ||
|
||||
npush_x(gedp, scaled_comb) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
if (!has_nonuniform_attr(gedp->dbip, scaled_leaf)) {
|
||||
bu_log("[nonuniform] %s missing %s before inverse cancellation\n", scaled_leaf, NONUNIFORM_ATTR);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
mat_t invmat;
|
||||
MAT_IDN(invmat);
|
||||
invmat[0] = 1.0 / 1.7;
|
||||
if (write_wrapper(gedp, inverse_comb, scaled_leaf, invmat) != BRLCAD_OK ||
|
||||
npush_x(gedp, inverse_comb) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
std::snprintf(label, sizeof(label), "%s/nonuniform-cancel", tag);
|
||||
if (check_no_nonuniform_attr(gedp->dbip, scaled_leaf, label) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
fastf_t cancel_vol = -1.0;
|
||||
if (object_volume(gedp->dbip, scaled_leaf, &cancel_vol) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
return check_scaled_volume(label, base_csg_vol, cancel_vol, 1.0, csg_vol_tol);
|
||||
}
|
||||
|
||||
static int
|
||||
check_area_match(const char *label, fastf_t sampled, fastf_t tess, fastf_t tol)
|
||||
{
|
||||
fastf_t rel = std::fabs((sampled - tess) / tess);
|
||||
if (rel > tol) {
|
||||
bu_log("[nonuniform] %s surface area mismatch: sampled %.17g tess %.17g rel %.6g tol %.6g\n",
|
||||
label, sampled, tess, rel, tol);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
return BRLCAD_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
check_nonuniform_normal(struct ged *gedp, const char *base, const char *scaled,
|
||||
const char *label, const scale_case &sc)
|
||||
{
|
||||
point_t bmin, bmax, center;
|
||||
fastf_t span;
|
||||
static const vect_t dirs[] = {
|
||||
{1.0, 0.0, 0.0},
|
||||
{-1.0, 0.0, 0.0},
|
||||
{0.0, 1.0, 0.0},
|
||||
{0.0, -1.0, 0.0},
|
||||
{0.0, 0.0, 1.0},
|
||||
{0.0, 0.0, -1.0}
|
||||
};
|
||||
static const fastf_t offsets[] = {-0.25, 0.0, 0.25};
|
||||
|
||||
if (object_bbox(gedp->dbip, scaled, bmin, bmax) != BRLCAD_OK) {
|
||||
bu_log("[nonuniform] %s normal bbox failure\n", label);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
VADD2(center, bmin, bmax);
|
||||
VSCALE(center, center, 0.5);
|
||||
span = DIST_PNT_PNT(bmin, bmax);
|
||||
if (span <= SMALL_FASTF)
|
||||
span = 100.0;
|
||||
|
||||
for (const auto &dir : dirs) {
|
||||
vect_t u, v;
|
||||
if (!ZERO(dir[X])) {
|
||||
VSET(u, 0.0, 1.0, 0.0);
|
||||
VSET(v, 0.0, 0.0, 1.0);
|
||||
} else if (!ZERO(dir[Y])) {
|
||||
VSET(u, 1.0, 0.0, 0.0);
|
||||
VSET(v, 0.0, 0.0, 1.0);
|
||||
} else {
|
||||
VSET(u, 1.0, 0.0, 0.0);
|
||||
VSET(v, 0.0, 1.0, 0.0);
|
||||
}
|
||||
|
||||
for (fastf_t ou : offsets) {
|
||||
for (fastf_t ov : offsets) {
|
||||
point_t origin, body_origin;
|
||||
vect_t body_dir, base_normal, got_normal, expect_normal;
|
||||
|
||||
VJOIN3(origin, center, -1.25 * span, dir, ou * span, u, ov * span, v);
|
||||
VSET(body_origin, origin[X] / sc.sx, origin[Y] / sc.sy, origin[Z] / sc.sz);
|
||||
VSET(body_dir, dir[X] / sc.sx, dir[Y] / sc.sy, dir[Z] / sc.sz);
|
||||
VUNITIZE(body_dir);
|
||||
|
||||
if (object_normal(gedp->dbip, scaled, origin, dir, got_normal) != BRLCAD_OK ||
|
||||
object_normal(gedp->dbip, base, body_origin, body_dir, base_normal) != BRLCAD_OK)
|
||||
continue;
|
||||
|
||||
VSET(expect_normal, base_normal[X] / sc.sx, base_normal[Y] / sc.sy, base_normal[Z] / sc.sz);
|
||||
VUNITIZE(expect_normal);
|
||||
|
||||
fastf_t dot = VDOT(got_normal, expect_normal);
|
||||
if (dot < NORMAL_DOT_TOL) {
|
||||
bu_log("[nonuniform] %s normal mismatch: got [%g %g %g] expected [%g %g %g] dot %.17g tol %.17g\n",
|
||||
label,
|
||||
got_normal[X], got_normal[Y], got_normal[Z],
|
||||
expect_normal[X], expect_normal[Y], expect_normal[Z],
|
||||
dot, NORMAL_DOT_TOL);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
return BRLCAD_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bu_log("[nonuniform] %s normal ray miss/setup failure\n", label);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
static int
|
||||
check_ratio(const char *label, fastf_t base_csg, fastf_t base_bot, fastf_t csg, fastf_t bot, fastf_t tol)
|
||||
{
|
||||
fastf_t base_ratio = base_bot / base_csg;
|
||||
fastf_t ratio = bot / csg;
|
||||
fastf_t rel = std::fabs((ratio - base_ratio) / base_ratio);
|
||||
if (rel > tol) {
|
||||
bu_log("[nonuniform] %s BoT/CSG ratio mismatch: got %.17g baseline %.17g rel %.6g tol %.6g\n",
|
||||
label, ratio, base_ratio, rel, tol);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
return BRLCAD_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
run_scaled_case(struct ged *gedp, const char *base_csg, const char *base_bot,
|
||||
fastf_t base_csg_vol, fastf_t base_bot_vol,
|
||||
const char *tag, const scale_case &sc)
|
||||
{
|
||||
char csg_leaf[128], bot_leaf[128], csg_comb[128], bot_comb[128];
|
||||
std::snprintf(csg_leaf, sizeof(csg_leaf), "%s_%s_csg.s", tag, sc.name);
|
||||
std::snprintf(bot_leaf, sizeof(bot_leaf), "%s_%s_bot.s", tag, sc.name);
|
||||
std::snprintf(csg_comb, sizeof(csg_comb), "%s_%s_csg.c", tag, sc.name);
|
||||
std::snprintf(bot_comb, sizeof(bot_comb), "%s_%s_bot.c", tag, sc.name);
|
||||
|
||||
if (copy_obj(gedp, base_csg, csg_leaf) != BRLCAD_OK ||
|
||||
copy_obj(gedp, base_bot, bot_leaf) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
mat_t mat;
|
||||
scale_mat(mat, sc);
|
||||
if (write_wrapper(gedp, csg_comb, csg_leaf, mat) != BRLCAD_OK ||
|
||||
write_wrapper(gedp, bot_comb, bot_leaf, mat) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
if (npush_x(gedp, csg_comb) != BRLCAD_OK ||
|
||||
npush_x(gedp, bot_comb) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
if (!has_nonuniform_attr(gedp->dbip, csg_leaf)) {
|
||||
bu_log("[nonuniform] %s missing %s after npush -x\n", csg_leaf, NONUNIFORM_ATTR);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
fastf_t csg_vol = -1.0;
|
||||
fastf_t bot_vol = -1.0;
|
||||
fastf_t det = sc.sx * sc.sy * sc.sz;
|
||||
if (object_volume(gedp->dbip, csg_leaf, &csg_vol) != BRLCAD_OK ||
|
||||
object_volume(gedp->dbip, bot_leaf, &bot_vol) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
char label[192];
|
||||
fastf_t csg_vol_tol = csg_volume_tol_for_tag(tag);
|
||||
fastf_t ratio_tol = ratio_tol_for_tag(tag);
|
||||
|
||||
std::snprintf(label, sizeof(label), "%s/%s/csg", tag, sc.name);
|
||||
if (check_scaled_volume(label, base_csg_vol, csg_vol, det, csg_vol_tol) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
std::snprintf(label, sizeof(label), "%s/%s/bot", tag, sc.name);
|
||||
if (check_scaled_volume(label, base_bot_vol, bot_vol, det, VOL_TOL_BOT) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
std::snprintf(label, sizeof(label), "%s/%s", tag, sc.name);
|
||||
if (check_ratio(label, base_csg_vol, base_bot_vol, csg_vol, bot_vol, ratio_tol) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
point_t base_centroid, csg_centroid, expected_centroid;
|
||||
bool have_base_centroid = false;
|
||||
bool have_csg_centroid = false;
|
||||
if (object_centroid(gedp->dbip, base_csg, &base_centroid, &have_base_centroid) != BRLCAD_OK ||
|
||||
object_centroid(gedp->dbip, csg_leaf, &csg_centroid, &have_csg_centroid) != BRLCAD_OK ||
|
||||
have_base_centroid != have_csg_centroid)
|
||||
return BRLCAD_ERROR;
|
||||
if (have_csg_centroid) {
|
||||
MAT4X3PNT(expected_centroid, mat, base_centroid);
|
||||
if (!VNEAR_EQUAL(csg_centroid, expected_centroid, BN_TOL_DIST)) {
|
||||
bu_log("[nonuniform] %s centroid mismatch: got (%g %g %g), expected (%g %g %g)\n",
|
||||
label, V3ARGS(csg_centroid), V3ARGS(expected_centroid));
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
fastf_t csg_area = -1.0;
|
||||
fastf_t csg_tess_area = -1.0;
|
||||
fastf_t bot_area = -1.0;
|
||||
fastf_t bot_tess_area = -1.0;
|
||||
if (object_surface_area(gedp->dbip, csg_leaf, &csg_area) != BRLCAD_OK ||
|
||||
object_tess_surface_area(gedp->dbip, csg_leaf, &csg_tess_area) != BRLCAD_OK ||
|
||||
object_surface_area(gedp->dbip, bot_leaf, &bot_area) != BRLCAD_OK ||
|
||||
object_tess_surface_area(gedp->dbip, bot_leaf, &bot_tess_area) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
std::snprintf(label, sizeof(label), "%s/%s/csg", tag, sc.name);
|
||||
if (check_area_match(label, csg_area, csg_tess_area, SA_TOL_CSG_TESS) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
std::snprintf(label, sizeof(label), "%s/%s/bot", tag, sc.name);
|
||||
if (check_area_match(label, bot_area, bot_tess_area, SA_TOL_BOT_TESS) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
std::snprintf(label, sizeof(label), "%s/%s/csg", tag, sc.name);
|
||||
return check_nonuniform_normal(gedp, base_csg, csg_leaf, label, sc);
|
||||
}
|
||||
|
||||
static int
|
||||
run_prim_orientation(struct ged *gedp, const prim_case &pc, const char *orient)
|
||||
{
|
||||
char base[128], bot[128], tag[128];
|
||||
std::snprintf(tag, sizeof(tag), "%s_%s", pc.name, orient);
|
||||
std::snprintf(base, sizeof(base), "%s_base.s", tag);
|
||||
std::snprintf(bot, sizeof(bot), "%s_base.bot", tag);
|
||||
|
||||
if (facetize_obj(gedp, base, bot) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
fastf_t base_csg_vol = -1.0;
|
||||
fastf_t base_bot_vol = -1.0;
|
||||
if (object_volume(gedp->dbip, base, &base_csg_vol) != BRLCAD_OK) {
|
||||
bu_log("[nonuniform] %s baseline CSG volume unavailable\n", base);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
if (object_volume(gedp->dbip, bot, &base_bot_vol) != BRLCAD_OK) {
|
||||
bu_log("[nonuniform] %s baseline BoT volume unavailable\n", bot);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
if (run_attr_minimality_case(gedp, base, base_csg_vol, tag) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
for (const auto &sc : scales) {
|
||||
if (run_scaled_case(gedp, base, bot, base_csg_vol, base_bot_vol, tag, sc) != BRLCAD_OK)
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
bu_log("[nonuniform] %s %s baseline csg %.17g bot %.17g ratio %.17g\n",
|
||||
pc.name, orient, base_csg_vol, base_bot_vol, base_bot_vol / base_csg_vol);
|
||||
return BRLCAD_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
build_db(const char *gfile)
|
||||
{
|
||||
struct rt_wdb *wdbp = wdb_fopen(gfile);
|
||||
if (!wdbp)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
int failures = 0;
|
||||
for (const auto &pc : prims) {
|
||||
for (int oi = 0; oi < 2; oi++) {
|
||||
frame f;
|
||||
make_frame(&f, oi != 0);
|
||||
char name[128];
|
||||
std::snprintf(name, sizeof(name), "%s_%s_base.s", pc.name, oi ? "oblique" : "axis");
|
||||
if (write_primitive(wdbp, pc, name, &f) != 0) {
|
||||
bu_log("[nonuniform] failed to write %s\n", name);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
db_close(wdbp->dbip);
|
||||
return failures ? BRLCAD_ERROR : BRLCAD_OK;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, const char **argv)
|
||||
{
|
||||
bu_setprogname(argv[0]);
|
||||
|
||||
if (argc < 2) {
|
||||
bu_log("Usage: %s tmpdir\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct bu_vls bu_cache = BU_VLS_INIT_ZERO;
|
||||
struct bu_vls rt_cache = BU_VLS_INIT_ZERO;
|
||||
bu_vls_printf(&bu_cache, "%s/cache", argv[1]);
|
||||
bu_vls_printf(&rt_cache, "%s/rtcache", argv[1]);
|
||||
bu_dirclear(bu_vls_cstr(&bu_cache));
|
||||
bu_dirclear(bu_vls_cstr(&rt_cache));
|
||||
bu_mkdir(bu_vls_cstr(&bu_cache));
|
||||
bu_mkdir(bu_vls_cstr(&rt_cache));
|
||||
bu_setenv("BU_DIR_CACHE", bu_vls_cstr(&bu_cache), 1);
|
||||
bu_setenv("LIBRT_CACHE", bu_vls_cstr(&rt_cache), 1);
|
||||
|
||||
struct bu_vls gfile = BU_VLS_INIT_ZERO;
|
||||
bu_vls_printf(&gfile, "%s/ged_nonuniform.g", argv[1]);
|
||||
bu_file_delete(bu_vls_cstr(&gfile));
|
||||
|
||||
if (build_db(bu_vls_cstr(&gfile)) != BRLCAD_OK) {
|
||||
bu_vls_free(&bu_cache);
|
||||
bu_vls_free(&rt_cache);
|
||||
bu_vls_free(&gfile);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct ged *gedp = ged_open("db", bu_vls_cstr(&gfile), 1);
|
||||
if (!gedp) {
|
||||
bu_log("[nonuniform] ged_open failed for %s\n", bu_vls_cstr(&gfile));
|
||||
bu_vls_free(&bu_cache);
|
||||
bu_vls_free(&rt_cache);
|
||||
bu_vls_free(&gfile);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int failures = 0;
|
||||
for (const auto &pc : prims) {
|
||||
if (run_prim_orientation(gedp, pc, "axis") != BRLCAD_OK)
|
||||
failures++;
|
||||
if (run_prim_orientation(gedp, pc, "oblique") != BRLCAD_OK)
|
||||
failures++;
|
||||
}
|
||||
|
||||
ged_close(gedp);
|
||||
bu_dirclear(bu_vls_cstr(&bu_cache));
|
||||
bu_dirclear(bu_vls_cstr(&rt_cache));
|
||||
bu_vls_free(&bu_cache);
|
||||
bu_vls_free(&rt_cache);
|
||||
bu_vls_free(&gfile);
|
||||
|
||||
if (failures) {
|
||||
bu_log("[nonuniform] %d primitive/orientation failures\n", failures);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bu_log("[nonuniform] all primitive non-uniform metric checks passed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C++
|
||||
* tab-width: 8
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "stroustrup"
|
||||
* End:
|
||||
* ex: shiftwidth=4 tabstop=8
|
||||
*/
|
||||
@@ -0,0 +1,54 @@
|
||||
# Values set at CMake configure time
|
||||
set(CBDIR "@CMAKE_CURRENT_BINARY_DIR@")
|
||||
set(LOGFILE "${CBDIR}/regress-ged_nonuniform.log")
|
||||
|
||||
set(BU_DIR_CACHE ${CBDIR}/cache_nonuniform)
|
||||
set(LIBRT_CACHE ${CBDIR}/rtcache_nonuniform)
|
||||
set(ENV{BU_DIR_CACHE} ${BU_DIR_CACHE})
|
||||
set(ENV{LIBRT_CACHE} ${LIBRT_CACHE})
|
||||
file(REMOVE_RECURSE "${BU_DIR_CACHE}")
|
||||
file(REMOVE_RECURSE "${LIBRT_CACHE}")
|
||||
file(MAKE_DIRECTORY "${BU_DIR_CACHE}")
|
||||
file(MAKE_DIRECTORY "${LIBRT_CACHE}")
|
||||
|
||||
set(TMPDIR "${CBDIR}/nonuniform_regress_tmp")
|
||||
file(REMOVE_RECURSE "${TMPDIR}")
|
||||
file(MAKE_DIRECTORY "${TMPDIR}")
|
||||
|
||||
file(WRITE "${LOGFILE}" "Starting nonuniform primitive regression test run\n")
|
||||
|
||||
string(REPLACE "\\" "" NONUNIFORM_EXEC "${EXEC}")
|
||||
if(NOT EXISTS "${NONUNIFORM_EXEC}")
|
||||
file(WRITE "${LOGFILE}" "nonuniform regression program not found at location \"${NONUNIFORM_EXEC}\" - aborting\n")
|
||||
file(READ "${LOGFILE}" LOG)
|
||||
message(FATAL_ERROR "Unable to find nonuniform regression program, aborting.\nSee ${LOGFILE} for more details.\n${LOG}")
|
||||
endif(NOT EXISTS "${NONUNIFORM_EXEC}")
|
||||
|
||||
file(APPEND "${LOGFILE}" "Running ${NONUNIFORM_EXEC} ${TMPDIR}\n")
|
||||
execute_process(
|
||||
COMMAND "${NONUNIFORM_EXEC}" "${TMPDIR}"
|
||||
RESULT_VARIABLE nonuniform_result
|
||||
OUTPUT_VARIABLE nonuniform_log
|
||||
ERROR_VARIABLE nonuniform_log
|
||||
WORKING_DIRECTORY "${CBDIR}"
|
||||
)
|
||||
file(APPEND "${LOGFILE}" "${nonuniform_log}")
|
||||
if(nonuniform_result)
|
||||
file(READ "${LOGFILE}" LOG)
|
||||
message(FATAL_ERROR "[regress-ged_nonuniform] Failure: ${nonuniform_result}. See ${LOGFILE} for more info.\n${LOG}")
|
||||
endif(nonuniform_result)
|
||||
|
||||
file(REMOVE_RECURSE "${TMPDIR}")
|
||||
execute_process(
|
||||
COMMAND "@CMAKE_COMMAND@" -E rm -rf "${BU_DIR_CACHE}"
|
||||
)
|
||||
execute_process(
|
||||
COMMAND "@CMAKE_COMMAND@" -E rm -rf "${LIBRT_CACHE}"
|
||||
)
|
||||
|
||||
# Local Variables:
|
||||
# tab-width: 8
|
||||
# mode: cmake
|
||||
# indent-tabs-mode: t
|
||||
# End:
|
||||
# ex: shiftwidth=2 tabstop=8
|
||||
@@ -56,6 +56,11 @@
|
||||
* Facetize may legitimately return an empty solid BoT for an empty CSG
|
||||
* result. lint --raytrace should treat this as a valid zero-metric
|
||||
* reference instead of reporting facetize failure.
|
||||
*
|
||||
* TC6 Surface-mode BoT metric failure propagation.
|
||||
* Surface BoTs have a valid area but no solid volume. Analyze and lint
|
||||
* must report that failed analytic calculation, and lint must block the
|
||||
* parent combination from validation.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
@@ -185,7 +190,8 @@ make_tmp_g(const char *tag)
|
||||
* non-NULL. Returns ged_exec return value. */
|
||||
static int
|
||||
run_lint_raytrace(const char *gfile, const char *json_out_path,
|
||||
const char *extra_tol_arg, int argc_objs, const char **objs)
|
||||
const char *extra_tol_arg, int argc_objs, const char **objs,
|
||||
std::string *result = NULL)
|
||||
{
|
||||
struct ged *gedp = ged_open("db", gfile, 1);
|
||||
if (!gedp) {
|
||||
@@ -209,6 +215,8 @@ run_lint_raytrace(const char *gfile, const char *json_out_path,
|
||||
av.push_back(objs[i]);
|
||||
|
||||
int ret = ged_exec(gedp, (int)av.size(), av.data());
|
||||
if (result)
|
||||
*result = bu_vls_cstr(gedp->ged_result_str);
|
||||
ged_close(gedp);
|
||||
return ret;
|
||||
}
|
||||
@@ -583,6 +591,105 @@ tc5_empty_comb_valid(void)
|
||||
return pass ? 0 : 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* TC6 - analytic callback errors reach command-level results */
|
||||
/* ------------------------------------------------------------------ */
|
||||
static int
|
||||
tc6_metric_error_propagation(void)
|
||||
{
|
||||
bu_log("TC6: surface BoT metric failures propagate to GED commands\n");
|
||||
|
||||
std::string gfile = make_tmp_g("tc6");
|
||||
if (gfile.empty()) {
|
||||
bu_log("TC6: FAIL - could not create temp .g file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
{
|
||||
struct rt_wdb *wdbp = wdb_fopen(gfile.c_str());
|
||||
if (!wdbp) {
|
||||
bu_file_delete(gfile.c_str());
|
||||
bu_log("TC6: FAIL - wdb_fopen failed\n");
|
||||
return 1;
|
||||
}
|
||||
fastf_t vertices[] = {
|
||||
0.0, 0.0, 0.0,
|
||||
10.0, 0.0, 0.0,
|
||||
0.0, 10.0, 0.0
|
||||
};
|
||||
int faces[] = {0, 1, 2};
|
||||
if (mk_bot(wdbp, "tc6_surface_bot.s", RT_BOT_SURFACE, RT_BOT_CCW, 0,
|
||||
3, 1, vertices, faces, NULL, NULL) != 0) {
|
||||
db_close(wdbp->dbip);
|
||||
bu_file_delete(gfile.c_str());
|
||||
bu_log("TC6: FAIL - mk_bot failed\n");
|
||||
return 1;
|
||||
}
|
||||
struct wmember head;
|
||||
BU_LIST_INIT(&head.l);
|
||||
mk_addmember("tc6_surface_bot.s", &head.l, NULL, WMOP_UNION);
|
||||
if (mk_lcomb(wdbp, "tc6_comb.c", &head, 0, NULL, NULL, NULL, 0) != 0) {
|
||||
db_close(wdbp->dbip);
|
||||
bu_file_delete(gfile.c_str());
|
||||
bu_log("TC6: FAIL - mk_lcomb failed\n");
|
||||
return 1;
|
||||
}
|
||||
db_close(wdbp->dbip);
|
||||
}
|
||||
|
||||
int failures = 0;
|
||||
{
|
||||
struct ged *gedp = ged_open("db", gfile.c_str(), 1);
|
||||
if (!gedp) {
|
||||
bu_file_delete(gfile.c_str());
|
||||
bu_log("TC6: FAIL - ged_open failed\n");
|
||||
return 1;
|
||||
}
|
||||
const char *av[] = {"analyze", "summarize", "tc6_surface_bot.s"};
|
||||
int ret = ged_exec(gedp, 3, av);
|
||||
const char *result = bu_vls_cstr(gedp->ged_result_str);
|
||||
if (ret != BRLCAD_ERROR || !strstr(result, "COULD NOT DETERMINE")) {
|
||||
bu_log("TC6: FAIL - analyze ret=%d result=%s\n", ret, result);
|
||||
failures++;
|
||||
}
|
||||
ged_close(gedp);
|
||||
}
|
||||
|
||||
char json_path[MAXPATHLEN] = {0};
|
||||
FILE *jfp = bu_temp_file(json_path, MAXPATHLEN);
|
||||
if (!jfp) {
|
||||
bu_file_delete(gfile.c_str());
|
||||
bu_log("TC6: FAIL - could not create temporary JSON output\n");
|
||||
return 1;
|
||||
}
|
||||
fclose(jfp);
|
||||
|
||||
const char *obj = "tc6_comb.c";
|
||||
std::string lint_output;
|
||||
int lint_ret = run_lint_raytrace(gfile.c_str(), json_path,
|
||||
NULL, 1, &obj, &lint_output);
|
||||
std::string jtext = read_file(json_path);
|
||||
int analytic_failed = json_has_pair_in_same_object(
|
||||
jtext.c_str(), "problem_type", "raytrace_analytic_failed",
|
||||
"object_name", "tc6_surface_bot.s");
|
||||
int parent_skipped = json_has_pair_in_same_object(
|
||||
jtext.c_str(), "problem_type", "raytrace_skip",
|
||||
"object_name", obj);
|
||||
int failure_reported = lint_output.find("Analytic metric failures:") != std::string::npos &&
|
||||
lint_output.find("tc6_surface_bot.s") != std::string::npos;
|
||||
if (lint_ret != BRLCAD_ERROR || !analytic_failed || !parent_skipped || !failure_reported) {
|
||||
bu_log("TC6: FAIL - lint ret=%d analytic_failed=%d parent_skipped=%d failure_reported=%d JSON=%.600s\n",
|
||||
lint_ret, analytic_failed, parent_skipped, failure_reported, jtext.c_str());
|
||||
failures++;
|
||||
}
|
||||
|
||||
bu_file_delete(json_path);
|
||||
bu_file_delete(gfile.c_str());
|
||||
if (!failures)
|
||||
bu_log("TC6: PASS - command and lint failures propagated\n");
|
||||
return failures;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* main */
|
||||
/* ------------------------------------------------------------------ */
|
||||
@@ -602,6 +709,7 @@ main(int argc, char *argv[])
|
||||
if (tc_select < 0 || tc_select == 3) failures += tc3_halfspace();
|
||||
if (tc_select < 0 || tc_select == 4) failures += tc4_child_blocks_parent();
|
||||
if (tc_select < 0 || tc_select == 5) failures += tc5_empty_comb_valid();
|
||||
if (tc_select < 0 || tc_select == 6) failures += tc6_metric_error_propagation();
|
||||
|
||||
bu_log("\nRegress-lint-raytrace: %s (%d failure(s))\n",
|
||||
failures == 0 ? "PASS" : "FAIL", failures);
|
||||
|
||||
+176
-3
@@ -385,6 +385,44 @@ puts "*** Testing broad MGED CSG edit integration ***"
|
||||
}
|
||||
}
|
||||
|
||||
# Read the actual vlist coordinates retained by MGED after an edit. Database
|
||||
# bounds alone cannot detect a stale wireframe whose temporary edit matrix
|
||||
# was reset by Accept.
|
||||
proc csg_edit_display_bbox_at {expected} {
|
||||
set point_pattern {^[[:space:]]+[^=]+\(([-+0-9.eE]+), ([-+0-9.eE]+), ([-+0-9.eE]+)\)$}
|
||||
set found 0
|
||||
set actual_min {Inf Inf Inf}
|
||||
set actual_max {-Inf -Inf -Inf}
|
||||
|
||||
foreach line [split [who solids 3] "\n"] {
|
||||
if {![regexp $point_pattern $line -> x y z]} {
|
||||
continue
|
||||
}
|
||||
set point [list $x $y $z]
|
||||
for {set axis 0} {$axis < 3} {incr axis} {
|
||||
set value [lindex $point $axis]
|
||||
if {$value < [lindex $actual_min $axis]} {
|
||||
lset actual_min $axis $value
|
||||
}
|
||||
if {$value > [lindex $actual_max $axis]} {
|
||||
lset actual_max $axis $value
|
||||
}
|
||||
}
|
||||
set found 1
|
||||
}
|
||||
|
||||
if {!$found} {
|
||||
error "display contains no vlist points"
|
||||
}
|
||||
set actual [concat $actual_min $actual_max]
|
||||
set tolerance 1.0e-5
|
||||
for {set i 0} {$i < 6} {incr i} {
|
||||
if {abs([lindex $actual $i] - [lindex $expected $i]) > $tolerance} {
|
||||
error "display bounding box component $i expected [lindex $expected $i], got [lindex $actual $i]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc csg_edit_bbox_translated {source delta prim} {
|
||||
set tolerance 1.0e-5
|
||||
set before [csg_edit_bbox $source]
|
||||
@@ -459,19 +497,26 @@ puts "*** Testing broad MGED CSG edit integration ***"
|
||||
return [lindex $tree 2]
|
||||
}
|
||||
|
||||
proc csg_edit_comb_leaf_matrix_at {comb member expected} {
|
||||
proc csg_edit_matrix_at {actual expected context} {
|
||||
set tolerance 1.0e-9
|
||||
if {[llength $actual] != 16} {
|
||||
error "$context matrix is not 4x4: $actual"
|
||||
}
|
||||
if {[llength $expected] != 16} {
|
||||
error "expected matrix is not 4x4: $expected"
|
||||
}
|
||||
set actual [csg_edit_comb_leaf_matrix $comb $member]
|
||||
for {set i 0} {$i < 16} {incr i} {
|
||||
if {abs([lindex $actual $i] - [lindex $expected $i]) > $tolerance} {
|
||||
error "matrix component $i expected [lindex $expected $i], got [lindex $actual $i]"
|
||||
error "$context matrix component $i expected [lindex $expected $i], got [lindex $actual $i]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc csg_edit_comb_leaf_matrix_at {comb member expected} {
|
||||
csg_edit_matrix_at [csg_edit_comb_leaf_matrix $comb $member] $expected \
|
||||
"$comb leaf $member"
|
||||
}
|
||||
|
||||
# Center, size, and quaternion together identify every view mutation that a
|
||||
# mouse edit could accidentally receive through MGED's view path.
|
||||
proc csg_edit_view_state {} {
|
||||
@@ -852,6 +897,119 @@ proc csg_matrix_mouse_check {label menu_item activate_menu commands expected_mat
|
||||
}
|
||||
}
|
||||
|
||||
proc csg_tor_matrix_scale_check {axis menu_item factor expected_bbox} {
|
||||
global csg_matrix_mouse_case_id orig_gui transform
|
||||
global csg_mouse_dm_pixels csg_mouse_view_size
|
||||
incr csg_matrix_mouse_case_id
|
||||
|
||||
set prim [format "csg_matrix_tor_%02d.s" $csg_matrix_mouse_case_id]
|
||||
set saved_orig_gui $orig_gui
|
||||
|
||||
set failed [catch {
|
||||
Z
|
||||
in $prim tor 0 0 0 0 0 1 10 2
|
||||
e $prim
|
||||
center 0 0 0
|
||||
size $csg_mouse_view_size
|
||||
setview 0 0 0
|
||||
dm size $csg_mouse_dm_pixels $csg_mouse_dm_pixels
|
||||
set orig_gui 0
|
||||
|
||||
_mged_press oill
|
||||
_mged_ill -e -i 1 /$prim
|
||||
_mged_matpick 0
|
||||
if {[status state] ne "OBJ EDIT"} {
|
||||
error "matrix selection entered [status state], not OBJ EDIT"
|
||||
}
|
||||
if {$transform ne "e"} {
|
||||
error "matrix pick left mouse transform set to '$transform', not edit"
|
||||
}
|
||||
if {[lsearch -exact [mmenu_get 1] $menu_item] < 0} {
|
||||
error "matrix edit menu does not contain '$menu_item'"
|
||||
}
|
||||
|
||||
set view_before [csg_edit_view_state]
|
||||
press $menu_item
|
||||
p 1024
|
||||
press accept
|
||||
if {[status state] ne "VIEWING"} {
|
||||
error "accept entered [status state], not VIEWING"
|
||||
}
|
||||
|
||||
set expected_matrix [csg_matrix_scale $axis $factor]
|
||||
csg_edit_matrix_at [attr get $prim matrix:nonuniform] $expected_matrix \
|
||||
"$prim matrix:nonuniform"
|
||||
csg_edit_bbox_at $expected_bbox $prim
|
||||
csg_edit_display_bbox_at $expected_bbox
|
||||
csg_edit_view_state_at $view_before
|
||||
d $prim
|
||||
} failure]
|
||||
|
||||
set orig_gui $saved_orig_gui
|
||||
catch {press reject}
|
||||
catch {Z}
|
||||
catch {kill $prim}
|
||||
|
||||
if {$failed} {
|
||||
puts " FAIL: top-level TOR Scale [string toupper $axis] ($failure)"
|
||||
} else {
|
||||
puts " PASS: top-level TOR Scale [string toupper $axis]"
|
||||
}
|
||||
}
|
||||
|
||||
set csg_primitive_scale_case_id 0
|
||||
proc csg_tor_primitive_scale_check {axis menu_item command factor expected_bbox} {
|
||||
global csg_primitive_scale_case_id orig_gui transform
|
||||
incr csg_primitive_scale_case_id
|
||||
|
||||
set prim [format "csg_primitive_tor_%02d.s" $csg_primitive_scale_case_id]
|
||||
set saved_orig_gui $orig_gui
|
||||
|
||||
set failed [catch {
|
||||
Z
|
||||
in $prim tor 0 0 0 0 0 1 10 2
|
||||
e $prim
|
||||
set orig_gui 0
|
||||
sed $prim
|
||||
if {[status state] ne "SOL EDIT"} {
|
||||
error "primitive selection entered [status state], not SOL EDIT"
|
||||
}
|
||||
if {$transform ne "e"} {
|
||||
error "primitive pick left mouse transform set to '$transform', not edit"
|
||||
}
|
||||
if {[lsearch -exact [mmenu_get 1] $menu_item] < 0} {
|
||||
error "primitive edit menu does not contain '$menu_item'"
|
||||
}
|
||||
|
||||
set view_before [csg_edit_view_state]
|
||||
press $menu_item
|
||||
uplevel #0 $command
|
||||
press accept
|
||||
if {[status state] ne "VIEWING"} {
|
||||
error "accept entered [status state], not VIEWING"
|
||||
}
|
||||
|
||||
set expected_matrix [csg_matrix_scale $axis $factor]
|
||||
csg_edit_matrix_at [attr get $prim matrix:nonuniform] $expected_matrix \
|
||||
"$prim matrix:nonuniform"
|
||||
csg_edit_bbox_at $expected_bbox $prim
|
||||
csg_edit_display_bbox_at $expected_bbox
|
||||
csg_edit_view_state_at $view_before
|
||||
d $prim
|
||||
} failure]
|
||||
|
||||
set orig_gui $saved_orig_gui
|
||||
catch {press reject}
|
||||
catch {Z}
|
||||
catch {kill $prim}
|
||||
|
||||
if {$failed} {
|
||||
puts " FAIL: primitive TOR Scale [string toupper $axis] ($failure)"
|
||||
} else {
|
||||
puts " PASS: primitive TOR Scale [string toupper $axis]"
|
||||
}
|
||||
}
|
||||
|
||||
proc csg_primitive_pick_mouse_check {} {
|
||||
global orig_gui transform
|
||||
global csg_bv_coord_half_range csg_mouse_view_size
|
||||
@@ -913,6 +1071,14 @@ set csg_direct_scale [expr {1.0 + 1024.0 / $csg_bv_coord_half_range}]
|
||||
set csg_direct_rotation [csg_matrix_multiply \
|
||||
[csg_matrix_axis_rotation x 10] \
|
||||
[csg_matrix_axis_rotation y 5]]
|
||||
|
||||
foreach test [list \
|
||||
[list x {Scale X} {p 1024} $csg_direct_scale {-18 -12 -2 18 12 2}] \
|
||||
[list y {Scale Y} {M 1 0 1024} $csg_direct_scale {-12 -18 -2 12 18 2}] \
|
||||
[list z {Scale Z} {M 1 0 1024} $csg_direct_scale {-12 -12 -3 12 12 3}]] {
|
||||
csg_tor_primitive_scale_check {*}$test
|
||||
}
|
||||
|
||||
# Matrix move and scale interpret p values in the same normalized BV coordinate
|
||||
# range used by M. Running both inputs proves the menu callback initializes
|
||||
# librt before either input path gets an opportunity to repair the edit flag.
|
||||
@@ -935,6 +1101,13 @@ foreach test [list \
|
||||
csg_matrix_mouse_check {*}$test
|
||||
}
|
||||
|
||||
foreach test [list \
|
||||
[list x {Scale X} $csg_direct_scale {-18 -12 -2 18 12 2}] \
|
||||
[list y {Scale Y} $csg_direct_scale {-12 -18 -2 12 18 2}] \
|
||||
[list z {Scale Z} $csg_direct_scale {-12 -12 -3 12 12 3}]] {
|
||||
csg_tor_matrix_scale_check {*}$test
|
||||
}
|
||||
|
||||
# Tk's default modifier-drag bindings use a 512-pixel display here. A
|
||||
# 64-pixel translation drag is one quarter of the 40-unit view, or 5 units.
|
||||
# A positive 64-pixel scale drag supplies an absolute knob value of 0.125;
|
||||
|
||||
@@ -208,7 +208,7 @@ _analyze_cmd_msgs(void *cs, int argc, const char **argv, const char *us, const c
|
||||
* metaball
|
||||
* nmg
|
||||
*/
|
||||
static void
|
||||
static int
|
||||
analyze_do_summary(struct ged *gedp, const struct rt_db_internal *ip)
|
||||
{
|
||||
/* XXX Could give solid name, and current units, here */
|
||||
@@ -216,81 +216,66 @@ analyze_do_summary(struct ged *gedp, const struct rt_db_internal *ip)
|
||||
switch (ip->idb_type) {
|
||||
|
||||
case ID_ARB8:
|
||||
analyze_arb8(gedp, ip);
|
||||
break;
|
||||
return analyze_arb8(gedp, ip);
|
||||
|
||||
case ID_BOT:
|
||||
analyze_general(gedp, ip);
|
||||
break;
|
||||
return analyze_general(gedp, ip);
|
||||
|
||||
case ID_ARBN:
|
||||
analyze_arbn(gedp, ip);
|
||||
break;
|
||||
return BRLCAD_OK;
|
||||
|
||||
case ID_ARS:
|
||||
analyze_ars(gedp, ip);
|
||||
break;
|
||||
return BRLCAD_OK;
|
||||
|
||||
case ID_TGC:
|
||||
analyze_general(gedp, ip);
|
||||
break;
|
||||
return analyze_general(gedp, ip);
|
||||
|
||||
case ID_ELL:
|
||||
analyze_general(gedp, ip);
|
||||
break;
|
||||
return analyze_general(gedp, ip);
|
||||
|
||||
case ID_TOR:
|
||||
analyze_general(gedp, ip);
|
||||
break;
|
||||
return analyze_general(gedp, ip);
|
||||
|
||||
case ID_RPC:
|
||||
analyze_general(gedp, ip);
|
||||
break;
|
||||
return analyze_general(gedp, ip);
|
||||
|
||||
case ID_ETO:
|
||||
analyze_general(gedp, ip);
|
||||
break;
|
||||
return analyze_general(gedp, ip);
|
||||
|
||||
case ID_EPA:
|
||||
analyze_general(gedp, ip);
|
||||
break;
|
||||
return analyze_general(gedp, ip);
|
||||
|
||||
case ID_PARTICLE:
|
||||
analyze_general(gedp, ip);
|
||||
break;
|
||||
return analyze_general(gedp, ip);
|
||||
|
||||
case ID_SUPERELL:
|
||||
analyze_superell(gedp, ip);
|
||||
break;
|
||||
return BRLCAD_OK;
|
||||
|
||||
case ID_SKETCH:
|
||||
analyze_sketch(gedp, ip);
|
||||
break;
|
||||
return analyze_sketch(gedp, ip);
|
||||
|
||||
case ID_HYP:
|
||||
analyze_general(gedp, ip);
|
||||
break;
|
||||
return analyze_general(gedp, ip);
|
||||
|
||||
case ID_PIPE:
|
||||
analyze_general(gedp, ip);
|
||||
break;
|
||||
return analyze_general(gedp, ip);
|
||||
|
||||
case ID_VOL:
|
||||
analyze_general(gedp, ip);
|
||||
break;
|
||||
return analyze_general(gedp, ip);
|
||||
|
||||
case ID_EXTRUDE:
|
||||
analyze_general(gedp, ip);
|
||||
break;
|
||||
return analyze_general(gedp, ip);
|
||||
|
||||
case ID_RHC:
|
||||
analyze_general(gedp, ip);
|
||||
break;
|
||||
return analyze_general(gedp, ip);
|
||||
|
||||
default:
|
||||
bu_vls_printf(gedp->ged_result_str, "\nanalyze: unable to process %s solid\n",
|
||||
OBJ[ip->idb_type].ft_name);
|
||||
break;
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,6 +291,7 @@ _analyze_cmd_summarize(void *bs, int argc, const char **argv)
|
||||
struct _ged_analyze_info *gc = (struct _ged_analyze_info *)bs;
|
||||
struct ged *gedp = gc->gedp;
|
||||
struct rt_db_internal intern;
|
||||
int status = BRLCAD_OK;
|
||||
|
||||
argc--; argv++;
|
||||
if (!argc) {
|
||||
@@ -322,17 +308,20 @@ _analyze_cmd_summarize(void *bs, int argc, const char **argv)
|
||||
/* use the names that were input */
|
||||
for (int i = 0; i < argc; i++) {
|
||||
struct directory *ndp = db_lookup(gedp->dbip, argv[i], LOOKUP_NOISY);
|
||||
if (ndp == RT_DIR_NULL)
|
||||
if (ndp == RT_DIR_NULL) {
|
||||
status = BRLCAD_ERROR;
|
||||
continue;
|
||||
}
|
||||
|
||||
GED_DB_GET_INTERN(gedp, &intern, ndp, bn_mat_identity, BRLCAD_ERROR);
|
||||
|
||||
_ged_do_list(gedp, ndp, 1);
|
||||
analyze_do_summary(gedp, &intern);
|
||||
if (analyze_do_summary(gedp, &intern) != BRLCAD_OK)
|
||||
status = BRLCAD_ERROR;
|
||||
rt_db_free_internal(&intern);
|
||||
}
|
||||
|
||||
return BRLCAD_OK;
|
||||
return status;
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "vmath.h"
|
||||
|
||||
#include "bg/polygon.h"
|
||||
@@ -72,14 +74,14 @@ analyze_edge(struct ged *gedp, const int edge, const struct rt_arb_internal *arb
|
||||
|
||||
|
||||
|
||||
void
|
||||
int
|
||||
analyze_arb8(struct ged *gedp, const struct rt_db_internal *ip)
|
||||
{
|
||||
int i, type;
|
||||
int cgtype; /* COMGEOM arb type: # of vertices */
|
||||
table_t table; /* holds table data from child functions */
|
||||
fastf_t tot_vol = 0.0, tot_area = 0.0;
|
||||
point_t center_pt = VINIT_ZERO;
|
||||
fastf_t tot_vol = -1.0, tot_area = 0.0;
|
||||
point_t center_pt;
|
||||
struct poly_face face = POLY_FACE_INIT_ZERO;
|
||||
struct rt_arb_internal earb;
|
||||
struct rt_arb_internal *arb = (struct rt_arb_internal *)ip->idb_ptr;
|
||||
@@ -90,7 +92,7 @@ analyze_arb8(struct ged *gedp, const struct rt_db_internal *ip)
|
||||
/* find the specific arb type, in GIFT order. */
|
||||
if ((cgtype = rt_arb_std_type(ip, &wdbp->wdb_tol)) == 0) {
|
||||
bu_vls_printf(gedp->ged_result_str, "analyze_arb: bad ARB\n");
|
||||
return;
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
type = cgtype - 4;
|
||||
@@ -101,7 +103,18 @@ analyze_arb8(struct ged *gedp, const struct rt_db_internal *ip)
|
||||
|
||||
/* TABLE 1 =========================================== */
|
||||
/* analyze each face, use center point of arb for reference */
|
||||
VSETALL(center_pt, -1.0);
|
||||
rt_arb_centroid(¢er_pt, ip);
|
||||
{
|
||||
point_t error_centroid;
|
||||
VSETALL(error_centroid, -1.0);
|
||||
if (!std::isfinite(center_pt[X]) || !std::isfinite(center_pt[Y]) ||
|
||||
!std::isfinite(center_pt[Z]) ||
|
||||
VNEAR_EQUAL(center_pt, error_centroid, SMALL_FASTF)) {
|
||||
bu_vls_printf(gedp->ged_result_str, "analyze_arb: unable to calculate centroid\n");
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* allocate pts array, maximum 4 verts per arb8 face */
|
||||
face.pts = (point_t *)bu_calloc(4, sizeof(point_t), "analyze_arb8: pts");
|
||||
@@ -185,6 +198,12 @@ analyze_arb8(struct ged *gedp, const struct rt_db_internal *ip)
|
||||
|
||||
if (OBJ[ID_ARB8].ft_volume)
|
||||
OBJ[ID_ARB8].ft_volume(&tot_vol, ip);
|
||||
if (!std::isfinite(tot_vol) || NEAR_EQUAL(tot_vol, -1.0, SMALL_FASTF)) {
|
||||
bu_vls_printf(gedp->ged_result_str, "analyze_arb: unable to calculate volume\n");
|
||||
bu_free((char *)face.pts, "analyze_arb8: pts");
|
||||
bu_free((char *)table.rows, "analyze_arb8: rows");
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
print_volume_table(gedp,
|
||||
tot_vol
|
||||
@@ -199,6 +218,7 @@ analyze_arb8(struct ged *gedp, const struct rt_db_internal *ip)
|
||||
|
||||
bu_free((char *)face.pts, "analyze_arb8: pts");
|
||||
bu_free((char *)table.rows, "analyze_arb8: rows");
|
||||
return BRLCAD_OK;
|
||||
}
|
||||
// Local Variables:
|
||||
// tab-width: 8
|
||||
|
||||
@@ -94,7 +94,7 @@ extern void
|
||||
print_faces_table(struct ged *gedp, table_t *table);
|
||||
|
||||
|
||||
extern void
|
||||
extern int
|
||||
analyze_arb8(struct ged *gedp, const struct rt_db_internal *ip);
|
||||
|
||||
extern void
|
||||
@@ -106,10 +106,10 @@ analyze_ars(struct ged *gedp, const struct rt_db_internal *ip);
|
||||
extern void
|
||||
analyze_superell(struct ged *gedp, const struct rt_db_internal *ip);
|
||||
|
||||
extern void
|
||||
extern int
|
||||
analyze_sketch(struct ged *gedp, const struct rt_db_internal *ip);
|
||||
|
||||
extern void
|
||||
extern int
|
||||
analyze_general(struct ged *gedp, const struct rt_db_internal *ip);
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "vmath.h"
|
||||
|
||||
#include "rt/geom.h"
|
||||
@@ -32,30 +34,46 @@
|
||||
#include "../ged_private.h"
|
||||
#include "./ged_analyze.h"
|
||||
|
||||
void
|
||||
int
|
||||
analyze_sketch(struct ged *gedp, const struct rt_db_internal *ip)
|
||||
{
|
||||
fastf_t area = -1;
|
||||
const fastf_t metric_error = -1.0;
|
||||
fastf_t area = metric_error;
|
||||
point_t centroid;
|
||||
point_t error_centroid;
|
||||
int status = BRLCAD_OK;
|
||||
|
||||
if (OBJ[ID_SKETCH].ft_surf_area)
|
||||
if (OBJ[ID_SKETCH].ft_surf_area) {
|
||||
OBJ[ID_SKETCH].ft_surf_area(&area, ip);
|
||||
|
||||
if (area > 0.0) {
|
||||
bu_vls_printf(gedp->ged_result_str, "\nTotal Area: %10.8f",
|
||||
area
|
||||
* gedp->dbip->dbi_local2base
|
||||
* gedp->dbip->dbi_local2base
|
||||
);
|
||||
if (!std::isfinite(area) || NEAR_EQUAL(area, metric_error, SMALL_FASTF)) {
|
||||
bu_vls_printf(gedp->ged_result_str, "\nTotal Area: COULD NOT DETERMINE");
|
||||
status = BRLCAD_ERROR;
|
||||
} else {
|
||||
bu_vls_printf(gedp->ged_result_str, "\nTotal Area: %10.8f",
|
||||
area
|
||||
* gedp->dbip->dbi_local2base
|
||||
* gedp->dbip->dbi_local2base
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (OBJ[ID_SKETCH].ft_centroid) {
|
||||
VSETALL(centroid, metric_error);
|
||||
VSETALL(error_centroid, metric_error);
|
||||
OBJ[ID_SKETCH].ft_centroid(¢roid, ip);
|
||||
bu_vls_printf(gedp->ged_result_str, "\n Centroid: (%g, %g, %g)\n",
|
||||
centroid[X] * gedp->dbip->dbi_base2local,
|
||||
centroid[Y] * gedp->dbip->dbi_base2local,
|
||||
centroid[Z] * gedp->dbip->dbi_base2local);
|
||||
if (std::isfinite(centroid[X]) && std::isfinite(centroid[Y]) &&
|
||||
std::isfinite(centroid[Z]) &&
|
||||
!VNEAR_EQUAL(centroid, error_centroid, SMALL_FASTF)) {
|
||||
bu_vls_printf(gedp->ged_result_str, "\n Centroid: (%g, %g, %g)\n",
|
||||
centroid[X] * gedp->dbip->dbi_base2local,
|
||||
centroid[Y] * gedp->dbip->dbi_base2local,
|
||||
centroid[Z] * gedp->dbip->dbi_base2local);
|
||||
} else {
|
||||
bu_vls_printf(gedp->ged_result_str, "\n Centroid: COULD NOT DETERMINE\n");
|
||||
status = BRLCAD_ERROR;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
// Local Variables:
|
||||
|
||||
@@ -32,6 +32,7 @@ extern "C" {
|
||||
}
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
extern "C" {
|
||||
@@ -41,6 +42,28 @@ extern "C" {
|
||||
#include "./ged_analyze.h"
|
||||
#include "../ged_private.h"
|
||||
|
||||
static const fastf_t analyze_metric_error = -1.0;
|
||||
|
||||
|
||||
static bool
|
||||
analyze_metric_failed(fastf_t value)
|
||||
{
|
||||
return !std::isfinite(value) || NEAR_EQUAL(value, analyze_metric_error, SMALL_FASTF);
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
analyze_centroid_failed(const point_t centroid)
|
||||
{
|
||||
point_t error_centroid;
|
||||
|
||||
VSETALL(error_centroid, analyze_metric_error);
|
||||
return !std::isfinite(centroid[X]) || !std::isfinite(centroid[Y]) ||
|
||||
!std::isfinite(centroid[Z]) ||
|
||||
VNEAR_EQUAL(centroid, error_centroid, SMALL_FASTF);
|
||||
}
|
||||
|
||||
|
||||
void get_dashes(field_t *f, const int ndashes)
|
||||
{
|
||||
int i;
|
||||
@@ -648,27 +671,38 @@ analyze_poly_face(struct ged *gedp, struct poly_face *face, row_t *row)
|
||||
* - part
|
||||
* - rhc
|
||||
*/
|
||||
void
|
||||
int
|
||||
analyze_general(struct ged *gedp, const struct rt_db_internal *ip)
|
||||
{
|
||||
fastf_t vol, area;
|
||||
point_t centroid;
|
||||
int status = BRLCAD_OK;
|
||||
|
||||
vol = area = -1.0;
|
||||
vol = area = analyze_metric_error;
|
||||
VSETALL(centroid, analyze_metric_error);
|
||||
|
||||
if (OBJ[ip->idb_minor_type].ft_volume) {
|
||||
OBJ[ip->idb_minor_type].ft_volume(&vol, ip);
|
||||
if (analyze_metric_failed(vol))
|
||||
status = BRLCAD_ERROR;
|
||||
}
|
||||
if (OBJ[ip->idb_minor_type].ft_surf_area) {
|
||||
OBJ[ip->idb_minor_type].ft_surf_area(&area, ip);
|
||||
if (analyze_metric_failed(area))
|
||||
status = BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
if (OBJ[ip->idb_minor_type].ft_centroid) {
|
||||
OBJ[ip->idb_minor_type].ft_centroid(¢roid, ip);
|
||||
bu_vls_printf(gedp->ged_result_str, "\n Centroid: (%g, %g, %g)\n",
|
||||
centroid[X] * gedp->dbip->dbi_base2local,
|
||||
centroid[Y] * gedp->dbip->dbi_base2local,
|
||||
centroid[Z] * gedp->dbip->dbi_base2local);
|
||||
if (!analyze_centroid_failed(centroid)) {
|
||||
bu_vls_printf(gedp->ged_result_str, "\n Centroid: (%g, %g, %g)\n",
|
||||
centroid[X] * gedp->dbip->dbi_base2local,
|
||||
centroid[Y] * gedp->dbip->dbi_base2local,
|
||||
centroid[Z] * gedp->dbip->dbi_base2local);
|
||||
} else {
|
||||
bu_vls_printf(gedp->ged_result_str, "\n Centroid: COULD NOT DETERMINE\n");
|
||||
status = BRLCAD_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
print_volume_table(gedp,
|
||||
@@ -681,6 +715,7 @@ analyze_general(struct ged *gedp, const struct rt_db_internal *ip)
|
||||
* gedp->dbip->dbi_base2local,
|
||||
vol/GALLONS_TO_MM3
|
||||
);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -694,4 +729,3 @@ analyze_general(struct ged *gedp, const struct rt_db_internal *ip)
|
||||
// c-file-style: "stroustrup"
|
||||
// End:
|
||||
// ex: shiftwidth=4 tabstop=8
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bu/cmd.h"
|
||||
@@ -271,7 +272,13 @@ ged_bb_core(struct ged *gedp, int argc, const char *argv[])
|
||||
}
|
||||
|
||||
if (print_vol == 1) {
|
||||
vol = -1.0;
|
||||
new_intern.idb_meth->ft_volume(&vol, &new_intern);
|
||||
if (!isfinite(vol) || NEAR_EQUAL(vol, -1.0, SMALL_FASTF)) {
|
||||
bu_vls_printf(gedp->ged_result_str, "Unable to calculate bounding box volume.\n");
|
||||
rt_db_free_internal(&new_intern);
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
/* convert to local units */
|
||||
vol *= pow(gedp->dbip->dbi_base2local,3.0);
|
||||
bu_vls_printf(gedp->ged_result_str, "Bounding Box Volume: %g %s^3\n", vol, str);
|
||||
|
||||
@@ -1048,4 +1048,3 @@ draw_gather_paths(struct db_full_path *path, mat_t *curr_mat, void *client_data)
|
||||
// c-file-style: "stroustrup"
|
||||
// End:
|
||||
// ex: shiftwidth=4 tabstop=8
|
||||
|
||||
|
||||
@@ -340,7 +340,7 @@ append_solid_to_display_list(
|
||||
VSETALL(sp->s_center, 0.0);
|
||||
|
||||
if (ip->idb_meth->ft_bbox) {
|
||||
if (ip->idb_meth->ft_bbox(ip, &min, &max, tsp->ts_tol) < 0) {
|
||||
if (ip->idb_meth->ft_bbox(ip, &min, &max, tsp->ts_tol) < 0) {
|
||||
if (pathp && DB_FULL_PATH_CUR_DIR(pathp)) {
|
||||
bu_log("%s: plot failure\n", DB_FULL_PATH_CUR_DIR(pathp)->d_namep);
|
||||
} else {
|
||||
@@ -1781,4 +1781,3 @@ GED_DECLARE_PLUGIN_MANIFEST("libged_draw", 1, GED_DRAW_COMMANDS)
|
||||
* End:
|
||||
* ex: shiftwidth=4 tabstop=8
|
||||
*/
|
||||
|
||||
|
||||
@@ -268,7 +268,18 @@ arbin(struct ged *gedp,
|
||||
RT_ARB_CK_MAGIC(arb);
|
||||
|
||||
/* find reference point (center_pt[3]) to find direction of normals */
|
||||
VSETALL(center_pt, -1.0);
|
||||
rt_arb_centroid(¢er_pt, ip);
|
||||
{
|
||||
point_t error_centroid;
|
||||
VSETALL(error_centroid, -1.0);
|
||||
if (!isfinite(center_pt[X]) || !isfinite(center_pt[Y]) ||
|
||||
!isfinite(center_pt[Z]) ||
|
||||
VNEAR_EQUAL(center_pt, error_centroid, SMALL_FASTF)) {
|
||||
bu_vls_printf(gedp->ged_result_str, "cannot determine ARB centroid\n");
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (cgtype == ARB7)
|
||||
return arb7in(gedp, ip, thick, planes, center_pt, tol);
|
||||
|
||||
@@ -69,6 +69,7 @@ lint_data::summary()
|
||||
double vol_err_pct = -1.0;
|
||||
};
|
||||
std::vector<rt_entry> rt_mismatches;
|
||||
std::vector<rt_entry> rt_analytic_failed;
|
||||
std::vector<rt_entry> rt_facetize_failed;
|
||||
std::vector<rt_entry> rt_skips;
|
||||
std::vector<rt_entry> rt_ok;
|
||||
@@ -99,6 +100,8 @@ lint_data::summary()
|
||||
rt_ok.push_back(e);
|
||||
} else if (ptype == std::string("raytrace_mismatch")) {
|
||||
rt_mismatches.push_back(e);
|
||||
} else if (ptype == std::string("raytrace_analytic_failed")) {
|
||||
rt_analytic_failed.push_back(e);
|
||||
} else if (ptype == std::string("raytrace_facetize_failed")) {
|
||||
rt_facetize_failed.push_back(e);
|
||||
} else if (ptype == std::string("raytrace_skip")) {
|
||||
@@ -151,7 +154,7 @@ lint_data::summary()
|
||||
|
||||
/* ---- Raytrace results section ---- */
|
||||
size_t rt_n_ok = rt_ok.size();
|
||||
size_t rt_n_fail = rt_mismatches.size();
|
||||
size_t rt_n_fail = rt_mismatches.size() + rt_analytic_failed.size();
|
||||
size_t rt_n_skip = rt_skips.size() + rt_facetize_failed.size();
|
||||
if (rt_n_ok + rt_n_fail + rt_n_skip > 0) {
|
||||
ostr.append(std::string("Raytrace validation: "));
|
||||
@@ -188,6 +191,17 @@ lint_data::summary()
|
||||
}
|
||||
}
|
||||
|
||||
if (!rt_analytic_failed.empty()) {
|
||||
ostr.append(std::string("Analytic metric failures:\n"));
|
||||
for (const auto &e : rt_analytic_failed) {
|
||||
ostr.append(std::string("\t") + e.name);
|
||||
ostr.append(std::string(" [") + e.type + std::string("]"));
|
||||
if (!e.reason.empty())
|
||||
ostr.append(std::string(" (") + e.reason + std::string(")"));
|
||||
ostr.append(std::string("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!rt_facetize_failed.empty()) {
|
||||
ostr.append(std::string("Raytrace checks skipped (facetize failed):\n"));
|
||||
for (const auto &e : rt_facetize_failed)
|
||||
@@ -517,6 +531,7 @@ ged_lint_core(struct ged *gedp, int argc, const char *argv[])
|
||||
/* Only include raytrace failures, not passing or skipped objects */
|
||||
if (!ptype.compare(0, 9, std::string("raytrace_"))) {
|
||||
if (ptype != std::string("raytrace_mismatch") &&
|
||||
ptype != std::string("raytrace_analytic_failed") &&
|
||||
ptype != std::string("raytrace_facetize_failed"))
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ enum rt_result_t {
|
||||
RT_RESULT_UNKNOWN = 0,
|
||||
RT_RESULT_OK,
|
||||
RT_RESULT_MISMATCH,
|
||||
RT_RESULT_ANALYTIC_FAILED,
|
||||
RT_RESULT_FACETIZE_FAILED,
|
||||
RT_RESULT_SKIP_HALFSPACE,
|
||||
RT_RESULT_SKIP_CHILD_FAIL,
|
||||
@@ -105,6 +106,22 @@ struct obj_rt_info {
|
||||
};
|
||||
|
||||
static const double EMPTY_METRIC_TOL = 1.0e-9;
|
||||
static const double ANALYTIC_METRIC_ERROR = -1.0;
|
||||
|
||||
static bool
|
||||
analytic_metric_failed(double value)
|
||||
{
|
||||
return !std::isfinite(value) ||
|
||||
NEAR_EQUAL(value, ANALYTIC_METRIC_ERROR, SMALL_FASTF);
|
||||
}
|
||||
|
||||
static double
|
||||
metric_relative_error(double measured, double reference)
|
||||
{
|
||||
if (std::fabs(reference) <= EMPTY_METRIC_TOL)
|
||||
return (std::fabs(measured) <= EMPTY_METRIC_TOL) ? 0.0 : 1.0;
|
||||
return std::fabs(measured - reference) / std::fabs(reference);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Tree-walk helpers */
|
||||
@@ -445,7 +462,13 @@ check_primitive(lint_data *ldata, struct directory *dp,
|
||||
const char *name = dp->d_namep;
|
||||
|
||||
/* Query the primitive's analytic SA and volume when available */
|
||||
double analytic_sa = -1.0, analytic_vol = -1.0;
|
||||
double analytic_sa = ANALYTIC_METRIC_ERROR;
|
||||
double analytic_vol = ANALYTIC_METRIC_ERROR;
|
||||
bool have_analytic_sa = false;
|
||||
bool have_analytic_vol = false;
|
||||
bool analytic_sa_failed = false;
|
||||
bool analytic_vol_failed = false;
|
||||
bool analytic_query_bombed = false;
|
||||
{
|
||||
struct rt_db_internal intern;
|
||||
RT_DB_INTERNAL_INIT(&intern);
|
||||
@@ -454,11 +477,22 @@ check_primitive(lint_data *ldata, struct directory *dp,
|
||||
const struct rt_functab *ft = &OBJ[intern.idb_minor_type];
|
||||
|
||||
if (!BU_SETJUMP) {
|
||||
if (ft->ft_surf_area) ft->ft_surf_area(&analytic_sa, &intern);
|
||||
if (ft->ft_volume) ft->ft_volume(&analytic_vol, &intern);
|
||||
if (ft->ft_surf_area) {
|
||||
ft->ft_surf_area(&analytic_sa, &intern);
|
||||
analytic_sa_failed = analytic_metric_failed(analytic_sa);
|
||||
have_analytic_sa = !analytic_sa_failed;
|
||||
}
|
||||
if (ft->ft_volume) {
|
||||
ft->ft_volume(&analytic_vol, &intern);
|
||||
analytic_vol_failed = analytic_metric_failed(analytic_vol);
|
||||
have_analytic_vol = !analytic_vol_failed;
|
||||
}
|
||||
} else {
|
||||
BU_UNSETJUMP;
|
||||
analytic_sa = analytic_vol = -1.0;
|
||||
analytic_sa = analytic_vol = ANALYTIC_METRIC_ERROR;
|
||||
have_analytic_sa = have_analytic_vol = false;
|
||||
analytic_sa_failed = analytic_vol_failed = false;
|
||||
analytic_query_bombed = true;
|
||||
}
|
||||
BU_UNSETJUMP;
|
||||
}
|
||||
@@ -466,19 +500,39 @@ check_primitive(lint_data *ldata, struct directory *dp,
|
||||
}
|
||||
}
|
||||
|
||||
/* Run Crofton on the CSG primitive */
|
||||
double csa = -1.0, cvol = -1.0;
|
||||
int cret = crofton_on_obj(gedp->dbip, name, ldata->rt_crofton_rays, csa, cvol);
|
||||
|
||||
info.analytic_sa = analytic_sa;
|
||||
info.analytic_vol = analytic_vol;
|
||||
info.crofton_sa = csa;
|
||||
info.crofton_vol = cvol;
|
||||
|
||||
nlohmann::json jentry;
|
||||
jentry["object_name"] = name;
|
||||
jentry["object_type"] = "primitive";
|
||||
|
||||
if (analytic_query_bombed || analytic_sa_failed || analytic_vol_failed) {
|
||||
info.result = RT_RESULT_ANALYTIC_FAILED;
|
||||
jentry["problem_type"] = "raytrace_analytic_failed";
|
||||
if (analytic_query_bombed)
|
||||
jentry["reason"] = "analytic_metric_query_bombed";
|
||||
else if (analytic_sa_failed && analytic_vol_failed)
|
||||
jentry["reason"] = "analytic_surface_area_and_volume_failed";
|
||||
else if (analytic_sa_failed)
|
||||
jentry["reason"] = "analytic_surface_area_failed";
|
||||
else
|
||||
jentry["reason"] = "analytic_volume_failed";
|
||||
if (have_analytic_sa)
|
||||
jentry["analytic_sa"] = analytic_sa;
|
||||
if (have_analytic_vol)
|
||||
jentry["analytic_vol"] = analytic_vol;
|
||||
ldata->j.push_back(jentry);
|
||||
return;
|
||||
}
|
||||
|
||||
/* A callback failure is definitive and must not be hidden by a later
|
||||
* raytrace preparation failure. */
|
||||
double csa = -1.0, cvol = -1.0;
|
||||
int cret = crofton_on_obj(gedp->dbip, name, ldata->rt_crofton_rays, csa, cvol);
|
||||
info.crofton_sa = csa;
|
||||
info.crofton_vol = cvol;
|
||||
|
||||
if (cret != 0) {
|
||||
info.result = RT_RESULT_SKIP_PREP_FAIL;
|
||||
jentry["problem_type"] = "raytrace_skip";
|
||||
@@ -490,10 +544,10 @@ check_primitive(lint_data *ldata, struct directory *dp,
|
||||
jentry["crofton_sa"] = csa;
|
||||
jentry["crofton_vol"] = cvol;
|
||||
|
||||
bool has_analytic = (analytic_sa > 0.0 && analytic_vol > 0.0);
|
||||
bool has_analytic = have_analytic_sa && have_analytic_vol;
|
||||
if (has_analytic) {
|
||||
double sa_err = std::fabs(csa - analytic_sa) / analytic_sa;
|
||||
double vol_err = std::fabs(cvol - analytic_vol) / analytic_vol;
|
||||
double sa_err = metric_relative_error(csa, analytic_sa);
|
||||
double vol_err = metric_relative_error(cvol, analytic_vol);
|
||||
|
||||
info.ref_sa = analytic_sa;
|
||||
info.ref_vol = analytic_vol;
|
||||
@@ -526,6 +580,10 @@ check_primitive(lint_data *ldata, struct directory *dp,
|
||||
info.result = RT_RESULT_OK;
|
||||
jentry["problem_type"] = "raytrace_ok";
|
||||
jentry["reason"] = "no_analytic_reference";
|
||||
if (have_analytic_sa)
|
||||
jentry["analytic_sa"] = analytic_sa;
|
||||
if (have_analytic_vol)
|
||||
jentry["analytic_vol"] = analytic_vol;
|
||||
}
|
||||
|
||||
ldata->j.push_back(jentry);
|
||||
@@ -604,8 +662,8 @@ check_comb(lint_data *ldata, struct directory *dp,
|
||||
|
||||
/* Relative error uses the BoT (exact mesh) as the reference denominator,
|
||||
* consistent with how primitives use the analytic formula as denominator. */
|
||||
double sa_err = (bsa > 0.0) ? std::fabs(csa - bsa) / bsa : 1.0;
|
||||
double vol_err = (bvol > 0.0) ? std::fabs(cvol - bvol) / bvol : 1.0;
|
||||
double sa_err = metric_relative_error(csa, bsa);
|
||||
double vol_err = metric_relative_error(cvol, bvol);
|
||||
info.sa_err_pct = sa_err * 100.0;
|
||||
info.vol_err_pct = vol_err * 100.0;
|
||||
jentry["sa_err_pct"] = info.sa_err_pct;
|
||||
@@ -750,6 +808,7 @@ _ged_raytrace_check(lint_data *ldata)
|
||||
if (!universe.count(cname)) continue;
|
||||
rt_result_t cr = universe[cname].result;
|
||||
if (cr == RT_RESULT_MISMATCH ||
|
||||
cr == RT_RESULT_ANALYTIC_FAILED ||
|
||||
cr == RT_RESULT_FACETIZE_FAILED ||
|
||||
cr == RT_RESULT_SKIP_PREP_FAIL ||
|
||||
cr == RT_RESULT_SKIP_CHILD_FAIL) {
|
||||
@@ -777,6 +836,7 @@ _ged_raytrace_check(lint_data *ldata)
|
||||
switch (info.result) {
|
||||
case RT_RESULT_OK: n_pass++; break;
|
||||
case RT_RESULT_MISMATCH: n_fail++; break;
|
||||
case RT_RESULT_ANALYTIC_FAILED: n_fail++; break;
|
||||
default: n_skip++; break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1042,27 +1042,11 @@ tree_update_walk(
|
||||
// If there is a non-IDN matrix, this is where we apply it
|
||||
// First step, check for validity.
|
||||
//
|
||||
// Note: tor has a specific restriction in that it needs a uniform
|
||||
// matrix - if I'm deciphering the code in rt_tor_import5
|
||||
// correctly, it handles this by simply ignoring a non-conforming
|
||||
// matrix on import. As currently implemented, this limitation
|
||||
// applies whether the matrix is above the torus in a given tree or
|
||||
// an attempt is made to apply the matrix directly to the solid's
|
||||
// parameters.
|
||||
//
|
||||
// Because the same bn_mat_is_non_unif matrix is ignored for a tor
|
||||
// regardless of where the problem matrix is in the tree, trying to
|
||||
// apply this matrix to the tor is not currently considered a
|
||||
// "failure" - previously a "push" operation on such a tor would
|
||||
// complete, clear the matrix from the tree, and simply skip
|
||||
// applying the updates to the torus. This certainly is a failure
|
||||
// to make the specified changes to the torus, but since the desired
|
||||
// shape was never achieved in the tree with or without the matrix
|
||||
// above it, the geometry the raytracer reports has not changed and
|
||||
// the realized shape pre and post push is consistent.
|
||||
//
|
||||
// Accordingly, we're deliberately NOT checking for the tor+non_unif
|
||||
// case in this validation step.
|
||||
// Some analytic solids preserve non-uniform scaling in their v5
|
||||
// attributes when the scale cannot be absorbed into body
|
||||
// parameters. The tree-level matrix still needs to satisfy the
|
||||
// same perpendicular-axis constraints used elsewhere before it is
|
||||
// handed to the solid importer.
|
||||
if (bn_mat_ck(dpi.dp->d_namep, dpi.mat) < 0) {
|
||||
if (s->msgs) {
|
||||
char *ps = db_path_to_string(dfp);
|
||||
|
||||
@@ -192,7 +192,7 @@ pull_leaf(struct db_i *dbip, struct directory *dp, void *mp)
|
||||
* it then passes the minimum and maximum point to translate()
|
||||
* which then extracts the translation.
|
||||
*/
|
||||
(intern.idb_meth)->ft_bbox(&intern, &min, &max, &tol);
|
||||
intern.idb_meth->ft_bbox(&intern, &min, &max, &tol);
|
||||
|
||||
/* pulls primitive translation matrix copying inverse
|
||||
* transformation to restore primitive and bbox
|
||||
|
||||
@@ -145,6 +145,7 @@ set(
|
||||
mater.c
|
||||
memalloc.c
|
||||
mkbundle.c
|
||||
nonuniform.c
|
||||
op.c
|
||||
pr.c
|
||||
prep.cpp
|
||||
|
||||
+6
-2
@@ -450,12 +450,16 @@ rt_bound_instance(point_t *bmin, point_t *bmax,
|
||||
*/
|
||||
struct bu_list vhead;
|
||||
BU_LIST_INIT(&(vhead));
|
||||
if (ip->idb_meth->ft_plot(&vhead, ip, ttol, tol, NULL) >= 0) {
|
||||
if (bv_vlist_bbox(&vhead, bmin, bmax, NULL, NULL)) {
|
||||
if (rt_obj_plot(&vhead, ip, ttol, tol) >= 0) {
|
||||
int bbox_ret = bv_vlist_bbox(&vhead, bmin, bmax, NULL, NULL);
|
||||
BV_FREE_VLIST(&rt_vlfree, &vhead);
|
||||
if (bbox_ret) {
|
||||
rt_db_free_internal(&dbintern);
|
||||
return -1;
|
||||
}
|
||||
bbret = 0;
|
||||
} else {
|
||||
BV_FREE_VLIST(&rt_vlfree, &vhead);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1354,8 +1354,11 @@ comb_crofton_sample(const struct rt_db_internal *ip, double *out_sa, double *out
|
||||
double sa = 0.0;
|
||||
double vol = 0.0;
|
||||
/* Use default params (NULL → 2 000-ray convergence loop) */
|
||||
(void)rt_crofton_shoot(&sa, &vol, NULL, NULL, NULL, NULL, NULL,
|
||||
rtip, NULL, NULL, NULL);
|
||||
if (rt_crofton_shoot(&sa, &vol, NULL, NULL, NULL, NULL, NULL,
|
||||
rtip, NULL, NULL, NULL) < 0) {
|
||||
rt_i_destroy(rtip);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (out_sa) *out_sa = sa;
|
||||
if (out_vol) *out_vol = vol;
|
||||
|
||||
@@ -65,6 +65,8 @@
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
#define RT_MATRIX_NONUNIFORM_ATTR "matrix:nonuniform"
|
||||
|
||||
struct db_i_internal {
|
||||
uint32_t dbi_magic;
|
||||
|
||||
@@ -109,6 +111,46 @@ struct db_i_internal {
|
||||
struct db_i_internal * db_i_internal_create(void);
|
||||
void db_i_internal_destroy(struct db_i_internal *i);
|
||||
|
||||
int _rt_nonuniform_mat_validate(const mat_t mat);
|
||||
int _rt_nonuniform_attr_get(mat_t mat, const struct rt_db_internal *ip);
|
||||
int _rt_nonuniform_attr_set(struct rt_db_internal *ip, const mat_t mat);
|
||||
int _rt_nonuniform_attr_remove(struct rt_db_internal *ip);
|
||||
int _rt_nonuniform_attr_compose(struct rt_db_internal *ip, const mat_t mat);
|
||||
int _rt_nonuniform_attr_copy(struct rt_db_internal *op, const struct rt_db_internal *ip);
|
||||
int _rt_nonuniform_export4_check(const char *func, const struct rt_db_internal *ip);
|
||||
int _rt_nonuniform_transform_needed(const struct rt_db_internal *ip, const mat_t mat);
|
||||
int _rt_nonuniform_transform_resolve(mat_t effective, int *remove_attr, const struct rt_db_internal *ip, const mat_t mat);
|
||||
fastf_t _rt_nonuniform_volume_scale(const mat_t mat);
|
||||
int _rt_nonuniform_body_bbox(const struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *tol);
|
||||
/* Attribute adapters return zero when the primitive should continue with its
|
||||
* body-space implementation, positive when handled, and negative on failure.
|
||||
* Metric failures leave the functab's -1 output sentinel in place. */
|
||||
int _rt_nonuniform_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *tol);
|
||||
int _rt_nonuniform_volume(fastf_t *volume, const struct rt_db_internal *ip);
|
||||
int _rt_nonuniform_surf_area(fastf_t *area, const struct rt_db_internal *ip);
|
||||
int _rt_nonuniform_centroid(point_t *cent, const struct rt_db_internal *ip);
|
||||
int _rt_nonuniform_prep_finalize(struct soltab *stp, const struct rt_db_internal *ip, const struct bn_tol *tol);
|
||||
int _rt_nonuniform_tess_finalize(struct nmgregion *r, const struct rt_db_internal *ip, const struct bn_tol *tol);
|
||||
|
||||
struct rt_nonuniform_vlist_state {
|
||||
struct bv_vlist *last;
|
||||
size_t nused;
|
||||
};
|
||||
|
||||
void _rt_nonuniform_vlist_state_init(struct rt_nonuniform_vlist_state *state, struct bu_list *vhead);
|
||||
int _rt_nonuniform_plot_finalize(struct bu_list *vhead, const struct rt_nonuniform_vlist_state *state, const struct rt_db_internal *ip);
|
||||
int _rt_nonuniform_soltab_setup(struct soltab *stp, const mat_t mat, const struct bn_tol *tol);
|
||||
void _rt_nonuniform_soltab_free(struct soltab *stp);
|
||||
int _rt_nonuniform_shot(struct soltab *stp, struct xray *rp, struct application *ap, struct seg *seghead);
|
||||
int _rt_nonuniform_norm(struct hit *hitp, struct soltab *stp, struct xray *rp);
|
||||
int _rt_nonuniform_uv(struct application *ap, struct soltab *stp, struct hit *hitp, struct uvcoord *uvp);
|
||||
int _rt_nonuniform_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp);
|
||||
void _rt_nonuniform_transform_bbox(point_t *omin, point_t *omax, const mat_t mat, const point_t imin, const point_t imax);
|
||||
void _rt_nonuniform_transform_nmgregion(struct nmgregion *r, const mat_t mat, const struct bn_tol *tol);
|
||||
|
||||
void rt_crofton_surf_area_implicit(fastf_t *area, const struct rt_db_internal *ip);
|
||||
void rt_crofton_volume_implicit(fastf_t *volume, const struct rt_db_internal *ip);
|
||||
|
||||
|
||||
/**
|
||||
* Private internal state for struct rt_i. All fields listed under
|
||||
@@ -129,6 +171,7 @@ struct rt_i_internal {
|
||||
/* Per-type solid tables (filled during prep) */
|
||||
struct soltab ** rti_sol_by_type[ID_MAX_SOLID+1];
|
||||
size_t rti_nsol_by_type[ID_MAX_SOLID+1];
|
||||
unsigned char rti_has_nonuniform_by_type[ID_MAX_SOLID+1];
|
||||
size_t rti_maxsol_by_type;
|
||||
struct soltab ** rti_Solids; /**< @brief ptrs to soltab [st_bit] */
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -391,6 +391,7 @@ rt_prep_parallel(struct rt_i *rtip, int ncpu)
|
||||
}
|
||||
/* Malloc the storage and zero the counts */
|
||||
for (i=0; i <= ID_MAX_SOLID; i++) {
|
||||
rtip->i->rti_has_nonuniform_by_type[i] = 0;
|
||||
if (rtip->i->rti_nsol_by_type[i] <= 0)
|
||||
continue;
|
||||
rtip->i->rti_sol_by_type[i] = (struct soltab **)bu_calloc(rtip->i->rti_nsol_by_type[i], sizeof(struct soltab *), "rti_sol_by_type[]");
|
||||
@@ -401,6 +402,8 @@ rt_prep_parallel(struct rt_i *rtip, int ncpu)
|
||||
int id;
|
||||
id = stp->st_id;
|
||||
rtip->i->rti_sol_by_type[id][rtip->i->rti_nsol_by_type[id]++] = stp;
|
||||
if (stp->st_nu_inv_matp)
|
||||
rtip->i->rti_has_nonuniform_by_type[id] = 1;
|
||||
} RT_VISIT_ALL_SOLTABS_END;
|
||||
if (RT_G_DEBUG & (RT_DEBUG_DB|RT_DEBUG_SOLIDS)) {
|
||||
bu_log("rt_prep_parallel(%s, %d) printing number of primitives by type\n",
|
||||
@@ -1198,6 +1201,7 @@ rt_clean(struct rt_i *rtip)
|
||||
|
||||
/* Free array of solid table pointers indexed by solid ID */
|
||||
for (i=0; i <= ID_MAX_SOLID; i++) {
|
||||
rtip->i->rti_has_nonuniform_by_type[i] = 0;
|
||||
if (rtip->i->rti_nsol_by_type[i] <= 0)
|
||||
continue;
|
||||
if (rtip->i->rti_sol_by_type[i]) {
|
||||
@@ -1929,6 +1933,7 @@ rt_reprep(struct rt_i *rtip, struct rt_reprep_obj_list *objs)
|
||||
bu_free(rtip->i->rti_sol_by_type[id], "rti_sol_by_type");
|
||||
rtip->i->rti_sol_by_type[id] = NULL;
|
||||
rtip->i->rti_nsol_by_type[id] = 0;
|
||||
rtip->i->rti_has_nonuniform_by_type[id] = 0;
|
||||
}
|
||||
RT_VISIT_ALL_SOLTABS_START(stp, rtip) {
|
||||
rtip->i->rti_nsol_by_type[stp->st_id]++;
|
||||
@@ -1946,6 +1951,8 @@ rt_reprep(struct rt_i *rtip, struct rt_reprep_obj_list *objs)
|
||||
RT_VISIT_ALL_SOLTABS_START(stp, rtip) {
|
||||
int id = stp->st_id;
|
||||
rtip->i->rti_sol_by_type[id][rtip->i->rti_nsol_by_type[id]++] = stp;
|
||||
if (stp->st_nu_inv_matp)
|
||||
rtip->i->rti_has_nonuniform_by_type[id] = 1;
|
||||
} RT_VISIT_ALL_SOLTABS_END;
|
||||
|
||||
for (i=0; i<BU_PTBL_LEN(&rtip->i->rti_new_solids); i++) {
|
||||
|
||||
@@ -2149,6 +2149,9 @@ rt_bot_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
RT_BOT_CK_MAGIC(bot_ip);
|
||||
|
||||
rt_bot_condense(bot_ip);
|
||||
VSETALL(*cent, -1.0);
|
||||
if (bot_ip->num_vertices == 0)
|
||||
return;
|
||||
VSETALL(*cent, 0.0);
|
||||
for (i = 0; i < bot_ip->num_vertices; i++) {
|
||||
VADD2(*cent, *cent, &bot_ip->vertices[i*3]);
|
||||
@@ -2179,8 +2182,12 @@ rt_bot_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, co
|
||||
size_t faces[RT_BOT_TESS_MAX_FACES];
|
||||
plane_t planes[RT_BOT_TESS_MAX_FACES];
|
||||
point_t center;
|
||||
point_t error_center;
|
||||
|
||||
VSETALL(error_center, -1.0);
|
||||
rt_bot_centroid(¢er, ip);
|
||||
if (VNEAR_EQUAL(center, error_center, SMALL_FASTF))
|
||||
return -1;
|
||||
bu_log("center pt = (%f %f %f)\n", V3ARGS(center));
|
||||
|
||||
/* get the faces that use each vertex */
|
||||
@@ -6029,9 +6036,10 @@ rt_bot_volume(fastf_t *volume, const struct rt_db_internal *ip)
|
||||
struct bn_tol tol;
|
||||
RT_BOT_CK_MAGIC(bot);
|
||||
|
||||
*volume = 0.0;
|
||||
if (bot->mode == RT_BOT_SURFACE)
|
||||
*volume = -1.0;
|
||||
if (bot->mode != RT_BOT_SOLID)
|
||||
return;
|
||||
*volume = 0.0;
|
||||
|
||||
/* allocate pts array, 3 vertices per bot face */
|
||||
face.pts = (point_t *)bu_calloc(3, sizeof(point_t), "rt_bot_volume: pts");
|
||||
|
||||
@@ -66,7 +66,11 @@ EXTERNCPP const struct bu_structparse rt_cline_parse[] = {
|
||||
* Calculate bounding RPP for cline
|
||||
*/
|
||||
C_DECL int
|
||||
rt_cline_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *UNUSED(tol)) {
|
||||
rt_cline_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *tol) {
|
||||
int nu_bbox = _rt_nonuniform_bbox(ip, min, max, tol);
|
||||
if (nu_bbox)
|
||||
return (nu_bbox > 0) ? 0 : -1;
|
||||
|
||||
struct rt_cline_internal *cline_ip;
|
||||
vect_t rad, work;
|
||||
point_t top;
|
||||
@@ -91,6 +95,26 @@ rt_cline_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struc
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
C_DECL void
|
||||
rt_cline_volume(fastf_t *volume, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_volume(volume, ip))
|
||||
return;
|
||||
|
||||
rt_crofton_volume_implicit(volume, ip);
|
||||
}
|
||||
|
||||
|
||||
C_DECL void
|
||||
rt_cline_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_surf_area(area, ip))
|
||||
return;
|
||||
|
||||
rt_crofton_surf_area_implicit(area, ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a pointer to a GED database record, determine if this is a
|
||||
* valid cline solid, and if so, precompute various terms of the
|
||||
@@ -132,19 +156,24 @@ rt_cline_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
|
||||
max_tr = 0.0;
|
||||
tmp = MAGNITUDE(cline_ip->h) * 0.5;
|
||||
stp->st_aradius = sqrt(tmp*tmp + cline_ip->radius*cline_ip->radius);
|
||||
stp->st_bradius = stp->st_aradius + max_tr;
|
||||
stp->st_bradius = stp->st_aradius;
|
||||
|
||||
if (rt_cline_bbox(ip, &(stp->st_min), &(stp->st_max), &rtip->rti_tol)) return 1;
|
||||
|
||||
int ret = _rt_nonuniform_prep_finalize(stp, ip, &rtip->rti_tol);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* expand the bounding box to include the additional beam radius */
|
||||
if (max_tr > 0.0) {
|
||||
vect_t extra;
|
||||
VSETALL(extra, max_tr);
|
||||
VSUB2(stp->st_min, stp->st_min, extra);
|
||||
VADD2(stp->st_max, stp->st_max, extra);
|
||||
stp->st_bradius += max_tr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_prep_finalize(stp, ip, &rtip->rti_tol);
|
||||
}
|
||||
|
||||
|
||||
@@ -176,6 +205,9 @@ rt_cline_print(register const struct soltab *stp)
|
||||
C_DECL int
|
||||
rt_cline_shot(struct soltab *stp, register struct xray *rp, struct application *ap, struct seg *seghead)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp)
|
||||
return _rt_nonuniform_shot(stp, rp, ap, seghead);
|
||||
|
||||
register struct cline_specific *cline =
|
||||
(struct cline_specific *)stp->st_specific;
|
||||
register struct seg *segp;
|
||||
@@ -446,6 +478,11 @@ rt_cline_vshot(struct soltab *stp[], struct xray *rp[], struct seg *segp, int n,
|
||||
C_DECL void
|
||||
rt_cline_norm(struct hit *hitp, struct soltab *stp, struct xray *rp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_norm(hitp, stp, rp);
|
||||
return;
|
||||
}
|
||||
|
||||
vect_t tmp;
|
||||
fastf_t dot;
|
||||
|
||||
@@ -486,6 +523,12 @@ rt_cline_norm(struct hit *hitp, struct soltab *stp, struct xray *rp)
|
||||
C_DECL void
|
||||
rt_cline_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
if (cvp)
|
||||
*cvp = (struct curvature)RT_CURVATURE_INIT_ZERO;
|
||||
return;
|
||||
}
|
||||
|
||||
if (stp) RT_CK_SOLTAB(stp);
|
||||
if (hitp) RT_CK_HIT(hitp);
|
||||
|
||||
@@ -504,6 +547,11 @@ rt_cline_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp)
|
||||
C_DECL void
|
||||
rt_cline_uv(struct application *ap, struct soltab *stp, struct hit *hitp, struct uvcoord *uvp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_uv(ap, stp, hitp, uvp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ap) RT_CK_APPLICATION(ap);
|
||||
if (stp) RT_CK_SOLTAB(stp);
|
||||
if (hitp) RT_CK_HIT(hitp);
|
||||
@@ -530,6 +578,7 @@ rt_cline_free(register struct soltab *stp)
|
||||
C_DECL int
|
||||
rt_cline_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_tess_tol *UNUSED(ttol), const struct bn_tol *UNUSED(tol), const struct bview *UNUSED(info))
|
||||
{
|
||||
struct rt_nonuniform_vlist_state nonuniform_state;
|
||||
struct rt_cline_internal *cline_ip;
|
||||
fastf_t top[16*3];
|
||||
fastf_t bottom[16*3];
|
||||
@@ -541,6 +590,7 @@ rt_cline_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_
|
||||
|
||||
BU_CK_LIST_HEAD(vhead);
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
_rt_nonuniform_vlist_state_init(&nonuniform_state, vhead);
|
||||
struct bu_list *vlfree = &rt_vlfree;
|
||||
cline_ip = (struct rt_cline_internal *)ip->idb_ptr;
|
||||
RT_CLINE_CK_MAGIC(cline_ip);
|
||||
@@ -604,7 +654,7 @@ rt_cline_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
|
||||
@@ -887,7 +937,7 @@ rt_cline_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *ip,
|
||||
nmg_region_a(*r, tol);
|
||||
bu_ptbl_free(&faces);
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_tess_finalize(*r, ip, tol);
|
||||
}
|
||||
|
||||
|
||||
@@ -954,6 +1004,8 @@ rt_cline_export4(struct bu_external *ep, const struct rt_db_internal *ip, double
|
||||
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
if (ip->idb_type != ID_CLINE) return -1;
|
||||
if (_rt_nonuniform_export4_check("rt_cline_export4", ip) < 0)
|
||||
return -1;
|
||||
cline_ip = (struct rt_cline_internal *)ip->idb_ptr;
|
||||
RT_CLINE_CK_MAGIC(cline_ip);
|
||||
|
||||
@@ -990,14 +1042,33 @@ rt_cline_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_int
|
||||
struct rt_cline_internal *top = (struct rt_cline_internal *)rop->idb_ptr;
|
||||
RT_CLINE_CK_MAGIC(top);
|
||||
|
||||
mat_t effective_mat;
|
||||
int remove_nonuniform = 0;
|
||||
{
|
||||
int nonuniform = _rt_nonuniform_transform_resolve(effective_mat, &remove_nonuniform, ip, mat);
|
||||
if (nonuniform < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (nonuniform) {
|
||||
*top = *tip;
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
return (_rt_nonuniform_attr_compose(rop, mat) < 0) ? BRLCAD_ERROR : BRLCAD_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (remove_nonuniform && _rt_nonuniform_attr_remove(rop) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
vect_t cv, ch;
|
||||
VMOVE(cv, tip->v);
|
||||
VMOVE(ch, tip->h);
|
||||
|
||||
MAT4X3PNT(top->v, mat, cv);
|
||||
MAT4X3VEC(top->h, mat, ch);
|
||||
top->thickness = tip->thickness / mat[15];
|
||||
top->radius = tip->radius / mat[15];
|
||||
MAT4X3PNT(top->v, effective_mat, cv);
|
||||
MAT4X3VEC(top->h, effective_mat, ch);
|
||||
top->thickness = tip->thickness / effective_mat[15];
|
||||
top->radius = tip->radius / effective_mat[15];
|
||||
|
||||
return BRLCAD_OK;
|
||||
}
|
||||
|
||||
+222
-473
@@ -45,28 +45,24 @@
|
||||
* intersecting closed surface), and total_chord is the sum of solid
|
||||
* segment lengths.
|
||||
*
|
||||
* Chord endpoints are independent pseudorandom points on the bounding
|
||||
* sphere. Reinitializing the generator with a fixed seed makes sampling
|
||||
* reproducible without changing the ordinary Monte Carlo convergence
|
||||
* behavior on which the estimator's stability checks rely.
|
||||
* References:
|
||||
* Li et al. (2003), "Using low-discrepancy sequences and the Crofton
|
||||
* formula to compute surface areas of geometric models",
|
||||
* Computer-Aided Design 35, 771-782.
|
||||
*
|
||||
* Liu et al. (2010), "Surface area estimation of digitized 3D objects
|
||||
* using quasi-Monte Carlo methods", Pattern Recognition 43, 3900-3909.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <mutex>
|
||||
#include <random>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <stdexcept>
|
||||
#include <random>
|
||||
#include <string.h>
|
||||
#include <thread>
|
||||
#include <time.h>
|
||||
#include <vector>
|
||||
|
||||
#include "vmath.h"
|
||||
#include "bu/log.h"
|
||||
@@ -104,15 +100,8 @@
|
||||
#define RT_CROFTON_STABILITY_MIN_WINDOWS 2u
|
||||
|
||||
/** Fixed seed for reproducible estimates in tests and cross-platform runs. */
|
||||
#define RT_CROFTON_RNG_SEED UINT64_C(0x9e3779b97f4a7c15)
|
||||
#define RT_CROFTON_RNG_SEED 0x9e3779b97f4a7c15ULL
|
||||
|
||||
/** Two sphere coordinates for each of two chord endpoints. */
|
||||
#define RT_CROFTON_RANDOM_VALUES_PER_RAY 4u
|
||||
|
||||
/** Convert the high 53 random bits exactly to a double in [0, 1). */
|
||||
static constexpr unsigned int CROFTON_RANDOM_SHIFT = 11u;
|
||||
static constexpr double CROFTON_RANDOM_SCALE =
|
||||
1.0 / 9007199254740992.0;
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Internal types */
|
||||
@@ -121,7 +110,6 @@ static constexpr double CROFTON_RANDOM_SCALE =
|
||||
struct crofton_ray {
|
||||
point_t r_pt;
|
||||
vect_t r_dir;
|
||||
size_t id;
|
||||
};
|
||||
|
||||
struct crofton_shared {
|
||||
@@ -129,19 +117,14 @@ struct crofton_shared {
|
||||
size_t total_crossings; /* in+out hit events */
|
||||
double total_chord; /* solid segment length sum (mm) */
|
||||
size_t total_rays; /* rays fired (hits + misses) */
|
||||
size_t ray_offset; /* first identifier in this sample stream */
|
||||
point_t *points;
|
||||
size_t point_count;
|
||||
size_t point_capacity;
|
||||
struct rt_crofton_segment *segments;
|
||||
size_t segment_count;
|
||||
size_t segment_capacity;
|
||||
bool collect_points;
|
||||
bool collect_segments;
|
||||
};
|
||||
|
||||
struct crofton_worker_data {
|
||||
struct application ap; /* per-CPU application struct */
|
||||
struct application *ap; /* per-CPU application struct */
|
||||
struct crofton_ray *rays; /* shared ray array (read-only) */
|
||||
size_t start;
|
||||
size_t end;
|
||||
@@ -152,10 +135,6 @@ struct crofton_worker_data {
|
||||
point_t *local_points;
|
||||
size_t local_point_count;
|
||||
size_t local_point_capacity;
|
||||
struct rt_crofton_segment *local_segments;
|
||||
size_t local_segment_count;
|
||||
size_t local_segment_capacity;
|
||||
size_t current_ray;
|
||||
};
|
||||
|
||||
|
||||
@@ -164,88 +143,46 @@ struct crofton_worker_data {
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static int
|
||||
crofton_hit(struct application *ap, struct partition *PartHeadp,
|
||||
struct seg *UNUSED(segs))
|
||||
crofton_hit(struct application *ap, struct partition *PartHeadp, struct seg *UNUSED(segs))
|
||||
{
|
||||
struct crofton_worker_data *worker =
|
||||
(struct crofton_worker_data *)ap->a_uptr;
|
||||
struct crofton_worker_data *wd = (struct crofton_worker_data *)ap->a_uptr;
|
||||
|
||||
size_t crossings = 0;
|
||||
double chord = 0.0;
|
||||
double chord = 0.0;
|
||||
|
||||
struct partition *partition;
|
||||
for (partition = PartHeadp->pt_forw; partition != PartHeadp;
|
||||
partition = partition->pt_forw) {
|
||||
fastf_t thickness = partition->pt_outhit->hit_dist -
|
||||
partition->pt_inhit->hit_dist;
|
||||
crossings += 2;
|
||||
chord += thickness;
|
||||
|
||||
if (worker->shared->collect_points) {
|
||||
if (worker->local_point_count + 2 >
|
||||
worker->local_point_capacity) {
|
||||
const size_t initial_capacity = 1024;
|
||||
size_t new_capacity = worker->local_point_capacity ?
|
||||
2 * worker->local_point_capacity : initial_capacity;
|
||||
while (new_capacity < worker->local_point_count + 2)
|
||||
new_capacity *= 2;
|
||||
worker->local_points = (point_t *)bu_realloc(
|
||||
worker->local_points,
|
||||
new_capacity * sizeof(point_t),
|
||||
"Crofton worker hit points");
|
||||
worker->local_point_capacity = new_capacity;
|
||||
}
|
||||
VJOIN1(worker->local_points[worker->local_point_count],
|
||||
ap->a_ray.r_pt, partition->pt_inhit->hit_dist,
|
||||
ap->a_ray.r_dir);
|
||||
worker->local_point_count++;
|
||||
VJOIN1(worker->local_points[worker->local_point_count],
|
||||
ap->a_ray.r_pt, partition->pt_outhit->hit_dist,
|
||||
ap->a_ray.r_dir);
|
||||
worker->local_point_count++;
|
||||
}
|
||||
|
||||
if (worker->shared->collect_segments) {
|
||||
if (worker->local_segment_count + 1 >
|
||||
worker->local_segment_capacity) {
|
||||
const size_t initial_capacity = 512;
|
||||
size_t new_capacity = worker->local_segment_capacity ?
|
||||
2 * worker->local_segment_capacity : initial_capacity;
|
||||
worker->local_segments =
|
||||
(struct rt_crofton_segment *)bu_realloc(
|
||||
worker->local_segments,
|
||||
new_capacity * sizeof(struct rt_crofton_segment),
|
||||
"Crofton worker segments");
|
||||
worker->local_segment_capacity = new_capacity;
|
||||
}
|
||||
|
||||
struct rt_crofton_segment *sample =
|
||||
&worker->local_segments[worker->local_segment_count];
|
||||
struct soltab *in_solid =
|
||||
partition->pt_inseg->seg_stp;
|
||||
struct soltab *out_solid =
|
||||
partition->pt_outseg->seg_stp;
|
||||
VJOIN1(sample->in_point, ap->a_ray.r_pt,
|
||||
partition->pt_inhit->hit_dist, ap->a_ray.r_dir);
|
||||
VJOIN1(sample->out_point, ap->a_ray.r_pt,
|
||||
partition->pt_outhit->hit_dist, ap->a_ray.r_dir);
|
||||
RT_HIT_NORMAL(sample->in_normal, partition->pt_inhit,
|
||||
in_solid, &ap->a_ray, partition->pt_inflip);
|
||||
RT_HIT_NORMAL(sample->out_normal, partition->pt_outhit,
|
||||
out_solid, &ap->a_ray, partition->pt_outflip);
|
||||
sample->thickness = thickness;
|
||||
sample->ray_id = worker->rays[worker->current_ray].id;
|
||||
if (!VNEAR_ZERO(sample->in_normal, VUNITIZE_TOL) &&
|
||||
!VNEAR_ZERO(sample->out_normal, VUNITIZE_TOL))
|
||||
worker->local_segment_count++;
|
||||
}
|
||||
struct partition *pp;
|
||||
for (pp = PartHeadp->pt_forw; pp != PartHeadp; pp = pp->pt_forw) {
|
||||
/* Each partition contributes an in-hit and an out-hit */
|
||||
crossings += 2;
|
||||
chord += pp->pt_outhit->hit_dist - pp->pt_inhit->hit_dist;
|
||||
if (wd->shared->collect_points) {
|
||||
if (wd->local_point_count + 2 > wd->local_point_capacity) {
|
||||
const size_t initial_capacity = 1024;
|
||||
size_t new_capacity = wd->local_point_capacity ?
|
||||
2 * wd->local_point_capacity : initial_capacity;
|
||||
while (new_capacity < wd->local_point_count + 2)
|
||||
new_capacity *= 2;
|
||||
wd->local_points = (point_t *)bu_realloc(wd->local_points,
|
||||
new_capacity * sizeof(point_t), "Crofton worker hit points");
|
||||
wd->local_point_capacity = new_capacity;
|
||||
}
|
||||
VJOIN1(wd->local_points[wd->local_point_count], ap->a_ray.r_pt,
|
||||
pp->pt_inhit->hit_dist, ap->a_ray.r_dir);
|
||||
wd->local_point_count++;
|
||||
VJOIN1(wd->local_points[wd->local_point_count], ap->a_ray.r_pt,
|
||||
pp->pt_outhit->hit_dist, ap->a_ray.r_dir);
|
||||
wd->local_point_count++;
|
||||
}
|
||||
}
|
||||
|
||||
worker->local_crossings += crossings;
|
||||
worker->local_chord += chord;
|
||||
worker->local_rays++;
|
||||
wd->local_crossings += crossings;
|
||||
wd->local_chord += chord;
|
||||
wd->local_rays += 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
crofton_miss(struct application *ap)
|
||||
{
|
||||
@@ -256,273 +193,159 @@ crofton_miss(struct application *ap)
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Parallel worker */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static void
|
||||
crofton_worker(int id, void *data)
|
||||
{
|
||||
struct crofton_worker_data *wd = &((struct crofton_worker_data *)data)[id - 1];
|
||||
struct application *ap = wd->ap;
|
||||
struct crofton_ray *rays = wd->rays;
|
||||
|
||||
for (size_t i = wd->start; i < wd->end; i++) {
|
||||
VMOVE(ap->a_ray.r_pt, rays[i].r_pt);
|
||||
VMOVE(ap->a_ray.r_dir, rays[i].r_dir);
|
||||
/* r_dir is already unit-length (set during ray generation) */
|
||||
rt_shootray(ap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Point / ray generation */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static double
|
||||
crofton_random01(std::mt19937_64 &rng)
|
||||
crofton_rand01(std::mt19937_64 &rng)
|
||||
{
|
||||
return static_cast<double>(rng() >> CROFTON_RANDOM_SHIFT) *
|
||||
CROFTON_RANDOM_SCALE;
|
||||
return std::generate_canonical<double, 53>(rng);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
random_point_on_sphere(double radius, const point_t center, double u,
|
||||
double v, point_t point)
|
||||
random_point_on_sphere(double radius, const point_t center, point_t out, std::mt19937_64 &rng)
|
||||
{
|
||||
double z = 1.0 - 2.0 * u;
|
||||
double radial = sqrt(std::max(0.0, 1.0 - z * z));
|
||||
double angle = 2.0 * M_PI * v;
|
||||
point[X] = center[X] + radius * radial * cos(angle);
|
||||
point[Y] = center[Y] + radius * radial * sin(angle);
|
||||
point[Z] = center[Z] + radius * z;
|
||||
double theta = 2.0 * M_PI * crofton_rand01(rng);
|
||||
double phi = acos(2.0 * crofton_rand01(rng) - 1.0);
|
||||
double sp = sin(phi);
|
||||
out[X] = center[X] + radius * sp * cos(theta);
|
||||
out[Y] = center[Y] + radius * sp * sin(theta);
|
||||
out[Z] = center[Z] + radius * cos(phi);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate independent random chord endpoints. Pairing consecutive IID
|
||||
* sphere points has the same distribution as shuffling a point array before
|
||||
* pairing, but consumes exactly four random values per ray. That fixed
|
||||
* stride lets callers continue the deterministic stream with ray_offset.
|
||||
* Generate @p nrays chord rays from 2*nrays random points on the
|
||||
* bounding sphere. Each ray connects one randomly chosen point to
|
||||
* another.
|
||||
*/
|
||||
static void
|
||||
generate_rays(struct crofton_ray *rays, size_t ray_count, size_t first_id,
|
||||
double radius, const point_t center, std::mt19937_64 &rng)
|
||||
generate_rays(struct crofton_ray *rays, size_t nrays,
|
||||
double radius, const point_t center, std::mt19937_64 &rng)
|
||||
{
|
||||
for (size_t i = 0; i < ray_count; i++) {
|
||||
point_t endpoint;
|
||||
random_point_on_sphere(radius, center, crofton_random01(rng),
|
||||
crofton_random01(rng), rays[i].r_pt);
|
||||
random_point_on_sphere(radius, center, crofton_random01(rng),
|
||||
crofton_random01(rng), endpoint);
|
||||
VSUB2(rays[i].r_dir, endpoint, rays[i].r_pt);
|
||||
VUNITIZE(rays[i].r_dir);
|
||||
rays[i].id = first_id + i;
|
||||
size_t npts = nrays * 2;
|
||||
|
||||
point_t *pts = (point_t *)bu_calloc(npts, sizeof(point_t), "crofton pts");
|
||||
for (size_t i = 0; i < npts; i++)
|
||||
random_point_on_sphere(radius, center, pts[i], rng);
|
||||
|
||||
/* Shuffle so pairing is random */
|
||||
for (size_t i = npts - 1; i > 0; i--) {
|
||||
size_t j = (size_t)(crofton_rand01(rng) * (i + 1));
|
||||
if (j > i) j = i;
|
||||
point_t tmp;
|
||||
VMOVE(tmp, pts[i]);
|
||||
VMOVE(pts[i], pts[j]);
|
||||
VMOVE(pts[j], tmp);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < nrays; i++) {
|
||||
VMOVE(rays[i].r_pt, pts[i * 2]);
|
||||
VSUB2(rays[i].r_dir, pts[i * 2 + 1], pts[i * 2]);
|
||||
VUNITIZE(rays[i].r_dir);
|
||||
}
|
||||
|
||||
bu_free(pts, "crofton pts");
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Reusable parallel workers */
|
||||
/* One iteration: fire nrays, accumulate into shared */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
class CroftonWorkerPool
|
||||
{
|
||||
public:
|
||||
CroftonWorkerPool(const struct application *ap_template,
|
||||
struct resource *resources, size_t worker_count) :
|
||||
count(worker_count), data(worker_count)
|
||||
{
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
data[i].ap = *ap_template;
|
||||
data[i].ap.a_resource = &resources[i];
|
||||
data[i].shared = NULL;
|
||||
data[i].ap.a_uptr = &data[i];
|
||||
}
|
||||
|
||||
try {
|
||||
executor = std::thread(&CroftonWorkerPool::run_workers, this);
|
||||
} catch (...) {
|
||||
free_worker_points();
|
||||
throw;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (!workers_started.wait_for(lock, worker_start_timeout,
|
||||
[this]() { return registered == count; })) {
|
||||
stopping = true;
|
||||
work_ready.notify_all();
|
||||
lock.unlock();
|
||||
executor.join();
|
||||
free_worker_points();
|
||||
throw std::runtime_error("worker startup timed out");
|
||||
}
|
||||
}
|
||||
|
||||
~CroftonWorkerPool()
|
||||
{
|
||||
stop_and_join();
|
||||
free_worker_points();
|
||||
}
|
||||
|
||||
CroftonWorkerPool(const CroftonWorkerPool &) = delete;
|
||||
CroftonWorkerPool &operator=(const CroftonWorkerPool &) = delete;
|
||||
|
||||
void run(struct crofton_ray *rays, size_t nrays,
|
||||
struct crofton_shared *shared)
|
||||
{
|
||||
const size_t rays_per_worker = nrays / count;
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
struct crofton_worker_data &wd = data[i];
|
||||
wd.rays = rays;
|
||||
wd.start = i * rays_per_worker;
|
||||
wd.end = (i == count - 1) ? nrays : (i + 1) * rays_per_worker;
|
||||
wd.shared = shared;
|
||||
wd.local_crossings = 0;
|
||||
wd.local_chord = 0.0;
|
||||
wd.local_rays = 0;
|
||||
wd.local_point_count = 0;
|
||||
wd.local_segment_count = 0;
|
||||
}
|
||||
completed = 0;
|
||||
generation++;
|
||||
work_ready.notify_all();
|
||||
work_done.wait(lock, [this]() { return completed == count; });
|
||||
}
|
||||
|
||||
const std::vector<struct crofton_worker_data> &worker_data() const
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
private:
|
||||
/* Native thread creation should complete promptly. A bounded startup wait
|
||||
* turns partial bu_parallel startup into a reported failure, not a hang. */
|
||||
static constexpr std::chrono::seconds worker_start_timeout{5};
|
||||
|
||||
static void worker_entry(int UNUSED(id), void *context)
|
||||
{
|
||||
CroftonWorkerPool *pool = static_cast<CroftonWorkerPool *>(context);
|
||||
size_t slot = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(pool->mutex);
|
||||
slot = pool->registered++;
|
||||
if (pool->registered == pool->count)
|
||||
pool->workers_started.notify_one();
|
||||
}
|
||||
pool->worker(slot);
|
||||
}
|
||||
|
||||
void run_workers()
|
||||
{
|
||||
bu_parallel(worker_entry, count, this);
|
||||
}
|
||||
|
||||
void worker(size_t slot)
|
||||
{
|
||||
size_t observed_generation = 0;
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
while (true) {
|
||||
work_ready.wait(lock, [this, observed_generation]() {
|
||||
return stopping || generation != observed_generation;
|
||||
});
|
||||
if (stopping)
|
||||
return;
|
||||
|
||||
observed_generation = generation;
|
||||
struct crofton_worker_data *wd = &data[slot];
|
||||
lock.unlock();
|
||||
for (size_t i = wd->start; i < wd->end; i++) {
|
||||
wd->current_ray = i;
|
||||
VMOVE(wd->ap.a_ray.r_pt, wd->rays[i].r_pt);
|
||||
VMOVE(wd->ap.a_ray.r_dir, wd->rays[i].r_dir);
|
||||
rt_shootray(&wd->ap);
|
||||
}
|
||||
lock.lock();
|
||||
completed++;
|
||||
if (completed == count)
|
||||
work_done.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void stop_and_join()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
stopping = true;
|
||||
work_ready.notify_all();
|
||||
}
|
||||
if (executor.joinable())
|
||||
executor.join();
|
||||
}
|
||||
|
||||
void free_worker_points()
|
||||
{
|
||||
for (struct crofton_worker_data &wd : data) {
|
||||
if (wd.local_points)
|
||||
bu_free(wd.local_points, "Crofton worker hit points");
|
||||
if (wd.local_segments)
|
||||
bu_free(wd.local_segments, "Crofton worker segments");
|
||||
wd.local_points = NULL;
|
||||
wd.local_segments = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
size_t count;
|
||||
std::vector<struct crofton_worker_data> data;
|
||||
std::thread executor;
|
||||
std::mutex mutex;
|
||||
std::condition_variable workers_started;
|
||||
std::condition_variable work_ready;
|
||||
std::condition_variable work_done;
|
||||
size_t registered = 0;
|
||||
size_t generation = 0;
|
||||
size_t completed = 0;
|
||||
bool stopping = false;
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
do_one_iteration(CroftonWorkerPool &pool, size_t ray_count, double radius,
|
||||
const point_t center, struct crofton_shared *shared,
|
||||
std::mt19937_64 &rng)
|
||||
do_one_iteration(struct application *ap_template,
|
||||
struct resource *resources,
|
||||
size_t nrays,
|
||||
double radius,
|
||||
const point_t center,
|
||||
struct crofton_shared *shared,
|
||||
std::mt19937_64 &rng)
|
||||
{
|
||||
struct crofton_ray *rays = (struct crofton_ray *)bu_calloc(
|
||||
ray_count, sizeof(struct crofton_ray), "Crofton rays");
|
||||
generate_rays(rays, ray_count, shared->ray_offset + shared->total_rays,
|
||||
radius, center, rng);
|
||||
pool.run(rays, ray_count, shared);
|
||||
nrays, sizeof(struct crofton_ray), "crofton rays");
|
||||
|
||||
for (const struct crofton_worker_data &worker : pool.worker_data()) {
|
||||
shared->total_crossings += worker.local_crossings;
|
||||
shared->total_chord += worker.local_chord;
|
||||
shared->total_rays += worker.local_rays;
|
||||
generate_rays(rays, nrays, radius, center, rng);
|
||||
|
||||
if (worker.local_point_count) {
|
||||
const size_t needed =
|
||||
shared->point_count + worker.local_point_count;
|
||||
if (needed > shared->point_capacity) {
|
||||
size_t new_capacity = shared->point_capacity ?
|
||||
2 * shared->point_capacity : needed;
|
||||
if (new_capacity < needed)
|
||||
new_capacity = needed;
|
||||
shared->points = (point_t *)bu_realloc(shared->points,
|
||||
new_capacity * sizeof(point_t),
|
||||
"Crofton hit points");
|
||||
shared->point_capacity = new_capacity;
|
||||
}
|
||||
memcpy(&shared->points[shared->point_count],
|
||||
worker.local_points,
|
||||
worker.local_point_count * sizeof(point_t));
|
||||
shared->point_count = needed;
|
||||
}
|
||||
size_t ncpus = bu_avail_cpus();
|
||||
if (ncpus < 1) ncpus = 1;
|
||||
if (ncpus > MAX_PSW) ncpus = MAX_PSW;
|
||||
|
||||
if (worker.local_segment_count) {
|
||||
const size_t needed =
|
||||
shared->segment_count + worker.local_segment_count;
|
||||
if (needed > shared->segment_capacity) {
|
||||
size_t new_capacity = shared->segment_capacity ?
|
||||
2 * shared->segment_capacity : needed;
|
||||
if (new_capacity < needed)
|
||||
new_capacity = needed;
|
||||
shared->segments =
|
||||
(struct rt_crofton_segment *)bu_realloc(
|
||||
shared->segments,
|
||||
new_capacity * sizeof(struct rt_crofton_segment),
|
||||
"Crofton segments");
|
||||
shared->segment_capacity = new_capacity;
|
||||
}
|
||||
memcpy(&shared->segments[shared->segment_count],
|
||||
worker.local_segments,
|
||||
worker.local_segment_count *
|
||||
sizeof(struct rt_crofton_segment));
|
||||
shared->segment_count = needed;
|
||||
}
|
||||
struct crofton_worker_data *wdata = (struct crofton_worker_data *)bu_calloc(
|
||||
ncpus, sizeof(struct crofton_worker_data), "crofton wdata");
|
||||
|
||||
size_t per_cpu = nrays / ncpus;
|
||||
|
||||
for (size_t i = 0; i < ncpus; i++) {
|
||||
struct application *a = (struct application *)bu_calloc(
|
||||
1, sizeof(struct application), "crofton app");
|
||||
*a = *ap_template; /* struct copy */
|
||||
a->a_resource = &resources[i];
|
||||
|
||||
struct crofton_worker_data *wd = &wdata[i];
|
||||
wd->ap = a;
|
||||
wd->rays = rays;
|
||||
wd->start = i * per_cpu;
|
||||
wd->end = (i == ncpus - 1) ? nrays : (i + 1) * per_cpu;
|
||||
wd->shared = shared;
|
||||
a->a_uptr = wd;
|
||||
}
|
||||
|
||||
bu_free(rays, "Crofton rays");
|
||||
bu_parallel(crofton_worker, (int)ncpus, (void *)wdata);
|
||||
|
||||
for (size_t i = 0; i < ncpus; i++) {
|
||||
shared->total_crossings += wdata[i].local_crossings;
|
||||
shared->total_chord += wdata[i].local_chord;
|
||||
shared->total_rays += wdata[i].local_rays;
|
||||
if (wdata[i].local_point_count) {
|
||||
const size_t needed = shared->point_count + wdata[i].local_point_count;
|
||||
if (needed > shared->point_capacity) {
|
||||
size_t new_capacity = shared->point_capacity ?
|
||||
2 * shared->point_capacity : needed;
|
||||
if (new_capacity < needed)
|
||||
new_capacity = needed;
|
||||
shared->points = (point_t *)bu_realloc(shared->points,
|
||||
new_capacity * sizeof(point_t), "Crofton hit points");
|
||||
shared->point_capacity = new_capacity;
|
||||
}
|
||||
memcpy(&shared->points[shared->point_count], wdata[i].local_points,
|
||||
wdata[i].local_point_count * sizeof(point_t));
|
||||
shared->point_count = needed;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ncpus; i++) {
|
||||
if (wdata[i].local_points)
|
||||
bu_free(wdata[i].local_points, "Crofton worker hit points");
|
||||
bu_free(wdata[i].ap, "crofton app");
|
||||
}
|
||||
bu_free(wdata, "crofton wdata");
|
||||
bu_free(rays, "crofton rays");
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Public API: rt_crofton_shoot */
|
||||
/* ------------------------------------------------------------------ */
|
||||
@@ -552,8 +375,7 @@ do_one_iteration(CroftonWorkerPool &pool, size_t ray_count, double radius,
|
||||
* sampling (>= 0) on success; -1 on bad arguments.
|
||||
*/
|
||||
static int
|
||||
crofton_shoot_impl(struct rt_crofton_result *out_result,
|
||||
point_t **out_points,
|
||||
crofton_shoot_impl(point_t **out_points,
|
||||
size_t *out_point_count,
|
||||
double *out_surf_area,
|
||||
double *out_volume,
|
||||
@@ -562,22 +384,13 @@ crofton_shoot_impl(struct rt_crofton_result *out_result,
|
||||
point_t out_obb[8],
|
||||
struct rt_i *rtip,
|
||||
const struct rt_crofton_params *params,
|
||||
size_t ray_offset,
|
||||
const fastf_t *bbox_min,
|
||||
const fastf_t *bbox_max)
|
||||
{
|
||||
if (!rtip ||
|
||||
(!out_result && !out_surf_area && !out_volume && !out_aabb_min &&
|
||||
!out_obb && !out_points) ||
|
||||
((out_points == NULL) != (out_point_count == NULL)) ||
|
||||
((out_aabb_min == NULL) != (out_aabb_max == NULL)) ||
|
||||
(out_result && out_result->segments) ||
|
||||
ray_offset > UINT64_MAX / RT_CROFTON_RANDOM_VALUES_PER_RAY)
|
||||
return -1;
|
||||
if (out_result) {
|
||||
struct rt_crofton_result empty = RT_CROFTON_RESULT_INIT;
|
||||
*out_result = empty;
|
||||
}
|
||||
if (!rtip || (!out_surf_area && !out_volume && !out_aabb_min && !out_obb && !out_points) ||
|
||||
((out_points == NULL) != (out_point_count == NULL)) ||
|
||||
((out_aabb_min == NULL) != (out_aabb_max == NULL)))
|
||||
return -1;
|
||||
if (out_points) {
|
||||
*out_points = NULL;
|
||||
*out_point_count = 0;
|
||||
@@ -660,14 +473,10 @@ crofton_shoot_impl(struct rt_crofton_result *out_result,
|
||||
if (!use_default && max_rays > 0 && stability_mm <= 0.0 && time_ms <= 0.0)
|
||||
batch = max_rays; /* single-iteration mode */
|
||||
|
||||
size_t ncpus = bu_avail_cpus();
|
||||
if (ncpus < 1) ncpus = 1;
|
||||
if (ncpus > MAX_PSW) ncpus = MAX_PSW;
|
||||
|
||||
/* ---- Initialize per-CPU resources ---- */
|
||||
struct resource *resources = (struct resource *)bu_calloc(
|
||||
ncpus, sizeof(struct resource), "crofton resources");
|
||||
for (size_t i = 0; i < ncpus; i++)
|
||||
MAX_PSW, sizeof(struct resource), "crofton resources");
|
||||
for (int i = 0; i < MAX_PSW; i++)
|
||||
rt_init_resource(&resources[i], i, rtip);
|
||||
|
||||
/* ---- Set up application template ---- */
|
||||
@@ -685,32 +494,10 @@ crofton_shoot_impl(struct rt_crofton_result *out_result,
|
||||
/* ---- Shared accumulator ---- */
|
||||
struct crofton_shared shared;
|
||||
memset(&shared, 0, sizeof(shared));
|
||||
shared.ray_offset = ray_offset;
|
||||
shared.collect_points =
|
||||
out_points != NULL || out_aabb_min != NULL || out_obb != NULL;
|
||||
shared.collect_segments = out_result != NULL;
|
||||
const bool stabilize_surface =
|
||||
out_surf_area != NULL || out_result != NULL ||
|
||||
shared.collect_points;
|
||||
|
||||
CroftonWorkerPool *pool = NULL;
|
||||
try {
|
||||
pool = new CroftonWorkerPool(&ap, resources, ncpus);
|
||||
} catch (const std::exception &e) {
|
||||
bu_log("rt_crofton: unable to start worker pool: %s\n", e.what());
|
||||
for (size_t i = 0; i < ncpus; i++) {
|
||||
if (resources[i].re_magic == RESOURCE_MAGIC) {
|
||||
rt_clean_resource_basic(rtip, &resources[i]);
|
||||
BU_PTBL_SET(&rtip->rti_resources, i, NULL);
|
||||
}
|
||||
}
|
||||
bu_free(resources, "crofton resources");
|
||||
return -1;
|
||||
}
|
||||
shared.collect_points = out_points != NULL || out_aabb_min != NULL || out_obb != NULL;
|
||||
const bool stabilize_surface = out_surf_area != NULL || shared.collect_points;
|
||||
|
||||
std::mt19937_64 rng(RT_CROFTON_RNG_SEED);
|
||||
rng.discard(static_cast<uint64_t>(ray_offset) *
|
||||
RT_CROFTON_RANDOM_VALUES_PER_RAY);
|
||||
|
||||
const double FOUR_PI = 4.0 * M_PI;
|
||||
const double PI = M_PI;
|
||||
@@ -734,7 +521,7 @@ crofton_shoot_impl(struct rt_crofton_result *out_result,
|
||||
curr_rays = batch;
|
||||
}
|
||||
|
||||
do_one_iteration(*pool, curr_rays, R, center, &shared, rng);
|
||||
do_one_iteration(&ap, resources, curr_rays, R, center, &shared, rng);
|
||||
iteration++;
|
||||
|
||||
if (shared.total_rays == 0) break;
|
||||
@@ -775,12 +562,9 @@ crofton_shoot_impl(struct rt_crofton_result *out_result,
|
||||
} else {
|
||||
/* ---- Parametric loop: n_rays / stability_mm / time_ms ---- */
|
||||
double prev_r_sa = -1.0, prev_r_v = -1.0;
|
||||
double prev_sa = -1.0, prev_v = -1.0;
|
||||
size_t total_fired = 0;
|
||||
size_t stable_windows = 0;
|
||||
int64_t t0 = (time_ms > 0.0) ? bu_gettime() : 0;
|
||||
const bool use_relative_stability = out_result && time_ms > 0.0 &&
|
||||
stability_mm <= 0.0;
|
||||
|
||||
for (;;) {
|
||||
/* Time-budget check before firing */
|
||||
@@ -797,7 +581,7 @@ crofton_shoot_impl(struct rt_crofton_result *out_result,
|
||||
if (fire > remaining) fire = remaining;
|
||||
}
|
||||
|
||||
do_one_iteration(*pool, fire, R, center, &shared, rng);
|
||||
do_one_iteration(&ap, resources, fire, R, center, &shared, rng);
|
||||
total_fired += fire;
|
||||
|
||||
if (shared.total_rays == 0) break;
|
||||
@@ -810,39 +594,27 @@ crofton_shoot_impl(struct rt_crofton_result *out_result,
|
||||
/ (double)shared.total_rays;
|
||||
|
||||
/* Stability check */
|
||||
if ((stability_mm > 0.0 || use_relative_stability) &&
|
||||
if (stability_mm > 0.0 &&
|
||||
shared.total_rays >= RT_CROFTON_STABILITY_MIN_RAYS &&
|
||||
shared.total_crossings >= RT_CROFTON_STABILITY_MIN_CROSSINGS) {
|
||||
bool sa_ok = false;
|
||||
bool v_ok = false;
|
||||
if (use_relative_stability) {
|
||||
sa_ok = !stabilize_surface || (prev_sa > 0.0 &&
|
||||
fabs(curr_est_sa - prev_sa) / prev_sa * 100.0 <=
|
||||
RT_CROFTON_DEFAULT_THRESHOLD);
|
||||
v_ok = !out_volume || (prev_v > 0.0 &&
|
||||
fabs(curr_est_v - prev_v) / prev_v * 100.0 <=
|
||||
RT_CROFTON_DEFAULT_THRESHOLD);
|
||||
prev_sa = curr_est_sa;
|
||||
prev_v = curr_est_v;
|
||||
} else {
|
||||
double r_sa = (curr_est_sa > 0.0) ?
|
||||
sqrt(curr_est_sa * INV_4PI) : 0.0;
|
||||
double r_v = (curr_est_v > 0.0) ?
|
||||
cbrt(curr_est_v * INV_4PI3) : 0.0;
|
||||
sa_ok = !stabilize_surface || (prev_r_sa >= 0.0 &&
|
||||
fabs(r_sa - prev_r_sa) < stability_mm);
|
||||
v_ok = !out_volume || (prev_r_v >= 0.0 &&
|
||||
fabs(r_v - prev_r_v) < stability_mm);
|
||||
prev_r_sa = r_sa;
|
||||
prev_r_v = r_v;
|
||||
}
|
||||
if (sa_ok && v_ok) {
|
||||
stable_windows++;
|
||||
if (stable_windows >= RT_CROFTON_STABILITY_MIN_WINDOWS)
|
||||
break;
|
||||
} else {
|
||||
stable_windows = 0;
|
||||
double r_sa = (curr_est_sa > 0.0) ? sqrt(curr_est_sa * INV_4PI) : 0.0;
|
||||
double r_v = (curr_est_v > 0.0) ? cbrt(curr_est_v * INV_4PI3) : 0.0;
|
||||
|
||||
if (prev_r_sa >= 0.0) {
|
||||
int sa_ok = (!stabilize_surface) ||
|
||||
fabs(r_sa - prev_r_sa) < stability_mm;
|
||||
int v_ok = (!out_volume) ||
|
||||
fabs(r_v - prev_r_v) < stability_mm;
|
||||
if (sa_ok && v_ok) {
|
||||
stable_windows++;
|
||||
if (stable_windows >= RT_CROFTON_STABILITY_MIN_WINDOWS)
|
||||
break;
|
||||
} else {
|
||||
stable_windows = 0;
|
||||
}
|
||||
}
|
||||
prev_r_sa = r_sa;
|
||||
prev_r_v = r_v;
|
||||
}
|
||||
|
||||
/* Time-budget check after firing */
|
||||
@@ -852,16 +624,8 @@ crofton_shoot_impl(struct rt_crofton_result *out_result,
|
||||
}
|
||||
}
|
||||
|
||||
if (out_surf_area)
|
||||
*out_surf_area = curr_est_sa;
|
||||
if (out_volume)
|
||||
*out_volume = curr_est_v;
|
||||
if (out_result) {
|
||||
out_result->surface_area = curr_est_sa;
|
||||
out_result->volume = curr_est_v;
|
||||
out_result->ray_count = shared.total_rays;
|
||||
out_result->crossing_count = shared.total_crossings;
|
||||
}
|
||||
if (out_surf_area) *out_surf_area = curr_est_sa;
|
||||
if (out_volume) *out_volume = curr_est_v;
|
||||
if (out_aabb_min) {
|
||||
for (size_t i = 0; i < shared.point_count; ++i)
|
||||
VMINMAX(*out_aabb_min, *out_aabb_max, shared.points[i]);
|
||||
@@ -877,19 +641,11 @@ crofton_shoot_impl(struct rt_crofton_result *out_result,
|
||||
bounds_ret = -1;
|
||||
}
|
||||
if (out_points && !bounds_ret) {
|
||||
*out_points = shared.points;
|
||||
*out_point_count = shared.point_count;
|
||||
*out_points = shared.points;
|
||||
*out_point_count = shared.point_count;
|
||||
} else if (shared.points) {
|
||||
bu_free(shared.points, "Crofton hit points");
|
||||
bu_free(shared.points, "Crofton hit points");
|
||||
}
|
||||
if (out_result && !bounds_ret) {
|
||||
out_result->segments = shared.segments;
|
||||
out_result->segment_count = shared.segment_count;
|
||||
} else if (shared.segments) {
|
||||
bu_free(shared.segments, "Crofton segments");
|
||||
}
|
||||
|
||||
delete pool;
|
||||
|
||||
/* Clean each resource and NULL out its slot in rtip->rti_resources.
|
||||
* This is necessary because crofton_from_ip calls rt_i_destroy(rtip)
|
||||
@@ -901,7 +657,7 @@ crofton_shoot_impl(struct rt_crofton_result *out_result,
|
||||
*
|
||||
* By setting the slot to NULL we let rt_i_destroy's cleanup skip it,
|
||||
* and then we can safely bu_free the resources array. */
|
||||
for (size_t i = 0; i < ncpus; i++) {
|
||||
for (int i = 0; i < MAX_PSW; i++) {
|
||||
if (resources[i].re_magic == RESOURCE_MAGIC) {
|
||||
rt_clean_resource_basic(rtip, &resources[i]);
|
||||
BU_PTBL_SET(&rtip->rti_resources, i, NULL);
|
||||
@@ -925,31 +681,9 @@ rt_crofton_shoot(double *out_surf_area, double *out_volume,
|
||||
const struct rt_crofton_params *params,
|
||||
const fastf_t *bbox_min, const fastf_t *bbox_max)
|
||||
{
|
||||
return crofton_shoot_impl(NULL, out_points, out_point_count,
|
||||
out_surf_area, out_volume, out_aabb_min, out_aabb_max, out_obb,
|
||||
rtip, params, 0, bbox_min, bbox_max);
|
||||
}
|
||||
|
||||
int
|
||||
rt_crofton_collect(struct rt_crofton_result *result, struct rt_i *rtip,
|
||||
const struct rt_crofton_params *params, size_t ray_offset,
|
||||
const fastf_t *bbox_min, const fastf_t *bbox_max)
|
||||
{
|
||||
if (!result)
|
||||
return -1;
|
||||
return crofton_shoot_impl(result, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, rtip, params, ray_offset, bbox_min, bbox_max);
|
||||
}
|
||||
|
||||
void
|
||||
rt_crofton_result_free(struct rt_crofton_result *result)
|
||||
{
|
||||
if (!result)
|
||||
return;
|
||||
if (result->segments)
|
||||
bu_free(result->segments, "Crofton segments");
|
||||
struct rt_crofton_result empty = RT_CROFTON_RESULT_INIT;
|
||||
*result = empty;
|
||||
return crofton_shoot_impl(out_points, out_point_count, out_surf_area,
|
||||
out_volume, out_aabb_min, out_aabb_max, out_obb, rtip, params,
|
||||
bbox_min, bbox_max);
|
||||
}
|
||||
|
||||
|
||||
@@ -1039,6 +773,12 @@ crofton_from_ip_n(const struct rt_db_internal *ip,
|
||||
tmp_intern.idb_meth = &OBJ[ip->idb_minor_type];
|
||||
else
|
||||
tmp_intern.idb_meth = ip->idb_meth; /* last resort: trust the caller */
|
||||
if (ip->idb_avs.magic == BU_AVS_MAGIC) {
|
||||
bu_avs_init(&tmp_intern.idb_avs, ip->idb_avs.count, "crofton tmp attrs");
|
||||
bu_avs_merge(&tmp_intern.idb_avs, &ip->idb_avs);
|
||||
} else {
|
||||
bu_avs_init_empty(&tmp_intern.idb_avs);
|
||||
}
|
||||
|
||||
struct bu_external ext;
|
||||
BU_EXTERNAL_INIT(&ext);
|
||||
@@ -1046,6 +786,7 @@ crofton_from_ip_n(const struct rt_db_internal *ip,
|
||||
if (rt_db_cvt_to_ext5(&ext, scratch, &tmp_intern, 1.0,
|
||||
dbip, ip->idb_major_type) < 0) {
|
||||
bu_log("rt_crofton: rt_db_cvt_to_ext5() failed\n");
|
||||
bu_avs_free(&tmp_intern.idb_avs);
|
||||
bu_free_external(&ext);
|
||||
db_close(dbip);
|
||||
return -1;
|
||||
@@ -1057,10 +798,12 @@ crofton_from_ip_n(const struct rt_db_internal *ip,
|
||||
(unsigned char)ip->idb_minor_type) < 0) {
|
||||
bu_log("rt_crofton: wdb_export_external() failed\n");
|
||||
/* ext.ext_buf stolen by db_inmem on success; free any remainder */
|
||||
bu_avs_free(&tmp_intern.idb_avs);
|
||||
bu_free_external(&ext);
|
||||
db_close(dbip);
|
||||
return -1;
|
||||
}
|
||||
bu_avs_free(&tmp_intern.idb_avs);
|
||||
/* In the INMEM path ext_buf is stolen; this is safe to call regardless */
|
||||
bu_free_external(&ext);
|
||||
|
||||
@@ -1086,8 +829,12 @@ crofton_from_ip_n(const struct rt_db_internal *ip,
|
||||
/* ---- Run Crofton estimator ---- */
|
||||
double sa = 0.0;
|
||||
double vol = 0.0;
|
||||
(void)rt_crofton_shoot(&sa, &vol, NULL, NULL, NULL, NULL, NULL,
|
||||
rtip, params, NULL, NULL);
|
||||
if (rt_crofton_shoot(&sa, &vol, NULL, NULL, NULL, NULL, NULL,
|
||||
rtip, params, NULL, NULL) < 0) {
|
||||
rt_i_destroy(rtip);
|
||||
db_close(dbip);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (out_sa) *out_sa = sa;
|
||||
if (out_vol) *out_vol = vol;
|
||||
@@ -1115,14 +862,16 @@ rt_crofton_sample(fastf_t *area, fastf_t *vol,
|
||||
const struct rt_db_internal *ip,
|
||||
const struct rt_crofton_params *params)
|
||||
{
|
||||
if (area)
|
||||
*area = -1.0;
|
||||
if (vol)
|
||||
*vol = -1.0;
|
||||
if ((!area && !vol) || !ip)
|
||||
return;
|
||||
|
||||
double sa = 0.0, v = 0.0;
|
||||
if (crofton_from_ip_n(ip, area ? &sa : NULL, vol ? &v : NULL, params) < 0) {
|
||||
sa = 0.0;
|
||||
v = 0.0;
|
||||
}
|
||||
if (crofton_from_ip_n(ip, area ? &sa : NULL, vol ? &v : NULL, params) < 0)
|
||||
return;
|
||||
|
||||
if (area) *area = (fastf_t)sa;
|
||||
if (vol) *vol = (fastf_t)v;
|
||||
|
||||
@@ -106,11 +106,12 @@ rt_dsp_volume(fastf_t *vol, const struct rt_db_internal *ip)
|
||||
|
||||
if (!vol)
|
||||
return;
|
||||
*vol = 0.0;
|
||||
*vol = -1.0;
|
||||
|
||||
dsp = rt_dsp_internal_from_ip(ip);
|
||||
if (!dsp)
|
||||
return;
|
||||
*vol = 0.0;
|
||||
|
||||
xsiz = (size_t)dsp->dsp_xcnt - 1;
|
||||
ysiz = (size_t)dsp->dsp_ycnt - 1;
|
||||
@@ -135,6 +136,8 @@ rt_dsp_volume(fastf_t *vol, const struct rt_db_internal *ip)
|
||||
}
|
||||
|
||||
*vol = solid_vol * stom_volume_scale(dsp->dsp_stom);
|
||||
if (!isfinite(*vol))
|
||||
*vol = -1.0;
|
||||
}
|
||||
|
||||
|
||||
@@ -156,11 +159,12 @@ rt_dsp_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
|
||||
if (!area)
|
||||
return;
|
||||
*area = 0.0;
|
||||
*area = -1.0;
|
||||
|
||||
dsp = rt_dsp_internal_from_ip(ip);
|
||||
if (!dsp)
|
||||
return;
|
||||
*area = 0.0;
|
||||
|
||||
stom = dsp->dsp_stom;
|
||||
xsiz = (size_t)dsp->dsp_xcnt - 1;
|
||||
@@ -236,7 +240,7 @@ rt_dsp_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
}
|
||||
}
|
||||
|
||||
*area = total;
|
||||
*area = isfinite(total) ? total : -1.0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -205,6 +205,7 @@ rt_ebm_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
eip = (struct rt_ebm_internal *)ip->idb_ptr;
|
||||
RT_EBM_CK_MAGIC(eip);
|
||||
VSETALL(*cent, -1.0);
|
||||
|
||||
bu_log("WARNING: EBM centroids have not been verified\n");
|
||||
|
||||
@@ -220,6 +221,8 @@ rt_ebm_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (totalcells == 0)
|
||||
return;
|
||||
avgx = (fastf_t)xsum / totalcells + 0.5;
|
||||
avgy = (fastf_t)ysum / totalcells + 0.5;
|
||||
avgz = eip->tallness / 2;
|
||||
|
||||
@@ -223,7 +223,11 @@ clt_ehy_pack(struct bu_pool *pool, struct soltab *stp)
|
||||
* Create a bounding RPP for an ehy
|
||||
*/
|
||||
C_DECL int
|
||||
rt_ehy_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *UNUSED(tol)) {
|
||||
rt_ehy_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *tol) {
|
||||
int nu_bbox = _rt_nonuniform_bbox(ip, min, max, tol);
|
||||
if (nu_bbox)
|
||||
return (nu_bbox > 0) ? 0 : -1;
|
||||
|
||||
struct rt_ehy_internal *xip;
|
||||
vect_t ehy_A, ehy_B, ehy_An, ehy_Bn, ehy_H;
|
||||
vect_t pt1, pt2, pt3, pt4, pt5, pt6, pt7, pt8;
|
||||
@@ -356,7 +360,7 @@ rt_ehy_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
|
||||
|
||||
if (rt_ehy_bbox(ip, &(stp->st_min), &(stp->st_max), &rtip->rti_tol)) return 1;
|
||||
|
||||
return 0; /* OK */
|
||||
return _rt_nonuniform_prep_finalize(stp, ip, &rtip->rti_tol);
|
||||
}
|
||||
|
||||
|
||||
@@ -392,6 +396,9 @@ rt_ehy_print(const struct soltab *stp)
|
||||
C_DECL int
|
||||
rt_ehy_shot(struct soltab *stp, struct xray *rp, struct application *ap, struct seg *seghead)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp)
|
||||
return _rt_nonuniform_shot(stp, rp, ap, seghead);
|
||||
|
||||
struct ehy_specific *ehy =
|
||||
(struct ehy_specific *)stp->st_specific;
|
||||
vect_t dp; /* D' */
|
||||
@@ -654,6 +661,11 @@ rt_ehy_vshot(struct soltab **stp, struct xray **rp, struct seg *segp, int n, str
|
||||
C_DECL void
|
||||
rt_ehy_norm(struct hit *hitp, struct soltab *stp, struct xray *rp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_norm(hitp, stp, rp);
|
||||
return;
|
||||
}
|
||||
|
||||
vect_t can_normal; /* normal to canonical ehy */
|
||||
fastf_t cp, scale;
|
||||
struct ehy_specific *ehy =
|
||||
@@ -690,6 +702,11 @@ rt_ehy_norm(struct hit *hitp, struct soltab *stp, struct xray *rp)
|
||||
C_DECL void
|
||||
rt_ehy_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_curve(cvp, hitp, stp);
|
||||
return;
|
||||
}
|
||||
|
||||
fastf_t a, b, c, scale;
|
||||
mat_t M1, M2;
|
||||
struct ehy_specific *ehy =
|
||||
@@ -749,6 +766,11 @@ rt_ehy_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp)
|
||||
C_DECL void
|
||||
rt_ehy_uv(struct application *ap, struct soltab *stp, struct hit *hitp, struct uvcoord *uvp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_uv(ap, stp, hitp, uvp);
|
||||
return;
|
||||
}
|
||||
|
||||
struct ehy_specific *ehy =
|
||||
(struct ehy_specific *)stp->st_specific;
|
||||
vect_t work;
|
||||
@@ -1004,6 +1026,7 @@ ehy_ellipse_points(
|
||||
C_DECL int
|
||||
rt_ehy_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bn_tol *UNUSED(tol), const struct bview *v, fastf_t s_size)
|
||||
{
|
||||
struct rt_nonuniform_vlist_state nonuniform_state;
|
||||
vect_t ehy_H, Hu, Au, Bu;
|
||||
fastf_t mag_H, z, z_step, c, r1, r2;
|
||||
int i, num_curve_points, num_ellipse_points, num_curves;
|
||||
@@ -1015,6 +1038,7 @@ rt_ehy_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const str
|
||||
|
||||
BU_CK_LIST_HEAD(vhead);
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
_rt_nonuniform_vlist_state_init(&nonuniform_state, vhead);
|
||||
ehy = (struct rt_ehy_internal *)ip->idb_ptr;
|
||||
RT_EHY_CK_MAGIC(ehy);
|
||||
|
||||
@@ -1080,13 +1104,14 @@ rt_ehy_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const str
|
||||
bu_free(node, "rt_pnt_node");
|
||||
}
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
|
||||
C_DECL int
|
||||
rt_ehy_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_tess_tol *ttol, const struct bn_tol *UNUSED(tol), const struct bview *UNUSED(info))
|
||||
{
|
||||
struct rt_nonuniform_vlist_state nonuniform_state;
|
||||
struct bu_list *vlfree = &rt_vlfree;
|
||||
fastf_t c, dtol, mag_h, ntol, r1, r2;
|
||||
fastf_t min_abs;
|
||||
@@ -1101,6 +1126,7 @@ rt_ehy_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_te
|
||||
|
||||
BU_CK_LIST_HEAD(vhead);
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
_rt_nonuniform_vlist_state_init(&nonuniform_state, vhead);
|
||||
xip = (struct rt_ehy_internal *)ip->idb_ptr;
|
||||
|
||||
if (!ehy_is_valid(xip)) {
|
||||
@@ -1353,7 +1379,7 @@ rt_ehy_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_te
|
||||
bu_free((char *)pts_dbl, "dbl ints");
|
||||
bu_free((char *)segs_per_ell, "segs_per_ell");
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
|
||||
@@ -1932,7 +1958,7 @@ rt_ehy_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, co
|
||||
|
||||
bu_ptbl_free(&vert_tab);
|
||||
bu_free((char *)segs_per_ell, "segs_per_ell");
|
||||
return 0;
|
||||
return _rt_nonuniform_tess_finalize(*r, ip, tol);
|
||||
|
||||
fail:
|
||||
/* free mem */
|
||||
@@ -2036,6 +2062,8 @@ rt_ehy_export4(struct bu_external *ep, const struct rt_db_internal *ip, double l
|
||||
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
if (ip->idb_type != ID_EHY) return -1;
|
||||
if (_rt_nonuniform_export4_check("rt_ehy_export4", ip) < 0)
|
||||
return -1;
|
||||
xip = (struct rt_ehy_internal *)ip->idb_ptr;
|
||||
RT_EHY_CK_MAGIC(xip);
|
||||
|
||||
@@ -2093,23 +2121,42 @@ rt_ehy_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
|
||||
struct rt_ehy_internal *top = (struct rt_ehy_internal *)rop->idb_ptr;
|
||||
RT_EHY_CK_MAGIC(top);
|
||||
|
||||
mat_t effective_mat;
|
||||
int remove_nonuniform = 0;
|
||||
{
|
||||
int nonuniform = _rt_nonuniform_transform_resolve(effective_mat, &remove_nonuniform, ip, mat);
|
||||
if (nonuniform < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (nonuniform) {
|
||||
*top = *tip;
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
return (_rt_nonuniform_attr_compose(rop, mat) < 0) ? BRLCAD_ERROR : BRLCAD_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (remove_nonuniform && _rt_nonuniform_attr_remove(rop) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
vect_t eV, eH, eAu;
|
||||
double er1, er2, ec;
|
||||
VMOVE(eV, tip->ehy_V);
|
||||
VMOVE(eH, tip->ehy_H);
|
||||
VMOVE(eAu, tip->ehy_Au);
|
||||
er1 = tip->ehy_r1 / mat[15];
|
||||
er2 = tip->ehy_r2 / mat[15];
|
||||
ec = tip->ehy_c / mat[15];
|
||||
er1 = tip->ehy_r1 / effective_mat[15];
|
||||
er2 = tip->ehy_r2 / effective_mat[15];
|
||||
ec = tip->ehy_c / effective_mat[15];
|
||||
|
||||
if (er1 <= SMALL_FASTF || er2 <= SMALL_FASTF || ec <= SMALL_FASTF) {
|
||||
bu_log("rt_ehy_mat: r1, r2, or c are zero\n");
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
MAT4X3PNT(top->ehy_V, mat, eV);
|
||||
MAT4X3VEC(top->ehy_H, mat, eH);
|
||||
MAT4X3VEC(top->ehy_Au, mat, eAu);
|
||||
MAT4X3PNT(top->ehy_V, effective_mat, eV);
|
||||
MAT4X3VEC(top->ehy_H, effective_mat, eH);
|
||||
MAT4X3VEC(top->ehy_Au, effective_mat, eAu);
|
||||
VUNITIZE(top->ehy_Au);
|
||||
top->ehy_r1 = er1;
|
||||
top->ehy_r2 = er2;
|
||||
@@ -2435,6 +2482,9 @@ ehy_is_valid(struct rt_ehy_internal *ehy)
|
||||
C_DECL void
|
||||
rt_ehy_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_surf_area(area, ip))
|
||||
return;
|
||||
|
||||
struct rt_ehy_internal *eip;
|
||||
fastf_t h, r, c, P, alpha, B, sqrtAlpha;
|
||||
fastf_t u_top, u_bot, v_top, v_bot, F_top, F_bot;
|
||||
@@ -2505,6 +2555,9 @@ rt_ehy_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_ehy_volume(fastf_t *volume, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_volume(volume, ip))
|
||||
return;
|
||||
|
||||
struct rt_ehy_internal *eip;
|
||||
fastf_t h, c;
|
||||
|
||||
@@ -2543,6 +2596,9 @@ rt_ehy_volume(fastf_t *volume, const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_ehy_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_centroid(cent, ip))
|
||||
return;
|
||||
|
||||
struct rt_ehy_internal *eip;
|
||||
fastf_t h, c, z_c;
|
||||
vect_t Hu;
|
||||
|
||||
@@ -218,7 +218,11 @@ clt_epa_pack(struct bu_pool *pool, struct soltab *stp)
|
||||
* Create a bounding RPP for an epa
|
||||
*/
|
||||
C_DECL int
|
||||
rt_epa_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *UNUSED(tol)) {
|
||||
rt_epa_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *tol) {
|
||||
int nu_bbox = _rt_nonuniform_bbox(ip, min, max, tol);
|
||||
if (nu_bbox)
|
||||
return (nu_bbox > 0) ? 0 : -1;
|
||||
|
||||
struct rt_epa_internal *xip;
|
||||
vect_t epa_A, epa_B, epa_An, epa_Bn, epa_H;
|
||||
vect_t pt1, pt2, pt3, pt4, pt5, pt6, pt7, pt8;
|
||||
@@ -355,7 +359,7 @@ rt_epa_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
|
||||
/* Calculate bounding box (RPP) */
|
||||
if (rt_epa_bbox(ip, &(stp->st_min), &(stp->st_max), &rtip->rti_tol)) return 1;
|
||||
|
||||
return 0; /* OK */
|
||||
return _rt_nonuniform_prep_finalize(stp, ip, &rtip->rti_tol);
|
||||
}
|
||||
|
||||
|
||||
@@ -390,6 +394,9 @@ rt_epa_print(const struct soltab *stp)
|
||||
C_DECL int
|
||||
rt_epa_shot(struct soltab *stp, struct xray *rp, struct application *ap, struct seg *seghead)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp)
|
||||
return _rt_nonuniform_shot(stp, rp, ap, seghead);
|
||||
|
||||
struct epa_specific *epa =
|
||||
(struct epa_specific *)stp->st_specific;
|
||||
vect_t dprime; /* D' */
|
||||
@@ -631,6 +638,11 @@ rt_epa_vshot(struct soltab **stp, struct xray **rp, struct seg *segp, int n, str
|
||||
C_DECL void
|
||||
rt_epa_norm(struct hit *hitp, struct soltab *stp, struct xray *rp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_norm(hitp, stp, rp);
|
||||
return;
|
||||
}
|
||||
|
||||
fastf_t scale;
|
||||
vect_t can_normal; /* normal to canonical epa */
|
||||
struct epa_specific *epa =
|
||||
@@ -666,6 +678,11 @@ rt_epa_norm(struct hit *hitp, struct soltab *stp, struct xray *rp)
|
||||
C_DECL void
|
||||
rt_epa_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_curve(cvp, hitp, stp);
|
||||
return;
|
||||
}
|
||||
|
||||
fastf_t a, b, c, scale;
|
||||
mat_t M1, M2;
|
||||
struct epa_specific *epa =
|
||||
@@ -721,6 +738,11 @@ rt_epa_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp)
|
||||
C_DECL void
|
||||
rt_epa_uv(struct application *ap, struct soltab *stp, struct hit *hitp, struct uvcoord *uvp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_uv(ap, stp, hitp, uvp);
|
||||
return;
|
||||
}
|
||||
|
||||
struct epa_specific *epa =
|
||||
(struct epa_specific *)stp->st_specific;
|
||||
|
||||
@@ -952,6 +974,7 @@ epa_ellipse_points(
|
||||
C_DECL int
|
||||
rt_epa_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bn_tol *UNUSED(tol), const struct bview *v, fastf_t s_size)
|
||||
{
|
||||
struct rt_nonuniform_vlist_state nonuniform_state;
|
||||
vect_t epa_H, Hu, Au, Bu;
|
||||
fastf_t mag_H, z, z_step, r1, r2;
|
||||
int i, num_curve_points, num_ellipse_points, num_curves;
|
||||
@@ -960,6 +983,7 @@ rt_epa_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const str
|
||||
|
||||
BU_CK_LIST_HEAD(vhead);
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
_rt_nonuniform_vlist_state_init(&nonuniform_state, vhead);
|
||||
|
||||
struct bu_list *vlfree = &rt_vlfree;
|
||||
epa = (struct rt_epa_internal *)ip->idb_ptr;
|
||||
@@ -1032,12 +1056,13 @@ rt_epa_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const str
|
||||
bu_free(node, "rt_pnt_node");
|
||||
}
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
C_DECL int
|
||||
rt_epa_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_tess_tol *ttol, const struct bn_tol *UNUSED(tol), const struct bview *UNUSED(info))
|
||||
{
|
||||
struct rt_nonuniform_vlist_state nonuniform_state;
|
||||
struct bu_list *vlfree = &rt_vlfree;
|
||||
fastf_t dtol, mag_h, ntol, r1, r2;
|
||||
fastf_t min_abs;
|
||||
@@ -1053,6 +1078,7 @@ rt_epa_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_te
|
||||
|
||||
BU_CK_LIST_HEAD(vhead);
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
_rt_nonuniform_vlist_state_init(&nonuniform_state, vhead);
|
||||
|
||||
xip = (struct rt_epa_internal *)ip->idb_ptr;
|
||||
if (!epa_is_valid(xip)) {
|
||||
@@ -1314,7 +1340,7 @@ rt_epa_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_te
|
||||
bu_free((char *)pts_dbl, "dbl ints");
|
||||
bu_free((char *)segs_per_ell, "segs_per_ell");
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
|
||||
@@ -1860,7 +1886,7 @@ rt_epa_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, co
|
||||
bu_free((char *)pts_dbl, "dbl ints");
|
||||
bu_free((char *)vells, "vertex [][]");
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_tess_finalize(*r, ip, tol);
|
||||
|
||||
fail:
|
||||
/* free mem */
|
||||
@@ -1964,6 +1990,8 @@ rt_epa_export4(struct bu_external *ep, const struct rt_db_internal *ip, double l
|
||||
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
if (ip->idb_type != ID_EPA) return -1;
|
||||
if (_rt_nonuniform_export4_check("rt_epa_export4", ip) < 0)
|
||||
return -1;
|
||||
xip = (struct rt_epa_internal *)ip->idb_ptr;
|
||||
RT_EPA_CK_MAGIC(xip);
|
||||
|
||||
@@ -2020,23 +2048,42 @@ rt_epa_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
|
||||
struct rt_epa_internal *top = (struct rt_epa_internal *)rop->idb_ptr;
|
||||
RT_EPA_CK_MAGIC(top);
|
||||
|
||||
mat_t effective_mat;
|
||||
int remove_nonuniform = 0;
|
||||
{
|
||||
int nonuniform = _rt_nonuniform_transform_resolve(effective_mat, &remove_nonuniform, ip, mat);
|
||||
if (nonuniform < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (nonuniform) {
|
||||
*top = *tip;
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
return (_rt_nonuniform_attr_compose(rop, mat) < 0) ? BRLCAD_ERROR : BRLCAD_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (remove_nonuniform && _rt_nonuniform_attr_remove(rop) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
vect_t eV, eH, eAu;
|
||||
double epa_r1, epa_r2;
|
||||
|
||||
VMOVE(eV, tip->epa_V);
|
||||
VMOVE(eH, tip->epa_H);
|
||||
VMOVE(eAu, tip->epa_Au);
|
||||
epa_r1 = tip->epa_r1 / mat[15];
|
||||
epa_r2 = tip->epa_r2 / mat[15];
|
||||
epa_r1 = tip->epa_r1 / effective_mat[15];
|
||||
epa_r2 = tip->epa_r2 / effective_mat[15];
|
||||
|
||||
if (epa_r1 <= SMALL_FASTF || epa_r2 <= SMALL_FASTF) {
|
||||
bu_log("rt_epa_mat: r1 or r2 are zero\n");
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
MAT4X3PNT(top->epa_V, mat, eV);
|
||||
MAT4X3VEC(top->epa_H, mat, eH);
|
||||
MAT4X3VEC(top->epa_Au, mat, eAu);
|
||||
MAT4X3PNT(top->epa_V, effective_mat, eV);
|
||||
MAT4X3VEC(top->epa_H, effective_mat, eH);
|
||||
MAT4X3VEC(top->epa_Au, effective_mat, eAu);
|
||||
VUNITIZE(top->epa_Au);
|
||||
top->epa_r1 = epa_r1;
|
||||
top->epa_r2 = epa_r2;
|
||||
@@ -2075,9 +2122,7 @@ rt_epa_import5(struct rt_db_internal *ip, const struct bu_external *ep, const fa
|
||||
|
||||
/* Sanity */
|
||||
if (mat == NULL) mat = bn_mat_identity;
|
||||
double epa_r1 = vec[3*3] / mat[15];
|
||||
double epa_r2 = vec[3*3+1] / mat[15];
|
||||
if (epa_r1 <= SMALL_FASTF || epa_r2 <= SMALL_FASTF) {
|
||||
if (vec[3*3] <= SMALL_FASTF || vec[3*3+1] <= SMALL_FASTF) {
|
||||
bu_log("rt_epa_import5: r1 or r2 are zero\n");
|
||||
bu_free((char *)ip->idb_ptr, "rt_epa_import5: ip->idb_ptr");
|
||||
return -1;
|
||||
@@ -2092,20 +2137,7 @@ rt_epa_import5(struct rt_db_internal *ip, const struct bu_external *ep, const fa
|
||||
xip->epa_r2 = vec[3*3+1];
|
||||
|
||||
/* Apply modeling transformations */
|
||||
MAT4X3PNT(xip->epa_V, mat, &vec[0*3]);
|
||||
MAT4X3VEC(xip->epa_H, mat, &vec[1*3]);
|
||||
MAT4X3VEC(xip->epa_Au, mat, &vec[2*3]);
|
||||
VUNITIZE(xip->epa_Au);
|
||||
xip->epa_r1 = vec[3*3] / mat[15];
|
||||
xip->epa_r2 = vec[3*3+1] / mat[15];
|
||||
|
||||
if (xip->epa_r1 <= SMALL_FASTF || xip->epa_r2 <= SMALL_FASTF) {
|
||||
bu_log("rt_epa_import5: r1 or r2 are zero\n");
|
||||
bu_free((char *)ip->idb_ptr, "rt_epa_import4: ip->idb_ptr");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0; /* OK */
|
||||
return rt_epa_mat(ip, mat, ip);
|
||||
}
|
||||
|
||||
|
||||
@@ -2266,6 +2298,9 @@ rt_epa_params(struct pc_pc_set *ps, const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_epa_volume(fastf_t *vol, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_volume(vol, ip))
|
||||
return;
|
||||
|
||||
fastf_t mag_h;
|
||||
struct rt_epa_internal *xip = (struct rt_epa_internal *)ip->idb_ptr;
|
||||
RT_EPA_CK_MAGIC(xip);
|
||||
@@ -2278,6 +2313,9 @@ rt_epa_volume(fastf_t *vol, const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_epa_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_centroid(cent, ip))
|
||||
return;
|
||||
|
||||
struct rt_epa_internal *xip = (struct rt_epa_internal *)ip->idb_ptr;
|
||||
RT_EPA_CK_MAGIC(xip);
|
||||
VJOIN1(*cent, xip->epa_V, 1.0/3.0, xip->epa_H);
|
||||
@@ -2287,6 +2325,9 @@ rt_epa_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_epa_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_surf_area(area, ip))
|
||||
return;
|
||||
|
||||
fastf_t magsq_h, m;
|
||||
struct rt_epa_internal *xip = (struct rt_epa_internal *)ip->idb_ptr;
|
||||
RT_EPA_CK_MAGIC(xip);
|
||||
|
||||
@@ -193,8 +193,12 @@ clt_eto_pack(struct bu_pool *pool, struct soltab *stp)
|
||||
* Calculate bounding RPP of elliptical torus
|
||||
*/
|
||||
C_DECL int
|
||||
rt_eto_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *UNUSED(tol))
|
||||
rt_eto_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *tol)
|
||||
{
|
||||
int nu_bbox = _rt_nonuniform_bbox(ip, min, max, tol);
|
||||
if (nu_bbox)
|
||||
return (nu_bbox > 0) ? 0 : -1;
|
||||
|
||||
vect_t P, Nu, w1; /* for RPP calculation */
|
||||
fastf_t f, eto_rc;
|
||||
struct rt_eto_internal *tip;
|
||||
@@ -320,7 +324,7 @@ rt_eto_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
|
||||
|
||||
if (rt_eto_bbox(ip, &(stp->st_min), &(stp->st_max), &rtip->rti_tol)) return 1;
|
||||
|
||||
return 0; /* OK */
|
||||
return _rt_nonuniform_prep_finalize(stp, ip, &rtip->rti_tol);
|
||||
}
|
||||
|
||||
|
||||
@@ -374,6 +378,9 @@ rt_eto_print(const struct soltab *stp)
|
||||
C_DECL int
|
||||
rt_eto_shot(struct soltab *stp, struct xray *rp, struct application *ap, struct seg *seghead)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp)
|
||||
return _rt_nonuniform_shot(stp, rp, ap, seghead);
|
||||
|
||||
struct eto_specific *eto =
|
||||
(struct eto_specific *)stp->st_specific;
|
||||
struct seg *segp;
|
||||
@@ -733,6 +740,11 @@ rt_eto_vshot(struct soltab **stp, struct xray **rp, struct seg *segp, int n, str
|
||||
C_DECL void
|
||||
rt_eto_norm(struct hit *hitp, struct soltab *stp, struct xray *rp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_norm(hitp, stp, rp);
|
||||
return;
|
||||
}
|
||||
|
||||
struct eto_specific *eto =
|
||||
(struct eto_specific *)stp->st_specific;
|
||||
fastf_t sqrt_x2y2, efact, ffact, xcomp, ycomp, zcomp;
|
||||
@@ -767,6 +779,11 @@ rt_eto_norm(struct hit *hitp, struct soltab *stp, struct xray *rp)
|
||||
C_DECL void
|
||||
rt_eto_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_curve(cvp, hitp, stp);
|
||||
return;
|
||||
}
|
||||
|
||||
fastf_t a, b, ch, cv, dh, dv, k_circ, k_ell, phi, rad, xp,
|
||||
yp1, yp2, work;
|
||||
struct eto_specific *eto =
|
||||
@@ -830,6 +847,11 @@ rt_eto_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp)
|
||||
C_DECL void
|
||||
rt_eto_uv(struct application *ap, struct soltab *stp, struct hit *hitp, struct uvcoord *uvp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_uv(ap, stp, hitp, uvp);
|
||||
return;
|
||||
}
|
||||
|
||||
fastf_t horz, theta_u, theta_v, vert;
|
||||
vect_t Hit_Ell, Nu, Radius, Ru;
|
||||
|
||||
@@ -1097,6 +1119,7 @@ eto_ellipse_points(
|
||||
C_DECL int
|
||||
rt_eto_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bn_tol *tol, const struct bview *v, fastf_t s_size)
|
||||
{
|
||||
struct rt_nonuniform_vlist_state nonuniform_state;
|
||||
struct rt_eto_internal *eto;
|
||||
fastf_t radian, radian_step;
|
||||
vect_t ellipse_A, ellipse_B, contour_A, contour_B, I, J;
|
||||
@@ -1106,6 +1129,7 @@ rt_eto_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const str
|
||||
|
||||
BU_CK_LIST_HEAD(vhead);
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
_rt_nonuniform_vlist_state_init(&nonuniform_state, vhead);
|
||||
|
||||
struct bu_list *vlfree = &rt_vlfree;
|
||||
eto = (struct rt_eto_internal *)ip->idb_ptr;
|
||||
@@ -1224,7 +1248,7 @@ rt_eto_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const str
|
||||
radian += radian_step;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1239,6 +1263,7 @@ rt_eto_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const str
|
||||
C_DECL int
|
||||
rt_eto_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_tess_tol *ttol, const struct bn_tol *UNUSED(tol), const struct bview *UNUSED(info))
|
||||
{
|
||||
struct rt_nonuniform_vlist_state nonuniform_state;
|
||||
fastf_t a, b; /* axis lengths of ellipse */
|
||||
fastf_t ang, ch, cv, dh, dv, ntol, dtol, phi, theta;
|
||||
fastf_t *eto_ells;
|
||||
@@ -1250,6 +1275,7 @@ rt_eto_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_te
|
||||
|
||||
BU_CK_LIST_HEAD(vhead);
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
_rt_nonuniform_vlist_state_init(&nonuniform_state, vhead);
|
||||
|
||||
struct bu_list *vlfree = &rt_vlfree;
|
||||
tip = (struct rt_eto_internal *)ip->idb_ptr;
|
||||
@@ -1362,7 +1388,7 @@ rt_eto_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_te
|
||||
}
|
||||
|
||||
bu_free((char *)eto_ells, "ells[]");
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
|
||||
@@ -1794,7 +1820,7 @@ rt_eto_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, co
|
||||
bu_free((char *)faces, "rt_eto_tess *faces[]");
|
||||
bu_free((char *)norms, "rt_eto_tess: norms[]");
|
||||
|
||||
return fail;
|
||||
return fail ? fail : _rt_nonuniform_tess_finalize(*r, ip, tol);
|
||||
}
|
||||
|
||||
|
||||
@@ -1878,6 +1904,8 @@ rt_eto_export4(struct bu_external *ep, const struct rt_db_internal *ip, double l
|
||||
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
if (ip->idb_type != ID_ETO) return -1;
|
||||
if (_rt_nonuniform_export4_check("rt_eto_export4", ip) < 0)
|
||||
return -1;
|
||||
|
||||
tip = (struct rt_eto_internal *)ip->idb_ptr;
|
||||
if (!eto_is_valid(tip)) {
|
||||
@@ -1913,6 +1941,25 @@ rt_eto_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
|
||||
struct rt_eto_internal *top = (struct rt_eto_internal *)rop->idb_ptr;
|
||||
RT_ETO_CK_MAGIC(top);
|
||||
|
||||
mat_t effective_mat;
|
||||
int remove_nonuniform = 0;
|
||||
{
|
||||
int nonuniform = _rt_nonuniform_transform_resolve(effective_mat, &remove_nonuniform, ip, mat);
|
||||
if (nonuniform < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (nonuniform) {
|
||||
*top = *tip;
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
return (_rt_nonuniform_attr_compose(rop, mat) < 0) ? BRLCAD_ERROR : BRLCAD_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (remove_nonuniform && _rt_nonuniform_attr_remove(rop) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
vect_t eV, eN, eC;
|
||||
double er, erd;
|
||||
VMOVE(eV, tip->eto_V);
|
||||
@@ -1921,11 +1968,11 @@ rt_eto_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
|
||||
er = tip->eto_r;
|
||||
erd = tip->eto_rd;
|
||||
|
||||
MAT4X3PNT(top->eto_V, mat, eV);
|
||||
MAT4X3VEC(top->eto_N, mat, eN);
|
||||
MAT4X3VEC(top->eto_C, mat, eC);
|
||||
top->eto_r = er / mat[15];
|
||||
top->eto_rd = erd / mat[15];
|
||||
MAT4X3PNT(top->eto_V, effective_mat, eV);
|
||||
MAT4X3VEC(top->eto_N, effective_mat, eN);
|
||||
MAT4X3VEC(top->eto_C, effective_mat, eC);
|
||||
top->eto_r = er / effective_mat[15];
|
||||
top->eto_rd = erd / effective_mat[15];
|
||||
|
||||
return BRLCAD_OK;
|
||||
}
|
||||
@@ -1969,7 +2016,8 @@ rt_eto_import5(struct rt_db_internal *ip, const struct bu_external *ep, const fa
|
||||
|
||||
/* Apply modeling transformations */
|
||||
if (mat == NULL) mat = bn_mat_identity;
|
||||
rt_eto_mat(ip, mat, ip);
|
||||
if (rt_eto_mat(ip, mat, ip) < 0)
|
||||
return -1;
|
||||
|
||||
if (!eto_is_valid(tip))
|
||||
return -1;
|
||||
@@ -2148,6 +2196,9 @@ eto_is_self_intersecting(const struct rt_eto_internal *tip)
|
||||
C_DECL void
|
||||
rt_eto_volume(fastf_t *vol, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_volume(vol, ip))
|
||||
return;
|
||||
|
||||
fastf_t mag_c;
|
||||
struct rt_eto_internal *tip = (struct rt_eto_internal *)ip->idb_ptr;
|
||||
RT_ETO_CK_MAGIC(tip);
|
||||
@@ -2170,6 +2221,9 @@ rt_eto_volume(fastf_t *vol, const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_eto_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_centroid(cent, ip))
|
||||
return;
|
||||
|
||||
struct rt_eto_internal *tip = (struct rt_eto_internal *)ip->idb_ptr;
|
||||
RT_ETO_CK_MAGIC(tip);
|
||||
VMOVE(*cent, tip->eto_V);
|
||||
@@ -2179,6 +2233,9 @@ rt_eto_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_eto_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_surf_area(area, ip))
|
||||
return;
|
||||
|
||||
fastf_t circum, mag_c;
|
||||
struct rt_eto_internal *tip = (struct rt_eto_internal *)ip->idb_ptr;
|
||||
RT_ETO_CK_MAGIC(tip);
|
||||
|
||||
@@ -353,7 +353,7 @@ rt_extrude_volume(fastf_t *vol, const struct rt_db_internal *ip)
|
||||
struct rt_extrude_internal *eip;
|
||||
struct rt_sketch_internal *skt;
|
||||
struct rt_db_internal db_skt;
|
||||
fastf_t area;
|
||||
fastf_t area = -1.0;
|
||||
fastf_t h_norm;
|
||||
fastf_t u_norm;
|
||||
fastf_t v_norm;
|
||||
@@ -365,7 +365,10 @@ rt_extrude_volume(fastf_t *vol, const struct rt_db_internal *ip)
|
||||
RT_DB_INTERNAL_INIT(&db_skt);
|
||||
db_skt.idb_ptr = (void *)skt;
|
||||
|
||||
*vol = -1.0;
|
||||
rt_sketch_surf_area(&area, &db_skt);
|
||||
if (!isfinite(area) || NEAR_EQUAL(area, -1.0, SMALL_FASTF))
|
||||
return;
|
||||
|
||||
h_norm = MAGNITUDE(eip->h);
|
||||
u_norm = MAGNITUDE(eip->u_vec);
|
||||
@@ -1508,6 +1511,7 @@ rt_extrude_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
struct rt_sketch_internal *skt;
|
||||
struct rt_db_internal db_skt;
|
||||
point_t skt_cent;
|
||||
point_t error_centroid;
|
||||
point_t middle_h;
|
||||
eip = (struct rt_extrude_internal *)ip->idb_ptr;
|
||||
RT_EXTRUDE_CK_MAGIC(eip);
|
||||
@@ -1517,7 +1521,14 @@ rt_extrude_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
RT_DB_INTERNAL_INIT(&db_skt);
|
||||
db_skt.idb_ptr = (void *)skt;
|
||||
|
||||
VSETALL(*cent, -1.0);
|
||||
VSETALL(skt_cent, -1.0);
|
||||
VSETALL(error_centroid, -1.0);
|
||||
rt_sketch_centroid(&skt_cent, &db_skt);
|
||||
if (!isfinite(skt_cent[X]) || !isfinite(skt_cent[Y]) ||
|
||||
!isfinite(skt_cent[Z]) ||
|
||||
VNEAR_EQUAL(skt_cent, error_centroid, SMALL_FASTF))
|
||||
return;
|
||||
|
||||
VSCALE(middle_h, eip->h, 0.5);
|
||||
VADD2(*cent, skt_cent, middle_h);
|
||||
|
||||
@@ -161,7 +161,11 @@ clt_hyp_pack(struct bu_pool *pool, struct soltab *stp)
|
||||
* Create a bounding RPP for an hyp
|
||||
*/
|
||||
C_DECL int
|
||||
rt_hyp_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *UNUSED(tol)) {
|
||||
rt_hyp_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *tol) {
|
||||
int nu_bbox = _rt_nonuniform_bbox(ip, min, max, tol);
|
||||
if (nu_bbox)
|
||||
return (nu_bbox > 0) ? 0 : -1;
|
||||
|
||||
struct rt_hyp_internal *xip;
|
||||
vect_t hyp_Au, hyp_B, hyp_An, hyp_Bn, hyp_H;
|
||||
vect_t pt1, pt2, pt3, pt4, pt5, pt6, pt7, pt8;
|
||||
@@ -250,7 +254,7 @@ rt_hyp_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
|
||||
|
||||
/* calculate bounding RPP */
|
||||
if (rt_hyp_bbox(ip, &(stp->st_min), &(stp->st_max), &rtip->rti_tol)) return 1;
|
||||
return 0; /* OK */
|
||||
return _rt_nonuniform_prep_finalize(stp, ip, &rtip->rti_tol);
|
||||
}
|
||||
|
||||
|
||||
@@ -285,6 +289,9 @@ rt_hyp_print(const struct soltab *stp)
|
||||
C_DECL int
|
||||
rt_hyp_shot(struct soltab *stp, struct xray *rp, struct application *ap, struct seg *seghead)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp)
|
||||
return _rt_nonuniform_shot(stp, rp, ap, seghead);
|
||||
|
||||
struct hyp_specific *hyp = (struct hyp_specific *)stp->st_specific;
|
||||
struct seg *segp;
|
||||
|
||||
@@ -605,6 +612,11 @@ rt_hyp_vshot(struct soltab **stp, struct xray **rp, struct seg *segp, int n, str
|
||||
C_DECL void
|
||||
rt_hyp_norm(struct hit *hitp, struct soltab *stp, struct xray *rp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_norm(hitp, stp, rp);
|
||||
return;
|
||||
}
|
||||
|
||||
struct hyp_specific *hyp =
|
||||
(struct hyp_specific *)stp->st_specific;
|
||||
|
||||
@@ -652,6 +664,11 @@ rt_hyp_norm(struct hit *hitp, struct soltab *stp, struct xray *rp)
|
||||
C_DECL void
|
||||
rt_hyp_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_curve(cvp, hitp, stp);
|
||||
return;
|
||||
}
|
||||
|
||||
struct hyp_specific *hyp =
|
||||
(struct hyp_specific *)stp->st_specific;
|
||||
vect_t vert, horiz;
|
||||
@@ -728,6 +745,11 @@ rt_hyp_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp)
|
||||
C_DECL void
|
||||
rt_hyp_uv(struct application *ap, struct soltab *stp, struct hit *hitp, struct uvcoord *uvp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_uv(ap, stp, hitp, uvp);
|
||||
return;
|
||||
}
|
||||
|
||||
struct hyp_specific *hyp = (struct hyp_specific *)stp->st_specific;
|
||||
|
||||
if (ap) RT_CK_APPLICATION(ap);
|
||||
@@ -784,6 +806,7 @@ rt_hyp_free(struct soltab *stp)
|
||||
C_DECL int
|
||||
rt_hyp_plot(struct bu_list *vhead, struct rt_db_internal *incoming, const struct bg_tess_tol *UNUSED(ttol), const struct bn_tol *UNUSED(tol), const struct bview *UNUSED(info))
|
||||
{
|
||||
struct rt_nonuniform_vlist_state nonuniform_state;
|
||||
int i, j; /* loop indices */
|
||||
struct rt_hyp_internal *hyp_in;
|
||||
struct hyp_specific *hyp;
|
||||
@@ -799,6 +822,7 @@ rt_hyp_plot(struct bu_list *vhead, struct rt_db_internal *incoming, const struct
|
||||
|
||||
BU_CK_LIST_HEAD(vhead);
|
||||
RT_CK_DB_INTERNAL(incoming);
|
||||
_rt_nonuniform_vlist_state_init(&nonuniform_state, vhead);
|
||||
struct bu_list *vlfree = &rt_vlfree;
|
||||
hyp_in = (struct rt_hyp_internal *)incoming->idb_ptr;
|
||||
RT_HYP_CK_MAGIC(hyp_in);
|
||||
@@ -880,7 +904,7 @@ rt_hyp_plot(struct bu_list *vhead, struct rt_db_internal *incoming, const struct
|
||||
|
||||
BU_PUT(hyp, struct hyp_specific);
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, incoming);
|
||||
}
|
||||
|
||||
|
||||
@@ -1435,7 +1459,7 @@ rt_hyp_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, co
|
||||
|
||||
bu_ptbl_free(&vert_tab);
|
||||
BU_PUT(xip, struct hyp_specific);
|
||||
return 0;
|
||||
return _rt_nonuniform_tess_finalize(*r, ip, tol);
|
||||
|
||||
fail:
|
||||
/* free mem */
|
||||
@@ -1468,15 +1492,34 @@ rt_hyp_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
|
||||
struct rt_hyp_internal *top = (struct rt_hyp_internal *)rop->idb_ptr;
|
||||
RT_HYP_CK_MAGIC(top);
|
||||
|
||||
mat_t effective_mat;
|
||||
int remove_nonuniform = 0;
|
||||
{
|
||||
int nonuniform = _rt_nonuniform_transform_resolve(effective_mat, &remove_nonuniform, ip, mat);
|
||||
if (nonuniform < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (nonuniform) {
|
||||
*top = *tip;
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
return (_rt_nonuniform_attr_compose(rop, mat) < 0) ? BRLCAD_ERROR : BRLCAD_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (remove_nonuniform && _rt_nonuniform_attr_remove(rop) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
vect_t Vi, Hi, A;
|
||||
VMOVE(Vi, tip->hyp_Vi);
|
||||
VMOVE(Hi, tip->hyp_Hi);
|
||||
VMOVE(A, tip->hyp_A);
|
||||
|
||||
MAT4X3PNT(top->hyp_Vi, mat, Vi);
|
||||
MAT4X3VEC(top->hyp_Hi, mat, Hi);
|
||||
MAT4X3VEC(top->hyp_A, mat, A);
|
||||
top->hyp_b = (!ZERO(mat[15])) ? tip->hyp_b / mat[15] : INFINITY;
|
||||
MAT4X3PNT(top->hyp_Vi, effective_mat, Vi);
|
||||
MAT4X3VEC(top->hyp_Hi, effective_mat, Hi);
|
||||
MAT4X3VEC(top->hyp_A, effective_mat, A);
|
||||
top->hyp_b = (!ZERO(effective_mat[15])) ? tip->hyp_b / effective_mat[15] : INFINITY;
|
||||
|
||||
return BRLCAD_OK;
|
||||
}
|
||||
@@ -1679,6 +1722,9 @@ rt_hyp_params(struct pc_pc_set * UNUSED(ps), const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_hyp_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_centroid(cent, ip))
|
||||
return;
|
||||
|
||||
if (cent != NULL && ip != NULL) {
|
||||
struct rt_hyp_internal *hip;
|
||||
|
||||
@@ -1700,6 +1746,9 @@ rt_hyp_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_hyp_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_surf_area(area, ip))
|
||||
return;
|
||||
|
||||
if (!area || !ip)
|
||||
return;
|
||||
do { static const struct rt_crofton_params _p = {50000u, 0.0, 0.0}; rt_crofton_sample(area, NULL, ip, &_p); } while (0);
|
||||
@@ -1709,6 +1758,9 @@ rt_hyp_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_hyp_volume(fastf_t *volume, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_volume(volume, ip))
|
||||
return;
|
||||
|
||||
if (volume != NULL && ip != NULL) {
|
||||
struct rt_hyp_internal *hip;
|
||||
struct hyp_specific *hyp;
|
||||
|
||||
@@ -164,8 +164,12 @@ rt_metaball_get_bounding_sphere(point_t *center, fastf_t threshold, struct rt_me
|
||||
* Calculate a bounding RPP around a metaball
|
||||
*/
|
||||
C_DECL int
|
||||
rt_metaball_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *UNUSED(tol))
|
||||
rt_metaball_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *tol)
|
||||
{
|
||||
int nu_bbox = _rt_nonuniform_bbox(ip, min, max, tol);
|
||||
if (nu_bbox)
|
||||
return (nu_bbox > 0) ? 0 : -1;
|
||||
|
||||
struct rt_metaball_internal *mb;
|
||||
point_t center;
|
||||
fastf_t radius;
|
||||
@@ -181,6 +185,26 @@ rt_metaball_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const st
|
||||
}
|
||||
|
||||
|
||||
C_DECL void
|
||||
rt_metaball_volume(fastf_t *volume, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_volume(volume, ip))
|
||||
return;
|
||||
|
||||
rt_crofton_volume_implicit(volume, ip);
|
||||
}
|
||||
|
||||
|
||||
C_DECL void
|
||||
rt_metaball_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_surf_area(area, ip))
|
||||
return;
|
||||
|
||||
rt_crofton_surf_area_implicit(area, ip);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* prep and build bounding volumes... unfortunately, generating the
|
||||
* bounding sphere is too 'loose' (I think) and O(n^2).
|
||||
@@ -231,7 +255,7 @@ rt_metaball_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rti
|
||||
* XXX this can be optimized greatly to reduce the BSP presence... */
|
||||
if (rt_metaball_bbox(ip, &(stp->st_min), &(stp->st_max), &rtip->rti_tol)) return 1;
|
||||
stp->st_specific = (void *)nmb;
|
||||
return 0;
|
||||
return _rt_nonuniform_prep_finalize(stp, ip, &rtip->rti_tol);
|
||||
}
|
||||
|
||||
|
||||
@@ -366,6 +390,9 @@ rt_metaball_find_intersection(point_t *intersect, const struct rt_metaball_inter
|
||||
C_DECL int
|
||||
rt_metaball_shot(struct soltab *stp, register struct xray *rp, struct application *ap, struct seg *seghead)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp)
|
||||
return _rt_nonuniform_shot(stp, rp, ap, seghead);
|
||||
|
||||
struct rt_metaball_internal *mb = (struct rt_metaball_internal *)stp->st_specific;
|
||||
struct seg *segp = NULL;
|
||||
int retval = 0;
|
||||
@@ -558,6 +585,11 @@ rt_metaball_norm_internal(vect_t *n, point_t *p, struct rt_metaball_internal *mb
|
||||
C_DECL void
|
||||
rt_metaball_norm(register struct hit *hitp, struct soltab *stp, register struct xray *rp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_norm(hitp, stp, rp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rp) RT_CK_RAY(rp); /* unused. */
|
||||
rt_metaball_norm_internal(&(hitp->hit_normal), &(hitp->hit_point), (struct rt_metaball_internal *)(stp->st_specific));
|
||||
return;
|
||||
@@ -570,6 +602,12 @@ rt_metaball_norm(register struct hit *hitp, struct soltab *stp, register struct
|
||||
C_DECL void
|
||||
rt_metaball_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
if (cvp)
|
||||
*cvp = (struct curvature)RT_CURVATURE_INIT_ZERO;
|
||||
return;
|
||||
}
|
||||
|
||||
struct rt_metaball_internal *metaball = (struct rt_metaball_internal *)stp->st_specific;
|
||||
|
||||
if (!metaball || !cvp) return;
|
||||
@@ -590,6 +628,11 @@ rt_metaball_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp)
|
||||
C_DECL void
|
||||
rt_metaball_uv(struct application *ap, struct soltab *stp, struct hit *hitp, struct uvcoord *uvp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_uv(ap, stp, hitp, uvp);
|
||||
return;
|
||||
}
|
||||
|
||||
struct rt_metaball_internal *metaball = (struct rt_metaball_internal *)stp->st_specific;
|
||||
vect_t work, pprime;
|
||||
fastf_t r;
|
||||
@@ -662,6 +705,7 @@ rt_metaball_plot_sph(struct bu_list *vlfree, struct bu_list *vhead, point_t *cen
|
||||
C_DECL int
|
||||
rt_metaball_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_tess_tol *UNUSED(ttol), const struct bn_tol *UNUSED(tol), const struct bview *UNUSED(info))
|
||||
{
|
||||
struct rt_nonuniform_vlist_state nonuniform_state;
|
||||
struct rt_metaball_internal *mb;
|
||||
struct wdb_metaball_pnt *mbpt;
|
||||
point_t bsc;
|
||||
@@ -669,19 +713,20 @@ rt_metaball_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct
|
||||
|
||||
BU_CK_LIST_HEAD(vhead);
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
_rt_nonuniform_vlist_state_init(&nonuniform_state, vhead);
|
||||
struct bu_list *vlfree = &rt_vlfree;
|
||||
mb = (struct rt_metaball_internal *)ip->idb_ptr;
|
||||
RT_METABALL_CK_MAGIC(mb);
|
||||
rad = rt_metaball_get_bounding_sphere(&bsc, mb->threshold, mb);
|
||||
/* cope with the case where 0 points are defined. */
|
||||
if (rad<0)
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
#if PLOT_THE_BIG_BOUNDING_SPHERE
|
||||
rt_metaball_plot_sph(vlfree, vhead, &bsc, rad);
|
||||
#endif
|
||||
for (BU_LIST_FOR(mbpt, wdb_metaball_pnt, &mb->metaball_ctrl_head))
|
||||
rt_metaball_plot_sph(vlfree, vhead, &mbpt->coord, mbpt->field_strength / mb->threshold);
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
C_DECL int
|
||||
@@ -690,6 +735,8 @@ rt_metaball_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_
|
||||
if (!rop || !mat)
|
||||
return BRLCAD_OK;
|
||||
|
||||
const struct rt_db_internal *src = ip ? ip : rop;
|
||||
|
||||
// For the moment, we only support applying a mat to a metaball in place - the
|
||||
// input and output must be the same.
|
||||
if (ip && rop != ip) {
|
||||
@@ -700,12 +747,30 @@ rt_metaball_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_
|
||||
struct rt_metaball_internal *top = (struct rt_metaball_internal *)rop->idb_ptr;
|
||||
RT_METABALL_CK_MAGIC(top);
|
||||
|
||||
mat_t effective_mat;
|
||||
int remove_nonuniform = 0;
|
||||
{
|
||||
int nonuniform = _rt_nonuniform_transform_resolve(effective_mat, &remove_nonuniform, src, mat);
|
||||
if (nonuniform < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (nonuniform) {
|
||||
if (_rt_nonuniform_attr_copy(rop, src) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
return (_rt_nonuniform_attr_compose(rop, mat) < 0) ? BRLCAD_ERROR : BRLCAD_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (_rt_nonuniform_attr_copy(rop, src) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (remove_nonuniform && _rt_nonuniform_attr_remove(rop) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
struct wdb_metaball_pnt *mbpt;
|
||||
for (BU_LIST_FOR(mbpt, wdb_metaball_pnt, &top->metaball_ctrl_head)) {
|
||||
vect_t ctmp;
|
||||
VMOVE(ctmp, mbpt->coord);
|
||||
MAT4X3PNT(mbpt->coord, mat, ctmp);
|
||||
mbpt->field_strength = mbpt->field_strength / mat[15];
|
||||
MAT4X3PNT(mbpt->coord, effective_mat, ctmp);
|
||||
mbpt->field_strength = mbpt->field_strength / effective_mat[15];
|
||||
}
|
||||
|
||||
return BRLCAD_OK;
|
||||
|
||||
@@ -57,6 +57,8 @@
|
||||
|
||||
#include "metaball.h"
|
||||
|
||||
#include "../../librt_private.h"
|
||||
|
||||
/**
|
||||
* Tessellate a metaball.
|
||||
*/
|
||||
@@ -96,7 +98,8 @@ rt_metaball_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *i
|
||||
bu_log("Attempting to tessellate metaball with no control points");
|
||||
return -1;
|
||||
}
|
||||
rt_metaball_bbox(ip, &min, &max, tol);
|
||||
if (_rt_nonuniform_body_bbox(ip, &min, &max, tol) != 0)
|
||||
return -1;
|
||||
|
||||
/* TODO: get better sampling tolerance, unless this is "good enough" */
|
||||
mtol = ttol->abs;
|
||||
@@ -171,7 +174,7 @@ rt_metaball_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *i
|
||||
rt_get_timer(×, NULL);
|
||||
bu_log("metaball tessellate (%d triangles): %s\n", numtri, bu_vls_addr(×));
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_tess_finalize(*r, ip, tol);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -2430,6 +2430,10 @@ rt_nmg_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
struct model *m;
|
||||
struct nmgregion* r;
|
||||
struct bu_list *vlfree = &rt_vlfree;
|
||||
fastf_t total_area = 0.0;
|
||||
int have_faces = 0;
|
||||
|
||||
*area = -1.0;
|
||||
|
||||
/*Iterate through all regions and shells */
|
||||
m = (struct model *)ip->idb_ptr;
|
||||
@@ -2444,6 +2448,8 @@ rt_nmg_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
/*get faces of this shell*/
|
||||
nmg_face_tabulate(&nmg_faces, &s->l.magic, vlfree);
|
||||
num_faces = BU_PTBL_LEN(&nmg_faces);
|
||||
if (num_faces)
|
||||
have_faces = 1;
|
||||
faces = (struct poly_face *)bu_calloc(num_faces, sizeof(struct poly_face), "rt_nmg_surf_area: faces");
|
||||
|
||||
for (i = 0; i < num_faces; i++) {
|
||||
@@ -2452,7 +2458,7 @@ rt_nmg_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
}
|
||||
rt_nmg_faces_area(faces, s, vlfree);
|
||||
for (i = 0; i < num_faces; i++) {
|
||||
*area += faces[i].area;
|
||||
total_area += faces[i].area;
|
||||
}
|
||||
for (i = 0; i < num_faces; i++) {
|
||||
bu_free((char *)faces[i].pts, "rt_nmg_surf_area: pts");
|
||||
@@ -2460,6 +2466,8 @@ rt_nmg_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
bu_free((char *)faces, "rt_nmg_surf_area: faces");
|
||||
}
|
||||
}
|
||||
if (have_faces)
|
||||
*area = total_area;
|
||||
}
|
||||
|
||||
|
||||
@@ -2477,11 +2485,13 @@ rt_nmg_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
size_t i = 0;
|
||||
struct bu_list *vlfree = &rt_vlfree;
|
||||
|
||||
*cent[0] = 0.0;
|
||||
*cent[1] = 0.0;
|
||||
*cent[2] = 0.0;
|
||||
VSETALL(*cent, -1.0);
|
||||
m = (struct model *)ip->idb_ptr;
|
||||
if (BU_LIST_IS_EMPTY(&m->r_hd))
|
||||
return;
|
||||
r = BU_LIST_FIRST(nmgregion, &m->r_hd);
|
||||
if (BU_LIST_IS_EMPTY(&r->s_hd))
|
||||
return;
|
||||
s = BU_LIST_FIRST(shell, &r->s_hd);
|
||||
|
||||
/*get faces*/
|
||||
@@ -2492,6 +2502,8 @@ rt_nmg_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
if (!num_faces)
|
||||
return;
|
||||
|
||||
VSETALL(*cent, 0.0);
|
||||
|
||||
faces = (struct poly_face *)bu_calloc(num_faces, sizeof(struct poly_face), "rt_nmg_centroid: faces");
|
||||
|
||||
for (i = 0; i < num_faces; i++) {
|
||||
@@ -2500,6 +2512,8 @@ rt_nmg_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
}
|
||||
rt_nmg_faces_area(faces, s, vlfree);
|
||||
for (i = 0; i < num_faces; i++) {
|
||||
if (faces[i].npts < 3)
|
||||
goto fail;
|
||||
bg_3d_polygon_centroid(&faces[i].cent, faces[i].npts, (const point_t *) faces[i].pts);
|
||||
VADD2(arbit_point, arbit_point, faces[i].cent);
|
||||
}
|
||||
@@ -2509,7 +2523,6 @@ rt_nmg_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
vect_t tmp = VINIT_ZERO;
|
||||
|
||||
/* calculate volume */
|
||||
volume = 0.0;
|
||||
VSCALE(tmp, faces[i].plane_eqn, faces[i].area);
|
||||
faces[i].vol_pyramid = (VDOT(faces[i].pts[0], tmp)/3);
|
||||
volume += faces[i].vol_pyramid;
|
||||
@@ -2524,12 +2537,22 @@ rt_nmg_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
/* add cent_pyramid to the centroid of the polyhedron */
|
||||
VADD2(*cent, *cent, faces[i].cent_pyramid);
|
||||
}
|
||||
if (NEAR_ZERO(volume, SMALL_FASTF))
|
||||
goto fail;
|
||||
/* reverse the weighting */
|
||||
VSCALE(*cent, *cent, (1/volume));
|
||||
for (i = 0; i < num_faces; i++) {
|
||||
bu_free((char *)faces[i].pts, "rt_nmg_centroid: pts");
|
||||
}
|
||||
bu_free((char *)faces, "rt_nmg_centroid: faces");
|
||||
return;
|
||||
|
||||
fail:
|
||||
for (i = 0; i < num_faces; i++) {
|
||||
bu_free((char *)faces[i].pts, "rt_nmg_centroid: pts");
|
||||
}
|
||||
bu_free((char *)faces, "rt_nmg_centroid: faces");
|
||||
VSETALL(*cent, -1.0);
|
||||
}
|
||||
|
||||
|
||||
@@ -2539,6 +2562,10 @@ rt_nmg_volume(fastf_t *volume, const struct rt_db_internal *ip)
|
||||
struct model *m;
|
||||
struct nmgregion* r;
|
||||
struct bu_list *vlfree = &rt_vlfree;
|
||||
fastf_t total_volume = 0.0;
|
||||
int have_faces = 0;
|
||||
|
||||
*volume = -1.0;
|
||||
|
||||
/*Iterate through all regions and shells */
|
||||
m = (struct model *)ip->idb_ptr;
|
||||
@@ -2553,6 +2580,8 @@ rt_nmg_volume(fastf_t *volume, const struct rt_db_internal *ip)
|
||||
/*get faces of this shell*/
|
||||
nmg_face_tabulate(&nmg_faces, &s->l.magic, vlfree);
|
||||
num_faces = BU_PTBL_LEN(&nmg_faces);
|
||||
if (num_faces)
|
||||
have_faces = 1;
|
||||
faces = (struct poly_face *)bu_calloc(num_faces, sizeof(struct poly_face), "rt_nmg_volume: faces");
|
||||
|
||||
for (i = 0; i < num_faces; i++) {
|
||||
@@ -2562,10 +2591,17 @@ rt_nmg_volume(fastf_t *volume, const struct rt_db_internal *ip)
|
||||
rt_nmg_faces_area(faces, s, vlfree);
|
||||
for (i = 0; i < num_faces; i++) {
|
||||
vect_t tmp = VINIT_ZERO;
|
||||
if (faces[i].npts < 3) {
|
||||
size_t j;
|
||||
for (j = 0; j < num_faces; j++)
|
||||
bu_free((char *)faces[j].pts, "rt_nmg_volume: pts");
|
||||
bu_free((char *)faces, "rt_nmg_volume: faces");
|
||||
return;
|
||||
}
|
||||
|
||||
/* calculate volume of pyramid*/
|
||||
VSCALE(tmp, faces[i].plane_eqn, faces[i].area);
|
||||
*volume = (VDOT(faces[i].pts[0], tmp)/3);
|
||||
total_volume += (VDOT(faces[i].pts[0], tmp)/3);
|
||||
}
|
||||
for (i = 0; i < num_faces; i++) {
|
||||
bu_free((char *)faces[i].pts, "rt_nmg_volume: pts");
|
||||
@@ -2573,6 +2609,8 @@ rt_nmg_volume(fastf_t *volume, const struct rt_db_internal *ip)
|
||||
bu_free((char *)faces, "rt_nmg_volume: faces");
|
||||
}
|
||||
}
|
||||
if (have_faces)
|
||||
*volume = fabs(total_volume);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,7 @@ rt_obj_vshot(struct soltab *stp[], struct xray *rp[], struct seg *segp, int n, s
|
||||
int i;
|
||||
int id;
|
||||
const struct rt_functab *ft;
|
||||
int use_scalar = 0;
|
||||
|
||||
if (!stp || !rp || n < 1)
|
||||
return 0;
|
||||
@@ -39,6 +40,8 @@ rt_obj_vshot(struct soltab *stp[], struct xray *rp[], struct seg *segp, int n, s
|
||||
RT_CK_SOLTAB(stp[i]);
|
||||
if (rp[i])
|
||||
RT_CK_RAY(rp[i]);
|
||||
if (stp[i]->st_nu_inv_matp)
|
||||
use_scalar = 1;
|
||||
}
|
||||
if (segp)
|
||||
RT_CK_SEG(segp);
|
||||
@@ -53,8 +56,34 @@ rt_obj_vshot(struct soltab *stp[], struct xray *rp[], struct seg *segp, int n, s
|
||||
ft = &OBJ[id];
|
||||
if (!ft)
|
||||
return -3;
|
||||
if (!ft->ft_vshot)
|
||||
if (use_scalar && !ft->ft_shot)
|
||||
return -4;
|
||||
if (!use_scalar && !ft->ft_vshot)
|
||||
return -4;
|
||||
|
||||
if (use_scalar) {
|
||||
for (i = 0; i < n; i++) {
|
||||
struct seg seghead;
|
||||
struct seg *tmp_seg;
|
||||
int ret;
|
||||
|
||||
BU_LIST_INIT(&seghead.l);
|
||||
ret = ft->ft_shot(stp[i], rp[i], ap, &seghead);
|
||||
if (ret <= 0) {
|
||||
segp[i].seg_stp = (struct soltab *)0;
|
||||
continue;
|
||||
}
|
||||
tmp_seg = BU_LIST_FIRST(seg, &seghead.l);
|
||||
BU_LIST_DEQUEUE(&tmp_seg->l);
|
||||
segp[i] = *tmp_seg;
|
||||
RT_FREE_SEG(tmp_seg, ap->a_resource);
|
||||
while (BU_LIST_WHILE(tmp_seg, seg, &seghead.l)) {
|
||||
BU_LIST_DEQUEUE(&tmp_seg->l);
|
||||
RT_FREE_SEG(tmp_seg, ap->a_resource);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ft->ft_vshot(stp, rp, segp, n, ap);
|
||||
return 0;
|
||||
|
||||
@@ -271,7 +271,11 @@ clt_part_pack(struct bu_pool *pool, struct soltab *stp)
|
||||
* Compute the bounding RPP for a particle
|
||||
*/
|
||||
C_DECL int
|
||||
rt_part_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *UNUSED(tol)) {
|
||||
rt_part_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *tol) {
|
||||
int nu_bbox = _rt_nonuniform_bbox(ip, min, max, tol);
|
||||
if (nu_bbox)
|
||||
return (nu_bbox > 0) ? 0 : -1;
|
||||
|
||||
struct rt_part_internal *pip;
|
||||
vect_t tip_pt, tmp_min, tmp_max;
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
@@ -350,7 +354,7 @@ rt_part_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
|
||||
/* Compute bounding sphere*/
|
||||
VMOVE(stp->st_center, pip->part_V);
|
||||
stp->st_aradius = stp->st_bradius = pip->part_vrad;
|
||||
return 0; /* OK */
|
||||
return _rt_nonuniform_prep_finalize(stp, ip, &rtip->rti_tol);
|
||||
}
|
||||
|
||||
/* Compute some essential terms */
|
||||
@@ -447,7 +451,7 @@ rt_part_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
|
||||
stp->st_aradius = f;
|
||||
stp->st_bradius = MAGNITUDE(work);
|
||||
}
|
||||
return 0; /* OK */
|
||||
return _rt_nonuniform_prep_finalize(stp, ip, &rtip->rti_tol);
|
||||
}
|
||||
|
||||
|
||||
@@ -495,6 +499,9 @@ rt_part_print(register const struct soltab *stp)
|
||||
C_DECL int
|
||||
rt_part_shot(struct soltab *stp, register struct xray *rp, struct application *ap, struct seg *seghead)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp)
|
||||
return _rt_nonuniform_shot(stp, rp, ap, seghead);
|
||||
|
||||
register struct part_specific *part =
|
||||
(struct part_specific *)stp->st_specific;
|
||||
struct seg *segp;
|
||||
@@ -1097,6 +1104,11 @@ rt_part_vshot(struct soltab **stp, struct xray **rp, struct seg *segp, int n, st
|
||||
C_DECL void
|
||||
rt_part_norm(register struct hit *hitp, struct soltab *stp, register struct xray *rp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_norm(hitp, stp, rp);
|
||||
return;
|
||||
}
|
||||
|
||||
register struct part_specific *part =
|
||||
(struct part_specific *)stp->st_specific;
|
||||
|
||||
@@ -1146,6 +1158,11 @@ rt_part_norm(register struct hit *hitp, struct soltab *stp, register struct xray
|
||||
C_DECL void
|
||||
rt_part_curve(register struct curvature *cvp, register struct hit *hitp, struct soltab *stp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_curve(cvp, hitp, stp);
|
||||
return;
|
||||
}
|
||||
|
||||
register struct part_specific *part =
|
||||
(struct part_specific *)stp->st_specific;
|
||||
point_t hit_local; /* hit_point, with V as origin */
|
||||
@@ -1192,6 +1209,11 @@ rt_part_curve(register struct curvature *cvp, register struct hit *hitp, struct
|
||||
C_DECL void
|
||||
rt_part_uv(struct application *ap, struct soltab *stp, register struct hit *hitp, register struct uvcoord *uvp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_uv(ap, stp, hitp, uvp);
|
||||
return;
|
||||
}
|
||||
|
||||
register const struct part_specific *part =
|
||||
(struct part_specific *)stp->st_specific;
|
||||
point_t hit_local; /* hit_point, with V as origin */
|
||||
@@ -1278,6 +1300,7 @@ rt_part_hemisphere(register point_t (*ov), register fastf_t *v, fastf_t *a, fast
|
||||
C_DECL int
|
||||
rt_part_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_tess_tol *UNUSED(ttol), const struct bn_tol *UNUSED(tol), const struct bview *UNUSED(info))
|
||||
{
|
||||
struct rt_nonuniform_vlist_state nonuniform_state;
|
||||
struct rt_part_internal *pip;
|
||||
point_t tail;
|
||||
point_t sphere_rim[16];
|
||||
@@ -1290,6 +1313,7 @@ rt_part_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_t
|
||||
|
||||
BU_CK_LIST_HEAD(vhead);
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
_rt_nonuniform_vlist_state_init(&nonuniform_state, vhead);
|
||||
struct bu_list *vlfree = &rt_vlfree;
|
||||
pip = (struct rt_part_internal *)ip->idb_ptr;
|
||||
RT_PART_CK_MAGIC(pip);
|
||||
@@ -1317,7 +1341,7 @@ rt_part_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_t
|
||||
for (i=0; i<16; i++) {
|
||||
BV_ADD_VLIST(vlfree, vhead, sphere_rim[i], BV_VLIST_LINE_DRAW);
|
||||
}
|
||||
return 0; /* OK */
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
VMOVE(Hunit, pip->part_H);
|
||||
@@ -1378,7 +1402,7 @@ rt_part_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_t
|
||||
BV_ADD_VLIST(vlfree, vhead, vhemi[6], BV_VLIST_LINE_MOVE);
|
||||
BV_ADD_VLIST(vlfree, vhead, hhemi[6], BV_VLIST_LINE_DRAW);
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
|
||||
@@ -1770,7 +1794,7 @@ rt_part_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, c
|
||||
bu_free((char *)strips[i].fu, "strip faceuse[]");
|
||||
}
|
||||
bu_free((char *)strips, "strips[]");
|
||||
return 0;
|
||||
return _rt_nonuniform_tess_finalize(*r, ip, tol);
|
||||
fail:
|
||||
/* Release memory */
|
||||
/* All strips have vertices and normals */
|
||||
@@ -1893,6 +1917,8 @@ rt_part_export4(struct bu_external *ep, const struct rt_db_internal *ip, double
|
||||
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
if (ip->idb_type != ID_PARTICLE) return -1;
|
||||
if (_rt_nonuniform_export4_check("rt_part_export4", ip) < 0)
|
||||
return -1;
|
||||
pip = (struct rt_part_internal *)ip->idb_ptr;
|
||||
RT_PART_CK_MAGIC(pip);
|
||||
|
||||
@@ -1928,11 +1954,30 @@ rt_part_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inte
|
||||
struct rt_part_internal *part = (struct rt_part_internal *)rop->idb_ptr;
|
||||
RT_PART_CK_MAGIC(part);
|
||||
|
||||
mat_t effective_mat;
|
||||
int remove_nonuniform = 0;
|
||||
{
|
||||
int nonuniform = _rt_nonuniform_transform_resolve(effective_mat, &remove_nonuniform, ip, mat);
|
||||
if (nonuniform < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (nonuniform) {
|
||||
*part = *tip;
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
return (_rt_nonuniform_attr_compose(rop, mat) < 0) ? BRLCAD_ERROR : BRLCAD_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (remove_nonuniform && _rt_nonuniform_attr_remove(rop) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
vect_t part_V, part_H;
|
||||
MAT4X3PNT(part_V, mat, tip->part_V);
|
||||
MAT4X3VEC(part_H, mat, tip->part_H);
|
||||
double vrad = tip->part_vrad / mat[15];
|
||||
double hrad = tip->part_hrad / mat[15];
|
||||
MAT4X3PNT(part_V, effective_mat, tip->part_V);
|
||||
MAT4X3VEC(part_H, effective_mat, tip->part_H);
|
||||
double vrad = tip->part_vrad / effective_mat[15];
|
||||
double hrad = tip->part_hrad / effective_mat[15];
|
||||
double maxrad = (vrad > hrad) ? vrad : hrad;
|
||||
double minrad = (vrad < hrad) ? vrad : hrad;
|
||||
|
||||
@@ -2203,6 +2248,9 @@ rt_part_params(struct pc_pc_set *UNUSED(ps), const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_part_volume(fastf_t *vol, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_volume(vol, ip))
|
||||
return;
|
||||
|
||||
fastf_t vrad, hrad, mag_h;
|
||||
struct rt_part_internal *pip = (struct rt_part_internal *)ip->idb_ptr;
|
||||
RT_PART_CK_MAGIC(pip);
|
||||
@@ -2226,6 +2274,9 @@ rt_part_volume(fastf_t *vol, const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_part_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_surf_area(area, ip))
|
||||
return;
|
||||
|
||||
fastf_t vrad, hrad, mag_h;
|
||||
struct rt_part_internal *pip = (struct rt_part_internal *)ip->idb_ptr;
|
||||
RT_PART_CK_MAGIC(pip);
|
||||
@@ -2247,6 +2298,9 @@ rt_part_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_part_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_centroid(cent, ip))
|
||||
return;
|
||||
|
||||
fastf_t vrad, hrad, mag_h, nm, dm, c_frst, cv_hem, ch_hem;
|
||||
vect_t hvec, hvec_n;
|
||||
point_t vpt, fcent, hhcent, cvcent;
|
||||
|
||||
@@ -156,6 +156,35 @@ struct pipe_bend {
|
||||
|
||||
#define RT_PIPE_MAXHITS 128
|
||||
|
||||
static struct hit *
|
||||
pipe_append_hit(
|
||||
struct hit *hits,
|
||||
int *hit_count,
|
||||
struct soltab *stp)
|
||||
{
|
||||
struct hit *hitp;
|
||||
|
||||
if (*hit_count >= RT_PIPE_MAXHITS) {
|
||||
bu_log("Too many hits (%d) on primitive (%s)\n", *hit_count + 1, stp->st_dp->d_namep);
|
||||
return (struct hit *)NULL;
|
||||
}
|
||||
|
||||
hitp = &hits[(*hit_count)++];
|
||||
memset(hitp, 0, sizeof(struct hit));
|
||||
hitp->hit_magic = RT_HIT_MAGIC;
|
||||
return hitp;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pipe_ray_inv_dir(vect_t inv_dir, const struct xray *rp)
|
||||
{
|
||||
inv_dir[X] = !ZERO(rp->r_dir[X]) ? 1.0 / rp->r_dir[X] : INFINITY;
|
||||
inv_dir[Y] = !ZERO(rp->r_dir[Y]) ? 1.0 / rp->r_dir[Y] : INFINITY;
|
||||
inv_dir[Z] = !ZERO(rp->r_dir[Z]) ? 1.0 / rp->r_dir[Z] : INFINITY;
|
||||
}
|
||||
|
||||
|
||||
static fastf_t
|
||||
pipe_seg_bend_angle(const struct pipe_segment *seg)
|
||||
{
|
||||
@@ -665,8 +694,12 @@ rt_pipe_bbox(
|
||||
struct rt_db_internal *ip,
|
||||
point_t *min,
|
||||
point_t *max,
|
||||
const struct bn_tol *UNUSED(tol))
|
||||
const struct bn_tol *tol)
|
||||
{
|
||||
int nu_bbox = _rt_nonuniform_bbox(ip, min, max, tol);
|
||||
if (nu_bbox)
|
||||
return (nu_bbox > 0) ? 0 : -1;
|
||||
|
||||
pipe_elements_calculate(NULL, ip, min, max);
|
||||
return 0;
|
||||
}
|
||||
@@ -720,7 +753,7 @@ rt_pipe_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
|
||||
stp->st_aradius = f;
|
||||
stp->st_bradius = sqrt(dx * dx + dy * dy + dz * dz);
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_prep_finalize(stp, ip, rtip ? &rtip->rti_tol : NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -842,8 +875,10 @@ discont_radius_shot(
|
||||
/* if we are within one of the radius ranges, we have a hit */
|
||||
if ((radius_sq <= or2_sq && radius_sq >= ir2_sq) ||
|
||||
(radius_sq <= or1_sq && radius_sq >= ir1_sq)) {
|
||||
hitp = &hits[*hit_count];
|
||||
hitp->hit_magic = RT_HIT_MAGIC;
|
||||
hitp = pipe_append_hit(hits, hit_count, stp);
|
||||
if (!hitp)
|
||||
return;
|
||||
|
||||
hitp->hit_dist = t_tmp;
|
||||
hitp->hit_surfno = seg_no * 10 + PIPE_RADIUS_CHANGE;
|
||||
|
||||
@@ -853,10 +888,6 @@ discont_radius_shot(
|
||||
} else {
|
||||
VREVERSE(hitp->hit_normal, norm);
|
||||
}
|
||||
if ((*hit_count)++ >= RT_PIPE_MAXHITS) {
|
||||
bu_log("Too many hits (%d) on primitive (%s)\n", *hit_count, stp->st_dp->d_namep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1045,16 +1076,13 @@ bend_pipe_shot(
|
||||
angle += M_2PI;
|
||||
}
|
||||
if (angle <= bp->bend_angle) {
|
||||
hitp = &hits[*hit_count];
|
||||
hitp->hit_magic = RT_HIT_MAGIC;
|
||||
hitp = pipe_append_hit(hits, hit_count, stp);
|
||||
if (!hitp)
|
||||
return;
|
||||
|
||||
hitp->hit_dist = distance;
|
||||
VJOIN1(hitp->hit_vpriv, pprime, normalized_dist, dprime);
|
||||
hitp->hit_surfno = seg_no * 10 + PIPE_BEND_OUTER_BODY;
|
||||
|
||||
if ((*hit_count)++ >= RT_PIPE_MAXHITS) {
|
||||
bu_log("Too many hits (%d) on primitive (%s)\n", *hit_count, stp->st_dp->d_namep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1137,16 +1165,13 @@ bend_pipe_shot(
|
||||
angle += M_2PI;
|
||||
}
|
||||
if (angle <= bp->bend_angle) {
|
||||
hitp = &hits[*hit_count];
|
||||
hitp->hit_magic = RT_HIT_MAGIC;
|
||||
hitp = pipe_append_hit(hits, hit_count, stp);
|
||||
if (!hitp)
|
||||
return;
|
||||
|
||||
hitp->hit_dist = distance;
|
||||
VJOIN1(hitp->hit_vpriv, pprime, normalized_dist, dprime);
|
||||
hitp->hit_surfno = seg_no * 10 + PIPE_BEND_INNER_BODY;
|
||||
|
||||
if ((*hit_count)++ >= RT_PIPE_MAXHITS) {
|
||||
bu_log("Too many hits (%d) on primitive (%s)\n", *hit_count, stp->st_dp->d_namep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1242,7 +1267,7 @@ linear_pipe_shot(
|
||||
|
||||
descrim = b * b - 4.0 * a * c;
|
||||
|
||||
if (descrim > 0.0) {
|
||||
if (descrim > 0.0 && !NEAR_ZERO(a, SMALL_FASTF)) {
|
||||
fastf_t sqrt_descrim;
|
||||
point_t hit_pt;
|
||||
|
||||
@@ -1251,35 +1276,29 @@ linear_pipe_shot(
|
||||
t_tmp = (-b - sqrt_descrim) / (2.0 * a);
|
||||
VJOIN1(hit_pt, ray_start, t_tmp, ray_dir);
|
||||
if (hit_pt[Z] >= 0.0 && hit_pt[Z] <= 1.0) {
|
||||
hitp = &hits[*hit_count];
|
||||
hitp->hit_magic = RT_HIT_MAGIC;
|
||||
hitp = pipe_append_hit(hits, hit_count, stp);
|
||||
if (!hitp)
|
||||
return;
|
||||
|
||||
hitp->hit_dist = t_tmp;
|
||||
hitp->hit_surfno = seg_no * 10 + PIPE_LINEAR_OUTER_BODY;
|
||||
VMOVE(hitp->hit_vpriv, hit_pt);
|
||||
hitp->hit_vpriv[Z] = (-lp->pipe_robase - hit_pt[Z] * lp->pipe_rodiff) *
|
||||
lp->pipe_rodiff;
|
||||
|
||||
if ((*hit_count)++ >= RT_PIPE_MAXHITS) {
|
||||
bu_log("Too many hits (%d) on primitive (%s)\n", *hit_count, stp->st_dp->d_namep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
t_tmp = (-b + sqrt_descrim) / (2.0 * a);
|
||||
VJOIN1(hit_pt, ray_start, t_tmp, ray_dir);
|
||||
if (hit_pt[Z] >= 0.0 && hit_pt[Z] <= 1.0) {
|
||||
hitp = &hits[*hit_count];
|
||||
hitp->hit_magic = RT_HIT_MAGIC;
|
||||
hitp = pipe_append_hit(hits, hit_count, stp);
|
||||
if (!hitp)
|
||||
return;
|
||||
|
||||
hitp->hit_dist = t_tmp;
|
||||
hitp->hit_surfno = seg_no * 10 + PIPE_LINEAR_OUTER_BODY;
|
||||
VMOVE(hitp->hit_vpriv, hit_pt);
|
||||
hitp->hit_vpriv[Z] = (-lp->pipe_robase - hit_pt[Z] * lp->pipe_rodiff) *
|
||||
lp->pipe_rodiff;
|
||||
|
||||
if ((*hit_count)++ >= RT_PIPE_MAXHITS) {
|
||||
bu_log("Too many hits (%d) on primitive (%s)\n", *hit_count, stp->st_dp->d_namep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1301,7 +1320,7 @@ linear_pipe_shot(
|
||||
|
||||
descrim = b * b - 4.0 * a * c;
|
||||
|
||||
if (descrim > 0.0) {
|
||||
if (descrim > 0.0 && !NEAR_ZERO(a, SMALL_FASTF)) {
|
||||
fastf_t sqrt_descrim;
|
||||
point_t hit_pt;
|
||||
|
||||
@@ -1310,35 +1329,29 @@ linear_pipe_shot(
|
||||
t_tmp = (-b - sqrt_descrim) / (2.0 * a);
|
||||
VJOIN1(hit_pt, ray_start, t_tmp, ray_dir);
|
||||
if (hit_pt[Z] >= 0.0 && hit_pt[Z] <= 1.0) {
|
||||
hitp = &hits[*hit_count];
|
||||
hitp->hit_magic = RT_HIT_MAGIC;
|
||||
hitp = pipe_append_hit(hits, hit_count, stp);
|
||||
if (!hitp)
|
||||
return;
|
||||
|
||||
hitp->hit_dist = t_tmp;
|
||||
hitp->hit_surfno = seg_no * 10 + PIPE_LINEAR_INNER_BODY;
|
||||
VMOVE(hitp->hit_vpriv, hit_pt);
|
||||
hitp->hit_vpriv[Z] = (-lp->pipe_ribase - hit_pt[Z] * lp->pipe_ridiff) *
|
||||
lp->pipe_ridiff;
|
||||
|
||||
if ((*hit_count)++ >= RT_PIPE_MAXHITS) {
|
||||
bu_log("Too many hits (%d) on primitive (%s)\n", *hit_count, stp->st_dp->d_namep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
t_tmp = (-b + sqrt_descrim) / (2.0 * a);
|
||||
VJOIN1(hit_pt, ray_start, t_tmp, ray_dir);
|
||||
if (hit_pt[Z] >= 0.0 && hit_pt[Z] <= 1.0) {
|
||||
hitp = &hits[*hit_count];
|
||||
hitp->hit_magic = RT_HIT_MAGIC;
|
||||
hitp = pipe_append_hit(hits, hit_count, stp);
|
||||
if (!hitp)
|
||||
return;
|
||||
|
||||
hitp->hit_dist = t_tmp;
|
||||
hitp->hit_surfno = seg_no * 10 + PIPE_LINEAR_INNER_BODY;
|
||||
VMOVE(hitp->hit_vpriv, hit_pt);
|
||||
hitp->hit_vpriv[Z] = (-lp->pipe_ribase - hit_pt[Z] * lp->pipe_ridiff) *
|
||||
lp->pipe_ridiff;
|
||||
|
||||
if ((*hit_count)++ >= RT_PIPE_MAXHITS) {
|
||||
bu_log("Too many hits (%d) on primitive (%s)\n", *hit_count, stp->st_dp->d_namep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1377,15 +1390,12 @@ pipe_start_shot(
|
||||
VSUB2(to_center, lin->pipe_V, hit_pt);
|
||||
radius_sq = MAGSQ(to_center);
|
||||
if (radius_sq <= lin->pipe_robase_sq && radius_sq >= lin->pipe_ribase_sq) {
|
||||
hitp = &hits[*hit_count];
|
||||
hitp->hit_magic = RT_HIT_MAGIC;
|
||||
hitp = pipe_append_hit(hits, hit_count, stp);
|
||||
if (!hitp)
|
||||
return;
|
||||
|
||||
hitp->hit_dist = t_tmp;
|
||||
hitp->hit_surfno = seg_no * 10 + PIPE_LINEAR_BASE;
|
||||
|
||||
if ((*hit_count)++ >= RT_PIPE_MAXHITS) {
|
||||
bu_log("Too many hits (%d) on primitive (%s)\n", *hit_count, stp->st_dp->d_namep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (id_p->pipe_is_bend) {
|
||||
@@ -1406,15 +1416,12 @@ pipe_start_shot(
|
||||
VSUB2(to_center, bend->bend_start, hit_pt);
|
||||
radius_sq = MAGSQ(to_center);
|
||||
if (radius_sq <= bend->bend_or * bend->bend_or && radius_sq >= bend->bend_ir * bend->bend_ir) {
|
||||
hitp = &hits[*hit_count];
|
||||
hitp->hit_magic = RT_HIT_MAGIC;
|
||||
hitp = pipe_append_hit(hits, hit_count, stp);
|
||||
if (!hitp)
|
||||
return;
|
||||
|
||||
hitp->hit_dist = t_tmp;
|
||||
hitp->hit_surfno = seg_no * 10 + PIPE_BEND_BASE;
|
||||
|
||||
if ((*hit_count)++ >= RT_PIPE_MAXHITS) {
|
||||
bu_log("Too many hits (%d) on primitive (%s)\n", *hit_count, stp->st_dp->d_namep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1454,15 +1461,12 @@ pipe_end_shot(
|
||||
VSUB2(to_center, top, hit_pt);
|
||||
radius_sq = MAGSQ(to_center);
|
||||
if (radius_sq <= lin->pipe_rotop_sq && radius_sq >= lin->pipe_ritop_sq) {
|
||||
hitp = &hits[*hit_count];
|
||||
hitp->hit_magic = RT_HIT_MAGIC;
|
||||
hitp = pipe_append_hit(hits, hit_count, stp);
|
||||
if (!hitp)
|
||||
return;
|
||||
|
||||
hitp->hit_dist = t_tmp;
|
||||
hitp->hit_surfno = seg_no * 10 + PIPE_LINEAR_TOP;
|
||||
|
||||
if ((*hit_count)++ >= RT_PIPE_MAXHITS) {
|
||||
bu_log("Too many hits (%d) on primitive (%s)\n", *hit_count, stp->st_dp->d_namep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (id_p->pipe_is_bend) {
|
||||
@@ -1489,15 +1493,12 @@ pipe_end_shot(
|
||||
VSUB2(to_center, bend->bend_end, hit_pt);
|
||||
radius_sq = MAGSQ(to_center);
|
||||
if (radius_sq <= bend->bend_or * bend->bend_or && radius_sq >= bend->bend_ir * bend->bend_ir) {
|
||||
hitp = &hits[*hit_count];
|
||||
hitp->hit_magic = RT_HIT_MAGIC;
|
||||
hitp = pipe_append_hit(hits, hit_count, stp);
|
||||
if (!hitp)
|
||||
return;
|
||||
|
||||
hitp->hit_dist = t_tmp;
|
||||
hitp->hit_surfno = seg_no * 10 + PIPE_BEND_TOP;
|
||||
|
||||
if ((*hit_count)++ >= RT_PIPE_MAXHITS) {
|
||||
bu_log("Too many hits (%d) on primitive (%s)\n", *hit_count, stp->st_dp->d_namep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1588,6 +1589,11 @@ rt_pipe_elim_dups(
|
||||
C_DECL void
|
||||
rt_pipe_norm(struct hit *hitp, struct soltab *stp, struct xray *rp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_norm(hitp, stp, rp);
|
||||
return;
|
||||
}
|
||||
|
||||
struct bu_list *head = (struct bu_list *)stp->st_specific;
|
||||
struct id_pipe *pipe_id;
|
||||
struct lin_pipe *pipe_lin;
|
||||
@@ -1682,14 +1688,20 @@ rt_pipe_shot(
|
||||
struct application *ap,
|
||||
struct seg *seghead)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp)
|
||||
return _rt_nonuniform_shot(stp, rp, ap, seghead);
|
||||
|
||||
struct bu_list *head = (struct bu_list *)stp->st_specific;
|
||||
struct id_pipe *pipe_id;
|
||||
struct seg *segp;
|
||||
struct hit hits[RT_PIPE_MAXHITS];
|
||||
vect_t inv_dir;
|
||||
int total_hits = 0;
|
||||
int seg_no;
|
||||
int i;
|
||||
|
||||
pipe_ray_inv_dir(inv_dir, rp);
|
||||
|
||||
pipe_start_shot(stp, rp, BU_LIST_FIRST(id_pipe, head), hits, &total_hits, 1);
|
||||
seg_no = 0;
|
||||
for (BU_LIST_FOR(pipe_id, id_pipe, head)) {
|
||||
@@ -1703,7 +1715,7 @@ rt_pipe_shot(
|
||||
|
||||
if (!pipe_id->pipe_is_bend) {
|
||||
struct lin_pipe *lin = (struct lin_pipe *)pipe_id;
|
||||
if (!rt_in_rpp(rp, ap->a_inv_dir, lin->pipe_min, lin->pipe_max)) {
|
||||
if (!rt_in_rpp(rp, inv_dir, lin->pipe_min, lin->pipe_max)) {
|
||||
continue;
|
||||
}
|
||||
linear_pipe_shot(stp, rp, lin, hits, &total_hits, seg_no);
|
||||
@@ -1786,6 +1798,12 @@ rt_pipe_vshot(
|
||||
C_DECL void
|
||||
rt_pipe_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
if (cvp)
|
||||
*cvp = (struct curvature)RT_CURVATURE_INIT_ZERO;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cvp || !hitp) {
|
||||
return;
|
||||
}
|
||||
@@ -1813,6 +1831,11 @@ rt_pipe_uv(
|
||||
struct hit *hitp,
|
||||
struct uvcoord *uvp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_uv(ap, stp, hitp, uvp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ap || !stp || !hitp || !uvp) {
|
||||
return;
|
||||
}
|
||||
@@ -2325,18 +2348,20 @@ draw_pipe_end_adaptive(
|
||||
C_DECL int
|
||||
rt_pipe_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bn_tol *UNUSED(tol), const struct bview *v, fastf_t s_size)
|
||||
{
|
||||
struct rt_nonuniform_vlist_state nonuniform_state;
|
||||
struct rt_pipe_internal *pipeobj;
|
||||
struct pipe_segment *cur_seg;
|
||||
|
||||
BU_CK_LIST_HEAD(vhead);
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
_rt_nonuniform_vlist_state_init(&nonuniform_state, vhead);
|
||||
struct bu_list *vlfree = &rt_vlfree;
|
||||
pipeobj = (struct rt_pipe_internal *)ip->idb_ptr;
|
||||
RT_PIPE_CK_MAGIC(pipeobj);
|
||||
|
||||
cur_seg = pipe_seg_first(pipeobj);
|
||||
if (cur_seg == NULL) {
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
fastf_t point_spacing = solid_point_spacing(v, s_size);
|
||||
@@ -2361,7 +2386,7 @@ rt_pipe_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const st
|
||||
|
||||
BU_PUT(cur_seg, struct pipe_segment);
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
|
||||
@@ -2373,18 +2398,20 @@ rt_pipe_plot(
|
||||
const struct bn_tol *UNUSED(tol),
|
||||
const struct bview *UNUSED(info))
|
||||
{
|
||||
struct rt_nonuniform_vlist_state nonuniform_state;
|
||||
struct rt_pipe_internal *pip;
|
||||
struct pipe_segment *cur_seg;
|
||||
|
||||
BU_CK_LIST_HEAD(vhead);
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
_rt_nonuniform_vlist_state_init(&nonuniform_state, vhead);
|
||||
struct bu_list *vlfree = &rt_vlfree;
|
||||
pip = (struct rt_pipe_internal *)ip->idb_ptr;
|
||||
RT_PIPE_CK_MAGIC(pip);
|
||||
|
||||
cur_seg = pipe_seg_first(pip);
|
||||
if (cur_seg == NULL) {
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
cur_seg->connecting_arcs = PIPE_CONNECTING_ARCS;
|
||||
@@ -2407,7 +2434,7 @@ rt_pipe_plot(
|
||||
|
||||
BU_PUT(cur_seg, struct pipe_segment);
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
|
||||
@@ -3803,7 +3830,7 @@ rt_pipe_tess(
|
||||
*r = (struct nmgregion *)NULL;
|
||||
|
||||
if (BU_LIST_IS_EMPTY(&pip->pipe_segs_head)) {
|
||||
return 0; /* nothing to tessellate */
|
||||
return _rt_nonuniform_tess_finalize(*r, ip, tol); /* nothing to tessellate */
|
||||
}
|
||||
|
||||
/* find max diameter */
|
||||
@@ -3814,7 +3841,7 @@ rt_pipe_tess(
|
||||
}
|
||||
|
||||
if (max_diam <= tol->dist) {
|
||||
return 0; /* nothing to tessellate */
|
||||
return _rt_nonuniform_tess_finalize(*r, ip, tol); /* nothing to tessellate */
|
||||
}
|
||||
|
||||
/* calculate number of segments for circles */
|
||||
@@ -3866,7 +3893,7 @@ rt_pipe_tess(
|
||||
|
||||
pp2 = BU_LIST_NEXT(wdb_pipe_pnt, &pp1->l);
|
||||
if (BU_LIST_IS_HEAD(&pp2->l, &(pip->pipe_segs_head))) {
|
||||
return 0;
|
||||
return _rt_nonuniform_tess_finalize(*r, ip, tol);
|
||||
}
|
||||
pp3 = BU_LIST_NEXT(wdb_pipe_pnt, &pp2->l);
|
||||
if (BU_LIST_IS_HEAD(&pp3->l, &(pip->pipe_segs_head))) {
|
||||
@@ -3951,7 +3978,7 @@ rt_pipe_tess(
|
||||
nmg_rebound(m, tol);
|
||||
nmg_edge_fuse(&s->l.magic, vlfree, tol);
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_tess_finalize(*r, ip, tol);
|
||||
}
|
||||
|
||||
|
||||
@@ -4051,6 +4078,8 @@ rt_pipe_export4(
|
||||
}
|
||||
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
if (_rt_nonuniform_export4_check("rt_pipe_export4", ip) < 0)
|
||||
return -1;
|
||||
pip = (struct rt_pipe_internal *)ip->idb_ptr;
|
||||
RT_PIPE_CK_MAGIC(pip);
|
||||
|
||||
@@ -4125,16 +4154,34 @@ rt_pipe_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inte
|
||||
if (tip->pipe_count != top->pipe_count)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
mat_t effective_mat;
|
||||
int remove_nonuniform = 0;
|
||||
{
|
||||
int nonuniform = _rt_nonuniform_transform_resolve(effective_mat, &remove_nonuniform, ip, mat);
|
||||
if (nonuniform < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (nonuniform) {
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
return (_rt_nonuniform_attr_compose(rop, mat) < 0) ? BRLCAD_ERROR : BRLCAD_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (remove_nonuniform && _rt_nonuniform_attr_remove(rop) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
struct wdb_pipe_pnt *in_pipe_pnt, *out_pipe_pnt;
|
||||
in_pipe_pnt = BU_LIST_FIRST(wdb_pipe_pnt, &tip->pipe_segs_head);
|
||||
out_pipe_pnt = BU_LIST_FIRST(wdb_pipe_pnt, &top->pipe_segs_head);
|
||||
while (BU_LIST_NOT_HEAD(&in_pipe_pnt->l, &tip->pipe_segs_head)) {
|
||||
vect_t v;
|
||||
VMOVE(v, in_pipe_pnt->pp_coord);
|
||||
MAT4X3PNT(out_pipe_pnt->pp_coord, mat, v);
|
||||
out_pipe_pnt->pp_id = in_pipe_pnt->pp_id / mat[15];
|
||||
out_pipe_pnt->pp_od = in_pipe_pnt->pp_od / mat[15];
|
||||
out_pipe_pnt->pp_bendradius = in_pipe_pnt->pp_bendradius / mat[15];
|
||||
MAT4X3PNT(out_pipe_pnt->pp_coord, effective_mat, v);
|
||||
out_pipe_pnt->pp_id = in_pipe_pnt->pp_id / effective_mat[15];
|
||||
out_pipe_pnt->pp_od = in_pipe_pnt->pp_od / effective_mat[15];
|
||||
out_pipe_pnt->pp_bendradius = in_pipe_pnt->pp_bendradius / effective_mat[15];
|
||||
in_pipe_pnt = BU_LIST_NEXT(wdb_pipe_pnt, &in_pipe_pnt->l);
|
||||
out_pipe_pnt = BU_LIST_NEXT(wdb_pipe_pnt, &out_pipe_pnt->l);
|
||||
}
|
||||
@@ -5035,6 +5082,9 @@ rt_pipe_params(struct pc_pc_set *UNUSED(ps), const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_pipe_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_surf_area(area, ip))
|
||||
return;
|
||||
|
||||
struct bu_list head;
|
||||
point_t min, max;
|
||||
struct id_pipe *p;
|
||||
@@ -5047,9 +5097,12 @@ rt_pipe_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
int connected;
|
||||
char overlap;
|
||||
|
||||
*area = -1.0;
|
||||
BU_LIST_INIT(&head);
|
||||
|
||||
pipe_elements_calculate(&head, ip, &min, &max);
|
||||
if (BU_LIST_IS_EMPTY(&head))
|
||||
return;
|
||||
|
||||
/* The following calculation establishes if the last pipe segment
|
||||
* is in fact connected to the first one. The last end point is
|
||||
@@ -5157,7 +5210,9 @@ rt_pipe_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
break;
|
||||
default:
|
||||
bu_log("rt_pipe_surf_area: Unexpected cross-section overlap code: (%d)\n", overlap);
|
||||
break;
|
||||
*area = -1.0;
|
||||
pipe_elements_free(&head);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* not connected, both areas are added regardless of overlaps */
|
||||
@@ -5238,13 +5293,19 @@ pipe_elem_volume_and_centroid(struct id_pipe *p, fastf_t *vol, point_t *cent)
|
||||
C_DECL void
|
||||
rt_pipe_volume(fastf_t *vol, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_volume(vol, ip))
|
||||
return;
|
||||
|
||||
struct bu_list head;
|
||||
point_t min, max;
|
||||
struct id_pipe *p;
|
||||
|
||||
*vol = -1.0;
|
||||
BU_LIST_INIT(&head);
|
||||
|
||||
pipe_elements_calculate(&head, ip, &min, &max);
|
||||
if (BU_LIST_IS_EMPTY(&head))
|
||||
return;
|
||||
|
||||
*vol = 0;
|
||||
for (BU_LIST_FOR(p, id_pipe, &head)) {
|
||||
@@ -5258,14 +5319,20 @@ rt_pipe_volume(fastf_t *vol, const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_pipe_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_centroid(cent, ip))
|
||||
return;
|
||||
|
||||
struct bu_list head;
|
||||
point_t min, max;
|
||||
struct id_pipe *p;
|
||||
fastf_t vol;
|
||||
|
||||
VSETALL(*cent, -1.0);
|
||||
BU_LIST_INIT(&head);
|
||||
|
||||
pipe_elements_calculate(&head, ip, &min, &max);
|
||||
if (BU_LIST_IS_EMPTY(&head))
|
||||
return;
|
||||
|
||||
VSETALL(*cent, 0);
|
||||
vol = 0;
|
||||
@@ -5273,6 +5340,11 @@ rt_pipe_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
for (BU_LIST_FOR(p, id_pipe, &head)) {
|
||||
pipe_elem_volume_and_centroid(p, &vol, cent);
|
||||
}
|
||||
if (NEAR_ZERO(vol, SMALL_FASTF)) {
|
||||
VSETALL(*cent, -1.0);
|
||||
pipe_elements_free(&head);
|
||||
return;
|
||||
}
|
||||
VSCALE(*cent, *cent, 1/vol);
|
||||
pipe_elements_free(&head);
|
||||
}
|
||||
|
||||
@@ -239,8 +239,11 @@ EXTERNCPP const struct bu_structparse rt_rhc_parse[] = {
|
||||
* Calculate the bounding RPP for an RHC
|
||||
*/
|
||||
C_DECL int
|
||||
rt_rhc_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *UNUSED(tol))
|
||||
rt_rhc_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *tol)
|
||||
{
|
||||
int nu_bbox = _rt_nonuniform_bbox(ip, min, max, tol);
|
||||
if (nu_bbox)
|
||||
return (nu_bbox > 0) ? 0 : -1;
|
||||
|
||||
struct rt_rhc_internal *xip;
|
||||
vect_t rinv, rvect, rv2, working;
|
||||
@@ -379,7 +382,7 @@ rt_rhc_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0; /* OK */
|
||||
return _rt_nonuniform_prep_finalize(stp, ip, &rtip->rti_tol);
|
||||
}
|
||||
|
||||
|
||||
@@ -416,6 +419,9 @@ rt_rhc_print(const struct soltab *stp)
|
||||
C_DECL int
|
||||
rt_rhc_shot(struct soltab *stp, struct xray *rp, struct application *ap, struct seg *seghead)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp)
|
||||
return _rt_nonuniform_shot(stp, rp, ap, seghead);
|
||||
|
||||
struct rhc_specific *rhc =
|
||||
(struct rhc_specific *)stp->st_specific;
|
||||
vect_t dprime; /* D' */
|
||||
@@ -792,6 +798,11 @@ rt_rhc_vshot(struct soltab **stp, struct xray **rp, struct seg *segp, int n, str
|
||||
C_DECL void
|
||||
rt_rhc_norm(struct hit *hitp, struct soltab *stp, struct xray *rp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_norm(hitp, stp, rp);
|
||||
return;
|
||||
}
|
||||
|
||||
fastf_t c;
|
||||
vect_t can_normal; /* normal to canonical rhc */
|
||||
struct rhc_specific *rhc =
|
||||
@@ -835,6 +846,11 @@ rt_rhc_norm(struct hit *hitp, struct soltab *stp, struct xray *rp)
|
||||
C_DECL void
|
||||
rt_rhc_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_curve(cvp, hitp, stp);
|
||||
return;
|
||||
}
|
||||
|
||||
fastf_t b, c, rsq, y;
|
||||
fastf_t zp1_sq, zp2; /* 1st deriv sqr, 2nd deriv */
|
||||
struct rhc_specific *rhc =
|
||||
@@ -876,6 +892,11 @@ rt_rhc_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp)
|
||||
C_DECL void
|
||||
rt_rhc_uv(struct application *ap, struct soltab *stp, struct hit *hitp, struct uvcoord *uvp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_uv(ap, stp, hitp, uvp);
|
||||
return;
|
||||
}
|
||||
|
||||
struct rhc_specific *rhc = (struct rhc_specific *)stp->st_specific;
|
||||
|
||||
vect_t work;
|
||||
@@ -1144,6 +1165,7 @@ rhc_curve_points(
|
||||
C_DECL int
|
||||
rt_rhc_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bn_tol *tol, const struct bview *v, fastf_t s_size)
|
||||
{
|
||||
struct rt_nonuniform_vlist_state nonuniform_state;
|
||||
point_t p;
|
||||
vect_t rhc_R;
|
||||
int num_curve_points, num_connections;
|
||||
@@ -1152,6 +1174,7 @@ rt_rhc_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const str
|
||||
|
||||
BU_CK_LIST_HEAD(vhead);
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
_rt_nonuniform_vlist_state_init(&nonuniform_state, vhead);
|
||||
|
||||
struct bu_list *vlfree = &rt_vlfree;
|
||||
rhc = (struct rt_rhc_internal *)ip->idb_ptr;
|
||||
@@ -1206,13 +1229,14 @@ rt_rhc_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const str
|
||||
VJOIN1(p, p, 2.0, rhc_R);
|
||||
BV_ADD_VLIST(vlfree, vhead, p, BV_VLIST_LINE_DRAW);
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
|
||||
C_DECL int
|
||||
rt_rhc_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_tess_tol *ttol, const struct bn_tol *UNUSED(tol), const struct bview *UNUSED(info))
|
||||
{
|
||||
struct rt_nonuniform_vlist_state nonuniform_state;
|
||||
int i, n;
|
||||
fastf_t b, c, *back, *front, rh;
|
||||
fastf_t dtol, ntol, min_abs;
|
||||
@@ -1224,6 +1248,7 @@ rt_rhc_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_te
|
||||
|
||||
BU_CK_LIST_HEAD(vhead);
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
_rt_nonuniform_vlist_state_init(&nonuniform_state, vhead);
|
||||
|
||||
struct bu_list *vlfree = &rt_vlfree;
|
||||
xip = (struct rt_rhc_internal *)ip->idb_ptr;
|
||||
@@ -1328,7 +1353,7 @@ rt_rhc_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_te
|
||||
bu_free((char *)front, "fastf_t");
|
||||
bu_free((char *)back, "fastf_t");
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
|
||||
@@ -1781,7 +1806,7 @@ fail:
|
||||
bu_free((char *)norms, "rt_rhc_tess: norms");
|
||||
bu_free((char *)outfaceuses, "faceuse *");
|
||||
|
||||
return failure;
|
||||
return failure ? failure : _rt_nonuniform_tess_finalize(*r, ip, tol);
|
||||
}
|
||||
|
||||
|
||||
@@ -1876,6 +1901,8 @@ rt_rhc_export4(struct bu_external *ep, const struct rt_db_internal *ip, double l
|
||||
if (ip->idb_type != ID_RHC) {
|
||||
return -1;
|
||||
}
|
||||
if (_rt_nonuniform_export4_check("rt_rhc_export4", ip) < 0)
|
||||
return -1;
|
||||
|
||||
xip = (struct rt_rhc_internal *)ip->idb_ptr;
|
||||
if (!rhc_is_valid(xip)) {
|
||||
@@ -1911,21 +1938,40 @@ rt_rhc_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
|
||||
struct rt_rhc_internal *top = (struct rt_rhc_internal *)rop->idb_ptr;
|
||||
RT_RHC_CK_MAGIC(top);
|
||||
|
||||
mat_t effective_mat;
|
||||
int remove_nonuniform = 0;
|
||||
{
|
||||
int nonuniform = _rt_nonuniform_transform_resolve(effective_mat, &remove_nonuniform, ip, mat);
|
||||
if (nonuniform < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (nonuniform) {
|
||||
*top = *tip;
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
return (_rt_nonuniform_attr_compose(rop, mat) < 0) ? BRLCAD_ERROR : BRLCAD_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (remove_nonuniform && _rt_nonuniform_attr_remove(rop) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
vect_t rV, rH, rB;
|
||||
VMOVE(rV, tip->rhc_V);
|
||||
VMOVE(rH, tip->rhc_H);
|
||||
VMOVE(rB, tip->rhc_B);
|
||||
double rhc_r = tip->rhc_r / mat[15];
|
||||
double rhc_c = tip->rhc_c / mat[15];
|
||||
double rhc_r = tip->rhc_r / effective_mat[15];
|
||||
double rhc_c = tip->rhc_c / effective_mat[15];
|
||||
|
||||
if (rhc_r <= SMALL_FASTF || rhc_c <= SMALL_FASTF) {
|
||||
bu_log("rt_rhc_mat: r or c are zero\n");
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
|
||||
MAT4X3PNT(top->rhc_V, mat, rV);
|
||||
MAT4X3VEC(top->rhc_H, mat, rH);
|
||||
MAT4X3VEC(top->rhc_B, mat, rB);
|
||||
MAT4X3PNT(top->rhc_V, effective_mat, rV);
|
||||
MAT4X3VEC(top->rhc_H, effective_mat, rH);
|
||||
MAT4X3VEC(top->rhc_B, effective_mat, rB);
|
||||
top->rhc_r = rhc_r;
|
||||
top->rhc_c = rhc_c;
|
||||
|
||||
@@ -2166,6 +2212,9 @@ rhc_is_valid(struct rt_rhc_internal *rhc)
|
||||
C_DECL void
|
||||
rt_rhc_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_surf_area(area, ip))
|
||||
return;
|
||||
|
||||
struct rt_rhc_internal *rip;
|
||||
fastf_t A, arclen, integralArea, a, b, magB, sqrt_ra, height;
|
||||
|
||||
@@ -2228,6 +2277,9 @@ rt_rhc_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_rhc_volume(fastf_t *volume, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_volume(volume, ip))
|
||||
return;
|
||||
|
||||
struct rt_rhc_internal *rip;
|
||||
fastf_t A, integralArea, a, b, magB, sqrt_ra, height;
|
||||
if (volume == NULL || ip == NULL) {
|
||||
@@ -2256,6 +2308,9 @@ rt_rhc_volume(fastf_t *volume, const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_rhc_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_centroid(cent, ip))
|
||||
return;
|
||||
|
||||
if (cent != NULL && ip != NULL) {
|
||||
struct rt_rhc_internal *rip;
|
||||
fastf_t totalArea, guessArea, a, b, magB, sqrt_xa, sqrt_ga, xf, epsilon, high, low, scale_factor;
|
||||
|
||||
@@ -235,7 +235,11 @@ EXTERNCPP const struct bu_structparse rt_rpc_parse[] = {
|
||||
* Calculate the RPP for an RPC
|
||||
*/
|
||||
C_DECL int
|
||||
rt_rpc_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *UNUSED(tol)) {
|
||||
rt_rpc_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *tol) {
|
||||
int nu_bbox = _rt_nonuniform_bbox(ip, min, max, tol);
|
||||
if (nu_bbox)
|
||||
return (nu_bbox > 0) ? 0 : -1;
|
||||
|
||||
struct rt_rpc_internal *xip;
|
||||
vect_t rinv, rvect, rv2, working;
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
@@ -367,7 +371,7 @@ rt_rpc_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
|
||||
stp->st_aradius = stp->st_bradius;
|
||||
/* bounding RPP */
|
||||
if (rt_rpc_bbox(ip, &(stp->st_min), &(stp->st_max), &rtip->rti_tol)) return 1;
|
||||
return 0; /* OK */
|
||||
return _rt_nonuniform_prep_finalize(stp, ip, &rtip->rti_tol);
|
||||
}
|
||||
|
||||
|
||||
@@ -404,6 +408,9 @@ rt_rpc_print(const struct soltab *stp)
|
||||
C_DECL int
|
||||
rt_rpc_shot(struct soltab *stp, struct xray *rp, struct application *ap, struct seg *seghead)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp)
|
||||
return _rt_nonuniform_shot(stp, rp, ap, seghead);
|
||||
|
||||
struct rpc_specific *rpc =
|
||||
(struct rpc_specific *)stp->st_specific;
|
||||
vect_t dprime; /* D' */
|
||||
@@ -708,6 +715,11 @@ rt_rpc_vshot(struct soltab **stp, struct xray **rp, struct seg *segp, int n, str
|
||||
C_DECL void
|
||||
rt_rpc_norm(struct hit *hitp, struct soltab *stp, struct xray *rp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_norm(hitp, stp, rp);
|
||||
return;
|
||||
}
|
||||
|
||||
vect_t can_normal; /* normal to canonical rpc */
|
||||
struct rpc_specific *rpc =
|
||||
(struct rpc_specific *)stp->st_specific;
|
||||
@@ -741,6 +753,11 @@ rt_rpc_norm(struct hit *hitp, struct soltab *stp, struct xray *rp)
|
||||
C_DECL void
|
||||
rt_rpc_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_curve(cvp, hitp, stp);
|
||||
return;
|
||||
}
|
||||
|
||||
fastf_t zp1, zp2; /* 1st & 2nd derivatives */
|
||||
struct rpc_specific *rpc =
|
||||
(struct rpc_specific *)stp->st_specific;
|
||||
@@ -775,6 +792,11 @@ rt_rpc_curve(struct curvature *cvp, struct hit *hitp, struct soltab *stp)
|
||||
C_DECL void
|
||||
rt_rpc_uv(struct application *ap, struct soltab *stp, struct hit *hitp, struct uvcoord *uvp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_uv(ap, stp, hitp, uvp);
|
||||
return;
|
||||
}
|
||||
|
||||
struct rpc_specific *rpc = (struct rpc_specific *)stp->st_specific;
|
||||
|
||||
vect_t work;
|
||||
@@ -1026,6 +1048,7 @@ rpc_curve_points(
|
||||
C_DECL int
|
||||
rt_rpc_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bn_tol *tol, const struct bview *v, fastf_t s_size)
|
||||
{
|
||||
struct rt_nonuniform_vlist_state nonuniform_state;
|
||||
point_t p;
|
||||
vect_t rpc_R;
|
||||
int num_curve_points, num_connections;
|
||||
@@ -1035,6 +1058,7 @@ rt_rpc_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const str
|
||||
|
||||
BU_CK_LIST_HEAD(vhead);
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
_rt_nonuniform_vlist_state_init(&nonuniform_state, vhead);
|
||||
|
||||
rpc = (struct rt_rpc_internal *)ip->idb_ptr;
|
||||
if (!rpc_is_valid(rpc)) {
|
||||
@@ -1087,12 +1111,13 @@ rt_rpc_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const str
|
||||
VJOIN1(p, p, 2.0, rpc_R);
|
||||
BV_ADD_VLIST(vlfree, vhead, p, BV_VLIST_LINE_DRAW);
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
C_DECL int
|
||||
rt_rpc_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_tess_tol *ttol, const struct bn_tol *UNUSED(tol), const struct bview *UNUSED(info))
|
||||
{
|
||||
struct rt_nonuniform_vlist_state nonuniform_state;
|
||||
struct rt_rpc_internal *xip;
|
||||
fastf_t *front;
|
||||
fastf_t *back;
|
||||
@@ -1105,6 +1130,7 @@ rt_rpc_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_te
|
||||
|
||||
BU_CK_LIST_HEAD(vhead);
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
_rt_nonuniform_vlist_state_init(&nonuniform_state, vhead);
|
||||
|
||||
xip = (struct rt_rpc_internal *)ip->idb_ptr;
|
||||
if (!rpc_is_valid(xip)) {
|
||||
@@ -1202,7 +1228,7 @@ rt_rpc_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_te
|
||||
bu_free((char *)front, "fastf_t");
|
||||
bu_free((char *)back, "fastf_t");
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
|
||||
@@ -1571,7 +1597,7 @@ rt_rpc_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, co
|
||||
bu_free((char*)vtemp, "vertex *");
|
||||
bu_free((char*)outfaceuses, "faceuse *");
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_tess_finalize(*r, ip, tol);
|
||||
}
|
||||
|
||||
|
||||
@@ -1654,6 +1680,8 @@ rt_rpc_export4(struct bu_external *ep, const struct rt_db_internal *ip, double l
|
||||
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
if (ip->idb_type != ID_RPC) return -1;
|
||||
if (_rt_nonuniform_export4_check("rt_rpc_export4", ip) < 0)
|
||||
return -1;
|
||||
xip = (struct rt_rpc_internal *)ip->idb_ptr;
|
||||
RT_RPC_CK_MAGIC(xip);
|
||||
|
||||
@@ -1699,9 +1727,27 @@ rt_rpc_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
|
||||
struct rt_rpc_internal *top = (struct rt_rpc_internal *)rop->idb_ptr;
|
||||
RT_RPC_CK_MAGIC(top);
|
||||
|
||||
mat_t effective_mat;
|
||||
int remove_nonuniform = 0;
|
||||
{
|
||||
int nonuniform = _rt_nonuniform_transform_resolve(effective_mat, &remove_nonuniform, ip, mat);
|
||||
if (nonuniform < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (nonuniform) {
|
||||
*top = *tip;
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
return (_rt_nonuniform_attr_compose(rop, mat) < 0) ? BRLCAD_ERROR : BRLCAD_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (remove_nonuniform && _rt_nonuniform_attr_remove(rop) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
/* Sanity */
|
||||
if (tip->rpc_r / mat[15] <= SMALL_FASTF) {
|
||||
if (tip->rpc_r / effective_mat[15] <= SMALL_FASTF) {
|
||||
bu_log("rt_rpc_mat: r is zero\n");
|
||||
return BRLCAD_ERROR;
|
||||
}
|
||||
@@ -1711,10 +1757,10 @@ rt_rpc_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
|
||||
VMOVE(rV, tip->rpc_V);
|
||||
VMOVE(rH, tip->rpc_H);
|
||||
VMOVE(rB, tip->rpc_B);
|
||||
MAT4X3PNT(top->rpc_V, mat, rV);
|
||||
MAT4X3VEC(top->rpc_H, mat, rH);
|
||||
MAT4X3VEC(top->rpc_B, mat, rB);
|
||||
top->rpc_r = tip->rpc_r / mat[15];
|
||||
MAT4X3PNT(top->rpc_V, effective_mat, rV);
|
||||
MAT4X3VEC(top->rpc_H, effective_mat, rH);
|
||||
MAT4X3VEC(top->rpc_B, effective_mat, rB);
|
||||
top->rpc_r = tip->rpc_r / effective_mat[15];
|
||||
|
||||
return BRLCAD_OK;
|
||||
}
|
||||
@@ -1917,6 +1963,9 @@ rt_rpc_params(struct pc_pc_set *UNUSED(ps), const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_rpc_volume(fastf_t *vol, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_volume(vol, ip))
|
||||
return;
|
||||
|
||||
fastf_t mag_h, mag_b, mag_r;
|
||||
struct rt_rpc_internal *xip = (struct rt_rpc_internal *)ip->idb_ptr;
|
||||
RT_RPC_CK_MAGIC(xip);
|
||||
@@ -1932,6 +1981,9 @@ rt_rpc_volume(fastf_t *vol, const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_rpc_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_centroid(cent, ip))
|
||||
return;
|
||||
|
||||
struct rt_rpc_internal *xip = (struct rt_rpc_internal *)ip->idb_ptr;
|
||||
RT_RPC_CK_MAGIC(xip);
|
||||
|
||||
@@ -1950,6 +2002,9 @@ rt_rpc_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_rpc_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_surf_area(area, ip))
|
||||
return;
|
||||
|
||||
fastf_t area_base, area_shell, area_rect;
|
||||
fastf_t mag_b, mag_r, mag_h;
|
||||
fastf_t magsq_b, magsq_r;
|
||||
|
||||
@@ -1050,7 +1050,8 @@ sketch_centroid_with_precision(point_t *cent,
|
||||
}
|
||||
}
|
||||
}
|
||||
VSCALE(*cent, *cent, 1.0 / (fastf_t)n);
|
||||
if (n > 0)
|
||||
VSCALE(*cent, *cent, 1.0 / (fastf_t)n);
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -1083,7 +1084,7 @@ rt_sketch_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
int i = 1;
|
||||
RT_SKETCH_CK_MAGIC(sketch_ip);
|
||||
|
||||
VSETALL(*cent, 0.0);
|
||||
VSETALL(*cent, -1.0);
|
||||
VSETALL(current_cents[0], 0.0);
|
||||
VSETALL(current_cents[1], 0.0);
|
||||
|
||||
@@ -1094,6 +1095,8 @@ rt_sketch_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
|
||||
while (!NEAR_ZERO(MAGNITUDE(difference), TOLERANCE)) {
|
||||
n[i] = sketch_centroid_with_precision(¤t_cents[i], sketch_ip, precision, n[i], bounds);
|
||||
if (n[i] == 0)
|
||||
return;
|
||||
precision *= M_SQRT2;
|
||||
VSUB2(difference, current_cents[i], current_cents[!i]);
|
||||
i = !i;
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
#include "raytrace.h"
|
||||
@@ -276,9 +277,11 @@ rt_sketch_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
struct rt_sketch_internal *sketch_ip = (struct rt_sketch_internal *)ip->idb_ptr;
|
||||
RT_SKETCH_CK_MAGIC(sketch_ip);
|
||||
struct rt_curve crv = sketch_ip->curve;
|
||||
*area = -1.0;
|
||||
|
||||
// a sketch with no curves has no area
|
||||
if (UNLIKELY(crv.count == 0)) {
|
||||
*area = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -336,10 +339,12 @@ rt_sketch_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
break;
|
||||
case CURVE_NURB_MAGIC:
|
||||
default:
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
*area = 0.5 * fabs(poly_area) + carc_area;
|
||||
if (!std::isfinite(*area))
|
||||
*area = -1.0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1741,8 +1741,8 @@ const struct rt_functab OBJ[] = {
|
||||
RTFUNCTAB_FUNC_MAKE_CAST(rt_cline_make), /* make */
|
||||
RTFUNCTAB_FUNC_PARAMS_CAST(rt_cline_params),
|
||||
RTFUNCTAB_FUNC_BBOX_CAST(rt_cline_bbox),
|
||||
NULL, /* volume */
|
||||
NULL, /* surf_area */
|
||||
RTFUNCTAB_FUNC_VOLUME_CAST(rt_cline_volume),
|
||||
RTFUNCTAB_FUNC_SURF_AREA_CAST(rt_cline_surf_area),
|
||||
NULL, /* centroid */
|
||||
NULL, /* oriented_bbox */
|
||||
NULL, /* find_selections */
|
||||
@@ -2109,8 +2109,8 @@ const struct rt_functab OBJ[] = {
|
||||
RTFUNCTAB_FUNC_MAKE_CAST(rt_metaball_make),
|
||||
RTFUNCTAB_FUNC_PARAMS_CAST(rt_metaball_params),
|
||||
RTFUNCTAB_FUNC_BBOX_CAST(rt_metaball_bbox),
|
||||
RTFUNCTAB_FUNC_VOLUME_CAST(rt_crofton_volume_implicit),
|
||||
RTFUNCTAB_FUNC_SURF_AREA_CAST(rt_crofton_surf_area_implicit),
|
||||
RTFUNCTAB_FUNC_VOLUME_CAST(rt_metaball_volume),
|
||||
RTFUNCTAB_FUNC_SURF_AREA_CAST(rt_metaball_surf_area),
|
||||
NULL, /* centroid */
|
||||
NULL, /* oriented_bbox */
|
||||
NULL, /* find_selections */
|
||||
|
||||
@@ -392,14 +392,6 @@ rt_edit_tor_edit_xy(
|
||||
if (f)
|
||||
(*f)(0, NULL, d, NULL);
|
||||
return BRLCAD_ERROR;
|
||||
case RT_MATRIX_EDIT_SCALE_X:
|
||||
case RT_MATRIX_EDIT_SCALE_Y:
|
||||
case RT_MATRIX_EDIT_SCALE_Z:
|
||||
bu_vls_printf(s->log_str, "%s: non-uniform scaling is not supported by %s objects\n", EDOBJ[ip->idb_type].ft_label, EDOBJ[ip->idb_type].ft_label);
|
||||
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
|
||||
if (f)
|
||||
(*f)(0, NULL, d, NULL);
|
||||
return BRLCAD_ERROR;
|
||||
default:
|
||||
return edit_generic_xy(s, mousevec);
|
||||
}
|
||||
|
||||
@@ -188,7 +188,11 @@ clt_tor_pack(struct bu_pool *pool, struct soltab *stp)
|
||||
* Compute the bounding RPP for a circular torus.
|
||||
*/
|
||||
C_DECL int
|
||||
rt_tor_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *UNUSED(tol)) {
|
||||
rt_tor_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *tol) {
|
||||
int nu_bbox = _rt_nonuniform_bbox(ip, min, max, tol);
|
||||
if (nu_bbox)
|
||||
return (nu_bbox > 0) ? 0 : -1;
|
||||
|
||||
vect_t P, w1; /* for RPP calculation */
|
||||
fastf_t f;
|
||||
struct rt_tor_internal *tip = (struct rt_tor_internal *)ip->idb_ptr;
|
||||
@@ -328,7 +332,7 @@ rt_tor_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0; /* OK */
|
||||
return _rt_nonuniform_prep_finalize(stp, ip, &rtip->rti_tol);
|
||||
}
|
||||
|
||||
|
||||
@@ -401,6 +405,9 @@ inside_overlapping_region(struct tor_specific *tor, point_t hp)
|
||||
C_DECL int
|
||||
rt_tor_shot(struct soltab *stp, register struct xray *rp, struct application *ap, struct seg *seghead)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp)
|
||||
return _rt_nonuniform_shot(stp, rp, ap, seghead);
|
||||
|
||||
register struct tor_specific *tor =
|
||||
(struct tor_specific *)stp->st_specific;
|
||||
register struct seg *segp;
|
||||
@@ -877,6 +884,11 @@ rt_tor_vshot(struct soltab **stp, struct xray **rp, struct seg *segp, int n, str
|
||||
C_DECL void
|
||||
rt_tor_norm(register struct hit *hitp, struct soltab *stp, register struct xray *rp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_norm(hitp, stp, rp);
|
||||
return;
|
||||
}
|
||||
|
||||
register struct tor_specific *tor =
|
||||
(struct tor_specific *)stp->st_specific;
|
||||
|
||||
@@ -904,6 +916,11 @@ rt_tor_norm(register struct hit *hitp, struct soltab *stp, register struct xray
|
||||
C_DECL void
|
||||
rt_tor_curve(register struct curvature *cvp, register struct hit *hitp, struct soltab *stp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_curve(cvp, hitp, stp);
|
||||
return;
|
||||
}
|
||||
|
||||
register struct tor_specific *tor =
|
||||
(struct tor_specific *)stp->st_specific;
|
||||
vect_t w4, w5;
|
||||
@@ -947,6 +964,11 @@ rt_tor_curve(register struct curvature *cvp, register struct hit *hitp, struct s
|
||||
C_DECL void
|
||||
rt_tor_uv(struct application *ap, struct soltab *stp, struct hit *hitp, struct uvcoord *uvp)
|
||||
{
|
||||
if (stp && stp->st_nu_inv_matp) {
|
||||
(void)_rt_nonuniform_uv(ap, stp, hitp, uvp);
|
||||
return;
|
||||
}
|
||||
|
||||
struct tor_specific *tor = (struct tor_specific *) stp->st_specific;
|
||||
vect_t work;
|
||||
vect_t pprime;
|
||||
@@ -1061,6 +1083,7 @@ tor_ellipse_points(
|
||||
C_DECL int
|
||||
rt_tor_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bn_tol *tol, const struct bview *v, fastf_t s_size)
|
||||
{
|
||||
struct rt_nonuniform_vlist_state nonuniform_state;
|
||||
vect_t a, b, tor_a, tor_b, tor_h, center;
|
||||
fastf_t mag_a, mag_b, mag_h;
|
||||
struct rt_tor_internal *tor;
|
||||
@@ -1069,6 +1092,7 @@ rt_tor_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const str
|
||||
|
||||
BU_CK_LIST_HEAD(vhead);
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
_rt_nonuniform_vlist_state_init(&nonuniform_state, vhead);
|
||||
struct bu_list *vlfree = &rt_vlfree;
|
||||
tor = (struct rt_tor_internal *)ip->idb_ptr;
|
||||
RT_TOR_CK_MAGIC(tor);
|
||||
@@ -1144,7 +1168,7 @@ rt_tor_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const str
|
||||
radian += radian_step;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1156,6 +1180,7 @@ rt_tor_adaptive_plot(struct bu_list *vhead, struct rt_db_internal *ip, const str
|
||||
C_DECL int
|
||||
rt_tor_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_tess_tol *ttol, const struct bn_tol *UNUSED(tol), const struct bview *UNUSED(info))
|
||||
{
|
||||
struct rt_nonuniform_vlist_state nonuniform_state;
|
||||
fastf_t alpha;
|
||||
fastf_t beta;
|
||||
fastf_t cos_alpha, sin_alpha;
|
||||
@@ -1174,6 +1199,7 @@ rt_tor_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_te
|
||||
|
||||
BU_CK_LIST_HEAD(vhead);
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
_rt_nonuniform_vlist_state_init(&nonuniform_state, vhead);
|
||||
struct bu_list *vlfree = &rt_vlfree;
|
||||
tip = (struct rt_tor_internal *)ip->idb_ptr;
|
||||
RT_TOR_CK_MAGIC(tip);
|
||||
@@ -1247,7 +1273,7 @@ rt_tor_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct bg_te
|
||||
}
|
||||
|
||||
bu_free((char *)pts, "rt_tor_plot pts[]");
|
||||
return 0;
|
||||
return _rt_nonuniform_plot_finalize(vhead, &nonuniform_state, ip);
|
||||
}
|
||||
|
||||
|
||||
@@ -1478,7 +1504,7 @@ rt_tor_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, co
|
||||
bu_log("rt_tor_tess: tube radius (%g) smaller than calculational tolerance (%g); returning empty mesh\n",
|
||||
r_h_eff, tol->dist);
|
||||
*r = nmg_mrsv(m);
|
||||
return 0;
|
||||
return _rt_nonuniform_tess_finalize(*r, ip, tol);
|
||||
}
|
||||
|
||||
/* Compute segment counts for the major circle (nlen, radius r_a) and the
|
||||
@@ -1514,7 +1540,8 @@ rt_tor_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, co
|
||||
* axis. Produce the OUTER surface as a closed manifold (sphere topology)
|
||||
* rather than the self-intersecting full tube. */
|
||||
if (r_h_eff > tip->r_a) {
|
||||
return rt_tor_spindle_tess(r, m, tip, r_h_eff, nw, nlen, tol);
|
||||
int ret = rt_tor_spindle_tess(r, m, tip, r_h_eff, nw, nlen, tol);
|
||||
return ret ? ret : _rt_nonuniform_tess_finalize(*r, ip, tol);
|
||||
}
|
||||
|
||||
/* Compute the points on the surface of the torus */
|
||||
@@ -1615,7 +1642,7 @@ rt_tor_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, co
|
||||
bu_free((char *)verts, "rt_tor_tess *verts[]");
|
||||
bu_free((char *)faces, "rt_tor_tess *faces[]");
|
||||
bu_free((char *)norms, "rt_tor_tess norms[]");
|
||||
return 0;
|
||||
return _rt_nonuniform_tess_finalize(*r, ip, tol);
|
||||
}
|
||||
|
||||
|
||||
@@ -1736,6 +1763,8 @@ rt_tor_export4(struct bu_external *ep, const struct rt_db_internal *ip, double l
|
||||
if (ip->idb_type != ID_TOR) return -1;
|
||||
tip = (struct rt_tor_internal *)ip->idb_ptr;
|
||||
RT_TOR_CK_MAGIC(tip);
|
||||
if (_rt_nonuniform_export4_check("rt_tor_export4", ip) < 0)
|
||||
return -1;
|
||||
|
||||
BU_CK_EXTERNAL(ep);
|
||||
ep->ext_nbytes = sizeof(union record);
|
||||
@@ -1812,6 +1841,25 @@ rt_tor_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
|
||||
struct rt_tor_internal *top = (struct rt_tor_internal *)rop->idb_ptr;
|
||||
RT_TOR_CK_MAGIC(top);
|
||||
|
||||
mat_t effective_mat;
|
||||
int remove_nonuniform = 0;
|
||||
{
|
||||
int nonuniform = _rt_nonuniform_transform_resolve(effective_mat, &remove_nonuniform, ip, mat);
|
||||
if (nonuniform < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (nonuniform) {
|
||||
*top = *tip;
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
return (_rt_nonuniform_attr_compose(rop, mat) < 0) ? BRLCAD_ERROR : BRLCAD_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (_rt_nonuniform_attr_copy(rop, ip) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
if (remove_nonuniform && _rt_nonuniform_attr_remove(rop) < 0)
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
double r_a, r_h;
|
||||
vect_t v, h;
|
||||
VMOVE(v, tip->v);
|
||||
@@ -1820,12 +1868,12 @@ rt_tor_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
|
||||
r_h = tip->r_h;
|
||||
|
||||
/* Apply modeling transformations */
|
||||
MAT4X3PNT(top->v, mat, v);
|
||||
MAT4X3VEC(top->h, mat, h);
|
||||
MAT4X3PNT(top->v, effective_mat, v);
|
||||
MAT4X3VEC(top->h, effective_mat, h);
|
||||
VUNITIZE(top->h); /* just to be sure */
|
||||
|
||||
top->r_a = r_a / mat[15];
|
||||
top->r_h = r_h / mat[15];
|
||||
top->r_a = r_a / effective_mat[15];
|
||||
top->r_h = r_h / effective_mat[15];
|
||||
|
||||
/* Prepare the extra information */
|
||||
top->r_b = top->r_a;
|
||||
@@ -1873,9 +1921,6 @@ rt_tor_import5(struct rt_db_internal *ip, const struct bu_external *ep, register
|
||||
RT_CK_DB_INTERNAL(ip);
|
||||
|
||||
if (mat == NULL) mat = bn_mat_identity;
|
||||
if (bn_mat_is_non_unif (mat)) {
|
||||
bu_log("------------------ WARNING ----------------\nNon-uniform matrix transform on torus. Ignored\n");
|
||||
}
|
||||
|
||||
ip->idb_major_type = DB5_MAJORTYPE_BRLCAD;
|
||||
ip->idb_type = ID_TOR;
|
||||
@@ -2010,6 +2055,9 @@ rt_tor_params(struct pc_pc_set *UNUSED(ps), const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_tor_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_surf_area(area, ip))
|
||||
return;
|
||||
|
||||
struct rt_tor_internal *tip = (struct rt_tor_internal *)ip->idb_ptr;
|
||||
RT_TOR_CK_MAGIC(tip);
|
||||
|
||||
@@ -2035,6 +2083,9 @@ rt_tor_surf_area(fastf_t *area, const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_tor_volume(fastf_t *vol, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_volume(vol, ip))
|
||||
return;
|
||||
|
||||
struct rt_tor_internal *tip = (struct rt_tor_internal *)ip->idb_ptr;
|
||||
RT_TOR_CK_MAGIC(tip);
|
||||
|
||||
@@ -2053,6 +2104,9 @@ rt_tor_volume(fastf_t *vol, const struct rt_db_internal *ip)
|
||||
C_DECL void
|
||||
rt_tor_centroid(point_t *cent, const struct rt_db_internal *ip)
|
||||
{
|
||||
if (_rt_nonuniform_centroid(cent, ip))
|
||||
return;
|
||||
|
||||
struct rt_tor_internal *tip = (struct rt_tor_internal *)ip->idb_ptr;
|
||||
RT_TOR_CK_MAGIC(tip);
|
||||
VMOVE(*cent,tip->v);
|
||||
|
||||
@@ -196,9 +196,15 @@ brlcad_add_test(NAME rt_dsp_sa_vol_check COMMAND rt_dsp_sa_vol_check)
|
||||
# "-c" compares against Crofton
|
||||
brlcad_add_test(NAME rt_dsp_sa_vol_compare COMMAND rt_dsp_sa_vol_check -c)
|
||||
|
||||
# DSP raytrace consistency + timing check (HBB+DDA path)
|
||||
brlcad_addexec(rt_dsp_raytrace_check dsp_raytrace_check.c "librt;libwdb;libbu;${M_LIBRARY}" TEST)
|
||||
brlcad_add_test(NAME rt_dsp_raytrace_check COMMAND rt_dsp_raytrace_check)
|
||||
|
||||
brlcad_addexec(rt_nonuniform_curve nonuniform_curve.c "librt;libwdb;libbu;${M_LIBRARY}" TEST)
|
||||
brlcad_add_test(NAME rt_nonuniform_curve COMMAND rt_nonuniform_curve)
|
||||
|
||||
brlcad_addexec(rt_nonuniform_ray nonuniform_ray.c "librt;libwdb;libbu;${M_LIBRARY}" TEST)
|
||||
brlcad_add_test(NAME rt_nonuniform_ray COMMAND rt_nonuniform_ray)
|
||||
|
||||
brlcad_addexec(rt_pipe_shotline pipe_shotline.c "librt;libwdb;libbu;${M_LIBRARY}" TEST)
|
||||
brlcad_add_test(NAME rt_pipe_shotline COMMAND rt_pipe_shotline)
|
||||
|
||||
# in-memory test for primitives
|
||||
brlcad_addexec(rt_invalid_inmem invalid_inmem.c "librt;libbn;libbu;${M_LIBRARY}" TEST)
|
||||
@@ -231,6 +237,9 @@ set(
|
||||
lcomb_large.c
|
||||
matrix_tests.g
|
||||
metaball_adjust.c
|
||||
nonuniform_curve.c
|
||||
nonuniform_ray.c
|
||||
pipe_shotline.c
|
||||
nurbs_surfaces.g
|
||||
rt_datum.c
|
||||
rt_perturb.c
|
||||
|
||||
@@ -104,6 +104,8 @@ tor_reset(struct rt_edit *s, struct rt_tor_internal *edit_tor,
|
||||
MAT_IDN(s->incr_change);
|
||||
MAT_IDN(s->model_changes);
|
||||
s->acc_sc_sol = 1.0;
|
||||
s->acc_sc_obj = 1.0;
|
||||
VSETALL(s->acc_sc, 1.0);
|
||||
s->e_inpara = 0;
|
||||
s->es_scale = 0.0;
|
||||
s->mv_context = 1;
|
||||
@@ -551,6 +553,43 @@ main(int argc, char *argv[])
|
||||
bu_log("RT_PARAMS_EDIT_ROT(xy) SUCCESS: original v value %g,%g,%g modified to %g,%g,%g\n", V3ARGS(orig_tor->v), V3ARGS(edit_tor->v));
|
||||
bu_log("RT_PARAMS_EDIT_ROT(xy) SUCCESS: original h value %g,%g,%g modified to %g,%g,%g\n", V3ARGS(orig_tor->h), V3ARGS(edit_tor->h));
|
||||
|
||||
/* The MGED matrix-edit faceplate routes its Scale X/Y/Z buttons through
|
||||
* ft_edit_xy. TOR must allow those generic affine edit modes. */
|
||||
const int scale_modes[3] = {
|
||||
RT_MATRIX_EDIT_SCALE_X,
|
||||
RT_MATRIX_EDIT_SCALE_Y,
|
||||
RT_MATRIX_EDIT_SCALE_Z
|
||||
};
|
||||
const char *scale_labels[3] = {"X", "Y", "Z"};
|
||||
const fastf_t mouse_scale = 0.5;
|
||||
const fastf_t expected_scale = 1.0 + mouse_scale;
|
||||
|
||||
for (int axis = X; axis <= Z; axis++) {
|
||||
tor_reset(s, edit_tor, cmp_tor, orig_tor);
|
||||
EDOBJ[dp->d_minor_type].ft_set_edit_mode(s, scale_modes[axis]);
|
||||
|
||||
vect_t scale_mouse = VINIT_ZERO;
|
||||
scale_mouse[Y] = mouse_scale;
|
||||
bu_vls_trunc(s->log_str, 0);
|
||||
if ((*EDOBJ[dp->d_minor_type].ft_edit_xy)(s, scale_mouse) != BRLCAD_OK)
|
||||
bu_exit(1, "ERROR: RT_MATRIX_EDIT_SCALE_%s failed ft_edit_xy call: %s\n",
|
||||
scale_labels[axis], bu_vls_cstr(s->log_str));
|
||||
|
||||
vect_t factors;
|
||||
VSETALL(factors, 1.0);
|
||||
factors[axis] = expected_scale;
|
||||
mat_t diagonal, expected;
|
||||
MAT_IDN(diagonal);
|
||||
MAT_SCALE(diagonal, factors[X], factors[Y], factors[Z]);
|
||||
bn_mat_xform_about_pnt(expected, diagonal, s->e_keypoint);
|
||||
if (!bn_mat_is_equal(s->model_changes, expected, &tol))
|
||||
bu_exit(1, "ERROR: RT_MATRIX_EDIT_SCALE_%s produced an unexpected edit matrix\n",
|
||||
scale_labels[axis]);
|
||||
|
||||
bu_log("RT_MATRIX_EDIT_SCALE_%s SUCCESS: faceplate XY edit produced scale %g\n",
|
||||
scale_labels[axis], expected_scale);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ================================================================
|
||||
@@ -625,4 +664,3 @@ bu_log("RT_MATRIX_EDIT_TRANS_MODEL_XYZ SUCCESS: "
|
||||
// c-file-style: "stroustrup"
|
||||
// End:
|
||||
// ex: shiftwidth=4 tabstop=8
|
||||
|
||||
|
||||
@@ -0,0 +1,673 @@
|
||||
/* N O N U N I F O R M _ C U R V E . C
|
||||
* BRL-CAD
|
||||
*
|
||||
* Copyright (c) 2026 United States Government as represented by
|
||||
* the U.S. Army Research Laboratory.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* version 2.1 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this file; see the file named COPYING for more
|
||||
* information.
|
||||
*/
|
||||
/** @file librt/tests/nonuniform_curve.c
|
||||
*
|
||||
* Stand-alone tests for matrix:nonuniform curvature callbacks.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bu/app.h"
|
||||
#include "bu/log.h"
|
||||
#include "bu/str.h"
|
||||
#include "bu/vls.h"
|
||||
#include "raytrace.h"
|
||||
#include "rt/geom.h"
|
||||
#include "wdb.h"
|
||||
|
||||
#include "../librt_private.h"
|
||||
|
||||
|
||||
#define CURVE_TOL 1.0e-5
|
||||
#define TANGENT_TOL 1.0e-5
|
||||
|
||||
|
||||
struct prim_case {
|
||||
const char *name;
|
||||
int affine_enabled;
|
||||
};
|
||||
|
||||
|
||||
struct body_ray {
|
||||
point_t p;
|
||||
vect_t d;
|
||||
};
|
||||
|
||||
|
||||
struct curve_sample {
|
||||
int hit;
|
||||
struct curvature curve;
|
||||
vect_t normal;
|
||||
};
|
||||
|
||||
|
||||
struct affine_case {
|
||||
const char *name;
|
||||
fastf_t sx;
|
||||
fastf_t sy;
|
||||
fastf_t sz;
|
||||
fastf_t rx;
|
||||
fastf_t ry;
|
||||
fastf_t rz;
|
||||
};
|
||||
|
||||
|
||||
static const struct prim_case prims[] = {
|
||||
{"tor", 1},
|
||||
{"eto", 1},
|
||||
{"epa", 1},
|
||||
{"ehy", 1},
|
||||
{"rpc", 1},
|
||||
{"rhc", 1},
|
||||
{"particle", 1},
|
||||
{"hyp", 1},
|
||||
{"cline", 0},
|
||||
{"pipe", 0},
|
||||
{"metaball", 0}
|
||||
};
|
||||
|
||||
|
||||
static const struct affine_case affine_cases[] = {
|
||||
{"nonuniform", 1.7, 0.65, 1.3, 0.0, 0.0, 0.0},
|
||||
{"rot_xyz", 1.7, 0.65, 1.3, 17.0, -23.0, 31.0},
|
||||
{"rot_xz", 1.7, 1.0, 0.55, -12.0, 28.0, 9.0}
|
||||
};
|
||||
|
||||
|
||||
static const struct body_ray rays[] = {
|
||||
{{8.0, 0.0, 8.0}, {0.0, 0.0, -1.0}},
|
||||
{{8.0, 0.0, 10.0}, {-1.0, 0.0, 0.0}},
|
||||
{{0.0, 8.0, 10.0}, {0.0, -1.0, 0.0}},
|
||||
{{8.0, 0.0, 6.0}, {-1.0, 0.0, 0.0}},
|
||||
{{0.0, 8.0, 6.0}, {0.0, -1.0, 0.0}},
|
||||
{{8.0, 0.0, 3.0}, {-1.0, 0.0, 0.0}},
|
||||
{{0.0, 8.0, 3.0}, {0.0, -1.0, 0.0}},
|
||||
{{8.0, 0.0, 1.0}, {-1.0, 0.0, 0.0}},
|
||||
{{0.0, 8.0, 1.0}, {0.0, -1.0, 0.0}},
|
||||
{{10.0, 0.0, 0.0}, {-1.0, 0.0, 0.0}},
|
||||
{{0.0, 10.0, 0.0}, {0.0, -1.0, 0.0}},
|
||||
{{0.0, 0.0, 20.0}, {0.0, 0.0, -1.0}}
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
make_uniform_mat(mat_t mat, fastf_t scale)
|
||||
{
|
||||
MAT_IDN(mat);
|
||||
mat[0] = scale;
|
||||
mat[5] = scale;
|
||||
mat[10] = scale;
|
||||
mat[3] = 4.0;
|
||||
mat[7] = -3.0;
|
||||
mat[11] = 2.0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
make_affine_mat(mat_t mat, const struct affine_case *ac)
|
||||
{
|
||||
mat_t rot, scale;
|
||||
|
||||
MAT_IDN(scale);
|
||||
scale[0] = ac->sx;
|
||||
scale[5] = ac->sy;
|
||||
scale[10] = ac->sz;
|
||||
|
||||
bn_mat_angles(rot, ac->rx, ac->ry, ac->rz);
|
||||
bn_mat_mul(mat, rot, scale);
|
||||
mat[3] = 12.0;
|
||||
mat[7] = -5.0;
|
||||
mat[11] = 3.0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
transform_ray(struct xray *ray, const struct body_ray *body, const mat_t mat)
|
||||
{
|
||||
memset(ray, 0, sizeof(struct xray));
|
||||
ray->magic = RT_RAY_MAGIC;
|
||||
MAT4X3PNT(ray->r_pt, mat, body->p);
|
||||
MAT4X3VEC(ray->r_dir, mat, body->d);
|
||||
VUNITIZE(ray->r_dir);
|
||||
ray->r_min = 0.0;
|
||||
ray->r_max = INFINITY;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
curve_finite(const struct curvature *curve)
|
||||
{
|
||||
return isfinite(curve->crv_c1) && isfinite(curve->crv_c2) &&
|
||||
isfinite(curve->crv_pdir[X]) &&
|
||||
isfinite(curve->crv_pdir[Y]) &&
|
||||
isfinite(curve->crv_pdir[Z]);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
significant_curve(const struct curvature *curve)
|
||||
{
|
||||
return fabs(curve->crv_c1) > CURVE_TOL || fabs(curve->crv_c2) > CURVE_TOL;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
near_value(fastf_t a, fastf_t b)
|
||||
{
|
||||
fastf_t scale = fabs(b);
|
||||
if (scale < 1.0)
|
||||
scale = 1.0;
|
||||
return fabs(a - b) <= CURVE_TOL * scale;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
unitize_checked(vect_t v)
|
||||
{
|
||||
fastf_t mag;
|
||||
|
||||
if (!isfinite(v[X]) || !isfinite(v[Y]) || !isfinite(v[Z]))
|
||||
return -1;
|
||||
|
||||
mag = MAGNITUDE(v);
|
||||
if (!isfinite(mag) || NEAR_ZERO(mag, VDIVIDE_TOL))
|
||||
return -1;
|
||||
|
||||
VSCALE(v, v, 1.0 / mag);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
expected_affine_curve(struct curvature *out, const struct curvature *body_curve, const vect_t body_normal, const mat_t body_to_model, const mat_t model_to_body)
|
||||
{
|
||||
vect_t n, e1, e2;
|
||||
vect_t t1, t2, model_n;
|
||||
vect_t ev1, ev2;
|
||||
fastf_t normal_dot;
|
||||
fastf_t alpha;
|
||||
fastf_t g11, g12, g22;
|
||||
fastf_t l11, l21, l22, rad;
|
||||
fastf_t a, c, d;
|
||||
fastf_t b1, b2;
|
||||
fastf_t h00, h01, h11;
|
||||
fastf_t v0, v1;
|
||||
|
||||
*out = (struct curvature)RT_CURVATURE_INIT_ZERO;
|
||||
|
||||
VMOVE(n, body_normal);
|
||||
if (unitize_checked(n) < 0)
|
||||
return -1;
|
||||
|
||||
VMOVE(e1, body_curve->crv_pdir);
|
||||
if (unitize_checked(e1) < 0)
|
||||
bn_vec_ortho(e1, n);
|
||||
|
||||
normal_dot = VDOT(e1, n);
|
||||
VJOIN1(e1, e1, -normal_dot, n);
|
||||
if (unitize_checked(e1) < 0)
|
||||
bn_vec_ortho(e1, n);
|
||||
|
||||
VCROSS(e2, n, e1);
|
||||
if (unitize_checked(e2) < 0)
|
||||
return -1;
|
||||
|
||||
MAT4X3VEC(t1, body_to_model, e1);
|
||||
MAT4X3VEC(t2, body_to_model, e2);
|
||||
|
||||
VEC3X3MAT(model_n, n, model_to_body);
|
||||
alpha = MAGNITUDE(model_n);
|
||||
if (!isfinite(alpha) || NEAR_ZERO(alpha, VDIVIDE_TOL))
|
||||
return -1;
|
||||
VSCALE(model_n, model_n, 1.0 / alpha);
|
||||
|
||||
g11 = VDOT(t1, t1);
|
||||
g12 = VDOT(t1, t2);
|
||||
g22 = VDOT(t2, t2);
|
||||
|
||||
l11 = sqrt(g11);
|
||||
if (!isfinite(l11) || NEAR_ZERO(l11, VDIVIDE_TOL))
|
||||
return -1;
|
||||
|
||||
l21 = g12 / l11;
|
||||
rad = g22 - l21 * l21;
|
||||
if (!isfinite(rad) || rad < VDIVIDE_TOL)
|
||||
return -1;
|
||||
l22 = sqrt(rad);
|
||||
if (!isfinite(l22) || NEAR_ZERO(l22, VDIVIDE_TOL))
|
||||
return -1;
|
||||
|
||||
a = 1.0 / l11;
|
||||
c = -l21 / (l11 * l22);
|
||||
d = 1.0 / l22;
|
||||
b1 = body_curve->crv_c1 / alpha;
|
||||
b2 = body_curve->crv_c2 / alpha;
|
||||
|
||||
h00 = a * a * b1;
|
||||
h01 = a * c * b1;
|
||||
h11 = c * c * b1 + d * d * b2;
|
||||
|
||||
bn_eigen2x2(&out->crv_c1, &out->crv_c2, ev1, ev2, h00, h01, h11);
|
||||
|
||||
v1 = ev1[Y] / l22;
|
||||
v0 = (ev1[X] - l21 * v1) / l11;
|
||||
VCOMB2(out->crv_pdir, v0, t1, v1, t2);
|
||||
normal_dot = VDOT(out->crv_pdir, model_n);
|
||||
VJOIN1(out->crv_pdir, out->crv_pdir, -normal_dot, model_n);
|
||||
|
||||
return unitize_checked(out->crv_pdir);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
near_curve(const struct curvature *expected, const struct curvature *got)
|
||||
{
|
||||
if (!near_value(expected->crv_c1, got->crv_c1) ||
|
||||
!near_value(expected->crv_c2, got->crv_c2))
|
||||
return 0;
|
||||
|
||||
return fabs(VDOT(expected->crv_pdir, got->crv_pdir)) > 1.0 - TANGENT_TOL;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
valid_affine_sample(const struct curve_sample *sample)
|
||||
{
|
||||
fastf_t pdir_mag = MAGNITUDE(sample->curve.crv_pdir);
|
||||
|
||||
if (!curve_finite(&sample->curve))
|
||||
return 0;
|
||||
if (fabs(sample->curve.crv_c1) > fabs(sample->curve.crv_c2) + CURVE_TOL)
|
||||
return 0;
|
||||
if (fabs(pdir_mag - 1.0) > TANGENT_TOL)
|
||||
return 0;
|
||||
if (fabs(VDOT(sample->curve.crv_pdir, sample->normal)) > TANGENT_TOL)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
write_pipe(struct rt_wdb *wdbp, const char *name)
|
||||
{
|
||||
struct bu_list head;
|
||||
point_t p0 = {0.0, 0.0, 0.0};
|
||||
point_t p1 = {0.0, 0.0, 16.0};
|
||||
int ret;
|
||||
|
||||
mk_pipe_init(&head);
|
||||
mk_add_pipe_pnt(&head, p0, 4.0, 0.0, 5.0);
|
||||
mk_add_pipe_pnt(&head, p1, 4.0, 0.0, 5.0);
|
||||
ret = mk_pipe(wdbp, name, &head);
|
||||
mk_pipe_free(&head);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
write_metaball(struct rt_wdb *wdbp, const char *name)
|
||||
{
|
||||
fastf_t mb0[5] = {-2.0, 0.0, 0.0, 3.0, 0.0};
|
||||
fastf_t mb1[5] = {2.0, 0.0, 0.0, 3.0, 0.0};
|
||||
fastf_t mb2[5] = {0.0, 2.0, 1.0, 2.5, 0.0};
|
||||
const fastf_t *verts[5] = {mb0, mb1, mb2, NULL, NULL};
|
||||
|
||||
return mk_metaball(wdbp, name, 3, METABALL_ISOPOTENTIAL, 1.0, verts);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
write_primitive(struct rt_wdb *wdbp, const char *type, const char *name)
|
||||
{
|
||||
point_t v = {0.0, 0.0, 0.0};
|
||||
vect_t h = {0.0, 0.0, 12.0};
|
||||
vect_t b = {0.0, 6.0, 0.0};
|
||||
vect_t a = {4.0, 0.0, 0.0};
|
||||
vect_t au = {1.0, 0.0, 0.0};
|
||||
vect_t n = {0.0, 0.0, 1.0};
|
||||
vect_t c = {3.0, 0.0, 0.0};
|
||||
|
||||
if (BU_STR_EQUAL(type, "tor"))
|
||||
return mk_tor(wdbp, name, v, n, 6.0, 1.5);
|
||||
if (BU_STR_EQUAL(type, "eto"))
|
||||
return mk_eto(wdbp, name, v, n, c, 6.0, 1.2);
|
||||
if (BU_STR_EQUAL(type, "epa"))
|
||||
return mk_epa(wdbp, name, v, h, au, 4.0, 2.5);
|
||||
if (BU_STR_EQUAL(type, "ehy"))
|
||||
return mk_ehy(wdbp, name, v, h, au, 4.0, 2.5, 16.0);
|
||||
if (BU_STR_EQUAL(type, "rpc"))
|
||||
return mk_rpc(wdbp, name, v, h, b, 2.5);
|
||||
if (BU_STR_EQUAL(type, "rhc"))
|
||||
return mk_rhc(wdbp, name, v, h, b, 2.5, 5.0);
|
||||
if (BU_STR_EQUAL(type, "particle"))
|
||||
return mk_particle(wdbp, name, v, h, 2.0, 3.0);
|
||||
if (BU_STR_EQUAL(type, "hyp"))
|
||||
return mk_hyp(wdbp, name, v, h, a, 2.5, 0.45);
|
||||
if (BU_STR_EQUAL(type, "cline"))
|
||||
return mk_cline(wdbp, name, v, h, 2.0, 0.5);
|
||||
if (BU_STR_EQUAL(type, "pipe"))
|
||||
return write_pipe(wdbp, name);
|
||||
if (BU_STR_EQUAL(type, "metaball"))
|
||||
return write_metaball(wdbp, name);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
set_nonuniform_attr(struct db_i *dbip, const char *name, const mat_t mat)
|
||||
{
|
||||
struct directory *dp = db_lookup(dbip, name, LOOKUP_QUIET);
|
||||
struct rt_db_internal intern;
|
||||
struct bu_vls value = BU_VLS_INIT_ZERO;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
if (!dp)
|
||||
return -1;
|
||||
|
||||
RT_DB_INTERNAL_INIT(&intern);
|
||||
if (rt_db_get_internal(&intern, dp, dbip, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (i)
|
||||
bu_vls_putc(&value, ',');
|
||||
bu_vls_printf(&value, "%.17g", mat[i]);
|
||||
}
|
||||
|
||||
if (bu_avs_add(&intern.idb_avs, RT_MATRIX_NONUNIFORM_ATTR, bu_vls_cstr(&value)) < 0)
|
||||
ret = -1;
|
||||
else if (rt_db_put_internal(dp, dbip, &intern) < 0)
|
||||
ret = -1;
|
||||
|
||||
bu_vls_free(&value);
|
||||
rt_db_free_internal(&intern);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
curve_hit(struct application *ap, struct partition *PartHeadp, struct seg *UNUSED(segp))
|
||||
{
|
||||
struct curve_sample *sample = (struct curve_sample *)ap->a_uptr;
|
||||
struct partition *pp = PartHeadp->pt_forw;
|
||||
|
||||
if (pp == PartHeadp)
|
||||
return 0;
|
||||
|
||||
RT_HIT_NORMAL(sample->normal, pp->pt_inhit, pp->pt_inseg->seg_stp, NULL, pp->pt_inflip);
|
||||
RT_CURVATURE(&sample->curve, pp->pt_inhit, pp->pt_inflip, pp->pt_inseg->seg_stp);
|
||||
sample->hit = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
curve_miss(struct application *UNUSED(ap))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
shoot_curve(struct db_i *dbip, const char *objname, const struct xray *ray, struct curve_sample *sample)
|
||||
{
|
||||
struct rt_i *rtip;
|
||||
struct application ap;
|
||||
|
||||
memset(sample, 0, sizeof(struct curve_sample));
|
||||
|
||||
rtip = rt_i_create(dbip);
|
||||
if (!rtip)
|
||||
return -1;
|
||||
|
||||
if (rt_gettree(rtip, objname) != 0) {
|
||||
rt_i_destroy(rtip);
|
||||
return -1;
|
||||
}
|
||||
rt_prep_parallel(rtip, 1);
|
||||
|
||||
RT_APPLICATION_INIT(&ap);
|
||||
ap.a_rt_i = rtip;
|
||||
ap.a_onehit = 1;
|
||||
ap.a_uptr = sample;
|
||||
ap.a_hit = curve_hit;
|
||||
ap.a_miss = curve_miss;
|
||||
ap.a_ray = *ray;
|
||||
|
||||
rt_shootray(&ap);
|
||||
rt_i_destroy(rtip);
|
||||
|
||||
return sample->hit ? 0 : -1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test_enabled_primitive(struct db_i *dbip, const char *name)
|
||||
{
|
||||
char base[64], uni[64];
|
||||
mat_t identity, uniform_mat;
|
||||
int uniform_ok = 0;
|
||||
int affine_ok[sizeof(affine_cases) / sizeof(affine_cases[0])] = {0};
|
||||
|
||||
snprintf(base, sizeof(base), "%s_base.s", name);
|
||||
snprintf(uni, sizeof(uni), "%s_uniform.s", name);
|
||||
|
||||
MAT_IDN(identity);
|
||||
make_uniform_mat(uniform_mat, 2.0);
|
||||
|
||||
for (size_t i = 0; i < sizeof(rays) / sizeof(rays[0]); i++) {
|
||||
struct xray base_ray, uni_ray, nonu_ray;
|
||||
struct curve_sample base_sample, uni_sample, nonu_sample;
|
||||
|
||||
transform_ray(&base_ray, &rays[i], identity);
|
||||
if (shoot_curve(dbip, base, &base_ray, &base_sample) < 0 ||
|
||||
!curve_finite(&base_sample.curve) ||
|
||||
!significant_curve(&base_sample.curve))
|
||||
continue;
|
||||
|
||||
transform_ray(&uni_ray, &rays[i], uniform_mat);
|
||||
if (shoot_curve(dbip, uni, &uni_ray, &uni_sample) == 0 &&
|
||||
valid_affine_sample(&uni_sample) &&
|
||||
near_value(uni_sample.curve.crv_c1, base_sample.curve.crv_c1 / 2.0) &&
|
||||
near_value(uni_sample.curve.crv_c2, base_sample.curve.crv_c2 / 2.0))
|
||||
uniform_ok = 1;
|
||||
|
||||
for (size_t j = 0; j < sizeof(affine_cases) / sizeof(affine_cases[0]); j++) {
|
||||
char nonu[64];
|
||||
mat_t affine_mat, inv_mat;
|
||||
struct curvature expected;
|
||||
|
||||
if (affine_ok[j])
|
||||
continue;
|
||||
|
||||
snprintf(nonu, sizeof(nonu), "%s_%s.s", name, affine_cases[j].name);
|
||||
make_affine_mat(affine_mat, &affine_cases[j]);
|
||||
if (!bn_mat_inverse(inv_mat, affine_mat))
|
||||
continue;
|
||||
|
||||
transform_ray(&nonu_ray, &rays[i], affine_mat);
|
||||
if (expected_affine_curve(&expected, &base_sample.curve, base_sample.normal, affine_mat, inv_mat) == 0 &&
|
||||
shoot_curve(dbip, nonu, &nonu_ray, &nonu_sample) == 0 &&
|
||||
valid_affine_sample(&nonu_sample) &&
|
||||
near_curve(&expected, &nonu_sample.curve))
|
||||
affine_ok[j] = 1;
|
||||
}
|
||||
|
||||
if (uniform_ok) {
|
||||
int all_affine_ok = 1;
|
||||
for (size_t j = 0; j < sizeof(affine_cases) / sizeof(affine_cases[0]); j++)
|
||||
all_affine_ok = all_affine_ok && affine_ok[j];
|
||||
if (all_affine_ok)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!uniform_ok)
|
||||
bu_log("[nonuniform_curve] %s failed uniform-scale curvature check\n", name);
|
||||
for (size_t j = 0; j < sizeof(affine_cases) / sizeof(affine_cases[0]); j++) {
|
||||
if (!affine_ok[j])
|
||||
bu_log("[nonuniform_curve] %s failed %s curvature check\n", name, affine_cases[j].name);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test_disabled_primitive(struct db_i *dbip, const char *name)
|
||||
{
|
||||
for (size_t j = 0; j < sizeof(affine_cases) / sizeof(affine_cases[0]); j++) {
|
||||
char nonu[64];
|
||||
mat_t affine_mat;
|
||||
int unavailable_ok = 0;
|
||||
|
||||
snprintf(nonu, sizeof(nonu), "%s_%s.s", name, affine_cases[j].name);
|
||||
make_affine_mat(affine_mat, &affine_cases[j]);
|
||||
|
||||
for (size_t i = 0; i < sizeof(rays) / sizeof(rays[0]); i++) {
|
||||
struct xray ray;
|
||||
struct curve_sample sample;
|
||||
|
||||
transform_ray(&ray, &rays[i], affine_mat);
|
||||
if (shoot_curve(dbip, nonu, &ray, &sample) < 0)
|
||||
continue;
|
||||
|
||||
if (curve_finite(&sample.curve) &&
|
||||
!significant_curve(&sample.curve) &&
|
||||
NEAR_ZERO(MAGSQ(sample.curve.crv_pdir), VDIVIDE_TOL)) {
|
||||
unavailable_ok = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
bu_log("[nonuniform_curve] %s unexpectedly returned %s affine curvature\n", name, affine_cases[j].name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!unavailable_ok) {
|
||||
bu_log("[nonuniform_curve] %s did not produce a transformed hit for %s disabled-curve check\n", name, affine_cases[j].name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
build_db(struct db_i **dbip_out, struct rt_wdb **wdbp_out)
|
||||
{
|
||||
struct db_i *dbip = db_open_inmem();
|
||||
struct rt_wdb *wdbp;
|
||||
mat_t uniform_mat;
|
||||
|
||||
if (!dbip)
|
||||
return -1;
|
||||
|
||||
wdbp = wdb_dbopen(dbip, RT_WDB_TYPE_DB_INMEM);
|
||||
if (!wdbp) {
|
||||
db_close(dbip);
|
||||
return -1;
|
||||
}
|
||||
|
||||
make_uniform_mat(uniform_mat, 2.0);
|
||||
|
||||
for (size_t i = 0; i < sizeof(prims) / sizeof(prims[0]); i++) {
|
||||
char base[64], uni[64];
|
||||
|
||||
snprintf(base, sizeof(base), "%s_base.s", prims[i].name);
|
||||
snprintf(uni, sizeof(uni), "%s_uniform.s", prims[i].name);
|
||||
|
||||
if (write_primitive(wdbp, prims[i].name, base) < 0 ||
|
||||
write_primitive(wdbp, prims[i].name, uni) < 0) {
|
||||
wdb_close(wdbp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (set_nonuniform_attr(dbip, uni, uniform_mat) < 0) {
|
||||
wdb_close(wdbp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < sizeof(affine_cases) / sizeof(affine_cases[0]); j++) {
|
||||
char nonu[64];
|
||||
mat_t affine_mat;
|
||||
|
||||
snprintf(nonu, sizeof(nonu), "%s_%s.s", prims[i].name, affine_cases[j].name);
|
||||
make_affine_mat(affine_mat, &affine_cases[j]);
|
||||
|
||||
if (write_primitive(wdbp, prims[i].name, nonu) < 0 ||
|
||||
set_nonuniform_attr(dbip, nonu, affine_mat) < 0) {
|
||||
wdb_close(wdbp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
db_update_nref(dbip);
|
||||
*dbip_out = dbip;
|
||||
*wdbp_out = wdbp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct db_i *dbip = DBI_NULL;
|
||||
struct rt_wdb *wdbp = RT_WDB_NULL;
|
||||
int failures = 0;
|
||||
|
||||
bu_setprogname(argv[0]);
|
||||
(void)argc;
|
||||
|
||||
if (build_db(&dbip, &wdbp) < 0)
|
||||
bu_exit(1, "[nonuniform_curve] failed to build test database\n");
|
||||
|
||||
for (size_t i = 0; i < sizeof(prims) / sizeof(prims[0]); i++) {
|
||||
if (prims[i].affine_enabled) {
|
||||
if (test_enabled_primitive(dbip, prims[i].name) < 0)
|
||||
failures++;
|
||||
} else {
|
||||
if (test_disabled_primitive(dbip, prims[i].name) < 0)
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
wdb_close(wdbp);
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* tab-width: 8
|
||||
* mode: C
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "stroustrup"
|
||||
* End:
|
||||
* ex: shiftwidth=4 tabstop=8
|
||||
*/
|
||||
@@ -0,0 +1,612 @@
|
||||
/* N O N U N I F O R M _ R A Y . C
|
||||
* BRL-CAD
|
||||
*
|
||||
* Copyright (c) 2026 United States Government as represented by
|
||||
* the U.S. Army Research Laboratory.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* version 2.1 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this file; see the file named COPYING for more
|
||||
* information.
|
||||
*/
|
||||
/** @file librt/tests/nonuniform_ray.c
|
||||
*
|
||||
* Ray-grid and silhouette-mask tests for matrix:nonuniform primitives.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bu/app.h"
|
||||
#include "bu/log.h"
|
||||
#include "bu/str.h"
|
||||
#include "bu/vls.h"
|
||||
#include "raytrace.h"
|
||||
#include "rt/geom.h"
|
||||
#include "wdb.h"
|
||||
|
||||
#include "../librt_private.h"
|
||||
|
||||
|
||||
#define POINT_TOL 1.0e-4
|
||||
#define NORMAL_DOT_TOL 0.995
|
||||
#define SPARSE_GRID 16
|
||||
#define MASK_GRID 40
|
||||
|
||||
|
||||
struct prim_case {
|
||||
const char *name;
|
||||
int mask_tolerance;
|
||||
};
|
||||
|
||||
|
||||
struct scale_case {
|
||||
const char *name;
|
||||
fastf_t sx;
|
||||
fastf_t sy;
|
||||
fastf_t sz;
|
||||
fastf_t rx;
|
||||
fastf_t ry;
|
||||
fastf_t rz;
|
||||
};
|
||||
|
||||
|
||||
struct view_case {
|
||||
const char *name;
|
||||
vect_t dir;
|
||||
vect_t u;
|
||||
vect_t v;
|
||||
};
|
||||
|
||||
|
||||
struct ray_sample {
|
||||
int hit;
|
||||
point_t in_pt;
|
||||
point_t out_pt;
|
||||
vect_t in_norm;
|
||||
vect_t out_norm;
|
||||
};
|
||||
|
||||
|
||||
static const struct prim_case prims[] = {
|
||||
{"tor", 0},
|
||||
{"eto", 0},
|
||||
{"epa", 0},
|
||||
{"ehy", 0},
|
||||
{"rpc", 0},
|
||||
{"rhc", 0},
|
||||
{"particle", 0},
|
||||
{"hyp", 0},
|
||||
{"cline", 0},
|
||||
{"pipe", 0},
|
||||
/* Metaball shooting is iterative; sparse affine hit/normal checks remain exact. */
|
||||
{"metaball", 2}
|
||||
};
|
||||
|
||||
|
||||
static const struct scale_case scales[] = {
|
||||
{"x", 1.7, 1.0, 1.0, 0.0, 0.0, 0.0},
|
||||
{"y", 1.0, 0.65, 1.0, 0.0, 0.0, 0.0},
|
||||
{"z", 1.0, 1.0, 1.3, 0.0, 0.0, 0.0},
|
||||
{"xyz", 1.7, 0.65, 1.3, 0.0, 0.0, 0.0},
|
||||
{"rot_xyz", 1.7, 0.65, 1.3, 17.0, -23.0, 31.0},
|
||||
{"rot_xz", 1.7, 1.0, 0.55, -12.0, 28.0, 9.0}
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
make_scale_mat(mat_t mat, const struct scale_case *sc)
|
||||
{
|
||||
mat_t rot, scale;
|
||||
|
||||
MAT_IDN(scale);
|
||||
scale[0] = sc->sx;
|
||||
scale[5] = sc->sy;
|
||||
scale[10] = sc->sz;
|
||||
|
||||
bn_mat_angles(rot, sc->rx, sc->ry, sc->rz);
|
||||
bn_mat_mul(mat, rot, scale);
|
||||
mat[3] = 12.0;
|
||||
mat[7] = -5.0;
|
||||
mat[11] = 3.0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
make_views(struct view_case views[4])
|
||||
{
|
||||
vect_t up;
|
||||
|
||||
views[0].name = "x";
|
||||
VSET(views[0].dir, -1.0, 0.0, 0.0);
|
||||
VSET(views[0].u, 0.0, 1.0, 0.0);
|
||||
VSET(views[0].v, 0.0, 0.0, 1.0);
|
||||
|
||||
views[1].name = "y";
|
||||
VSET(views[1].dir, 0.0, -1.0, 0.0);
|
||||
VSET(views[1].u, 1.0, 0.0, 0.0);
|
||||
VSET(views[1].v, 0.0, 0.0, 1.0);
|
||||
|
||||
views[2].name = "z";
|
||||
VSET(views[2].dir, 0.0, 0.0, -1.0);
|
||||
VSET(views[2].u, 1.0, 0.0, 0.0);
|
||||
VSET(views[2].v, 0.0, 1.0, 0.0);
|
||||
|
||||
views[3].name = "obl";
|
||||
VSET(views[3].dir, -0.8, -0.55, -0.35);
|
||||
VUNITIZE(views[3].dir);
|
||||
VSET(up, 0.0, 0.0, 1.0);
|
||||
VCROSS(views[3].u, up, views[3].dir);
|
||||
VUNITIZE(views[3].u);
|
||||
VCROSS(views[3].v, views[3].dir, views[3].u);
|
||||
VUNITIZE(views[3].v);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
make_body_ray(struct xray *ray, const struct view_case *view, int ix, int iy, int n)
|
||||
{
|
||||
point_t center = {0.0, 0.0, 5.5};
|
||||
fastf_t span = 18.0;
|
||||
fastf_t x = 0.0;
|
||||
fastf_t y = 0.0;
|
||||
|
||||
memset(ray, 0, sizeof(struct xray));
|
||||
ray->magic = RT_RAY_MAGIC;
|
||||
|
||||
if (n > 0) {
|
||||
x = ((((fastf_t)ix + 0.5) / (fastf_t)n) * 2.0 - 1.0) * span;
|
||||
y = ((((fastf_t)iy + 0.5) / (fastf_t)n) * 2.0 - 1.0) * span;
|
||||
}
|
||||
|
||||
VJOIN3(ray->r_pt, center, -45.0, view->dir, x, view->u, y, view->v);
|
||||
VMOVE(ray->r_dir, view->dir);
|
||||
ray->r_min = 0.0;
|
||||
ray->r_max = INFINITY;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
transform_ray(struct xray *out, const struct xray *in, const mat_t mat)
|
||||
{
|
||||
vect_t dir;
|
||||
|
||||
memset(out, 0, sizeof(struct xray));
|
||||
out->magic = RT_RAY_MAGIC;
|
||||
MAT4X3PNT(out->r_pt, mat, in->r_pt);
|
||||
MAT4X3VEC(dir, mat, in->r_dir);
|
||||
VMOVE(out->r_dir, dir);
|
||||
VUNITIZE(out->r_dir);
|
||||
out->r_min = 0.0;
|
||||
out->r_max = INFINITY;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
transform_normal(vect_t out, const vect_t normal, const mat_t inv_mat)
|
||||
{
|
||||
VEC3X3MAT(out, normal, inv_mat);
|
||||
VUNITIZE(out);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
valid_vec(const vect_t v)
|
||||
{
|
||||
return isfinite(v[X]) && isfinite(v[Y]) && isfinite(v[Z]);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
write_pipe(struct rt_wdb *wdbp, const char *name)
|
||||
{
|
||||
struct bu_list head;
|
||||
point_t p0 = {0.0, 0.0, 0.0};
|
||||
point_t p1 = {0.0, 0.0, 16.0};
|
||||
int ret;
|
||||
|
||||
mk_pipe_init(&head);
|
||||
mk_add_pipe_pnt(&head, p0, 4.0, 0.0, 5.0);
|
||||
mk_add_pipe_pnt(&head, p1, 4.0, 0.0, 5.0);
|
||||
ret = mk_pipe(wdbp, name, &head);
|
||||
mk_pipe_free(&head);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
write_metaball(struct rt_wdb *wdbp, const char *name)
|
||||
{
|
||||
fastf_t mb0[5] = {-2.0, 0.0, 0.0, 3.0, 0.0};
|
||||
fastf_t mb1[5] = {2.0, 0.0, 0.0, 3.0, 0.0};
|
||||
fastf_t mb2[5] = {0.0, 2.0, 1.0, 2.5, 0.0};
|
||||
const fastf_t *verts[5] = {mb0, mb1, mb2, NULL, NULL};
|
||||
|
||||
return mk_metaball(wdbp, name, 3, METABALL_ISOPOTENTIAL, 1.0, verts);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
write_primitive(struct rt_wdb *wdbp, const char *type, const char *name)
|
||||
{
|
||||
point_t v = {0.0, 0.0, 0.0};
|
||||
vect_t h = {0.0, 0.0, 12.0};
|
||||
vect_t b = {0.0, 6.0, 0.0};
|
||||
vect_t a = {4.0, 0.0, 0.0};
|
||||
vect_t au = {1.0, 0.0, 0.0};
|
||||
vect_t n = {0.0, 0.0, 1.0};
|
||||
vect_t c = {3.0, 0.0, 0.0};
|
||||
|
||||
if (BU_STR_EQUAL(type, "tor"))
|
||||
return mk_tor(wdbp, name, v, n, 6.0, 1.5);
|
||||
if (BU_STR_EQUAL(type, "eto"))
|
||||
return mk_eto(wdbp, name, v, n, c, 6.0, 1.2);
|
||||
if (BU_STR_EQUAL(type, "epa"))
|
||||
return mk_epa(wdbp, name, v, h, au, 4.0, 2.5);
|
||||
if (BU_STR_EQUAL(type, "ehy"))
|
||||
return mk_ehy(wdbp, name, v, h, au, 4.0, 2.5, 16.0);
|
||||
if (BU_STR_EQUAL(type, "rpc"))
|
||||
return mk_rpc(wdbp, name, v, h, b, 2.5);
|
||||
if (BU_STR_EQUAL(type, "rhc"))
|
||||
return mk_rhc(wdbp, name, v, h, b, 2.5, 5.0);
|
||||
if (BU_STR_EQUAL(type, "particle"))
|
||||
return mk_particle(wdbp, name, v, h, 2.0, 3.0);
|
||||
if (BU_STR_EQUAL(type, "hyp"))
|
||||
return mk_hyp(wdbp, name, v, h, a, 2.5, 0.45);
|
||||
if (BU_STR_EQUAL(type, "cline"))
|
||||
return mk_cline(wdbp, name, v, h, 2.0, 0.5);
|
||||
if (BU_STR_EQUAL(type, "pipe"))
|
||||
return write_pipe(wdbp, name);
|
||||
if (BU_STR_EQUAL(type, "metaball"))
|
||||
return write_metaball(wdbp, name);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
set_nonuniform_attr(struct db_i *dbip, const char *name, const mat_t mat)
|
||||
{
|
||||
struct directory *dp = db_lookup(dbip, name, LOOKUP_QUIET);
|
||||
struct rt_db_internal intern;
|
||||
struct bu_vls value = BU_VLS_INIT_ZERO;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
if (!dp)
|
||||
return -1;
|
||||
|
||||
RT_DB_INTERNAL_INIT(&intern);
|
||||
if (rt_db_get_internal(&intern, dp, dbip, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (i)
|
||||
bu_vls_putc(&value, ',');
|
||||
bu_vls_printf(&value, "%.17g", mat[i]);
|
||||
}
|
||||
|
||||
if (bu_avs_add(&intern.idb_avs, RT_MATRIX_NONUNIFORM_ATTR, bu_vls_cstr(&value)) < 0)
|
||||
ret = -1;
|
||||
else if (rt_db_put_internal(dp, dbip, &intern) < 0)
|
||||
ret = -1;
|
||||
|
||||
bu_vls_free(&value);
|
||||
rt_db_free_internal(&intern);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
sample_hit(struct application *ap, struct partition *PartHeadp, struct seg *UNUSED(segp))
|
||||
{
|
||||
struct ray_sample *sample = (struct ray_sample *)ap->a_uptr;
|
||||
struct partition *pp = PartHeadp->pt_forw;
|
||||
|
||||
if (pp == PartHeadp)
|
||||
return 0;
|
||||
|
||||
sample->hit = 1;
|
||||
VJOIN1(sample->in_pt, ap->a_ray.r_pt, pp->pt_inhit->hit_dist, ap->a_ray.r_dir);
|
||||
VJOIN1(sample->out_pt, ap->a_ray.r_pt, pp->pt_outhit->hit_dist, ap->a_ray.r_dir);
|
||||
RT_HIT_NORMAL(sample->in_norm, pp->pt_inhit, pp->pt_inseg->seg_stp, NULL, pp->pt_inflip);
|
||||
RT_HIT_NORMAL(sample->out_norm, pp->pt_outhit, pp->pt_outseg->seg_stp, NULL, pp->pt_outflip);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
sample_miss(struct application *UNUSED(ap))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
shoot_sample(struct rt_i *rtip, const struct xray *ray, struct ray_sample *sample)
|
||||
{
|
||||
struct application ap;
|
||||
|
||||
memset(sample, 0, sizeof(struct ray_sample));
|
||||
RT_APPLICATION_INIT(&ap);
|
||||
ap.a_rt_i = rtip;
|
||||
ap.a_onehit = 1;
|
||||
ap.a_uptr = sample;
|
||||
ap.a_hit = sample_hit;
|
||||
ap.a_miss = sample_miss;
|
||||
ap.a_ray = *ray;
|
||||
|
||||
rt_shootray(&ap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct rt_i *
|
||||
prep_object(struct db_i *dbip, const char *name)
|
||||
{
|
||||
struct rt_i *rtip = rt_i_create(dbip);
|
||||
|
||||
if (!rtip)
|
||||
return RTI_NULL;
|
||||
|
||||
if (rt_gettree(rtip, name) != 0) {
|
||||
rt_i_destroy(rtip);
|
||||
return RTI_NULL;
|
||||
}
|
||||
|
||||
rt_prep_parallel(rtip, 1);
|
||||
return rtip;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
compare_hit_point(const point_t expected, const point_t got)
|
||||
{
|
||||
return DIST_PNT_PNT(expected, got) <= POINT_TOL;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
compare_normal(const vect_t expected, const vect_t got)
|
||||
{
|
||||
if (!valid_vec(expected) || !valid_vec(got))
|
||||
return 0;
|
||||
|
||||
return VDOT(expected, got) >= NORMAL_DOT_TOL;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
compare_sparse_sample(const char *label, const mat_t mat, const mat_t inv_mat, const struct ray_sample *base, const struct ray_sample *scaled)
|
||||
{
|
||||
point_t expected_in, expected_out;
|
||||
vect_t expected_in_norm, expected_out_norm;
|
||||
|
||||
if (base->hit != scaled->hit) {
|
||||
bu_log("[nonuniform_ray] %s hit mismatch: base=%d scaled=%d\n", label, base->hit, scaled->hit);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!base->hit)
|
||||
return 0;
|
||||
|
||||
MAT4X3PNT(expected_in, mat, base->in_pt);
|
||||
MAT4X3PNT(expected_out, mat, base->out_pt);
|
||||
transform_normal(expected_in_norm, base->in_norm, inv_mat);
|
||||
transform_normal(expected_out_norm, base->out_norm, inv_mat);
|
||||
|
||||
if (!compare_hit_point(expected_in, scaled->in_pt) ||
|
||||
!compare_hit_point(expected_out, scaled->out_pt)) {
|
||||
if (compare_hit_point(expected_out, scaled->out_pt) &&
|
||||
compare_normal(expected_out_norm, scaled->out_norm))
|
||||
return 0;
|
||||
|
||||
bu_log("[nonuniform_ray] %s hit point mismatch\n", label);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!compare_normal(expected_in_norm, scaled->in_norm) ||
|
||||
!compare_normal(expected_out_norm, scaled->out_norm)) {
|
||||
bu_log("[nonuniform_ray] %s normal mismatch\n", label);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
run_grid_compare(struct rt_i *base_rtip, struct rt_i *scaled_rtip, const struct view_case *view, const mat_t mat, const mat_t inv_mat, int n, int sparse, int mask_tolerance, const char *label)
|
||||
{
|
||||
int failures = 0;
|
||||
int base_hits = 0;
|
||||
int scaled_hits = 0;
|
||||
int mask_mismatches = 0;
|
||||
int ix, iy;
|
||||
|
||||
for (iy = 0; iy < n; iy++) {
|
||||
for (ix = 0; ix < n; ix++) {
|
||||
struct xray base_ray, scaled_ray;
|
||||
struct ray_sample base_sample, scaled_sample;
|
||||
|
||||
make_body_ray(&base_ray, view, ix, iy, n);
|
||||
transform_ray(&scaled_ray, &base_ray, mat);
|
||||
|
||||
shoot_sample(base_rtip, &base_ray, &base_sample);
|
||||
shoot_sample(scaled_rtip, &scaled_ray, &scaled_sample);
|
||||
|
||||
if (base_sample.hit)
|
||||
base_hits++;
|
||||
if (scaled_sample.hit)
|
||||
scaled_hits++;
|
||||
|
||||
if (base_sample.hit != scaled_sample.hit) {
|
||||
if (sparse)
|
||||
failures++;
|
||||
else
|
||||
mask_mismatches++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sparse && compare_sparse_sample(label, mat, inv_mat, &base_sample, &scaled_sample) < 0)
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sparse && mask_mismatches > mask_tolerance) {
|
||||
bu_log("[nonuniform_ray] %s silhouette mismatch count exceeded tolerance: mismatches=%d tolerance=%d\n", label, mask_mismatches, mask_tolerance);
|
||||
failures++;
|
||||
}
|
||||
|
||||
if (base_hits != scaled_hits && (sparse || abs(base_hits - scaled_hits) > mask_tolerance)) {
|
||||
bu_log("[nonuniform_ray] %s silhouette count mismatch: base=%d scaled=%d\n", label, base_hits, scaled_hits);
|
||||
failures++;
|
||||
}
|
||||
|
||||
return failures ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
run_case(struct db_i *dbip, const struct prim_case *pc, const struct scale_case *sc, const struct view_case *view)
|
||||
{
|
||||
char base_name[64];
|
||||
char scaled_name[64];
|
||||
char label[128];
|
||||
mat_t mat, inv_mat;
|
||||
struct rt_i *base_rtip;
|
||||
struct rt_i *scaled_rtip;
|
||||
int ret = 0;
|
||||
|
||||
snprintf(base_name, sizeof(base_name), "%s_base.s", pc->name);
|
||||
snprintf(scaled_name, sizeof(scaled_name), "%s_%s.s", pc->name, sc->name);
|
||||
snprintf(label, sizeof(label), "%s/%s/%s", pc->name, sc->name, view->name);
|
||||
|
||||
make_scale_mat(mat, sc);
|
||||
if (!bn_mat_inverse(inv_mat, mat)) {
|
||||
bu_log("[nonuniform_ray] %s scale matrix is not invertible\n", label);
|
||||
return -1;
|
||||
}
|
||||
|
||||
base_rtip = prep_object(dbip, base_name);
|
||||
scaled_rtip = prep_object(dbip, scaled_name);
|
||||
if (!base_rtip || !scaled_rtip) {
|
||||
bu_log("[nonuniform_ray] %s prep failed\n", label);
|
||||
if (base_rtip)
|
||||
rt_i_destroy(base_rtip);
|
||||
if (scaled_rtip)
|
||||
rt_i_destroy(scaled_rtip);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (run_grid_compare(base_rtip, scaled_rtip, view, mat, inv_mat, SPARSE_GRID, 1, 0, label) < 0)
|
||||
ret = -1;
|
||||
|
||||
if (run_grid_compare(base_rtip, scaled_rtip, view, mat, inv_mat, MASK_GRID, 0, pc->mask_tolerance, label) < 0)
|
||||
ret = -1;
|
||||
|
||||
rt_i_destroy(base_rtip);
|
||||
rt_i_destroy(scaled_rtip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
build_db(struct db_i **dbip_out, struct rt_wdb **wdbp_out)
|
||||
{
|
||||
struct db_i *dbip = db_open_inmem();
|
||||
struct rt_wdb *wdbp;
|
||||
size_t i, j;
|
||||
|
||||
if (!dbip)
|
||||
return -1;
|
||||
|
||||
wdbp = wdb_dbopen(dbip, RT_WDB_TYPE_DB_INMEM);
|
||||
if (!wdbp) {
|
||||
db_close(dbip);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(prims) / sizeof(prims[0]); i++) {
|
||||
char base_name[64];
|
||||
|
||||
snprintf(base_name, sizeof(base_name), "%s_base.s", prims[i].name);
|
||||
if (write_primitive(wdbp, prims[i].name, base_name) < 0) {
|
||||
wdb_close(wdbp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (j = 0; j < sizeof(scales) / sizeof(scales[0]); j++) {
|
||||
char scaled_name[64];
|
||||
mat_t mat;
|
||||
|
||||
snprintf(scaled_name, sizeof(scaled_name), "%s_%s.s", prims[i].name, scales[j].name);
|
||||
make_scale_mat(mat, &scales[j]);
|
||||
|
||||
if (write_primitive(wdbp, prims[i].name, scaled_name) < 0 ||
|
||||
set_nonuniform_attr(dbip, scaled_name, mat) < 0) {
|
||||
wdb_close(wdbp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
db_update_nref(dbip);
|
||||
*dbip_out = dbip;
|
||||
*wdbp_out = wdbp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct db_i *dbip = DBI_NULL;
|
||||
struct rt_wdb *wdbp = RT_WDB_NULL;
|
||||
struct view_case views[4];
|
||||
int failures = 0;
|
||||
size_t i, j, k;
|
||||
|
||||
bu_setprogname(argv[0]);
|
||||
(void)argc;
|
||||
|
||||
make_views(views);
|
||||
|
||||
if (build_db(&dbip, &wdbp) < 0)
|
||||
bu_exit(1, "[nonuniform_ray] failed to build test database\n");
|
||||
|
||||
for (i = 0; i < sizeof(prims) / sizeof(prims[0]); i++) {
|
||||
for (j = 0; j < sizeof(scales) / sizeof(scales[0]); j++) {
|
||||
for (k = 0; k < sizeof(views) / sizeof(views[0]); k++) {
|
||||
if (run_case(dbip, &prims[i], &scales[j], &views[k]) < 0)
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wdb_close(wdbp);
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* tab-width: 8
|
||||
* mode: C
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "stroustrup"
|
||||
* End:
|
||||
* ex: shiftwidth=4 tabstop=8
|
||||
*/
|
||||
@@ -0,0 +1,313 @@
|
||||
/* P I P E _ S H O T L I N E . C
|
||||
* BRL-CAD
|
||||
*
|
||||
* Copyright (c) 2026 United States Government as represented by
|
||||
* the U.S. Army Research Laboratory.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* version 2.1 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this file; see the file named COPYING for more
|
||||
* information.
|
||||
*/
|
||||
/** @file librt/tests/pipe_shotline.c
|
||||
*
|
||||
* Regression tests for pipe shotline invariance. Moving a ray start
|
||||
* along the same pre-entry line must not change the physical pipe
|
||||
* intervals reported by ray tracing.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bu/app.h"
|
||||
#include "bu/log.h"
|
||||
#include "bu/str.h"
|
||||
#include "raytrace.h"
|
||||
#include "wdb.h"
|
||||
|
||||
|
||||
#define MAX_INTERVALS 8
|
||||
#define POINT_TOL 1.0e-5
|
||||
|
||||
|
||||
struct interval_set {
|
||||
int count;
|
||||
point_t in[MAX_INTERVALS];
|
||||
point_t out[MAX_INTERVALS];
|
||||
};
|
||||
|
||||
|
||||
struct fixture_case {
|
||||
const char *name;
|
||||
point_t ray_pt;
|
||||
vect_t ray_dir;
|
||||
};
|
||||
|
||||
|
||||
static int
|
||||
collect_hit(struct application *ap, struct partition *PartHeadp, struct seg *UNUSED(segp))
|
||||
{
|
||||
struct interval_set *iset = (struct interval_set *)ap->a_uptr;
|
||||
struct partition *pp;
|
||||
|
||||
for (pp = PartHeadp->pt_forw; pp != PartHeadp; pp = pp->pt_forw) {
|
||||
if (iset->count >= MAX_INTERVALS) {
|
||||
bu_log("[pipe_shotline] too many intervals\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
VJOIN1(iset->in[iset->count], ap->a_ray.r_pt, pp->pt_inhit->hit_dist, ap->a_ray.r_dir);
|
||||
VJOIN1(iset->out[iset->count], ap->a_ray.r_pt, pp->pt_outhit->hit_dist, ap->a_ray.r_dir);
|
||||
iset->count++;
|
||||
}
|
||||
|
||||
return iset->count > 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
collect_miss(struct application *UNUSED(ap))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
make_ray(struct xray *ray, const point_t pt, const vect_t dir)
|
||||
{
|
||||
memset(ray, 0, sizeof(struct xray));
|
||||
ray->magic = RT_RAY_MAGIC;
|
||||
VMOVE(ray->r_pt, pt);
|
||||
VMOVE(ray->r_dir, dir);
|
||||
VUNITIZE(ray->r_dir);
|
||||
ray->r_min = 0.0;
|
||||
ray->r_max = INFINITY;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
shoot_intervals(struct rt_i *rtip, const struct xray *ray, struct interval_set *iset)
|
||||
{
|
||||
struct application ap;
|
||||
|
||||
memset(iset, 0, sizeof(struct interval_set));
|
||||
RT_APPLICATION_INIT(&ap);
|
||||
ap.a_rt_i = rtip;
|
||||
ap.a_hit = collect_hit;
|
||||
ap.a_miss = collect_miss;
|
||||
ap.a_uptr = iset;
|
||||
ap.a_ray = *ray;
|
||||
|
||||
return rt_shootray(&ap);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
compare_intervals(const char *label, const struct interval_set *expected, const struct interval_set *got)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (expected->count != got->count) {
|
||||
bu_log("[pipe_shotline] %s interval count mismatch: expected=%d got=%d\n",
|
||||
label, expected->count, got->count);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < expected->count; i++) {
|
||||
if (DIST_PNT_PNT(expected->in[i], got->in[i]) > POINT_TOL ||
|
||||
DIST_PNT_PNT(expected->out[i], got->out[i]) > POINT_TOL) {
|
||||
bu_log("[pipe_shotline] %s interval %d endpoint mismatch\n", label, i);
|
||||
bu_log(" expected in=(%g %g %g) out=(%g %g %g)\n",
|
||||
V3ARGS(expected->in[i]), V3ARGS(expected->out[i]));
|
||||
bu_log(" got in=(%g %g %g) out=(%g %g %g)\n",
|
||||
V3ARGS(got->in[i]), V3ARGS(got->out[i]));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
add_pipe(struct rt_wdb *wdbp, const char *name, int fixture)
|
||||
{
|
||||
struct bu_list head;
|
||||
int ret;
|
||||
|
||||
mk_pipe_init(&head);
|
||||
|
||||
if (fixture == 0) {
|
||||
point_t p0 = {0.0, 0.0, 0.0};
|
||||
point_t p1 = {0.0, 0.0, 16.0};
|
||||
mk_add_pipe_pnt(&head, p0, 4.0, 0.0, 5.0);
|
||||
mk_add_pipe_pnt(&head, p1, 4.0, 0.0, 5.0);
|
||||
} else if (fixture == 1) {
|
||||
point_t p0 = {0.0, 0.0, 0.0};
|
||||
point_t p1 = {0.0, 0.0, 16.0};
|
||||
mk_add_pipe_pnt(&head, p0, 6.0, 2.0, 5.0);
|
||||
mk_add_pipe_pnt(&head, p1, 6.0, 2.0, 5.0);
|
||||
} else if (fixture == 2) {
|
||||
point_t p0 = {0.0, 0.0, 0.0};
|
||||
point_t p1 = {0.0, 0.0, 16.0};
|
||||
mk_add_pipe_pnt(&head, p0, 6.0, 0.0, 5.0);
|
||||
mk_add_pipe_pnt(&head, p1, 3.0, 0.0, 5.0);
|
||||
} else {
|
||||
point_t p0 = {-10.0, 0.0, 0.0};
|
||||
point_t p1 = {0.0, 0.0, 0.0};
|
||||
point_t p2 = {0.0, 10.0, 0.0};
|
||||
mk_add_pipe_pnt(&head, p0, 3.0, 0.0, 4.0);
|
||||
mk_add_pipe_pnt(&head, p1, 3.0, 0.0, 4.0);
|
||||
mk_add_pipe_pnt(&head, p2, 3.0, 0.0, 4.0);
|
||||
}
|
||||
|
||||
ret = mk_pipe(wdbp, name, &head);
|
||||
mk_pipe_free(&head);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static struct rt_i *
|
||||
prep_object(struct db_i *dbip, const char *name)
|
||||
{
|
||||
struct rt_i *rtip = rt_i_create(dbip);
|
||||
|
||||
if (!rtip)
|
||||
return RTI_NULL;
|
||||
|
||||
if (rt_gettree(rtip, name) != 0) {
|
||||
rt_i_destroy(rtip);
|
||||
return RTI_NULL;
|
||||
}
|
||||
|
||||
rt_prep_parallel(rtip, 1);
|
||||
return rtip;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
run_origin_invariance(struct db_i *dbip, const struct fixture_case *fc)
|
||||
{
|
||||
static const fastf_t shifts[] = {8.0, 16.0, 24.0};
|
||||
struct rt_i *rtip;
|
||||
struct xray base_ray;
|
||||
struct interval_set expected;
|
||||
int failures = 0;
|
||||
size_t i;
|
||||
|
||||
rtip = prep_object(dbip, fc->name);
|
||||
if (!rtip) {
|
||||
bu_log("[pipe_shotline] failed to prep %s\n", fc->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
make_ray(&base_ray, fc->ray_pt, fc->ray_dir);
|
||||
shoot_intervals(rtip, &base_ray, &expected);
|
||||
if (expected.count == 0) {
|
||||
bu_log("[pipe_shotline] %s base ray missed\n", fc->name);
|
||||
rt_i_destroy(rtip);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(shifts) / sizeof(shifts[0]); i++) {
|
||||
struct xray shifted_ray;
|
||||
struct interval_set got;
|
||||
point_t shifted_pt;
|
||||
char label[128];
|
||||
|
||||
VJOIN1(shifted_pt, fc->ray_pt, -shifts[i], fc->ray_dir);
|
||||
make_ray(&shifted_ray, shifted_pt, fc->ray_dir);
|
||||
shoot_intervals(rtip, &shifted_ray, &got);
|
||||
|
||||
snprintf(label, sizeof(label), "%s/shift_%g", fc->name, shifts[i]);
|
||||
if (compare_intervals(label, &expected, &got) < 0)
|
||||
failures++;
|
||||
}
|
||||
|
||||
rt_i_destroy(rtip);
|
||||
return failures ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
build_db(struct db_i **dbip_out, struct rt_wdb **wdbp_out)
|
||||
{
|
||||
struct db_i *dbip = db_open_inmem();
|
||||
struct rt_wdb *wdbp;
|
||||
int i;
|
||||
|
||||
if (!dbip)
|
||||
return -1;
|
||||
|
||||
wdbp = wdb_dbopen(dbip, RT_WDB_TYPE_DB_INMEM);
|
||||
if (!wdbp) {
|
||||
db_close(dbip);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
char name[32];
|
||||
|
||||
snprintf(name, sizeof(name), "pipe_%d.s", i);
|
||||
if (add_pipe(wdbp, name, i) < 0) {
|
||||
wdb_close(wdbp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
db_update_nref(dbip);
|
||||
*dbip_out = dbip;
|
||||
*wdbp_out = wdbp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
static const struct fixture_case fixtures[] = {
|
||||
{"pipe_0.s", {30.0, 0.0, 8.0}, {-1.0, 0.0, 0.0}},
|
||||
{"pipe_1.s", {30.0, 0.0, 8.0}, {-1.0, 0.0, 0.0}},
|
||||
{"pipe_2.s", {30.0, 0.0, 8.0}, {-1.0, 0.0, 0.0}},
|
||||
{"pipe_3.s", {-5.0, 0.0, 20.0}, {0.0, 0.0, -1.0}}
|
||||
};
|
||||
struct db_i *dbip = DBI_NULL;
|
||||
struct rt_wdb *wdbp = RT_WDB_NULL;
|
||||
int failures = 0;
|
||||
size_t i;
|
||||
|
||||
bu_setprogname(argv[0]);
|
||||
(void)argc;
|
||||
|
||||
if (build_db(&dbip, &wdbp) < 0)
|
||||
bu_exit(1, "[pipe_shotline] failed to build test database\n");
|
||||
|
||||
for (i = 0; i < sizeof(fixtures) / sizeof(fixtures[0]); i++) {
|
||||
if (run_origin_invariance(dbip, &fixtures[i]) < 0)
|
||||
failures++;
|
||||
}
|
||||
|
||||
wdb_close(wdbp);
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* tab-width: 8
|
||||
* mode: C
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "stroustrup"
|
||||
* End:
|
||||
* ex: shiftwidth=4 tabstop=8
|
||||
*/
|
||||
+67
-22
@@ -139,6 +139,21 @@ static double g_scan_rel = 0.0;
|
||||
static double g_scan_abs = 0.0;
|
||||
static double g_scan_norm = 0.0;
|
||||
|
||||
static int
|
||||
prim_tess_metric_failed(fastf_t value)
|
||||
{
|
||||
return !isfinite(value) || NEAR_EQUAL(value, -1.0, SMALL_FASTF);
|
||||
}
|
||||
|
||||
|
||||
static double
|
||||
prim_tess_metric_relative_error(double measured, double reference)
|
||||
{
|
||||
if (fabs(reference) <= SMALL_FASTF)
|
||||
return (fabs(measured) <= SMALL_FASTF) ? 0.0 : 1.0;
|
||||
return fabs(measured - reference) / fabs(reference);
|
||||
}
|
||||
|
||||
/* Optional primitive filter: when non-NULL, only the named prim family runs */
|
||||
static const char *g_prim_filter = NULL;
|
||||
|
||||
@@ -496,6 +511,9 @@ check_nmg_mesh(const char *label, struct model *m,
|
||||
double err_v = -1.0;
|
||||
int res_limited = 0;
|
||||
int metrics_ok = -1; /* -1 = not checked */
|
||||
int have_analytic_sa = 0;
|
||||
int have_analytic_v = 0;
|
||||
int analytic_query_failed = 0;
|
||||
|
||||
if (passed && ip &&
|
||||
ip->idb_minor_type >= 0 && ip->idb_minor_type < ID_MAXIMUM) {
|
||||
@@ -503,16 +521,35 @@ check_nmg_mesh(const char *label, struct model *m,
|
||||
const struct rt_functab *ft = &OBJ[ip->idb_minor_type];
|
||||
|
||||
if (!BU_SETJUMP) {
|
||||
if (ft->ft_surf_area)
|
||||
if (ft->ft_surf_area) {
|
||||
ft->ft_surf_area(&analytic_sa, ip);
|
||||
if (ft->ft_volume)
|
||||
if (prim_tess_metric_failed(analytic_sa)) {
|
||||
analytic_query_failed = 1;
|
||||
if (!quiet)
|
||||
fprintf(stderr, " METRICS: %-44s surface-area query failed\n", label);
|
||||
} else {
|
||||
have_analytic_sa = 1;
|
||||
}
|
||||
}
|
||||
if (ft->ft_volume) {
|
||||
ft->ft_volume(&analytic_v, ip);
|
||||
if (prim_tess_metric_failed(analytic_v)) {
|
||||
analytic_query_failed = 1;
|
||||
if (!quiet)
|
||||
fprintf(stderr, " METRICS: %-44s volume query failed\n", label);
|
||||
} else {
|
||||
have_analytic_v = 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
BU_UNSETJUMP;
|
||||
analytic_sa = -1.0;
|
||||
analytic_v = -1.0;
|
||||
have_analytic_sa = 0;
|
||||
have_analytic_v = 0;
|
||||
analytic_query_failed = 1;
|
||||
if (!quiet)
|
||||
fprintf(stderr, " METRICS: %-44s analytic query bombed, using NA/fallback\n", label);
|
||||
fprintf(stderr, " METRICS: %-44s analytic query bombed\n", label);
|
||||
} BU_UNSETJUMP;
|
||||
|
||||
/* crofton_from_ip() requires ip->idb_meth to export the primitive to
|
||||
@@ -520,36 +557,42 @@ check_nmg_mesh(const char *label, struct model *m,
|
||||
* uninitialised (garbage, not NULL), so always set it from the OBJ
|
||||
* function table when the minor type is known. For IPs returned by
|
||||
* rt_db_get_internal the assignment is a no-op (same pointer). */
|
||||
if (g_validate_metrics && !quiet && (analytic_sa <= 0.0 || analytic_v <= 0.0)) {
|
||||
if (g_validate_metrics && !quiet && (!ft->ft_surf_area || !ft->ft_volume)) {
|
||||
struct rt_db_internal ip_meth = *ip;
|
||||
if (ip_meth.idb_minor_type >= 0 && ip_meth.idb_minor_type < (int)ID_MAXIMUM)
|
||||
ip_meth.idb_meth = &OBJ[ip_meth.idb_minor_type];
|
||||
if (analytic_sa <= 0.0) {
|
||||
fastf_t croft_sa = 0.0;
|
||||
if (!ft->ft_surf_area) {
|
||||
fastf_t croft_sa = -1.0;
|
||||
if (!BU_SETJUMP) {
|
||||
rt_crofton_sample(&croft_sa, NULL, &ip_meth, NULL);
|
||||
} else {
|
||||
BU_UNSETJUMP;
|
||||
croft_sa = 0.0;
|
||||
croft_sa = -1.0;
|
||||
} BU_UNSETJUMP;
|
||||
if (croft_sa > 0.0) analytic_sa = croft_sa;
|
||||
if (!prim_tess_metric_failed(croft_sa)) {
|
||||
analytic_sa = croft_sa;
|
||||
have_analytic_sa = 1;
|
||||
}
|
||||
}
|
||||
if (analytic_v <= 0.0) {
|
||||
fastf_t croft_v = 0.0;
|
||||
if (!ft->ft_volume) {
|
||||
fastf_t croft_v = -1.0;
|
||||
if (!BU_SETJUMP) {
|
||||
rt_crofton_sample(NULL, &croft_v, &ip_meth, NULL);
|
||||
} else {
|
||||
BU_UNSETJUMP;
|
||||
croft_v = 0.0;
|
||||
croft_v = -1.0;
|
||||
} BU_UNSETJUMP;
|
||||
if (croft_v > 0.0) analytic_v = croft_v;
|
||||
if (!prim_tess_metric_failed(croft_v)) {
|
||||
analytic_v = croft_v;
|
||||
have_analytic_v = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (analytic_sa > 0.0)
|
||||
err_sa = fabs((double)(area - analytic_sa)) / (double)analytic_sa;
|
||||
if (analytic_v > 0.0)
|
||||
err_v = fabs((double)(vol - analytic_v)) / (double)analytic_v;
|
||||
if (have_analytic_sa)
|
||||
err_sa = prim_tess_metric_relative_error((double)area, (double)analytic_sa);
|
||||
if (have_analytic_v)
|
||||
err_v = prim_tess_metric_relative_error((double)vol, (double)analytic_v);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
@@ -558,12 +601,14 @@ check_nmg_mesh(const char *label, struct model *m,
|
||||
if (g_validate_metrics && passed && ip &&
|
||||
ip->idb_minor_type >= 0 && ip->idb_minor_type < ID_MAXIMUM) {
|
||||
|
||||
int enforce_metrics = (analytic_sa > 0.0 && analytic_v > 0.0);
|
||||
int enforce_metrics = have_analytic_sa && have_analytic_v;
|
||||
int fail_sa = 0;
|
||||
int fail_v = 0;
|
||||
metrics_ok = enforce_metrics ? 1 : -1;
|
||||
metrics_ok = analytic_query_failed ? 0 : (enforce_metrics ? 1 : -1);
|
||||
if (analytic_query_failed)
|
||||
passed = 0;
|
||||
|
||||
if (analytic_sa > 0.0) {
|
||||
if (have_analytic_sa) {
|
||||
const char *tag = (err_sa <= g_metrics_tol) ? "SA-OK" : (enforce_metrics ? "SA-FAIL" : "SA-INFO");
|
||||
if (!quiet)
|
||||
fprintf(stderr,
|
||||
@@ -575,7 +620,7 @@ check_nmg_mesh(const char *label, struct model *m,
|
||||
fprintf(stderr, " METRICS: %-44s SA-analytic=NA\n", label);
|
||||
}
|
||||
|
||||
if (analytic_v > 0.0) {
|
||||
if (have_analytic_v) {
|
||||
const char *tag_v;
|
||||
|
||||
if (err_v <= g_metrics_tol) {
|
||||
@@ -698,8 +743,8 @@ check_nmg_mesh(const char *label, struct model *m,
|
||||
res->n_tris = (long)bot->num_faces;
|
||||
res->area = (double)area;
|
||||
res->vol = (double)(open_cnt == 0 ? vol : 0.0);
|
||||
res->err_sa = (analytic_sa > 0.0) ? err_sa : -1.0;
|
||||
res->err_vol = (analytic_v > 0.0) ? err_v : -1.0;
|
||||
res->err_sa = have_analytic_sa ? err_sa : -1.0;
|
||||
res->err_vol = have_analytic_v ? err_v : -1.0;
|
||||
res->metrics_ok = metrics_ok;
|
||||
res->res_limited = res_limited;
|
||||
}
|
||||
|
||||
@@ -433,6 +433,14 @@ _rt_find_identical_solid(const matp_t mat, struct directory *dp, struct rt_i *rt
|
||||
} else {
|
||||
stp->st_matp = (matp_t)0;
|
||||
}
|
||||
stp->st_nu_matp = (matp_t)0;
|
||||
stp->st_nu_inv_matp = (matp_t)0;
|
||||
stp->st_nu_norm_matp = (matp_t)0;
|
||||
VSETALL(stp->st_nu_body_center, 0.0);
|
||||
VSETALL(stp->st_nu_body_min, 0.0);
|
||||
VSETALL(stp->st_nu_body_max, 0.0);
|
||||
stp->st_nu_body_aradius = 0.0;
|
||||
stp->st_nu_body_bradius = 0.0;
|
||||
|
||||
/* Add to the appropriate soltab list head */
|
||||
/* PARALLEL NOTE: Uses critical section on rt_solidheads element */
|
||||
@@ -666,6 +674,7 @@ rt_free_soltab(struct soltab *stp)
|
||||
}
|
||||
if (stp->st_matp) bu_free((char *)stp->st_matp, "st_matp");
|
||||
stp->st_matp = (matp_t)0; /* Sanity */
|
||||
_rt_nonuniform_soltab_free(stp);
|
||||
|
||||
bu_ptbl_free(&stp->st_regions);
|
||||
|
||||
|
||||
+4
-1
@@ -306,6 +306,7 @@ rt_vshootray(struct application *ap)
|
||||
/* For each solid type present, batch-shoot all instances. */
|
||||
for (id = 1; id <= ID_MAX_SOLID; id++) {
|
||||
int nsol = (int)rtip->i->rti_nsol_by_type[id];
|
||||
int use_stub = 0;
|
||||
if (nsol <= 0)
|
||||
continue;
|
||||
if (!OBJ[id].ft_vshot && !OBJ[id].ft_shot)
|
||||
@@ -314,6 +315,8 @@ rt_vshootray(struct application *ap)
|
||||
for (i = 0; i < nsol; i++) {
|
||||
struct soltab *stp = rtip->i->rti_sol_by_type[id][i];
|
||||
ary_stp[i] = stp;
|
||||
if (stp->st_nu_inv_matp)
|
||||
use_stub = 1;
|
||||
ary_rp[i] = &ap->a_ray;
|
||||
ary_seg[i].seg_stp = SOLTAB_NULL;
|
||||
BU_BITSET(solidbits, stp->st_bit); /* mark as shot */
|
||||
@@ -321,7 +324,7 @@ rt_vshootray(struct application *ap)
|
||||
|
||||
resp->re_shots += nsol;
|
||||
|
||||
if (OBJ[id].ft_vshot && !vshoot_force_scalar()) {
|
||||
if (!use_stub && OBJ[id].ft_vshot && !vshoot_force_scalar()) {
|
||||
OBJ[id].ft_vshot(ary_stp, ary_rp, ary_seg, nsol, ap);
|
||||
} else {
|
||||
vshot_stub(ary_stp, ary_rp, ary_seg, nsol, ap);
|
||||
|
||||
+80
-3
@@ -56,6 +56,9 @@ int be_s_illuminate(ClientData, Tcl_Interp *, int, char **);
|
||||
int be_s_rotate(ClientData, Tcl_Interp *, int, char **);
|
||||
int be_s_scale(ClientData, Tcl_Interp *, int, char **);
|
||||
int be_s_trans(ClientData, Tcl_Interp *, int, char **);
|
||||
int be_s_xscale(ClientData, Tcl_Interp *, int, char **);
|
||||
int be_s_yscale(ClientData, Tcl_Interp *, int, char **);
|
||||
int be_s_zscale(ClientData, Tcl_Interp *, int, char **);
|
||||
int bv_35_25(ClientData, Tcl_Interp *, int, char **);
|
||||
int bv_45_45(ClientData, Tcl_Interp *, int, char **);
|
||||
int bv_adcursor(ClientData, Tcl_Interp *, int, char **);
|
||||
@@ -127,6 +130,9 @@ struct buttons {
|
||||
{BE_S_ROTATE, "srot", be_s_rotate},
|
||||
{BE_S_SCALE, "sscale", be_s_scale},
|
||||
{BE_S_TRANS, "sxy", be_s_trans},
|
||||
{BE_S_XSCALE, "sxscale", be_s_xscale},
|
||||
{BE_S_YSCALE, "syscale", be_s_yscale},
|
||||
{BE_S_ZSCALE, "szscale", be_s_zscale},
|
||||
{BV_TOP, "top", bv_top},
|
||||
{BV_ZOOM_IN, "zoomin", bv_zoomin},
|
||||
{BV_ZOOM_OUT, "zoomout", bv_zoomout},
|
||||
@@ -755,10 +761,10 @@ be_accept(ClientData clientData, Tcl_Interp *UNUSED(interp), int UNUSED(argc), c
|
||||
|
||||
if (s->global_editing_state == ST_S_EDIT) {
|
||||
/* Accept a solid edit */
|
||||
if (sedit_accept(s) != TCL_OK)
|
||||
return TCL_ERROR;
|
||||
edsol = 0;
|
||||
|
||||
sedit_accept(s); /* zeros "edsol" var */
|
||||
|
||||
mmenu_set_all(s, MENU_L1, NULL);
|
||||
mmenu_set_all(s, MENU_L2, NULL);
|
||||
|
||||
@@ -952,12 +958,62 @@ be_s_scale(ClientData clientData, Tcl_Interp *UNUSED(interp), int UNUSED(argc),
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
be_s_axis_scale(struct mged_state *s, int button_code, int edit_flag, int axis, const char *description)
|
||||
{
|
||||
if (not_state(s, ST_S_EDIT, description))
|
||||
return TCL_ERROR;
|
||||
|
||||
edsol = button_code;
|
||||
movedir = SARROW;
|
||||
rt_edit_set_edflag(MEDIT(s), edit_flag);
|
||||
mmenu_set(s, MENU_L1, NULL);
|
||||
|
||||
MEDIT(s)->k.sca_abs = MEDIT(s)->acc_sc[axis] - 1.0;
|
||||
if (MEDIT(s)->k.sca_abs > 0.0)
|
||||
MEDIT(s)->k.sca_abs /= 3.0;
|
||||
|
||||
set_e_axes_pos(s, 1);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
be_s_xscale(ClientData clientData, Tcl_Interp *UNUSED(interp), int UNUSED(argc), char *UNUSED(argv[]))
|
||||
{
|
||||
struct cmdtab *ctp = (struct cmdtab *)clientData;
|
||||
MGED_CK_CMD(ctp);
|
||||
return be_s_axis_scale(ctp->s, BE_S_XSCALE, RT_MATRIX_EDIT_SCALE_X, X,
|
||||
"Primitive Local X Scale");
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
be_s_yscale(ClientData clientData, Tcl_Interp *UNUSED(interp), int UNUSED(argc), char *UNUSED(argv[]))
|
||||
{
|
||||
struct cmdtab *ctp = (struct cmdtab *)clientData;
|
||||
MGED_CK_CMD(ctp);
|
||||
return be_s_axis_scale(ctp->s, BE_S_YSCALE, RT_MATRIX_EDIT_SCALE_Y, Y,
|
||||
"Primitive Local Y Scale");
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
be_s_zscale(ClientData clientData, Tcl_Interp *UNUSED(interp), int UNUSED(argc), char *UNUSED(argv[]))
|
||||
{
|
||||
struct cmdtab *ctp = (struct cmdtab *)clientData;
|
||||
MGED_CK_CMD(ctp);
|
||||
return be_s_axis_scale(ctp->s, BE_S_ZSCALE, RT_MATRIX_EDIT_SCALE_Z, Z,
|
||||
"Primitive Local Z Scale");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns 0 if current state is as desired,
|
||||
* Returns !0 and prints error message if state mismatch.
|
||||
*/
|
||||
int
|
||||
not_state(struct mged_state *s, int desired, char *str)
|
||||
not_state(struct mged_state *s, int desired, const char *str)
|
||||
{
|
||||
if (s->global_editing_state != desired) {
|
||||
Tcl_AppendResult(s->interp, "Unable to do <", str, "> from ", state_str[s->global_editing_state], " state.\n", (char *)NULL);
|
||||
@@ -1158,6 +1214,27 @@ f_be_s_scale(ClientData clientData, Tcl_Interp *interp, int argc, const char *ar
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
f_be_s_xscale(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
|
||||
{
|
||||
return be_s_xscale(clientData, interp, argc, (char **)argv);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
f_be_s_yscale(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
|
||||
{
|
||||
return be_s_yscale(clientData, interp, argc, (char **)argv);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
f_be_s_zscale(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
|
||||
{
|
||||
return be_s_zscale(clientData, interp, argc, (char **)argv);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
f_be_s_trans(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
|
||||
{
|
||||
|
||||
+18
-13
@@ -201,8 +201,11 @@ token_should_edit(const struct knob_token_entry *e,
|
||||
}
|
||||
|
||||
static int
|
||||
matrix_scale_edit_flag(void)
|
||||
matrix_scale_edit_flag(struct mged_state *s)
|
||||
{
|
||||
if (SEDIT_MATRIX_SCALE)
|
||||
return MEDIT(s)->edit_flag;
|
||||
|
||||
switch (edobj) {
|
||||
case BE_O_SCALE:
|
||||
return RT_MATRIX_EDIT_SCALE;
|
||||
@@ -229,7 +232,7 @@ mged_librt_knob_edit_apply(struct mged_state *s,
|
||||
return BRLCAD_ERROR;
|
||||
|
||||
struct rt_edit *re = MEDIT(s);
|
||||
int matrix_edit = (s->global_editing_state == ST_O_EDIT);
|
||||
int matrix_edit = (s->global_editing_state == ST_O_EDIT || SEDIT_MATRIX_SCALE);
|
||||
|
||||
/* Rotation */
|
||||
if (did_rot) {
|
||||
@@ -248,8 +251,8 @@ mged_librt_knob_edit_apply(struct mged_state *s,
|
||||
if (did_sca) {
|
||||
int saved_edit_flag = re->edit_flag;
|
||||
int saved_edit_mode = re->edit_mode;
|
||||
if (matrix_edit) {
|
||||
int scale_flag = matrix_scale_edit_flag();
|
||||
if (s->global_editing_state == ST_O_EDIT) {
|
||||
int scale_flag = matrix_scale_edit_flag(s);
|
||||
if (scale_flag == RT_EDIT_DEFAULT)
|
||||
return BRLCAD_ERROR;
|
||||
rt_edit_set_edflag(re, scale_flag);
|
||||
@@ -2680,12 +2683,13 @@ int
|
||||
mged_escale(struct mged_state *s, fastf_t sfactor)
|
||||
{
|
||||
fastf_t old_scale;
|
||||
int matrix_edit = (s->global_editing_state == ST_O_EDIT || SEDIT_MATRIX_SCALE);
|
||||
|
||||
if (-SMALL_FASTF < sfactor && sfactor < SMALL_FASTF) {
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
if (s->global_editing_state == ST_S_EDIT) {
|
||||
if (!matrix_edit) {
|
||||
int save_edflag;
|
||||
|
||||
save_edflag = MEDIT(s)->edit_flag;
|
||||
@@ -2724,8 +2728,8 @@ mged_escale(struct mged_state *s, fastf_t sfactor)
|
||||
inv_sfactor = 1.0 / sfactor;
|
||||
MAT_IDN(smat);
|
||||
|
||||
switch (edobj) {
|
||||
case BE_O_XSCALE: /* local scaling ... X-axis */
|
||||
switch (matrix_scale_edit_flag(s)) {
|
||||
case RT_MATRIX_EDIT_SCALE_X: /* local scaling ... X-axis */
|
||||
smat[0] = sfactor;
|
||||
old_scale = MEDIT(s)->acc_sc[X];
|
||||
MEDIT(s)->acc_sc[X] *= sfactor;
|
||||
@@ -2736,7 +2740,7 @@ mged_escale(struct mged_state *s, fastf_t sfactor)
|
||||
}
|
||||
|
||||
break;
|
||||
case BE_O_YSCALE: /* local scaling ... Y-axis */
|
||||
case RT_MATRIX_EDIT_SCALE_Y: /* local scaling ... Y-axis */
|
||||
smat[5] = sfactor;
|
||||
old_scale = MEDIT(s)->acc_sc[Y];
|
||||
MEDIT(s)->acc_sc[Y] *= sfactor;
|
||||
@@ -2747,7 +2751,7 @@ mged_escale(struct mged_state *s, fastf_t sfactor)
|
||||
}
|
||||
|
||||
break;
|
||||
case BE_O_ZSCALE: /* local scaling ... Z-axis */
|
||||
case RT_MATRIX_EDIT_SCALE_Z: /* local scaling ... Z-axis */
|
||||
smat[10] = sfactor;
|
||||
old_scale = MEDIT(s)->acc_sc[Z];
|
||||
MEDIT(s)->acc_sc[Z] *= sfactor;
|
||||
@@ -2758,8 +2762,7 @@ mged_escale(struct mged_state *s, fastf_t sfactor)
|
||||
}
|
||||
|
||||
break;
|
||||
case BE_O_SCALE: /* global scaling */
|
||||
default:
|
||||
case RT_MATRIX_EDIT_SCALE: /* global scaling */
|
||||
smat[15] = inv_sfactor;
|
||||
old_scale = MEDIT(s)->acc_sc_sol;
|
||||
MEDIT(s)->acc_sc_sol *= inv_sfactor;
|
||||
@@ -2770,6 +2773,8 @@ mged_escale(struct mged_state *s, fastf_t sfactor)
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
/* Have scaling take place with respect to keypoint,
|
||||
@@ -2892,8 +2897,8 @@ cmd_sca(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
|
||||
edobj = save_edobj;
|
||||
return ret;
|
||||
} else {
|
||||
/* argc was 4 but state was ST_S_EDIT so do nothing */
|
||||
bu_log("ERROR: Can only scale primitives uniformly (one scale factor).\n");
|
||||
/* The three-factor form remains an object-matrix operation. */
|
||||
bu_log("ERROR: sca accepts one factor in primitive edit mode.\n");
|
||||
return TCL_OK;
|
||||
}
|
||||
}
|
||||
|
||||
+95
-37
@@ -96,7 +96,7 @@ set_e_axes_pos_clbk(int UNUSED(ac), const char **UNUSED(av), void *d, void *id)
|
||||
} else if (EDIT_SCALE) {
|
||||
s->s_edit->es_edclass = EDIT_CLASS_SCALE;
|
||||
|
||||
if (SEDIT_SCALE) {
|
||||
if (SEDIT_PARAM_SCALE) {
|
||||
MEDIT(s)->k.sca_abs = 0.0;
|
||||
MEDIT(s)->acc_sc_sol = 1.0;
|
||||
}
|
||||
@@ -632,6 +632,8 @@ init_sedit_vars(struct mged_state *s)
|
||||
VSETALL(MEDIT(s)->k.tra_v_abs_last, 0.0);
|
||||
MEDIT(s)->k.sca_abs = 0.0;
|
||||
MEDIT(s)->acc_sc_sol = 1.0;
|
||||
MEDIT(s)->acc_sc_obj = 1.0;
|
||||
VSETALL(MEDIT(s)->acc_sc, 1.0);
|
||||
|
||||
VSETALL(MEDIT(s)->k.rot_m, 0.0);
|
||||
VSETALL(MEDIT(s)->k.rot_o, 0.0);
|
||||
@@ -686,6 +688,34 @@ replot_editing_solid(int UNUSED(ac), const char **UNUSED(av), void *d, void *UNU
|
||||
}
|
||||
|
||||
|
||||
/* Rebuild every displayed path that ends in the edited primitive. */
|
||||
static void
|
||||
replot_original_solid_instances(struct mged_state *s, struct directory *edited_dp)
|
||||
{
|
||||
struct display_list *gdlp;
|
||||
struct display_list *next_gdlp;
|
||||
struct bv_scene_obj *sp;
|
||||
|
||||
if (!edited_dp)
|
||||
return;
|
||||
|
||||
gdlp = BU_LIST_NEXT(display_list, (struct bu_list *)ged_dl(s->gedp));
|
||||
while (BU_LIST_NOT_HEAD(gdlp, (struct bu_list *)ged_dl(s->gedp))) {
|
||||
next_gdlp = BU_LIST_PNEXT(display_list, gdlp);
|
||||
|
||||
for (BU_LIST_FOR(sp, bv_scene_obj, &gdlp->dl_head_scene_obj)) {
|
||||
if (!sp->s_u_data)
|
||||
continue;
|
||||
struct ged_bv_data *bdata = (struct ged_bv_data *)sp->s_u_data;
|
||||
if (LAST_SOLID(bdata) == edited_dp)
|
||||
(void)replot_original_solid(s, sp);
|
||||
}
|
||||
|
||||
gdlp = next_gdlp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Apply a matrix transform to an editing solid. s->dbip is threaded
|
||||
* through to rt_matrix_transform so the ft_xform import/export routines
|
||||
@@ -782,6 +812,8 @@ sedit_mouse(struct mged_state *s, const vect_t mousevec)
|
||||
return;
|
||||
|
||||
rt_edit_process(MEDIT(s));
|
||||
if (SEDIT_MATRIX_SCALE)
|
||||
new_edit_mats(s);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1177,6 +1209,41 @@ f_eqn(ClientData clientData, Tcl_Interp *UNUSED(interp), int argc, const char *a
|
||||
|
||||
/* Hooks from buttons.c */
|
||||
|
||||
/*
|
||||
* Matrix scaling is accumulated in model coordinates for display. Primitive
|
||||
* editing writes the underlying solid, so conjugate the pending change through
|
||||
* the selected path before applying it to the primitive's local parameters.
|
||||
*/
|
||||
static int
|
||||
sedit_apply_model_changes(struct mged_state *s)
|
||||
{
|
||||
if (bn_mat_is_identity(MEDIT(s)->model_changes))
|
||||
return TCL_OK;
|
||||
|
||||
mat_t inv_path;
|
||||
mat_t local_changes;
|
||||
mat_t model_path;
|
||||
bn_mat_inv(inv_path, MEDIT(s)->e_mat);
|
||||
bn_mat_mul(model_path, MEDIT(s)->model_changes, MEDIT(s)->e_mat);
|
||||
bn_mat_mul(local_changes, inv_path, model_path);
|
||||
|
||||
struct rt_db_internal transformed;
|
||||
RT_DB_INTERNAL_INIT(&transformed);
|
||||
if (rt_matrix_transform(&transformed, local_changes, &MEDIT(s)->es_int, 0,
|
||||
s->dbip) < 0) {
|
||||
Tcl_AppendResult(s->interp, "Primitive scale cannot be represented by ",
|
||||
EDOBJ[MEDIT(s)->es_int.idb_type].ft_label, "\n", (char *)NULL);
|
||||
rt_db_free_internal(&transformed);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rt_db_free_internal(&MEDIT(s)->es_int);
|
||||
MEDIT(s)->es_int = transformed;
|
||||
MAT_IDN(MEDIT(s)->model_changes);
|
||||
new_edit_mats(s);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copied from sedit_accept - modified to optionally leave
|
||||
* solid edit state.
|
||||
@@ -1215,6 +1282,9 @@ sedit_apply(struct mged_state *s, int accept_flag)
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if (sedit_apply_model_changes(s) != TCL_OK)
|
||||
return TCL_ERROR;
|
||||
|
||||
/* make sure that any BOT solid is minimally legal */
|
||||
if (MEDIT(s)->es_int.idb_type == ID_BOT) {
|
||||
struct rt_bot_internal *bot = (struct rt_bot_internal *)MEDIT(s)->es_int.idb_ptr;
|
||||
@@ -1253,6 +1323,12 @@ sedit_apply(struct mged_state *s, int accept_flag)
|
||||
return TCL_ERROR; /* FAIL */
|
||||
}
|
||||
|
||||
/* Parameter edits regenerate their vlists as they happen, but matrix
|
||||
* scales only distort the existing vlist for interactive display. Once
|
||||
* model_changes is reset, rebuild from the persisted primitive so its
|
||||
* non-uniform transform remains visible. */
|
||||
replot_original_solid_instances(s, dp);
|
||||
|
||||
if (accept_flag) {
|
||||
menu_state->ms_flag = 0;
|
||||
movedir = 0;
|
||||
@@ -1268,10 +1344,9 @@ sedit_apply(struct mged_state *s, int accept_flag)
|
||||
* Since we are in "apply but stay editing" mode (sed_apply command),
|
||||
* we need es_int to remain valid so the user can keep editing.
|
||||
* Re-read the solid from disk to restore a clean internal state. */
|
||||
if (rt_db_get_internal(&MEDIT(s)->es_int, LAST_SOLID(bdata),
|
||||
s->dbip, NULL) < 0) {
|
||||
if (rt_db_get_internal(&MEDIT(s)->es_int, dp, s->dbip, NULL) < 0) {
|
||||
Tcl_AppendResult(s->interp, "sedit_apply(",
|
||||
LAST_SOLID(bdata)->d_namep,
|
||||
dp->d_namep,
|
||||
"): solid reimport failure\n", (char *)NULL);
|
||||
rt_db_free_internal(&MEDIT(s)->es_int);
|
||||
return TCL_ERROR;
|
||||
@@ -1284,23 +1359,23 @@ sedit_apply(struct mged_state *s, int accept_flag)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
int
|
||||
sedit_accept(struct mged_state *s)
|
||||
{
|
||||
if (s->dbip == DBI_NULL)
|
||||
return;
|
||||
return TCL_ERROR;
|
||||
|
||||
if (not_state(s, ST_S_EDIT, "Solid edit accept"))
|
||||
return;
|
||||
return TCL_ERROR;
|
||||
|
||||
if (s->dbip->dbi_read_only) {
|
||||
sedit_reject(s);
|
||||
bu_log("Sorry, this database is READ-ONLY\n");
|
||||
pr_prompt(s);
|
||||
return;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
(void)sedit_apply(s, 1);
|
||||
return sedit_apply(s, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -1312,34 +1387,15 @@ sedit_reject(struct mged_state *s)
|
||||
}
|
||||
|
||||
/* Restore the original solid everywhere */
|
||||
{
|
||||
struct display_list *gdlp;
|
||||
struct display_list *next_gdlp;
|
||||
struct bv_scene_obj *sp;
|
||||
if (!illump->s_u_data) {
|
||||
/* No solid data to replot; just reset to idle and clean up menus. */
|
||||
rt_edit_reset(MEDIT(s));
|
||||
mmenu_set(s, MENU_L1, NULL);
|
||||
mmenu_set(s, MENU_L2, NULL);
|
||||
return;
|
||||
}
|
||||
struct ged_bv_data *bdata = (struct ged_bv_data *)illump->s_u_data;
|
||||
|
||||
gdlp = BU_LIST_NEXT(display_list, (struct bu_list *)ged_dl(s->gedp));
|
||||
while (BU_LIST_NOT_HEAD(gdlp, (struct bu_list *)ged_dl(s->gedp))) {
|
||||
next_gdlp = BU_LIST_PNEXT(display_list, gdlp);
|
||||
|
||||
for (BU_LIST_FOR(sp, bv_scene_obj, &gdlp->dl_head_scene_obj)) {
|
||||
if (!sp->s_u_data)
|
||||
continue;
|
||||
struct ged_bv_data *bdatas = (struct ged_bv_data *)sp->s_u_data;
|
||||
if (LAST_SOLID(bdatas) == LAST_SOLID(bdata))
|
||||
(void)replot_original_solid(s, sp);
|
||||
}
|
||||
|
||||
gdlp = next_gdlp;
|
||||
}
|
||||
if (!illump->s_u_data) {
|
||||
/* No solid data to replot; just reset to idle and clean up menus. */
|
||||
rt_edit_reset(MEDIT(s));
|
||||
mmenu_set(s, MENU_L1, NULL);
|
||||
mmenu_set(s, MENU_L2, NULL);
|
||||
return;
|
||||
}
|
||||
struct ged_bv_data *bdata = (struct ged_bv_data *)illump->s_u_data;
|
||||
replot_original_solid_instances(s, LAST_SOLID(bdata));
|
||||
|
||||
menu_state->ms_flag = 0;
|
||||
movedir = 0;
|
||||
@@ -1383,11 +1439,13 @@ mged_param(struct mged_state *s, Tcl_Interp *interp, int argc, fastf_t *argvect)
|
||||
VMOVE(MEDIT(s)->k.tra_m_abs_last, MEDIT(s)->k.tra_m_abs);
|
||||
} else if (SEDIT_ROTATE) {
|
||||
VMOVE(MEDIT(s)->k.rot_m_abs, MEDIT(s)->e_para);
|
||||
} else if (SEDIT_SCALE) {
|
||||
} else if (SEDIT_PARAM_SCALE) {
|
||||
MEDIT(s)->k.sca_abs = MEDIT(s)->acc_sc_sol - 1.0;
|
||||
if (MEDIT(s)->k.sca_abs > 0)
|
||||
MEDIT(s)->k.sca_abs /= 3.0;
|
||||
}
|
||||
if (SEDIT_MATRIX_SCALE)
|
||||
new_edit_mats(s);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -128,6 +128,9 @@ extern int f_be_s_edit(ClientData, Tcl_Interp *, int, const char *[]);
|
||||
extern int f_be_s_illuminate(ClientData, Tcl_Interp *, int, const char *[]);
|
||||
extern int f_be_s_rotate(ClientData, Tcl_Interp *, int, const char *[]);
|
||||
extern int f_be_s_scale(ClientData, Tcl_Interp *, int, const char *[]);
|
||||
extern int f_be_s_xscale(ClientData, Tcl_Interp *, int, const char *[]);
|
||||
extern int f_be_s_yscale(ClientData, Tcl_Interp *, int, const char *[]);
|
||||
extern int f_be_s_zscale(ClientData, Tcl_Interp *, int, const char *[]);
|
||||
extern int f_be_s_trans(ClientData, Tcl_Interp *, int, const char *[]);
|
||||
extern int f_bv_35_25(ClientData, Tcl_Interp *, int, const char *[]);
|
||||
extern int f_bv_45_45(ClientData, Tcl_Interp *, int, const char *[]);
|
||||
|
||||
@@ -75,6 +75,9 @@ struct rt_edit_menu_item sed_menu[] = {
|
||||
{ "Rotate", btn_item_hit, BE_S_ROTATE },
|
||||
{ "Translate", btn_item_hit, BE_S_TRANS },
|
||||
{ "Scale", btn_item_hit, BE_S_SCALE },
|
||||
{ "Scale X", btn_item_hit, BE_S_XSCALE },
|
||||
{ "Scale Y", btn_item_hit, BE_S_YSCALE },
|
||||
{ "Scale Z", btn_item_hit, BE_S_ZSCALE },
|
||||
{ "", NULL, 0 }
|
||||
};
|
||||
|
||||
|
||||
+2
-2
@@ -366,7 +366,7 @@ extern void mged_view_callback(struct bview *gvp, void *clientData);
|
||||
extern void button(struct mged_state *s, int bnum);
|
||||
extern void press(char *str);
|
||||
extern char *label_button(struct mged_state *s, int bnum);
|
||||
extern int not_state(struct mged_state *s, int desired, char *str);
|
||||
extern int not_state(struct mged_state *s, int desired, const char *str);
|
||||
extern int chg_state(struct mged_state *s, int from, int to, char *str);
|
||||
extern void state_err(struct mged_state *s, char *str);
|
||||
|
||||
@@ -613,7 +613,7 @@ void transform_editing_solid(
|
||||
struct rt_db_internal *is, /* input solid */
|
||||
int freedbi);
|
||||
int replot_editing_solid(int, const char **, void *, void *);
|
||||
void sedit_accept(struct mged_state *s);
|
||||
int sedit_accept(struct mged_state *s);
|
||||
void sedit_mouse(struct mged_state *s, const vect_t mousevec);
|
||||
void sedit_reject(struct mged_state *s);
|
||||
void oedit_accept(struct mged_state *s);
|
||||
|
||||
@@ -514,6 +514,9 @@ __END_DECLS
|
||||
#define BV_EDIT_TOGGLE 2+32
|
||||
#define BV_EYEROT_TOGGLE 3+32
|
||||
#define BE_S_CONTEXT 4+32
|
||||
#define BE_S_XSCALE 5+32
|
||||
#define BE_S_YSCALE 6+32
|
||||
#define BE_S_ZSCALE 7+32
|
||||
|
||||
#define BV_MAXFUNC 64 /* largest code used */
|
||||
|
||||
|
||||
+6
-1
@@ -45,8 +45,13 @@
|
||||
s->s_edit->e->edit_mode == RT_PARAMS_EDIT_ROT)
|
||||
#define SEDIT_TRAN (s->global_editing_state == ST_S_EDIT && \
|
||||
s->s_edit->e->edit_mode == RT_PARAMS_EDIT_TRANS)
|
||||
#define SEDIT_SCALE (s->global_editing_state == ST_S_EDIT && \
|
||||
#define SEDIT_PARAM_SCALE (s->global_editing_state == ST_S_EDIT && \
|
||||
s->s_edit->e->edit_mode == RT_PARAMS_EDIT_SCALE)
|
||||
#define SEDIT_MATRIX_SCALE (s->global_editing_state == ST_S_EDIT && \
|
||||
(s->s_edit->e->edit_mode == RT_MATRIX_EDIT_SCALE_X || \
|
||||
s->s_edit->e->edit_mode == RT_MATRIX_EDIT_SCALE_Y || \
|
||||
s->s_edit->e->edit_mode == RT_MATRIX_EDIT_SCALE_Z))
|
||||
#define SEDIT_SCALE (SEDIT_PARAM_SCALE || SEDIT_MATRIX_SCALE)
|
||||
#define SEDIT_PICK (s->global_editing_state == ST_S_EDIT && \
|
||||
s->s_edit->e->edit_mode == RT_PARAMS_EDIT_PICK)
|
||||
|
||||
|
||||
@@ -441,7 +441,10 @@ static struct cmdtab mged_cmdtab[] = {
|
||||
{MGED_CMD_MAGIC, "summary", cmd_ged_plain_wrapper, ged_exec_summary, NULL},
|
||||
{MGED_CMD_MAGIC, "sv", f_slewview, GED_FUNC_PTR_NULL, NULL},
|
||||
{MGED_CMD_MAGIC, "svb", f_svbase, GED_FUNC_PTR_NULL, NULL},
|
||||
{MGED_CMD_MAGIC, "sxscale", f_be_s_xscale, GED_FUNC_PTR_NULL, NULL},
|
||||
{MGED_CMD_MAGIC, "sxy", f_be_s_trans, GED_FUNC_PTR_NULL, NULL},
|
||||
{MGED_CMD_MAGIC, "syscale", f_be_s_yscale, GED_FUNC_PTR_NULL, NULL},
|
||||
{MGED_CMD_MAGIC, "szscale", f_be_s_zscale, GED_FUNC_PTR_NULL, NULL},
|
||||
{MGED_CMD_MAGIC, "sync", cmd_ged_plain_wrapper, ged_exec_sync, NULL},
|
||||
{MGED_CMD_MAGIC, "t", cmd_ged_plain_wrapper, ged_exec_t, NULL},
|
||||
{MGED_CMD_MAGIC, "ted", f_tedit, GED_FUNC_PTR_NULL, NULL},
|
||||
|
||||
+12
-8
@@ -340,26 +340,30 @@ dotitles(struct mged_state *s, struct bu_vls *overlay_vls)
|
||||
|
||||
dm_set_line_attr(DMP, mged_variables->mv_linewidth, 0);
|
||||
|
||||
/* Label the vertices of the edited solid. Guard against MEDIT(s)==NULL
|
||||
* which can occur for one draw cycle immediately after sedit_accept()
|
||||
* frees and clears the rt_edit pointer before the next command resets
|
||||
* the editing state. */
|
||||
if (MEDIT(s) && (MEDIT(s)->edit_flag >= 0 || (s->global_editing_state == ST_O_EDIT && illump->s_old.s_Eflag == 0))) {
|
||||
struct rt_edit *edit_state = MEDIT(s);
|
||||
const struct rt_db_internal *edit_ip = edit_state ? &edit_state->es_int : NULL;
|
||||
|
||||
/* Accept resets the persistent edit internal before MGED leaves edit
|
||||
* state. A refresh in that interval must not dispatch primitive labels. */
|
||||
int have_edit_internal = edit_ip && edit_ip->idb_type > ID_NULL &&
|
||||
edit_ip->idb_type <= ID_MAXIMUM && edit_ip->idb_ptr;
|
||||
if (have_edit_internal && (edit_state->edit_flag >= 0 ||
|
||||
(s->global_editing_state == ST_O_EDIT && illump && illump->s_old.s_Eflag == 0))) {
|
||||
mat_t xform;
|
||||
struct rt_point_labels pl[8+1] = {RT_POINT_LABELS_INIT};
|
||||
point_t lines[2*4]; /* up to 4 lines to draw */
|
||||
int num_lines=0;
|
||||
|
||||
if (view_state->vs_gvp->gv_perspective <= 0)
|
||||
bn_mat_mul(xform, view_state->vs_model2objview, MEDIT(s)->e_mat);
|
||||
bn_mat_mul(xform, view_state->vs_model2objview, edit_state->e_mat);
|
||||
else {
|
||||
mat_t tmat;
|
||||
|
||||
bn_mat_mul(tmat, view_state->vs_model2objview, MEDIT(s)->e_mat);
|
||||
bn_mat_mul(tmat, view_state->vs_model2objview, edit_state->e_mat);
|
||||
bn_mat_mul(xform, perspective_mat, tmat);
|
||||
}
|
||||
|
||||
label_edited_solid(s, &num_lines, lines, pl, 8+1, xform, &MEDIT(s)->es_int);
|
||||
label_edited_solid(s, &num_lines, lines, pl, 8+1, xform, &edit_state->es_int);
|
||||
|
||||
dm_set_fg(DMP,
|
||||
color_scheme->cs_geo_label[0],
|
||||
|
||||
@@ -76,6 +76,18 @@ proc init_solid_edit_menus { stype menu } {
|
||||
-label "Scale" -underline 0 -command "press sscale; \
|
||||
set mged_gui($id,transform) e; set_transform $id"
|
||||
incr i
|
||||
.$id.menubar.edit insert $i radiobutton -variable edit_type \
|
||||
-label "Scale X" -underline 6 -command "press sxscale; \
|
||||
set mged_gui($id,transform) e; set_transform $id"
|
||||
incr i
|
||||
.$id.menubar.edit insert $i radiobutton -variable edit_type \
|
||||
-label "Scale Y" -underline 6 -command "press syscale; \
|
||||
set mged_gui($id,transform) e; set_transform $id"
|
||||
incr i
|
||||
.$id.menubar.edit insert $i radiobutton -variable edit_type \
|
||||
-label "Scale Z" -underline 6 -command "press szscale; \
|
||||
set mged_gui($id,transform) e; set_transform $id"
|
||||
incr i
|
||||
.$id.menubar.edit insert $i radiobutton -variable edit_type \
|
||||
-label "None Of Above" -underline 0 -command "set edit_solid_flag 0; \
|
||||
set mged_gui($id,transform) e; set_transform $id"
|
||||
@@ -347,6 +359,15 @@ proc init_solid_edit_menu_hoc { stype } {
|
||||
hoc_register_menu_data "Edit" "Scale" \
|
||||
"Primitive Edit - Scale" \
|
||||
{{summary "Scale"}}
|
||||
hoc_register_menu_data "Edit" "Scale X" \
|
||||
"Primitive Edit - Scale X" \
|
||||
{{summary "Scale X"}}
|
||||
hoc_register_menu_data "Edit" "Scale Y" \
|
||||
"Primitive Edit - Scale Y" \
|
||||
{{summary "Scale Y"}}
|
||||
hoc_register_menu_data "Edit" "Scale Z" \
|
||||
"Primitive Edit - Scale Z" \
|
||||
{{summary "Scale Z"}}
|
||||
hoc_register_menu_data "Edit" "none of above" \
|
||||
"Primitive Edit - none of above" \
|
||||
{{summary "none of above"}}
|
||||
|
||||
Reference in New Issue
Block a user