SWT GC填充圆的上半部分和下半部分的颜色

问题描述 投票:0回答:1

我正在寻找一个圆,用一条线将圆的上半部分和下半部分分开,然后使用GC填充上半部分和下半部分的颜色。

如果线像这样越过圆心,我就能做到这一点(要旋转线,我只需更改fillArc()的startAngle:]

enter image description here

但是如果线垂直上下移动和/或旋转,则无法像这样填充上半部和下半部:

enter image description here

如果线向上或向下移动和/或旋转,是否有人知道如何填充上半部和下半部?

这是我的第一张图片的代码:

// Fill top half with red color
gc.setBackground( event.display.getSystemColor( SWT.COLOR_RED ) );
gc.fillArc( xCoord - ( diameter / 2 ),
            yCoord - ( diameter / 2 ),
            diameter,
            diameter,
            0,
            180 );

// Fill bottom half with blue color
gc.setBackground( event.display.getSystemColor( SWT.COLOR_BLUE ) );
gc.fillArc( xCoord - ( diameter/ 2 ),
            yCoord - ( diameter/ 2 ),
            diameter,
            diameter,
            180,
            180 );

// Draw the line separating top half and bottom half
Transform transform = new Transform( event.display );
transform.translate( xCoord, yCoord );
transform .rotate( 0);
gc.setTransform( transform );
gc.drawLine( -diameter / 2, 0, diameter / 2, 0 );
transform.dispose();

java swt
1个回答
0
投票

一种可能的方法是:

  1. 绘制没有剪切的完整蓝色圆圈
  2. 绘制一个完整的红色圆圈,该红色圆圈被计算的(可能是旋转的)矩形裁剪,它将覆盖部分蓝色圆圈
  3. 绘制圆圈轮廓
  4. 使用上一个剪切矩形的适当线段并用圆圈轮廓剪切的方式绘制分隔线

我创建了一个实现该解决方案的类和一个用于对其进行测试的小程序。

对于第2点,我发现使用Tranform进行旋转非常有问题,因为它将转换整个显示,并且我无法找到一种方法来将转换限制在有限的区域内。相反,我已经使用Eclipse GEF创建了Rectangle,将其旋转并将其转换为可以剪切的PathData

对于点4,我重用了点2的PathData来绘制剪切矩形的底部,该矩形相当于两种颜色之间的分隔线。为避免在圆之外绘制线段的一部分,我用圆轮廓对其进行了裁剪。

这是结果:

SeparatedCircleTest

这是测试程序,使用箭头键移动/旋转分隔线:

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

import static org.eclipse.swt.events.KeyListener.keyPressedAdapter;

public class SeparatedCircleTest {

    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setSize(600, 600);
        shell.setLayout(new FillLayout());

        // double buffering to avoid flickering while redrawing the circle
        final SeparatedCircle separatedCircle = new SeparatedCircle(shell, SWT.DOUBLE_BUFFERED, 300, 300, 200, 0, 0.f);

        // to move/rotate the separation
        separatedCircle.addKeyListener(keyPressedAdapter(e -> {
            if(e.keyCode == SWT.ARROW_UP) {
                separatedCircle.setySeparationDelta(separatedCircle.getySeparationDelta() - 5);
            } else if(e.keyCode == SWT.ARROW_DOWN) {
                separatedCircle.setySeparationDelta(separatedCircle.getySeparationDelta() + 5);
            } else if(e.keyCode == SWT.ARROW_LEFT) {
                separatedCircle.setSeparationAngle(separatedCircle.getSeparationAngle() + 5.f);
            } else if(e.keyCode == SWT.ARROW_RIGHT) {
                separatedCircle.setSeparationAngle(separatedCircle.getSeparationAngle() - 5.f);
            }

            if(separatedCircle.needRedraw()) {
                separatedCircle.redraw();
            }
        }));

        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) display.sleep();
        }
        display.dispose();
    }
}

这是实现类:

import org.eclipse.gef.geometry.convert.swt.Geometry2SWT;
import org.eclipse.gef.geometry.euclidean.Angle;
import org.eclipse.gef.geometry.planar.Polygon;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Path;
import org.eclipse.swt.graphics.PathData;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;

public class SeparatedCircle extends Canvas {

    private int xCoord;
    private int yCoord;
    private int diameter;
    private int ySeparationDelta;
    private float separationAngle;

    private boolean needRedraw;
    private Rectangle circleBounds;
    private PathData clippingData;

    public SeparatedCircle(Composite parent, int style, int x, int y, int diameter, int ySeparationDelta, float separationAngle) {
        super(parent, style);

        xCoord = x;
        yCoord = y;
        this.diameter = diameter;
        this.ySeparationDelta = ySeparationDelta;
        this.separationAngle = separationAngle;

        needRedraw = true;

        addPaintListener(new PaintListener() {
            public void paintControl(PaintEvent e) {
                paint(e);
            }
        });
    }

