Compare commits

...
2 Commits
42 changed files with 8862 additions and 58 deletions
+1
View File
@@ -371,6 +371,7 @@ set(
translate.adoc
tree.adoc
unhide.adoc
unpush.adoc
units.adoc
v2m_point.adoc
vars.adoc
+140
View File
@@ -0,0 +1,140 @@
= UNPUSH(nged)
:doctype: manpage
:mansource: BRL-CAD
:manmanual: BRL-CAD MGED Commands
== NAME
unpush - infer and restore shared geometry from pushed object trees
== SYNOPSIS
[source]
----
unpush [-D] [-L] [-v] [-m rigid|similarity|affine] object [object ...]
----
[[description]]
== DESCRIPTION
The *unpush* command examines the trees rooted at the specified _objects_ and
looks for primitive shapes and combinations that differ only by a recoverable
placement transform. It can replace those copies with shared objects and move
their differing placements into combination leaf matrices. This is a
geometric inference operation: it does not require provenance or other
metadata from an earlier *push*, *xpush*, or *npush* operation.
Candidate primitives are first canonicalized into a stable local frame.
Candidates are accepted as a group only after their attributes and geometry
match within the database tolerance and the proposed replacement has been
reconstructed under every leaf matrix that will be changed. Combination and
region candidates are analyzed from the leaves upward. Their boolean tree
structure, operand order, child identities and relative matrices, region
properties, material and rendering fields, and custom attributes must agree.
Boolean operands are not reordered.
The command currently canonicalizes halfspaces, ellipsoids and spheres, tori,
elliptical tori, right parabolic and hyperbolic cylinders, elliptical
paraboloids and hyperboloids, particles, TGC and REC shapes, ARB4 through ARB8
shapes, BOT meshes, and superellipsoids. Other primitive types are reported
as unsupported and are left unchanged.
Invalid supported shapes are reported as failures and are also left unchanged.
[[modes]]
== TRANSFORM MODES
The *-m* option limits the class of transforms that may be recovered:
* *rigid* permits translation and rotation.
* *similarity* additionally permits uniform scale.
* *affine* permits primitive canonicalizers to consider nonuniform scale and
shear.
Dry-run analysis defaults to *affine*. Database writes constrain an affine
request to *similarity*, because BRL-CAD combination matrices do not accept
general shear. The report explicitly notes this constraint.
[[options]]
== OPTIONS
*-D*, *--dry-run*::
Analyze and report possible sharing without modifying the database. Running a
dry run first is strongly recommended.
*-L*, *--local*::
Restrict changed references to the selected trees. Objects still referenced
outside those trees are retained, and descendants of a selected combination
whose definition is itself exposed outside the selection are not changed.
Without *-L*, *unpush* may update matching references elsewhere in the database
so a redundant object can be removed globally.
*-m* _mode_, *--mode* _mode_::
Select *rigid*, *similarity*, or *affine* transform inference as described
above.
*-v*, *--verbosity*::
Increase reporting detail. Repeat the option to include replacement matrices
for primitive references.
*-h*, *--help*::
Print command help.
[[writes]]
== DATABASE CHANGES AND RECOVERY
Before writing, *unpush* accounts for database references and prepares every
affected object in memory. It writes new canonical primitives first, then
updates parent combinations and top-level wrappers, reads the results back, and
validates the exact rewritten leaves. Superseded objects are deleted only
after the updated reference counts show that they are unused. If an object
write or validation fails, the command restores the saved object definitions
and removes newly written canonical primitives when it is safe to do so.
Selected top-level primitive names are preserved by replacing those primitives
with one-leaf wrapper combinations. A selected top-level non-region
combination may likewise become a one-leaf combination referencing the shared
representative. Top-level regions are never nested inside wrapper regions;
such consolidation is deferred, although safe sharing below those regions may
still be restored.
The recovery process does not copy the complete database, which keeps it
practical for multi-gigabyte models. It protects against errors detected by
the running command, but it is not a crash-atomic database transaction. Keep
an external backup when modifying valuable data, particularly when storage or
system reliability is uncertain.
[[examples]]
== EXAMPLES
.Analyze an assembly without changing it.
[example]
====
mged>**unpush -D -v -m similarity vehicle**
====
.Restore sharing only within two selected assemblies.
[example]
====
mged>**unpush -L left_wheel right_wheel**
====
[[see_also]]
== SEE ALSO
*npush*(nged), *push*(nged), *xpush*(nged), *facetize*(nged)
[[author]]
== AUTHOR
BRL-CAD Team
[[bug_reports]]
== BUG REPORTS
Reports of bugs or problems should be submitted via electronic mail to devs@brlcad.org
+1
View File
@@ -444,6 +444,7 @@ Interactive MGED and GED commands. See also link:man1_index.html[Command-Line To
== U
* link:mann/unhide.html[unhide]
* link:mann/unpush.html[unpush]
* link:mann/units.html[units]
== V
+16 -3
View File
@@ -21,7 +21,7 @@
/*----------------------------------------------------------------------*/
/** @addtogroup bg_pca
*
* Principle Component Analysis
* Principal Component Analysis
*
* Calculates an XYZ coordinate system such that it aligns with the largest
* variations in the supplied data. Intuitively, it "aligns" with the
@@ -61,6 +61,9 @@
#define BG_PCA_H
#include "common.h"
#include <stddef.h>
#include "vmath.h"
#include "bg/defines.h"
@@ -69,7 +72,7 @@ __BEGIN_DECLS
/**
* @brief
* Perform a Principle Component Analysis on a set of points.
* Perform a Principal Component Analysis on a set of points.
*
* Outputs are a center point and XYZ vectors for the coordinate system.
*
@@ -86,9 +89,19 @@ __BEGIN_DECLS
*/
BG_EXPORT extern int bg_pca(point_t *c, vect_t *xaxis, vect_t *yaxis, vect_t *zaxis, size_t npnts, const point_t *pnts);
/**
* Perform PCA and also report the singular values of the centered point
* matrix, ordered to correspond to xaxis, yaxis, and zaxis. A zero singular
* value identifies a dimension in which the input point set has no extent.
* The values scale with the square root of the number of input points.
*/
BG_EXPORT extern int bg_pca_svd(point_t *c, vect_t *xaxis, vect_t *yaxis,
vect_t *zaxis, vect_t *singular_values,
size_t npnts, const point_t *pnts);
__END_DECLS
#endif /* BG_PLANE_H */
#endif /* BG_PCA_H */
/** @} */
/*
* Local Variables:
+14
View File
@@ -178,6 +178,20 @@ RT_EXPORT extern int rt_obj_make(const char *label, const point_t origin, double
*/
RT_EXPORT extern int rt_obj_xform(struct rt_db_internal *op, const mat_t mat, struct rt_db_internal *ip, int release, struct db_i *dbip);
/**
* Factor an object's placement into a canonical primitive and a matrix mapping
* that primitive back to the input geometry.
*
* The caller must pass an initialized, empty @p canonical and owns its contents
* on success. Applying @p canonical_to_input to @p canonical with ft_mat must
* reproduce geometry equivalent to @p input within @p tol.
*/
RT_EXPORT extern int rt_obj_canonicalize(struct rt_db_internal *canonical,
mat_t canonical_to_input,
const struct rt_db_internal *input,
const struct bn_tol *tol,
enum rt_canonicalize_mode mode);
/**
* obtain parameters for an object in libpc form
*/
+37
View File
@@ -62,6 +62,27 @@ __BEGIN_DECLS
struct rt_piecestate; /* forward declaration for ft_piece_shot / ft_piece_hitsegs */
/**
* Transformation classes a primitive canonicalizer may factor out of an
* object's stored parameters. Each successive mode permits all operations
* allowed by the preceding mode.
*/
enum rt_canonicalize_mode {
RT_CANONICALIZE_RIGID = 0,
RT_CANONICALIZE_SIMILARITY,
RT_CANONICALIZE_AFFINE
};
/**
* Return values for primitive canonicalization. Unsupported is distinct from
* an error so callers may safely skip primitives without an implementation.
*/
enum rt_canonicalize_status {
RT_CANONICALIZE_OK = 0,
RT_CANONICALIZE_ERROR,
RT_CANONICALIZE_UNSUPPORTED
};
/**
* This needs to be at the end of the raytrace.h header file, so that
* all the structure names are known. The "union record" and "struct
@@ -332,6 +353,22 @@ struct rt_functab {
int (*ft_validate)(struct bu_vls *error_msg, const struct rt_db_internal *ip, const struct bn_tol *tol);
#define RTFUNCTAB_FUNC_VALIDATE_CAST(_func) ((int (*)(struct bu_vls *, const struct rt_db_internal *, const struct bn_tol *))((void (*)(void))_func))
/**
* Factor primitive geometry into a deterministic local representation and
* a matrix mapping that representation back to the input geometry.
*
* On success, applying @p canonical_to_input to @p canonical with ft_mat
* must reproduce geometry equivalent to @p input within @p tol. The
* callback initializes and owns the contents of @p canonical. It must not
* modify @p input or access database state.
*/
int (*ft_canonicalize)(struct rt_db_internal *canonical,
mat_t canonical_to_input,
const struct rt_db_internal *input,
const struct bn_tol *tol,
enum rt_canonicalize_mode mode);
#define RTFUNCTAB_FUNC_CANONICALIZE_CAST(_func) ((int (*)(struct rt_db_internal *, mat_t, const struct rt_db_internal *, const struct bn_tol *, enum rt_canonicalize_mode))((void (*)(void))_func))
};
/**
+8
View File
@@ -19,6 +19,13 @@ if(TARGET libged)
distclean("${CMAKE_CURRENT_BINARY_DIR}/regress-ged_push.log")
distclean("${CMAKE_CURRENT_BINARY_DIR}/ged_push.g")
brlcad_addexec(regress_unpush unpush.cpp "libged;libwdb;librt;libbg;libbu" TEST_USESDATA)
if(TARGET regress_unpush)
add_dependencies(regress_unpush ged_plugins)
endif(TARGET regress_unpush)
brlcad_add_test(NAME regress-ged_unpush COMMAND regress_unpush)
brlcad_regression_test(regress-ged_unpush "regress_unpush" TEST_DEFINED)
# Phase D regression: coplanar window-frame subtraction pattern.
# Exercises the instance-aware variant planning pass in the Manifold
# facetize pipeline (plan.cpp + _booltree_leaf_tess variant resolution).
@@ -44,6 +51,7 @@ cmakefiles(
mater.c
regdef.c
push.cpp
unpush.cpp
regress_facetize.cpp
regress_lint_raytrace.cpp
d2push_tests.g
File diff suppressed because it is too large Load Diff
+29 -3
View File
@@ -52,8 +52,9 @@
#include "bg/pca.h"
// Use SVD algorithm from Soderkvist to fit a plane to vertex points
extern "C" int
bg_pca(point_t *c, vect_t *xa, vect_t *ya, vect_t *za, size_t npnts, const point_t *pnts)
static int
bg_pca_impl(point_t *c, vect_t *xa, vect_t *ya, vect_t *za,
vect_t *singular_values, size_t npnts, const point_t *pnts)
{
if (!c || !xa || !ya || !za || npnts == 0 || !pnts)
return BRLCAD_ERROR;
@@ -74,7 +75,9 @@ bg_pca(point_t *c, vect_t *xa, vect_t *ya, vect_t *za, size_t npnts, const point
}
// 3. Perform SVD
Eigen::JacobiSVD<Eigen::MatrixXd> svd(A, Eigen::ComputeThinU);
// Full U supplies a complete 3D frame for one- and two-point inputs as
// well as ordinary 3D point sets.
Eigen::JacobiSVD<Eigen::MatrixXd> svd(A, Eigen::ComputeFullU);
// 4. Extract the vectors from the U matrix
vect_t xaxis, yaxis, zaxis;
@@ -93,10 +96,33 @@ bg_pca(point_t *c, vect_t *xa, vect_t *ya, vect_t *za, size_t npnts, const point
VMOVE(*xa, xaxis);
VMOVE(*ya, yaxis);
VMOVE(*za, zaxis);
if (singular_values) {
VSETALL(*singular_values, 0.0);
for (Eigen::Index i = 0; i < svd.singularValues().size() && i < 3; i++)
(*singular_values)[i] = svd.singularValues()(i);
}
return BRLCAD_OK;
}
extern "C" int
bg_pca_svd(point_t *c, vect_t *xa, vect_t *ya, vect_t *za,
vect_t *singular_values, size_t npnts, const point_t *pnts)
{
if (!singular_values)
return BRLCAD_ERROR;
return bg_pca_impl(c, xa, ya, za, singular_values, npnts, pnts);
}
extern "C" int
bg_pca(point_t *c, vect_t *xa, vect_t *ya, vect_t *za, size_t npnts, const point_t *pnts)
{
return bg_pca_impl(c, xa, ya, za, NULL, npnts, pnts);
}
/** @} */
// Local Variables:
// tab-width: 8
+4
View File
@@ -27,6 +27,9 @@ brlcad_addexec(bg_test "${CMAKE_CURRENT_BINARY_DIR}/bg_test.c" "${BG_TEST_LIBS}"
# TODO - convert this into the scheme used by other tests below
brlcad_addexec(bgtester bgtester.c "${BG_TEST_LIBS}" TEST)
brlcad_addexec(bg_pca pca.c "${BG_TEST_LIBS}" TEST)
brlcad_add_test(NAME bg_pca COMMAND bg_pca)
# ************ aabb_ray.c tests *************
brlcad_addexec(bg_aabb_ray aabb_ray.c "${BG_TEST_LIBS}" TEST)
@@ -492,6 +495,7 @@ brlcad_add_test(NAME bg_geogram_optimizer COMMAND bg_geogram_optimizer)
cmakefiles(
bg_test.c.in
geogram_optimizer.cpp
pca.c
plane_dist.c
plane_isect.c
plane_pt.c
+145
View File
@@ -0,0 +1,145 @@
/* P C A . 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.
*/
#include "common.h"
#include <math.h>
#include "bu/app.h"
#include "bu/log.h"
#include "bg/pca.h"
#define PCA_TEST_EPSILON 1.0e-9
static int
near_value(fastf_t a, fastf_t b)
{
fastf_t scale = fmax(1.0, fmax(fabs(a), fabs(b)));
return fabs(a - b) <= PCA_TEST_EPSILON * scale;
}
static int
test_box(void)
{
point_t points[8];
point_t center;
vect_t xaxis;
vect_t yaxis;
vect_t zaxis;
vect_t singular_values;
size_t index = 0;
for (int xsign = -1; xsign <= 1; xsign += 2) {
for (int ysign = -1; ysign <= 1; ysign += 2) {
for (int zsign = -1; zsign <= 1; zsign += 2) {
VSET(points[index], 4.0 + 3.0 * xsign,
-2.0 + 2.0 * ysign, 7.0 + zsign);
index++;
}
}
}
if (bg_pca_svd(&center, &xaxis, &yaxis, &zaxis, &singular_values,
8, (const point_t *)points) != BRLCAD_OK) {
bu_log("box PCA failed\n");
return 1;
}
if (!near_value(center[X], 4.0) || !near_value(center[Y], -2.0) ||
!near_value(center[Z], 7.0) ||
!near_value(singular_values[X], 3.0 * sqrt(8.0)) ||
!near_value(singular_values[Y], 2.0 * sqrt(8.0)) ||
!near_value(singular_values[Z], sqrt(8.0)) ||
!near_value(MAGNITUDE(xaxis), 1.0) ||
!near_value(MAGNITUDE(yaxis), 1.0) ||
!near_value(MAGNITUDE(zaxis), 1.0) ||
!near_value(VDOT(xaxis, yaxis), 0.0) ||
!near_value(VDOT(xaxis, zaxis), 0.0) ||
!near_value(VDOT(yaxis, zaxis), 0.0)) {
bu_log("box PCA returned incorrect center, frame, or singular values\n");
return 1;
}
return 0;
}
static int
test_single_point(void)
{
const point_t point = {3.0, -5.0, 8.0};
point_t center;
vect_t xaxis;
vect_t yaxis;
vect_t zaxis;
vect_t singular_values;
if (bg_pca_svd(&center, &xaxis, &yaxis, &zaxis, &singular_values,
1, (const point_t *)&point) != BRLCAD_OK) {
bu_log("single-point PCA failed\n");
return 1;
}
if (!near_value(center[X], point[X]) || !near_value(center[Y], point[Y]) ||
!near_value(center[Z], point[Z]) || !VNEAR_ZERO(singular_values, PCA_TEST_EPSILON) ||
!near_value(MAGNITUDE(xaxis), 1.0) ||
!near_value(MAGNITUDE(yaxis), 1.0) ||
!near_value(MAGNITUDE(zaxis), 1.0)) {
bu_log("single-point PCA returned invalid output\n");
return 1;
}
return 0;
}
int
main(int UNUSED(argc), const char *argv[])
{
point_t center;
vect_t xaxis;
vect_t yaxis;
vect_t zaxis;
bu_setprogname(argv[0]);
if (test_box() || test_single_point())
return 1;
if (bg_pca_svd(&center, &xaxis, &yaxis, &zaxis, NULL, 0, NULL) != BRLCAD_ERROR) {
bu_log("invalid PCA input was not rejected\n");
return 1;
}
return 0;
}
/*
* Local Variables:
* mode: C
* tab-width: 8
* indent-tabs-mode: t
* c-file-style: "stroustrup"
* End:
* ex: shiftwidth=4 tabstop=8
*/
@@ -188,8 +188,7 @@ test_uncertain()
require(result.shells.empty() || (result.shells.front().orientation == ShellOrientation::Indeterminate &&
!result.shells.front().corrected), "invalid parameter-loop senses caused an incorrect face repair");
auto flat = box();
ON_Xform flatten;
flatten.DiagonalTransformation(1.0);
ON_Xform flatten = ON_Xform::DiagonalTransformation(1.0);
flatten[2][2] = 1.0e-13;
require(flat->Transform(flatten), "could not construct thin shell");
result = repair_brep_orientation(*flat);
+3
View File
@@ -328,6 +328,7 @@ set(
LIBGED_SOURCES
${GED_CMD_SRC}
${GED_STATIC_REGISTRATION_CPP}
canonicalize.cpp
edit_buf.cpp
exec.cpp
ged_init.cpp
@@ -355,6 +356,8 @@ set(
wireframe_eval.c
)
cmakefiles(canonicalize_private.h)
set_property(
SOURCE ged_init.cpp
APPEND
File diff suppressed because it is too large Load Diff
+109
View File
@@ -0,0 +1,109 @@
/* C A N O N I C A L I Z E _ P R I V A T E . H
* 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.
*/
#ifndef LIBGED_CANONICALIZE_PRIVATE_H
#define LIBGED_CANONICALIZE_PRIVATE_H
#include "common.h"
#include "bn/tol.h"
#include "ged/defines.h"
#include "raytrace.h"
__BEGIN_DECLS
/* These helpers are private libged interfaces shared by commands that need to
* recognize or reconstruct canonical primitive geometry. */
GED_EXPORT fastf_t _ged_matrix_roundoff_tolerance(fastf_t a, fastf_t b,
fastf_t database_limit);
GED_EXPORT int _ged_matrices_numerically_equal(const mat_t a, const mat_t b,
const struct bn_tol *tol);
GED_EXPORT int _ged_canonical_geometry_equal(
const struct rt_db_internal *a, const struct rt_db_internal *b,
const struct bn_tol *tol);
GED_EXPORT int _ged_canonical_geometry_metric(
const struct rt_db_internal *object, const struct bn_tol *tol,
fastf_t *metric, fastf_t *metric_tolerance);
GED_EXPORT int _ged_primitive_geometry_equal(
const struct rt_db_internal *a, const struct rt_db_internal *b,
const struct bn_tol *tol);
/* Allocate and populate output with input transformed by matrix. Output must
* be empty and remains empty on error. */
GED_EXPORT int _ged_transform_primitive(struct rt_db_internal *output,
const mat_t matrix, const struct rt_db_internal *input);
GED_EXPORT int _ged_transformed_geometry_equal(
const struct rt_db_internal *a, const mat_t a_matrix,
const struct rt_db_internal *b, const mat_t b_matrix,
const struct bn_tol *tol);
__END_DECLS
#ifdef __cplusplus
#include <map>
#include <string>
#include <vector>
struct GedCanonicalChildInfo {
size_t identity = 0;
mat_t placement = MAT_INIT_IDN;
};
struct GedCanonicalCombinationLeaf {
size_t child_identity = 0;
mat_t effective_matrix = MAT_INIT_IDN;
mat_t relative_matrix = MAT_INIT_IDN;
};
struct GedCanonicalCombinationRecord {
struct directory *dp = nullptr;
mat_t placement = MAT_INIT_IDN;
mat_t identity_placement = MAT_INIT_IDN;
mat_t representative_to_input = MAT_INIT_IDN;
std::string bucket;
std::vector<GedCanonicalCombinationLeaf> leaves;
fastf_t metric = 0.0;
fastf_t metric_tolerance = 0.0;
size_t identity = 0;
bool region = false;
bool valid = false;
};
struct GedCanonicalCombinationGroup {
std::vector<size_t> records;
};
struct GedCanonicalCombinationAnalysis {
std::vector<GedCanonicalCombinationRecord> records;
std::vector<GedCanonicalCombinationGroup> groups;
size_t failures = 0;
};
/* Identify equivalent combination DAG nodes bottom-up. Primitive children
* supply identities and canonical-to-input placements established by the
* caller's selected transform mode. Database metadata participates in
* identity when include_metadata is true; geometry-only consumers such as
* facetize deliberately ignore it. */
GED_EXPORT int _ged_canonical_combination_analysis(
GedCanonicalCombinationAnalysis *analysis, struct db_i *dbip,
const std::vector<struct directory *> &combinations,
const std::map<struct directory *, GedCanonicalChildInfo> &primitive_children,
const struct bn_tol *tol, bool include_metadata);
#endif
#endif /* LIBGED_CANONICALIZE_PRIVATE_H */
+3 -1
View File
@@ -1,12 +1,14 @@
set(PLUGIN_LIBS libged libbu)
set(PLUGIN_LIBS libged librt libbg libbu)
ged_plugin_library(ged-npush
npush.cpp
unpush.cpp
LIBS ${PLUGIN_LIBS}
)
cmakefiles(
CMakeLists.txt
unpush.cpp
)
# Local Variables:
+4 -1
View File
@@ -1708,11 +1708,14 @@ ged_npush_core(struct ged *gedp, int argc, const char *argv[])
#include "../include/plugin.h"
extern "C" int ged_unpush_core(struct ged *gedp, int argc, const char *argv[]);
#define GED_NPUSH_COMMANDS(X, XID) \
X(npush, ged_npush_core, GED_CMD_DEFAULT) \
X(unpush, ged_unpush_core, GED_CMD_DEFAULT) \
GED_DECLARE_COMMAND_SET(GED_NPUSH_COMMANDS)
GED_DECLARE_PLUGIN_MANIFEST("libged_npush", 1, GED_NPUSH_COMMANDS)
GED_DECLARE_PLUGIN_MANIFEST("libged_npush", 2, GED_NPUSH_COMMANDS)
// Local Variables:
// tab-width: 8
File diff suppressed because it is too large Load Diff
+4
View File
@@ -4,6 +4,9 @@ if(TARGET ged_test_tops)
endif(TARGET ged_test_tops)
brlcad_add_test(NAME ged_test_tops_moss COMMAND ged_test_tops "${CMAKE_CURRENT_SOURCE_DIR}/ged_tests.g")
brlcad_addexec(ged_test_canonicalize canonicalize.cpp libged TEST)
brlcad_add_test(NAME ged_canonicalize COMMAND ged_test_canonicalize)
brlcad_addexec(ged_test_rm ged_test_rm.c "libged;libwdb;librt;libbu" TEST)
if(TARGET ged_test_rm)
add_dependencies(ged_test_rm ged_plugins)
@@ -122,6 +125,7 @@ add_subdirectory(edit)
# in a separate file, since its bad input examples stand an excellent chance of
# breaking commands.
cmakefiles(
canonicalize.cpp
lint_tests.g
rt_search_tests.g
search_tests.g
+127
View File
@@ -0,0 +1,127 @@
/* C A N O N I C A L I Z E . 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.
*/
#include "common.h"
#include <limits>
#include "bu/app.h"
#include "rt/func.h"
#include "../canonicalize_private.h"
namespace {
void
make_ell(struct rt_db_internal *intern, const point_t center,
const vect_t a, const vect_t b, const vect_t c)
{
RT_DB_INTERNAL_INIT(intern);
intern->idb_major_type = DB5_MAJORTYPE_BRLCAD;
intern->idb_minor_type = ID_ELL;
intern->idb_meth = &OBJ[ID_ELL];
BU_ALLOC(intern->idb_ptr, struct rt_ell_internal);
auto *ell = static_cast<struct rt_ell_internal *>(intern->idb_ptr);
ell->magic = RT_ELL_INTERNAL_MAGIC;
VMOVE(ell->v, center);
VMOVE(ell->a, a);
VMOVE(ell->b, b);
VMOVE(ell->c, c);
}
int
test_matrix_comparison(const struct bn_tol *tol)
{
mat_t a = MAT_INIT_IDN;
mat_t b = MAT_INIT_IDN;
const fastf_t epsilon = std::numeric_limits<fastf_t>::epsilon();
b[0] += 16.0 * epsilon;
if (!_ged_matrices_numerically_equal(a, b, tol))
return 1;
b[0] = a[0] + 0.5 * tol->perp;
if (_ged_matrices_numerically_equal(a, b, tol))
return 1;
b[0] = a[0];
b[3] = 0.5 * tol->dist;
return _ged_matrices_numerically_equal(a, b, tol) ? 1 : 0;
}
int
test_geometry_comparison(const struct bn_tol *tol)
{
const point_t center = VINIT_ZERO;
const vect_t x_axis = {2.0, 0.0, 0.0};
const vect_t y_axis = {0.0, 3.0, 0.0};
const vect_t z_axis = {0.0, 0.0, 4.0};
struct rt_db_internal a;
struct rt_db_internal equivalent;
make_ell(&a, center, x_axis, y_axis, z_axis);
make_ell(&equivalent, center, y_axis, x_axis, z_axis);
int failed = _ged_canonical_geometry_equal(&a, &equivalent, tol) ||
!_ged_primitive_geometry_equal(&a, &equivalent, tol);
rt_db_free_internal(&equivalent);
rt_db_free_internal(&a);
return failed;
}
int
test_transform(const struct bn_tol *tol)
{
const point_t center = VINIT_ZERO;
const vect_t x_axis = {2.0, 0.0, 0.0};
const vect_t y_axis = {0.0, 3.0, 0.0};
const vect_t z_axis = {0.0, 0.0, 4.0};
struct rt_db_internal input;
struct rt_db_internal transformed;
mat_t matrix = MAT_INIT_IDN;
make_ell(&input, center, x_axis, y_axis, z_axis);
matrix[0] = 0.0;
matrix[1] = -1.0;
matrix[4] = 1.0;
matrix[5] = 0.0;
MAT_DELTAS(matrix, 11.0, -7.0, 5.0);
RT_DB_INTERNAL_INIT(&transformed);
if (_ged_transform_primitive(&transformed, matrix, &input) != BRLCAD_OK) {
rt_db_free_internal(&input);
return 1;
}
int failed = !_ged_transformed_geometry_equal(&input, matrix,
&transformed, bn_mat_identity, tol);
rt_db_free_internal(&transformed);
rt_db_free_internal(&input);
return failed;
}
} /* namespace */
int
main(int UNUSED(argc), const char *argv[])
{
const struct bn_tol tol = BN_TOL_INIT_TOL;
bu_setprogname(argv[0]);
int failures = test_matrix_comparison(&tol) +
test_geometry_comparison(&tol) + test_transform(&tol);
if (failures)
bu_log("libged canonical geometry tests failed: %d\n", failures);
return failures ? 1 : 0;
}
+5
View File
@@ -161,6 +161,7 @@ set(
primitives/ars/ars_mirror.c
primitives/bot/bot.c
primitives/bot/bot_brep.cpp
primitives/bot/bot_canonicalize.cpp
primitives/bot/bot_edge.c
primitives/bot/bot_mirror.c
primitives/bot/bot_oriented_bbox.cpp
@@ -176,6 +177,7 @@ set(
primitives/bspline/bspline.cpp
primitives/bspline/bspline_brep.cpp
primitives/bspline/bspline_mirror.c
primitives/canonicalize.c
primitives/crofton.cpp
primitives/cline/cline.c
primitives/cline/cline_brep.cpp
@@ -228,6 +230,7 @@ set(
primitives/nmg/nmg_mirror.c
primitives/nmg/nmg_tri_mc.c
primitives/obj_adjust.c
primitives/obj_canonicalize.c
primitives/obj_curve.c
primitives/obj_describe.c
primitives/obj_export.c
@@ -325,6 +328,7 @@ set(
material/materialX.h
cut_hlbvh.h
prcomb.c
primitives/canonicalize_private.h
primitives/bot/bot_edge.h
primitives/bot/decimate_private.h
primitives/bot/bot_wireframe.cpp
@@ -482,6 +486,7 @@ brlcad_addlib(librt "${LIBRT_SOURCES}" "${RT_INCLUDE_DIRS}" "${RT_LOCAL_INCLUDE_
primitives/bot/edbot.c
primitives/bot/bot.c
primitives/bot/bot_brep.cpp
primitives/bot/bot_canonicalize.cpp
primitives/bot/bot_edge.c
primitives/bot/bot_mirror.c
primitives/bot/bot_oriented_bbox.cpp
+227
View File
@@ -1970,6 +1970,233 @@ rt_arb_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
}
static int
arb_affine_frame(mat_t canonical_to_input, mat_t input_to_canonical,
const struct rt_arb_internal *arb, const struct bn_tol *tol)
{
vect_t first, second, third;
vect_t cross;
fastf_t first_mag = 0.0;
fastf_t cross_mag = 0.0;
int first_index = -1;
int second_index = -1;
int third_index = -1;
for (int i = 1; i < 8; i++) {
VSUB2(first, arb->pt[i], arb->pt[0]);
first_mag = MAGNITUDE(first);
if (first_mag > tol->dist) {
first_index = i;
break;
}
}
if (first_index < 0)
return 0;
for (int i = first_index + 1; i < 8; i++) {
fastf_t second_mag;
VSUB2(second, arb->pt[i], arb->pt[0]);
second_mag = MAGNITUDE(second);
if (second_mag <= tol->dist)
continue;
VCROSS(cross, first, second);
cross_mag = MAGNITUDE(cross);
if (cross_mag > first_mag * second_mag * tol->perp) {
second_index = i;
break;
}
}
if (second_index < 0)
return 0;
for (int i = second_index + 1; i < 8; i++) {
fastf_t third_mag;
VSUB2(third, arb->pt[i], arb->pt[0]);
third_mag = MAGNITUDE(third);
if (third_mag <= tol->dist)
continue;
if (fabs(VDOT(third, cross)) > third_mag * cross_mag * tol->perp) {
third_index = i;
break;
}
}
if (third_index < 0)
return 0;
VSUB2(first, arb->pt[first_index], arb->pt[0]);
VSUB2(second, arb->pt[second_index], arb->pt[0]);
VSUB2(third, arb->pt[third_index], arb->pt[0]);
MAT_IDN(canonical_to_input);
canonical_to_input[0] = first[X];
canonical_to_input[4] = first[Y];
canonical_to_input[8] = first[Z];
canonical_to_input[1] = second[X];
canonical_to_input[5] = second[Y];
canonical_to_input[9] = second[Z];
canonical_to_input[2] = third[X];
canonical_to_input[6] = third[Y];
canonical_to_input[10] = third[Z];
MAT_DELTAS_VEC(canonical_to_input, arb->pt[0]);
return bn_mat_inverse(input_to_canonical, canonical_to_input);
}
static int
arb_rigid_frame(mat_t canonical_to_input, mat_t input_to_canonical,
const struct rt_arb_internal *arb, const struct bn_tol *tol,
enum rt_canonicalize_mode mode)
{
vect_t xaxis, yaxis, zaxis;
fastf_t longest_sq = 0.0;
fastf_t widest_sq = 0.0;
fastf_t highest = 0.0;
fastf_t uniform_scale = 1.0;
int x_index = -1;
int y_index = -1;
int z_index = -1;
for (int i = 1; i < 8; i++) {
vect_t delta;
fastf_t distance_sq;
VSUB2(delta, arb->pt[i], arb->pt[0]);
distance_sq = MAGSQ(delta);
if (distance_sq > longest_sq + tol->dist_sq) {
longest_sq = distance_sq;
x_index = i;
}
}
if (x_index < 0 || longest_sq <= tol->dist_sq)
return 0;
VSUB2(xaxis, arb->pt[x_index], arb->pt[0]);
VUNITIZE(xaxis);
for (int i = 1; i < 8; i++) {
vect_t delta, perpendicular;
fastf_t width_sq;
VSUB2(delta, arb->pt[i], arb->pt[0]);
VJOIN1(perpendicular, delta, -VDOT(delta, xaxis), xaxis);
width_sq = MAGSQ(perpendicular);
if (width_sq > widest_sq + tol->dist_sq) {
widest_sq = width_sq;
VMOVE(yaxis, perpendicular);
y_index = i;
}
}
if (y_index < 0 || widest_sq <= tol->dist_sq)
return 0;
VUNITIZE(yaxis);
VCROSS(zaxis, xaxis, yaxis);
for (int i = 1; i < 8; i++) {
vect_t delta;
fastf_t height;
VSUB2(delta, arb->pt[i], arb->pt[0]);
height = fabs(VDOT(delta, zaxis));
if (height > highest + tol->dist) {
highest = height;
z_index = i;
}
}
if (z_index < 0 || highest <= tol->dist)
return 0;
{
vect_t delta;
VSUB2(delta, arb->pt[z_index], arb->pt[0]);
if (VDOT(delta, zaxis) < 0.0) {
VREVERSE(yaxis, yaxis);
VREVERSE(zaxis, zaxis);
}
}
if (mode == RT_CANONICALIZE_SIMILARITY) {
fastf_t diameter_sq = 0.0;
for (int i = 0; i < 7; i++) {
for (int j = i + 1; j < 8; j++) {
vect_t delta;
VSUB2(delta, arb->pt[j], arb->pt[i]);
diameter_sq = fmax(diameter_sq, MAGSQ(delta));
}
}
uniform_scale = sqrt(diameter_sq);
if (uniform_scale <= tol->dist)
return 0;
}
MAT_IDN(canonical_to_input);
canonical_to_input[0] = xaxis[X];
canonical_to_input[4] = xaxis[Y];
canonical_to_input[8] = xaxis[Z];
canonical_to_input[1] = yaxis[X];
canonical_to_input[5] = yaxis[Y];
canonical_to_input[9] = yaxis[Z];
canonical_to_input[2] = zaxis[X];
canonical_to_input[6] = zaxis[Y];
canonical_to_input[10] = zaxis[Z];
MAT_DELTAS_VEC(canonical_to_input, arb->pt[0]);
canonical_to_input[15] = 1.0 / uniform_scale;
canonical_to_input[3] /= uniform_scale;
canonical_to_input[7] /= uniform_scale;
canonical_to_input[11] /= uniform_scale;
return bn_mat_inverse(input_to_canonical, canonical_to_input);
}
C_DECL int
rt_arb_canonicalize(struct rt_db_internal *canonical,
mat_t canonical_to_input,
const struct rt_db_internal *input,
const struct bn_tol *tol,
enum rt_canonicalize_mode mode)
{
const struct rt_arb_internal *aip;
struct rt_arb_internal *caip;
mat_t input_to_canonical;
int frame_ok;
int validation_issues = 0;
if (!canonical || !canonical_to_input || !input || !tol)
return RT_CANONICALIZE_ERROR;
if (mode < RT_CANONICALIZE_RIGID || mode > RT_CANONICALIZE_AFFINE)
return RT_CANONICALIZE_ERROR;
if (input->idb_type != ID_ARB8)
return RT_CANONICALIZE_ERROR;
aip = (const struct rt_arb_internal *)input->idb_ptr;
RT_ARB_CK_MAGIC(aip);
for (int i = 0; i < 8; i++) {
if (!isfinite(aip->pt[i][X]) || !isfinite(aip->pt[i][Y]) ||
!isfinite(aip->pt[i][Z]))
return RT_CANONICALIZE_ERROR;
}
(void)rt_arb_validate(NULL, aip, tol, &validation_issues);
/* Historic databases contain usable non-standard vertex encodings. The
* index-stable factorization preserves those encodings exactly. */
if (validation_issues & ~RT_ARB_VALIDATE_NONSTANDARD)
return RT_CANONICALIZE_ERROR;
if (mode == RT_CANONICALIZE_AFFINE)
frame_ok = arb_affine_frame(canonical_to_input, input_to_canonical, aip, tol);
else
frame_ok = arb_rigid_frame(canonical_to_input, input_to_canonical,
aip, tol, mode);
if (!frame_ok)
return RT_CANONICALIZE_ERROR;
canonical->idb_major_type = DB5_MAJORTYPE_BRLCAD;
canonical->idb_minor_type = ID_ARB8;
canonical->idb_meth = &OBJ[ID_ARB8];
BU_ALLOC(canonical->idb_ptr, struct rt_arb_internal);
caip = (struct rt_arb_internal *)canonical->idb_ptr;
caip->magic = RT_ARB_INTERNAL_MAGIC;
for (int i = 0; i < 8; i++)
MAT4X3PNT(caip->pt[i], input_to_canonical, aip->pt[i]);
return RT_CANONICALIZE_OK;
}
/**
* Import an arb from the db5 format and convert to the internal
* structure. Code duplicated from rt_arb_import4() with db5 help from
@@ -0,0 +1,283 @@
/* B O T _ C A N O N I C A L I Z E . 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.
*/
#include "common.h"
#include <cmath>
#include "bg/pca.h"
#include "raytrace.h"
#include "rt/primitives/bot.h"
namespace {
constexpr size_t FRAME_AXIS_COUNT = 3;
constexpr size_t FRAME_PERMUTATION_COUNT = 6;
const size_t frame_permutations[FRAME_PERMUTATION_COUNT][FRAME_AXIS_COUNT] = {
{0, 1, 2},
{0, 2, 1},
{1, 0, 2},
{1, 2, 0},
{2, 0, 1},
{2, 1, 0}
};
const int frame_permutation_parity[FRAME_PERMUTATION_COUNT] = {
1, -1, -1, 1, 1, -1
};
bool
bot_input_valid(const struct rt_bot_internal *bot)
{
if (!bot || bot->num_vertices < 3 || !bot->vertices ||
(bot->num_faces && !bot->faces))
return false;
for (size_t i = 0; i < bot->num_vertices; i++) {
if (VINVALID(&bot->vertices[3 * i]))
return false;
}
for (size_t i = 0; i < 3 * bot->num_faces; i++) {
if (bot->faces[i] < 0 || static_cast<size_t>(bot->faces[i]) >= bot->num_vertices)
return false;
}
return true;
}
void
proper_pca_frame(vect_t axes[FRAME_AXIS_COUNT])
{
vect_t proper_z;
VCROSS(proper_z, axes[X], axes[Y]);
VUNITIZE(proper_z);
VMOVE(axes[Z], proper_z);
}
bool
pca_frame_is_unique(const vect_t singular_values, size_t point_count,
const struct bn_tol *tol)
{
fastf_t singular_tolerance = tol->dist * std::sqrt(static_cast<fastf_t>(point_count));
return !NEAR_EQUAL(singular_values[X], singular_values[Y], singular_tolerance) &&
!NEAR_EQUAL(singular_values[Y], singular_values[Z], singular_tolerance);
}
bool
anchor_frame(vect_t axes[FRAME_AXIS_COUNT], const point_t center,
const struct rt_bot_internal *bot, const struct bn_tol *tol)
{
vect_t offset;
vect_t residual;
fastf_t farthest_sq = -1.0;
fastf_t widest_sq = -1.0;
size_t farthest = 0;
size_t widest = 0;
for (size_t i = 0; i < bot->num_vertices; i++) {
VSUB2(offset, &bot->vertices[3 * i], center);
fastf_t distance_sq = MAGSQ(offset);
if (distance_sq > farthest_sq + tol->dist_sq) {
farthest_sq = distance_sq;
farthest = i;
}
}
if (farthest_sq <= tol->dist_sq)
return false;
VSUB2(axes[X], &bot->vertices[3 * farthest], center);
VUNITIZE(axes[X]);
for (size_t i = 0; i < bot->num_vertices; i++) {
VSUB2(offset, &bot->vertices[3 * i], center);
VJOIN1(residual, offset, -VDOT(offset, axes[X]), axes[X]);
fastf_t residual_sq = MAGSQ(residual);
if (residual_sq > widest_sq + tol->dist_sq) {
widest_sq = residual_sq;
widest = i;
}
}
if (widest_sq <= tol->dist_sq)
return false;
VSUB2(offset, &bot->vertices[3 * widest], center);
VJOIN1(axes[Y], offset, -VDOT(offset, axes[X]), axes[X]);
VUNITIZE(axes[Y]);
VCROSS(axes[Z], axes[X], axes[Y]);
VUNITIZE(axes[Z]);
return true;
}
void
frame_matrix(mat_t matrix, const vect_t axes[FRAME_AXIS_COUNT],
const size_t permutation[FRAME_AXIS_COUNT], const int signs[FRAME_AXIS_COUNT],
const point_t center)
{
mat_t rotate;
mat_t translate;
MAT_IDN(rotate);
for (size_t row = 0; row < FRAME_AXIS_COUNT; row++)
VSCALE(&rotate[4 * row], axes[permutation[row]], signs[row]);
MAT_IDN(translate);
MAT_DELTAS_VEC_NEG(translate, center);
bn_mat_mul(matrix, rotate, translate);
}
fastf_t
comparison_coordinate(fastf_t coordinate, const struct bn_tol *tol)
{
return NEAR_ZERO(coordinate, tol->dist) ? 0.0 : coordinate;
}
bool
candidate_is_less(const mat_t candidate, const mat_t current,
const struct rt_bot_internal *bot, const struct bn_tol *tol)
{
point_t candidate_point;
point_t current_point;
for (size_t i = 0; i < bot->num_vertices; i++) {
MAT4X3PNT(candidate_point, candidate, &bot->vertices[3 * i]);
MAT4X3PNT(current_point, current, &bot->vertices[3 * i]);
for (size_t coordinate = 0; coordinate < ELEMENTS_PER_POINT; coordinate++) {
fastf_t candidate_value = comparison_coordinate(candidate_point[coordinate], tol);
fastf_t current_value = comparison_coordinate(current_point[coordinate], tol);
if (candidate_value < current_value - tol->dist)
return true;
if (candidate_value > current_value + tol->dist)
return false;
}
}
return false;
}
}
extern "C" int
rt_bot_canonicalize(struct rt_db_internal *canonical,
mat_t canonical_to_input,
const struct rt_db_internal *input,
const struct bn_tol *tol,
enum rt_canonicalize_mode mode)
{
const struct rt_bot_internal *bot;
struct rt_bot_internal *canonical_bot;
struct rt_db_internal transformed;
point_t center;
vect_t axes[FRAME_AXIS_COUNT];
vect_t singular_values;
mat_t candidate;
mat_t input_to_canonical;
bool have_candidate = false;
if (!canonical || !canonical_to_input || !input || !tol)
return RT_CANONICALIZE_ERROR;
if (mode < RT_CANONICALIZE_RIGID || mode > RT_CANONICALIZE_AFFINE)
return RT_CANONICALIZE_ERROR;
if (input->idb_type != ID_BOT)
return RT_CANONICALIZE_ERROR;
bot = static_cast<const struct rt_bot_internal *>(input->idb_ptr);
RT_BOT_CK_MAGIC(bot);
if (!bot_input_valid(bot))
return RT_CANONICALIZE_ERROR;
/* rt_bot_mat currently reconstructs only the common per-vertex normal
* layout. Decline other layouts until that transform contract is fixed. */
if ((bot->bot_flags & RT_BOT_HAS_SURFACE_NORMALS) &&
bot->num_normals != bot->num_vertices)
return RT_CANONICALIZE_UNSUPPORTED;
if (bg_pca_svd(&center, &axes[X], &axes[Y], &axes[Z], &singular_values,
bot->num_vertices, reinterpret_cast<const point_t *>(bot->vertices)) != BRLCAD_OK)
return RT_CANONICALIZE_ERROR;
if (pca_frame_is_unique(singular_values, bot->num_vertices, tol)) {
proper_pca_frame(axes);
} else if (!anchor_frame(axes, center, bot, tol)) {
return RT_CANONICALIZE_ERROR;
}
/* PCA axis signs are arbitrary, and equivalent parameterizations may
* permute axes. Search all proper signed permutations and retain the
* lexicographically smallest vertex sequence. This preserves BOT index
* topology while making pushed copies choose the same local frame. */
for (size_t permutation = 0; permutation < FRAME_PERMUTATION_COUNT; permutation++) {
for (int xsign = -1; xsign <= 1; xsign += 2) {
for (int ysign = -1; ysign <= 1; ysign += 2) {
int signs[FRAME_AXIS_COUNT] = {
xsign,
ysign,
frame_permutation_parity[permutation] * xsign * ysign
};
frame_matrix(candidate, axes, frame_permutations[permutation], signs, center);
if (!have_candidate || candidate_is_less(candidate, input_to_canonical, bot, tol)) {
MAT_COPY(input_to_canonical, candidate);
have_candidate = true;
}
}
}
}
canonical_bot = rt_bot_dup(bot);
if (!canonical_bot)
return RT_CANONICALIZE_ERROR;
RT_DB_INTERNAL_INIT(&transformed);
transformed.idb_major_type = DB5_MAJORTYPE_BRLCAD;
transformed.idb_minor_type = ID_BOT;
transformed.idb_meth = &OBJ[ID_BOT];
transformed.idb_ptr = canonical_bot;
if (OBJ[ID_BOT].ft_mat(&transformed, input_to_canonical, input) != BRLCAD_OK) {
rt_bot_internal_free(canonical_bot);
return RT_CANONICALIZE_ERROR;
}
bn_mat_inv(canonical_to_input, input_to_canonical);
canonical->idb_major_type = DB5_MAJORTYPE_BRLCAD;
canonical->idb_minor_type = ID_BOT;
canonical->idb_meth = &OBJ[ID_BOT];
canonical->idb_ptr = canonical_bot;
return RT_CANONICALIZE_OK;
}
/*
* Local Variables:
* mode: C++
* tab-width: 8
* indent-tabs-mode: t
* c-file-style: "stroustrup"
* End:
* ex: shiftwidth=4 tabstop=8
*/
+93
View File
@@ -0,0 +1,93 @@
/* C A N O N I C A L I Z 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.
*/
#include "common.h"
#include <math.h>
#include "bn/mat.h"
#include "vmath.h"
#include "canonicalize_private.h"
int
rt_make_canonical_similarity_frame(mat_t canonical_to_input,
fastf_t *canonical_lengths, const point_t origin,
const vect_t input_x, const vect_t input_z, const fastf_t *lengths,
size_t length_count, const struct bn_tol *tol,
enum rt_canonicalize_mode mode)
{
vect_t x_axis;
vect_t y_axis;
vect_t z_axis;
fastf_t x_length;
fastf_t z_length;
fastf_t projection;
fastf_t uniform_scale = mode == RT_CANONICALIZE_RIGID ? 1.0 : 0.0;
mat_t orientation;
mat_t scale;
mat_t oriented_scale;
mat_t translate;
if (!canonical_to_input || !canonical_lengths || !origin || !input_x ||
!input_z || !lengths || !length_count || !tol ||
mode < RT_CANONICALIZE_RIGID || mode > RT_CANONICALIZE_AFFINE)
return 0;
if (!isfinite(origin[X]) || !isfinite(origin[Y]) ||
!isfinite(origin[Z]))
return 0;
x_length = MAGNITUDE(input_x);
z_length = MAGNITUDE(input_z);
if (!isfinite(x_length) || !isfinite(z_length) ||
x_length <= tol->dist || z_length <= tol->dist)
return 0;
VSCALE(x_axis, input_x, 1.0 / x_length);
VSCALE(z_axis, input_z, 1.0 / z_length);
projection = VDOT(x_axis, z_axis);
if (!NEAR_ZERO(projection, tol->perp))
return 0;
/* Remove accepted input roundoff so write-mode matrices are proper
* rotations rather than barely-sheared approximations. */
VJOIN1(x_axis, x_axis, -projection, z_axis);
VUNITIZE(x_axis);
VCROSS(y_axis, z_axis, x_axis);
VUNITIZE(y_axis);
for (size_t i = 0; i < length_count; i++) {
if (!isfinite(lengths[i]) || lengths[i] <= tol->dist)
return 0;
if (mode != RT_CANONICALIZE_RIGID)
uniform_scale = fmax(uniform_scale, lengths[i]);
}
for (size_t i = 0; i < length_count; i++)
canonical_lengths[i] = lengths[i] / uniform_scale;
MAT_IDN(orientation);
orientation[0] = x_axis[X];
orientation[4] = x_axis[Y];
orientation[8] = x_axis[Z];
orientation[1] = y_axis[X];
orientation[5] = y_axis[Y];
orientation[9] = y_axis[Z];
orientation[2] = z_axis[X];
orientation[6] = z_axis[Y];
orientation[10] = z_axis[Z];
MAT_IDN(scale);
scale[15] = 1.0 / uniform_scale;
bn_mat_mul(oriented_scale, orientation, scale);
MAT_IDN(translate);
MAT_DELTAS_VEC(translate, origin);
bn_mat_mul(canonical_to_input, translate, oriented_scale);
return 1;
}
@@ -0,0 +1,33 @@
/* C A N O N I C A L I Z E _ P R I V A T E . H
* 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.
*/
#ifndef LIBRT_PRIMITIVES_CANONICALIZE_PRIVATE_H
#define LIBRT_PRIMITIVES_CANONICALIZE_PRIVATE_H
#include "common.h"
#include <stddef.h>
#include "rt/functab.h"
/* Construct a right-handed frame whose local X and Z axes follow two
* perpendicular primitive directions. Scalar-radius primitives cannot
* represent a nonuniform affine transform in ft_mat, so affine requests use
* the strongest exactly reconstructable subset: a similarity transform. */
int
rt_make_canonical_similarity_frame(mat_t canonical_to_input,
fastf_t *canonical_lengths, const point_t origin,
const vect_t input_x, const vect_t input_z, const fastf_t *lengths,
size_t length_count, const struct bn_tol *tol,
enum rt_canonicalize_mode mode);
#endif /* LIBRT_PRIMITIVES_CANONICALIZE_PRIVATE_H */
+43
View File
@@ -162,6 +162,7 @@
#include "raytrace.h"
#include "../../librt_private.h"
#include "../canonicalize_private.h"
struct ehy_specific {
@@ -2118,6 +2119,48 @@ rt_ehy_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
return BRLCAD_OK;
}
C_DECL int
rt_ehy_canonicalize(struct rt_db_internal *canonical,
mat_t canonical_to_input,
const struct rt_db_internal *input,
const struct bn_tol *tol,
enum rt_canonicalize_mode mode)
{
const struct rt_ehy_internal *eip;
struct rt_ehy_internal *ceip;
fastf_t lengths[4];
fastf_t canonical_lengths[4];
if (!canonical || !canonical_to_input || !input || !tol ||
input->idb_type != ID_EHY)
return RT_CANONICALIZE_ERROR;
eip = (const struct rt_ehy_internal *)input->idb_ptr;
RT_EHY_CK_MAGIC(eip);
lengths[0] = MAGNITUDE(eip->ehy_H);
lengths[1] = eip->ehy_r1;
lengths[2] = eip->ehy_r2;
lengths[3] = eip->ehy_c;
if (!rt_make_canonical_similarity_frame(canonical_to_input,
canonical_lengths, eip->ehy_V, eip->ehy_Au, eip->ehy_H,
lengths, 4, tol, mode))
return RT_CANONICALIZE_ERROR;
canonical->idb_major_type = DB5_MAJORTYPE_BRLCAD;
canonical->idb_minor_type = ID_EHY;
canonical->idb_meth = &OBJ[ID_EHY];
BU_ALLOC(canonical->idb_ptr, struct rt_ehy_internal);
ceip = (struct rt_ehy_internal *)canonical->idb_ptr;
ceip->ehy_magic = RT_EHY_INTERNAL_MAGIC;
VSETALL(ceip->ehy_V, 0.0);
VSET(ceip->ehy_H, 0.0, 0.0, canonical_lengths[0]);
VSET(ceip->ehy_Au, 1.0, 0.0, 0.0);
ceip->ehy_r1 = canonical_lengths[1];
ceip->ehy_r2 = canonical_lengths[2];
ceip->ehy_c = canonical_lengths[3];
return RT_CANONICALIZE_OK;
}
int
rt_ehy_functab_validate(struct bu_vls *error_msg, const struct rt_db_internal *ip, const struct bn_tol *tol)
{
+150
View File
@@ -1423,6 +1423,156 @@ rt_ell_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
return BRLCAD_OK;
}
struct ell_canonical_axis {
vect_t dir;
fastf_t length;
size_t input_index;
};
static void
ell_canonical_axis_swap(struct ell_canonical_axis *a, struct ell_canonical_axis *b)
{
struct ell_canonical_axis tmp;
VMOVE(tmp.dir, a->dir);
tmp.length = a->length;
tmp.input_index = a->input_index;
VMOVE(a->dir, b->dir);
a->length = b->length;
a->input_index = b->input_index;
VMOVE(b->dir, tmp.dir);
b->length = tmp.length;
b->input_index = tmp.input_index;
}
static void
ell_canonical_axes_sort(struct ell_canonical_axis axes[3], const struct bn_tol *tol)
{
for (size_t i = 0; i < 2; i++) {
for (size_t j = i + 1; j < 3; j++) {
if (axes[j].length > axes[i].length + tol->dist ||
(NEAR_EQUAL(axes[j].length, axes[i].length, tol->dist) &&
axes[j].input_index < axes[i].input_index))
ell_canonical_axis_swap(&axes[i], &axes[j]);
}
}
}
static void
ell_canonical_orientation(mat_t orientation, const struct ell_canonical_axis axes[3])
{
MAT_IDN(orientation);
orientation[0] = axes[0].dir[X];
orientation[4] = axes[0].dir[Y];
orientation[8] = axes[0].dir[Z];
orientation[1] = axes[1].dir[X];
orientation[5] = axes[1].dir[Y];
orientation[9] = axes[1].dir[Z];
orientation[2] = axes[2].dir[X];
orientation[6] = axes[2].dir[Y];
orientation[10] = axes[2].dir[Z];
}
C_DECL int
rt_ell_canonicalize(struct rt_db_internal *canonical,
mat_t canonical_to_input,
const struct rt_db_internal *input,
const struct bn_tol *tol,
enum rt_canonicalize_mode mode)
{
const struct rt_ell_internal *eip;
struct rt_ell_internal *ceip;
struct ell_canonical_axis axes[3];
const fastf_t *input_axes[3];
vect_t cross;
mat_t orientation;
mat_t scale_mat;
mat_t oriented_scale;
mat_t translate;
fastf_t canonical_lengths[3];
fastf_t uniform_scale = 1.0;
if (!canonical || !canonical_to_input || !input || !tol)
return RT_CANONICALIZE_ERROR;
if (mode < RT_CANONICALIZE_RIGID || mode > RT_CANONICALIZE_AFFINE)
return RT_CANONICALIZE_ERROR;
if (input->idb_type != ID_ELL && input->idb_type != ID_SPH)
return RT_CANONICALIZE_ERROR;
eip = (const struct rt_ell_internal *)input->idb_ptr;
RT_ELL_CK_MAGIC(eip);
input_axes[0] = eip->a;
input_axes[1] = eip->b;
input_axes[2] = eip->c;
for (size_t i = 0; i < 3; i++) {
axes[i].length = MAGNITUDE(input_axes[i]);
if (axes[i].length <= tol->dist)
return RT_CANONICALIZE_ERROR;
VSCALE(axes[i].dir, input_axes[i], 1.0 / axes[i].length);
axes[i].input_index = i;
}
if (!NEAR_ZERO(VDOT(axes[0].dir, axes[1].dir), tol->perp) ||
!NEAR_ZERO(VDOT(axes[0].dir, axes[2].dir), tol->perp) ||
!NEAR_ZERO(VDOT(axes[1].dir, axes[2].dir), tol->perp))
return RT_CANONICALIZE_ERROR;
ell_canonical_axes_sort(axes, tol);
/* Axis signs do not alter ellipsoid geometry. Select the sign of the
* third axis so the placement matrix is a proper rotation. */
VCROSS(cross, axes[0].dir, axes[1].dir);
if (VDOT(cross, axes[2].dir) < 0.0)
VREVERSE(axes[2].dir, axes[2].dir);
if (mode == RT_CANONICALIZE_AFFINE) {
VSETALL(canonical_lengths, 1.0);
for (size_t i = 0; i < 3; i++)
VSCALE(axes[i].dir, axes[i].dir, axes[i].length);
ell_canonical_orientation(orientation, axes);
MAT_IDN(translate);
MAT_DELTAS_VEC(translate, eip->v);
bn_mat_mul(canonical_to_input, translate, orientation);
} else {
if (mode == RT_CANONICALIZE_SIMILARITY)
uniform_scale = axes[0].length;
for (size_t i = 0; i < 3; i++)
canonical_lengths[i] = axes[i].length / uniform_scale;
ell_canonical_orientation(orientation, axes);
MAT_IDN(scale_mat);
scale_mat[15] = 1.0 / uniform_scale;
bn_mat_mul(oriented_scale, orientation, scale_mat);
MAT_IDN(translate);
MAT_DELTAS_VEC(translate, eip->v);
bn_mat_mul(canonical_to_input, translate, oriented_scale);
}
canonical->idb_major_type = DB5_MAJORTYPE_BRLCAD;
canonical->idb_minor_type = input->idb_minor_type;
canonical->idb_meth = &OBJ[input->idb_minor_type];
BU_ALLOC(canonical->idb_ptr, struct rt_ell_internal);
ceip = (struct rt_ell_internal *)canonical->idb_ptr;
ceip->magic = RT_ELL_INTERNAL_MAGIC;
VSETALL(ceip->v, 0.0);
VSET(ceip->a, canonical_lengths[0], 0.0, 0.0);
VSET(ceip->b, 0.0, canonical_lengths[1], 0.0);
VSET(ceip->c, 0.0, 0.0, canonical_lengths[2]);
return RT_CANONICALIZE_OK;
}
/**
* Import an ellipsoid/sphere from the database format to the internal
* structure. Apply modeling transformations as well.
+41
View File
@@ -159,6 +159,7 @@
#include "raytrace.h"
#include "../../librt_private.h"
#include "../canonicalize_private.h"
static int epa_is_valid(struct rt_epa_internal *epa);
@@ -2044,6 +2045,46 @@ rt_epa_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
return BRLCAD_OK;
}
C_DECL int
rt_epa_canonicalize(struct rt_db_internal *canonical,
mat_t canonical_to_input,
const struct rt_db_internal *input,
const struct bn_tol *tol,
enum rt_canonicalize_mode mode)
{
const struct rt_epa_internal *eip;
struct rt_epa_internal *ceip;
fastf_t lengths[3];
fastf_t canonical_lengths[3];
if (!canonical || !canonical_to_input || !input || !tol ||
input->idb_type != ID_EPA)
return RT_CANONICALIZE_ERROR;
eip = (const struct rt_epa_internal *)input->idb_ptr;
RT_EPA_CK_MAGIC(eip);
lengths[0] = MAGNITUDE(eip->epa_H);
lengths[1] = eip->epa_r1;
lengths[2] = eip->epa_r2;
if (!rt_make_canonical_similarity_frame(canonical_to_input,
canonical_lengths, eip->epa_V, eip->epa_Au, eip->epa_H,
lengths, 3, tol, mode))
return RT_CANONICALIZE_ERROR;
canonical->idb_major_type = DB5_MAJORTYPE_BRLCAD;
canonical->idb_minor_type = ID_EPA;
canonical->idb_meth = &OBJ[ID_EPA];
BU_ALLOC(canonical->idb_ptr, struct rt_epa_internal);
ceip = (struct rt_epa_internal *)canonical->idb_ptr;
ceip->epa_magic = RT_EPA_INTERNAL_MAGIC;
VSETALL(ceip->epa_V, 0.0);
VSET(ceip->epa_H, 0.0, 0.0, canonical_lengths[0]);
VSET(ceip->epa_Au, 1.0, 0.0, 0.0);
ceip->epa_r1 = canonical_lengths[1];
ceip->epa_r2 = canonical_lengths[2];
return RT_CANONICALIZE_OK;
}
/**
* Import an EPA from the database format to the internal format.
* Apply modeling transformations as well.
+111
View File
@@ -1931,6 +1931,117 @@ rt_eto_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
}
C_DECL int
rt_eto_canonicalize(struct rt_db_internal *canonical,
mat_t canonical_to_input,
const struct rt_db_internal *input,
const struct bn_tol *tol,
enum rt_canonicalize_mode mode)
{
const struct rt_eto_internal *eip;
struct rt_eto_internal *ceip;
vect_t normal, horizontal, xaxis, yaxis;
mat_t orientation;
mat_t scale_mat;
mat_t oriented_scale;
mat_t translate;
fastf_t normal_mag;
fastf_t major_mag;
fastf_t vertical_mag;
fastf_t horizontal_mag;
fastf_t horizontal_sq;
fastf_t uniform_scale = 1.0;
if (!canonical || !canonical_to_input || !input || !tol)
return RT_CANONICALIZE_ERROR;
if (mode < RT_CANONICALIZE_RIGID || mode > RT_CANONICALIZE_AFFINE)
return RT_CANONICALIZE_ERROR;
if (input->idb_type != ID_ETO)
return RT_CANONICALIZE_ERROR;
eip = (const struct rt_eto_internal *)input->idb_ptr;
RT_ETO_CK_MAGIC(eip);
normal_mag = MAGNITUDE(eip->eto_N);
major_mag = MAGNITUDE(eip->eto_C);
if (!isfinite(eip->eto_V[X]) || !isfinite(eip->eto_V[Y]) ||
!isfinite(eip->eto_V[Z]) || !isfinite(normal_mag) ||
!isfinite(major_mag) || !isfinite(eip->eto_r) ||
!isfinite(eip->eto_rd) || normal_mag <= tol->dist ||
major_mag <= tol->dist || eip->eto_r <= tol->dist ||
eip->eto_rd <= tol->dist || eip->eto_rd > major_mag)
return RT_CANONICALIZE_ERROR;
VSCALE(normal, eip->eto_N, 1.0 / normal_mag);
vertical_mag = VDOT(eip->eto_C, normal);
if (vertical_mag < 0.0) {
VREVERSE(normal, normal);
vertical_mag = -vertical_mag;
}
VJOIN1(horizontal, eip->eto_C, -vertical_mag, normal);
horizontal_sq = MAGSQ(horizontal);
horizontal_mag = sqrt(fmax(0.0, horizontal_sq));
if (horizontal_mag > SMALL_FASTF) {
VSCALE(xaxis, horizontal, 1.0 / horizontal_mag);
} else {
/* A vertical C leaves rotation around N unconstrained. Project the
* least-aligned world axis so canonical input still produces an identity
* placement while other orientations remain numerically stable. */
vect_t reference = VINIT_ZERO;
if (fabs(normal[X]) <= fabs(normal[Y]) && fabs(normal[X]) <= fabs(normal[Z]))
reference[X] = 1.0;
else if (fabs(normal[Y]) <= fabs(normal[Z]))
reference[Y] = 1.0;
else
reference[Z] = 1.0;
VJOIN1(xaxis, reference, -VDOT(reference, normal), normal);
VUNITIZE(xaxis);
VSETALL(horizontal, 0.0);
horizontal_mag = 0.0;
}
VCROSS(yaxis, normal, xaxis);
VUNITIZE(yaxis);
/* rt_eto_mat can apply only rigid and uniform-scale placements to r and
* rd. AFFINE therefore intentionally uses the strongest transform that
* this primitive can reconstruct exactly: a similarity. */
if (mode != RT_CANONICALIZE_RIGID)
uniform_scale = eip->eto_r;
MAT_IDN(orientation);
orientation[0] = xaxis[X];
orientation[4] = xaxis[Y];
orientation[8] = xaxis[Z];
orientation[1] = yaxis[X];
orientation[5] = yaxis[Y];
orientation[9] = yaxis[Z];
orientation[2] = normal[X];
orientation[6] = normal[Y];
orientation[10] = normal[Z];
MAT_IDN(scale_mat);
scale_mat[15] = 1.0 / uniform_scale;
bn_mat_mul(oriented_scale, orientation, scale_mat);
MAT_IDN(translate);
MAT_DELTAS_VEC(translate, eip->eto_V);
bn_mat_mul(canonical_to_input, translate, oriented_scale);
canonical->idb_major_type = DB5_MAJORTYPE_BRLCAD;
canonical->idb_minor_type = ID_ETO;
canonical->idb_meth = &OBJ[ID_ETO];
BU_ALLOC(canonical->idb_ptr, struct rt_eto_internal);
ceip = (struct rt_eto_internal *)canonical->idb_ptr;
ceip->eto_magic = RT_ETO_INTERNAL_MAGIC;
VSETALL(ceip->eto_V, 0.0);
VSET(ceip->eto_N, 0.0, 0.0, 1.0);
VSET(ceip->eto_C, horizontal_mag / uniform_scale, 0.0,
vertical_mag / uniform_scale);
ceip->eto_r = eip->eto_r / uniform_scale;
ceip->eto_rd = eip->eto_rd / uniform_scale;
return RT_CANONICALIZE_OK;
}
/**
* Import a eto from the database format to the internal format.
* Apply modeling transformations at the same time.
+55
View File
@@ -653,6 +653,61 @@ rt_hlf_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
return 0; /* OK */
}
C_DECL int
rt_hlf_canonicalize(struct rt_db_internal *canonical,
mat_t canonical_to_input,
const struct rt_db_internal *input,
const struct bn_tol *tol,
enum rt_canonicalize_mode mode)
{
struct rt_half_internal *hip;
struct rt_half_internal *chip;
vect_t normal;
vect_t zaxis = VINIT_ZERO;
point_t plane_point;
mat_t rotate;
mat_t translate;
fastf_t normal_mag;
fastf_t plane_dist;
if (!canonical || !canonical_to_input || !input || !tol)
return RT_CANONICALIZE_ERROR;
if (mode < RT_CANONICALIZE_RIGID || mode > RT_CANONICALIZE_AFFINE)
return RT_CANONICALIZE_ERROR;
if (input->idb_type != ID_HALF)
return RT_CANONICALIZE_ERROR;
hip = (struct rt_half_internal *)input->idb_ptr;
RT_HALF_CK_MAGIC(hip);
VMOVE(normal, hip->eqn);
normal_mag = MAGNITUDE(normal);
if (normal_mag <= tol->dist)
return RT_CANONICALIZE_ERROR;
VSCALE(normal, normal, 1.0 / normal_mag);
plane_dist = hip->eqn[W] / normal_mag;
VSCALE(plane_point, normal, plane_dist);
VSET(zaxis, 0.0, 0.0, 1.0);
bn_mat_fromto(rotate, zaxis, normal, tol);
MAT_IDN(translate);
MAT_DELTAS_VEC(translate, plane_point);
bn_mat_mul(canonical_to_input, translate, rotate);
canonical->idb_major_type = DB5_MAJORTYPE_BRLCAD;
canonical->idb_minor_type = ID_HALF;
canonical->idb_meth = &OBJ[ID_HALF];
BU_ALLOC(canonical->idb_ptr, struct rt_half_internal);
chip = (struct rt_half_internal *)canonical->idb_ptr;
chip->magic = RT_HALF_INTERNAL_MAGIC;
VSET(chip->eqn, 0.0, 0.0, 1.0);
chip->eqn[W] = 0.0;
return RT_CANONICALIZE_OK;
}
C_DECL int
rt_hlf_import5(struct rt_db_internal *ip, const struct bu_external *ep, register const fastf_t *mat, const struct db_i *dbip)
{
+68
View File
@@ -0,0 +1,68 @@
/* O B J _ C A N O N I C A L I Z 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.
*/
#include "common.h"
#include "raytrace.h"
#include "rt/func.h"
int
rt_obj_canonicalize(struct rt_db_internal *canonical,
mat_t canonical_to_input,
const struct rt_db_internal *input,
const struct bn_tol *tol,
enum rt_canonicalize_mode mode)
{
const struct rt_functab *ft;
if (!canonical || !canonical_to_input || !input || !tol)
return RT_CANONICALIZE_ERROR;
RT_CK_DB_INTERNAL(canonical);
RT_CK_DB_INTERNAL(input);
BN_CK_TOL(tol);
if (canonical->idb_ptr || canonical->idb_meth ||
canonical->idb_major_type != -1 || canonical->idb_minor_type != -1)
return RT_CANONICALIZE_ERROR;
if (mode < RT_CANONICALIZE_RIGID || mode > RT_CANONICALIZE_AFFINE)
return RT_CANONICALIZE_ERROR;
if (input->idb_minor_type < 0 || input->idb_minor_type > ID_MAX_SOLID)
return RT_CANONICALIZE_ERROR;
ft = &OBJ[input->idb_minor_type];
if (!ft->ft_canonicalize)
return RT_CANONICALIZE_UNSUPPORTED;
return ft->ft_canonicalize(canonical, canonical_to_input, input, tol, mode);
}
/*
* Local Variables:
* mode: C
* tab-width: 8
* indent-tabs-mode: t
* c-file-style: "stroustrup"
* End:
* ex: shiftwidth=4 tabstop=8
*/
+105
View File
@@ -191,6 +191,7 @@
#include "raytrace.h"
#include "nmg.h"
#include "../../librt_private.h"
#include "../canonicalize_private.h"
struct part_specific {
@@ -1978,6 +1979,110 @@ rt_part_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inte
}
C_DECL int
rt_part_canonicalize(struct rt_db_internal *canonical,
mat_t canonical_to_input,
const struct rt_db_internal *input,
const struct bn_tol *tol,
enum rt_canonicalize_mode mode)
{
const struct rt_part_internal *pip;
struct rt_part_internal *cpip;
point_t origin;
vect_t axis;
vect_t x_axis;
fastf_t vertex_radius;
fastf_t height_radius;
fastf_t height_length;
fastf_t maximum_radius;
fastf_t uniform_scale;
if (!canonical || !canonical_to_input || !input || !tol ||
input->idb_type != ID_PARTICLE || mode < RT_CANONICALIZE_RIGID ||
mode > RT_CANONICALIZE_AFFINE)
return RT_CANONICALIZE_ERROR;
pip = (const struct rt_part_internal *)input->idb_ptr;
RT_PART_CK_MAGIC(pip);
if (!isfinite(pip->part_V[X]) || !isfinite(pip->part_V[Y]) ||
!isfinite(pip->part_V[Z]) || !isfinite(pip->part_H[X]) ||
!isfinite(pip->part_H[Y]) || !isfinite(pip->part_H[Z]) ||
!isfinite(pip->part_vrad) || !isfinite(pip->part_hrad) ||
pip->part_vrad < 0.0 || pip->part_hrad < 0.0)
return RT_CANONICALIZE_ERROR;
height_length = MAGNITUDE(pip->part_H);
maximum_radius = fmax(pip->part_vrad, pip->part_hrad);
if (!isfinite(height_length) || maximum_radius <= tol->dist)
return RT_CANONICALIZE_ERROR;
uniform_scale = mode == RT_CANONICALIZE_RIGID ? 1.0 :
fmax(height_length, maximum_radius);
canonical->idb_major_type = DB5_MAJORTYPE_BRLCAD;
canonical->idb_minor_type = ID_PARTICLE;
canonical->idb_meth = &OBJ[ID_PARTICLE];
BU_ALLOC(canonical->idb_ptr, struct rt_part_internal);
cpip = (struct rt_part_internal *)canonical->idb_ptr;
cpip->part_magic = RT_PART_INTERNAL_MAGIC;
cpip->part_type = pip->part_type;
VSETALL(cpip->part_V, 0.0);
/* Imported particle spheres have H cleared by rt_part_mat. Do not turn a
* merely short cone into a different solid just because its height falls
* below the caller's modeling tolerance; the frame helper will reject
* nonzero axes that are too short to orient reliably. */
if (height_length <= SMALL_FASTF) {
mat_t scale;
mat_t translate;
MAT_IDN(scale);
scale[15] = 1.0 / uniform_scale;
MAT_IDN(translate);
MAT_DELTAS_VEC(translate, pip->part_V);
bn_mat_mul(canonical_to_input, translate, scale);
VSETALL(cpip->part_H, 0.0);
cpip->part_vrad = maximum_radius / uniform_scale;
cpip->part_hrad = cpip->part_vrad;
cpip->part_type = RT_PARTICLE_TYPE_SPHERE;
return RT_CANONICALIZE_OK;
}
if (pip->part_vrad <= pip->part_hrad) {
VMOVE(origin, pip->part_V);
VMOVE(axis, pip->part_H);
vertex_radius = pip->part_vrad;
height_radius = pip->part_hrad;
} else {
VADD2(origin, pip->part_V, pip->part_H);
VREVERSE(axis, pip->part_H);
vertex_radius = pip->part_hrad;
height_radius = pip->part_vrad;
}
/* Canonical input must retain an identity placement. Other axial
* directions may use any deterministic perpendicular because PART is
* rotationally symmetric about H. */
if (NEAR_ZERO(axis[X], tol->perp) &&
NEAR_ZERO(axis[Y], tol->perp) && axis[Z] > 0.0)
VSET(x_axis, 1.0, 0.0, 0.0);
else
bn_vec_ortho(x_axis, axis);
{
const fastf_t frame_lengths[2] = {height_length, maximum_radius};
fastf_t canonical_frame_lengths[2];
if (!rt_make_canonical_similarity_frame(canonical_to_input,
canonical_frame_lengths, origin, x_axis, axis, frame_lengths, 2,
tol, mode)) {
rt_db_free_internal(canonical);
return RT_CANONICALIZE_ERROR;
}
}
VSET(cpip->part_H, 0.0, 0.0, height_length / uniform_scale);
cpip->part_vrad = vertex_radius / uniform_scale;
cpip->part_hrad = height_radius / uniform_scale;
return RT_CANONICALIZE_OK;
}
C_DECL int
rt_part_import5(struct rt_db_internal *ip, const struct bu_external *ep, register const fastf_t *mat, const struct db_i *dbip)
{
+42
View File
@@ -174,6 +174,7 @@
#include "raytrace.h"
#include "../../librt_private.h"
#include "../canonicalize_private.h"
static int
rhc_is_valid(struct rt_rhc_internal *rhc);
@@ -1932,6 +1933,47 @@ rt_rhc_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
return BRLCAD_OK;
}
C_DECL int
rt_rhc_canonicalize(struct rt_db_internal *canonical,
mat_t canonical_to_input,
const struct rt_db_internal *input,
const struct bn_tol *tol,
enum rt_canonicalize_mode mode)
{
const struct rt_rhc_internal *rip;
struct rt_rhc_internal *crip;
fastf_t lengths[4];
fastf_t canonical_lengths[4];
if (!canonical || !canonical_to_input || !input || !tol ||
input->idb_type != ID_RHC)
return RT_CANONICALIZE_ERROR;
rip = (const struct rt_rhc_internal *)input->idb_ptr;
RT_RHC_CK_MAGIC(rip);
lengths[0] = MAGNITUDE(rip->rhc_B);
lengths[1] = MAGNITUDE(rip->rhc_H);
lengths[2] = rip->rhc_r;
lengths[3] = rip->rhc_c;
if (!rt_make_canonical_similarity_frame(canonical_to_input,
canonical_lengths, rip->rhc_V, rip->rhc_B, rip->rhc_H, lengths,
4, tol, mode))
return RT_CANONICALIZE_ERROR;
canonical->idb_major_type = DB5_MAJORTYPE_BRLCAD;
canonical->idb_minor_type = ID_RHC;
canonical->idb_meth = &OBJ[ID_RHC];
BU_ALLOC(canonical->idb_ptr, struct rt_rhc_internal);
crip = (struct rt_rhc_internal *)canonical->idb_ptr;
crip->rhc_magic = RT_RHC_INTERNAL_MAGIC;
VSETALL(crip->rhc_V, 0.0);
VSET(crip->rhc_B, canonical_lengths[0], 0.0, 0.0);
VSET(crip->rhc_H, 0.0, 0.0, canonical_lengths[1]);
crip->rhc_r = canonical_lengths[2];
crip->rhc_c = canonical_lengths[3];
return RT_CANONICALIZE_OK;
}
/**
* Import an RHC from the database format to the internal format.
* Apply modeling transformations as well.
+40
View File
@@ -172,6 +172,7 @@
#include "raytrace.h"
#include "../../librt_private.h"
#include "../canonicalize_private.h"
#if defined(HAVE_ASINH) && !defined(HAVE_DECL_ASINH)
extern double asinh(double x);
@@ -1719,6 +1720,45 @@ rt_rpc_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
return BRLCAD_OK;
}
C_DECL int
rt_rpc_canonicalize(struct rt_db_internal *canonical,
mat_t canonical_to_input,
const struct rt_db_internal *input,
const struct bn_tol *tol,
enum rt_canonicalize_mode mode)
{
const struct rt_rpc_internal *rip;
struct rt_rpc_internal *crip;
fastf_t lengths[3];
fastf_t canonical_lengths[3];
if (!canonical || !canonical_to_input || !input || !tol ||
input->idb_type != ID_RPC)
return RT_CANONICALIZE_ERROR;
rip = (const struct rt_rpc_internal *)input->idb_ptr;
RT_RPC_CK_MAGIC(rip);
lengths[0] = MAGNITUDE(rip->rpc_B);
lengths[1] = MAGNITUDE(rip->rpc_H);
lengths[2] = rip->rpc_r;
if (!rt_make_canonical_similarity_frame(canonical_to_input,
canonical_lengths, rip->rpc_V, rip->rpc_B, rip->rpc_H, lengths,
3, tol, mode))
return RT_CANONICALIZE_ERROR;
canonical->idb_major_type = DB5_MAJORTYPE_BRLCAD;
canonical->idb_minor_type = ID_RPC;
canonical->idb_meth = &OBJ[ID_RPC];
BU_ALLOC(canonical->idb_ptr, struct rt_rpc_internal);
crip = (struct rt_rpc_internal *)canonical->idb_ptr;
crip->rpc_magic = RT_RPC_INTERNAL_MAGIC;
VSETALL(crip->rpc_V, 0.0);
VSET(crip->rpc_B, canonical_lengths[0], 0.0, 0.0);
VSET(crip->rpc_H, 0.0, 0.0, canonical_lengths[1]);
crip->rpc_r = canonical_lengths[2];
return RT_CANONICALIZE_OK;
}
/**
* Import an RPC from the database format to the internal format.
* Apply modeling transformations as well.
+114
View File
@@ -998,11 +998,125 @@ rt_superell_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_
MAT4X3VEC(top->a, mat, sa);
MAT4X3VEC(top->b, mat, sb);
MAT4X3VEC(top->c, mat, sc);
top->n = tip->n;
top->e = tip->e;
return BRLCAD_OK;
}
static void
superell_canonical_orientation(mat_t orientation, const vect_t a,
const vect_t b, const vect_t c)
{
MAT_IDN(orientation);
orientation[0] = a[X];
orientation[4] = a[Y];
orientation[8] = a[Z];
orientation[1] = b[X];
orientation[5] = b[Y];
orientation[9] = b[Z];
orientation[2] = c[X];
orientation[6] = c[Y];
orientation[10] = c[Z];
}
C_DECL int
rt_superell_canonicalize(struct rt_db_internal *canonical,
mat_t canonical_to_input,
const struct rt_db_internal *input,
const struct bn_tol *tol,
enum rt_canonicalize_mode mode)
{
static const fastf_t maximum_curvature = 10000.0;
const struct rt_superell_internal *sip;
struct rt_superell_internal *csip;
const fastf_t *input_axes[3];
vect_t axes[3];
vect_t cross;
fastf_t lengths[3];
fastf_t canonical_lengths[3];
fastf_t uniform_scale = 1.0;
mat_t orientation;
mat_t scale;
mat_t oriented_scale;
mat_t translate;
if (!canonical || !canonical_to_input || !input || !tol)
return RT_CANONICALIZE_ERROR;
if (mode < RT_CANONICALIZE_RIGID || mode > RT_CANONICALIZE_AFFINE)
return RT_CANONICALIZE_ERROR;
if (input->idb_type != ID_SUPERELL)
return RT_CANONICALIZE_ERROR;
sip = (const struct rt_superell_internal *)input->idb_ptr;
RT_SUPERELL_CK_MAGIC(sip);
if (!isfinite(sip->v[X]) || !isfinite(sip->v[Y]) ||
!isfinite(sip->v[Z]) || !isfinite(sip->n) || !isfinite(sip->e) ||
sip->n <= tol->dist || sip->e <= tol->dist ||
sip->n > maximum_curvature || sip->e > maximum_curvature)
return RT_CANONICALIZE_ERROR;
input_axes[0] = sip->a;
input_axes[1] = sip->b;
input_axes[2] = sip->c;
for (size_t i = 0; i < 3; i++) {
lengths[i] = MAGNITUDE(input_axes[i]);
if (!isfinite(lengths[i]) || lengths[i] <= tol->dist)
return RT_CANONICALIZE_ERROR;
VSCALE(axes[i], input_axes[i], 1.0 / lengths[i]);
}
if (!NEAR_ZERO(VDOT(axes[0], axes[1]), tol->perp) ||
!NEAR_ZERO(VDOT(axes[0], axes[2]), tol->perp) ||
!NEAR_ZERO(VDOT(axes[1], axes[2]), tol->perp))
return RT_CANONICALIZE_ERROR;
/* Axis signs do not change a superellipsoid. Keep the placement a
* proper transform by selecting the third sign from the input frame. */
VCROSS(cross, axes[0], axes[1]);
if (VDOT(cross, axes[2]) < 0.0)
VREVERSE(axes[2], axes[2]);
if (mode == RT_CANONICALIZE_AFFINE) {
VSETALL(canonical_lengths, 1.0);
for (size_t i = 0; i < 3; i++)
VSCALE(axes[i], axes[i], lengths[i]);
superell_canonical_orientation(orientation, axes[0], axes[1], axes[2]);
MAT_IDN(translate);
MAT_DELTAS_VEC(translate, sip->v);
bn_mat_mul(canonical_to_input, translate, orientation);
} else {
if (mode == RT_CANONICALIZE_SIMILARITY)
uniform_scale = fmax(lengths[0], fmax(lengths[1], lengths[2]));
for (size_t i = 0; i < 3; i++)
canonical_lengths[i] = lengths[i] / uniform_scale;
superell_canonical_orientation(orientation, axes[0], axes[1], axes[2]);
MAT_IDN(scale);
scale[15] = 1.0 / uniform_scale;
bn_mat_mul(oriented_scale, orientation, scale);
MAT_IDN(translate);
MAT_DELTAS_VEC(translate, sip->v);
bn_mat_mul(canonical_to_input, translate, oriented_scale);
}
canonical->idb_major_type = DB5_MAJORTYPE_BRLCAD;
canonical->idb_minor_type = ID_SUPERELL;
canonical->idb_meth = &OBJ[ID_SUPERELL];
BU_ALLOC(canonical->idb_ptr, struct rt_superell_internal);
csip = (struct rt_superell_internal *)canonical->idb_ptr;
csip->magic = RT_SUPERELL_INTERNAL_MAGIC;
VSETALL(csip->v, 0.0);
VSET(csip->a, canonical_lengths[0], 0.0, 0.0);
VSET(csip->b, 0.0, canonical_lengths[1], 0.0);
VSET(csip->c, 0.0, 0.0, canonical_lengths[2]);
csip->n = sip->n;
csip->e = sip->e;
return RT_CANONICALIZE_OK;
}
/**
* Import an superellipsoid/sphere from the database format to the
* internal structure. Apply modeling transformations as wsuperell.
+97 -48
View File
@@ -83,6 +83,7 @@ extern "C" {
extern int rt_##name##_labels(struct rt_point_labels *pl, int pl_max, const mat_t xform, const struct rt_db_internal *ip, const struct bn_tol *utol); \
extern const char *rt_##name##_keypoint(point_t *pt, const char *keystr, const mat_t xform, const struct rt_db_internal *ip, const struct bn_tol *tol); \
extern int rt_##name##_mat(struct rt_db_internal *op, const mat_t mat, const struct rt_db_internal *ip); \
extern int rt_##name##_canonicalize(struct rt_db_internal *canonical, mat_t canonical_to_input, const struct rt_db_internal *input, const struct bn_tol *tol, enum rt_canonicalize_mode mode); \
extern int rt_##name##_perturb(struct rt_db_internal **oip, const struct rt_db_internal *ip, int planar_only, fastf_t factor); \
extern int rt_##name##_scene_obj(struct bv_scene_obj *vhead, struct directory *dp, struct db_i *dbip, const struct bg_tess_tol *ttol, const struct bn_tol *tol, const struct bview *info) \
@@ -246,7 +247,8 @@ const struct rt_functab OBJ[] = {
NULL,
NULL,
NULL,
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -298,7 +300,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_tor_mat),
RTFUNCTAB_FUNC_PERTURB_CAST(rt_tor_perturb), /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
RTFUNCTAB_FUNC_CANONICALIZE_CAST(rt_tor_canonicalize) /* canonicalize */
},
{
@@ -350,7 +353,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_tgc_mat),
RTFUNCTAB_FUNC_PERTURB_CAST(rt_tgc_perturb), /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
RTFUNCTAB_FUNC_VALIDATE_CAST(rt_tgc_functab_validate) /* validate */
RTFUNCTAB_FUNC_VALIDATE_CAST(rt_tgc_functab_validate), /* validate */
RTFUNCTAB_FUNC_CANONICALIZE_CAST(rt_tgc_canonicalize) /* canonicalize */
},
{
@@ -402,7 +406,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_ell_mat),
RTFUNCTAB_FUNC_PERTURB_CAST(rt_ell_perturb), /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
RTFUNCTAB_FUNC_VALIDATE_CAST(rt_ell_functab_validate) /* validate */
RTFUNCTAB_FUNC_VALIDATE_CAST(rt_ell_functab_validate), /* validate */
RTFUNCTAB_FUNC_CANONICALIZE_CAST(rt_ell_canonicalize)
},
{
@@ -454,7 +459,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_arb_mat),
RTFUNCTAB_FUNC_PERTURB_CAST(rt_arb_perturb), /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
RTFUNCTAB_FUNC_VALIDATE_CAST(rt_arb_functab_validate) /* validate */
RTFUNCTAB_FUNC_VALIDATE_CAST(rt_arb_functab_validate), /* validate */
RTFUNCTAB_FUNC_CANONICALIZE_CAST(rt_arb_canonicalize) /* canonicalize */
},
{
@@ -506,7 +512,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_ars_mat),
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -558,7 +565,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_hlf_mat),
RTFUNCTAB_FUNC_PERTURB_CAST(rt_hlf_perturb), /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
RTFUNCTAB_FUNC_CANONICALIZE_CAST(rt_hlf_canonicalize)
},
{
@@ -610,7 +618,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_tgc_mat),
RTFUNCTAB_FUNC_PERTURB_CAST(rt_tgc_perturb), /* perturb - REC shares rt_tgc_internal */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
RTFUNCTAB_FUNC_CANONICALIZE_CAST(rt_tgc_canonicalize) /* canonicalize */
},
{
@@ -662,7 +671,8 @@ const struct rt_functab OBJ[] = {
NULL, /* mat */
NULL, /* perturb */
NULL /* scene_obj */,
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -714,7 +724,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_nurb_mat),
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -766,7 +777,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_ell_mat),
RTFUNCTAB_FUNC_PERTURB_CAST(rt_ell_perturb), /* perturb - SPH shares rt_ell_internal */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
RTFUNCTAB_FUNC_CANONICALIZE_CAST(rt_ell_canonicalize)
},
{
@@ -818,7 +830,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_nmg_mat),
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -870,7 +883,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_ebm_mat),
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -922,7 +936,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_vol_mat),
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -974,7 +989,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_arbn_mat),
RTFUNCTAB_FUNC_PERTURB_CAST(rt_arbn_perturb), /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -1026,7 +1042,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_pipe_mat),
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -1078,7 +1095,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_part_mat),
RTFUNCTAB_FUNC_PERTURB_CAST(rt_part_perturb), /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
RTFUNCTAB_FUNC_CANONICALIZE_CAST(rt_part_canonicalize) /* canonicalize */
},
{
@@ -1130,7 +1148,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_rpc_mat),
RTFUNCTAB_FUNC_PERTURB_CAST(rt_rpc_perturb), /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
RTFUNCTAB_FUNC_VALIDATE_CAST(rt_rpc_functab_validate) /* validate */
RTFUNCTAB_FUNC_VALIDATE_CAST(rt_rpc_functab_validate), /* validate */
RTFUNCTAB_FUNC_CANONICALIZE_CAST(rt_rpc_canonicalize) /* canonicalize */
},
{
@@ -1182,7 +1201,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_rhc_mat),
RTFUNCTAB_FUNC_PERTURB_CAST(rt_rhc_perturb), /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
RTFUNCTAB_FUNC_VALIDATE_CAST(rt_rhc_functab_validate) /* validate */
RTFUNCTAB_FUNC_VALIDATE_CAST(rt_rhc_functab_validate), /* validate */
RTFUNCTAB_FUNC_CANONICALIZE_CAST(rt_rhc_canonicalize) /* canonicalize */
},
{
@@ -1234,7 +1254,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_epa_mat),
RTFUNCTAB_FUNC_PERTURB_CAST(rt_epa_perturb), /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
RTFUNCTAB_FUNC_VALIDATE_CAST(rt_epa_functab_validate) /* validate */
RTFUNCTAB_FUNC_VALIDATE_CAST(rt_epa_functab_validate), /* validate */
RTFUNCTAB_FUNC_CANONICALIZE_CAST(rt_epa_canonicalize) /* canonicalize */
},
{
@@ -1286,7 +1307,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_ehy_mat),
RTFUNCTAB_FUNC_PERTURB_CAST(rt_ehy_perturb), /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
RTFUNCTAB_FUNC_VALIDATE_CAST(rt_ehy_functab_validate) /* validate */
RTFUNCTAB_FUNC_VALIDATE_CAST(rt_ehy_functab_validate), /* validate */
RTFUNCTAB_FUNC_CANONICALIZE_CAST(rt_ehy_canonicalize) /* canonicalize */
},
{
@@ -1338,7 +1360,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_eto_mat),
RTFUNCTAB_FUNC_PERTURB_CAST(rt_eto_perturb), /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
RTFUNCTAB_FUNC_VALIDATE_CAST(rt_eto_functab_validate) /* validate */
RTFUNCTAB_FUNC_VALIDATE_CAST(rt_eto_functab_validate), /* validate */
RTFUNCTAB_FUNC_CANONICALIZE_CAST(rt_eto_canonicalize) /* canonicalize */
},
{
@@ -1390,7 +1413,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_grp_mat),
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -1442,7 +1466,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_joint_mat),
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -1494,7 +1519,8 @@ const struct rt_functab OBJ[] = {
NULL, /* mat */
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -1546,7 +1572,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_dsp_mat),
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -1598,7 +1625,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_sketch_mat),
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -1650,7 +1678,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_extrude_mat),
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -1702,7 +1731,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_submodel_mat),
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -1754,7 +1784,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_cline_mat),
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -1806,7 +1837,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_bot_mat),
NULL, /* perturb */
NULL /* scene_obj */,
RTFUNCTAB_FUNC_VALIDATE_CAST(rt_bot_functab_validate) /* validate */
RTFUNCTAB_FUNC_VALIDATE_CAST(rt_bot_functab_validate), /* validate */
RTFUNCTAB_FUNC_CANONICALIZE_CAST(rt_bot_canonicalize) /* canonicalize */
},
{
@@ -1858,7 +1890,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_comb_mat),
NULL, /* perturb */
NULL /* scene_obj */,
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -1912,7 +1945,8 @@ const struct rt_functab OBJ[] = {
NULL, /* mat */
NULL, /* perturb */
NULL /* scene_obj */,
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -1964,7 +1998,8 @@ const struct rt_functab OBJ[] = {
NULL, /* mat */
NULL, /* perturb */
NULL /* scene_obj */,
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -2018,7 +2053,8 @@ const struct rt_functab OBJ[] = {
NULL, /* mat */
NULL, /* perturb */
NULL /* scene_obj */,
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -2070,7 +2106,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_superell_mat),
RTFUNCTAB_FUNC_PERTURB_CAST(rt_superell_perturb), /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
RTFUNCTAB_FUNC_CANONICALIZE_CAST(rt_superell_canonicalize) /* canonicalize */
},
{
@@ -2122,7 +2159,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_metaball_mat),
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -2174,7 +2212,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_brep_mat),
NULL, /* perturb */
NULL /* scene_obj */,
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -2226,7 +2265,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_hyp_mat),
RTFUNCTAB_FUNC_PERTURB_CAST(rt_hyp_perturb), /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -2278,7 +2318,8 @@ const struct rt_functab OBJ[] = {
NULL, /* mat */
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -2330,7 +2371,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_revolve_mat),
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -2382,7 +2424,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_pnts_mat),
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -2434,7 +2477,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_annot_mat),
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -2486,7 +2530,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_hrt_mat),
RTFUNCTAB_FUNC_PERTURB_CAST(rt_hrt_perturb), /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
@@ -2539,7 +2584,8 @@ const struct rt_functab OBJ[] = {
RTFUNCTAB_FUNC_MAT_CAST(rt_datum_mat),
NULL, /* perturb */
RTFUNCTAB_FUNC_SCENE_OBJ_CAST(rt_generic_scene_obj),
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
@@ -2592,7 +2638,8 @@ const struct rt_functab OBJ[] = {
NULL, /* mat */
NULL, /* perturb */
NULL /* scene_obj */,
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -2644,7 +2691,8 @@ const struct rt_functab OBJ[] = {
NULL, /* mat */
NULL, /* perturb */
NULL /* scene_obj */,
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
},
{
@@ -2696,7 +2744,8 @@ const struct rt_functab OBJ[] = {
NULL, /* mat */
NULL, /* perturb */
NULL /* scene_obj */,
NULL /* validate */
NULL, /* validate */
NULL /* canonicalize */
}
};
+242
View File
@@ -1757,6 +1757,248 @@ rt_tgc_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
}
static void
tgc_swap_vectors(vect_t a, vect_t b)
{
vect_t tmp;
VMOVE(tmp, a);
VMOVE(a, b);
VMOVE(b, tmp);
}
static int
tgc_ratio_pair_greater(fastf_t a0, fastf_t a1, fastf_t b0, fastf_t b1,
fastf_t tolerance)
{
fastf_t ahi = fmax(a0, a1);
fastf_t alo = fmin(a0, a1);
fastf_t bhi = fmax(b0, b1);
fastf_t blo = fmin(b0, b1);
if (ahi > bhi + tolerance)
return 1;
if (bhi > ahi + tolerance)
return 0;
return alo > blo + tolerance;
}
C_DECL int
rt_tgc_canonicalize(struct rt_db_internal *canonical,
mat_t canonical_to_input,
const struct rt_db_internal *input,
const struct bn_tol *tol,
enum rt_canonicalize_mode mode)
{
const struct rt_tgc_internal *tip;
struct rt_tgc_internal *ctip;
point_t v;
vect_t h, a, b, c, d;
vect_t u, w, normal;
fastf_t mag_h, mag_a, mag_b, mag_c, mag_d;
fastf_t ratio_c, ratio_d;
fastf_t h_a, h_b, h_w;
fastf_t uniform_scale = 1.0;
fastf_t ratio_tolerance;
int a_sign = 1;
int b_sign = 1;
int a_projection_zero;
int b_projection_zero;
int reverse_ends = 0;
if (!canonical || !canonical_to_input || !input || !tol)
return RT_CANONICALIZE_ERROR;
if (mode < RT_CANONICALIZE_RIGID || mode > RT_CANONICALIZE_AFFINE)
return RT_CANONICALIZE_ERROR;
if (input->idb_type != ID_TGC && input->idb_type != ID_REC)
return RT_CANONICALIZE_ERROR;
tip = (const struct rt_tgc_internal *)input->idb_ptr;
RT_TGC_CK_MAGIC(tip);
VMOVE(v, tip->v);
VMOVE(h, tip->h);
VMOVE(a, tip->a);
VMOVE(b, tip->b);
VMOVE(c, tip->c);
VMOVE(d, tip->d);
mag_h = MAGNITUDE(h);
mag_a = MAGNITUDE(a);
mag_b = MAGNITUDE(b);
mag_c = MAGNITUDE(c);
mag_d = MAGNITUDE(d);
if (!isfinite(v[X]) || !isfinite(v[Y]) || !isfinite(v[Z]) ||
!isfinite(mag_h) || !isfinite(mag_a) || !isfinite(mag_b) ||
!isfinite(mag_c) || !isfinite(mag_d) || mag_h <= tol->dist)
return RT_CANONICALIZE_ERROR;
if ((mag_a <= tol->dist && mag_c <= tol->dist) ||
(mag_b <= tol->dist && mag_d <= tol->dist))
return RT_CANONICALIZE_ERROR;
if ((mag_a <= tol->dist || mag_b <= tol->dist) &&
(mag_c <= tol->dist || mag_d <= tol->dist))
return RT_CANONICALIZE_ERROR;
if (input->idb_type == ID_REC) {
vect_t difference;
VSUB2(difference, a, c);
if (MAGNITUDE(difference) > tol->dist)
return RT_CANONICALIZE_ERROR;
VSUB2(difference, b, d);
if (MAGNITUDE(difference) > tol->dist ||
!NEAR_ZERO(VDOT(h, a) / (mag_h * mag_a), tol->perp) ||
!NEAR_ZERO(VDOT(h, b) / (mag_h * mag_b), tol->perp))
return RT_CANONICALIZE_ERROR;
}
/* A complete end supplies the affine basis. When both ends are
* complete, select between the two reciprocal taper descriptions so
* reversing an otherwise identical TGC does not change its canonical
* form. */
ratio_tolerance = fmax(tol->perp, SMALL_FASTF);
if (mag_a <= tol->dist || mag_b <= tol->dist) {
reverse_ends = 1;
} else if (mag_c > tol->dist && mag_d > tol->dist) {
fastf_t taper_c = mag_c / mag_a;
fastf_t taper_d = mag_d / mag_b;
int forward_greater = tgc_ratio_pair_greater(taper_c, taper_d,
1.0 / taper_c, 1.0 / taper_d, ratio_tolerance);
int reverse_greater = tgc_ratio_pair_greater(1.0 / taper_c,
1.0 / taper_d, taper_c, taper_d, ratio_tolerance);
reverse_ends = forward_greater;
if (!forward_greater && !reverse_greater &&
mode != RT_CANONICALIZE_AFFINE)
reverse_ends = tgc_ratio_pair_greater(mag_c, mag_d,
mag_a, mag_b, tol->dist);
}
if (reverse_ends) {
VADD2(v, v, h);
VREVERSE(h, h);
tgc_swap_vectors(a, c);
tgc_swap_vectors(b, d);
mag_a = MAGNITUDE(a);
mag_b = MAGNITUDE(b);
mag_c = MAGNITUDE(c);
mag_d = MAGNITUDE(d);
}
ratio_c = mag_c / mag_a;
ratio_d = mag_d / mag_b;
/* Axis interchange is a parameterization change, not a geometry
* change. Taper ratio is affine-invariant, so use it as the primary
* ordering key. */
if (ratio_d > ratio_c + ratio_tolerance ||
(mode != RT_CANONICALIZE_AFFINE &&
NEAR_EQUAL(ratio_c, ratio_d, ratio_tolerance) &&
mag_b > mag_a + tol->dist)) {
tgc_swap_vectors(a, b);
tgc_swap_vectors(c, d);
{
fastf_t tmp = mag_a;
mag_a = mag_b;
mag_b = tmp;
tmp = mag_c;
mag_c = mag_d;
mag_d = tmp;
tmp = ratio_c;
ratio_c = ratio_d;
ratio_d = tmp;
}
}
VSCALE(u, a, 1.0 / mag_a);
VSCALE(w, b, 1.0 / mag_b);
if (!NEAR_ZERO(VDOT(u, w), tol->perp))
return RT_CANONICALIZE_ERROR;
if (mag_c > tol->dist && !NEAR_EQUAL(VDOT(u, c) / mag_c, 1.0, tol->perp))
return RT_CANONICALIZE_ERROR;
if (mag_d > tol->dist && !NEAR_EQUAL(VDOT(w, d) / mag_d, 1.0, tol->perp))
return RT_CANONICALIZE_ERROR;
h_a = VDOT(h, u);
h_b = VDOT(h, w);
VCROSS(normal, u, w);
h_w = VDOT(h, normal);
if (fabs(h_w) <= mag_h * tol->perp)
return RT_CANONICALIZE_ERROR;
/* A/C and B/D may each be negated without changing the TGC. Use the
* height projections to resolve those signs without referring to world
* coordinates, which keeps the result invariant under placement. */
a_projection_zero = fabs(h_a) <= tol->dist;
b_projection_zero = fabs(h_b) <= tol->dist;
if (!a_projection_zero && h_a < 0.0)
a_sign = -1;
if (!b_projection_zero && h_b < 0.0)
b_sign = -1;
if (a_projection_zero && a_sign * b_sign * h_w < 0.0)
a_sign = -1;
else if (b_projection_zero && a_sign * b_sign * h_w < 0.0)
b_sign = -1;
if (a_sign < 0)
VREVERSE(u, u);
if (b_sign < 0)
VREVERSE(w, w);
VCROSS(normal, u, w);
MAT_IDN(canonical_to_input);
MAT_DELTAS_VEC(canonical_to_input, v);
if (mode == RT_CANONICALIZE_AFFINE) {
canonical_to_input[0] = u[X] * mag_a;
canonical_to_input[4] = u[Y] * mag_a;
canonical_to_input[8] = u[Z] * mag_a;
canonical_to_input[1] = w[X] * mag_b;
canonical_to_input[5] = w[Y] * mag_b;
canonical_to_input[9] = w[Z] * mag_b;
canonical_to_input[2] = h[X];
canonical_to_input[6] = h[Y];
canonical_to_input[10] = h[Z];
} else {
if (mode == RT_CANONICALIZE_SIMILARITY)
uniform_scale = fmax(mag_h, fmax(mag_a, fmax(mag_b, fmax(mag_c, mag_d))));
canonical_to_input[0] = u[X];
canonical_to_input[4] = u[Y];
canonical_to_input[8] = u[Z];
canonical_to_input[1] = w[X];
canonical_to_input[5] = w[Y];
canonical_to_input[9] = w[Z];
canonical_to_input[2] = normal[X];
canonical_to_input[6] = normal[Y];
canonical_to_input[10] = normal[Z];
canonical_to_input[15] = 1.0 / uniform_scale;
canonical_to_input[3] /= uniform_scale;
canonical_to_input[7] /= uniform_scale;
canonical_to_input[11] /= uniform_scale;
}
canonical->idb_major_type = DB5_MAJORTYPE_BRLCAD;
canonical->idb_minor_type = input->idb_minor_type;
canonical->idb_meth = &OBJ[input->idb_minor_type];
BU_ALLOC(canonical->idb_ptr, struct rt_tgc_internal);
ctip = (struct rt_tgc_internal *)canonical->idb_ptr;
ctip->magic = RT_TGC_INTERNAL_MAGIC;
VSETALL(ctip->v, 0.0);
if (mode == RT_CANONICALIZE_AFFINE) {
VSET(ctip->h, 0.0, 0.0, 1.0);
VSET(ctip->a, 1.0, 0.0, 0.0);
VSET(ctip->b, 0.0, 1.0, 0.0);
VSET(ctip->c, ratio_c, 0.0, 0.0);
VSET(ctip->d, 0.0, ratio_d, 0.0);
} else {
VSET(ctip->h, VDOT(h, u) / uniform_scale,
VDOT(h, w) / uniform_scale, VDOT(h, normal) / uniform_scale);
VSET(ctip->a, mag_a / uniform_scale, 0.0, 0.0);
VSET(ctip->b, 0.0, mag_b / uniform_scale, 0.0);
VSET(ctip->c, mag_c / uniform_scale, 0.0, 0.0);
VSET(ctip->d, 0.0, mag_d / uniform_scale, 0.0);
}
return RT_CANONICALIZE_OK;
}
/**
* Import a TGC from the database format to the internal format.
* Apply modeling transformations as well.
+62
View File
@@ -1840,6 +1840,68 @@ rt_tor_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
return BRLCAD_OK;
}
C_DECL int
rt_tor_canonicalize(struct rt_db_internal *canonical,
mat_t canonical_to_input,
const struct rt_db_internal *input,
const struct bn_tol *tol,
enum rt_canonicalize_mode mode)
{
const struct rt_tor_internal *tip;
struct rt_tor_internal *ctip;
vect_t normal;
vect_t zaxis = VINIT_ZERO;
mat_t rotate;
mat_t scale_mat;
mat_t rotated_scale;
mat_t translate;
fastf_t normal_mag;
fastf_t uniform_scale = 1.0;
if (!canonical || !canonical_to_input || !input || !tol)
return RT_CANONICALIZE_ERROR;
if (mode < RT_CANONICALIZE_RIGID || mode > RT_CANONICALIZE_AFFINE)
return RT_CANONICALIZE_ERROR;
if (input->idb_type != ID_TOR)
return RT_CANONICALIZE_ERROR;
tip = (const struct rt_tor_internal *)input->idb_ptr;
RT_TOR_CK_MAGIC(tip);
normal_mag = MAGNITUDE(tip->h);
if (normal_mag <= tol->dist || tip->r_a <= tol->dist || tip->r_h <= tol->dist)
return RT_CANONICALIZE_ERROR;
VSCALE(normal, tip->h, 1.0 / normal_mag);
VSET(zaxis, 0.0, 0.0, 1.0);
bn_mat_fromto(rotate, zaxis, normal, tol);
if (mode != RT_CANONICALIZE_RIGID)
uniform_scale = tip->r_a;
MAT_IDN(scale_mat);
scale_mat[15] = 1.0 / uniform_scale;
bn_mat_mul(rotated_scale, rotate, scale_mat);
MAT_IDN(translate);
MAT_DELTAS_VEC(translate, tip->v);
bn_mat_mul(canonical_to_input, translate, rotated_scale);
canonical->idb_major_type = DB5_MAJORTYPE_BRLCAD;
canonical->idb_minor_type = ID_TOR;
canonical->idb_meth = &OBJ[ID_TOR];
BU_ALLOC(canonical->idb_ptr, struct rt_tor_internal);
ctip = (struct rt_tor_internal *)canonical->idb_ptr;
ctip->magic = RT_TOR_INTERNAL_MAGIC;
VSETALL(ctip->v, 0.0);
VSET(ctip->h, 0.0, 0.0, 1.0);
ctip->r_a = tip->r_a / uniform_scale;
ctip->r_h = tip->r_h / uniform_scale;
VSET(ctip->a, ctip->r_a, 0.0, 0.0);
VSET(ctip->b, 0.0, ctip->r_a, 0.0);
ctip->r_b = ctip->r_a;
return RT_CANONICALIZE_OK;
}
/**
* Taken from the database record:
* v vertex (point) of center of torus.
+4
View File
@@ -55,6 +55,9 @@ brlcad_addexec(rt_diff diff.c "${RT_TEST_LIBS}" TEST)
brlcad_addexec(rt_default_object default_object.c "librt;libwdb;libbu;${M_LIBRARY}" TEST)
brlcad_add_test(NAME rt_default_object COMMAND rt_default_object)
brlcad_addexec(rt_canonicalize canonicalize.c "${RT_TEST_LIBS}" TEST)
brlcad_add_test(NAME rt_canonicalize COMMAND rt_canonicalize)
brlcad_addexec(rt_db_name db_name.c "librt;libbu" TEST)
brlcad_add_test(NAME rt_db_name COMMAND rt_db_name)
@@ -216,6 +219,7 @@ set(
binunif.c
brep_io.cpp
bound_internal.c
canonicalize.c
crofton.c
db_name.c
default_object.c
File diff suppressed because it is too large Load Diff
+1
View File
@@ -796,6 +796,7 @@ static struct to_cmdtab ged_cmds[] = {
{"track", (char *)0, TO_UNLIMITED, to_pass_through_func, ged_exec_track},
{"tree", (char *)0, TO_UNLIMITED, to_pass_through_func, ged_exec_tree},
{"unhide", (char *)0, TO_UNLIMITED, to_pass_through_func, ged_exec_unhide},
{"unpush", (char *)0, TO_UNLIMITED, to_pass_through_func, ged_exec_unpush},
{"units", (char *)0, TO_UNLIMITED, to_pass_through_func, ged_exec_units},
{"v2m_point", "x y z", 5, to_view_func, ged_exec_v2m_point},
{"vdraw", (char *)0, TO_UNLIMITED, to_pass_through_and_refresh_func, ged_exec_vdraw},
+1
View File
@@ -457,6 +457,7 @@ static struct cmdtab mged_cmdtab[] = {
{MGED_CMD_MAGIC, "translate", f_tr_obj, GED_FUNC_PTR_NULL, NULL},
{MGED_CMD_MAGIC, "tree", cmd_ged_plain_wrapper, ged_exec_tree, NULL},
{MGED_CMD_MAGIC, "unhide", cmd_ged_plain_wrapper, ged_exec_unhide, NULL},
{MGED_CMD_MAGIC, "unpush", cmd_ged_plain_wrapper, ged_exec_unpush, NULL},
{MGED_CMD_MAGIC, "units", cmd_units, GED_FUNC_PTR_NULL, NULL},
{MGED_CMD_MAGIC, "v2m_point", cmd_ged_plain_wrapper, ged_exec_v2m_point, NULL},
{MGED_CMD_MAGIC, "vars", f_set, GED_FUNC_PTR_NULL, NULL},