Using stroke() on a 2D array of objects to only highlight the border of a specified object

import processing.core.*;

public class floodFillGUI extends PApplet {

    private cell grid[][]; // this is the grid layout of the cell objects
    private boolean overCell = false; // used to determine when the mouse is hovering over a cell
    private int rowSize = 4; // currently hard coded size of the grid array "grid[rowSize][columnSize]", plan to make this dynamic in the future
    private int columnSize = 4; // currently hard coded size of the grid array "grid[rowSize][columnSize]", plan to make this dynamic in the future
    private int currentRow = 0; // used to track the index of the current row (current == mouse over) 
    private int currentColumn = 0; // used to track the index of the current column (current == mouse over) 

    public static void main(String[] args)
    {
        PApplet.main("floodFillGUI");
    }

    public void setup()
    {
        size(400, 600);
        buildGrid();                
    }

    public void draw()
    {           
        for(int row = 0; row < grid.length; row++)
        {
            for(int column = 0; column < grid[row].length; column++)
            {
                isMouseOverCell();
                grid[row][column].display(grid.length, row, grid[row].length, column, overCell);
            }
        }           
    }

    public void buildGrid()
    {
        grid = new cell[rowSize][columnSize];

        for(int row = 0; row < grid.length; row++)
        {
            for(int column = 0; column < grid[row].length; column++)
            {
                grid[row][column] = new cell(this, width, height-200); //initialize the grid object, -200 height is to give a buffer at the bottom of the window for adding new  display features
                grid[row][column].set_bgColor(column);
                grid[row][column].set_fgColor(column);
                grid[row][column].setNumber(column);
            }
        }
    }

    public void isMouseOverCell()
    {
        int rowIndex = 0;
        int columnIndex = 0;

        // get mouse position Y and divide by applet height / rowSize ([R][C] or [Y][X])
        // Example if mouseY is on pixel 150 and total height of applet is 400 and our rowSize array is 4
        // then it is (150 / (400 / 4)) which is (150 / 100) which is 1, therefore my rowIndex is 1 in [R][C] 
        rowIndex = (mouseY / ((int) (height-200) / rowSize));
        columnIndex = (mouseX / ((int) width / columnSize));

        // If our mouse is in a cell set overCell to true and record that cell's indexes
        if(rowIndex < rowSize && columnIndex < columnSize)
        {
            currentRow = rowIndex;
            currentColumn = columnIndex;
            overCell = true;
            //System.out.println("[" + currentRow + "][" + currentColumn + "]");
        }
        else
        {
            overCell = false;
        }
    }
}


import processing.core.*;

public class cell{
    private final int RADIUS = 2; // int processing.core.PConstants.RADIUS = 2
    private final int CENTER = 3; // int processing.core.PConstants.CENTER = 3

    private PApplet parent; // the parent PApplet that we will render ourselves onto
    private int number = 0; // stores integer value that is to be displayed on the cell
    private int fgColor = 0; // stores foreground color
    private int bgColor = 0; // stores background color
    private int rowSize = 0; // stores [R].length of the [R][C] cell array
    private int columnSize = 0; // stores [C].length of the [R][C] cell array
    private float xPos = 0; // center position of cell on x axis
    private float yPos = 0; // center position of cell on y axis
    private float xSize = 0; // size of cell in the x axis
    private float ySize = 0; // size of cell in the y axis
    private float width = 0; // width of area containing all cells (usually going to be equal to parent.width)
    private float height = 0; // height of area containing all cells (usually going to be less than parent.height)