    private void paint(PaintEvent event) {

        // if some variable changed, we recalculate the bounds
        if(needRedraw) {
            calculateBounds();
            needRedraw = false;
        }

        GC gc = event.gc;

        // enable high quality drawing
        gc.setAntialias(SWT.ON);
        gc.setInterpolation(SWT.HIGH);

        // draw the first circle, no clipping
        gc.setBackground( event.display.getSystemColor( SWT.COLOR_BLUE ) );
        gc.fillOval(circleBounds.x, circleBounds.y, circleBounds.width, circleBounds.height);

        // clipping for the second circle
        Path clipping = new Path(gc.getDevice(), clippingData);
        gc.setClipping(clipping);
        clipping.dispose();

        // draw the second circle
        gc.setBackground( event.display.getSystemColor( SWT.COLOR_RED ) );
        gc.fillOval(circleBounds.x, circleBounds.y, circleBounds.width, circleBounds.height);

        // remove the clipping
        gc.setClipping((Rectangle) null);

        // draw the circle outline
        gc.setForeground(event.display.getSystemColor( SWT.COLOR_BLACK ));
        gc.setLineWidth(4);
        gc.drawOval(circleBounds.x, circleBounds.y, circleBounds.width, circleBounds.height);

        // clipping for the separation line
        Path circlePath = new Path(gc.getDevice());
        circlePath.addArc(circleBounds.x, circleBounds.y, circleBounds.width, circleBounds.height, 0.f, 360.f);
        gc.setClipping(circlePath);
        circlePath.dispose();

        // draw the separation line
        // we want to draw the bottom segment of the clipping rectangle (the third segment), so we use its third and fourth point
        gc.drawLine(
                (int) clippingData.points[4], // third point x
                (int) clippingData.points[5], // third point y
                (int) clippingData.points[6], // fourth point x
                (int) clippingData.points[7]  // fourth point y
        );
    }

    private void calculateBounds() {
        circleBounds = calculateCircleBounds();
        clippingData = calculateClipping();
    }

    private Rectangle calculateCircleBounds() {
        return new Rectangle(calculateLeft(), calculateTop(), diameter, diameter);
    }

    private int calculateLeft() {
        return xCoord - ( diameter / 2 );
    }

    private int calculateTop() {
        return yCoord - ( diameter / 2 );
    }

    private PathData calculateClipping() {

        // create the clipping rectangle
        org.eclipse.gef.geometry.planar.Rectangle rectangle = new org.eclipse.gef.geometry.planar.Rectangle(
                circleBounds.x, circleBounds.y, circleBounds.width, calculateClippingRectangleHeight());

        // rotate it, using the center of our circle as its point of rotation
        Polygon rotatedRectangle = rectangle.getRotatedCCW(Angle.fromDeg(separationAngle), xCoord, yCoord);

        // convert the rotated rectangle to PathData
        return Geometry2SWT.toSWTPathData(rotatedRectangle.toPath());
    }

    private int calculateClippingRectangleHeight() {
        return circleBounds.height / 2 + ySeparationDelta;
    }

    public int getxCoord() {
        return xCoord;
    }

    public void setxCoord(int xCoord) {
        this.xCoord = xCoord;
        needRedraw = true;
    }

    public int getyCoord() {
        return yCoord;
    }

    public void setyCoord(int yCoord) {
        this.yCoord = yCoord;
        needRedraw = true;
    }

    public int getDiameter() {
        return diameter;
    }

    public void setDiameter(int diameter) {
        this.diameter = diameter;
        needRedraw = true;
    }

    public int getySeparationDelta() {
        return ySeparationDelta;
    }

    public void setySeparationDelta(int ySeparationDelta) {
        this.ySeparationDelta = ySeparationDelta;
        needRedraw = true;
    }

    public float getSeparationAngle() {
        return separationAngle;
    }

    public void setSeparationAngle(float separationAngle) {
        this.separationAngle = separationAngle;
        needRedraw = true;
    }

    public boolean needRedraw() {
        return needRedraw;
    }

}

要使用GEF:

要使用GEF,您只需要包括以下jar:

org.eclipse.gef.geometry.convert.swt.Geometry2SWT<version>.jar
org.eclipse.gef.geometry<version>.jar

您可以从以下版本的https://www.eclipse.org/gef/downloads/index.php中的“插件”文件夹中检索它们:

选择最新版本,然后单击更新站点链接以下载完整的zip。

© www.soinside.com 2019 - 2024. All rights reserved.