Forked from
gmsh / gmsh
15530 commits behind the upstream repository.
-
Jean-François Remacle authoredJean-François Remacle authored
classificationEditor.cpp 21.60 KiB
// Gmsh - Copyright (C) 1997-2009 C. Geuzaine, J.-F. Remacle
//
// See the LICENSE.txt file for license information. Please report all
// bugs and problems to <gmsh@geuz.org>.
#include <FL/Fl_Tabs.H>
#include <FL/Fl_Value_Input.H>
#include "GUI.h"
#include "classificationEditor.h"
#include "paletteWindow.h"
#include "Numeric.h"
#include "Draw.h"
#include "Options.h"
#include "Context.h"
#include "GmshMessage.h"
#include "MLine.h"
#include "meshGFaceDelaunayInsertion.h"
#include "meshGFaceOptimize.h"
#include "discreteEdge.h"
#include "discreteFace.h"
static void NoElementsSelectedMode(classificationEditor *e)
{
e->_buttons[CLASSBUTTON_DEL]->deactivate();
e->_buttons[CLASSBUTTON_ADD]->deactivate();
e->_buttons[CLASSBUTTON_CLEAR]->deactivate();
// e->_buttons[CLASSBUTTON_OK]->deactivate();
e->_togbuttons[CLASSTOGBUTTON_CLOS]->deactivate();
e->_inputs[CLASSVALUE_ANGLE]->deactivate();
e->_buttons[CLASSBUTTON_SELECT]->activate();
e->_togbuttons[CLASSTOGBUTTON_HIDE]->activate();
}
static void ElementsSelectedMode(classificationEditor *e)
{
e->_buttons[CLASSBUTTON_DEL]->activate();
e->_buttons[CLASSBUTTON_ADD]->activate();
e->_buttons[CLASSBUTTON_CLEAR]->activate();
e->_togbuttons[CLASSTOGBUTTON_CLOS]->activate();
e->_inputs[CLASSVALUE_ANGLE]->activate();
// e->_buttons[CLASSBUTTON_OK]->activate();
e->_buttons[CLASSBUTTON_SELECT]->deactivate();
e->_togbuttons[CLASSTOGBUTTON_HIDE]->deactivate();
}
// we should
static void class_selectgface_cb(Fl_Widget *w, void *data)
{
classificationEditor *e = (classificationEditor*)data;
std::vector<GFace*> temp;
opt_geometry_surfaces(0, GMSH_SET | GMSH_GUI, 1);
while(1) {
CTX::instance()->mesh.changed = ENT_ALL;
Draw();
Msg::StatusBar(3, false, "Select Model Face\n"
"[Press 'e' to end selection or 'q' to abort]");
char ib = GUI::instance()->selectEntity(ENT_SURFACE);
if(ib == 'l') {
for(unsigned int i = 0; i < GUI::instance()->selectedFaces.size(); i++){
GUI::instance()->selectedFaces[i]->setSelection(1);
temp.push_back(GUI::instance()->selectedFaces[i]);
}
}
// ok store the list of gfaces !
if(ib == 'e') {
GModel::current()->setSelection(0);
for(unsigned int i = 0; i < temp.size(); i++){
e->_faces.insert(temp[i]);
}
break;
}
// do nothing
if(ib == 'q') {
GModel::current()->setSelection(0);
break;
}
}
CTX::instance()->mesh.changed = ENT_ALL;
Draw();
Msg::StatusBar(3, false, "");
}
static void class_deleteedge_cb(Fl_Widget *w, void *data)
{
classificationEditor *e = (classificationEditor*)data;
std::vector<MLine*> ele;
CTX::instance()->pickElements = 1;
while(1) {
CTX::instance()->mesh.changed = ENT_ALL;
Draw();
Msg::StatusBar(3, false, "Select Elements\n"
"[Press 'e' to end selection or 'q' to abort]");
char ib = GUI::instance()->selectEntity(ENT_ALL);
if(ib == 'l') {
if(CTX::instance()->pickElements){
for(unsigned int i = 0; i < GUI::instance()->selectedElements.size(); i++){
MElement *me = GUI::instance()->selectedElements[i];
if(me->getNumEdges() == 1 && me->getVisibility() != 2){
me->setVisibility(2); ele.push_back((MLine*)me);
}
}
}
}
if(ib == 'r') {
for(unsigned int i = 0; i < GUI::instance()->selectedElements.size(); i++)
GUI::instance()->selectedElements[i]->setVisibility(1);
}
// ok, we compute edges !
if(ib == 'e') {
GModel::current()->setSelection(0);
break;
}
// do nothing
if(ib == 'q') {
GModel::current()->setSelection(0);
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(unsigned 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::instance()->mesh.changed = ENT_ALL;
CTX::instance()->pickElements = 0;
Draw();
Msg::StatusBar(3, false, "");
}
static 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::instance()->mesh.changed = ENT_ALL;
CTX::instance()->pickElements = 0;
NoElementsSelectedMode(e);
Draw();
Msg::StatusBar(3, false, "");
}
static void class_clear_cb(Fl_Widget *w, void *data)
{
classificationEditor *e = (classificationEditor*)data;
for(unsigned int i = 0; i < e->temporary->lines.size(); i++){
delete e->temporary->lines[i];
}
e->temporary->lines.clear();
CTX::instance()->mesh.changed = ENT_ALL;
CTX::instance()->pickElements = 0;
NoElementsSelectedMode(e);
Draw();
Msg::StatusBar(3, false, "");
}
static void class_ok_cb(Fl_Widget *w, void *data)
{
classificationEditor *e = (classificationEditor*)data;
e->edge_detec->deactivate();
e->edge_detec->hide();
e->face_color->activate();
e->face_color->show();
class_save_cb(w,data);
opt_mesh_lines(0, GMSH_SET | GMSH_GUI, e->op[0]);
opt_mesh_surfaces_edges(0, GMSH_SET | GMSH_GUI, e->op[1]);
opt_mesh_surfaces_faces(0, GMSH_SET | GMSH_GUI, e->op[2]);
opt_mesh_line_width(0, GMSH_SET | GMSH_GUI, e->op[3]);
Msg::StatusBar(3, false, "");
}
static 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;
}
static 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));
}
};
static void recurClassify(MTri3 *t, GFace *gf,
std::map<MLine*, GEdge*, compareMLinePtr> &lines,
std::map<MTriangle*, GFace*> &reverse)
{
if(!t->isDeleted()){
gf->triangles.push_back(t->tri());
reverse[t->tri()] = gf;
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, gf, lines, reverse);
}
}
}
}
static GEdge *getNewModelEdge(GFace *gf1, GFace *gf2,
std::map<std::pair<int, int>, GEdge* > &newEdges)
{
int t1 = gf1 ? gf1->tag() : -1;
int t2 = gf2 ? gf2->tag() : -1;
int i1 = std::min(t1,t2);
int i2 = std::max(t1,t2);
if(i1 == i2) return 0;
std::map<std::pair<int, int>, GEdge*>::iterator it =
newEdges.find(std::make_pair<int, int>(i1, i2));
if(it == newEdges.end()){
discreteEdge *temporary = new discreteEdge(GModel::current(), maxEdgeNum() + 1, 0, 0);
printf("add new edge gf1=%d gf2=%d \n", t1, t2);
GModel::current()->add(temporary);
newEdges[std::make_pair<int, int>(i1, i2)] = temporary;
return temporary;
}
else
return it->second;
}
static void recurClassifyEdges(MTri3 *t,
std::map<MTriangle*, GFace*> &reverse,
std::map<MLine*, GEdge*, compareMLinePtr> &lines,
std::set<MLine*> &touched,
std::map<std::pair<int, int>, GEdge*> &newEdges)
{
if(!t->isDeleted()){
t->setDeleted(true);
GFace *gf1 = reverse[t->tri()];
for(int i = 0; i < 3; i++){
GFace *gf2 = 0;
MTri3 *tn = t->getNeigh(i);
if(tn)
gf2 = reverse[tn->tri()];
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()){
if(touched.find(it->first) == touched.end()){
GEdge *ge = getNewModelEdge(gf1, gf2, newEdges);
if(ge) ge->lines.push_back(it->first);
touched.insert(it->first);
}
}
if(tn)
recurClassifyEdges(tn, reverse, lines, touched, newEdges);
}
}
}
static 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(unsigned 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(unsigned int i = 0; i < gf->triangles.size(); i++){
tris.push_back(new MTri3(gf->triangles[i], 0));
}
gf->triangles.clear();
++it;
}
}
if ( !tris.size() )return;
connectTriangles(tris);
{
std::map<MTriangle*,GFace*> reverse;
// color all triangles
std::list<MTri3*> ::iterator it = tris.begin();
while(it != tris.end()){
if(!(*it)->isDeleted()){
discreteFace *temporary = new discreteFace(GModel::current(), maxFaceNum() + 1);
recurClassify(*it, temporary, lines, reverse);
GModel::current()->add(temporary);
}
++it;
}
// color some lines
it = tris.begin();
while(it != tris.end()){
(*it)->setDeleted(false);
++it;
}
it = tris.begin();
//classify edges that are bound by different GFaces
//--------------------------------------------------
std::map<std::pair<int, int>, GEdge*> newEdges;
std::set<MLine*> touched;
recurClassifyEdges(*it, reverse, lines, touched, newEdges);
GModel::current()->remove(e->saved);
//check if new edges should not be splitted
//splitted if composed of several open or closed edges
//-----------------------------------------------------
for (std::map<std::pair<int, int>, GEdge*>::iterator it = newEdges.begin() ; it != newEdges.end() ; ++it){
GEdge *ge = it->second;
printf("NEW edge with tag = %d \n", ge->tag());
std::list<MLine*> segments;
for (unsigned int i = 0; i < ge->lines.size(); i++){
segments.push_back(ge->lines[i]);
}
//for each actual GEdge
while (!segments.empty()) {
std::vector<MLine*> myLines;
std::list<MLine*>::iterator it = segments.begin();
MVertex *vB = (*it)->getVertex(0);
MVertex *vE = (*it)->getVertex(1);
myLines.push_back(*it);
segments.erase(it);
it++;
//printf("***candidate mline %d %d of size %d \n", vB->getNum(), vE->getNum(), segments.size());
for (int i=0; i<2; i++) {
for (std::list<MLine*>::iterator it = segments.begin() ; it != segments.end(); ++it){
MVertex *v1 = (*it)->getVertex(0);
MVertex *v2 = (*it)->getVertex(1);
//printf("mline %d %d \n", v1->getNum(), v2->getNum());
std::list<MLine*>::iterator itp;
if ( v1 == vE ){
//printf("->push back this mline \n");
myLines.push_back(*it);
itp = it;
it++;
segments.erase(itp);
vE = v2;
i = -1;
}
else if ( v2 == vE){
//printf("->push back this mline \n");
myLines.push_back(*it);
itp = it;
it++;
segments.erase(itp);
vE = v1;
i=-1;
}
if (it == segments.end()) break;
}
if (vB == vE) break;
if (segments.empty()) break;
//printf("not found VB=%d vE=%d\n", vB->getNum(), vE->getNum());
MVertex *temp = vB;
vB = vE;
vE = temp;
//printf("not found VB=%d vE=%d\n", vB->getNum(), vE->getNum());
}
// printf("************ CANDIDATE NEW EDGE \n");
// for (std::vector<MLine*>::iterator it = myLines.begin() ; it != myLines.end() ; ++it){
// MVertex *v1 = (*it)->getVertex(0);
// MVertex *v2 = (*it)->getVertex(1);
// printf("Line %d %d \n", v1->getNum(), v2->getNum());
// }
GEdge *newGe = new discreteEdge(GModel::current(), maxEdgeNum() + 1, 0, 0);
newGe->lines.insert(newGe->lines.end(), myLines.begin(), myLines.end());
GModel::current()->add(newGe);
printf("create new edge with tag =%d\n", maxEdgeNum());
}//end for each actual GEdge
}
printf("end new edge with tag \n");
for (std::map<std::pair<int, int>, GEdge*>::iterator it = newEdges.begin() ; it != newEdges.end() ; ++it){
GEdge *ge = it->second;
GModel::current()->remove(ge);
}
while(it != tris.end()){
delete *it;
++it;
}
}
printf("before drawing \n");
CTX::instance()->mesh.changed = ENT_ALL;
Draw();
Msg::StatusBar(3, false, "");
}
static void updateedges_cb(Fl_Widget* w, void* data)
{
classificationEditor *e = (classificationEditor*)data;
printf("%d inside edges detected\n", (int)e->edges_detected.size());
for(unsigned int i = 0; i < e->temporary->lines.size(); i++){
delete e->temporary->lines[i];
}
e->temporary->lines.clear();
for(unsigned 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));
}
printf("%d boundary edges detected\n", (int)e->edges_lonly.size());
if(e->_togbuttons[CLASSTOGBUTTON_CLOS]->value()){
for(unsigned int i = 0 ; i < e->edges_lonly.size(); i++){
edge_angle ea = e->edges_lonly[i];
e->temporary->lines.push_back(new MLine(ea.v1, ea.v2));
//check if closed loop
}
}
CTX::instance()->mesh.changed = ENT_ALL;
Draw();
}
static void class_hide_cb(Fl_Widget *w, void *data)
{
CTX::instance()->hideUnselected = !CTX::instance()->hideUnselected;
CTX::instance()->mesh.changed = ENT_ALL;
Draw();
}
static 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());
}
static void class_select_cb(Fl_Widget *w, void *data)
{
classificationEditor *e = (classificationEditor*)data;
std::vector<MTriangle*> &ele(e->getElements());
CTX::instance()->pickElements = 1;
while(1) {
CTX::instance()->mesh.changed = ENT_ALL;
Draw();
Msg::StatusBar(3, false, "Select Elements\n"
"[Press 'e' to end selection or 'q' to abort]");
char ib = GUI::instance()->selectEntity(ENT_ALL);
if(ib == 'l') {
if(CTX::instance()->pickElements){
for(unsigned int i = 0; i < GUI::instance()->selectedElements.size(); i++){
MElement *me = GUI::instance()->selectedElements[i];
if(me->getNumEdges() == 3 && me->getVisibility() != 2){
me->setVisibility(2); ele.push_back((MTriangle*)me);
}
}
}
}
if(ib == 'r') {
for(unsigned int i = 0; i < GUI::instance()->selectedElements.size(); i++)
GUI::instance()->selectedElements[i]->setVisibility(1);
}
// ok, we compute edges !
if(ib == 'e') {
GModel::current()->setSelection(0);
e2t_cont adj;
buildEdgeToTriangle(ele, adj);
buildListOfEdgeAngle(adj, e->edges_detected, e->edges_lonly);
ElementsSelectedMode(e);
break;
}
// do nothing
if(ib == 'q') {
GModel::current()->setSelection(0);
ele.clear();
break;
}
}
updateedges_cb(0, data);
CTX::instance()->mesh.changed = ENT_ALL;
CTX::instance()->pickElements = 0;
Draw();
Msg::StatusBar(3, false, "");
}
edge_angle::edge_angle(MVertex *_v1, MVertex *_v2, MElement *t1, MElement *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);
}
}
classificationEditor::classificationEditor()
{
op[0] = opt_mesh_lines(0, GMSH_GET, 0.);
op[1] = opt_mesh_surfaces_edges(0, GMSH_GET, 0.);
op[2] = opt_mesh_surfaces_faces(0, GMSH_GET, 0.);
op[3] = opt_mesh_line_width(0, GMSH_SET | GMSH_GET,0.);
opt_mesh_lines(0, GMSH_SET | GMSH_GUI, 1);
opt_mesh_surfaces_edges(0, GMSH_SET | GMSH_GUI, 0);
opt_mesh_surfaces_faces(0, GMSH_SET | GMSH_GUI, 1);
opt_mesh_line_width(0, GMSH_SET | GMSH_GUI, 1.5);
// construct GUI in terms of standard sizes
int BBB = (int)(1.4 * BB); // labels too long
const int width = (int)(3.5 * BBB), height = 10 * BH;
_window = new paletteWindow
(width, height, CTX::instance()->nonModalWindows ? true : false, "Classify");
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");
edge_detec = o;
_buttons[CLASSBUTTON_OK] = new Fl_Button
(4*WB+2*BBB, 7*WB+6*BH, BBB, BH, "OK");
_buttons[CLASSBUTTON_OK]->callback(class_ok_cb, this);
_buttons[CLASSBUTTON_SELECT] = new Fl_Button
(2*WB, 2*WB+1*BH, BBB, BH, "Select Elements");
_buttons[CLASSBUTTON_SELECT]->callback(class_select_cb, this);
_togbuttons[CLASSTOGBUTTON_HIDE] = new Fl_Toggle_Button
(3*WB+BBB, 2*WB+1*BH, BBB, BH, "Hide Unselected");
_togbuttons[CLASSTOGBUTTON_HIDE]->callback(class_hide_cb,this);
_togbuttons[CLASSTOGBUTTON_CLOS] = new Fl_Toggle_Button
(2*WB, 4*WB+3*BH, BBB, BH, "Include Closure");
_togbuttons[CLASSTOGBUTTON_CLOS]->callback(updateedges_cb,this);
_inputs[CLASSVALUE_ANGLE] = new Fl_Value_Input
(3*WB+BBB, 4*WB+3*BH, BBB, 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, BBB, BH, "Delete Edge");
_buttons[CLASSBUTTON_DEL]->callback(class_deleteedge_cb, this);
_buttons[CLASSBUTTON_DEL]->deactivate();
_buttons[CLASSBUTTON_ADD] = new Fl_Button
(2*WB, 6*WB+5*BH, BBB, BH, "Save Selection");
_buttons[CLASSBUTTON_ADD]->callback(class_save_cb, this);
_buttons[CLASSBUTTON_ADD]->deactivate();
_buttons[CLASSBUTTON_CLEAR] = new Fl_Button
(2*WB, 7*WB+6*BH, BBB, BH, "Clear Selection");
_buttons[CLASSBUTTON_CLEAR]->callback(class_clear_cb, this);
_buttons[CLASSBUTTON_CLEAR]->deactivate();
o->end();
}
{
Fl_Group *o = new Fl_Group
(WB, WB + BH, width - 2 * WB, height - 2 * WB - BH, "Face Colouring");
face_color = o;
o->deactivate();
o->hide();
_buttons[CLASSBUTTON_SELFAC] = new Fl_Button
(2*WB, 2*WB+1*BH, BBB, BH, "Select Model Face");
_buttons[CLASSBUTTON_SELFAC]->callback(class_selectgface_cb, this);
_buttons[CLASSBUTTON_COLOR] = new Fl_Button
(2*WB, 3*WB+2*BH, BBB, BH, "Classify Mesh Faces");
_buttons[CLASSBUTTON_COLOR]->callback(class_color_cb, this);
o->end();
}
{
Fl_Group *o = new Fl_Group
(WB, WB + BH, width - 2 * WB, height - 2 * WB - BH, "Reparametrize Surfaces");
reverse_eng = o;
o->hide();
o->deactivate();
o->end();
}
NoElementsSelectedMode(this);
// 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 discreteEdge(GModel::current(), maxEdgeNum() + 1, 0, 0);
GModel::current()->add(temporary);
saved = new discreteEdge(GModel::current(), maxEdgeNum() + 1, 0, 0);
GModel::current()->add(saved);
_window->end();
_window->hotspot(_window);
_window->size_range(width, (int)(0.85 * height));
}
void mesh_classify_cb(Fl_Widget* w, void* data)
{
// create the (static) editor
static classificationEditor *editor = 0;
if(!editor){
editor = new classificationEditor();
}
editor->show();
}