Fix of issue 8213 by consider length of a vector being zero if enough small, define diff_of_product in a specific file

This commit is contained in:
Léo Valque 2025-02-06 09:35:02 +01:00
parent b54a2eab1a
commit 95b4eba11e
4 changed files with 99 additions and 44 deletions

View File

@ -0,0 +1,60 @@
// Copyright (c) 2025 GeometryFactory (France). All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s) : Mael Rouxel-Labbé
//
#ifndef CGAL_CARTESIAN_CROSSPRODUCT_H
#define CGAL_CARTESIAN_CROSSPRODUCT_H
#include <CGAL/license/Surface_mesh_simplification.h>
#include <optional>
namespace CGAL {
// a*b - c*d
// The next two functions are from https://stackoverflow.com/questions/63665010/accurate-floating-point-computation-of-the-sum-and-difference-of-two-products
static double diff_of_products_kahan(const double a, const double b, const double c, const double d)
{
double w = d * c;
double e = std::fma(c, -d, w);
double f = std::fma(a, b, -w);
return f + e;
}
static double diff_of_products_cht(const double a, const double b, const double c, const double d)
{
double p1 = a * b;
double p2 = c * d;
double e1 = std::fma (a, b, -p1);
double e2 = std::fma (c, -d, p2);
double r = p1 - p2;
double e = e1 + e2;
return r + e;
}
static double diff_of_products(const double a, const double b, const double c, const double d)
{
// return a*b - c*d;
// the next two are equivalent in results and speed
return diff_of_products_kahan(a, b, c, d);
// return diff_of_products_cht(a, b, c, d);
}
template <typename OFT>
static OFT diff_of_products(const OFT& a, const OFT& b, const OFT& c, const OFT& d)
{
return a*b - c*d;
}
} // namespace CGAL
#endif // CGAL_CARTESIAN_CROSSPRODUCT_H //
// EOF //

View File

