java - SWT GC 为圆的上半部分和下半部分填充颜色
问题描述
我正在寻找一个圆圈,用一条线分隔圆的上半部分和下半部分,然后使用 GC 填充上半部分和下半部分的颜色。
如果线像这样穿过圆心,我可以做到这一点(要旋转线,我可以简单地更改 fillArc() 的 startAngle):
但是,如果线垂直向上或向下移动和/或旋转,我无法像这样填充上半部分和下半部分:
如果线条向上或向下移动和/或旋转,有谁知道如何填充上半部分和下半部分?
这是我的第一张图片的代码:
// 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();
解决方案
一种可能的方法是:
- 绘制没有剪裁的完整蓝色圆圈
- 绘制由计算(并且可能旋转)的矩形裁剪的完整红色圆圈,该矩形将覆盖蓝色圆圈的一部分
- 绘制圆形轮廓
- 使用前一个剪切矩形的适当部分绘制分隔线并由圆形轮廓剪切
我创建了一个实现这个解决方案的类和一个小程序来测试它。
对于第 2 点,我发现使用Tranform
来执行旋转是非常有问题的,因为它会变换整个显示,而且我无法找到将变换限制在有限区域的方法。相反,我使用Eclipse GEF创建了一个Rectangle,将其旋转并转换为PathData
可用于剪辑的。
对于第 4 点,我重用了PathData
第 2 点的起点来绘制剪切矩形的底部段,这相当于两种颜色之间的分隔线。为了避免在圆外绘制部分线段,我用圆轮廓剪裁了它。
这是结果:
这是测试程序,使用箭头键移动/旋转分隔线:
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。
推荐阅读
- asp.net - 脚手架时尝试激活控制器时无法解析服务上下文
- javascript - 为 mongodb 中的现有值添加价值
- azure - AADSTS90027 我们无法在 MSA 租户上从此 API 版本颁发令牌
- node.js - 使用 express-validator 验证对象数组
- flutter - Overalyed 图像不会在颤动中出现在左下角
- php - 如果值等于,则使用 OR 语句回显一次
- python - 无法使用 importlib 在 python 3.8.8 中导入文件
- java - 是否为 spring mvc rest api 或 spring boot api 对应用程序服务器的每个新请求创建了新的 Service、Repository 和 Component 实例?
- scala - 如何指定传递给 Scala 映射(高阶)函数的函数的参数?
- swift - 为 Objective-C Pod 添加 SPM 支持:提供包含目录