2011-02-16 26 views
8

Tôi chưa bao giờ làm việc với các phương thức vẽ Java trước đây, vì vậy tôi quyết định đi sâu vào và tạo một đồng hồ tương tự như một PoC. Ngoài các bàn tay, tôi vẽ một mặt đồng hồ bao gồm dấu tick cho phút/giờ. Tôi sử dụng phép tính sin/cos đơn giản để xác định vị trí của các đường quanh vòng tròn.Java - Độ chính xác của đường con subpixel có yêu cầu một AffineTransform không?

Tuy nhiên, tôi đã nhận thấy rằng vì các dấu tick phút rất ngắn, góc của các đường trông có vẻ sai. Tôi chắc chắn điều này là do cả hai phương pháp Graphics2D.drawLine()Line2D.double() không thể vẽ bằng độ chính xác của pixel phụ.

Tôi biết tôi có thể vẽ các đường có nguồn gốc từ trung tâm và che nó ra với một vòng tròn (để tạo các đường dài hơn, chính xác hơn), nhưng điều đó có vẻ như một giải pháp không phù hợp và tốn kém. Tôi đã thực hiện một số nghiên cứu về cách thực hiện điều này, nhưng câu trả lời hay nhất mà tôi đã gặp phải là sử dụng AffineTransform. Tôi cho rằng tôi có thể sử dụng AffineTransform chỉ với xoay vòng, trái ngược với việc phải thực hiện thay thế siêu âm.

Đây có phải là phương pháp vẽ duy nhất/tốt nhất có độ chính xác dưới pixel không? Hoặc là có một giải pháp có khả năng nhanh hơn?

Chỉnh sửa: Tôi đã đặt RenderingHint thành đối tượng Graphics2D.

Theo yêu cầu, đây là một chút của mã (không được tối ưu hóa đầy đủ như thế này chỉ là một PoC):

diameter = Math.max(Math.min(pnlOuter.getSize().getWidth(), 
          pnlOuter.getSize().getHeight()) - 2, MIN_DIAMETER); 

for (double radTick = 0d; radTick < 360d; radTick += 6d) { 
    g2d.draw(new Line2D.Double(
     (diameter/2) + (Math.cos(Math.toRadians(radTick))) * diameter/2.1d, 
     (diameter/2) + (Math.sin(Math.toRadians(radTick))) * diameter/2.1d, 
     (diameter/2) + (Math.cos(Math.toRadians(radTick))) * diameter/2.05d, 
     (diameter/2) + (Math.sin(Math.toRadians(radTick))) * diameter/2.05d)); 
} // End for(radTick) 

Dưới đây là một ảnh chụp màn hình của bản vẽ. Nó có thể hơi khó nhìn thấy, nhưng nhìn vào dấu tích trong 59 phút. Nó là hoàn toàn thẳng đứng.

Sample image

+0

sc reenshot của đồng hồ và mã để tính toán/vẽ đồng hồ sẽ giúp đỡ. –

Trả lời

8

Line2D.double() phương pháp không thể vẽ với độ chính xác subpixel.

Sai, sử dụng RenderingHints.VALUE_STROKE_PURE đối tượng Graphics2D có thể vẽ độ chính xác "subpixel" với hình dạng Line2D.


Tôi giả sử tôi có thể sử dụng một AffineTransform với chỉ luân chuyển, như trái ngược với phải thực hiện một SuperSampling . Đây có phải là phương pháp vẽ duy nhất/tốt nhất có độ chính xác phụ pixel không? Hoặc có giải pháp nhanh hơn có khả năng là không?

Tôi nghĩ bạn đang thiếu một số thứ ở đây. Đối tượng Graphics2D đã giữ một AffineTransform và nó đang sử dụng nó cho tất cả các hành động vẽ và hiệu suất giá rẻ của nó khôn ngoan.


Nhưng để lấy lại cho bạn những gì là mất tích từ mã của bạn - điều này là mất tích:

g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, 
        RenderingHints.VALUE_STROKE_PURE); 

Dưới đây là một khép kín ví dụ mà tạo ra hình ảnh này:

screenshot

public static void main(String[] args) throws Exception { 

    final JFrame frame = new JFrame("Test"); 

    frame.add(new JComponent() { 
     @Override 
     protected void paintComponent(Graphics g) { 
      super.paintComponent(g); 

      Graphics2D g2d = (Graphics2D) g; 

      System.out.println(g2d.getTransform()); 
      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
           RenderingHints.VALUE_ANTIALIAS_ON); 
      g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, 
           RenderingHints.VALUE_STROKE_PURE); 

      double dia = Math.min(getWidth(), getHeight()) - 2; 

      for (int i = 0; i < 60 ; i++) { 
       double angle = 2 * Math.PI * i/60; 
       g2d.draw(new Line2D.Double(
         (dia/2) + Math.cos(angle) * dia/2.1d, 
         (dia/2) + Math.sin(angle) * dia/2.1d, 
         (dia/2) + Math.cos(angle) * dia/2.05d, 
         (dia/2) + Math.sin(angle) * dia/2.05d)); 
      } 

      g2d.draw(new Ellipse2D.Double(1, 1, dia - 1, dia - 1)); 
     } 
    }); 

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    frame.setSize(400, 400); 
    frame.setVisible(true); 
} 
+0

Hmm, có vẻ như bạn đang sử dụng 'AffineTransform' * có * phải dịch (ví dụ: supersample) cũng như xoay. Tôi hy vọng để tránh điều này, vì tôi hiện đang có cập nhật này mỗi 50ms. –

+0

Bạn không nên vẽ mặt đồng hồ một cách riêng biệt và sau đó hợp nhất với mặt đồng hồ thay đổi? –

+0

@ D.N .: Tôi chỉ tạo hình ảnh ngoại tuyến * một lần *? Bạn có cần tạo mỗi bản cập nhật không? (@ typo.pl: yes) – dacwe

Các vấn đề liên quan