使用 SWT 围绕圆弧绘制边

Drawing an edge around an arc using SWT

我正在尝试围绕使用 SWT 图形绘制的圆弧绘制边缘,曲线边缘本身效果很好,因为它也使用 SWT 绘制圆弧,但是当涉及到绘制直边时,我使用了一些三角函数找出连接线的位置,不幸的是,由于我猜测我无法发现的舍入错误,或者绘制弧函数的一些神秘之处,当形状的宽度(以度为单位)是奇数。您可以看到边缘没有对齐,我试过尝试并在此处包含我的代码的可重现版本:

import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Spinner;

public class ArcDialog extends Dialog {

    private Color blue;

    public ArcDialog(final Shell parentShell) {
    super(parentShell);
    }

    @Override
    protected void configureShell(final Shell shell) {

    blue = new Color(shell.getDisplay(), new RGB(0, 100, 255));
    super.configureShell(shell);
    shell.setSize(new Point(450, 550));
    shell.setText("Arc Edges"); //$NON-NLS-1$
    }

    @Override
    public Control createDialogArea(final Composite comp) {

    final Composite content = (Composite) super.createDialogArea(comp);
    final Composite parent = new Composite(content, SWT.NONE);

    final GridLayout gridLayout2 = new GridLayout(6, false);
    parent.setLayout(gridLayout2);
    parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

    new Label(parent, SWT.NONE).setText("Direction");

    Spinner direction = new Spinner(parent, SWT.NONE);

    direction.setMaximum(360);
    direction.setMinimum(0);
    direction.setIncrement(1);
    direction.setSelection(0);

    new Label(parent, SWT.NONE).setText("Width");

    Spinner width = new Spinner(parent, SWT.NONE);
    width.setMaximum(270);
    width.setMinimum(5);
    width.setSelection(65);
    width.setIncrement(1);

    new Label(parent, SWT.NONE).setText("Length");

    Spinner length = new Spinner(parent, SWT.NONE);
    length.setMaximum(200);
    length.setMinimum(10);
    length.setIncrement(1);
    length.setSelection(150);

    final Canvas c = new Canvas(parent, SWT.BORDER);
    c.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 6, 0));
    c.addPaintListener(e -> {
        e.gc.setAntialias(SWT.ON);
        drawArc(e.gc, (double) direction.getSelection(), new Point(c.getSize().x / 2, c.getSize().y / 2),
            length.getSelection(), width.getSelection());
        drawArcEdges(e.gc, (double) direction.getSelection(), new Point(c.getSize().x / 2, c.getSize().y / 2),
            length.getSelection(), width.getSelection());
    });

    length.addListener(SWT.Selection, event -> c.redraw());
    width.addListener(SWT.Selection, event -> c.redraw());
    direction.addListener(SWT.Selection, event -> c.redraw());

    return content;
    }

    protected void drawArc(final GC gc, final Double b, final Point screenLocation, final int length,
        final double arcWidth) {

    if (length > 0) {

        gc.setBackground(blue);
        gc.fillArc(screenLocation.x - length, screenLocation.y - length, length * 2, length * 2,
            (int) Math.round(90 - (b - (arcWidth / 2))), (int) Math.round(-arcWidth));

    }
    }

    protected void drawArcEdges(final GC gc, final Double b, final Point screenLocation, final int length,
        final double arcWidth) {

    if (length > 0) {
        Point edge = getFinalLocation(screenLocation, (double) Math.round((b - (arcWidth / 2))), length);
        gc.setLineStyle(SWT.LINE_DASH);

        gc.drawLine(screenLocation.x, screenLocation.y, edge.x, edge.y);
        edge = getFinalLocation(screenLocation, (double) Math.round(b + (arcWidth / 2)), length);

        gc.drawLine(screenLocation.x, screenLocation.y, edge.x, edge.y);

        gc.drawArc(screenLocation.x - length, screenLocation.y - length, length * 2, length * 2,
            (int) Math.round(90 - (b - (arcWidth / 2))), (int) Math.round(-arcWidth));

    }
    }

    protected Point getFinalLocation(final Point start, final Double angle, final int length) {

    final int newX = (int) Math.floor((start.x + (length * Math.sin(Math.toRadians(angle))) + 0.5));
    final int newY = (int) Math.floor(((start.y - (length * Math.cos(Math.toRadians(angle)))) + 0.5));
    return new Point(newX, newY);
    }

    public static void main(final String[] args) {
    new Display();

    final ArcDialog fml = new ArcDialog(new Shell());
    fml.open();
    }
}

似乎是一个舍入错误。

当您调用 getFinalLocation 时,只需将 Math.rounds 替换为 Math.floor:

Point edge = getFinalLocation(screenLocation, Math.floor(b - (arcWidth / 2)), length);
...
edge = getFinalLocation(screenLocation, Math.floor(b + (arcWidth / 2)), length);

对齐看起来好多了。

您可以使用 Eclipse GEF 来正确绘制圆弧轮廓,而不是自己计算。

绘制整体轮廓:

使用 GEF,您可以创建 Pie(这是弧形的轮廓)并将其转换为 SWT PathData,您可以完全使用 gc.drawPath 绘制。

drawArcEdges 则变为:

protected void drawArcEdges(final GC gc, final Double b, final Point screenLocation, final int length,
                            final double arcWidth) {

    if (length > 0) {
        gc.setLineStyle(SWT.LINE_DASH);

        Pie pie = new Pie(screenLocation.x - length, screenLocation.y - length, length * 2, length * 2,
                Angle.fromDeg((int) Math.round(90 - (b - (-arcWidth / 2)))), Angle.fromDeg((int) Math.round(arcWidth)));

        PathData arcSwtPathData = Geometry2SWT.toSWTPathData(pie.toPath());
        Path arcSwtPath = new Path(gc.getDevice(), arcSwtPathData);

        gc.drawPath(arcSwtPath);

        arcSwtPath.dispose();
    }
}

绘制不同部分的轮廓:

使用 GEF,您可以创建一个 Arc 并检索其开始 (X1,Y1) 和结束 (X2,Y2) 点。

使用这些点,您可以分别用 gc 绘制直线和圆弧。

drawArcEdges 则变为:

protected void drawArcEdges(final GC gc, final Double b, final Point screenLocation, final int length,
                            final double arcWidth) {

    if (length > 0) {
        gc.setLineStyle(SWT.LINE_DASH);

        Arc arc = new Arc(screenLocation.x - length, screenLocation.y - length, length * 2, length * 2,
                Angle.fromDeg((int) Math.round(90 - (b - (-arcWidth / 2)))), Angle.fromDeg((int) Math.round(arcWidth)));

        gc.drawLine(screenLocation.x, screenLocation.y, (int)arc.getX1(), (int)arc.getY1());
        gc.drawLine(screenLocation.x, screenLocation.y, (int)arc.getX2(), (int)arc.getY2());

        gc.drawArc(screenLocation.x - length, screenLocation.y - length, length * 2, length * 2,
                (int) Math.round(90 - (b - (arcWidth / 2))), (int) Math.round(-arcWidth));
    }
}

要使用 GEF:

要使用 GEF,您只需要包含以下 jars:

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

您可以从此处的构建中的 "plugin" 文件夹中检索它们:https://www.eclipse.org/gef/downloads/index.php

选择最新版本并单击更新站点link下载完整的 zip。