How to perform Boolean operation on thin surface using libigl? - graphics

I am currently working on libigl, and trying to grab the part of the a surface which locates inside another body. However, it seems than libigl only works with closed bodies:
Here is the code which works for closed bodies. VA,VF is a triangular prism and VB, FB is a tetrahedron:
#include <igl/readOFF.h>
//#define IGL_NO_CORK
//#undef IGL_STATIC_LIBRARY
#include <igl/copyleft/cgal/mesh_boolean.h>
#include <igl/viewer/Viewer.h>
#include <Eigen/Core>
#include <iostream>
Eigen::MatrixXd VA,VB,VC;
Eigen::VectorXi J,I;
Eigen::MatrixXi FA,FB,FC;
igl::MeshBooleanType boolean_type(
igl::MESH_BOOLEAN_TYPE_UNION);
const char * MESH_BOOLEAN_TYPE_NAMES[] =
{
"Union",
"Intersect",
"Minus",
"XOR",
"Resolve",
};
bool key_down(igl::viewer::Viewer &viewer, unsigned char key, int mods)
{
switch(key)
{
default:
return false;
case 'A':
viewer.data.clear();
std::cout << "Loading A" << std::endl;
viewer.data.set_mesh(VA, FA);
break;
case 'B':
viewer.data.clear();
std::cout << "Loading B" << std::endl;
viewer.data.set_mesh(VB, FB);
break;
case 'C':
viewer.data.clear();
std::cout << "Loading C" << std::endl;
viewer.data.set_mesh(VC, FC);
return true;
}
return true;
}
int main(int argc, char *argv[])
{
using namespace Eigen;
using namespace std;
double prismSize = 150;
double Heigh = 300;
VA.resize(6, 3);
VA << -prismSize, prismSize, 0,
prismSize, prismSize, 0,
0, 2 * prismSize, 0,
-prismSize, prismSize, Heigh,
prismSize, prismSize, Heigh,
0, 2 * prismSize, Heigh;
FA.resize(8, 3);
FA << 1, 0, 2,
5, 3, 4,
4, 1, 2,
2, 5, 4,
3, 5, 2,
2, 0, 3,
0, 1, 4,
4, 3, 0;
double tetsize = 300;
VB.resize(4, 3);
VB << 0, 0, tetsize,
-tetsize, 0, 0,
tetsize, 0, 0,
0, tetsize*2, 0;
FB.resize(4, 3);
FB << 2, 1, 3,
2, 0, 1,
3, 0, 2,
1, 0, 3;
igl::copyleft::cgal::mesh_boolean(VA, FA, VB, FB, igl::MESH_BOOLEAN_TYPE_INTERSECT, VC, FC);
std::cout
<< "VA:" << std::endl << VA << std::endl << "==============" << std::endl
<< "FA:" << std::endl << FA << std::endl << "==============" << std::endl
<< "VB:" << std::endl << VB << std::endl << "==============" << std::endl
<< "FB:" << std::endl << FB << std::endl << "==============" << std::endl
<< "VC:" << std::endl << VC << std::endl << "==============" << std::endl
<< "FC:" << std::endl << FC << std::endl << "==============" << std::endl;
// Plot the mesh with pseudocolors
igl::viewer::Viewer viewer;
viewer.data.set_mesh(VA, FA);
//viewer.data.set_mesh(VB, FB);
//viewer.data.set_mesh(VC, FC);
viewer.core.show_lines = true;
viewer.callback_key_down = &key_down;
viewer.core.camera_dnear = 3.9;
cout<<
"Press '.' to switch to next boolean operation type."<<endl<<
"Press ',' to switch to previous boolean operation type."<<endl<<
"Press ']' to push near cutting plane away from camera."<<endl<<
"Press '[' to pull near cutting plane closer to camera."<<endl<<
"Hint: investigate _inside_ the model to see orientation changes."<<endl;
viewer.launch();
}
However, if I deleted one of the surfaces from A or B, like below for example:
//FA.resize(8, 3);
FA.resize(7, 3);
//FA << 1, 0, 2,
FA << 5, 3, 4,
4, 1, 2,
2, 5, 4,
3, 5, 2,
2, 0, 3,
0, 1, 4,
4, 3, 0;
The result mesh C will be empty (I want to get a open thin surface instead of a closed body). I think I am using the wrong function. Anyone knows how to do this?

