Removing geometrical objects from geomview window when used in CGAL - c++

I am interested in implementing my computational geometry algorithms using the CGAL library.
Ideally, I am also interested in being able to animate my algorithm.CGAL has an interface to geomview built in which I am interested in using for illustrating these algorithms.
Based on what I little I understand of the CGAL geomview interface (from this example), below is a very simple code I wrote, which inserts 5 random points, and segments between some of the points.
However, once I render the objects to the screen, I don't know how unrender them or delete them from the geomview window, if they need to be deleted at the
next iteration(say) of my algorithm. So how would I modify the code below to do just that?
If someone knows of a better way than using geomview to animate geometry algorithms with CGAL that would also be helpful.
#include <iostream>
#include <vector>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <unistd.h>
#include <CGAL/IO/Geomview_stream.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::Point_2 Point_2;
typedef K::Segment_2 Segment_2;
using namespace std;
int main(int argc, char *argv[])
{
Point_2 points[5] = { Point_2(0.,0.), Point_2(10.,0.),Point_2(10.,10.),Point_2(6.,5.),Point_2(4.,1.) };
CGAL::Geomview_stream gv(CGAL::Bbox_3(-12, -12, -0.1, 12,12,0.1));
gv << CGAL::RED; // red points
for (int i = 0; i <= 2; ++i)
{
gv << points[i];
}
gv << CGAL::BLUE;// bluepoints
for (int i = 3; i <= 4; ++i)
{
gv << points[i];
}
// segments between some points
gv << CGAL::BLACK;
Segment_2 AB = Segment_2(points[0],points[1]);
gv << CGAL::YELLOW << AB ;
Segment_2 CD = Segment_2(points[1],points[2]);
gv << CGAL::BLUE << CD ;
sleep(300);
return 0;
}

