How to debug this ? (zoomable grid)

edited October 2017 in Questions about Code

Hi,
Down this post is the code I wrote for a moveable and zoomable 2D grid. It is working almost well, a bug appears and I don't really understand where it comes from. Does anyone have a hint and can explain to me how I can debug this myself ? I tried to use de debugger without succes, I don't find the suitable line to place the breakpoint. If you need I could translate the comments in the code.

How to reproduce the bug :
- zoom out to the maximum
- move far from the origin (3-4 window-sized drags)
- zoom in to the maximum

When I do this, after a while zooming in the graduations don't behave as expected (they seem to move randomly) and once zoomed in to the maximum, it is impossible to zoom out.

Repere R;

void settings()
{
  size(400, 400);
  //fullScreen();
}

void setup()
{
  R = new Repere();
}

void draw()
{
  background(255);
  R.draw();
  stroke(0);
  strokeWeight(1);
  noFill();
  PVector p = R.pointToPixel(1,0);
  ellipse(p.x, p.y, 10, 10);
}

void mouseWheel(MouseEvent e)
{
  R.mouseWheel(e);
}

void mouseDragged()
{
  R.mouseDragged();
}

void mouseReleased()
{
  R.mouseReleased();
}

void keyPressed()
{
  R.keyPressed();
}

class Repere
{
  PVector p, s;
  float infX, supX; // valeurs au bords de l'écran
  float infY, supY; // valeurs au bords de l'écran
  float scaleX, scaleY; // nb de pixels par unité
  float gradX, gradY; // tailles des graduations en unité
  int nbGradMax, nbGradMin; // nombre de graduation maximum et minimum sur chaque axe
  int divGrad; // divise les graduations en divGrad graduations lors du zoom
  int centerX, centerY; // coord de l'origine du repère dans la matrice de l'écran
  color c; // couleur
  boolean dragging;

  Repere()
  {
    p = new PVector(0, 0);
    s = new PVector(width, height);
    c = color(200, 100, 0);
    constructor();
  }

  Repere(int _positionX, int _positionY, int _sizeX, int _sizeY, color _c)
  {
    p = new PVector(_positionX, _positionY);
    s = new PVector(_sizeX, _sizeY);
    c = _c;
    constructor();
  }

  private void constructor()
  {
    centerX = getPositionX() + getSizeX()/2;
    centerY = getPositionY() + getSizeY()/2;
    setScale(100, 100);
    gradX = 1;
    gradY = 1;
    nbGradMax = 25;
    nbGradMin = 5;
    divGrad = 5;
    dragging = false;
  }

  PVector pixelToPoint(int x, int y) // retourne le point du repère correspondant au pixel passé en paramètre
  {
    return new PVector(map(x, 0, width, infX, supX), map(y, height, 0, infY, supY));
  }
  PVector pixelToPoint(PVector p)
  {
    return new PVector(map(p.x, 0, width, infX, supX), map(p.y, height, 0, infY, supY));
  }

  PVector pointToPixel(float x, float y) // retourne le pixel correspondant au point passé en paramètre
  {
    return new PVector(round(map(x, infX, supX, 0, width)), round(map(y, infY, supY, height, 0)));
  }
  PVector pointToPixel(PVector p) // retourne le pixel correspondant au point passé en paramètre
  {
    return new PVector(round(map(p.x, infX, supX, 0, width)), round(map(p.y, infY, supY, height, 0)));
  }

  void draw()
  {
    strokeWeight(1.5);
    stroke(c, 150);
    // axe des ordonnées
    if(getPositionX() <= centerX && centerX <= getPositionX()+getSizeX()){line(centerX, getPositionY(), centerX, getPositionY()+getSizeY());}
    // axe des abcisses
    if(getPositionY() <= centerY && centerY <= getPositionY()+getSizeY()){line(getPositionX(), centerY, getPositionX()+getSizeX(), centerY);}

    strokeWeight(1);

    // graduations verticales

    // verifie la taille de la graduation
    while((supX-infX)/gradX < nbGradMin){gradX = gradX/divGrad;}
    while((supX-infX)/gradX > nbGradMax){gradX = gradX*divGrad;}

    if(centerX >= getPositionX()) // à gauche de l'axe des ordonées
    {
      float x = centerX;
      int n = 0;
      while(x >= getPositionX())
      {
        if(x <= getPositionX() + getSizeX() && x != centerX)
        {
          if(n % divGrad == 0){stroke(c, 100);}
          else{stroke(c, 60);}
          line(x, getPositionY(), x, getPositionY()+getSizeY());
        }
        x -= gradX*scaleX;
        n++;
      }
    }
    if(centerX <= getPositionX() + getSizeX()) // à droite de l'axe des ordonées
    {
      float x = centerX;
      int n = 0;
      while(x <= getPositionX() + getSizeX())
      {
        if(x >= getPositionX() && x != centerX)
        {
          if(n % divGrad == 0){stroke(c, 100);}
          else{stroke(c, 60);}
          line(x, getPositionY(), x, getPositionY()+getSizeY());
        }
        x += gradX*scaleX;
        n++;
      }
    }

    // graduations horizontales

    // verifie la taille de la graduation
    //while((supY-infY)/gradY < nbGradMin){gradY = gradY/divGrad;}
    //while((supY-infY)/gradY > nbGradMax){gradY = gradY*divGrad;}
    gradY = gradX;

    if(centerY >= getPositionY()) // en haut de l'axe des abcisses
    {
      float y = centerY;
      int n = 0;
      while(y >= getPositionY())
      {
        if(y <= getPositionY()+getSizeY() && y != centerY)
        {
          if(n % divGrad == 0){stroke(c, 100);}
          else{stroke(c, 60);}
          line(getPositionX(), y, getPositionX()+getSizeX(), y);
        }
        y -= gradY*scaleY;
        n++;
      }
    }
    if(centerY <= getPositionY()+getSizeY()) // en bas de l'axe des abcisses
    {
      float y = centerY;
      int n = 0;
      while(y <= getPositionY()+getSizeY())
      {
        if(y >= getPositionY() && y != centerY)
        {
          if(n % divGrad == 0){stroke(c, 100);}
          else{stroke(c, 60);}
          line(getPositionX(), y, getPositionX()+getSizeX(), y);
        }
        y += gradY*scaleY;
        n++;
      }
    }
  }

