Skip to content
Snippets Groups Projects
Select Git revision
  • 3306d60d058450d302d3766b9fb77511c44052b4
  • master default
  • cgnsUnstructured
  • partitioning
  • poppler
  • HighOrderBLCurving
  • gmsh_3_0_4
  • gmsh_3_0_3
  • gmsh_3_0_2
  • gmsh_3_0_1
  • gmsh_3_0_0
  • gmsh_2_16_0
  • gmsh_2_15_0
  • gmsh_2_14_1
  • gmsh_2_14_0
  • gmsh_2_13_2
  • gmsh_2_13_1
  • gmsh_2_12_0
  • gmsh_2_11_0
  • gmsh_2_10_1
  • gmsh_2_10_0
  • gmsh_2_9_3
  • gmsh_2_9_2
  • gmsh_2_9_1
  • gmsh_2_9_0
  • gmsh_2_8_6
26 results

Draw.cpp

Blame
  • Forked from gmsh / gmsh
    Source project has a limited visibility.
    • Christophe Geuzaine's avatar
      3306d60d
      · 3306d60d
      Christophe Geuzaine authored
      - rewrote selection code to use dynamic arrays, so that we should never
      overflow the selection buffer again (even on huuuuge models)
      - cleanup some leftovers "useing namespace std"
      3306d60d
      History
      Christophe Geuzaine authored
      - rewrote selection code to use dynamic arrays, so that we should never
      overflow the selection buffer again (even on huuuuge models)
      - cleanup some leftovers "useing namespace std"
    Draw.cpp 17.17 KiB
    // $Id: Draw.cpp,v 1.105 2006-08-19 01:12:39 geuzaine Exp $
    //
    // Copyright (C) 1997-2006 C. Geuzaine, J.-F. Remacle
    //
    // This program is free software; you can redistribute it and/or modify
    // it under the terms of the GNU General Public License as published by
    // the Free Software Foundation; either version 2 of the License, or
    // (at your option) any later version.
    //
    // This program is distributed in the hope that it will be useful,
    // but WITHOUT ANY WARRANTY; without even the implied warranty of
    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    // GNU General Public License for more details.
    //
    // You should have received a copy of the GNU General Public License
    // along with this program; if not, write to the Free Software
    // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
    // USA.
    // 
    // Please report all bugs and problems to <gmsh@geuz.org>.
    
    #include "Gmsh.h"
    #include "GmshUI.h"
    #include "GmshDefines.h"
    #include "CAD.h"
    #include "Draw.h"
    #include "Context.h"
    #include "Numeric.h"
    #include "GModel.h"
    
    extern Context_T CTX;
    extern GModel *GMODEL;
    
    int NeedPolygonOffset()
    {
      if(GMODEL->getMeshStatus() == 2 &&
         (CTX.mesh.surfaces_edges || CTX.geom.lines || CTX.geom.surfaces))
        return 1;
      if(GMODEL->getMeshStatus() == 3 && 
         (CTX.mesh.surfaces_edges || CTX.mesh.volumes_edges))
        return 1;
      for(int i = 0; i < List_Nbr(CTX.post.list); i++){
        Post_View *v = *(Post_View**)List_Pointer(CTX.post.list, i);
        if(v->Visible){
          if(v->ShowElement)
    	return 1;
          if((v->NbST || v->NbSQ) && (v->Axes || v->IntervalsType == DRAW_POST_ISO))
    	return 1;
        }
      }
      return 0;
    }
    
    void Draw3d(void)
    {
      // We should only enable the polygon offset when there is a mix of
      // lines and polygons to be drawn; enabling it all the time can lead
      // to very small but annoying artifacts in the picture. Since there
      // are so many ways in Gmsh to combine polygons and lines
      // (geometries + meshes + views...), we do our best here to
      // automatically detect if we should enable it. Note: the formula
      // for the offset is "offset = factor*DZ+r*units", where DZ is a
      // measurement of the change in depth relative to the screen area of
      // the polygon, and r is the smallest value that is guaranteed to
      // produce a resolvable offset for a given implementation.
      glPolygonOffset(CTX.polygon_offset_factor, CTX.polygon_offset_units);
      if(CTX.polygon_offset_factor || CTX.polygon_offset_units)
        CTX.polygon_offset = CTX.polygon_offset_always ? 1 : NeedPolygonOffset();
      else
        CTX.polygon_offset = 0;
    
      glDepthFunc(GL_LESS);
      glEnable(GL_DEPTH_TEST);
    
      InitProjection();
      InitRenderModel();
      InitPosition();
      Draw_Geom();
      Draw_Mesh();
      Draw_Post();
    }
    
    void Draw2d(void)
    {
      glDisable(GL_DEPTH_TEST);
      for(int i = 0; i < 6; i++)
        glDisable((GLenum)(GL_CLIP_PLANE0 + i));
    
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      glOrtho((double)CTX.viewport[0], (double)CTX.viewport[2],
              (double)CTX.viewport[1], (double)CTX.viewport[3], -1., 1.);
      // hack to make the 2D primitives appear "in front" in GL2PS
      glTranslated(0., 0., CTX.clip_factor > 1. ? 1./CTX.clip_factor : CTX.clip_factor);
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
      Draw_Graph2D();
      Draw_Text2D();
      Draw_OnScreenMessages();
      if(CTX.post.draw)
        Draw_Scales();
      if(CTX.small_axes)
        Draw_SmallAxes();
    }
    
    void DrawPlugin(void (*draw)(void))
    {
      CTX.post.plugin_draw_function = draw;
      int old = CTX.draw_bbox;
      CTX.draw_bbox = 1;
      if(CTX.fast_redraw){
        CTX.post.draw = 0;
        CTX.mesh.draw = 0;
      }
      if(!CTX.batch) 
        Draw();
      // this is reset in each plugin run/cancel callback:
      // CTX.post.plugin_draw_function = NULL;
      CTX.draw_bbox = old;
      CTX.post.draw = 1;
      CTX.mesh.draw = 1;
    }
    
    void ClearOpengl(void)
    {
      glClearColor(CTX.UNPACK_RED(CTX.color.bg) / 255.,
                   CTX.UNPACK_GREEN(CTX.color.bg) / 255.,
                   CTX.UNPACK_BLUE(CTX.color.bg) / 255., 0.);
      glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    }
    
    void InitProjection(int xpick, int ypick, int wpick, int hpick)
    {
      double Va = 
        (GLdouble) (CTX.viewport[3] - CTX.viewport[1]) /
        (GLdouble) (CTX.viewport[2] - CTX.viewport[0]);
      double Wa = (CTX.max[1] - CTX.min[1]) / (CTX.max[0] - CTX.min[0]);
    
      // compute the viewport in World coordinates (with margins)
      if(Va > Wa) {
        CTX.vxmin = CTX.min[0];
        CTX.vxmax = CTX.max[0];
        CTX.vymin = 0.5 * (CTX.min[1] + CTX.max[1] - Va * (CTX.max[0] - CTX.min[0]));
        CTX.vymax = 0.5 * (CTX.min[1] + CTX.max[1] + Va * (CTX.max[0] - CTX.min[0]));
      }
      else {
        CTX.vxmin = 0.5 * (CTX.min[0] + CTX.max[0] - (CTX.max[1] - CTX.min[1]) / Va);
        CTX.vxmax = 0.5 * (CTX.min[0] + CTX.max[0] + (CTX.max[1] - CTX.min[1]) / Va);
        CTX.vymin = CTX.min[1];
        CTX.vymax = CTX.max[1];
      }
      CTX.vxmin -= (CTX.vxmax - CTX.vxmin) / 3.;
      CTX.vxmax += 0.25 * (CTX.vxmax - CTX.vxmin);
      CTX.vymin -= (CTX.vymax - CTX.vymin) / 3.;
      CTX.vymax += 0.25 * (CTX.vymax - CTX.vymin);
    
      // store what one pixel represents in world coordinates
      CTX.pixel_equiv_x = (CTX.vxmax - CTX.vxmin) / (CTX.viewport[2] - CTX.viewport[0]);
      CTX.pixel_equiv_y = (CTX.vymax - CTX.vymin) / (CTX.viewport[3] - CTX.viewport[1]);
    
      // no initial translation of the model
      CTX.t_init[0] = CTX.t_init[1] = CTX.t_init[2] = 0.;
    
      // setup ortho or perspective projection matrix
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
    
      // restrict picking to a rectangular region around xpick,ypick in SELECT mode
      if(CTX.render_mode == GMSH_SELECT)
        gluPickMatrix((GLdouble)xpick, (GLdouble)(CTX.viewport[3] - ypick),
                      (GLdouble)wpick, (GLdouble)hpick, (GLint *)CTX.viewport);
    
      double grad_z, grad_xy;
      double zmax = MAX(fabs(CTX.min[2]), fabs(CTX.max[2]));
      if(zmax < CTX.lc) zmax = CTX.lc;
    
      if(CTX.ortho) {
        // setting up the near and far clipping planes so that the box is
        // large enough to manipulate the model and zoom, but not too big
        // (the z-buffer resolution, e.g., on software Mesa can become
        // insufficient)
        double clip = zmax * CTX.s[2] * CTX.clip_factor;
        glOrtho(CTX.vxmin, CTX.vxmax, CTX.vymin, CTX.vymax, -clip, clip);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        grad_z = 0.99 * clip;
        grad_xy = 1.;
      }
      else {
        double clip_near = 0.75 * CTX.clip_factor * zmax;
        double clip_far = 75. * CTX.clip_factor * zmax;
        double coef = (75./0.75) / 3.;
        // recenter the model such that the perspective is always at the
        // center of gravity (we should maybe add an option to choose
        // this, as we do for the rotation center)
        CTX.t_init[0] = CTX.cg[0];
        CTX.t_init[1] = CTX.cg[1];
        CTX.vxmin -= CTX.t_init[0];
        CTX.vxmax -= CTX.t_init[0];
        CTX.vymin -= CTX.t_init[1];
        CTX.vymax -= CTX.t_init[1];
        glFrustum(CTX.vxmin, CTX.vxmax, CTX.vymin, CTX.vymax, clip_near, clip_far);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glTranslated(-coef * CTX.t_init[0], -coef * CTX.t_init[1], -coef * clip_near);
        glScaled(coef, coef, coef);
        grad_z = 0.99 * clip_far;
        grad_xy = clip_far / clip_near;
      }
    
      // draw background gradient
      if(CTX.render_mode != GMSH_SELECT && CTX.bg_gradient){
        glPushMatrix();
        glLoadIdentity();
        glTranslated(0., 0., -grad_z);
        if(CTX.bg_gradient == 1){ // vertical
          glBegin(GL_QUADS);
          glColor4ubv((GLubyte *) & CTX.color.bg);
          glVertex3d(grad_xy * CTX.vxmin, grad_xy * CTX.vymin, 0.);
          glVertex3d(grad_xy * CTX.vxmax, grad_xy * CTX.vymin, 0.);
          glColor4ubv((GLubyte *) & CTX.color.bg_grad);
          glVertex3d(grad_xy * CTX.vxmax, grad_xy * CTX.vymax, 0.);
          glVertex3d(grad_xy * CTX.vxmin, grad_xy * CTX.vymax, 0.);
          glEnd();
        }
        else if(CTX.bg_gradient == 2){ // horizontal
          glBegin(GL_QUADS);
          glColor4ubv((GLubyte *) & CTX.color.bg);
          glVertex3d(grad_xy * CTX.vxmax, grad_xy * CTX.vymin, 0.);
          glVertex3d(grad_xy * CTX.vxmax, grad_xy * CTX.vymax, 0.);
          glColor4ubv((GLubyte *) & CTX.color.bg_grad);
          glVertex3d(grad_xy * CTX.vxmin, grad_xy * CTX.vymax, 0.);
          glVertex3d(grad_xy * CTX.vxmin, grad_xy * CTX.vymin, 0.);
          glEnd();
        }
        else{ // radial
          double cx = grad_xy * (CTX.vxmin + CTX.vxmax) / 2.;
          double cy = grad_xy * (CTX.vymin + CTX.vymax) / 2.;
          double r = grad_xy * MAX(CTX.vxmax - CTX.vxmin, CTX.vymax - CTX.vymin) / 2.;
          glBegin(GL_TRIANGLE_FAN);
          glColor4ubv((GLubyte *) & CTX.color.bg_grad);
          glVertex3d(cx, cy, 0.);
          glColor4ubv((GLubyte *) & CTX.color.bg);
          glVertex3d(cx + r, cy, 0.);
          int ntheta = 36;
          for(int i = 1; i < ntheta + 1; i ++){
    	double theta = i * 2 * M_PI / (double)ntheta;
    	glVertex3d(cx + r * cos(theta), cy + r * sin(theta), 0.);	
          }
          glEnd();
        }
        glPopMatrix();
      }
    }
    
    void InitRenderModel(void)
    {
      GLfloat r, g, b;
    
      for(int i = 0; i < 6; i++) {
        if(CTX.light[i]) {
          GLfloat position[4] = {(GLfloat)CTX.light_position[i][0],
    			     (GLfloat)CTX.light_position[i][1],
    			     (GLfloat)CTX.light_position[i][2],
    			     (GLfloat)CTX.light_position[i][3]};
          glLightfv((GLenum)(GL_LIGHT0 + i), GL_POSITION, position);
    
          r = CTX.UNPACK_RED(CTX.color.ambient_light[i])/255.;
          g = CTX.UNPACK_GREEN(CTX.color.ambient_light[i])/255.;
          b = CTX.UNPACK_BLUE(CTX.color.ambient_light[i])/255.;
          GLfloat ambient[4] = {r, g, b, 1.0};
          glLightfv((GLenum)(GL_LIGHT0 + i), GL_AMBIENT, ambient);
    
          r = CTX.UNPACK_RED(CTX.color.diffuse_light[i])/255.;
          g = CTX.UNPACK_GREEN(CTX.color.diffuse_light[i])/255.;
          b = CTX.UNPACK_BLUE(CTX.color.diffuse_light[i])/255.;
          GLfloat diffuse[4] = {r, g, b, 1.0};
          glLightfv((GLenum)(GL_LIGHT0 + i), GL_DIFFUSE, diffuse);
    
          r = CTX.UNPACK_RED(CTX.color.specular_light[i])/255.;
          g = CTX.UNPACK_GREEN(CTX.color.specular_light[i])/255.;
          b = CTX.UNPACK_BLUE(CTX.color.specular_light[i])/255.;
          GLfloat specular[4] = {r, g, b, 1.0};
          glLightfv((GLenum)(GL_LIGHT0 + i), GL_SPECULAR, specular);
    
          glEnable((GLenum)(GL_LIGHT0 + i));
        }
        else{
          glDisable((GLenum)(GL_LIGHT0 + i));
        }
      }
    
      // ambient and diffuse material colors track glColor automatically
      glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
      glEnable(GL_COLOR_MATERIAL);
      // "white"-only specular material reflection color
      GLfloat spec[4] = {CTX.shine, CTX.shine, CTX.shine, 1.0};
      glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, spec);
      // specular exponent in [0,128] (larger means more "focused"
      // reflection)
      glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, CTX.shine_exponent);
    
      glShadeModel(GL_SMOOTH);
    
      // Normalize the normals automatically. We could use the more
      // efficient glEnable(GL_RESCALE_NORMAL) instead (since we initially
      // specify unit normals), but GL_RESCALE_NORMAL does only work with
      // isotropic scalings (and we allow anistotropic scalings in
      // myZoom). Note that GL_RESCALE_NORMAL is only available in
      // GL_VERSION_1_2.
      glEnable(GL_NORMALIZE);
    
      // lighting is enabled/disabled for each particular primitive later
      glDisable(GL_LIGHTING);
    }
    
    void InitPosition(void)
    {
      glScaled(CTX.s[0], CTX.s[1], CTX.s[2]);
      glTranslated(CTX.t[0], CTX.t[1], CTX.t[2]);
    
      if(CTX.rotation_center_cg)
        glTranslated(CTX.cg[0], CTX.cg[1], CTX.cg[2]);
      else
        glTranslated(CTX.rotation_center[0],
    		 CTX.rotation_center[1],
    		 CTX.rotation_center[2]);
      
      CTX.buildRotationMatrix();
      glMultMatrixd(CTX.rot);
    
      if(CTX.rotation_center_cg)
        glTranslated(-CTX.cg[0], -CTX.cg[1], -CTX.cg[2]);
      else
        glTranslated(-CTX.rotation_center[0],
    		 -CTX.rotation_center[1],
    		 -CTX.rotation_center[2]);
    
      // store the projection and modelview matrices at this precise
      // moment (so that we can use them at any later time, even if the
      // context has changed, i.e., even if we are out of Draw())
      glGetDoublev(GL_PROJECTION_MATRIX, CTX.proj);
      glGetDoublev(GL_MODELVIEW_MATRIX, CTX.model);
    
      for(int i = 0; i < 6; i++)
        glClipPlane((GLenum)(GL_CLIP_PLANE0 + i), CTX.clip_plane[i]);
    }
    
    // Entity selection routines
    
    class hit{
    public:
      GLuint type, ient, depth;
      hit(GLuint t, GLuint i, GLuint d) : type(t), ient(i), depth(d) {}
    };
    
    class hitDepthLessThan{
     public:
      bool operator()(const hit &h1, const hit &h2) const
      {
        return h1.depth < h2.depth;
      }
    };
    
    bool ProcessSelectionBuffer(int entityType, bool multipleSelection, 
    			    bool selectMesh, int x, int y, int w, int h,
    			    std::vector<GVertex*> &vertices,
    			    std::vector<GEdge*> &edges,
    			    std::vector<GFace*> &faces,
    			    std::vector<GRegion*> &regions)
    {
      vertices.clear();
      edges.clear();
      faces.clear();
      regions.clear();
    
      // In our case the selection buffer size is equal to 5 x the maximum
      // number of possible hits
      int size = 5 * (GMODEL->numVertex() + GMODEL->numEdge() + 
    		  GMODEL->numFace() + GMODEL->numRegion()) + 100;
      GLuint *selectionBuffer = new GLuint[size];
      glSelectBuffer(size, selectionBuffer);
    
      glRenderMode(GL_SELECT);
      CTX.render_mode = GMSH_SELECT;
    
      glInitNames();
      glPushMatrix();
      InitProjection(x, y, w, h);
      InitPosition();
      Draw_Geom();
      if(selectMesh) Draw_Mesh();
      glPopMatrix();
    
      GLint numhits = glRenderMode(GL_RENDER);
      CTX.render_mode = GMSH_RENDER;
    
      if(!numhits){ // no hits
        delete [] selectionBuffer;
        return false;
      }
      else if(numhits < 0){ // overflow
        delete [] selectionBuffer;
        Msg(WARNING, "Selection buffer size exceeded");
        return false;
      }
    
      std::vector<hit> hits;
      GLuint *ptr = selectionBuffer;
      for(int i = 0; i < numhits; i++) {
        // in Gmsh 'names' should always be 2 or 0. If names is 2, the
        // first name is the type of the entity (0 for point, 1 for line,
        // etc.) and the second is the entity number; if names is 0 there
        // is nothing on the stack.
        GLuint names = *ptr++; 
        GLuint mindepth = *ptr++;
        *ptr++; // maxdepth
        if(names == 2){
          GLuint depth = mindepth;
          GLuint type = *ptr++; 
          GLuint ient = *ptr++;
          hits.push_back(hit(type, ient, depth));
        }
      }
    
      delete [] selectionBuffer;
      
      if(!hits.size()){ // no entities
        return false;
      }
    
      // sort hits to get closest entities first
      std::sort(hits.begin(), hits.end(), hitDepthLessThan());
    
      // filter result: if entityType == ENT_NONE, return the closest
      // entity of "lowest dimension" (point < line < surface <
      // volume). Otherwise, return the closest entity of type
      // "entityType"
      GLuint typmin = 4;
      for(unsigned int i = 0; i < hits.size(); i++)
        typmin = std::min(typmin, hits[i].type);
    
      for(unsigned int i = 0; i < hits.size(); i++) {
        if((entityType == ENT_ALL) ||
           (entityType == ENT_NONE && hits[i].type == typmin) ||
           (entityType == ENT_POINT && hits[i].type == 0) ||
           (entityType == ENT_LINE && hits[i].type == 1) ||
           (entityType == ENT_SURFACE && hits[i].type == 2) ||
           (entityType == ENT_VOLUME && hits[i].type == 3)){
          switch (hits[i].type) {
          case 0:
    	{
    	  GVertex *v = GMODEL->vertexByTag(hits[i].ient);
    	  if(!v){
    	    Msg(GERROR, "Problem in point selection processing");
    	    return false;
    	  }
    	  vertices.push_back(v);
    	  if(!multipleSelection) return true;
    	}
    	break;
          case 1:
    	{
    	  GEdge *e = GMODEL->edgeByTag(hits[i].ient);
    	  if(!e){
    	    Msg(GERROR, "Problem in line selection processing");
    	    return false;
    	  }
    	  edges.push_back(e);
    	  if(!multipleSelection) return true;
    	}
    	break;
          case 2:
    	{
    	  GFace *f = GMODEL->faceByTag(hits[i].ient);
    	  if(!f){
    	    Msg(GERROR, "Problem in surface selection processing");
    	    return false;
    	  }
    	  faces.push_back(f);
    	  if(!multipleSelection) return true;
    	}
    	break;
          case 3:
    	{
    	  GRegion *r = GMODEL->regionByTag(hits[i].ient);
    	  if(!r){
    	    Msg(GERROR, "Problem in volume selection processing");
    	    return false;
    	  }
    	  regions.push_back(r);
    	  if(!multipleSelection) return true;
    	}
    	break;
          }
        }
      }
      return true;
    }
    
    // Takes a cursor position in window coordinates and returns the line
    // (given by a point and a unit direction vector), in real space, that
    // corresponds to that cursor position
    
    void Unproject(double x, double y, double p[3], double d[3])
    {
      GLint viewport[4];
      glGetIntegerv(GL_VIEWPORT, viewport);
    
      y = viewport[3]-y;
    
      GLdouble x0, y0, z0, x1, y1, z1;
    
      // we use CTX.model and CTX.proj instead of directly getGetDouble'ing
      // the matrices since unproject can be called in or after Draw2D
      
      if(!gluUnProject(x, y, 0.0, CTX.model, CTX.proj, viewport, &x0, &y0, &z0))
        Msg(WARNING, "unproject1 failed");
      if(!gluUnProject(x, y, 1.0, CTX.model, CTX.proj, viewport, &x1, &y1, &z1))
        Msg(WARNING, "unproject2 failed");
      
      p[0] = x0;
      p[1] = y0;
      p[2] = z0;
      d[0] = x1-x0;
      d[1] = y1-y0;
      d[2] = z1-z0;
      double len = sqrt(d[0]*d[0] + d[1]*d[1] + d[2]*d[2]);
      d[0] /= len;
      d[1] /= len;
      d[2] /= len;
    }
    
    void Viewport2World(double win[3], double xyz[3])
    {
      GLint viewport[4];
      GLdouble model[16], proj[16];
      glGetIntegerv(GL_VIEWPORT, viewport);
      glGetDoublev(GL_PROJECTION_MATRIX, proj);
      glGetDoublev(GL_MODELVIEW_MATRIX, model);
      gluUnProject(win[0], win[1], win[2], model, proj, viewport, &xyz[0], &xyz[1], &xyz[2]);
    }
    
    void World2Viewport(double xyz[3], double win[3])
    {
      GLint viewport[4];
      GLdouble model[16], proj[16];
      glGetIntegerv(GL_VIEWPORT, viewport);
      glGetDoublev(GL_PROJECTION_MATRIX, proj);
      glGetDoublev(GL_MODELVIEW_MATRIX, model);
      gluProject(xyz[0], xyz[1], xyz[2], model, proj, viewport, &win[0], &win[1], &win[2]);
    }