// -*- C++ -*- // $RCSfile: meshcmds.C,v $ // $Revision: 1.23 $ // $Author: langer $ // $Date: 2004/10/26 02:17:17 $ /* This software was produced by NIST, an agency of the U.S. government, * and by statute is not subject to copyright in the United States. * Recipients of this software assume all responsibilities associated * with its operation, modification and maintenance. However, to * facilitate maintenance we ask that before distributing modifed * versions of this software, you first contact the authors at * oof_manager@ctcms.nist.gov. */ #include "adaptmesh.h" #include "amtriangle.h" #include "elector.h" #include "fiddlemesh.h" #include "filename.h" #include "gfx.h" #include "goof.h" #include "imageform.h" #include "material.h" #include "menuDef.h" #include "meshcmds.h" #include "meshgroups.h" #include "mouseclick.h" #include "memusage.h" static Menu *meshgroupmenu(); static CommandFn selectnonecmd(); static MeshGroup *getgroup(); int meshexists() { if(!current_goof) { garcon()->msout << ms_error << "No image!" << endl << ms_normal; return 0; } if(!current_goof->mesh()) { garcon()->msout << ms_error << "No mesh!" << endl << ms_normal; return 0; } return 1; } //=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=// // these functions are called to indicate that the rules for // transferring pixel properties to triangles have changed, so that if // properties are being transferred automatically, they should be // transferred the next time they're needed. // should be static, but Sun doesn't like it... void group_rulechange() { if(current_goof) { AdaptiveMesh *mesh = current_goof->mesh(); if(mesh) { mesh->groups_need_recomputing(); mesh->inherit_pixel_groups(); current_goof->redraw(); } } } static void groups_timechange(const CharString&) { if(current_goof) { AdaptiveMesh *mesh = current_goof->mesh(); if(mesh) mesh->groups_timechange(); } } void AdaptiveMesh::groups_timechange() { if(grouptransfertime == CONTINUALLY) { if(groups_rules_changed > groups_recompute_performed) ++groups_recompute_requested; } } void AdaptiveMesh::groups_need_recomputing() { // Called by other routines to indicate that groups are out of date. ++groups_rules_changed; if(grouptransfertime == CONTINUALLY) ++groups_recompute_requested; } // should be static, but Sun doesn't like it... void material_rulechange() { if(current_goof) { AdaptiveMesh *mesh = current_goof->mesh(); if(mesh) { mesh->materials_need_recomputing(); mesh->inherit_pixel_materials(); current_goof->redraw(); } } } static void material_timechange(const CharString&) { if(current_goof) { AdaptiveMesh *mesh = current_goof->mesh(); if(mesh) mesh->material_timechange(); } } void AdaptiveMesh::material_timechange() { // If the transfertime has just been changed to CONTINUALLY, then if // the rules have changed since the last recomputation, a new // computation is required. if(materialtransfertime == CONTINUALLY) { if(material_rules_changed > material_recompute_performed) ++material_recompute_requested; } } void AdaptiveMesh::materials_need_recomputing() { // Called by other routines to indicate that materials are out of // date. ++material_rules_changed; if(materialtransfertime == CONTINUALLY) { ++material_recompute_requested; } } // when called from menus, these need a dummy argument static void group_rulechange(const CharString&) { group_rulechange(); } static void material_rulechange(const CharString&) { material_rulechange(); } //=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=// class CreateMeshCmd : public CommandM { private: int nx, ny; public: CreateMeshCmd() : CommandM("create", "create initial mesh"), nx(1), ny(1) { AddArgument(this, "nx", nx); AddArgument(this, "ny", ny); } virtual CommandFn func() { if(current_goof) { if(nx <= 0 || ny <= 0) { garcon()->msout << ms_error << "Mesh dimensions must be positive!" << endl << ms_normal; } else { material_rulechange(); group_rulechange(); current_goof->new_mesh(nx, ny); current_goof->redraw(); } } } virtual CommandM *clone() const { CreateMeshCmd *cmc = new CreateMeshCmd; cmc->nx = nx; cmc->ny = ny; return cmc; } }; static CommandFn destroymesh() { if(meshexists()) { current_goof->destroy_mesh(); current_goof->redraw(); } } //=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=// // mesh stack manipulations static CommandFn undomesh() { if(meshexists()) { if(!current_goof->prev_mesh()) garcon()->msout << ms_error << "No previous mesh!" << endl << ms_normal; else current_goof->redraw(); } } static CommandFn redomesh() { if(meshexists()) { if(!current_goof->next_mesh()) garcon()->msout << ms_error << "No next mesh!" << endl << ms_normal; else current_goof->redraw(); } } static CommandFn oldestmesh() { if(meshexists()) { current_goof->oldest_mesh(); current_goof->redraw(); } } static CommandFn newestmesh() { if(meshexists()) { current_goof->newest_mesh(); current_goof->redraw(); } } Menu *meshstackmenu() { static Menu *menu = 0; if(menu) return menu; menu = new Menu("mesh stack"); menu->AddCommand("undo", "undo mesh modifications", undomesh); menu->AddCommand("redo", "redo previously undone mesh modification", redomesh); menu->AddCommand("newest", "display most recent mesh", newestmesh); menu->AddCommand("oldest", "display oldest mesh", oldestmesh); AddVariable(menu, "stacksize", "number of meshes to retain", Goof::meshstacksize, Goof::set_meshstacksize); return menu; } //=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=// class MeshOutputCmd : public CommandM { private: FileName filename; public: MeshOutputCmd() : CommandM("save", "write an OOF file"), filename("out.goof") { AddArgument(this, "filename", filename); } virtual CommandFn func() { if(meshexists()) current_goof->mesh()->writegoof(filename); } virtual CommandM *clone() const { MeshOutputCmd *moc = new MeshOutputCmd; moc->filename = filename; return moc; } }; class OOF2OutputCmd : public CommandM { private: FileName filename; public: OOF2OutputCmd() : CommandM("save_oof2", "write an OOF2 file"), filename("oofout.py") { AddArgument(this, "filename", filename); } virtual CommandFn func() { if(meshexists()) { ofstream os(filename); if(!os) { garcon()->msout << ms_error << "Could not open file: " << filename << "!" << endl << ms_normal; return; } current_goof->mesh()->write_oof2(os, filename); } } virtual CommandM *clone() const { OOF2OutputCmd *o2oc = new OOF2OutputCmd; o2oc->filename = filename; return o2oc; } }; //=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=// static CommandFn findErange() { if(meshexists()) { double min, max; current_goof->mesh()->Erange(min, max); garcon()->msout << "min=" << min << " max=" << max << endl; } } //=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=// // Mesh Refinement Commands class RefineCmd : public CommandM { private: RefinementCondition * const condition; int iterations; public: RefineCmd(RefinementCondition *rc) : condition(rc), iterations(1), CommandM(rc->name(), "refine by " + rc->name()) { AddArgument(this, "iterations", iterations); condition->AddArgs(this); } virtual ~RefineCmd() { delete condition; } virtual CommandFn func() { if(meshexists()) { if(condition->setup()) { current_goof->dup_mesh(); #ifdef MEMUSAGE print_memusage("after duplicating mesh"); #endif current_goof->mesh()->refine(iterations, *condition); #ifdef MEMUSAGE print_memusage("after refining mesh"); #endif current_goof->redraw(); } else garcon()->msout << ms_error << "Cannot refine mesh!" << endl << ms_normal; } } virtual CommandM *clone() const { RefineCmd *rc = new RefineCmd(condition->clone()); rc->iterations = iterations; return rc; } }; static CommandFn refinegroup() { if(getgroup()) { current_goof->dup_mesh(); // Have to get the group again, since it lives in the mesh, and // we've duplicated the mesh! MeshGroup *g = getgroup(); g->weed(); current_goof->mesh()->refine_group(1, g); current_goof->redraw(); } } template <> VarObject *Var >::create_varobject(Variable *var, Form *form, VarObjectType t, int x, int y, int w, int h) { return new EnumVarObject(var, form, t, x, y, w, h); } static Menu *refinemenu() { static Menu *menu = 0; if(menu) return menu; menu = new Menu("refine"); menu->AddCommand(new RefineCmd(new RefineAll)); menu->AddCommand(new RefineCmd(new RefineE)); menu->AddCommand(new RefineCmd(new RefineSelected)); menu->AddCommand(new RefineCmd(new RefineDepth)); menu->AddCommand(new RefineCmd(new RefineInterface)); menu->AddCommand(new RefineCmd(new RefineDoubleInterface)); menu->AddSubMenu("group", "refine a group of triangles", new GroupMenu("refine group", "refine group", refinegroup)); AddVariable(menu, "division", "how to divide triangles", AMTriangle::divisionmethod); AddVariable(menu, "min_area", "don't divide triangles smaller than this", AdaptiveMesh::min_area); AddVariable(menu, "max_divisions",\ "limit on number of subdivisions per triangle per refinement iteration", AdaptiveMesh::max_divisions); return menu; } //=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=// // Group management commands static MeshGroup *getgroup() { if(!meshexists()) return 0; CharString whichgroup = garcon()->get_command(); return current_goof->mesh()->get_group(whichgroup); } // add selected triangles to a group static CommandFn addtriangles() { MeshGroup *g = getgroup(); if(g) { AdaptiveMesh *mesh = current_goof->mesh(); for(AMIterator i(mesh, AMI_SELECTED); !i.end(); ++i) { AMTriangle *tri = (*mesh)[i]; g->append(tri); tri->inhibit_group_inheritance(); } current_goof->redraw(); // in case groups are displayed in dashboard, eg } } // remove selected triangles from a group static CommandFn subtracttriangles() { MeshGroup *g = getgroup(); if(g) { AdaptiveMesh *mesh = current_goof->mesh(); for(AMIterator i(mesh, AMI_SELECTED); !i.end(); ++i) { AMTriangle *tri = (*mesh)[i]; g->remove(tri); tri->inhibit_group_inheritance(); } current_goof->redraw(); // in case groups are displayed in dashboard, eg } } // select triangles in a group static CommandFn selectgroup() { MeshGroup *g = getgroup(); if(g) { current_goof->mesh()->select(*g); current_goof->redraw(); } } static CommandFn selectgroup2() { MeshGroup *g = getgroup(); if(g) { current_goof->mesh()->select_too(*g); current_goof->redraw(); } } static Menu *selectmshgrpmnu() { static Menu *menu = 0; if(menu) return menu; menu = new GroupMenu("select group", "select only triangles in group", selectgroup); menu->AddCommand("none", "unselect all triangles", selectnonecmd); return menu; } static Menu *selectmshgrpmnu2() { static Menu *menu = 0; if(menu) return menu; menu = new GroupMenu("select group too", "also select all triangles in group", selectgroup2); menu->AddCommand("none", "unselect all triangles", selectnonecmd); return menu; } // create a new group class NewMeshGroupCmd : public CommandM { private: CharString name; public: NewMeshGroupCmd() : CommandM("new", "create a new group"), name("groupname") { AddArgument(this, "name", name); } virtual CommandFn func() { if(meshexists()) { char *c; int modified = 0; while(c = (char*) strchr(name, ' ')) { // cast required by Sun *c = '_'; modified = 1; } if(modified) garcon()->msout << ms_info << "Group name changed to \"" << name << "\"" << endl << ms_normal; current_goof->mesh()->add_group(name); } } virtual CommandM *clone() const { NewMeshGroupCmd *ngc = new NewMeshGroupCmd; ngc->name = name; return ngc; } }; // get information on a group static CommandFn infogroup() { MeshGroup *g = getgroup(); if(g) { g->weed(); double grouparea = 0.0; for(int i=0; isize(); i++) grouparea += (*g)[i]->area(); garcon()->msout << "Group " << g->query_name() << " contains " << g->size() << " triangles, and " << grouparea << " pixels (" << 100.*grouparea/ (current_goof->query_height()*current_goof->query_width()) << "% of total area)" << endl; } } // transfer groups from pixels to mesh static CommandFn transfergroups() { if(meshexists()) { current_goof->mesh()->inherit_pixel_groups(1); current_goof->redraw(); } } CommandFn removegrpcmd() { MeshGroup *g = getgroup(); if(g) { current_goof->mesh()->remove_group(g, 1); material_rulechange(); group_rulechange(); } } CommandFn removeallgrps() { if(meshexists()) { current_goof->mesh()->remove_all_groups(1); material_rulechange(); group_rulechange(); } } static Menu *deletegrpmenu() { static Menu *menu = 0; if(menu) return menu; menu = new GroupMenu("delete group", "delete group", removegrpcmd); menu->AddCommand("all", "delete all groups", removeallgrps); return menu; } static Menu *meshgroupmenu() { static Menu *menu = 0; if(menu) return menu; menu = new Menu("groups"); menu->AddCommand(new NewMeshGroupCmd); menu->AddSubMenu("select", "select a group of triangles", selectmshgrpmnu()); menu->AddSubMenu("add", "add selected triangles to a group", new GroupMenu("add to group", "add selected triangles to group", addtriangles)); menu->AddSubMenu("subtract", "remove selected triangles from a group", new GroupMenu("remove from group", "remove selected triangles from group", subtracttriangles)); menu->AddSubMenu("delete", "delete a group", deletegrpmenu()); menu->AddSubMenu("info", "print information about a group", new GroupMenu("group info", "print information about group", infogroup)); Menu *electmenu = menu->AddSubMenu("election_rules", "criteria for electing groups"); AddVariable(electmenu, "matl_type", "elect by material type", group_elector.mattype, group_rulechange); AddVariable(electmenu, "matl_type/params", "elect by material type and parameters", group_elector.mattypeparam, group_rulechange); AddVariable(electmenu, "group_membership", "elect by pixel group membership", group_elector.groupmemb, group_rulechange); menu->AddCommand("transfer", "transfer groups from pixels to triangles", transfergroups); AddVariable(menu, "transfer_method", "how to transfer groups from pixels to triangles", grouptransfermethod, group_rulechange); AddVariable(menu, "transfer_time", "when to transfer groups from pixels to triangles", grouptransfertime, groups_timechange); AddVariable(menu, "transfer_replace", "replace existing groups or add to them?", grouptransferreplace); return menu; } //=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=// // transfer materials from pixels to mesh static CommandFn transfermaterials() { if(meshexists()) { current_goof->dup_mesh(); current_goof->mesh()->inherit_pixel_materials(1); current_goof->redraw(); } } Menu *meshmaterialmenu() { static Menu *menu = 0; if(menu) return menu; menu = new Menu("materials"); Menu *setmatmenu = menu->AddSubMenu("set", "explicitly set material type for selected triangles"); Vec ®istry = material_registry(); MaterialTypeRegistration::sort(); for(int i=0; iAddCommand(registry[i]->tricommand); AddVariable(menu, "transfer_method", "how to transfer groups from pixels to triangles", materialtransfermethod, material_rulechange); AddVariable(menu, "transfer_time", "when to transfer groups from pixels to triangles", materialtransfertime, material_timechange); menu->AddCommand("transfer", "transfer materials from pixels to triangles", transfermaterials); Menu *electmenu = menu->AddSubMenu("election_rules", "criteria for electing materials"); AddVariable(electmenu, "group_membership", "elect by pixel group membership", material_elector.groupmemb, material_rulechange); return menu; } //=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=// // Triangle selection commands static CommandFn selectallcmd() { if(meshexists()) { current_goof->mesh()->select_all(); current_goof->redraw(); } } static CommandFn selectnonecmd() { if(meshexists()) { current_goof->mesh()->unselect_all(); current_goof->redraw(); } } static CommandFn selectdimples() { if(meshexists()) { int n = current_goof->mesh()->select_dimples(); garcon()->msout << ms_info << "Selected " << n << " additional " << (n==1? "triangle." : "triangles.") << endl << ms_normal; current_goof->redraw(); } } static CommandFn selectthumbs() { if(meshexists()) { int n = current_goof->mesh()->unselect_sorethumbs(); garcon()->msout << ms_info << "Unselected " << n << (n==1? " triangle." :" triangles.") << endl << ms_normal; current_goof->redraw(); } } static CommandFn select_interface() { if(meshexists()) { int n = current_goof->mesh()->select_interface(); garcon()->msout << ms_info << "Selected " << n << (n==1?" triangle." : " triangles.") << endl << ms_normal; current_goof->redraw(); } } static CommandFn select_neighbors() { // select neighbors of selected triangles. Mostly for debugging. if(meshexists()) { int n = current_goof->mesh()->select_neighbors(); garcon()->msout << ms_info << "Selected " << n << (n==1? " triangle." : " triangles.") << endl << ms_normal; current_goof->redraw(); } } class SelectECmd : public CommandM { private: double min, max; public: SelectECmd() : min(0), max(1), CommandM("E", "select triangles with a range of E values") { AddArgument(this, "min", min); AddArgument(this, "max", max); } virtual CommandFn func() { if(meshexists()) { int n = current_goof->mesh()->selectE(min, max); garcon()->msout << ms_info << "Selected " << n << (n==1? " triangle." : " triangles.") << endl << ms_normal; current_goof->redraw(); } } virtual CommandM *clone() const { SelectECmd *sec = new SelectECmd; sec->min = min; sec->max = max; return sec; } }; static Menu *selectmenu() { static Menu *menu = 0; if(menu) return menu; menu = new Menu("select"); menu->AddCommand("all", "select all triangles", selectallcmd); menu->AddCommand("none", "select no triangles", selectnonecmd); menu->AddCommand(new SelectECmd); menu->AddCommand("dimples", "select triangles with 2 or 3 selected neighbors", selectdimples); menu->AddCommand("thumbs", "unselect triangles with fewer than 2 selected neighbors", selectthumbs); menu->AddCommand("interface", "select all triangles with neighbors of a different material", select_interface); menu->AddCommand("neighbors", "select all neighbors of currently selected triangles", select_neighbors); menu->AddSubMenu("group", "select mesh groups", selectmshgrpmnu()); menu->AddSubMenu("group_too", "select additional mesh groups", selectmshgrpmnu2()); return menu; } //=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=// static CommandFn sanity() { if(meshexists()) current_goof->mesh()->sanity(); } //=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=// Menu *meshmenu() { static Menu *menu = 0; if(menu) return menu; Enum::name(GTM_ELECTION, "election"); Enum::name(GTM_ALLPIXELS, "all"); Enum::name(GTM_CENTERPIXEL, "center"); Enum::name(MTM_ELECTION, "election"); Enum::name(MTM_CENTERPIXEL, "center"); Enum::name(CONTINUALLY, "automatic"); Enum::name(EXPLICITLY, "on_demand"); menu = new Menu("adaptive mesh"); menu->AddCommand(new CreateMeshCmd); menu->AddCommand("destroy", "destroy the mesh", destroymesh); menu->AddCommand(new FiddleCmd); menu->AddCommand(new FiddleDownHillCmd); menu->AddCommand(new FiddleLaplaceCmd); menu->AddCommand("swap_two", "swap two triangles", swapedges); menu->AddCommand("swap_worst", "make swaps that lower energy", swapworst); menu->AddCommand("E_range", "find range of E values", findErange); menu->AddCommand(new MeshOutputCmd); menu->AddCommand(new OOF2OutputCmd); menu->AddSubMenu("groups", "manage triangle groups", meshgroupmenu()); menu->AddSubMenu("materials", "transfer materials from pixels to mesh", meshmaterialmenu()); menu->AddSubMenu("refine", "refine the mesh", refinemenu()); menu->AddSubMenu("select", "select triangles", selectmenu()); menu->AddSubMenu("stack", "manipulate stack of old meshes", meshstackmenu()); AddVariable(menu, "alpha_E", "balance between homogeneity and shape", AdaptiveMesh::alpha); AddVariable(menu, "redraw", "redraw mesh continuously?", AdaptiveMesh::continuous_redraw); menu->AddCommand("sanity_check", "check mesh", sanity); return menu; } template <> VarObject *Var >::create_varobject(Variable *var, Form *form, VarObjectType t, int x, int y, int w, int h) { return new EnumVarObject(var, form, t, x, y, w, h); } template <> VarObject *Var >::create_varobject(Variable *var, Form *form, VarObjectType t, int x, int y, int w, int h) { return new EnumVarObject(var, form, t, x, y, w, h); } template <> VarObject *Var >::create_varobject(Variable *var, Form *form, VarObjectType t, int x, int y, int w, int h) { return new EnumVarObject(var, form, t, x, y, w, h); } //=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=// void select_mesh_triangle(const MeshCoord &where, int shiftkey) { if(meshexists()) { if(!shiftkey) current_goof->mesh()->unselect_all(); AMTriangle *clicked = current_goof->mesh() ->smallest_triangle_containing(where); if(clicked && clicked->active()) clicked->toggle_select(); } } void select_mesh_triangles(const MeshCoord &c1, const MeshCoord &c2, int shiftkey) { if(meshexists()) { double xmin, xmax, ymin, ymax; if(c1.x < c2.x) { xmin = c1.x; xmax = c2.x; } else { xmin = c2.x; xmax = c1.x; } if(c1.y < c2.y) { ymin = c1.y; ymax = c2.y; } else { ymin = c2.y; ymax = c1.y; } AdaptiveMesh *mesh = current_goof->mesh(); if(!shiftkey) mesh->unselect_all(); for(AMIterator i(mesh, AMI_ALL); !i.end(); ++i) { AMTriangle *tri = (*mesh)[i]; if(tri->active()) { MeshCoord ctr(tri->center()); if(ctr.x >= xmin && ctr.x <= xmax && ctr.y >= ymin && ctr.y <= ymax) { if(shiftkey) tri->toggle_select(); else tri->select(); } } } } } //=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=//=\\=// // include template definitions #include "enumio.C"