The current trend among CGAL developers is to use Qt framework and associated visualisation tools such as QGLViewer rather than Geomview which are more recent, fully portative and allows you to do much more (especially if you want to make a demo for your algorithms with user interactions).
If you want to do 3D visualisation with CGAL I will advise you to use QGLViewer as they are already a lot of demos in CGAL that uses that library. For instance as an entry point, I will suggest you to have a look to Alpha_shape_3 demo. The code of this demo is quite light and straightforward, you can easily add new features without understanding the whole Qt framework first (you may have to eventually but that way the learning curve will be less steep and you can quickly start implementing stuff).
If you want to do 2D visualisation, you may have a look to the Alpha_shape_2 demo and use QPainter from Qt (note that you can combine both 3d and 2d viewer in QGL viewer as shown in this example.

Related

CGAL Inexact Volume calculation

My Problem
I would like to calculate the exact volume for an intersection with polygonmeshes. Unfortunately the result is wrong!?!?!.
I think it has something to do with me choosing the wrong options.
I have an STEP/OFF file (both work). I move a smaller Cylinder into a bigger Cylinder.
Then I calculate the Intersection. I do not use a pointmap.
If I calculate the Volume of the first three intersections, CGAL tells me their volume is zero, but it is not.
Why the Result is wrong
I know this, because:
I described this Problem analytically and solved the integral with matlab => Volume is not 0
I solved the Problem Using FreeCAD => Volume is the same as in Matlab
I solved the Volume in CGAL Result do not match 1 and 2.
I wrote the Results of many all the intersection into files, and the files concerning the problem are not empty. With a mesh viewer like (gmsh or meshlab) I can confirm heigth width and length. So the volume should not be 0, because it is intersecting so the volume cannot be 0.
What I have done
I have read this:
The Exact Computation Paradigm
Robustness and Precision Issues in Geometric Computation
FAQ: I am using double (or float or ...) as my number type and get assertion failures or incorrect output. Is this a bug in CGAL?
I did not understand how these three apply to my situation.
I am using the Exact_predicates_exact_constructions_kernel, I defined CGAL_DONT_USE_LAZY_KERNEL. I have used the other Kernels and not defined CGAL_DONT_USE_LAZY_KERNEL, the result does not change.
I do not use the the same output and input variable for intersection like in the
Polygon_mesh_processing/corefinement_consecutive_bool_op.cpp, so i do not use a point map as a result.
If needed I will supply the entire example, but I think, I did something wrong and the includes and way how I calculate the volume should suffice.
// originalExampleFrom corefinement_parallel_union_meshes.cpp;
#include <CGAL/Exact_predicates_exact_constructions_kernel_with_sqrt.h>
//#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polygon_mesh_processing/transform.h>
#include <CGAL/Polygon_mesh_processing/intersection.h>
#include <CGAL/Named_function_parameters.h>
#include <CGAL/boost/graph/named_params_helper.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/aff_transformation_tags.h>
#include <CGAL/Polygon_mesh_processing/triangulate_faces.h>
#include <CGAL/Polygon_mesh_processing/corefinement.h>
#include <CGAL/Polygon_mesh_processing/repair.h>
#include <CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h>
#include <CGAL/Aff_transformation_3.h>
#include <iostream>
#include <fstream> // for write to file
#include <cassert>
#include <typeinfo>
//#define CGAL_DONT_USE_LAZY_KERNEL
/*
The corefinement operation (which is also internally used in the three Boolean operations) will correctly change the topology of the input surface mesh
if the point type used in the point property maps of the input meshes is from a CGAL Kernel with exact predicates.
If that kernel does not have exact constructions, the embedding of the output surface mesh might have self-intersections.
In case of consecutive operations, it is thus recommended to use a point property map with points from a kernel
with exact predicates and exact constructions (such as CGAL::Exact_predicates_exact_constructions_kernel).
In practice, this means that with exact predicates and inexact constructions, edges will be split at each intersection with a triangle but the position of the intersection point might create self-intersections due to the limited precision of floating point numbers.
*/
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel; //read text abouve about kernel
typedef Kernel::Point_3 Point_3;
typedef CGAL::Surface_mesh<Kernel::Point_3> Mesh;
#define CGAL_DONT_USE_LAZY_KERNEL
namespace PMP = CGAL::Polygon_mesh_processing;
bool simulateDrillingRadial(Mesh cutter, Mesh rotor, Mesh &out, double step) { //Kernel::Point_3 *volume
bool validIntersection = false;
CGAL::Aff_transformation_3<Kernel> trans(CGAL::Translation(),
Kernel::Vector_3(0, step, Z_INIT)); // step * 2
PMP::transform(trans, cutter);
assert(!CGAL::Polygon_mesh_processing::does_self_intersect(rotor));
assert(!CGAL::Polygon_mesh_processing::does_self_intersect(cutter));
assert(CGAL::Polygon_mesh_processing::does_bound_a_volume(cutter));
assert(CGAL::Polygon_mesh_processing::does_bound_a_volume(rotor));
validIntersection = CGAL::exact(
PMP::corefine_and_compute_intersection(cutter, rotor, out));
#ifndef NDEBUG
CGAL::IO::write_polygon_mesh("union" + std::to_string(step) + ".off", out,
CGAL::parameters::stream_precision(17));
assert(validIntersection);
std::cout << "Cutter Volume: " << PMP::volume(cutter) << std::endl;
std::cout << "Rotor Volume: " << PMP::volume(rotor) << std::endl;
std::cout << "Out Volume: " << CGAL::exact(PMP::volume(out)) << std::endl;
std::cout << "Numb. of Step: " << step << std::endl;
std::cout << "Bohrtiefe : " << Y_INIT - DRMAX * 1.0 / ND * step
<< std::endl;
#endif
return validIntersection;
}
The Rest of the CODE:
bool simulateDrillingRadial(Mesh cutter, Mesh rotor, Mesh &out, double step) { //Kernel::Point_3 *volume
bool validIntersection = false;
CGAL::Aff_transformation_3<Kernel> trans(CGAL::Translation(),
Kernel::Vector_3(0, step, Z_INIT)); // step * 2
PMP::transform(trans, cutter);
assert(!CGAL::Polygon_mesh_processing::does_self_intersect(rotor));
assert(!CGAL::Polygon_mesh_processing::does_self_intersect(cutter));
assert(CGAL::Polygon_mesh_processing::does_bound_a_volume(cutter));
assert(CGAL::Polygon_mesh_processing::does_bound_a_volume(rotor));
validIntersection = CGAL::exact(
PMP::corefine_and_compute_intersection(cutter, rotor, out));
#ifndef NDEBUG
CGAL::IO::write_polygon_mesh("union" + std::to_string(step) + ".step", out,
CGAL::parameters::stream_precision(17));
assert(validIntersection);
std::cout << "Cutter Volume: " << PMP::volume(cutter) << std::endl;
std::cout << "Rotor Volume: " << PMP::volume(rotor) << std::endl;
std::cout << "Out Volume: " << CGAL::exact(PMP::volume(out)) << std::endl;
std::cout << "Bohrtiefe : " << step << std::endl;
#endif
return validIntersection;
}
int main(int argc, char **argv) {
bool validRead = false;
bool validIntersection = false;
Mesh cutter, rotor; //out; // , out;
Mesh out[ND];
Kernel::Point_3 centers[ND];
//Kernel::FT volume[ND];
//GetGeomTraits<TriangleMesh, CGAL_NP_CLASS>::type::FT volume[ND];
double steps[40] = { 40.0000, 39.9981, 39.9926, 39.9833, 39.9703, 39.9535,
39.9330, 39.9086, 39.8804, 39.8482, 39.8121, 39.7719, 39.7276,
39.6791, 39.6262, 39.5689, 39.5070, 39.4404, 39.3689, 39.2922,
39.2103, 39.1227, 39.0293, 38.9297, 38.8235, 38.7102, 38.5893,
38.4601, 38.3219, 38.1736, 38.0140, 37.8415, 37.6542, 37.4490,
37.2221, 36.9671, 36.6740, 36.3232, 35.8661, 35.0000 };
const std::string cutterFile = CGAL::data_file_path("Cutter.off");
const std::string rotorFile = CGAL::data_file_path("Rotor.off");
validRead = (!PMP::IO::read_polygon_mesh(cutterFile, cutter)
|| !PMP::IO::read_polygon_mesh(rotorFile, rotor));
assert(!validRead);
PMP::triangulate_faces(cutter);
PMP::triangulate_faces(rotor);
PMP::transform(rotAroundX(M_PI / 2), cutter);
for (int i = 0; i < ND; i++) {
//simulateDrillingRadial(Mesh & cutter, Mesh & rotor, Mesh & out, unsigned int step)
simulateDrillingRadial(cutter, rotor, out[i], steps[i] + 10);
}
writeToCSV("tmp.csv", ND, out, steps);
return 0;
}
Change the output format of your mesh from *.off to *.stl, then open the intersection mesh in a software like Autodesk Netfabb which can detect and repair errors in the meshes being loaded. I think there’s a high chance the functions you’re using generate meshes with bugs. Possible bugs include holes, duplicate triangles, and self-intersections. Strictly speaking, such meshes do not unambiguously define a solid body and they have no volume.
If you confirm that’s the problem, there’re two ways to fix.
Replace or fix the intersection algorithm making it produce watertight meshes with no self-intersections or duplicate triangles. Maybe the Nef Polyhedra from the same library will help.
Replace volume computation algorithm making it tolerant to the bugs you have in your intersection meshes.
I realize the answer is rather vague. The reason for that — the problem is very hard to solve well. Very smart people published research papers over several decades. Some companies even selling commercial libraries just for reliable Boolean operations on 3D meshes.

Shrink/Expand the outline of a polygon with holes

I want to expand/shrink a polygon with holes using boost::polygon. So to clarify that a bit, I have a single data structure
boost::polygon::polygon_with_holes_data<int> inPoly
where inPoly contains data that describe a rectangular outline and a triangle which forms the hole within this rectangle (in picture below this is the left, black drawing).
Now I want to
a) expand the whole stuff so that the rectangle becomes bigger and the hole becomes smaller (resulting in the red polygon in image below) or
b) shrink it so that the rectangle becomes smaller and the hole bigger (resulting in the green image below).
The corners don't necessarily need to be straight, the also can be rounded or somehow "rough".
My question: how can this be done using boost::polygon?
Thanks!
I answered this Expand polygons with boost::geometry?
And yes you can teach Boost Geometry to act on Boost Polygon types:
#include <boost/geometry/geometries/adapted/boost_polygon.hpp>
I came up with a test polygon like you described:
boost::polygon::polygon_with_holes_data<int> inPoly;
bg::read_wkt("POLYGON ((0 0,0 1000,1000 1000,1000 0,0 0),(100 100,900 100,500 700,100 100))", inPoly);
Now, apparently we can't just buffer on the adapted polygon, nor can we bg::assign or bg::convert directly. So, I came up with an ugly workaround of converting to WKT and back. And then you can do the buffer, and conver back similarly.
It's not very elegant, but it does work:
poly in;
bg::read_wkt(boost::lexical_cast<std::string>(bg::wkt(inPoly)), in);
Full Demo
Include SVG output:
Live On Coliru
#include <boost/polygon/polygon.hpp>
#include <boost/polygon/polygon_set_data.hpp>
#include <boost/polygon/polygon_with_holes_data.hpp>
#include <boost/geometry.hpp>
#include <boost/geometry/strategies/buffer.hpp>
#include <boost/geometry/algorithms/buffer.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/geometry/geometries/multi_polygon.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/adapted/boost_polygon.hpp>
#include <fstream>
namespace bp = boost::polygon;
namespace bg = boost::geometry;
using P = bp::polygon_with_holes_data<int>;
using PS = bp::polygon_set_data<int>;
using coordinate_type = bg::coordinate_type<P>::type;
int main() {
P inPoly, grow, shrink;
bg::read_wkt("POLYGON ((0 0,0 1000,1000 1000,1000 0,0 0),(100 100,900 100,500 700,100 100))", inPoly);
{
// define our boost geometry types
namespace bs = bg::strategy::buffer;
namespace bgm = bg::model;
using pt = bgm::d2::point_xy<coordinate_type>;
using poly = bgm::polygon<pt>;
using mpoly = bgm::multi_polygon<poly>;
// define our buffering strategies
using dist = bs::distance_symmetric<coordinate_type>;
bs::side_straight side_strategy;
const int points_per_circle = 12;
bs::join_round join_strategy(points_per_circle);
bs::end_round end_strategy(points_per_circle);
bs::point_circle point_strategy(points_per_circle);
poly in;
bg::read_wkt(boost::lexical_cast<std::string>(bg::wkt(inPoly)), in);
for (auto [offset, output_p] : { std::tuple(+15, &grow), std::tuple(-15, &shrink) }) {
mpoly out;
bg::buffer(in, out, dist(offset), side_strategy, join_strategy, end_strategy, point_strategy);
assert(out.size() == 1);
bg::read_wkt(boost::lexical_cast<std::string>(bg::wkt(out.front())), *output_p);
}
}
{
std::ofstream svg("output.svg");
using pt = bg::model::d2::point_xy<coordinate_type>;
boost::geometry::svg_mapper<pt> mapper(svg, 400, 400);
mapper.add(inPoly);
mapper.add(grow);
mapper.add(shrink);
mapper.map(inPoly, "fill-opacity:0.3;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
mapper.map(grow, "fill-opacity:0.05;fill:rgb(255,0,0);stroke:rgb(255,0,0);stroke-width:2");
mapper.map(shrink, "fill-opacity:0.05;fill:rgb(0,0,255);stroke:rgb(0,0,255);stroke-width:2");
}
}
The output.svg written:
More or less accidentally I found boost::polygon also provides a single function for that which is quite easy to use: boost::polygon::polygon_set_data offers a function resize() which is doing exactly what is described above. Using the additional, parameters corner_fill_arc and num_segments rounded corners can be created.
No idea why this function is located in boost::polygon::polygon_set_data and not in boost::polygon::polygon_with_holes_data which in my opinion would be the more logically place for such a function...

How to draw a plot chart using visual studio c++

I want to create a plot chart using visual studio with c++ code. The chart should be based on two axis. "x" axis display the time and "y" axis display the array data. array data have 100 elements and one data read in one second. How do I implement the code using any other graph library?
1) checkout and install Microsoft vcpkg to new folder (see 1-step instruction here: https://github.com/Microsoft/vcpkg)
2) vcpkg.exe install plplot from vcpkg folder
3) vcpkg.exe integrate project will give you instruction to add plplot to your MSVC project
4) paste this instruction to the Nuget Console:
5) after you paste and project reloads you can try this code:
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <cmath>
#include "plplot\plstream.h"
using namespace std;
const int NSIZE = 101;
int main(int argc, char ** argv) {
PLFLT x[NSIZE], y[NSIZE];
PLFLT xmin = 0., xmax = 1., ymin = 0., ymax = 100.;
int i;
for (i = 0; i < NSIZE; i++) {
x[i] = (PLFLT)(i) / (PLFLT)(NSIZE - 1);
y[i] = ymax * x[i] * x[i];
}
auto pls = new plstream();
plsdev("wingcc");
pls->init();
pls->env(xmin, xmax, ymin, ymax, 0, 0);
pls->lab("x", "y=100 x#u2#d", "Simple PLplot demo of a 2D line plot");
pls->line(NSIZE, x, y);
delete pls;
}
and you get:
tested on MSVC2015
I answered a very similar question a few years ago... there's a simple, straight and compilable example:
Graphical representation - Data Distribution
Obviously, the chart is not the same one that you need. But you can modify it in order to draw anything you want using C++ and then make any chart.
Plotting is a little bit tricky job in C++, as there is no default plotting library available in any C++ IDE. However, there are many libraries available online for making plotting possible in C++. Some plotting tools like Gnuplot, PPlot, Matlab, Python, KoolPlot (May be enough for your requirement).
I have answered a similar question a few days (plotting package for c++). The answer may be helpful.

