SWT GC заполняет цвета для верхней половины и нижней половины круга - PullRequest
0 голосов
/ 29 октября 2019

Я хочу нарисовать круг с линией, разделяющей верхнюю половину и нижнюю половину круга, а затем залить цвета для верхней половины и нижней половины с помощью GC.

Я могу сделать это, еслилиния пересекает центр круга следующим образом (чтобы повернуть линию, я могу просто изменить начальный угол fillArc ()):

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();

1 Ответ

1 голос
/ 30 октября 2019

Возможный способ:

  1. нарисовать полный синий круг без обрезки
  2. нарисовать полный красный круг, обрезанный вычисленным (и возможно повернутым) прямоугольником, который будет перезаписанчасть синего круга
  3. нарисуйте контур круга
  4. нарисуйте разделительную линию, используя соответствующий сегмент предыдущего прямоугольника отсечения, и обрежьте контур круга

IЯ создал класс, который реализует это решение, и небольшую программу для его тестирования.

Для пункта 2 я обнаружил, что использование Tranform для выполнения поворота очень проблематично, поскольку оно преобразуетвесь дисплей, и я не смог найти способ ограничить преобразования в ограниченной области. Вместо этого я использовал Eclipse GEF , чтобы создать Прямоугольник , повернуть его и преобразовать в PathData, который можно использовать для отсечения.

Для пункта 4,Я повторно использовал PathData из точки 2, чтобы нарисовать нижний сегмент прямоугольника отсечения, который эквивалентен разделительной линии между двумя цветами. Чтобы не рисовать часть сегмента за пределами круга, я обрезал его контуром круга.

Это результат:

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, вам просто нужно включить следующие файлы:

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

Вы можете получить их в папке "plugin" из сборок здесь: https://www.eclipse.org/gef/downloads/index.php.

Выберите последнюю версию и нажмите на ссылку сайта обновления, чтобы загрузить полный zip.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...