    public enum colors
    {
        BLUE        (0, 0, 255),
        DARK_BLUE   (0, 0, 200),
        ORANGE      (255, 102, 0),
        DARK_ORANGE (179, 71, 0),
        GREEN       (0, 255, 0),
        DARK_GREEN  (0, 200, 0),
        PURPLE      (153, 51, 255),
        DARK_PURPLE (115, 0, 230),
        RED         (255, 0, 0),
        DARK_RED    (200, 0, 0),
        YELLOW      (255, 255, 0),
        DARK_YELLOW (179, 179, 0),
        AQUA        (51, 204, 204),
        DARK_AQUA   (36, 143, 143),
        PINK        (255, 51, 153),
        DARK_PINK   (230, 0, 115),
        LIME        (153, 255, 102),
        DARK_LIME   (119, 255, 51),
        SALMON      (255, 128, 128),
        DARK_SALMON (255, 85, 85),
        GRAY        (150, 150, 150),
        DARK_GRAY   (100, 100, 100);

        private final int red;
        private final int green;
        private final int blue;

        private colors(int r, int g, int b)
        {
            red = r;
            green = g;
            blue = b;
        }

        public int[] getColor()
        {
            int[] array = {this.red, this.green, this.blue};
            return array;
        }

        public int getR_val()
        {
            return this.red;
        }

        public int getG_val()
        {
            return this.green;
        }

        public int getB_val()
        {
            return this.blue;
        }
    }

    public cell(PApplet p, float width, float height)
    {
        parent = p;
        this.width = width;
        this.height = height;
    }

    public void display(int rSize, int rowIndex, int cSize, int columnIndex, boolean mouseOver)
    {
        columnSize = cSize;
        rowSize = rSize;
        float xDis = 2 * columnSize; // double column array length [R][C]
        float yDis = 2 * rowSize; // double the row array length [R][C]
        xSize = width / xDis; // this is the size of the square in the X axis
        ySize = height / yDis; // this is the size of the square in the Y axis
        xPos = (width * ((1 + columnIndex * 2) / xDis)); // position of the square based on its center in the X axis.  The (1 + index * 2) is the offset from the left edge (X axis)
        yPos = (height * ((1 + rowIndex * 2) / yDis));  // position of the square based on its center in the Y axis  The (1 + index * 2) is the offset from the top edge (Y axis)

        parent.stroke(0); // set the outline of the geometry to black

        this.set_bgColor(number); // set the background color to the "dark" variant
        parent.fill(bgColor); 
        parent.rectMode(RADIUS);
        parent.rect(xPos, yPos, xSize, ySize); 

        if(mouseOver)
        {
            parent.stroke(255);
        }

        this.set_fgColor(number);
        parent.fill(fgColor);
        parent.rectMode(CENTER);
        parent.rect(xPos, yPos, width * 7/32, height * 7/32, 5);

        parent.fill(0);
        parent.textSize(40);
        parent.textAlign(CENTER, CENTER);
        parent.text(number, xPos, yPos - 5);        
    }

    public void set_fgColor(int r, int g, int b)
    {
        fgColor = parent.color(r, g, b);
    }

    public void set_fgColor(int numValue)
    {
        switch(numValue){
            case 0:
                fgColor = parent.color(colors.BLUE.getR_val(), colors.BLUE.getG_val(), colors.BLUE.getB_val());
                break;
            case 1:
                fgColor = parent.color(colors.ORANGE.getR_val(), colors.ORANGE.getG_val(), colors.ORANGE.getB_val());
                break;
            case 2:
                fgColor = parent.color(colors.GREEN.getR_val(), colors.GREEN.getG_val(), colors.GREEN.getB_val());
                break;
            case 3:
                fgColor = parent.color(colors.PURPLE.getR_val(), colors.PURPLE.getG_val(), colors.PURPLE.getB_val());
                break;
            case 4:
                fgColor = parent.color(colors.RED.getR_val(), colors.RED.getG_val(), colors.RED.getB_val());
                break;
            case 5:
                fgColor = parent.color(colors.YELLOW.getR_val(), colors.YELLOW.getG_val(), colors.YELLOW.getB_val());
                break;
            case 6:
                fgColor = parent.color(colors.AQUA.getR_val(), colors.AQUA.getG_val(), colors.AQUA.getB_val());
                break;
            case 7:
                fgColor = parent.color(colors.PINK.getR_val(), colors.PINK.getG_val(), colors.PINK.getB_val());
                break;
            case 8:
                fgColor = parent.color(colors.LIME.getR_val(), colors.LIME.getG_val(), colors.LIME.getB_val());
                break;
            case 9:
                fgColor = parent.color(colors.SALMON.getR_val(), colors.SALMON.getG_val(), colors.SALMON.getB_val());
                break;
            default:
                fgColor = parent.color(colors.GRAY.getR_val(), colors.GRAY.getG_val(), colors.GRAY.getB_val());
                break;
        }
    }