Transformation and CSG Operations on grid in OpenVDB

OpenVDB seems really amazing, and the addressing of the nodes is really smart. There are some operations that I don't understand, in particular CSG operations. This is a example code. It takes as input two arguments:
a vdb input file with only one grid, representing a level set created starting from a triangular mesh,
a vdb output that stores the results of the operations.
The algorithm should take the input,
creates a deepCopy in gridA
creates a deepCopy in gridB
rotates gridB along Y axiz of M_PI/4.0f
performs the csgUnion between gridA and gridB
saves all grids in a vdb output file.
I'm trying to use VDB grids as data container in place of classicaloctree algorithm, for physical simulations that needs an high level of detail in collisions.
I understand the concept of transformation between world coordinates and grid coordinates, what I cannot understand is how to perform a transformation of data inside the tree, like translate or rotate the level-sets, like a rigid object. In the example, I think I'm only changing the transformation between world and lattice.
This is the result (the same for level-set and volume):Initial GridTrasformed grid, it seems that the rotation is performed...no final result?
Do you have suggestions?
Attached: one example and a link to the LINK REMOVED that I'm using (sorry, it is 133MB...)
#include <cmath>
#include "openvdb/openvdb.h"
#include "openvdb/util/Util.h"
#include "openvdb/io/Stream.h"
#include "openvdb/tools/Composite.h"
using namespace openvdb;
int main(int argc, char** argv) {
openvdb::initialize();
openvdb::io::File file(argv[1]);
file.open();
GridBase::Ptr baseGrid;
for (openvdb::io::File::NameIterator nameIter = file.beginName();
nameIter != file.endName(); ++nameIter)
{ baseGrid = file.readGrid(nameIter.gridName()); }
file.close();
FloatGrid::Ptr gridA = gridPtrCast<FloatGrid>(baseGrid);
FloatGrid::Ptr gridB = gridA->deepCopy();
FloatGrid::Ptr result = gridA ->deepCopy();
gridB->transform().postRotate(M_PI/4.0f, math::Y_AXIS);
tools::csgUnion(*result, *gridB);
openvdb::io::File file_out(argv[2]);
GridPtrVec grids;
grids.push_back(gridA);
grids.push_back(gridB);
grids.push_back(result);
file_out.write(grids);
file_out.close();
return 0;
}
The solution of my answer, thanks to VDB support in OpenVDB forum, is:
Execute a simple metadata copy of the initial grid
Execute the transformation (like the rotation in my code)
reinterpolate data form the initial grid in the new transformed grid, using tools::resampleToMatch, choosing one of the interpolator (in my case tools::BoxSample) available.
continue with csg operations
FYI, there is an extreme difference in execution time
using optimization flag -O3 (400% time reduction).
#include "openvdb/io/Stream.h"
#include "openvdb/openvdb.h"
#include "openvdb/tools/Composite.h"
#include "openvdb/tools/GridTransformer.h"
#include "openvdb/tools/Interpolation.h"
#include "openvdb/util/Util.h"
#include <cmath>
using namespace openvdb;
int main(int argc, char **argv) {
openvdb::initialize();
openvdb::io::File file(argv[1]);
file.open();
GridBase::Ptr baseGrid;
for (openvdb::io::File::NameIterator nameIter = file.beginName();
nameIter != file.endName(); ++nameIter) {
baseGrid = file.readGrid(nameIter.gridName());
}
file.close();
FloatGrid::Ptr gridA = gridPtrCast<FloatGrid>(baseGrid);
FloatGrid::Ptr gridB = gridA->copy(CP_NEW);
gridB->setTransform(gridA->transform().copy());
gridB->transform().postRotate(M_PI / 4.0f, math::Y_AXIS);
tools::resampleToMatch<tools::BoxSampler>(*gridA, *gridB);
FloatGrid::Ptr result = gridA->deepCopy();
FloatGrid::Ptr gridB2 = gridB->deepCopy();
tools::csgUnion(*result, *gridB);
openvdb::io::File file_out(argv[2]);
GridPtrVec grids;
grids.push_back(gridA);
grids.push_back(gridB2);
grids.push_back(result);
file_out.write(grids);
file_out.close();
return 0;
}
Reference: OpenVDB Forum

