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(); } }

}

  • one
    in my opinion, these conversions are not needed, in mouse events the position of the cursor comes in the coordinate system of your 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
  • In the events, the coordinates will come to the DrawArea box (ie, they will coincide with the coordinates on the DrawArea), only if I do not move the scrollbar, if I shift it, then I need to add another viewport shift to the mouse coordinate, and for this I use coordinate conversion. To the getNearestGridPosition function β€” which calculates the closest grid node β€” I transfer the coordinates by first transferring them to the DrawArea system (that is, to world coordinates), and this is where I get embarrassed. It seems that everything is as it should, but I do not understand why the shift occurs ( - xterro

0