powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Java [игнор отключен] [закрыт для гостей] / Java2D Не получается сделать привязку курсора к узлам сетки
11 сообщений из 11, страница 1 из 1
Java2D Не получается сделать привязку курсора к узлам сетки
    #39286388
I dont know
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Доброго времени суток. Есть JPanel, расчертил на ней сетку, нужно чтобы при движении мышки, курсор(нарисованный мной крестик) перемещался только по узлам этой сетки. Попробовал реализовать(код ниже). Вроде как всё работает, но немного не так как надо, курсор не совсем точно встаёт в узлы сетки - как-то неправильно высчитываются координаты узлов, причём, если поставить курсор у левой стороны окна(где координата Х меньше), то всё нормально, но чем больше вправо сдвигаюсь по Х, тем сильнее "убегает" курсор, тоже самое и по оси Y.
Пояснение на скриншотах. Не пойму в чём причина, грешу на округление, либо как-то не так считаю помогите разобраться (

Немного картинок для пояснения:
Картинка когда всё ровно


Теперь когда сдвигаемся в противоположный край окна - курсор убегает от узла сетки, причём, чем больше сдвиг - тем сильнее "убегание"


тоже самое убегание по Y:



Код панели

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274.
275.
276.
277.
278.
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289.
290.
291.
292.
293.
294.
295.
296.
297.
298.
299.
300.
301.
302.
303.
304.
305.
306.
307.
308.
309.
310.
311.
312.
313.
314.
315.
316.
317.
318.
319.
320.
321.
322.
323.
324.
325.
326.
327.
328.
329.
330.
331.
332.
333.
334.
335.
336.
337.
338.
339.
340.
341.
342.
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package testapp;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import javax.swing.JPanel;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;

import testapp.TR_Utils;


/**
 *
 * @author OlifiroKA
 */

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 double  scaleFactor           = 1.0;
    
    /** Размер сетки(во внутренник единицах измерения), если включена */
    private int     gridSize              = 40;
    /** Позиция курсора - позиция от которой рисуем(в логических, мировых координатах) */
    private Point2D crossHairPosition     = new Point2D.Double();
    /** Привязывать ли курсор к сетке */
    private boolean snapCursorToGrid      = true;
    /** Включаем выключаем сетку */
    private boolean isGridEnabled         = true;
    /** Крест по центру экрана, если надо */
    private boolean isCrossEnabled        = true;
    /** Сглаживание, если требуется */
    private boolean isAnlialiasingEnabled = false;
    
    /** Включен режим рисования(рисуется курсор для 
     * рисования на панели, иначе простой указатель) */
    private boolean isDrawMode            = true;
    
    /** Включен ли режим просмотра для панели.
     * В этом режиме запрещено редактировать или что-либо рисовать на панели,
     * события мыши и клавиатуры игнорируются, изображение на панели 
     * панорамируется(если необходимо) */
    private boolean isViewMode            = false;
    
    /** Координата, на которой нажали кнопку */
    private Point mousePressedPos;
    
    //GridSize s = new GridSize(15, SizeMeasureUnits.MIL);
    
    private AffineTransform at = new AffineTransform();
    
    public DrawArea() {
        
        setPreferredSize(aSize);
        
        System.out.println("Size w: " + aSize.width + " h: " + aSize.height);
        
        addMouseMotionListener(this);
        addMouseListener(this);
        addMouseWheelListener(this);    
    }
    
    @Override
    public Dimension getPreferredSize() {
        return new Dimension((int)(aSize.width*scaleFactor), 
                             (int)(aSize.height*scaleFactor));
    }
    
    /** Переводит координаты окна в координаты панели(мировые координаты) */
    private Point TranslateW2WCoordinates(int winX, int winY) {
        Container parent = getParent();
        Point point = null;
        if(parent != null)
            point = SwingUtilities.convertPoint(parent, new Point(winX, winY), this);
        
        return point;
    }
    
    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) {
        
        double scaledGridSize = gridSize * scaleFactor; 
               
        int cellx = (int)Math.round(curX / scaledGridSize);
        int celly = (int)Math.round(curY / scaledGridSize);
        
        int x = (int)Math.round(cellx * scaledGridSize);
        int y = (int)Math.round(celly * scaledGridSize);
        
        System.out.println("Grid size: " + gridSize + " ScaleFactor: " + scaleFactor + " Scaled grid size: " + scaledGridSize);
        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);
               
        if(isAnlialiasingEnabled) {
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                                    RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
                                    RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
        }
        
        /* Prepare draw area */
        if(isGridEnabled) {
            int w = getWidth();
            int h = getHeight();
            int gridStep = (int)Math.round(gridSize * scaleFactor);
            
            g2d.setColor(new Color(235, 235, 235));
            for(int i = 0; i < w; i += gridStep) 
                g2d.drawLine(i, 0, i, h);
            
            for(int j = 0; j < h; j += gridStep) 
                g2d.drawLine(0, j , w, j);
        }
        
        if(isCrossEnabled) {
            g2d.setColor(Color.GRAY);
            float[] dash = {2f, 0f, 2f};
            Stroke oldStroke = g2d.getStroke();
            BasicStroke bs = new BasicStroke(1, BasicStroke.CAP_BUTT, 
                                            BasicStroke.JOIN_ROUND, 1.0f, dash, 2f);
            g2d.setStroke(bs);
            g2d.drawLine(getWidth()/2, 0, getWidth()/2, getHeight());
            g2d.drawLine(0, getHeight()/2, getWidth(), getHeight()/2);
            g2d.setStroke(oldStroke);
        }
        
        if (isDrawMode) {
            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(p.x-cs/2, p.y, p.x+cs/2, p.y);
            g2d.drawLine(p.x, p.y-cs/2, p.x, p.y+cs/2);
        }
      
        /* Отрисовываем всё остальное(рисуем модель) */  
        /*
        g2d.setColor(Color.WHITE);
        g2d.fillRect(100, 100, 80, 160);
        g2d.setColor(Color.BLACK);
        g2d.drawRect(100, 100, 80, 160); */
        
        
        g2d.dispose();
        
    }
    
    public void setDPI(int dpi) {
        aDPI = dpi;
        repaint();
    }
    
    public int getDPI() {
        return aDPI;
    }
    
    public void setGridSize(int sizePx) {
        if(sizePx <= 0)
            sizePx = 1;
        
        gridSize = sizePx;
        repaint();
    }
    
    public void setGridEnabled(boolean enabled) {
        isGridEnabled = enabled;
        repaint();
    }
    
    public void setCrossEnabled(boolean enabled) {
        isCrossEnabled = enabled;
        repaint();
    }
    
    public void setAntialiasingEnabled(boolean enabled) {
        isAnlialiasingEnabled = enabled;
        repaint();
    }
    
    public void setViewModeEnabled(boolean enabled) {
        isViewMode = enabled;
        repaint();
    }
    
    /** Включает режим просмотра и панорамирует всё что нарисовано
     * (чтобы оно влезло в вид) */
    public void setViewModeEnabled(boolean enabled, boolean panning) {
        isViewMode = enabled;
        
        /* Если включено панорамирование, перерисуем всё что есть 
         * предварительно отмасштабировав под размеры viewport-а */
        
        //...
        
        repaint();
    }

    /****** MOUSE EVENTS  ******/
    
    @Override
    public void mouseClicked(MouseEvent me) {}

    @Override
    public void mousePressed(MouseEvent me) {
        mousePressedPos = new Point(me.getPoint());
        System.out.println("mousePressedPos " + mousePressedPos.x + "/" + mousePressedPos.y);
    }

    @Override
    public void mouseReleased(MouseEvent me) {
        
    }

    @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( "view.x: " + x + " view.y: " + y + 
            //                    " / area.x: " + p.x + " area.y: " + p.y + 
            //                    " / vpos.x: " + vp.x + " vpos.y: " + vp.y);
            
            if(snapCursorToGrid) {
                // Привязываем курсор к узлам сетки
                Point np = getNearestGridPosition(p.x, p.y);
                crossHairPosition.setLocation(np.x, np.y);
            }
            else {
                crossHairPosition.setLocation(p.x, p.y);
            }
            repaint();
        }
    }

    @Override
    public void mouseWheelMoved(MouseWheelEvent mwe) {
        if(!isViewMode) {
            /* минус для изменения направления движения колесика при маштабировании, так удобнее */
            scaleFactor += -(0.035 * mwe.getWheelRotation());
            scaleFactor = Math.max(0.05, scaleFactor);
            scaleFactor = Math.min(scaleFactor, 4);
            revalidate();
            repaint();
        }
    }
}


