Good day, trying to draw a grid and bind the cursor to the nodes of this grid. Inherited a class from JPanel, drew a grid on it, processed a mouse, asked the whole case a large size and put it in the JScrollPane for scrolling. And everything seems to be normal, but I noticed that if you move the scrollbar to the right, that is, to move the panel inside the window, then the cursor (my painted cross) stops to stand exactly in the grid nodes. If you move the scrollbars to the zero position (that is, the horizontal one is completely to the left, and the vertical one is fully upward viewport.x = 0 and viewport.y = 0) then everything works fine. Initially, I sinned not taking into account the viewport position, but it seems to be no, I translate the coordinates from window to world and back again in a timely manner, and subtracting the viewport position does not give anything. Tell me, what could be the error, but already broke his head?
public class DrawArea extends JPanel implements MouseListener,
MouseMotionListener,
MouseWheelListener {
private int aDPI = 300; /** ΠΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ ΠΏΠ°Π½Π΅Π»Ρ Π±ΡΠ΄Π΅Ρ ΡΠ°Π·ΠΌΠ΅ΡΠΎΠΌ Ρ Π»ΠΈΡΡ Π4 */ private Dimension aSize = TR_Utils.mmsToPixels(TR_Utils.A4_PAGE_SIZE_MM, aDPI); private Dimension operateAreaSize = new Dimension(aSize); /** ΠΠΎΡΡΡΠΈΡΠΈΠ΅Π½Ρ Π·ΡΠΌΠΈΡΠΎΠ²Π°Π½ΠΈΡ */ private double scaleFactor = 5.0; /** Π Π°Π·ΠΌΠ΅Ρ ΡΠ΅ΡΠΊΠΈ(Π² ΠΏΠΈΠΊΡΠ΅Π»Π°Ρ
), Π΅ΡΠ»ΠΈ Π²ΠΊΠ»ΡΡΠ΅Π½Π° */ private int aGridSize = TR_Utils.mmsToPixels(0.01, aDPI); private int operateGridSize = aGridSize; /** ΠΠΎΠ·ΠΈΡΠΈΡ ΠΊΡΡΡΠΎΡΠ° - ΠΏΠΎΠ·ΠΈΡΠΈΡ ΠΎΡ ΠΊΠΎΡΠΎΡΠΎΠΉ ΡΠΈΡΡΠ΅ΠΌ(Π² Π»ΠΎΠ³ΠΈΡΠ΅ΡΠΊΠΈΡ
, ΠΌΠΈΡΠΎΠ²ΡΡ
ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΠ°Ρ
) */ private Point crossHairPosition = new Point(0, 0); /** ΠΡΠΈΠ²ΡΠ·ΡΠ²Π°ΡΡ Π»ΠΈ ΠΊΡΡΡΠΎΡ ΠΊ ΡΠ΅ΡΠΊΠ΅ */ private boolean snapCursorToGrid = true; /** ΠΠΊΠ»ΡΡΠ°Π΅ΠΌ Π²ΡΠΊΠ»ΡΡΠ°Π΅ΠΌ ΡΠ΅ΡΠΊΡ */ private boolean isGridEnabled = true; /** ΠΡΠ΅ΡΡ ΠΏΠΎ ΡΠ΅Π½ΡΡΡ ΡΠΊΡΠ°Π½Π°, Π΅ΡΠ»ΠΈ Π½Π°Π΄ΠΎ */ private boolean isCrossEnabled = true; /** Π‘Π³Π»Π°ΠΆΠΈΠ²Π°Π½ΠΈΠ΅, Π΅ΡΠ»ΠΈ ΡΡΠ΅Π±ΡΠ΅ΡΡΡ */ private boolean isAnlialiasingEnabled = false; /** ΠΠΊΠ»ΡΡΠ΅Π½ ΡΠ΅ΠΆΠΈΠΌ ΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ(ΡΠΈΡΡΠ΅ΡΡΡ ΠΊΡΡΡΠΎΡ Π΄Π»Ρ * ΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ Π½Π° ΠΏΠ°Π½Π΅Π»ΠΈ, ΠΈΠ½Π°ΡΠ΅ ΠΏΡΠΎΡΡΠΎΠΉ ΡΠΊΠ°Π·Π°ΡΠ΅Π»Ρ) */ private boolean isDrawMode = true; private boolean isShowCrossHair = isDrawMode; /** ΠΠΊΠ»ΡΡΠ΅Π½ Π»ΠΈ ΡΠ΅ΠΆΠΈΠΌ ΠΏΡΠΎΡΠΌΠΎΡΡΠ° Π΄Π»Ρ ΠΏΠ°Π½Π΅Π»ΠΈ. * Π ΡΡΠΎΠΌ ΡΠ΅ΠΆΠΈΠΌΠ΅ Π·Π°ΠΏΡΠ΅ΡΠ΅Π½ΠΎ ΡΠ΅Π΄Π°ΠΊΡΠΈΡΠΎΠ²Π°ΡΡ ΠΈΠ»ΠΈ ΡΡΠΎ-Π»ΠΈΠ±ΠΎ ΡΠΈΡΠΎΠ²Π°ΡΡ Π½Π° ΠΏΠ°Π½Π΅Π»ΠΈ, * ΡΠΎΠ±ΡΡΠΈΡ ΠΌΡΡΠΈ ΠΈ ΠΊΠ»Π°Π²ΠΈΠ°ΡΡΡΡ ΠΈΠ³Π½ΠΎΡΠΈΡΡΡΡΡΡ, ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ Π½Π° ΠΏΠ°Π½Π΅Π»ΠΈ * ΠΏΠ°Π½ΠΎΡΠ°ΠΌΠΈΡΡΠ΅ΡΡΡ(Π΅ΡΠ»ΠΈ Π½Π΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌΠΎ) */ private boolean isViewMode = false; /** ΠΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΠ°, Π½Π° ΠΊΠΎΡΠΎΡΠΎΠΉ Π½Π°ΠΆΠ°Π»ΠΈ ΠΊΠ½ΠΎΠΏΠΊΡ */ private Point mousePressedPos; //GridSize s = new GridSize(15, SizeMeasureUnits.MIL); private AffineTransform at = new AffineTransform(); public DrawArea() { setPreferredSize(operateAreaSize); System.out.println("Size w: " + aSize.width + " h: " + aSize.height); addMouseMotionListener(this); addMouseListener(this); addMouseWheelListener(this); validate(); } @Override public Dimension getPreferredSize() { return new Dimension(operateAreaSize); } public void setCenterInViewport() { Container parent = getParent(); if (parent instanceof JViewport) { JViewport port = (JViewport)parent; Rectangle viewRect = port.getViewRect(); int width = getWidth(); int height = getHeight(); viewRect.x = (width - viewRect.width) / 2 - viewRect.x; viewRect.y = (height - viewRect.height) / 2 - viewRect.y; port.scrollRectToVisible(viewRect); } } /** ΠΠ°Ρ
ΠΎΠ΄ΠΈΡ Π±Π»ΠΈΠΆΠ°ΠΉΡΠΈΠΉ ΡΠ·Π΅Π» ΡΠ΅ΡΠΊΠΈ Π΄Π»Ρ ΡΡΡΠ°Π½ΠΎΠ²ΠΊΠΈ ΠΊΡΡΡΠΎΡΠ°. * curX, curY - ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ ΠΊΡΡΡΠΎΡΠ° Π² ΠΌΠΈΡΠΎΠ²ΡΡ
ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΠ°Ρ
*/ private Point getNearestGridPosition(int curX, int curY) { int cellx = (int)Math.round(curX / operateGridSize); int celly = (int)Math.round(curY / operateGridSize); int x = cellx * operateGridSize; int y = celly * operateGridSize; System.out.println("Grid size: " + aGridSize + " ScaleFactor: " + scaleFactor + " operateGridSize: " + operateGridSize); System.out.println("Cursor pos - x: " + curX + " y: " + curY); System.out.println("Cell num - x: " + cellx + " y: " + celly); System.out.println("CrossHairPos - x: " + x + " y: " + y); System.out.println("----------------------------------------"); return new Point(x, y); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; setBackground(Color.WHITE); /* Prepare draw area */ if(isGridEnabled) { int w = getWidth(); int h = getHeight(); // int gridStep = (int)(gridSize * Math.round(scaleFactor)); // int gridStep = (int) Math.floor(gridSize * scaleFactor); g2d.setColor(new Color(235, 235, 235)); System.out.print("X draw values:"); for(int i = operateGridSize; i < w; i += operateGridSize) { g2d.drawLine(i, 0, i, h); if( i <= w/7) System.out.print(" " + i); } System.out.println(""); for(int j = operateGridSize; j < h; j += operateGridSize) g2d.drawLine(0, j , w, j); } if (isShowCrossHair) { int cs = 12; // ΡΠ°Π·ΠΌΠ΅Ρ ΠΊΡΠ΅ΡΡΠΈΠΊΠ°-ΠΊΡΡΡΠΎΡΠ° Π² ΠΏΠΈΠΊΡΠ΅Π»ΡΡ
g2d.setColor(Color.GRAY); /* ΠΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ ΠΊΡΡΡΠΎΡΠ° Π² ΠΌΠΈΡΠΎΠ²ΡΡ
ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΠ°Ρ
, ΠΏΡΠΈΠ²Π΅Π΄Π΅ΠΌ ΠΈΡ
ΠΊ ΠΎΠΊΠΎΠ½Π½ΡΠΌ */ int cx = (int)crossHairPosition.getX(); int cy = (int)crossHairPosition.getY(); Point p = SwingUtilities.convertPoint(this, new Point(cx, cy), getParent()); g2d.drawLine(px-cs/2, py, p.x+cs/2, py); g2d.drawLine(px, py-cs/2, px, p.y+cs/2); } g2d.dispose(); } /****** MOUSE EVENTS ******/ @Override public void mouseClicked(MouseEvent me) {} @Override public void mousePressed(MouseEvent me) { mousePressedPos = me.getPoint(); System.out.println("mousePressedPos " + mousePressedPos.x + "/" + mousePressedPos.y); if(SwingUtilities.isMiddleMouseButton(me)) { setCursor(new Cursor(Cursor.MOVE_CURSOR)); isShowCrossHair = false; repaint(); } } @Override public void mouseReleased(MouseEvent me) { setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); isShowCrossHair = true; repaint(); } @Override public void mouseEntered(MouseEvent me) {} @Override public void mouseExited(MouseEvent me) {} @Override public void mouseDragged(MouseEvent me) { if(!isViewMode) { if(SwingUtilities.isMiddleMouseButton(me)) { if(mousePressedPos != null) { JViewport port = (JViewport)getParent(); Rectangle viewRect = port.getViewRect(); int deltaX = mousePressedPos.x - me.getX(); int deltaY = mousePressedPos.y - me.getY(); viewRect.x += deltaX; viewRect.y += deltaY; scrollRectToVisible(viewRect); } } } } @Override public void mouseMoved(MouseEvent me) { if(!isViewMode) { int x = me.getX(); int y = me.getY(); // Π² ΠΌΠΈΡΠΎΠ²ΡΠ΅ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ Point p = SwingUtilities.convertPoint(getParent(), new Point(x, y), this); JViewport port = (JViewport)getParent(); Point vp = port.getViewPosition(); System.out.println( "cur_wnd.x: " + x + " cur_wnd.y: " + y + " / cur_world.x: " + px + " cur_world.y: " + py + " / viewpos.x: " + vp.x + " viewpos.y: " + vp.y); if(snapCursorToGrid) { // ΠΡΠΈΠ²ΡΠ·ΡΠ²Π°Π΅ΠΌ ΠΊΡΡΡΠΎΡ ΠΊ ΡΠ·Π»Π°ΠΌ ΡΠ΅ΡΠΊΠΈ Point np = getNearestGridPosition(px, py); crossHairPosition.setLocation(np.x, np.y); } else { crossHairPosition.setLocation(px, py); } repaint(); } } @Override public void mouseWheelMoved(MouseWheelEvent mwe) { if(!isViewMode) { /* ΠΌΠΈΠ½ΡΡ Π΄Π»Ρ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ Π½Π°ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΡ ΠΊΠΎΠ»Π΅ΡΠΈΠΊΠ° ΠΏΡΠΈ ΠΌΠ°ΡΡΠ°Π±ΠΈΡΠΎΠ²Π°Π½ΠΈΠΈ, ΡΠ°ΠΊ ΡΠ΄ΠΎΠ±Π½Π΅Π΅ */ scaleFactor += -(0.05 * mwe.getWheelRotation()); scaleFactor = Math.max(0.05, scaleFactor); scaleFactor = Math.min(scaleFactor, 10); // ΠΏΠ΅ΡΠ΅ΡΡΠΈΡΡΠ²Π°Π΅ΠΌ ΡΠ°Π·ΠΌΠ΅Ρ ΠΎΠ±Π»Π°ΡΡΠΈ ΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ operateAreaSize.setSize((int)(aSize.width * scaleFactor), (int)(aSize.height * scaleFactor)); System.out.println("scaleFactor: " + scaleFactor); // ΠΏΠ΅ΡΠ΅ΡΡΠΈΡΡΠ²Π°Π΅ΠΌ ΡΠ°Π·ΠΌΠ΅Ρ ΡΠ΅ΡΠΊΠΈ operateGridSize = (int)Math.round(aGridSize * scaleFactor); System.out.println("operateGridSize(px): " + operateGridSize); validate(); repaint(); } } }
DrawArea, and you draw in it. The cross stops rising exactly in the grid, because you are doing the division in the coordinates of the viewport, and the grid is shifting. - zRrr