  void displayInfo(int x, int y)
  {
    text("Echelle X : "+scaleX, x, y);
    text("Echelle Y : "+scaleY, x, y+20);
  }

  void zoom(float x, float y, float k) // (x, y) point fixe et k facteur de zoom
  {
    scaleX *= k;
    scaleY *= k;
    setIntervalX(x-(x-infX)/k, x+(supX-x)/k);
    setIntervalY(y-(y-infY)/k, y+(supY-y)/k);
    updateCenter();
  }

  void keyPressed()
  {
    if(key == ' '){this.translate(width/2, height/2);}
    if(key == '+'){zoom((infX+supX), (infY+supY), 1.125);}
    if(key == '-'){zoom((infX+supX), (infY+supY), 0.8);}
  }

  void mouseWheel(MouseEvent event)
  {
    if(mouseIsOver())
    {
      float zoom = 0.8;
      if (event.getCount() < 0) {zoom = 1.125; if(scaleX > 50000){zoom = 1;}}
      else{if(scaleX < 0.005){zoom = 1;}}
      // point fixe
      PVector p = pixelToPoint(mouseX, mouseY);
      zoom(p.x, p.y, zoom);
    }
  }

  void mouseDragged()
  {
    if(mouseIsOver()){dragging = true;}
    if(dragging){this.translate(centerX+mouseX-pmouseX, centerY+mouseY-pmouseY);}
  }

  void mouseReleased(){dragging = false;}

  boolean mouseIsOver()
  {
    boolean b = false;
    if(mouseX >= getPositionX() && mouseX <= getPositionX() + getSizeX())
    {
      if(mouseY >= getPositionY() && mouseY <= getPositionY() + getSizeY())
      {
        b = true;
      }
    }
    return b;
  }

  // GETTERS ---------------------------------

  float getInfX(){return infX;}
  float getInfY(){return infY;}
  float getSupX(){return supX;}
  float getSupY(){return supY;}
  PVector getOrigin(){return new PVector(centerX, centerY);}
  PVector getPosition(){return p;}
  int getPositionX(){return (int)p.x;}
  int getPositionY(){return (int)p.y;}
  PVector getSize(){return s;}
  int getSizeX(){return (int)s.x;}
  int getSizeY(){return (int)s.y;}

  // SETTERS ------------------------------

  void setPosition(int x, int y){p.x = x; p.y = y;}
  void setSize(int x, int y){s.x = x; s.y = y;}
  void setIntervalX(float a, float b){centerX -= ((b+a)/2-(supX+infX)/2)*scaleX; infX = a; supX = b;}
  void setIntervalY(float a, float b){centerY += ((b+a)/2-(supY+infY)/2)*scaleY; infY = a; supY = b;}
  void setScale(float a, float b){scaleX = a; scaleY = b; updateIntervals();}
  void translate(int x, int y){centerX = x; centerY = y; updateIntervals();}
  void updateIntervals(){infX = -centerX/scaleX; supX = (width-centerX)/scaleX;
                         supY = centerY/scaleY; infY = -(height-centerY)/scaleY;}
  void updateCenter(){centerX = round(-infX*scaleX); centerY = round(supY*scaleY);}
}

Answers

  • I was thinking about a problem coming from the precision on the float type. I tried to put a breakpoint inside the mouseWheel function and the zoom function but then the zoom does not work anymore and I can't reproduce the bug. Any other ideas ?

Sign In or Register to comment.