Используется NetBeans 8.1, архив с проектом во вложении.
...
Рейтинг: 0 / 0
Java2D Не получается сделать привязку курсора к узлам сетки
    #39286395
I dont know
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
К сожалению не получилось загрузить архив через вложение(), поэтому через файлообменник, заранее прошу прощения
http://файлообменник.рф/3olcdx9t1lqp.html
...
Рейтинг: 0 / 0
Java2D Не получается сделать привязку курсора к узлам сетки
    #39286439
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
не нужно столько кода.
Делаешь демку на плипанеи курсора на координаты 0,0
- на MouseMove показ дистанции до 0,0
- при дистанции <10 пикселей установить через API операционки координаты мышки в 0,0
Всё.

Как заработает - сделать уже с сеткой и в ООП.
...
Рейтинг: 0 / 0
Java2D Не получается сделать привязку курсора к узлам сетки
    #39286490
I dont know
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Petro123,

Не совсем понял мысль... Зачем мне API операционки и находить дистанцию до 0,0 ?
...
Рейтинг: 0 / 0
Java2D Не получается сделать привязку курсора к узлам сетки
    #39286494
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
I dont knowPetro123,
Не совсем понял мысль... Зачем мне API операционки и находить дистанцию до 0,0 ?
Ты не в курсе зачем нужен Hello World?
https://ru.wikipedia.org/wiki/Hello,_world!
...
Рейтинг: 0 / 0
Java2D Не получается сделать привязку курсора к узлам сетки
    #39286498