Geometry rounding problems: object no longer convex after simple transformations

I'm making a little app to analyze geometry. In one part of my program, I use an algorithm that has to have a convex object as input. Luckily, all my objects are initially convex, but some are just barely so (see image).
After I apply some transformations, my algorithm fails to work (it produces "infinitely" long polygons, etc), and I think this is because of rounding errors as in the image; the top vertex in the cylinder gets "pushed in" slightly because of rounding errors (very exaggerated in image) and is no longer convex.
So my question is: Does anyone know of a method to "slightly convexify" an object? Here's one method I tried to implement but it didn't seem to work (or I implemented it wrong):
1. Average all vertices together to create a vertex C inside the convex shape.
2. Let d[v] be the distance from C to vertex v.
3. Scale each vertex v from the center C with the scale factor 1 / (1+d[v] * CONVEXIFICATION_FACTOR)
Thanks!! I have CGAL and Boost installed so I can use any of those library functions (and I already do).
You can certainly make the object convex by computing the convex hull of it. But that'll "convexify" anything. If you're sure your input has departed only slightly from being convex, then it shouldn't be a problem.
CGAL appears to have an implementation of 3D Quickhull in it, which would be the first thing to try. See http://doc.cgal.org/latest/Convex_hull_3/ for docs and some example programs. (I'm not sufficiently familiar with CGAL to want to reproduce any examples and claim they're correct.)
In the end I discovered the root of this problem was the fact that the convex hull contained lots of triangles, whereas my input shapes were often cube-shaped, making each quadrilateral region appear as 2 triangles which had extremely similar plane equations, causing some sort of problem in the algorithm I was using.
I solved it by "detriangulating" the polyhedra, using this code. If anyone can spot any improvements or problems, let me know!
#include <algorithm>
#include <cmath>
#include <vector>
#include <CGAL/convex_hull_traits_3.h>
#include <CGAL/convex_hull_3.h>
typedef Kernel::Point_3 Point;
typedef Kernel::Vector_3 Vector;
typedef Kernel::Aff_transformation_3 Transformation;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
struct Plane_from_facet {
Polyhedron::Plane_3 operator()(Polyhedron::Facet& f) {
Polyhedron::Halfedge_handle h = f.halfedge();
return Polyhedron::Plane_3(h->vertex()->point(),
h->next()->vertex()->point(),
h->opposite()->vertex()->point());
}
};
inline static double planeDistance(Plane &p, Plane &q) {
double sc1 = max(abs(p.a()),
max(abs(p.b()),
max(abs(p.c()),
abs(p.d()))));
double sc2 = max(abs(q.a()),
max(abs(q.b()),
max(abs(q.c()),
abs(q.d()))));
Plane r(p.a() * sc2,
p.b() * sc2,
p.c() * sc2,
p.d() * sc2);
Plane s(q.a() * sc1,
q.b() * sc1,
q.c() * sc1,
q.d() * sc1);
return ((r.a() - s.a()) * (r.a() - s.a()) +
(r.b() - s.b()) * (r.b() - s.b()) +
(r.c() - s.c()) * (r.c() - s.c()) +
(r.d() - s.d()) * (r.d() - s.d())) / (sc1 * sc2);
}
static void detriangulatePolyhedron(Polyhedron &poly) {
vector<Polyhedron::Halfedge_handle> toJoin;
for (auto edge = poly.edges_begin(); edge != poly.edges_end(); edge++) {
auto f1 = edge->facet();
auto f2 = edge->opposite()->facet();
if (planeDistance(f1->plane(), f2->plane()) < 1E-5) {
toJoin.push_back(edge);
}
}
for (auto edge = toJoin.begin(); edge != toJoin.end(); edge++) {
poly.join_facet(*edge);
}
}
...
Polyhedron convexHull;
CGAL::convex_hull_3(shape.begin(),
shape.end(),
convexHull);
transform(convexHull.facets_begin(),
convexHull.facets_end(),
convexHull.planes_begin(),
Plane_from_facet());
detriangulatePolyhedron(convexHull);
Plane bounds[convexHull.size_of_facets()];
int boundCount = 0;
for (auto facet = convexHull.facets_begin(); facet != convexHull.facets_end(); facet++) {
bounds[boundCount++] = facet->plane();
}
...
This gave the desired result (after and before):