绘制具有给定厚度、位置和半径的环。 (Java2D)

Posted

技术标签:

【中文标题】绘制具有给定厚度、位置和半径的环。 (Java2D)【英文标题】:Draw ring with given thickness, position, and radius. (Java2D) 【发布时间】:2016-06-02 03:40:17 【问题描述】:

我需要画一个给定厚度的环,看起来像这样:

中心必须是透明的,这样它就不会覆盖之前绘制的形状。 (或其他戒指)我尝试过这样的事情:

//g is a Graphics2D object
g.setColor(Color.RED);
g.drawOval(x,y,width,height);
g.setColor(Color.WHITE);
g.drawOval(x+thickness,y+thickness,width-2*thickness,height-2*thickness);

它绘制了一个令人满意的环,但它覆盖了其他形状;内部是白色的,不透明。我怎样才能修改/重写我的代码,使它不会那样做?

【问题讨论】:

【参考方案1】:

您可以从描述外圆的Ellipse2D 和描述内圆的椭圆subtract 创建一个Area。这样,您将获得一个实际的Shape,它可以是drawn 或filled(这只是指环实际覆盖的区域!)。

优点是您确实拥有戒指的几何形状。例如,这使您可以检查环形contains 是否为某个点,或者用不止一种颜色的Paint 填充它:

这里是一个例子,相关部分是createRingShape方法:

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class RingPaintTest

    public static void main(String[] args)
    
        SwingUtilities.invokeLater(new Runnable()
        
            @Override
            public void run()
            
                createAndShowGUI();
            
        );
    

    private static void createAndShowGUI()
    
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        RingPaintTestPanel p = new RingPaintTestPanel();
        f.getContentPane().add(p);
        f.setSize(800,800);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    



class RingPaintTestPanel extends JPanel

    @Override
    protected void paintComponent(Graphics gr)
    
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D)gr;

        g.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);

        g.setColor(Color.RED);
        g.drawString("Text", 100, 100);
        g.drawString("Text", 300, 100);

        Shape ring = createRingShape(100, 100, 80, 20); 
        g.setColor(Color.CYAN);
        g.fill(ring);
        g.setColor(Color.BLACK);
        g.draw(ring);

        Shape otherRing = createRingShape(300, 100, 80, 20); 
        g.setPaint(new GradientPaint(
            new Point(250, 40), Color.RED, 
            new Point(350, 200), Color.GREEN));
        g.fill(otherRing);
        g.setColor(Color.BLACK);
        g.draw(otherRing);

    

    private static Shape createRingShape(
        double centerX, double centerY, double outerRadius, double thickness)
    
        Ellipse2D outer = new Ellipse2D.Double(
            centerX - outerRadius, 
            centerY - outerRadius,
            outerRadius + outerRadius, 
            outerRadius + outerRadius);
        Ellipse2D inner = new Ellipse2D.Double(
            centerX - outerRadius + thickness, 
            centerY - outerRadius + thickness,
            outerRadius + outerRadius - thickness - thickness, 
            outerRadius + outerRadius - thickness - thickness);
        Area area = new Area(outer);
        area.subtract(new Area(inner));
        return area;
    


【讨论】:

(1+) @Marco13,添加渐变示例需要额外的时间:)【参考方案2】:

您可以使用ShapeArea 类来创建有趣的效果:

import java.awt.*;
import javax.swing.*;
import java.awt.geom.*;
import java.net.*;

public class Subtract extends JPanel

    @Override
    protected void paintComponent(Graphics g)
    
        super.paintComponent(g);

        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        int size = 100;
        int thickness = 10;
        int innerSize = size - (2 * thickness);

        Shape outer = new Ellipse2D.Double(0, 0, size, size);
        Shape inner = new Ellipse2D.Double(thickness, thickness, innerSize, innerSize);

        Area circle = new Area( outer );
        circle.subtract( new Area(inner) );

        int x = (getSize().width - size) / 2;
        int y = (getSize().height - size) / 2;
        g2d.translate(x, y);

        g2d.setColor(Color.CYAN);
        g2d.fill(circle);
        g2d.setColor(Color.BLACK);
        g2d.draw(circle);

        g2d.dispose();
     


    private static void createAndShowGUI()
    
        JFrame frame = new JFrame("Subtract");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new Subtract());
        frame.setLocationByPlatform( true );
        frame.setSize(200, 200);
        frame.setVisible( true );
    

    public static void main(String[] args)
    
        EventQueue.invokeLater( () -> createAndShowGUI() );
