Forked from
gmsh / gmsh
17011 commits behind the upstream repository.
-
Jean-François Remacle authoredJean-François Remacle authored
GUI_Classifier.cpp 13.30 KiB
#include "GUI_Classifier.h"
#include "Geo.h"
#include "Gmsh.h"
#include "Draw.h"
#include "Options.h"
#include "Context.h"
#include "SelectBuffer.h"
#include "GUI_Projection.h"
#include "GUI_Extras.h"
#include "Message.h"
#include "meshGFaceDelaunayInsertion.h"
#include "meshGFaceOptimize.h"
#include "gmshEdge.h"
#include "gmshFace.h"
extern Mesh *THEM;
extern Context_T CTX;
void buildListOfEdgeAngle ( e2t_cont adj,std::vector<edge_angle> &edges_detected,std::vector<edge_angle> &edges_lonly);
int maxEdgeNum ()
{
GModel::eiter it = GModel::current()->firstEdge();
GModel::eiter ite = GModel::current()->lastEdge();
int MAXX = 0;
while (it != ite)
{
MAXX = std::max (MAXX, (*it)->tag());
++it;
}
return MAXX;
}
int maxFaceNum ()
{
GModel::fiter it = GModel::current()->firstFace();
GModel::fiter ite = GModel::current()->lastFace();
int MAXX = 0;
while (it != ite)
{
MAXX = std::max (MAXX, (*it)->tag());
++it;
}
return MAXX;
}
struct compareMLinePtr
{
bool operator () ( MLine *l1 , MLine *l2) const
{
static Less_Edge le;
return le(l1->getEdge(0),l2->getEdge(0));
}
};
void recurClassify ( MTri3 *t ,
std::vector<MTriangle *> &triangles,
std::map<MLine*, GEdge*, compareMLinePtr> &lines,
std::set<GEdge*> &closure)
{
if (!t->isDeleted())
{
triangles.push_back(t->tri());
t->setDeleted ( true );
for (int i=0;i<3;i++)
{
MTri3 *tn = t->getNeigh(i);
if (tn)
{
edgeXface exf ( t, i);
MLine ml (exf.v[0],exf.v[1]);
std::map<MLine*, GEdge*, compareMLinePtr>::iterator it = lines.find(&ml);
if (it==lines.end())
recurClassify (tn, triangles,lines, closure);
else
closure.insert(it->second);
}
}
}
}
void class_color_cb(Fl_Widget* w, void* data)
{
classificationEditor *e = (classificationEditor*)data;
std::map<MLine*, GEdge*, compareMLinePtr> lines;
{
GModel::eiter it = GModel::current()->firstEdge();
GModel::eiter ite = GModel::current()->lastEdge();
for( ;it!=ite;++it)
{
for (int i=0;i<(*it)->lines.size();i++)lines[(*it)->lines[i] ] = *it;
}
}
std::list<MTri3*> tris;
{
std::set<GFace*>::iterator it = e->_faces.begin();
while (it != e->_faces.end())
{
GFace *gf = *it;
for (int i=0;i<gf->triangles.size();i++)
{
tris.push_back (new MTri3 ( gf->triangles [i] , 0 ) );
}
gf->triangles.clear();
++it;
}
}
connectTriangles (tris);
{
std::list<MTri3*> ::iterator it = tris.begin();
while (it != tris.end())
{
if (!(*it)->isDeleted())
{
std::set<GEdge*> closure;
std::vector<MTriangle*> triangles;
gmshFace *temporary = new gmshFace ( GModel::current(), maxFaceNum() + 1);
recurClassify ( *it , temporary->triangles,lines, closure);
GModel::current()->add (temporary);
e->tempFaces.push_back(temporary);
}
++it;
}
it = tris.begin();
while (it != tris.end())
{
delete *it;
++it;
}
}
CTX.mesh.changed = ENT_ALL;
Draw();
Msg(ONSCREEN, "");
}
void updateedges_cb(Fl_Widget* w, void* data)
{
classificationEditor *e = (classificationEditor*)data;
// printf("%d edges detected\n",e->edges_detected.size());
for (int i=0;i<e->temporary->lines.size();i++)
{
delete e->temporary->lines[i];
}
e->temporary->lines.clear();
for ( int i=0 ; i<e->edges_detected.size();i++)
{
edge_angle ea = e->edges_detected[i];
// printf("angle = %g\n",ea.angle);
if ( ea.angle <= e -> _inputs[CLASSVALUE_ANGLE] ->value() / 180 * M_PI)
break;
e->temporary->lines.push_back(new MLine(ea.v1, ea.v2));
}
CTX.mesh.changed = ENT_ALL;
Draw();
}
edge_angle:: edge_angle ( MVertex *_v1, MVertex *_v2, MTriangle *t1, MTriangle *t2)
: v1(_v1), v2(_v2)
{
if (!t2) angle = 0;
else
{
double c1[3];
double c2[3];
double c3[3];
{
MVertex *p1 = t1->getVertex(0);
MVertex *p2 = t1->getVertex(1);
MVertex *p3 = t1->getVertex(2);
double a[3] = { p1->x() - p2->x(), p1->y() - p2->y(), p1->z() - p2->z() };
double b[3] = { p1->x() - p3->x(), p1->y() - p3->y(), p1->z() - p3->z() };
c1[2] = a[0] * b[1] - a[1] * b[0];
c1[1] = -a[0] * b[2] + a[2] * b[0];
c1[0] = a[1] * b[2] - a[2] * b[1];
}
{
MVertex *p1 = t2->getVertex(0);
MVertex *p2 = t2->getVertex(1);
MVertex *p3 = t2->getVertex(2);
double a[3] = { p1->x() - p2->x(), p1->y() - p2->y(), p1->z() - p2->z() };
double b[3] = { p1->x() - p3->x(), p1->y() - p3->y(), p1->z() - p3->z() };
c2[2] = a[0] * b[1] - a[1] * b[0];
c2[1] = -a[0] * b[2] + a[2] * b[0];
c2[0] = a[1] * b[2] - a[2] * b[1];
}
norme(c1);
norme(c2);
prodve(c1,c2,c3);
double cosa ; prosca(c1,c2,&cosa);
double sina = norme (c3);
angle = atan2(sina,cosa);
}
}
void buildListOfEdgeAngle ( e2t_cont adj,std::vector<edge_angle> &edges_detected,std::vector<edge_angle> &edges_lonly)
{
e2t_cont::iterator it = adj.begin();
for ( ; it!=adj.end();++it )
{
if ( it->second.second ) edges_detected.push_back ( edge_angle ( it->first.getVertex (0) ,it->first.getVertex (1) ,
it->second.first,it->second.second) );
else edges_lonly.push_back ( edge_angle ( it->first.getVertex (0) ,it->first.getVertex (1) ,
it->second.first,it->second.second) );
}
std::sort ( edges_detected .begin() , edges_detected .end() );
}
classificationEditor::classificationEditor()
{
// construct GUI in terms of standard sizes
const int BH = 2 * GetFontSize() + 1, BB = 12 * GetFontSize(), WB = 7;
const int width = (int)(3.5 * BB), height = 10 * BH;
_window = new Dialog_Window(width, height, "Classify");
Fl_Tabs *o = new Fl_Tabs(WB, WB, width - 2 * WB, height - 2 * WB);
{
Fl_Group *o = new Fl_Group(WB, WB + BH, width - 2 * WB, height - 2 * WB - BH, "Edge Detection");
// o->hide();
// create all widgets (we construct this once, we never deallocate!)
_buttons[CLASSBUTTON_SELECT] =
new Fl_Button (2*WB, 2*WB+1*BH, BB, BH, "Select Elements");
_buttons[CLASSBUTTON_SELECT]->callback(class_select_cb, this);
_togbuttons[CLASSTOGBUTTON_HIDE] =
new Fl_Toggle_Button(3*WB+BB, 2*WB+1*BH, BB, BH, "Hide Unselected");
_togbuttons[CLASSTOGBUTTON_HIDE]->callback(hide_cb,this);
_inputs[CLASSVALUE_ANGLE] =
new Fl_Value_Input(2*WB, 3*WB+2*BH, BB, BH, "Treshold Angle");
_inputs[CLASSVALUE_ANGLE]->value(40);
_inputs [CLASSVALUE_ANGLE]->maximum(90);
_inputs[CLASSVALUE_ANGLE]->minimum(0);
_inputs[CLASSVALUE_ANGLE]->align(FL_ALIGN_RIGHT);
_inputs[CLASSVALUE_ANGLE]->step(1);
_inputs[CLASSVALUE_ANGLE]->when(FL_WHEN_RELEASE);
_inputs[CLASSVALUE_ANGLE]->callback(updateedges_cb,this);
_buttons[CLASSBUTTON_DEL] =
new Fl_Button (2*WB, 5*WB+4*BH, BB, BH, "Delete Edge");
_buttons[CLASSBUTTON_DEL]->callback(class_deleteedge_cb, this);
_buttons[CLASSBUTTON_ADD] =
new Fl_Button (2*WB, 6*WB+5*BH, BB, BH, "Save Selection");
_buttons[CLASSBUTTON_ADD]->callback(class_save_cb, this);
_buttons[CLASSBUTTON_CLEAR] =
new Fl_Button (2*WB, 7*WB+6*BH, BB, BH, "Clear All");
_buttons[CLASSBUTTON_CLEAR]->callback(class_clear_cb, this);
o->end();
}
{
Fl_Group *o = new Fl_Group(WB, WB + BH, width - 2 * WB, height - 2 * WB - BH, "Face Colouring");
o->hide();
_buttons[CLASSBUTTON_SELFAC] =
new Fl_Button (2*WB, 2*WB+1*BH, BB, BH, "Select Model Face");
_buttons[CLASSBUTTON_SELFAC]->callback(class_selectgface_cb, this);
_buttons[CLASSBUTTON_COLOR] =
new Fl_Button (2*WB, 3*WB+2*BH, BB, BH, "Classify Mesh Faces");
_buttons[CLASSBUTTON_COLOR]->callback(class_color_cb, this);
o->end();
}
// allocate detected edges
// temporary for the selection
// saved for the ones that have been saved by the user
// and that will be used for next step
temporary = new gmshEdge ( GModel::current(), maxEdgeNum() + 1 );
GModel::current()->add (temporary);
saved = new gmshEdge ( GModel::current(), maxEdgeNum() + 1 );
GModel::current()->add (saved);
_window->end();
_window->hotspot(_window);
_window->size_range(width, (int)(0.85 * height));
}
void class_select_cb(Fl_Widget *w, void *data)
{
classificationEditor *e = (classificationEditor*)data;
std::vector<GVertex*> vertices;
std::vector<GEdge*> edges;
std::vector<GFace*> faces;
std::vector<GRegion*> regions;
std::vector<MElement*> elements;
std::vector<MTriangle*> &ele(e->getElements());
CTX.pick_elements = 1;
while(1) {
CTX.mesh.changed = ENT_ALL;
Draw();
Msg(ONSCREEN, "Select Elements\n"
"[Press 'e' to end selection or 'q' to abort]");
char ib = SelectEntity(ENT_ALL, vertices, edges, faces, regions, elements);
if(ib == 'l') {
if(CTX.pick_elements){
for(unsigned int i = 0; i < elements.size(); i++){
if(elements[i]->getNumEdges() == 3 && elements[i]->getVisibility() != 2){
elements[i]->setVisibility(2); ele.push_back((MTriangle*)elements[i]);
}
}
}
}
if(ib == 'r') {
for(unsigned int i = 0; i < elements.size(); i++)
elements[i]->setVisibility(1);
}
// ok, we compute edges !
if(ib == 'e') {
ZeroHighlight();
e2t_cont adj;
buildEdgeToTriangle (ele , adj );
buildListOfEdgeAngle ( adj,e->edges_detected,e->edges_lonly);
break;
}
// do nothing
if(ib == 'q') {
ZeroHighlight();
ele.clear();
break;
}
}
updateedges_cb(0, data);
CTX.mesh.changed = ENT_ALL;
CTX.pick_elements = 0;
Draw();
Msg(ONSCREEN, "");
}
void class_selectgface_cb(Fl_Widget *w, void *data)
{
classificationEditor *e = (classificationEditor*)data;
std::vector<GVertex*> vertices;
std::vector<GEdge*> edges;
std::vector<GFace*> faces;
std::vector<GFace*> temp;
std::vector<GRegion*> regions;
std::vector<MElement*> elements;
opt_geometry_surfaces(0, GMSH_SET | GMSH_GUI, 1);
while(1) {
CTX.mesh.changed = ENT_ALL;
Draw();
Msg(ONSCREEN, "Select Model Face\n"
"[Press 'e' to end selection or 'q' to abort]");
char ib = SelectEntity(ENT_SURFACE, vertices, edges, faces, regions, elements);
if(ib == 'l') {
for(unsigned int i = 0; i < faces.size(); i++){
HighlightEntity(faces[i]);
temp.push_back(faces[i]);
}
}
// ok store the list of gfaces !
if(ib == 'e') {
ZeroHighlight();
for(unsigned int i = 0; i < temp.size(); i++){
e->_faces.insert (temp[i]);
}
break;
}
// do nothing
if(ib == 'q') {
ZeroHighlight();
break;
}
}
CTX.mesh.changed = ENT_ALL;
Draw();
Msg(ONSCREEN, "");
}
void class_deleteedge_cb(Fl_Widget *w, void *data)
{
classificationEditor *e = (classificationEditor*)data;
std::vector<GVertex*> vertices;
std::vector<GEdge*> edges;
std::vector<GFace*> faces;
std::vector<GRegion*> regions;
std::vector<MElement*> elements;
std::vector<MLine*> ele;
CTX.pick_elements = 1;
while(1) {
CTX.mesh.changed = ENT_ALL;
Draw();
Msg(ONSCREEN, "Select Elements\n"
"[Press 'e' to end selection or 'q' to abort]");
char ib = SelectEntity(ENT_ALL, vertices, edges, faces, regions, elements);
if(ib == 'l') {
if(CTX.pick_elements){
for(unsigned int i = 0; i < elements.size(); i++){
if(elements[i]->getNumEdges() == 1 && elements[i]->getVisibility() != 2){
elements[i]->setVisibility(2); ele.push_back((MLine*)elements[i]);
}
}
}
}
if(ib == 'r') {
for(unsigned int i = 0; i < elements.size(); i++)
elements[i]->setVisibility(1);
}
// ok, we compute edges !
if(ib == 'e') {
ZeroHighlight();
break;
}
// do nothing
if(ib == 'q') {
ZeroHighlight();
ele.clear();
break;
}
}
std::sort (ele.begin(),ele.end());
// look in all temporary edges if a deleted one is present and delete it !
std::vector<MLine*> temp = e->temporary->lines;
e->temporary->lines.clear();
for(int i=0;i<temp.size();i++)
{
std::vector<MLine*>::iterator it = std::find (ele.begin(),ele.end(),temp[i]);
if (it != ele.end())
{
delete temp[i];
}
else e->temporary->lines.push_back(temp[i]);
}
CTX.mesh.changed = ENT_ALL;
CTX.pick_elements = 0;
Draw();
Msg(ONSCREEN, "");
}
void class_save_cb(Fl_Widget *w, void *data)
{
classificationEditor *e = (classificationEditor*)data;
e->saved->lines.insert (e->saved->lines.end(), e->temporary->lines.begin(), e->temporary->lines.end());
e->temporary->lines.clear();
e->_elements.clear();
e->edges_detected.clear();
CTX.mesh.changed = ENT_ALL;
CTX.pick_elements = 0;
Draw();
Msg(ONSCREEN, "");
}
void class_clear_cb(Fl_Widget *w, void *data)
{
classificationEditor *e = (classificationEditor*)data;
for (int i=0;i<e->temporary->lines.size();i++)
{
delete e->temporary->lines[i];
}
e->temporary->lines.clear();
for (int i=0;i<e->saved->lines.size();i++)
{
delete e->saved->lines[i];
}
e->saved->lines.clear();
CTX.mesh.changed = ENT_ALL;
CTX.pick_elements = 0;
Draw();
Msg(ONSCREEN, "");
}
void mesh_classify_cb(Fl_Widget* w, void* data)
{
// create the (static) editor
static classificationEditor *editor = 0;
if(!editor){
editor = new classificationEditor();
}
editor->show();
}