    public int get_fgColor()
    {
        return fgColor;
    }

    public void set_bgColor(int r, int g, int b)
    {
        bgColor = parent.color(r, g, b);
    }

    public void set_bgColor(int numValue)
    {
        switch(numValue){
            case 0:
                bgColor = parent.color(colors.DARK_BLUE.getR_val(), colors.DARK_BLUE.getG_val(), colors.DARK_BLUE.getB_val());
                break;
            case 1:
                bgColor = parent.color(colors.DARK_ORANGE.getR_val(), colors.DARK_ORANGE.getG_val(), colors.DARK_ORANGE.getB_val());
                break;
            case 2:
                bgColor = parent.color(colors.DARK_GREEN.getR_val(), colors.DARK_GREEN.getG_val(), colors.DARK_GREEN.getB_val());
                break;
            case 3:
                bgColor = parent.color(colors.DARK_PURPLE.getR_val(), colors.DARK_PURPLE.getG_val(), colors.DARK_PURPLE.getB_val());
                break;
            case 4:
                bgColor = parent.color(colors.DARK_RED.getR_val(), colors.DARK_RED.getG_val(), colors.DARK_RED.getB_val());
                break;
            case 5:
                bgColor = parent.color(colors.DARK_YELLOW.getR_val(), colors.DARK_YELLOW.getG_val(), colors.DARK_YELLOW.getB_val());
                break;
            case 6:
                bgColor = parent.color(colors.DARK_AQUA.getR_val(), colors.DARK_AQUA.getG_val(), colors.DARK_AQUA.getB_val());
                break;
            case 7:
                bgColor = parent.color(colors.DARK_PINK.getR_val(), colors.DARK_PINK.getG_val(), colors.DARK_PINK.getB_val());
                break;
            case 8:
                bgColor = parent.color(colors.DARK_LIME.getR_val(), colors.DARK_LIME.getG_val(), colors.DARK_LIME.getB_val());
                break;
            case 9:
                bgColor = parent.color(colors.DARK_SALMON.getR_val(), colors.DARK_SALMON.getG_val(), colors.DARK_SALMON.getB_val());
                break;
            default:
                bgColor = parent.color(colors.DARK_GRAY.getR_val(), colors.DARK_GRAY.getG_val(), colors.DARK_GRAY.getB_val());
                break;
        }
    }

    public int get_bgColor()
    {
        return bgColor;
    }

    public void setNumber(int val)
    {
        number = val;
    }

    public int getNumber()
    {
        return number;
    }

    public float getXPos()
    {
        return xPos;
    }

    public float getYPos()
    {
        return yPos;
    }

    public float getXSize()
    {
        return xSize;
    }

    public float getYSize()
    {
        return ySize;
    }

}