/*
        EventQueue.invokeLater(new Runnable()
        
            public void run()
            
                createAndShowGUI();
            
        );
*/
    

使用一个区域,您还可以将多个形状添加在一起或获得多个形状的交集。

【讨论】:

所以,再一次,我太慢了... :-/【参考方案3】:

您可以为此使用graphics.setStroke(...)。这样,中心将是完全透明的,因此不会覆盖以前绘制的形状。在我的示例中,由于这种方法,我必须进行一些额外的计算,以确保显示的 xy 坐标实际上与 Ring 实例的坐标相同:


import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.util.ArrayList;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Example 

    public Example() 
        ArrayList<Ring> rings = new ArrayList<Ring>();
        rings.add(new Ring(10, 10, 100, 20, Color.CYAN));

        JPanel panel = new JPanel() 
            @Override
            public void paintComponent(Graphics g) 
                super.paintComponent(g);
                Graphics2D gg = (Graphics2D) g.create();
                gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

                for (Ring ring : rings) 
                    // Previously drawn
                    gg.setColor(Color.BLACK);
                    String str = "Hello!";
                    gg.drawString(str, ring.getX() + (ring.getWidth() - gg.getFontMetrics().stringWidth(str)) / 2,
                            ring.getY() + ring.getHeight() / 2 + gg.getFontMetrics().getAscent());

                    // The actual ring
                    ring.draw(gg);
                

                gg.dispose();
            
        ;

        JFrame frame = new JFrame();
        frame.setContentPane(panel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400, 400);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    

    public static void main(String[] args) 
        EventQueue.invokeLater(new Runnable() 
            @Override
            public void run() 
                new Example();
            
        );
    



class Ring 
    private int x, y, width, height, thickness;
    private Color color;

    public Ring(int x, int y, int width, int height, int thickness, Color color) 
        setX(x);
        setY(y);
        setWidth(width);
        setHeight(height);
        setThickness(thickness);
        setColor(color);
    

    public Ring(int x, int y, int radius, int thickness, Color color) 
        this(x, y, radius * 2, radius * 2, thickness, color);
    

    public void draw(Graphics2D gg) 
        Stroke oldStroke = gg.getStroke();
        Color oldColor = gg.getColor();

        gg.setColor(Color.BLACK);
        gg.setStroke(new BasicStroke(getThickness()));
        gg.drawOval(getX() + getThickness() / 2, getY() + getThickness() / 2, getWidth() - getThickness(),
                getHeight() - getThickness());
        gg.setColor(getColor());
        gg.setStroke(new BasicStroke(getThickness() - 2));
        gg.drawOval(getX() + getThickness() / 2, getY() + getThickness() / 2, getWidth() - getThickness(),
                getHeight() - getThickness());

        gg.setStroke(oldStroke);
        gg.setColor(oldColor);
    

    public int getX() 
        return x;
    

    public void setX(int x) 
        this.x = x;
    

    public int getY() 
        return y;
    

    public void setY(int y) 
        this.y = y;
    

    public int getWidth() 
        return width;
    

    public void setWidth(int width) 
        this.width = width;
    

    public int getHeight() 
        return height;
    

    public void setHeight(int height) 
        this.height = height;
    

    public int getThickness() 
        return thickness;
    

    public void setThickness(int thickness) 
        this.thickness = thickness;
    

    public Color getColor() 
        return color;
    

    public void setColor(Color color) 
        this.color = color;
    


【讨论】:

以上是关于绘制具有给定厚度、位置和半径的环。 (Java2D)的主要内容,如果未能解决你的问题,请参考以下文章

使用java程序查找具有指定半径的给定纬度和经度(即位置)的周围纬度和纬度值

通过点平滑曲线,仅使用水平、垂直线和固定半径弧

Java2D:如何将图像放置在具有正确旋转的中心周围

如何在 Chart.js 2.9.3 中绘制具有动态厚度的水平条(在浏览器窗口高度调整大小时自动调整大小)?

地球经纬度到 3D 球体上的纬度和经度

python 在给定半径的x,y中心绘制给定数量的等间隔圆周坐标