'How to extend the JavaFX Shape class to implement custom shapes
I'm working on an application to model some problem specific 2D geometry. And of course i would like to draw that geometry on the screen for the user to actually see it.
JavaFX has some 2D shapes like rectangles, ellipses, etc. It also has cubic and quartic curves, implemented as Bezier curves. Searching for custom shapes didn't help, since all I found was examples of taking some shape elements like paths and putting them together to draw a custom shape, like a heart or diamond. Searching for information on how to implement a new shape by extending the Shape class was even less helpfull.
What I want to draw are B-Spline curves of arbitrary order and length. I know how to calculate and implement the Splines itself, but i don't know how to implement them as a new Shape, or a Shape Wrapper.
I looked at the source code of JavaFX and the documentary. It seems as if those shapes are some kind of wrapper classes themselves. E.g. the Ellipse class contains an ellipse as member, which is part of the geometry package. This recursion goes pretty far.
Now there has to be some kind of method JavaFX uses to actually draw the curve, but I couldn't find it.
So how do I extend a JavaFX Shape to create e.g. a Spline class? What methods/classes are the key for Java to draw it?
I hope someone can help me.
Yours sincerely Thorsten
Solution 1:[1]
I'd take a look at how JSilhoutte has implemented their shapes.
Cross for example, does not extend Shape, but it generates a Shape representing a cross by creating two rectangles and combining them using Shape.union. Here is the relevant method:
@Override
protected void calculateShape() {
double cx = getCenterX();
double cy = getCenterY();
double r = getRadius();
double n = validateRoundness(getRoundness());
double w = validateWidth(getWidth(), r);
double arcWH = w * n;
Rectangle beam1 = new Rectangle(cx - r, cy - (w / 2), r * 2, w);
Rectangle beam2 = new Rectangle(cx - (w / 2), cy - r, w, r * 2);
beam1.setArcWidth(arcWH);
beam1.setArcHeight(arcWH);
beam2.setArcWidth(arcWH);
beam2.setArcHeight(arcWH);
Shape shape = Shape.union(beam1, beam2);
shape.getStyleClass().addAll("silhouette", "silhoutte-cross");
setShape(shape);
}
So you could use Shape.union to combine together several CubicCurve/QuadCurve shapes to create your B-Spline curves of arbitrary order and length.
Solution 2:[2]
I was trying to do a similar thing, but instead of trying to extend Shape, I tried to extend PathElement. I was doing that so I could implement a nurbsTo method that would allow me to draw NURBS or B-Spline curves. PathElement is an abstract class that wants you to override:
void addTo(NGPath)
It's not public so if you wanted to do that, your new class would have to be in the javafx/scene/shape package. So basically, you can't do that. Seeing this post, I took a quick look at whether it feasible to write a call that extends shapes, and as Thorsten correctly points out, that looks like a gnarly path to try to go down, so I didn't pursue it.
The approach I took was to make my NURBS class generate a set of MoveTo commands. I iterate over U(t) at a sufficient increment value to get the enough points so that curves look smooth. That's probably the most reasonable way to accomplish Thorsen posted needs. If you look at the javafx implementation for something like Circle, internally it's doing that same thing. By default, the Circle class computes 64 points on the circle shape and draws lines. For most NURBS and B-Spline curves I find that 100 points look nice and smooth, but that of course depends on the degree, control points and knots being used.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | hudsonb |
| Solution 2 | Tom Rutchik |