Sorry for the huge block of code but I figure I would just include it all to provide full context. I'm currently trying to build a game that I found while browsing reddit's daily programming challenges (http://entibo.fr/lvlr/). I'm just laying down the ground work at the moment and I've hit a road block. This is my first time using the processing library (found it from the University of California, San Diego OOP in Java class) but this is not my first time using Java or Eclipse and so naturally that is the IDE that I am currently using. Also, this is my first time using ENUM classes...so if the one I'm using is useless I apologize in advance.

Anyway, my programming issue is that when I attempt to draw the border around a cell that is being moused over I instead draw borders around all the rectangles (cells) on screen. I have already put the logic in place for determining which rectangle needs the border drawn around it (I've tested this by my commented out println statement and it does print out the correct indexes of the moused over cell). I just need help figuring out how I should modify my cell class's display() method to accomplish this.

This image is an example of the program running and the mouse is not hovering over any of the cells.
not rolled over
Notice no borders are lit up? This is working as intended!

This image is an example of the program running and the mouse is touching one of the cells.
rolled over
Notice how all the borders are lit up? This is not working as intended!

I only want 1 cell at any given time to become highlighted upon mouse roll over. For example if I mouse over the cell located in grid[1][2], then I would want the 3rd from the top "Orange" cell to be highlighted and nothing else.

Any help would be appreciated and again sorry for posting all my code, I just want to be as thorough as possible.

Answers

  • Not sure how to edit my original post but I did not realize line numbers would show up on my code. Points of interest (I think) are the draw() method on line 23 and the display method in my cell class on line 162.

  • Answer ✓

    Can't you just make mouseOver a field of class cell?
    BtW, classes & interfaces should be named following UpperCamelCase convention. L-)

  • Answer ✓

    In the online sketch link below, it got a field called isHovering:
    http://studio.SketchPad.cc/sp/pad/view/ro.9eDRvB4LRmLrr/latest

    It is checked out every time the mouseMoved() by invoking Button's method isInside():
    https://Processing.org/reference/mouseMoved_.html

  • Thanks! Took me a bit to get it worked out but that was helpful! I added "noLoop();" to my extended PApplet class's setup method. I also implemented the mouseMoved method that you provided. Lastly, I changed and moved the hover logic to the Cell class (and I renamed my classes to match camel casing). Just for reference I've listed the changes below. Thanks again!

    Listed below are the changes I made to the "FloodFillGUI" class.

    public void setup()
    {
        size(400, 600);
        buildGrid();
        noLoop();
    }
    public void mouseMoved()
    {
        for(int row = 0; row < grid.length; row++)
        {
            for(int column = 0; column < grid[row].length; column++)
            {
                grid[row][column].isMouseOverCell();
            }
        }   
        redraw();
    }
    

    Listed below are the changes I made to the "Cell" class.

    public void isMouseOverCell()
    {       
        if((parent.mouseX > xPos - xSize && parent.mouseX < xPos + xSize && parent.mouseY > yPos - ySize && parent.mouseY < yPos + ySize))
        {
            isHovering = true;
        }
        else
        {
            isHovering = false;
        }
    }
    
  • Nice it worked out alright! As a bonus, a more compact isMouseOverCell() method: :-bd

    public boolean isMouseOverCell() {
      return isHovering =
        parent.mouseX > xPos-xSize && parent.mouseX < xPos+xSize &&
        parent.mouseY > yPos-ySize && parent.mouseY < yPos+ySize;
    }
    
  • edited February 2017

    Oh, mouseMoved() can be shorten immensely by replacing regular w/ enhanced for () loops: :)>-
    https://Processing.org/reference/for.html

    public void mouseMoved() {
      for (Cell[] row : grid)  for (Cell c : row)  c.isMouseOverCell();
      redraw();
    }
    
  • Hmm. I've seen that notation before but never realized it was just a for each loop (used those in VB.net and C#.net).

    What you have is saying "for each row in grid and for each cell in row"? That does make it really concise. I'll have to refactor my loops to that notation as it is much shorter since as you could probably already tell I labeled my loops with "row/column" because for me it makes them easier to understand/trace. Thanks again!

  • What you have is saying "for each row in grid and for each cell in row"?

    That's it. Now even JS got those "enhanced" for loops too! ~O)
    Check it out if that was written for p5.js instead: :P
    https://developer.Mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of

    function mouseMoved() {
      for (let row of grid)  for (let c of row)  c.isMouseOverCell();
      redraw();
    }
    
Sign In or Register to comment.