Нарисуй полукольцо - JavaFX
Я хотел бы знать, как нарисовать полукруг в JavaFX. Я пытался использовать Shape и QuadCurve, но я не мог сделать идеальный полукруг.
Вот картинка того, что я пытаюсь нарисовать:
4 ответа
Картинка, которую вы связали, на самом деле является полукольцом. Вы можете получить его в JavaFX, нарисовав вложенные 2 дуги и несколько линий. Но я предпочитаю использовать Path
,
public class SemiDemo extends Application {
@Override
public void start(Stage primaryStage) {
Group root = new Group();
root.getChildren().add(drawSemiRing(120, 120, 100, 50, Color.LIGHTGREEN, Color.DARKGREEN));
root.getChildren().add(drawSemiRing(350, 350, 200, 30, Color.LIGHTSKYBLUE, Color.DARKBLUE));
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
private Path drawSemiRing(double centerX, double centerY, double radius, double innerRadius, Color bgColor, Color strkColor) {
Path path = new Path();
path.setFill(bgColor);
path.setStroke(strkColor);
path.setFillRule(FillRule.EVEN_ODD);
MoveTo moveTo = new MoveTo();
moveTo.setX(centerX + innerRadius);
moveTo.setY(centerY);
ArcTo arcToInner = new ArcTo();
arcToInner.setX(centerX - innerRadius);
arcToInner.setY(centerY);
arcToInner.setRadiusX(innerRadius);
arcToInner.setRadiusY(innerRadius);
MoveTo moveTo2 = new MoveTo();
moveTo2.setX(centerX + innerRadius);
moveTo2.setY(centerY);
HLineTo hLineToRightLeg = new HLineTo();
hLineToRightLeg.setX(centerX + radius);
ArcTo arcTo = new ArcTo();
arcTo.setX(centerX - radius);
arcTo.setY(centerY);
arcTo.setRadiusX(radius);
arcTo.setRadiusY(radius);
HLineTo hLineToLeftLeg = new HLineTo();
hLineToLeftLeg.setX(centerX - innerRadius);
path.getElements().add(moveTo);
path.getElements().add(arcToInner);
path.getElements().add(moveTo2);
path.getElements().add(hLineToRightLeg);
path.getElements().add(arcTo);
path.getElements().add(hLineToLeftLeg);
return path;
}
public static void main(String[] args) {
launch(args);
}
}
Обратитесь к API Формы JavaFX для получения дополнительной информации о формах, используемых в коде.
Скриншот:
Предложения:
- Если вам не нужен полный контур, вы можете просто использовать дуги.
- Если вам не нужна дуга, заполненная, и вы просто хотите проследить контурный контур дуги, установите для нуля заливку дуги.
- Если вы хотите, чтобы контур траектории дуги был толстым, установите параметры обводки на дуге.
- Если вам нужна толстая дуга, которая также очерчена, то лучше определить полную дугу, как в ответе Улука.
Образец кода:
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.stage.Stage;
public class SemiCircleSample extends Application {
@Override public void start(Stage stage) {
Arc arc = new Arc(50, 50, 25, 25, 0, 180);
arc.setType(ArcType.OPEN);
arc.setStrokeWidth(10);
arc.setStroke(Color.CORAL);
arc.setStrokeType(StrokeType.INSIDE);
arc.setFill(null);
stage.setScene(new Scene(new Group(arc), 100, 80));
stage.show();
}
public static void main(String[] args) { launch(args); }
}
В качестве эксперимента я попытался сделать то же самое на холсте. Это то, что я придумал, используя RadialGradient и функцию GraphicsContext.fillArc:
/**
*
* @param x Coordinate x of the centre of the arc
* @param y Coordinate y of the centre of the arc
* @param outer Outer radius of the arc
* @param innerPercentage Inner radius of the arc, from 0 to 1 (as percentage)
* @param arcStartAngle Start angle of the arc, in degrees
* @param arcExtent Extent of the arc, in degrees
*/
private void drawSemiCircle(float x, float y, float outer, float innerPercentage, float arcStartAngle, float arcExtent) {
RadialGradient rg = new RadialGradient(
0,
0,
x,
y,
outer,
false,
CycleMethod.NO_CYCLE,
new Stop((innerPercentage + (.0 * innerPercentage)), Color.TRANSPARENT),
new Stop((innerPercentage + (.1 * innerPercentage)), Color.RED),
new Stop((innerPercentage + (.6 * innerPercentage)), Color.YELLOW),
new Stop((innerPercentage + (1 * innerPercentage)), Color.GREEN)
);
gc.setFill(rg);
gc.fillArc(
x - outer,
y - outer,
outer * 2,
outer * 2,
arcStartAngle,
arcExtent,
ArcType.ROUND
);
}
Ключевыми точками здесь являются тип дуги ArcType.ROUND и использование Color.TRANSPARENT в качестве первого цвета.
Тогда это может быть использовано по линии:
drawSemiCircle(100, 100, 100, .5f, -45, 270);
Это не идеальное решение, но оно сработало для меня.
Path.arcTo() параметр SweepAngle ссылается на степень вращения, если sweepAngle положительный, дуга направлена по часовой стрелке, если sweepAngle отрицательный, дуга против часовой стрелки.
Этот код используется в моей производственной среде, он рисует полукруглое кольцо, используя растровое изображение, путь идет по часовой стрелке по внешнему радиусу и против часовой стрелки по внутреннему радиусу:
drawpercent = 0.85; //this draws a semi ring to 85% you can change it using your code.
DegreesStart = -90;
DegreesRotation = 180;
radiusPathRectF = new android.graphics.RectF((float)CentreX - (float)Radius, (float)CentreY - (float)Radius, (float)CentreX + (float)Radius, (float)CentreY + (float)Radius);
innerradiusPathRectF = new android.graphics.RectF((float)CentreX - (float)InnerRadius, (float)CentreY - (float)InnerRadius, (float)CentreX + (float)InnerRadius, (float)CentreY + (float)InnerRadius);
Path p = new Path(); //TODO put this outside your draw() function, you should never have a "new" keyword inside a fast loop.
degrees = (360 + (DegreesStart)) % 360;
radians = (360 - degrees + 90) * Math.PI / 180.0;
//radians = Math.toRadians(DegreesStart);
int XstartOuter = (int)Math.round((Math.cos(radians) * Radius + CentreX));
int YstartOuter = (int)Math.round((Math.sin(-radians)* Radius + CentreY));
int XstartInner = (int)Math.round((Math.cos(radians) * InnerRadius + CentreX));
int YstartInner = (int)Math.round((Math.sin(-radians) * InnerRadius + CentreY));
degrees = (360 + (DegreesStart + drawpercent * DegreesRotation)) % 360;
//radians = degrees * Math.PI / 180.0;
radians = (360 - degrees + 90) * Math.PI / 180.0;
//radians = Math.toRadians(DegreesStart + drawpercent * DegreesRotation);
int XendOuter = (int)Math.round((Math.cos(radians) * Radius + CentreX));
int YendOuter = (int)Math.round((Math.sin(-radians) * Radius + CentreY));
int XendInner = (int)Math.round((Math.cos(radians) * InnerRadius + CentreX));
int YendInner = (int)Math.round((Math.sin(-radians) * InnerRadius + CentreY));
//draw a path outlining the semi-circle ring.
p.moveTo(XstartInner, YstartInner);
p.lineTo(XstartOuter, YstartOuter);
p.arcTo(radiusPathRectF, (float)DegreesStart - (float)90, (float)drawpercent * (float)DegreesRotation);
p.lineTo(XendInner, YendInner);
p.arcTo(innerradiusPathRectF, (float)degrees - (float)90, -1 * (float)drawpercent * (float)DegreesRotation);
p.close();
g.clipPath(p);
g.drawBitmap(bitmapCircularBarImage, bitmapRect0, bitmapRectXY, paint);