I dont know
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Petro123I dont knowPetro123,
Не совсем понял мысль... Зачем мне API операционки и находить дистанцию до 0,0 ?
Ты не в курсе зачем нужен Hello World?
https://ru.wikipedia.org/wiki/Hello,_world!
В курсе, как это соотносится с моим вопросом?
...
Рейтинг: 0 / 0
Java2D Не получается сделать привязку курсора к узлам сетки
    #39286510
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
I dont knowВ курсе, как это соотносится с моим вопросом?
да.
Хочу чтобы ты сам разобрался в ерундовом вопросе.
...
Рейтинг: 0 / 0
Java2D Не получается сделать привязку курсора к узлам сетки
    #39286518
I dont know
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Petro123,
Я собственно и создал этот пост, т.к не смог разобраться в "ерундовом" вопросе, в надежде что мне подскажут, возможно я что-то упустил.
...
Рейтинг: 0 / 0
Java2D Не получается сделать привязку курсора к узлам сетки
    #39286538
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
I dont know,
Один из методов поиска я привел выше.
Это метод упрощения кода.
Вместо сетки взять точку и делать снаппинг к точке.
Можешь и ждать пока кто загрузит твой код. Мне без разницы.
...
Рейтинг: 0 / 0
Java2D Не получается сделать привязку курсора к узлам сетки
    #39286556
I dont know
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Petro123,

Понятно. Упрощённый код, выкинул всё лишнее, может кому так проще будет:

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
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 double  scaleFactor           = 1.0;
    
    /** Размер сетки(во внутренник единицах измерения), если включена */
    private int     gridSize              = 40;
    /** Позиция курсора - позиция от которой рисуем(в логических, мировых координатах) */
    private Point2D crossHairPosition     = new Point2D.Double();

    
    public DrawArea() {
        setPreferredSize(aSize);
        addMouseMotionListener(this);
        addMouseListener(this);
        addMouseWheelListener(this);    
    }
    
    @Override
    public Dimension getPreferredSize() {
        return new Dimension((int)(aSize.width*scaleFactor), 
                             (int)(aSize.height*scaleFactor));
    }
    

    /** Находит ближайший узел сетки для установки курсора.
      * curX, curY - координаты курсора в мировых координатах */
    private Point getNearestGridPosition(int curX, int curY) {
        
        double scaledGridSize = gridSize * scaleFactor; 
               
        int cellx = (int)Math.round(curX / scaledGridSize);
        int celly = (int)Math.round(curY / scaledGridSize);
        
        int x = (int)Math.round(cellx * scaledGridSize);
        int y = (int)Math.round(celly * scaledGridSize);
        
        return new Point(x, y);
    }
    
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); 
        Graphics2D g2d = (Graphics2D) g;
        
        setBackground(Color.WHITE);
               
        // . . .

        /* Расчерчиваем сетку на панели */
            int w = getWidth();
            int h = getHeight();
            int gridStep = (int)Math.round(gridSize * scaleFactor);
            
            g2d.setColor(new Color(235, 235, 235));
            for(int i = 0; i < w; i += gridStep) 
                g2d.drawLine(i, 0, i, h);
            
            for(int j = 0; j < h; j += gridStep) 
                g2d.drawLine(0, j , w, j);

           // вместо линий можно рисовать точки, сути это не изменит
        
        
       /* рисуем указатель, который прыгает по узлам сетки при движении мышкой */
            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(p.x-cs/2, p.y, p.x+cs/2, p.y);
            g2d.drawLine(p.x, p.y-cs/2, p.x, p.y+cs/2);
        
      
        // . . .
        
        g2d.dispose();
        
    }
   

    @Override
    public void mouseMoved(MouseEvent me) {
       
            int x = me.getX();
            int y = me.getY();
            
            // в мировые координаты
            Point p = SwingUtilities.convertPoint(getParent(), new Point(x, y), this);
            
            if(snapCursorToGrid) {
                // Привязываем курсор к узлам сетки
                Point np = getNearestGridPosition(p.x, p.y);
                crossHairPosition.setLocation(np.x, np.y);
            }
            else {
                crossHairPosition.setLocation(p.x, p.y);
            }
            repaint();
        }
}

...
Рейтинг: 0 / 0
Java2D Не получается сделать привязку курсора к узлам сетки
    #39286566
I dont know
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Решено, проблема была в округлении координаты курсора и размеров сетки, при обрезании до int-a всё работает как надо.
...
Рейтинг: 0 / 0
11 сообщений из 11, страница 1 из 1
Форумы / Java [игнор отключен] [закрыт для гостей] / Java2D Не получается сделать привязку курсора к узлам сетки
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]