Newer
Older
// Gmsh - Copyright (C) 1997-2014 C. Geuzaine, J.-F. Remacle
// See the LICENSE.txt file for license information. Please report all
// bugs and problems to the public mailing list <gmsh@geuz.org>.
#include "GmshConfig.h"

Christophe Geuzaine
committed
#include "GmshMessage.h"
#include "Geo.h"
#include "GModel.h"
#include "Numeric.h"
#include "Context.h"
#include "OpenFile.h"
#include "CommandLine.h"
#include "ReadImg.h"
#include "OS.h"
#include "StringUtils.h"

Jean-François Remacle
committed
#include "GeomMeshMatcher.h"
#if defined(HAVE_POPPLER)
#include "gmshPopplerWrapper.h"
#endif
#include "PView.h"
#include "PViewData.h"

Christophe Geuzaine
committed
#include "PViewOptions.h"

Christophe Geuzaine
committed
#include <FL/fl_ask.H>
#include "FlGui.h"

Christophe Geuzaine
committed
#include "onelabGroup.h"
#include "graphicWindow.h"
#if defined(HAVE_3M)
#include "3M.h"
#endif
static void FinishUpBoundingBox()
{
double range[3];
range[i] = CTX::instance()->max[i] - CTX::instance()->min[i];
if(range[0] < CTX::instance()->geom.tolerance &&
range[1] < CTX::instance()->geom.tolerance &&
range[2] < CTX::instance()->geom.tolerance) {
CTX::instance()->min[0] -= 1.; CTX::instance()->min[1] -= 1.;
CTX::instance()->max[0] += 1.; CTX::instance()->max[1] += 1.;
else if(range[0] < CTX::instance()->geom.tolerance &&
range[1] < CTX::instance()->geom.tolerance) {
CTX::instance()->min[0] -= range[2]; CTX::instance()->min[1] -= range[2];
CTX::instance()->max[0] += range[2]; CTX::instance()->max[1] += range[2];
else if(range[0] < CTX::instance()->geom.tolerance &&
range[2] < CTX::instance()->geom.tolerance) {
CTX::instance()->min[0] -= range[1]; CTX::instance()->max[0] += range[1];
else if(range[1] < CTX::instance()->geom.tolerance &&
range[2] < CTX::instance()->geom.tolerance) {
CTX::instance()->min[1] -= range[0]; CTX::instance()->max[1] += range[0];
else if(range[0] < CTX::instance()->geom.tolerance) {
CTX::instance()->min[0] -= l; CTX::instance()->max[0] += l;
else if(range[1] < CTX::instance()->geom.tolerance) {
CTX::instance()->min[1] -= l; CTX::instance()->max[1] += l;
CTX::instance()->lc = sqrt(SQU(CTX::instance()->max[0] - CTX::instance()->min[0]) +
SQU(CTX::instance()->max[1] - CTX::instance()->min[1]) +
SQU(CTX::instance()->max[2] - CTX::instance()->min[2]));
for(int i = 0; i < 3; i++)
CTX::instance()->cg[i] = 0.5 * (CTX::instance()->min[i] + CTX::instance()->max[i]);
}
void SetBoundingBox(double xmin, double xmax,
CTX::instance()->min[0] = xmin; CTX::instance()->max[0] = xmax;
CTX::instance()->min[1] = ymin; CTX::instance()->max[1] = ymax;
CTX::instance()->min[2] = zmin; CTX::instance()->max[2] = zmax;

Christophe Geuzaine
committed
void SetBoundingBox(bool aroundVisible)
if(CTX::instance()->forcedBBox) return;

Christophe Geuzaine
committed
SBoundingBox3d bb = GModel::current()->bounds(aroundVisible);
if(bb.empty()) {
for(unsigned int i = 0; i < PView::list.size(); i++)
if(!PView::list[i]->getData()->getBoundingBox().empty())

Christophe Geuzaine
committed
if(!aroundVisible || PView::list[i]->getOptions()->visible)
bb += PView::list[i]->getData()->getBoundingBox();
}
#endif
if(bb.empty()){
bb += SPoint3(-1., -1., -1.);
bb += SPoint3(1., 1., 1.);
}
CTX::instance()->min[0] = bb.min().x(); CTX::instance()->max[0] = bb.max().x();
CTX::instance()->min[1] = bb.min().y(); CTX::instance()->max[1] = bb.max().y();
CTX::instance()->min[2] = bb.min().z(); CTX::instance()->max[2] = bb.max().z();
Gilles Marckmann
committed
}

Christophe Geuzaine
committed
// FIXME: this is necessary for now to have an approximate CTX::instance()->lc
// *while* parsing input files (it's important since some of the geometrical
// operations use a tolerance that depends on CTX::instance()->lc). This will be
// removed once the new database is filled directly during the parsing step
static SBoundingBox3d temp_bb;
void ResetTemporaryBoundingBox()
{
temp_bb.reset();
}
void AddToTemporaryBoundingBox(double x, double y, double z)
{
temp_bb += SPoint3(x, y, z);
CTX::instance()->lc = sqrt(SQU(temp_bb.max().x() - temp_bb.min().x()) +
SQU(temp_bb.max().y() - temp_bb.min().y()) +
if(CTX::instance()->lc == 0) CTX::instance()->lc = 1.;
// to get correct cg during interactive point creation
for(int i = 0; i < 3; i++) CTX::instance()->cg[i] = temp_bb.center()[i];

Christophe Geuzaine
committed
static void ComputeMaxEntityNum()
{
GModel::current()->getGEOInternals()->MaxPointNum =
std::max(GModel::current()->getGEOInternals()->MaxPointNum,
GModel::current()->getMaxElementaryNumber(0));
GModel::current()->getGEOInternals()->MaxLineNum =
std::max(GModel::current()->getGEOInternals()->MaxLineNum,
GModel::current()->getMaxElementaryNumber(1));
GModel::current()->getGEOInternals()->MaxSurfaceNum =
std::max(GModel::current()->getGEOInternals()->MaxSurfaceNum,
GModel::current()->getMaxElementaryNumber(2));
GModel::current()->getGEOInternals()->MaxVolumeNum =
std::max(GModel::current()->getGEOInternals()->MaxVolumeNum,
GModel::current()->getMaxElementaryNumber(3));
}

Christophe Geuzaine
committed
static std::vector<gmshFILE> openedFiles;

Christophe Geuzaine
committed
int ParseFile(const std::string &fileName, bool close, bool warnIfMissing)
Msg::Error("Gmsh parser is not compiled in this version");
return 0;
#else
// add 'b' for pure Windows programs: opening in text mode messes up
// fsetpos/fgetpos (used e.g. for user-defined functions)

Christophe Geuzaine
committed
gmshFILE fp;
if(!(fp = gmshopen(fileName.c_str(), "rb"))){
if(warnIfMissing)
Msg::Warning("Unable to open file '%s'", fileName.c_str());
int numViewsBefore = PView::list.size();
#endif
std::string old_yyname = gmsh_yyname;

Christophe Geuzaine
committed
gmshFILE old_yyin = gmsh_yyin;
int old_yyerrorstate = gmsh_yyerrorstate;
int old_yylineno = gmsh_yylineno;
int old_yyviewindex = gmsh_yyviewindex;
gmsh_yyname = fileName;
gmsh_yyin = fp;
gmsh_yyerrorstate = 0;
gmsh_yylineno = 1;
gmsh_yyviewindex = 0;

Christophe Geuzaine
committed
while(!gmsheof(gmsh_yyin)){
gmsh_yyparse();
if(gmsh_yyerrorstate > 20){
if(gmsh_yyerrorstate != 999) // 999 is a volontary exit
Msg::Error("Too many errors: aborting parser...");
gmsh_yyflush();

Christophe Geuzaine
committed
if(close){
gmsh_yyflush();

Christophe Geuzaine
committed
gmshclose(gmsh_yyin);

Christophe Geuzaine
committed
}

Christophe Geuzaine
committed
else{
openedFiles.push_back(gmsh_yyin);
}
gmsh_yyname = old_yyname;
gmsh_yyin = old_yyin;
gmsh_yyerrorstate = old_yyerrorstate;
gmsh_yylineno = old_yylineno;
gmsh_yyviewindex = old_yyviewindex;
if(FlGui::available())
FlGui::instance()->updateViews(numViewsBefore != (int)PView::list.size(), false);
#endif
return 1;
#endif
}

Christophe Geuzaine
committed
static bool doSystemUncompress(std::string fileName, std::string noExt)
{
std::ostringstream sstream;
sstream << "File '"<< fileName << "' is in gzip format.\n\n"
<< "Do you want to uncompress it?";
if(Msg::GetAnswer(sstream.str().c_str(), 0, "Cancel", "Uncompress")){
if(SystemCall(std::string("gunzip -c ") + fileName + " > " + noExt, true))
Msg::Warning("Potentially failed to uncompress `%s': check directory permissions",
fileName.c_str());
GModel::current()->setFileName(noExt);
return true;
}
return false;
}
void ParseString(const std::string &str)
if(str.empty()) return;
std::string fileName = CTX::instance()->homeDir + CTX::instance()->tmpFileName;
FILE *fp = Fopen(fileName.c_str(), "w");
fprintf(fp, "%s\n", str.c_str());
// overwrite last one
opt_solver_name(NUM_SOLVERS - 1, GMSH_SET|GMSH_GUI, name);
return NUM_SOLVERS - 1;

Christophe Geuzaine
committed
int MergeFile(const std::string &fileName, bool warnIfMissing, bool setWindowTitle,
bool setBoundingBox)

Christophe Geuzaine
committed
if(GModel::current()->getName() == ""){
GModel::current()->setFileName(fileName);
GModel::current()->setName(SplitFileName(fileName)[1]);
}
#if defined(HAVE_FLTK)

Christophe Geuzaine
committed
if(FlGui::available() && setWindowTitle)
FlGui::instance()->setGraphicTitle(GModel::current()->getFileName());
// added 'b' for pure Windows programs, since some of these files
// contain binary data

Christophe Geuzaine
committed
gmshFILE fp = gmshopen(fileName.c_str(), "rb");
Msg::Warning("Unable to open file '%s'", fileName.c_str());
return 0;
}
char header[256];

Christophe Geuzaine
committed
if(!gmshgets(header, sizeof(header), fp)){ gmshclose(fp); return 0; }
gmshclose(fp);

Christophe Geuzaine
committed
Msg::StatusBar(true, "Reading '%s'...", fileName.c_str());
std::vector<std::string> split = SplitFileName(fileName);
std::string noExt = split[0] + split[1], ext = split[2];

Christophe Geuzaine
committed
#if defined(HAVE_COMPRESSED_IO) && defined(HAVE_LIBZ)
std::vector<std::string> subsplit = SplitFileName(noExt);
ext = subsplit[2];
if(ext != ".geo" && ext != ".GEO" &&
ext != ".unv" && ext != ".UNV"){
if(doSystemUncompress(fileName, noExt))
return MergeFile(noExt, false, setWindowTitle);

Christophe Geuzaine
committed
#else
if(doSystemUncompress(fileName, noExt))
return MergeFile(noExt, false, setWindowTitle);
#endif

Christophe Geuzaine
committed
// force reading msh file even if wrong extension if the header
// matches
// if(!strncmp(header, "$MeshFormat", 11)) ext = "";
CTX::instance()->geom.draw = 0; // don't try to draw the model while reading
#if defined(HAVE_FLTK) && defined(HAVE_POST)
int numViewsBefore = PView::list.size();
#endif
int status = 0;
if(ext == ".stl" || ext == ".STL"){
status = GModel::current()->readSTL(fileName, CTX::instance()->geom.tolerance);
else if(ext == ".brep" || ext == ".rle" || ext == ".brp" || ext == ".BRP"){
status = GModel::current()->readOCCBREP(fileName);
else if(ext == ".iges" || ext == ".IGES" || ext == ".igs" || ext == ".IGS"){
status = GModel::current()->readOCCIGES(fileName);
else if(ext == ".step" || ext == ".STEP" || ext == ".stp" || ext == ".STP"){
status = GModel::current()->readOCCSTEP(fileName);
else if(ext == ".sat" || ext == ".SAT"){
status = GModel::current()->readACISSAT(fileName);
}
else if(ext == ".unv" || ext == ".UNV"){
status = GModel::current()->readUNV(fileName);
else if(ext == ".vtk" || ext == ".VTK"){
status = GModel::current()->readVTK(fileName, CTX::instance()->bigEndian);
else if(ext == ".wrl" || ext == ".WRL" || ext == ".vrml" || ext == ".VRML" ||
ext == ".iv" || ext == ".IV"){
status = GModel::current()->readVRML(fileName);
else if(ext == ".mesh" || ext == ".MESH"){
status = GModel::current()->readMESH(fileName);
else if(ext == ".diff" || ext == ".DIFF"){
status = GModel::current()->readDIFF(fileName);
}
else if(ext == ".med" || ext == ".MED" || ext == ".mmed" || ext == ".MMED" ||
ext == ".rmed" || ext == ".RMED"){
status = GModel::readMED(fileName);
if(status > 1) status = PView::readMED(fileName);

Christophe Geuzaine
committed
}
else if(ext == ".bdf" || ext == ".BDF" || ext == ".nas" || ext == ".NAS"){
status = GModel::current()->readBDF(fileName);
else if(ext == ".dat" || ext == ".DAT"){
if(!strncmp(header, "BEGIN ACTRAN", 12))
status = GModel::current()->readACTRAN(fileName);
else
status = GModel::current()->readBDF(fileName);
}
else if(ext == ".p3d" || ext == ".P3D"){
status = GModel::current()->readP3D(fileName);
else if(ext == ".fm" || ext == ".FM") {
status = GModel::current()->readFourier(fileName);
else if(ext == ".pnm" || ext == ".PNM" || ext == ".pbm" || ext == ".PBM" ||
ext == ".pgm" || ext == ".PGM" || ext == ".ppm" || ext == ".PPM") {
status = read_pnm(fileName);
else if(ext == ".bmp" || ext == ".BMP") {
status = read_bmp(fileName);
else if(ext == ".jpg" || ext == ".JPG" || ext == ".jpeg" || ext == ".JPEG") {
status = read_jpeg(fileName);
}
#endif
#if defined(HAVE_LIBPNG)
else if(ext == ".png" || ext == ".PNG") {
status = read_png(fileName);
Emilie Marchandise
committed
status = GModel::current()->readPLY2(fileName);
}
Emilie Marchandise
committed
status = GModel::current()->readPLY(fileName);
}
else if(ext == ".geom" || ext == ".GEOM"){
status = GModel::current()->readGEOM(fileName);
}
Bastien Gorissen
committed
#if defined(HAVE_LIBCGNS)
else if(ext == ".cgns" || ext == ".CGNS"){
status = GModel::current()->readCGNS(fileName);
}
#endif
#if defined(HAVE_3M)
else if(ext == ".csv"){
status = readFile3M(fileName);
}
int num = defineSolver("GetDP");
GModel::current()->setName(split[1] + ".geo");
GModel::current()->setFileName(split[0] + split[1] + ".geo");
CTX::instance()->launchSolverAtStartup = num;
else if(ext == ".py" || ext == ".PY" ||
ext == ".exe" || ext == ".EXE"){
int num = defineSolver(split[1]);
opt_solver_executable(num, GMSH_SET, fileName);
CTX::instance()->launchSolverAtStartup = num;
return 1;
Bastien Gorissen
committed
#endif
CTX::instance()->geom.draw = 1;
if(!strncmp(header, "$PTS", 4) || !strncmp(header, "$NO", 3) ||
!strncmp(header, "$PARA", 5) || !strncmp(header, "$ELM", 4) ||
!strncmp(header, "$MeshFormat", 11) || !strncmp(header, "$Comments", 9)) {
if(CTX::instance()->geom.matchGeomAndMesh && !GModel::current()->empty()) {
GModel* tmp2 = GModel::current();
GModel* tmp = new GModel();
status = GeomMeshMatcher::instance()->match(tmp2, tmp);
GModel::setCurrent(tmp2);
tmp2->setVisibility(1);

Christophe Geuzaine
committed
}
else
status = GModel::current()->readMSH(fileName);
if(status > 1) status = PView::readMSH(fileName);
SetOrderN(GModel::current(), CTX::instance()->mesh.order,
CTX::instance()->mesh.secondOrderIncomplete);
else if(!strncmp(header, "$PostFormat", 11) ||
status = PView::readPOS(fileName);

Christophe Geuzaine
committed
ComputeMaxEntityNum();

Christophe Geuzaine
committed
if(setBoundingBox) SetBoundingBox();
CTX::instance()->geom.draw = 1;
CTX::instance()->mesh.changed = ENT_ALL;

Christophe Geuzaine
committed
// go directly to the first non-empty step after the one that is requested
for(unsigned int i = numViewsBefore; i < PView::list.size(); i++)
opt_view_timestep(i, GMSH_SET | GMSH_GUI,

Christophe Geuzaine
committed
PView::list[i]->getData()->getFirstNonEmptyTimeStep
(opt_view_timestep(i, GMSH_GET, 0)));
FlGui::instance()->updateViews(numViewsBefore != (int)PView::list.size(), false);
Msg::ImportPhysicalsAsOnelabRegions();
if(!status) Msg::Error("Error loading '%s'", fileName.c_str());

Christophe Geuzaine
committed
Msg::StatusBar(true, "Done reading '%s'", fileName.c_str());

Christophe Geuzaine
committed
CTX::instance()->fileread = true;
// merge the associated option file if there is one
if(!StatFile(fileName + ".opt"))
MergeFile(fileName + ".opt");

Christophe Geuzaine
committed
int MergePostProcessingFile(const std::string &fileName, int showViews,
bool showLastStep, bool warnIfMissing)
{
// check if there is a mesh in the file
FILE *fp = Fopen(fileName.c_str(), "rb");
if(!fp){
if(warnIfMissing) Msg::Warning("Unable to open file '%s'", fileName.c_str());
return 0;
}
char header[256];
if(!fgets(header, sizeof(header), fp)){ fclose(fp); return 0; }
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
bool haveMesh = false;
if(!strncmp(header, "$MeshFormat", 11)){
while(!feof(fp) && fgets(header, sizeof(header), fp)){
if(!strncmp(header, "$Nodes", 6)){
haveMesh = true;
break;
}
else if(!strncmp(header, "$NodeData", 9) ||
!strncmp(header, "$ElementData", 12) ||
!strncmp(header, "$ElementNodeData", 16)){
break;
}
}
}
fclose(fp);
// store old step values
unsigned int n = PView::list.size();
std::vector<int> steps(n, 0);
if(showLastStep){
for(unsigned int i = 0; i < PView::list.size(); i++)
steps[i] = (int)opt_view_nb_timestep(i, GMSH_GET, 0);
}
// if there is a mesh, create a new model to store it (don't merge elements in
// the current mesh!)
GModel *old = GModel::current();
if(haveMesh){
GModel *m = new GModel();
GModel::setCurrent(m);
}

Christophe Geuzaine
committed
int ret = MergeFile(fileName, warnIfMissing, true,
old->bounds().empty() ? true : false);
GModel::setCurrent(old);
old->setVisibility(1);
// hide everything except the onelab X-Y graphs

Christophe Geuzaine
committed
if(showViews == 0){
for(unsigned int i = 0; i < PView::list.size(); i++){
if(PView::list[i]->getData()->getFileName().substr(0, 6) != "ONELAB")
PView::list[i]->getOptions()->visible = 0;
}
}

Christophe Geuzaine
committed
else if(showViews == 2 && n < PView::list.size()){
// if we created new views, assume we only want to see those (and the
// onelab X-Y graphs)
for(unsigned int i = 0; i < n; i++){
if(PView::list[i]->getData()->getFileName().substr(0, 6) != "ONELAB")
PView::list[i]->getOptions()->visible = 0;
}
}
// if we added steps, go to the last one
if(showLastStep){
steps.resize(PView::list.size(), 0);
for(unsigned int i = 0; i < PView::list.size(); i++){
int step = (int)opt_view_nb_timestep(i, GMSH_GET, 0);
if(step > steps[i])
opt_view_timestep(i, GMSH_SET|GMSH_GUI, step - 1);
}
}
return ret;
}
for(int i = PView::list.size() - 1; i >= 0; i--)
delete PView::list[i];
#endif
gmsh_yysymbols.clear();

Christophe Geuzaine
committed
gmsh_yystringsymbols.clear();
for(int i = GModel::list.size() - 1; i >= 0; i--)
delete GModel::list[i];

Christophe Geuzaine
committed
// close the files that might have been left open by ParseFile
if(openedFiles.size()){
for(unsigned int i = 0; i < openedFiles.size(); i++)

Christophe Geuzaine
committed
gmshclose(openedFiles[i]);

Christophe Geuzaine
committed
openedFiles.clear();
}

Christophe Geuzaine
committed
new GModel();

Christophe Geuzaine
committed
GModel::current()->setFileName(CTX::instance()->defaultFileName);
GModel::current()->setName("");
#if defined(HAVE_FLTK)
if(FlGui::available()){
FlGui::instance()->setGraphicTitle(GModel::current()->getFileName());
FlGui::instance()->resetVisibility();
FlGui::instance()->updateViews(true, true);
FlGui::instance()->updateFields();
GModel::current()->setSelection(0);
}
#endif

Christophe Geuzaine
committed
Msg::ResetErrorCounter();

Christophe Geuzaine
committed
void OpenProject(const std::string &fileName, bool setWindowTitle)
if(CTX::instance()->lock) {
Msg::Info("I'm busy! Ask me that later...");
return;
}
CTX::instance()->lock = 1;

Christophe Geuzaine
committed
Msg::ResetErrorCounter();
if(GModel::current()->empty()){

Christophe Geuzaine
committed
// if the current model is empty, make sure it's reaaally cleaned-up, and
// reuse it
GModel::current()->destroy();
GModel::current()->getGEOInternals()->destroy();

Christophe Geuzaine
committed
// don't clear the parser variables if we just launched gmsh with the

Christophe Geuzaine
committed
// -string, -setstring or -setnumber command line options

Christophe Geuzaine
committed
#if defined(HAVE_PARSER)
std::string c = Msg::GetCommandLineArgs();

Christophe Geuzaine
committed
if(c.find("-string") == std::string::npos &&
c.find("-setstring") == std::string::npos &&
c.find("-setnumber") == std::string::npos){

Christophe Geuzaine
committed
gmsh_yysymbols.clear();

Christophe Geuzaine
committed
gmsh_yystringsymbols.clear();
}

Christophe Geuzaine
committed
#endif
}
else{

Christophe Geuzaine
committed
// if the current model is not empty make it invisible, clear the parser
// variables and add a new model
gmsh_yysymbols.clear();

Christophe Geuzaine
committed
gmsh_yystringsymbols.clear();
new GModel();
GModel::current(GModel::list.size() - 1);
}

Christophe Geuzaine
committed
// temporary hack until we fill the current GModel on the fly during parsing

Christophe Geuzaine
committed
if(fileName != CTX::instance()->recentFiles.front())
CTX::instance()->recentFiles.insert
(CTX::instance()->recentFiles.begin(), fileName);
if(CTX::instance()->recentFiles.size() > 5)
CTX::instance()->recentFiles.resize(5);
Bastien Gorissen
committed
#if defined(HAVE_FLTK)
if(FlGui::available())

Christophe Geuzaine
committed
FlGui::instance()->graph[0]->fillRecentHistoryMenu();
Bastien Gorissen
committed
#endif

Christophe Geuzaine
committed
// close the files that might have been left open by ParseFile
if(openedFiles.size()){
for(unsigned int i = 0; i < openedFiles.size(); i++)

Christophe Geuzaine
committed
gmshclose(openedFiles[i]);

Christophe Geuzaine
committed
openedFiles.clear();
}
CTX::instance()->lock = 0;
if(FlGui::available()){

Christophe Geuzaine
committed
file_watch_cb(0, 0);
FlGui::instance()->resetVisibility();
FlGui::instance()->updateViews(true, false);
FlGui::instance()->updateFields();
GModel::current()->setSelection(0);
GModel::current()->setCompoundVisibility();
void OpenProjectMacFinder(const char *fileName)
if(!FlGui::available()){
// Gmsh is not ready: will open the file later
FlGui::setOpenedThroughMacFinder(fileName);
}
else if(FlGui::available()){
// Gmsh is running
OpenProject(fileName);

Christophe Geuzaine
committed
if(CTX::instance()->launchSolverAtStartup >= 0)
solver_cb(0, (void*)CTX::instance()->launchSolverAtStartup);