Welcome to the Java Programming Forums


The professional, friendly Java community. 21,500 members and growing!


The Java Programming Forums are a community of Java programmers from all around the World. Our members have a wide range of skills and they all have one thing in common: A passion to learn and code Java. We invite beginner Java programmers right through to Java professionals to post here and share your knowledge. Become a part of the community, help others, expand your knowledge of Java and enjoy talking with like minded people. Registration is quick and best of all free. We look forward to meeting you.


>> REGISTER NOW TO START POSTING


Members have full access to the forums. Advertisements are removed for registered users.

Results 1 to 6 of 6

Thread: Swing, Hexagons, and odd drawing O MY

  1. #1
    Junior Member
    Join Date
    Apr 2012
    Posts
    4
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default Swing, Hexagons, and odd drawing O MY

    I am attempting to make a Hexagon based board.

    My first attempt was a JPanel that I repeatedly drew a hexagon image on. This had slowness issues when placed in a JScrollPane.

    Searching for a new way I found the code below. This seemed pretty sensible so I started playing around with it.

    However, it turns out that when you click one of the hexagons there is an odd painting effect going on. The paintComponent function looks as though it would only paint the area inside the hex, however when clicking a hex it looks as though it paints a blank "Square" that the control would live in and then paints the hexagon.

    Being new to swing I am unsure even how to track down why it isn't simply painting the hexagon.

    Any assistance would be greatly appreciated.

    package CodeSnips;
     
     
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.FontMetrics;
    import java.awt.Graphics;
    import java.awt.Point;
    import java.awt.Polygon;
    import java.awt.Rectangle;
    import java.awt.event.MouseEvent;
    import javax.swing.Action;
    import javax.swing.Icon;
    import javax.swing.JButton;
    import javax.swing.JComponent;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JToggleButton;
    import javax.swing.SwingUtilities;
     
    /**
     * A six sided toggle button. This is not guaranteed to be a perfect hexagon, it is just guaranteed to have six sides in
     * the form of a hexagon. To be a perfect hexagon the size of this component must have a height to width ratio of
     * 1 to 0.866
     *
     * @author keang
     * @date 5 Jun 2009
     *
     */
    public class HexButton extends JToggleButton
    //public class HexButton extends JComponent
    {
    	private static final long serialVersionUID = 4865976127980106774L;
     
    	private Polygon hexagon = new Polygon();
     
     
    	//public HexButton
     
    	/**
    	 * @param arg0
    	 */
    	public HexButton(String arg0)
    	{
    	    //super(arg0);
    	}
     
     
    	@Override
    	public boolean contains(Point p)
        {
    		return hexagon.contains(p);
        }
     
    	@Override
    	public boolean contains(int x, int y)
        {
    		return hexagon.contains(x, y);
        }
     
    	@Override
    	public void setSize(Dimension d)
        {
    		super.setSize(d);
    		calculateCoords();
        }
     
    	@Override
    	public void setSize(int w, int h)
        {
    		super.setSize(w, h);
    		calculateCoords();
        }
     
    	@Override
    	public void setBounds(int x, int y, int width, int height)
        {
    		super.setBounds(x, y, width, height);
    		calculateCoords();
        }
     
    	@Override
    	public void setBounds(Rectangle r)
        {
    		super.setBounds(r);
    		calculateCoords();
        }
     
    	@Override
    	protected void processMouseEvent(MouseEvent e)
        {
    		if ( contains(e.getPoint()) )
    			super.processMouseEvent(e);
        }
     
    	private void calculateCoords()
        {
    		int w = getWidth()-1;
    		int h = getHeight()-1;
     
    		int ratio = (int)(h*.25);
    		int nPoints = 6;
    		int[] hexX = new int[nPoints];
    		int[] hexY = new int[nPoints];
     
    	    agressiveCoords(w, h, ratio, hexX, hexY);
    		//passiveCoords(w, h, ratio, hexX, hexY);
     
     
    	    hexagon = new Polygon(hexX, hexY, nPoints);
        }
     
     
    	private void agressiveCoords(int w, int h, int ratio, int[] hexX, int[] hexY) {
    		hexX[0] = w/2;
    	    hexY[0] = 0;
     
    	    hexX[1] = w;
    	    hexY[1] = ratio;
     
    	    hexX[2] = w;
    	    hexY[2] = h - ratio;
     
    	    hexX[3] = w/2;
    	    hexY[3] = h;
     
    	    hexX[4] = 0;
    	    hexY[4] = h - ratio;
     
    	    hexX[5] = 0;
    	    hexY[5] = ratio;
    	}
     
    	private void passiveCoords(int w, int h, int ratio, int[] hexX, int[] hexY) {
    		hexY[0] = w/2;
    	    hexX[0] = 0;
     
    	    hexY[1] = w;
    	    hexX[1] = ratio;
     
    	    hexY[2] = w;
    	    hexX[2] = h - ratio;
     
    	    hexY[3] = w/2;
    	    hexX[3] = h;
     
    	    hexY[4] = 0;
    	    hexX[4] = h - ratio;
     
    	    hexY[5] = 0;
    	    hexX[5] = ratio;
    	}
     
    	@Override
    	protected void paintComponent(Graphics g)
        {		
    		if ( isSelected() )
            {
    			g.setColor(Color.lightGray);
            }
    		else
            {
    			g.setColor(getBackground());
            }
     
    	    g.fillPolygon(hexagon);
     
    	    g.setColor(getForeground());
    	    g.drawPolygon(hexagon);
     
    	    FontMetrics fm = getFontMetrics(getFont());
    	    Rectangle viewR = getBounds();
    	    Rectangle iconR = new Rectangle();
    	    Rectangle textR = new Rectangle();
     
     
    	    //SwingUtilities.layoutCompoundLabel(this, fm, getText(), null,
    	    //        SwingUtilities.CENTER, SwingUtilities.CENTER, SwingUtilities.BOTTOM, SwingUtilities.CENTER,
    	    //       viewR, iconR, textR, 0);
     
    	    //Point loc = getLocation();
    	    //g.drawString(getText(), textR.x-loc.x, textR.y-loc.y+fm.getAscent());
        }
     
    	@Override
    	protected void paintBorder(Graphics g)
        {
    		// do not paint a border
        }
     
    	/**
    	 * Application Entry Point
    	 */
    	public static void main(String[] args)
        {
    	    JFrame frame = new JFrame("Hex Button Test");
    	    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
    	    JPanel p = new JPanel();
    	    p.setLayout(new HexLayout(4, 0, 4));
     
    	    for ( int i = 0; i < 24; i++ )
    	        {
    	        HexButton b = new HexButton("B"+(i+1));
    	        b.setForeground(Color.black);
    	        b.setBackground(Color.green);
     
    	        p.add(b);
    	        }
     
    	    frame.add(p);
    	    frame.pack();
    	    frame.setSize(700, 400);
    	    frame.setVisible(true);
        } 
     
    }

    package CodeSnips;
     
    import java.awt.Component;
    import java.awt.Container;
    import java.awt.Dimension;
    import java.awt.Insets;
    import java.awt.LayoutManager;
     
    /**
     * This layout manager is based on java.awt.GridLayout
     *
     * The <code>GridLayout</code> class is a layout manager that
     * lays out a container's components in a hexagonal grid.
     * The container is divided into equal-sized hexagons,
     * and one component is placed in each hexagon.
     *
     *
     *
     *
     * @author keang
     * @date 5 Jun 2009
     *
     */
    public class HexLayout implements LayoutManager, java.io.Serializable
    {
    	private static final long serialVersionUID = -858342723067286796L;
     
    	/**
    	 * This is the gap (in pixels) which specifies the space
    	 * between components.  They can be changed at any time.
    	 * This should be a non-negative integer.
    	 *
    	 * @serial
    	 * @see #getHgap()
    	 * @see #setHgap(int)
    	 */
    	int cgap;
    	/**
    	 * This is the number of rows specified for the grid.  The number
    	 * of rows can be changed at any time.
    	 * This should be a non negative integer, where '0' means
    	 * 'any number' meaning that the number of Rows in that
    	 * dimension depends on the other dimension.
    	 *
    	 * @serial
    	 * @see #getRows()
    	 * @see #setRows(int)
    	 */
    	int rows;
    	/**
    	 * This is the number of columns specified for the grid.  The number
    	 * of columns can be changed at any time.
    	 * This should be a non negative integer, where '0' means
    	 * 'any number' meaning that the number of Columns in that
    	 * dimension depends on the other dimension.
    	 *
    	 * @serial
    	 * @see #getColumns()
    	 * @see #setColumns(int)
    	 */
    	int cols;
     
    	/**
    	 * Creates a grid layout with a default of one column per component,
    	 * in a single row.
    	 * @since JDK1.1
    	 */
    	public HexLayout() {
    		this(1, 0, 0);
    	}
     
    	/**
    	 * Creates a grid layout with the specified number of rows and
    	 * columns. All components in the layout are given equal size.
    	 * <p>
    	 * One, but not both, of <code>rows</code> and <code>cols</code> can
    	 * be zero, which means that any number of objects can be placed in a
    	 * row or in a column.
    	 * @param     r   the rows, with the value zero meaning
    	 *                   any number of rows.
    	 * @param     c   the columns, with the value zero meaning
    	 *                   any number of columns.
    	 */
    	public HexLayout(int r, int c) {
    		this(r, c, 0);
    	}
     
    	/**
    	 * Creates a grid layout with the specified number of rows and
    	 * columns. All components in the layout are given equal size.
    	 * <p>
    	 * In addition, the gap between components is set to the
    	 * specified value.
    	 * <p>
    	 * One, but not both, of <code>rows</code> and <code>cols</code> can
    	 * be zero, which means that any number of objects can be placed in a
    	 * row or in a column.
    	 * <p>
    	 * All <code>GridLayout</code> constructors defer to this one.
    	 * @param     r   the rows, with the value zero meaning
    	 *                   any number of rows
    	 * @param     c   the columns, with the value zero meaning
    	 *                   any number of columns
    	 * @param     hgap   the gap around the component
    	 * @exception   IllegalArgumentException  if the value of both
    	 *          <code>rows</code> and <code>cols</code> is
    	 *          set to zero
    	 */
    	public HexLayout(int r, int c, int hgap) {
    		if ((r == 0) && (c == 0)) {
    			throw new IllegalArgumentException("rows and cols cannot both be zero");
    		}
    		this.rows = r;
    		this.cols = c;
    		this.cgap = hgap;
    	}
     
    	/**
    	 * Gets the number of rows in this layout.
    	 * @return    the number of rows in this layout
    	 */
    	public int getRows() {
    		return rows;
    	}
     
    	/**
    	 * Sets the number of rows in this layout to the specified value.
    	 * @param        r   the number of rows in this layout
    	 * @exception    IllegalArgumentException  if the value of both
    	 *               <code>rows</code> and <code>cols</code> is set to zero
    	 */
    	public void setRows(int r) {
    		if ((r == 0) && (this.cols == 0)) {
    			throw new IllegalArgumentException("rows and cols cannot both be zero");
    		}
    		this.rows = r;
    	}
     
    	/**
    	 * Gets the number of columns in this layout.
    	 * @return     the number of columns in this layout
    	 */
    	public int getColumns() {
    		return cols;
    	}
     
    	/**
    	 * Sets the number of columns in this layout to the specified value.
    	 * Setting the number of columns has no affect on the layout
    	 * if the number of rows specified by a constructor or by
    	 * the <tt>setRows</tt> method is non-zero. In that case, the number
    	 * of columns displayed in the layout is determined by the total
    	 * number of components and the number of rows specified.
    	 * @param        c   the number of columns in this layout
    	 * @exception    IllegalArgumentException  if the value of both
    	 *               <code>rows</code> and <code>cols</code> is set to zero
    	 */
    	public void setColumns(int c) {
    		if ((c == 0) && (this.rows == 0)) {
    			throw new IllegalArgumentException("rows and cols cannot both be zero");
    		}
    		this.cols = c;
    	}
     
    	/**
    	 * Gets the gap between components.
    	 * @return       the gap between components
    	 */
    	public int getGap() {
    		return cgap;
    	}
     
    	/**
    	 * Sets the gap between components to the specified value.
    	 * @param         gap  the gap between components
    	 */
    	public void setGap(int gap) {
    		this.cgap = gap;
    	}
     
    	/**
    	 * Adds the specified component with the specified name to the layout.
    	 * @param name the name of the component
    	 * @param comp the component to be added
    	 */
    	public void addLayoutComponent(String name, Component comp)
    	{
    		// do nothing
    	}
     
    	/**
    	 * Removes the specified component from the layout.
    	 * @param comp the component to be removed
    	 */
    	public void removeLayoutComponent(Component comp)
    	{
    		// do nothing
    	}
     
    	/**
    	 * Determines the preferred size of the container argument using
    	 * this grid layout.
    	 * <p>
    	 * The preferred width of a grid layout is the largest preferred width
    	 * of all of the components in the container times the number of columns,
    	 * plus the horizontal padding times the number of columns minus one,
    	 * plus the left and right insets of the target container, plus the width
    	 * of half a component if there is more than one row.
    	 * <p>
    	 * The preferred height of a grid layout is the largest preferred height
    	 * of all of the components in the container plus three quarters of the
    	 * largest minimum height of all of the components in the container times
    	 * the number of rows greater than one,
    	 * plus the vertical padding times the number of rows minus one, plus
    	 * the top and bottom insets of the target container.
    	 *
    	 * @param     parent   the container in which to do the layout
    	 * @return    the preferred dimensions to lay out the
    	 *                      subcomponents of the specified container
    	 * @see       java.awt.GridLayout#minimumLayoutSize
    	 * @see       java.awt.Container#getPreferredSize()
    	 */
    	public Dimension preferredLayoutSize(Container parent)
    	{
    		synchronized ( parent.getTreeLock() )
    		{
    			Insets insets = parent.getInsets();
    			int ncomponents = parent.getComponentCount();
    			int nrows = rows;
    			int ncols = cols;
     
    			if ( nrows > 0 )
    			{
    				ncols = (ncomponents + nrows - 1) / nrows;
    			}
    			else
    			{
    				nrows = (ncomponents + ncols - 1) / ncols;
    			}
     
    			int w = 0;
    			int h = 0;
    			for ( int i = 0; i < ncomponents; i++ )
    			{
    				Component comp = parent.getComponent(i);
    				Dimension d = comp.getPreferredSize();
    				if ( w < d.width )
    				{
    					w = d.width;
    				}
    				if ( h < d.height )
    				{
    					h = d.height;
    				}
    			}
     
    			int dx = insets.left + insets.right + ncols * w + (ncols - 1) * cgap;
    			int dy = insets.top + insets.bottom + nrows * h + (nrows - 1) * cgap;
     
    			if ( nrows > 1 )
    			{
    				dx = dx + (int)(w * 0.5f);
     
    				dy /= nrows;
    				dy = dy + (int)(dy * (nrows - 1) * 0.75f);
    			}
     
    			return new Dimension(dx, dy);
    		}
    	}
     
    	/**
    	 * Determines the minimum size of the container argument using this
    	 * grid layout.
    	 * <p>
    	 * The minimum width of a grid layout is the largest minimum width
    	 * of all of the components in the container times the number of columns,
    	 * plus the horizontal padding times the number of columns minus one,
    	 * plus the left and right insets of the target container, plus the width
    	 * of half a component if there is more than one row.
    	 * <p>
    	 * The minimum height of a grid layout is the largest minimum height
    	 * of all of the components in the container plus three quarters of the
    	 * largest minimum height of all of the components in the container times
    	 * the number of rows greater than one,
    	 * plus the vertical padding times the number of rows minus one, plus
    	 * the top and bottom insets of the target container.
    	 *
    	 * @param       parent   the container in which to do the layout
    	 * @return      the minimum dimensions needed to lay out the
    	 *                      subcomponents of the specified container
    	 * @see         java.awt.GridLayout#preferredLayoutSize
    	 * @see         java.awt.Container#doLayout
    	 */
    	public Dimension minimumLayoutSize(Container parent)
    	{
    		synchronized ( parent.getTreeLock() )
    		{
    			Insets insets = parent.getInsets();
    			int ncomponents = parent.getComponentCount();
    			int nrows = rows;
    			int ncols = cols;
     
    			if ( nrows > 0 )
    			{
    				ncols = (ncomponents + nrows - 1) / nrows;
    			}
    			else
    			{
    				nrows = (ncomponents + ncols - 1) / ncols;
    			}
    			int w = 0;
    			int h = 0;
    			for ( int i = 0; i < ncomponents; i++ )
    			{
    				Component comp = parent.getComponent(i);
    				Dimension d = comp.getMinimumSize();
    				if ( w < d.width )
    				{
    					w = d.width;
    				}
    				if ( h < d.height )
    				{
    					h = d.height;
    				}
    			}
     
    			int dx = insets.left + insets.right + ncols * w + (ncols - 1) * cgap;
    			int dy = insets.top + insets.bottom + nrows * h + (nrows - 1) * cgap;
     
    			if ( nrows > 1 )
    			{
    				dx = dx + (int)(w * 0.5f);
     
    				dy /= nrows;
    				dy = dy + (int)(dy * (nrows - 1) * 0.75f);
    			}
     
    			return new Dimension(dx, dy);
    		}
    	}
     
    	/**
    	 * Lays out the specified container using this layout.
    	 * <p>
    	 * This method reshapes the components in the specified target
    	 * container in order to satisfy the constraints of the
    	 * <code>GridLayout</code> object.
    	 * <p>
    	 * The grid layout manager determines the size of individual
    	 * components by dividing the free space in the container into
    	 * equal-sized portions according to the number of rows and columns
    	 * in the layout. The container's free space equals the container's
    	 * size minus any insets and any specified horizontal or vertical
    	 * gap. All components in a grid layout are given the same size.
    	 *
    	 * @param      parent   the container in which to do the layout
    	 * @see        java.awt.Container
    	 * @see        java.awt.Container#doLayout
    	 */
    	public void layoutContainer(Container parent)
    	{
    		synchronized (parent.getTreeLock())
    		{
    			Insets insets = parent.getInsets();
    			int ncomponents = parent.getComponentCount();
    			int nrows = rows;
    			int ncols = cols;
     
    			if ( ncomponents == 0 )
    			{
    				return;
    			}
    			if ( nrows > 0 )
    			{
    				ncols = (ncomponents + nrows - 1) / nrows;
    			}
    			else
    			{
    				nrows = (ncomponents + ncols - 1) / ncols;
    			}
     
    			int w = parent.getWidth() - (insets.left + insets.right);
    			int h = parent.getHeight() - (insets.top + insets.bottom);
     
    			w = (int)((w - (ncols - 1) * cgap) / (ncols + (nrows>1?0.5f:0.0f)));
     
    			float effectiveRows = 1 + ((nrows - 1) * 0.75f);
    			h = (int)((h - (nrows - 1) * cgap) / effectiveRows);
     
    			int xoffset = (w+cgap)/2;
    			int yoffset = (int)(h * 0.75f);
    			boolean staggeredRow = false;
     
    			for ( int r = 0, y = insets.top; r < nrows; r++, y += yoffset + cgap )
    			{
    				int offset = 0;
     
    				if ( staggeredRow )
    					offset = xoffset;
     
    				for ( int c = 0, x = insets.left; c < ncols; c++, x += w + cgap )
    				{
    					int i = r * ncols + c;
     
    					if ( i < ncomponents )
    					{
    						parent.getComponent(i).setBounds(x+offset, y, w, h);
    					}
     
    				}
     
    				staggeredRow = !staggeredRow;
    			}
    		}
    	}
     
    	/**
    	 * Returns the string representation of this grid layout's values.
    	 * @return     a string representation of this grid layout
    	 */
    	public String toString() {
    		return getClass().getName() + "[gap=" + cgap +
    		",rows=" + rows + ",cols=" + cols + "]";
    	}
     
    }


  2. #2
    Junior Member
    Join Date
    Apr 2012
    Posts
    4
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default Re: Swing, Hexagons, and odd drawing O MY

    To add a little explanation.

    I have gone though this code extensively.
    I am simply new to Swing and not sure why it does not behave the way it "looks" like it should.

    The gist is:
    There is a Hexagon object and a HexagonLayout.

    The Layout simply sets the hexagons up in a panel so they look like a hexagon map by changing where they think they are. Pretty simple really.

    The Hexagon object really just draws the hexagon. It is a JToggleButton but has its paintComponent overridden.


    I just don't get why clicking the toggle button changes/paints on area that is not within the bounds on the hexagon.

  3. #3
    Super Moderator Norm's Avatar
    Join Date
    May 2010
    Location
    Eastern Florida
    Posts
    25,164
    Thanks
    65
    Thanked 2,725 Times in 2,675 Posts

    Default Re: Swing, Hexagons, and odd drawing O MY

    When I execute the program and click on a green hex, its color is changed to gray. The new color does not go outside of the hex area.

    Can you explain what you think is wrong with the way the code works?
    If you don't understand my answer, don't ignore it, ask a question.

  4. #4
    Junior Member
    Join Date
    Apr 2012
    Posts
    4
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default Re: Swing, Hexagons, and odd drawing O MY

    I have attached an image of what I see when running the code and clicking on the image.

    If that is not what you are seeing ... very odd. I am using Eclipse (not that I would think that would matter)

    What version of java are you compiling against?
    I would hate to think I have spent so long looking at this for it to be an environmental issue.

    OddHex.jpg

  5. #5
    Super Moderator Norm's Avatar
    Join Date
    May 2010
    Location
    Eastern Florida
    Posts
    25,164
    Thanks
    65
    Thanked 2,725 Times in 2,675 Posts

    Default Re: Swing, Hexagons, and odd drawing O MY

    I'm using F:\Java\jdk1.6.0_29\bin\javac.exe for compiles.
    and java version "1.6.0_29" for execution

    Cross posted at
    http://www.java-forums.org/awt-swing...tml#post277684
    Last edited by Norm; April 8th, 2012 at 02:31 PM.
    If you don't understand my answer, don't ignore it, ask a question.

  6. #6
    Junior Member
    Join Date
    Apr 2012
    Posts
    4
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default Re: Swing, Hexagons, and odd drawing O MY

    Easy fix, changed the HexButton to inherit from JComponent and implemented MouseInputListener for the clicks.
    Works like a charm.

    Thanks for the feedback!

Similar Threads

  1. drawing is in the same postion :(
    By kisokiso in forum What's Wrong With My Code?
    Replies: 4
    Last Post: December 20th, 2011, 08:02 AM
  2. Drawing a pentagon
    By Zomosa in forum AWT / Java Swing
    Replies: 7
    Last Post: October 11th, 2011, 10:21 PM
  3. Drawing a selection box using swing
    By edi233 in forum AWT / Java Swing
    Replies: 4
    Last Post: May 9th, 2011, 09:50 AM
  4. [SOLVED] Drawing on JFrame
    By kbarrett1989 in forum AWT / Java Swing
    Replies: 3
    Last Post: October 31st, 2010, 03:41 AM
  5. Drawing
    By toxikbuni in forum Java Theory & Questions
    Replies: 0
    Last Post: April 20th, 2010, 02:43 PM