We are about to switch to a new forum software. Until then we have removed the registration on this forum.
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 ?