Hello. I have a sketch that draws a grid, and places a rectangular cursor which is constrained to the grid area, and snaps to the center of the nearest grid cell when the mouse is released. The center “Tile” for a grid cell will turn red when the center of the cursor object is within that cell.
There are a couple of things I’m having trouble understanding, and any suggestions would be appreciated.
EDIT: I found a solution to the first thing, I no longer need help with that.
One thing:
How can I prevent the “cursor” object from getting “stuck” if while it is being dragged, the mouse leaves the constrained area?
I would like to have it continue to move within the grid even if the mouse leaves the constrained area, until the mouse is released, and then let it snap to the cell center that it’s closest to.
Another thing:
When the “cursor” is moved to the bottom-right grid cell, it turns red, which I don’t understand.
If a fill color for the cursor (line 49) is applied, this effect can be disguised, but I am trying to understand how this red fill color is being applied to anything other than a very specific “Tile” object. It seems like there is some hole in the logic that I can’t find.
I have tried lots of different grid sizes, it’s always only the bottom-right grid cell where this happens.
- In the Cursor class, checkOverCell() sets the values for activeCell, and sends it to the Grid class via grid.setActive(activeCell).
- setActive finds a Tile instance with matching coordinates in the array of center points, sets it’s boolean highlightOn to true, and false for all other elements.
- The color is set in the Tile class, based on boolean highlightOn.
Grid grid;
Cursor cursor;
PVector m;
void setup() {
//size(screen.width, screen.height);
size(500, 500);
grid = new Grid();
cursor = new Cursor();
}
void draw() {
background(92, 92, 255);
m = new PVector(mouseX, mouseY);
grid.display();
cursor.drawCursor();
}
void mouseDragged() {
if (m.x >= cursor.loc.x - cursor.cSize.x/2 &&
m.x <= cursor.loc.x + cursor.cSize.x/2
&& m.y >= cursor.loc.y - cursor.cSize.y/2 &&
m.y <= cursor.loc.y + cursor.cSize.y/2) {
cursor.loc = m; // set the Cursor center to mouse
}
}
void mouseReleased() {
cursor.snapToCenter();
}
class Cursor {
PVector loc, activeCell, cSize;
Cursor() {
loc = new PVector();
activeCell = new PVector();
cSize = new PVector(grid.colW, grid.rowH);
}
void snapToCenter() {
loc = activeCell;
}
void drawCursor() {
// find the grid coordinates of the cell center
// which is closest to the Cursor location
// and store it in activeCell
checkOverCell();
// keep the center of the Cursor inside the grid
loc.x = constrain(loc.x, grid.gCenter.x - grid.gSize.x/2-1 + cSize.x/2,
grid.gCenter.x + grid.gSize.x/2 - cSize.x/2);
loc.y = constrain(loc.y, grid.gCenter.y - grid.gSize.y/2-1 + cSize.y/2,
grid.gCenter.y + grid.gSize.y/2 - cSize.y/2);
stroke(255);
//fill(225, 255, 225, 127);
rectMode(CENTER);
rect(loc.x, loc.y, cSize.x, cSize.y);
}
void checkOverCell() {
checkOverColCenters();
checkOverRowCenters();
grid.setActive(activeCell); // send the PVector activeCell to the grid
}
void checkOverColCenters() {
// determine which column the Cursor is over
for (int i = 0; i < grid.cellCenters.length; i++) {
if (loc.x >= grid.cellCenters[i][0].x - grid.colW/2 &&
loc.x <= grid.cellCenters[i][0].x + grid.colW/2) {
// set x in activeCell to the grid's column index
activeCell.x = grid.cellCenters[i][0].x;
}
}
}
void checkOverRowCenters() {
// determine which row the Cursor is over
for (int i = 0; i < grid.cellCenters.length; i++) {
if (loc.y >= grid.cellCenters[0][i].y - grid.rowH/2 &&
loc.y <= grid.cellCenters[0][i].y + grid.rowH/2) {
// set y in activeCell to the grid's row index
activeCell.y = grid.cellCenters[0][i].y;
}
}
}
}
class Grid {
int colW, rowH, nCols, nRows;
PVector gOrigin, gSize, gCenter, cellSize, linePointSize, centerPointSize;
PVector[][] linePoints; // array of grid line locations
Tile[][] points; // array of grid line "Tile" objects
PVector[][] cellCenters; // array of grid cell center locations
Tile[][] centers; // array of grid cell center "Tile" objects
Grid() {
nCols = 3;
nRows = 3;
colW = 100;
rowH = 100;
linePointSize = new PVector(10, 10); // size of "Tile" object
centerPointSize = new PVector(5, 5); // size of "Tile" object
gSize = new PVector(colW * nCols, rowH * nRows); // overall grid W/H
gCenter = PVector.div(gSize, 2); // grid center point
// gOrigin is used to translate the center of the grid to a specific coordinate
gOrigin = new PVector(width/2 - gCenter.x, height/2 - gCenter.y);
cellSize = new PVector(colW, rowH);
linePoints = new PVector[nCols + 1][nRows + 1]; // array of grid line locations
points = new Tile[nCols + 1][nRows + 1]; // array of grid line "Tile" objects
cellCenters = new PVector[nCols][nRows]; // array of grid cell center locations
centers = new Tile[nCols][nRows]; // array of grid cell center "Tile" objects
makeGrid();
applyTranslation(gOrigin);
}
void makeGrid() {
for (int i = 0; i < nCols + 1; i++) {
for (int j = 0; j < nRows + 1; j++) {
// initialize array of grid line locations
linePoints[i][j] = new PVector(i * cellSize.x, j * cellSize.y);
// initialize array of grid line "Tile" objects
points[i][j] = new Tile(linePoints[i][j].x,
linePoints[i][j].y, linePointSize);
}
}
for (int i = 0; i < nCols; i++) {
for (int j = 0; j < nRows; j++) {
// initialize array of grid cell center locations
cellCenters[i][j] = new PVector(i * cellSize.x + cellSize.x/2,
j * cellSize.y + cellSize.y/2);
// initialize array of grid cell center "Tile" objects
centers[i][j] = new Tile(cellCenters[i][j].x,
cellCenters[i][j].y, centerPointSize);
}
}
}
void applyTranslation(PVector newOrigin) {
gCenter.add(newOrigin);
for (int i = 0; i < linePoints.length; i++) {
for (int j = 0; j < linePoints.length; j++) {
// translate each element in array of grid line locations
linePoints[i][j].add(newOrigin);
// translate each element in array of grid line "Tile" objects
points[i][j].loc.add(newOrigin);
}
}
for (int i = 0; i < cellCenters.length; i++) {
for (int j = 0; j < cellCenters.length; j++) {
// translate each element in array of grid cell center locations
cellCenters[i][j].add(newOrigin);
// translate each element in array of grid cell center "Tile" objects
centers[i][j].loc.add(newOrigin);
}
}
}
void setActive(PVector activeCell) {
for (int i = 0; i < centers.length; i++) {
for (int j = 0; j < centers.length; j++) {
// check the coordinates of each "Tile" instance
// in grid center "Tile" array to see if they
// match activeCell's coordinates
if (centers[i][j].loc.x == activeCell.x &&
centers[i][j].loc.y == activeCell.y) {
// if they match, set highlightOn to true
centers[i][j].highlightOn = true;
} else {
// if they don't match, set highlightOn to false
centers[i][j].highlightOn = false;
}
}
}
}
void display() {
for (int i = 0; i < nCols + 1; i++) {
for (int j = 0; j < nRows + 1; j++) {
points[i][j].display(); // display the grid line "Tile" object
}
}
for (int i = 0; i < nCols; i++) {
for (int j = 0; j < nRows; j++) {
centers[i][j].display(); // display the cell center "Tile" object
}
}
}
}
class Tile {
PVector loc, tileSize;
boolean highlightOn;
Tile(float x, float y, PVector tileSize) {
this.loc = new PVector(x, y);
this.tileSize = tileSize;
highlightOn = false;
}
void display() {
if (highlightOn) {
// set fill to red if highlightOn is true
stroke(255, 0, 0, 127);
fill(255, 0, 0, 127);
} else {
// set fill to white if highlightOn is false
stroke(255);
fill(255, 255, 255, 127);
}
rectMode(CENTER);
rect(loc.x, loc.y, tileSize.x, tileSize.y);
}
}