You might be looking for igl::copyleft::cgal::trim_with_solid. This assumes you have an arbitrary mesh A and you have another closed mesh (actually a solid or PWN mesh) B. The output will be a mesh of the same surface as A but with new edges added so that every face can be either flagged as inside B or outside/on B. Using the flags in the output list D, it's then trivial to extract the portion of A "trimmed" by B.
igl::copyleft::cgal::trim_with_solid(VA,FA,VB,FB,V,F,D,J);

Boolean geometry requires that all surfaces are manifold without boundary. Any operations will result in an empty set otherwise. Here is a simple explination of manifolds:
Manifolds
...and more reasoning on your question:
Boolean Operations

As described by #Eric Bischoff , Boolean geometry requires that all surfaces are manifold. However, I found a workaround for this, which at least fits with my requirement:
Assume that A is an arbitrary open surface and B is a closed manifold, and you want to cut off the outside part of surface A.
The workaround I want to describes, is to complete A to make it a manifold (A') firstly. Then, subtract A' from B, and find the newly created facets, which exactly constitute the open thin surface you want.

Related

Intersection between polygons as line Clipper

I'm trying to use the Clipper C++ library to implement an is_bordering function, as shown below.
bool is_bordering(Path p1, Path p2) {
Paths solutions;
Clipper c;
// execute intersection on paths
c.AddPath(p1, ptSubject, true);
c.AddPath(p2, ptClip, true);
c.Execute(ctIntersection, solutions, pftNonZero);
return (solutions.size() > 0); // the paths share edges
}
int main() {
Path p1, p2;
p1 << IntPoint(0,0) << IntPoint(1,0) << IntPoint(0,1) << IntPoint(0,0);
p2 << IntPoint(1,0) << IntPoint(1,1) << IntPoint(0,1) << IntPoint(1,0);
cout << is_bordering(p1, p2) << endl;
}
I thought that when two bordering polygons were tested with ctIntersection the result would contain the bordering edges, but for me this returns false. What I expect from the above would be the following, with green representing the solutions Paths.
How do I get this function working (with the Clipper library)?
The polygons in your example are not intersecting, so the function is_bordering() is returning 0 as expected. The union of adjacent polygons will be a single polygon, so you could test for that too:
#include "clipper.hpp"
#include <iostream>
using namespace std;
using namespace ClipperLib;
bool is_bordering(Path p1, Path p2) {
Paths _intersection, _union;
Clipper c;
c.AddPath(p1, ptSubject, true);
c.AddPath(p2, ptClip, true);
c.Execute(ctIntersection, _intersection, pftNonZero );
c.Execute(ctUnion, _union, pftNonZero);
return (_intersection.size() > 0 || _union.size() < 2);
}
int main() {
Path p1, p2;
cInt I = 10;
p1 << IntPoint(0, 0) << IntPoint(I, 0) << IntPoint(0, I) << IntPoint(0, 0);
p2 << IntPoint(I, 0) << IntPoint(I, I) << IntPoint(0, I) << IntPoint(I, 0);
cout << is_bordering(p1, p2) << endl;
}
This works only for the case when one polygon is not entirely inside the other.

I'm trying to make a loop to draw multiple lines in cairo but it stops drawing after the first iteration

I'm making a program where a person can input a direction and in the if statement, it adds/subtracts x/y axis and it draws a line after it gets over. The problem is that for some reason, it only works at the first iteration and doesn't draw any more lines after that.
I added a cin >> x >> y to test it out but it only draws one line and doesn't draw anymore.
Initially, the choices were in a switch statement but I changed to if because I thought that was causing the error.
#include "pch.h"
#include <iostream>
#include <fstream>
#include <string.h>
#include <cairo.h>
#define _USE_MATH_DEFINES
#include <math.h>
using namespace std;
char b = NULL;
char u = 'œ';
char d = 'd';
int main()
{
cairo_surface_t *surface = cairo_image_surface_create_from_png("background.png");
cairo_t *cr = cairo_create(surface);
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_set_line_width(cr, 5);
double x = 0, y = 240;
cairo_move_to(cr, x, y);
long int count = 0;
int cl = 0;
int crr = 0;
int choice = 0;
int n;
system("cls");
while (choice != 5)
{
cin >> x >> y;
cairo_line_to(cr, x, y);
cairo_stroke(cr);
cairo_surface_write_to_png(surface, "spiral.png");
cout << "Current no. of points are : " << count << "/4096" << endl;
cout << "Enter direction: \n" << endl;
cout << "1 - Top Left \t 2 - Up \t 3 - Top Right " << endl;
cout << "4 - Left \t 5 - Stop \t 6 - Right" << endl;
cout << "7 - Bot. Left \t 8 - Down \t 9 - Bot. Right" << endl << endl;
cout << "Enter you choice: ";
cin >> choice;
if (choice == 1)
cout << "Test";
else
{
//More choices include the direction the person needs to go and it subtracts/adds to the x/y part
cout << "How many times ?: ";
cin >> n;
for (int i = 1; i <= n; i++)
{
x++;
count++;
cl++;
if (cl == 256)
{
cl = 0;
crr++;
}
}
system("cls");
}
}
}
I expect it to draw lines to a particular direction. Say the person inputs right, it draws a line towards right and so on. But here, no lines get drawn at all (except if I add a cin >> x >> y at the start of the while loop, that draws one line and that's it, no more lines.)
This fails because there is no current point anymore. After cairo_stroke(cr);, you can add cairo_move_to(cr, x, y); and it should start drawing more lines in the way you expect. I think... I'm not quite sure what you are up to with this program.

MPI_Gather Vector of Objects

I am writing a mpich program for parallel sorting. I need to use the mpi_gather interface, but it doesn't support passing vector of objects. So I use boost_serialization.
Implementation
I use boost_serialization to serialize the vector.
std::string serial_str;
boost::iostreams::back_insert_device<std::string> inserter(serial_str);
boost::iostreams::stream<boost::iostreams::back_insert_device<std::string>> s(inserter);
boost::archive::binary_oarchive send_ar(s);
//samples is the vector<object>
send_ar << samples;
s.flush();
int len = serial_str.size();
Then, I use mpi_gather to send all the serial_str to root process(data_recv).
char *data_recv = NULL;
if(myid == 0){
data_recv = (char*)malloc(sizeof(char) * (len_all+1));
data_recv[len_all] = '\0';
}
MPI_Gather((void*)serial_str.data(), len, MPI_BYTE, data_recv, len, MPI_BYTE, 0, MPI_COMM_WORLD);
Finally, I deserialize the data in data_recv.
boost::iostreams::basic_array_source<char> device(data_recv,len_all);
boost::iostreams::stream<boost::iostreams::basic_array_source<char>> s(device);
boost::archive::binary_iarchive recv_ar(s);
std::vector<mdata> recv_vec;
recv_ar >> recv_vec;
My implementation is based on How to send a set object in MPI_Send
Problem
I can't deserialize the data in data_recv correctly. I printed the data_recv, then I found the data in data_recv is incorrectly formatted after mpi_gather. The second archive covered the first.(marked in bold)
serialization::archive
XylvXe-M X 00000000000000000000000000002595 DDDDFFFFCCCCBBBB111133332222DDDD333388888888FFFF2222serialization::archive000000000000000000023D0 EEEE7777EEEE44447777BBBB8888AAAA0000AAAAAAAAFFFF1111
XylvXe-M X 00000000000000000000000000002595 DDDDFFFFCCCCBBBB111133332222DDDD333388888888FFFF2222O#f&!O,t.b X 000000000000000000000000000023D0 EEEE7777EEEE44447777BBBB8888AAAA0000AAAAAAAAFFFF1111
The correct format should be:(no overlap so I can deserialize)
serialization::archive
XylvXe-M X 00000000000000000000000000002595 DDDDFFFFCCCCBBBB111133332222DDDD333388888888FFFF2222O#f&!O,t.b X 000000000000000000000000000023D0 EEEE7777EEEE44447777BBBB8888AAAA0000AAAAAAAAFFFF1111
serialization::archive
XylvXe-M X 00000000000000000000000000002595 DDDDFFFFCCCCBBBB111133332222DDDD333388888888FFFF2222O#f&!O,t.b X 000000000000000000000000000023D0 EEEE7777EEEE44447777BBBB8888AAAA0000AAAAAAAAFFFF1111
Question
Why did this happen? Is it because the mpi_gather isn't compatible with c++ object?
If someone could help me out, it would solve my big problem.
Thank you!
code
//processor rank, and total number of processors
int myid, world_size;
//for timing used by root processor
double startwtime = 0.0, endwtime;
//init MPI World
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
//get the processor name
char processor_name[MPI_MAX_PROCESSOR_NAME];
int name_len;
MPI_Get_processor_name(processor_name,&name_len);
//read local data
std::vector<mdata> mdatas;
string data_path = "/home/jiang/mpi_data";
readAsciiData(data_path, mdatas);
cout <<"rank: "<<myid <<" mdata_vector.size(): "<<mdatas.size()<<endl;
//local sort according ASCII order
std::sort(mdatas.begin(), mdatas.end());
//regular sample
std::vector<mdata> samples;
for(int i=0; i<mdatas.size(); i=i+mdatas.size()/world_size){
samples.push_back(mdatas[i]);
}
//gather the regular samples
//passing data in byte stream by using boost serialization
std::string serial_str;
boost::iostreams::back_insert_device<std::string> inserter(serial_str);
boost::iostreams::stream<boost::iostreams::back_insert_device<std::string>> s(inserter);
boost::archive::binary_oarchive send_ar(s);
send_ar << samples;
s.flush();
int len = serial_str.size();
//int len = s.str().size();
int *pivot_lens = NULL;
if(myid == 0){
pivot_lens = (int*)malloc(sizeof(int) * world_size);
}
cout <<serial_str <<endl;
//first, gathering the lens and calculate the sum
cout << "rank " << myid << " on "<< processor_name << " is sending len: "<< len << endl;
MPI_Gather(&len, 1, MPI_INT, pivot_lens, 1, MPI_INT, 0, MPI_COMM_WORLD);
//calculate the sum of lens
int len_all = 0;
if(myid == 0){
for(int i=0;i<world_size;i++){
len_all = len_all + pivot_lens[i];
//cout << pivot_lens[i] << endl;
}
cout << "len_all:" << len_all << endl;
free(pivot_lens);
}
//then, gathering string of bytes from all the processes
char *data_recv = NULL;
if(myid == 0){
data_recv = (char*)malloc(sizeof(char) * (len_all+1));
data_recv[len_all] = '\0';
}
MPI_Gather((void*)serial_str.data(), len, MPI_BYTE, data_recv, len, MPI_BYTE, 0, MPI_COMM_WORLD);
// cout << serial_str <<endl;
if(myid == 0){
//deconstructe from byte of string to vector<mdata>
boost::iostreams::basic_array_source<char> device(data_recv,len_all);
boost::iostreams::stream<boost::iostreams::basic_array_source<char>> s(device);
boost::archive::binary_iarchive recv_ar(s);
std::vector<mdata> recv_vec;
recv_ar >> recv_vec;
int count =0;
for(int i=0;i<len_all;i++){
cout<<data_recv[i];
count ++;
}
cout <<endl <<count ;
cout <<endl;
//cout << "rank " << myid << " gets the samples: " << recv_vec.size()<<endl;
iterateForTest(myid, recv_vec);
free(data_recv);
}
MPI_Finalize();
return 0;

Shape Transformers and Interfaces OpenCV3.0

I was trying to make use of the new Shape Transformers and Interfaces of OpenCV3.0. Unfortunately it doesn't work as expected. To ensure not making any fancy warps and getting strange results cause of that reason I initialized a transformation where nothing at all should happen. But output of the transformation for a testpoint is always [0,0] and the warped image is always completley gray. Any suggestions what could be wrong are welcome.
int main(void){
Mat img1 = imread("C:\\opencv\\sources\\samples\\data\\graf1.png", IMREAD_GRAYSCALE);
std::vector<cv::Point2f> points1, testpoints;
vector<DMatch> good_matches;
Mat respic, resmat;
points1.push_back(Point(0, 0)); //Corners 800x600 pic
points1.push_back(Point(799, 0));
points1.push_back(Point(799, 599));
points1.push_back(Point(0, 599));
Mat pointmatrix1(points1);
good_matches.push_back(DMatch(0, 0, 0));
good_matches.push_back(DMatch(1, 1, 0));
good_matches.push_back(DMatch(2, 2, 0));
good_matches.push_back(DMatch(3, 3, 0));
testpoints.push_back(Point(250, 250));
Mat testpointsmat(testpoints);
// Apply TPS
Ptr<ThinPlateSplineShapeTransformer> mytps = createThinPlateSplineShapeTransformer(0);
mytps->estimateTransformation(pointmatrix1, pointmatrix1, good_matches); // Using same pointmatrix nothing should change in res
mytps->applyTransformation(testpointsmat, resmat);
cout << "pointmatrix1 = " << endl << " " << pointmatrix1 << endl << endl;
cout << "testpointsmat = " << endl << " " << testpointsmat << endl << endl;
cout << "resmat = " << endl << " " << resmat << endl << endl; //Always [0,0] ?
imshow("img1", img1); // Just to see if I have a good picture
mytps->warpImage(img1, respic);
imwrite("Tranformed.png", respic);
imshow("Tranformed", respic); //Always completley grey ?
waitKey(0);
return 0;
}
Don't ask me why but if I add this two lines it works.
// Apply TPS
transpose(pointmatrix1, pointmatrix1); // ADD
transpose(testpoints, testpoints); // ADD
Ptr<ThinPlateSplineShapeTransformer> mytps = createThinPlateSplineShapeTransformer(0);
Now There is something strange in source code here why cols and not rows.
by LBerger

Find angle of right angled triangle

I am trying to work out the angle of a right angled triangle. I have an array containing the lengths of the two sides of the triangle. I also have an array containing the Euclidean Distance between these two points. How would I find the angle of the triangles? In other words, how would I do the sin and then arcsin methods to find the angle? I am just looking for the angle opposite of the hypotenuse. I'm trying to do this in C++.
Solved it now, misinterpreted what I had been asked to do
Solution: How would I find the angle of the triangles
#include <iostream>
#include <cmath>
using namespace std;
#define radians(x) return x * (180/pi)
int main()
{
double opposite, adjacent, angle1, angle2, angle3, choice, radians, hypotenuse;
cout << "Opposite: ";
cin >> opposite;
cout << "Adjacent: ";
cin >> adjacent;
cout << "Radians or Degrees: (R/D)";
cin >> choice;
if(choice == "R")
{
angle1 = arctan(adjacent/opposite);
hypotenuse = opposite\cos(radians(angle1));
angle2 = arcsin(adjacent\hypotenuse);
cout << "Angle 1: "<< radians(angle1) << endl;
cout << "Angle 2: "<< "90\n";
cout << "Angle 3: "<< radians(angle2) << endl;
cout << "Hypotenuse: " << hypotenuse;
}
else if(choice = "D")
{
angle1 = arctan(adjacent/opposite);
hypotenuse = opposite\cos((angle1));
angle2 = arcsin(adjacent\hypotenuse);
cout << "Angle 1: " << (angle1) << endl;
cout << "Angle 2: " << "90\n";
cout << "Angle 3: " << (angle2) << endl;
cout << "Hypotenuse: " << hypotenuse;
}
return 0;
}
or just
angle2 = 180 - (angle1 + 90)
The relation between sides and angles of triangle is:-
a/sinA = b/sinB = c/sinC
where 'a' is the side opposite angle 'A'.
You know one angle let's say it's A = 90. Then you can calculate other two angles from above equation.
You have the lengths of the sides, if you us tangents, you can find the angle for the corresponding side.
Also, once you find one angle, all you need to do is subtract 90 from it to get the final angle:
tan(angle) = opposite/adjacent;
angle = arctan(opposite/adjacent);
otherAngle = 90 - angle;

Resources