@ -17,6 +17,7 @@
#include <CGAL/Null_matrix.h>
#include <CGAL/number_utils.h>
#include <CGAL/Vector_3.h>
#include <CGAL/Cartesian/CrossProduct.h>
#include <optional>
@ -219,6 +220,19 @@ std::optional< MatrixC33<R> > inverse_matrix(const MatrixC33<R>& m)
if(! CGAL_NTS is_zero(det))
{
// RT c00 = diff_of_products(m.r1().y(),m.r2().z(),m.r2().y(),m.r1().z()) / det;
// RT c01 = diff_of_products(m.r2().y(),m.r0().z(),m.r0().y(),m.r2().z()) / det;
// RT c02 = diff_of_products(m.r0().y(),m.r1().z(),m.r1().y(),m.r0().z()) / det;
// RT c10 = diff_of_products(m.r2().x(),m.r1().z(),m.r1().x(),m.r2().z()) / det;
// RT c11 = diff_of_products(m.r0().x(),m.r2().z(),m.r2().x(),m.r0().z()) / det;
// RT c12 = diff_of_products(m.r1().x(),m.r0().z(),m.r0().x(),m.r1().z()) / det;
// RT c20 = diff_of_products(m.r1().x(),m.r2().y(),m.r2().x(),m.r1().y()) / det;
// RT c21 = diff_of_products(m.r2().x(),m.r0().y(),m.r0().x(),m.r2().y()) / det;
// RT c22 = diff_of_products(m.r0().x(),m.r1().y(),m.r1().x(),m.r0().y()) / det;
RT c00 = (m.r1().y()*m.r2().z() - m.r1().z()*m.r2().y()) / det;
RT c01 = (m.r2().y()*m.r0().z() - m.r0().y()*m.r2().z()) / det;
RT c02 = (m.r0().y()*m.r1().z() - m.r1().y()*m.r0().z()) / det;

View File

@ -18,6 +18,7 @@
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Edge_profile.h>
#include <CGAL/Cartesian/MatrixC33.h>
#include <CGAL/Cartesian/CrossProduct.h>
#include <limits>
#include <vector>
@ -101,6 +102,8 @@ private :
void extract_triangle_data();
void extract_boundary_data();
double maxBb;
void add_boundary_preservation_constraints(const Boundary_data_vector& aBdry);
void add_volume_preservation_constraints(const Triangle_data_vector& triangles);
void add_boundary_and_volume_optimization_constraints(const Boundary_data_vector& aBdry,
@ -119,42 +122,6 @@ private :
const Geom_traits& geom_traits() const { return mProfile.geom_traits(); }
const TM& surface() const { return mProfile.surface(); }
#if 0
// a*b - c*d
// The next two functions are from https://stackoverflow.com/questions/63665010/accurate-floating-point-computation-of-the-sum-and-difference-of-two-products
static double diff_of_products_kahan(const double a, const double b, const double c, const double d)
{
double w = d * c;
double e = std::fma(c, -d, w);
double f = std::fma(a, b, -w);
return f + e;
}
static double diff_of_products_cht(const double a, const double b, const double c, const double d)
{
double p1 = a * b;
double p2 = c * d;
double e1 = std::fma (a, b, -p1);
double e2 = std::fma (c, -d, p2);
double r = p1 - p2;
double e = e1 + e2;
return r + e;
}
static double diff_of_products(const double a, const double b, const double c, const double d)
{
// the next two are equivalent in results and speed
return diff_of_products_kahan(a, b, c, d);
// return diff_of_products_cht(a, b, c, d);
}
template <typename OFT>
static OFT diff_of_products(const OFT& a, const OFT& b, const OFT& c, const OFT& d)
{
return a*b - c*d;
}
#endif
#ifdef __AVX__
static Vector SL_cross_product_avx(const Vector& A, const Vector& B)
{
@ -353,12 +320,18 @@ extract_triangle_data()
{
mTriangle_data.reserve(mProfile.triangles().size());
//TODO for obscur reason, computing this abs_max increase running time by 10%
double abs_max;
for(const Triangle& tri : mProfile.triangles())
{
const Point_reference p0 = get_point(tri.v0);
const Point_reference p1 = get_point(tri.v1);
const Point_reference p2 = get_point(tri.v2);
abs_max=(std::max)({abs_max,std::abs(p0.x()),std::abs(p0.y()),std::abs(p0.z()),
std::abs(p1.x()),std::abs(p1.y()),std::abs(p1.z()),
std::abs(p2.x()),std::abs(p2.y()),std::abs(p2.z())});
Vector v01 = p1 - p0;
Vector v02 = p2 - p0;
@ -370,6 +343,7 @@ extract_triangle_data()
mTriangle_data.push_back(Triangle_data(lNormalV,lNormalL));
}
maxBb= abs_max;
}
template<class TM, class K>
@ -687,7 +661,11 @@ add_constraint_if_alpha_compatible(const Vector& Ai,
FT l = CGAL_NTS sqrt(slai);
CGAL_SMS_LT_TRACE(3, " l: " << n_to_string(l));
if(!CGAL_NTS is_zero(l))
// Due to double number type, l may have a small value instead of zero (example sum of the faces normals of a tetrahedra for volumic constraint)
// if bin is greater than maxBb, we consider that l is zero
CGAL_SMS_LT_TRACE(3, " error consider: " << (std::abs(bi) / (2*maxBb)));
if(l > (std::abs(bi) / (2*maxBb)))
// if(!CGAL_NTS is_zero(l))
{
Vector Ain = Ai / l;
FT bin = bi / l;
@ -862,6 +840,7 @@ add_constraint_from_gradient(const Matrix& H,
}
break;
}
}
} // namespace internal

View File

@ -3,6 +3,8 @@
#include <CGAL/Surface_mesh.h>
CGAL::Bbox_3 bbox_g;
double max_bbox_g;
#define CGAL_CHECK_EXPENSIVE
#define CGAL_SURFACE_SIMPLIFICATION_ENABLE_TRACE 5
@ -15,7 +17,7 @@ void Surface_simplification_external_trace(const std::string& s)
#include <CGAL/Surface_mesh_simplification/edge_collapse.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Bounded_normal_change_filter.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Bounding_box_filter.h>
// #include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Bounding_box_filter.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Face_count_stop_predicate.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/LindstromTurk_cost.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/LindstromTurk_placement.h>
@ -48,19 +50,19 @@ int main(int argc, char** argv)
return EXIT_FAILURE;
}
CGAL::Bbox_3 bbox = PMP::bbox(sm);
auto bb=PMP::bbox(sm);
std::cout << "Bbox:" << bb << std::endl;
std::cout << "Input mesh has " << num_vertices(sm) << " vertices" << std::endl;
std::cout << "Input mesh has " << num_faces(sm) << " faces" << std::endl;
SMS::Face_count_stop_predicate<Surface_mesh> stop(1);
SMS::edge_collapse(
surface_mesh,
sm,
stop,
CGAL::parameters::
filter(SMS::Bounded_normal_change_filter<SMS::Bounding_box_filter<>>(SMS::Bounding_box_filter<>()))
.get_cost(SMS::LindstromTurk_cost<Surface_mesh>())
// filter(SMS::Bounded_normal_change_filter<>()) //<SMS::Bounding_box_filter<>>(SMS::Bounding_box_filter<>()))
get_cost(SMS::LindstromTurk_cost<Surface_mesh>())
.get_placement(SMS::LindstromTurk_placement<Surface_mesh>())
);
CGAL::IO::write_OFF(std::cout, sm, CGAL::parameters::stream_precision(17));
@ -69,7 +71,7 @@ int main(int argc, char** argv)
{
// To be within the bounding box isn't a guarantee, but here it is a sufficient test
// to check if things went awry
if(!CGAL::do_overlap(bbox, sm.point(v).bbox()))
if(!CGAL::do_overlap(bb, sm.point(v).bbox()))
{
std::cerr << "Error: " << sm.point(v) << " is outside the initial bbox" << std::endl;
return EXIT_FAILURE;