LINUX.ORG.RU

Избранные сообщения xterro

Java2D Не получается сделать привязку курсора к узлам сетки

Форум — Development

Доброго времени суток. Есть JPanel, расчертил на ней сетку, нужно чтобы при движении мышки, курсор(не сам указатель мышки, а просто нарисованный мной крестик) перемещался только по узлам этой сетки. Попробовал реализовать(код ниже). Вроде как всё работает, но немного не так как надо, курсор не совсем точно встаёт в узлы сетки - как-то неправильно высчитываются координаты узлов, причём, если поставить курсор у левой стороны окна(где координата Х меньше), то всё нормально, но чем больше вправо сдвигаюсь по Х, тем сильнее «убегает» курсор, тоже самое и по оси Y. Пояснение на скриншотах. Не пойму в чём причина, грешу на округление, либо как-то не так считаю помогите разобраться (

Немного картинок, чтобы было понятней: Когда всё ровно - http://savepic.ru/10767351.png

Теперь когда сдвигаемся в противоположный край окна - курсор убегает от узла сетки, причём, чем больше сдвиг - тем сильнее «убегание» - http://savepic.ru/10754039.png

тоже самое убегание по Y - http://savepic.ru/10803190.png

Код класса панели, на которой рисую

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

P.S. Пытался убрать код под кат, не получилось (

 

xterro
()