Is there any way to make polyline's corners rounded in javafx. I tried stroke line join but it did not work. I want to make corners like the thin line in the image below.
Line join don't help here, since they control the behavior for 2 path elements that don't have the same direction at the point where the lines join. They don't modify the path elements themselves. You need to use Path instead.
ArcTo and QuadCurveTo provide you with options for creating round corners. Both look pretty similar. The following code let's you play around with distance from the corner where the curve starts a bit:
private Path arcPath(DoubleProperty distanceProperty) {
MoveTo moveTo = new MoveTo(300, 300);
// end the line at the given distance right of the intersection of the lines
HLineTo lineTo1 = new HLineTo();
lineTo1.xProperty().bind(distanceProperty.add(50));
ArcTo arcTo = new ArcTo();
// end the curve at the given distance above the intersection of the lines
arcTo.setX(50);
arcTo.yProperty().bind(distanceProperty.negate().add(300));
// radius is equal to the distance
arcTo.radiusXProperty().bind(distanceProperty);
arcTo.radiusYProperty().bind(distanceProperty);
arcTo.setSweepFlag(true);
VLineTo lineTo2 = new VLineTo(50);
return new Path(moveTo, lineTo1, arcTo, lineTo2);
}
private Path quadPath(DoubleProperty distanceProperty) {
MoveTo moveTo = new MoveTo(300, 300);
// end the line at the given distance right of the intersection of the lines
HLineTo lineTo1 = new HLineTo();
lineTo1.xProperty().bind(distanceProperty.add(50));
QuadCurveTo curveTo = new QuadCurveTo();
// control point at the location where the lines would intersect
curveTo.setControlX(50);
curveTo.setControlY(300);
// end the curve at the given distance above the intersection of the lines
curveTo.setX(50);
curveTo.yProperty().bind(distanceProperty.negate().add(300));
VLineTo lineTo2 = new VLineTo(50);
return new Path(moveTo, lineTo1, curveTo, lineTo2);
}
Slider distanceSlider = new Slider(0, 250, 10);
Label label = new Label();
label.textProperty().bind(distanceSlider.valueProperty().asString("%.2f"));
HBox controls = new HBox(distanceSlider, label);
Path path1 = quadPath(distanceSlider.valueProperty());
Path path2 = arcPath(distanceSlider.valueProperty());
VBox root = new VBox(new HBox(new Pane(path1), new Pane(path2)), controls);
Related
I'm working in JavaFX for a class, and I'm trying to apply a gradient to a sphere, but (obviously), I can't figure out how to do it. I'm stuck because I know that a sphere is an object, and so it needs to have a material, but (as far as colors go), a PhongMaterial only takes one color, and so it won't take a gradient because a gradient is a range of colors. So basically what I'm trying to is the following:
Sphere sphere = new Sphere(50);
RadialGradient rg = new RadialGradient(0, 0, 0, 0, 5, true, CycleMethod.REPEAT, /*arbitrary/irrelevant color Stop objects*/));
PhongMaterial pm = new PhongMaterial();
pm.setDiffuseMap(pm);
sphere.setMaterial(asdf);
Now obviously this code doesn't work, but I guess it's the idea/flow of what I'm trying to do.
You are right about one thing, PhongMaterial takes a Color as diffuse color, and that doesn't allow a Gradient. For that, it should accept Paint, but that is not the case.
So we have to look for different alternatives.
DiffuseMap
If you check PhongMaterial, you can set the diffuse map with an image. That means that you can use an existing image with some gradient and apply it to the sphere.
Something like this:
Sphere sphere = new Sphere(100);
PhongMaterial material = new PhongMaterial();
material.setDiffuseMap(new Image("http://westciv.com/images/wdblogs/radialgradients/simpleclorstops.png"));
sphere.setMaterial(material);
will produce the following result:
Dynamic DiffuseMap
Obviously, this has the disadvantage of depending on a static image. What if you want to modify that dynamically?
You can do that, if you generate your radial gradient, render it on a secondary scene and take a snapshot of it. This snapshot returns a WritableImage that you can use directly as diffuse map.
Something like this:
Scene aux = new Scene(new StackPane(), 100, 100,
new RadialGradient(0, 0, 0.5, 0.5, 1, true, CycleMethod.REPEAT,
new Stop(0, Color.GREEN), new Stop(0.4, Color.YELLOW),
new Stop(0.6, Color.BLUE), new Stop(0.7, Color.RED)));
WritableImage snapshot = aux.snapshot(null);
Sphere sphere = new Sphere(100);
PhongMaterial material = new PhongMaterial();
material.setDiffuseMap(snapshot);
sphere.setMaterial(material);
You will have now:
Density Map
There is still another option to use a mathematical function to generate a density map, and the colors will be given by a mapping to that function.
For that you can't use the built-in Sphere, but you have to either create your own TriangleMesh and play with the texture coordinates, or you can simply use FXyz, an open source JavaFX 3D library with a number of different primitives and texture options.
For this case, you can get the library from Maven Central (org.fxyz3d:fxyz3d:0.3.0), use a SegmentedSphereMesh control, and then select the texture mode `Vertices3D:
SegmentedSphereMesh sphere = new SegmentedSphereMesh(100);
sphere.setTextureModeVertices3D(1530, p -> p.z);
Note the function in this case is just based on the z coordinate, but obviously you can modify that as needed.
Check the library (there is a sampler), to explore other options.
I'm new to JavaFX and I have troubles with bindings.
I'm coding a game in which I draw lines between panes contains in the cells of a GridPane. I can draw lines with the code below, but I'd like to have the line coordinates to scale as the pane gets bigger when I resize the window.
The Panes and the GridPane already resize as wanted, and I've managed to bind the line's width to the size of the scene (cellSize is a binding on the scene width/height).
Pane lastPane = panes[lastC][lastR];
Pane curPane = panes[c][r];
Bounds boundsInSceneLast = lastPane.localToScene(lastPane.getBoundsInLocal());
Bounds boundsInSceneCur = curPane.localToScene(curPane.getBoundsInLocal());
double lastX = (boundsInSceneLast.getMinX() + boundsInSceneLast.getMaxX())/2;
double lastY = (boundsInSceneLast.getMinY() + boundsInSceneLast.getMaxY())/2;
double x = (boundsInSceneCur.getMinX() + boundsInSceneCur.getMaxX())/2;
double y= (boundsInSceneCur.getMinY() + boundsInSceneCur.getMaxY())/2;
Line line = new Line(lastX, lastY, x, y);
line.setStroke(Color.web(couleurs.get(symbole)));
line.strokeWidthProperty().bind(cellSize.divide(10));
line.setStrokeLineCap(StrokeLineCap.BUTT);
anchor.getChildren().add(line);
lines.get(symbole).add(line);
anchor is an AnchorPane which is the root of my Scene and lines is a HashMap which keeps track of the lines, but those shouldn't be relevant to my problem.
I'm sure it can be done pretty simply but I've searched the web pretty deeply and all I could understand and try wasn't getting the job done so I'm asking for your help.
Thanks in advance !! :)
It's problematic to listen to the bounds of a Node in the scene since there is no property for the scene bounds and every Parent on the path to the root node of the scene could apply a transform that require you to adjust the position of the line ends. This would require you to add a listener to descendant of the Panes.
It would be simpler to make the lines a part of the GridPane itself as unmanaged nodes. This allows you to work with the bounds of the nodes in the GridPane:
final Pane lastPane = panes[lastC][lastR];
final Pane curPane = panes[c][r];
final Line line = new Line();
line.setStroke(Color.web(couleurs.get(symbole)));
line.strokeWidthProperty().bind(cellSize.divide(10));
line.setStrokeLineCap(StrokeLineCap.BUTT);
InvalidationListener listener = o -> {
Bounds boundsInSceneLast = lastPane.getBoundsInParent();
Bounds boundsInSceneCur = curPane.getBoundsInParent();
double lastX = (boundsInSceneLast.getMinX() + boundsInSceneLast.getMaxX())/2;
double lastY = (boundsInSceneLast.getMinY() + boundsInSceneLast.getMaxY())/2;
double x = (boundsInSceneCur.getMinX() + boundsInSceneCur.getMaxX())/2;
double y = (boundsInSceneCur.getMinY() + boundsInSceneCur.getMaxY())/2;
line.setStartX(lastX);
line.setStartY(lastY);
line.setEndX(x);
line.setEndY(y);
};
// listen to location & size changes of panes in the GridPane
lastPane.boundsInParentProperty().addListener(listener);
curPane.boundsInParentProperty().addListener(listener);
// initial refresh
listener.invalidated(null);
// add line to GridPane without considering it for layout
line.setManaged(false);
gridPane.getChildren().add(line);
lines.get(symbole).add(line);
I am using JGraphX, and I have a vertex with 3 ports, but I want the vertex to be to the front, for some reason it is not bringing the vertex to the front, what could I be missing?
final int PORT_DIAMETER = 20;
final int PORT_RADIUS = PORT_DIAMETER / 2;
mxGeometry geo1 = new mxGeometry(0, 0.5, PORT_DIAMETER,
PORT_DIAMETER);
// Because the origin is at upper left corner, need to translate to
// position the center of port correctly
geo1.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS));
geo1.setRelative(true);
geo1.setWidth(10);
mxCell port1 = new mxCell(null, geo1,
"port;image=/Images/blue-right-arrow.png");
port1.setVertex(true);
mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER,
PORT_DIAMETER);
geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS));
geo2.setRelative(true);
mxCell port2 = new mxCell(null, geo2,
"port;image=/Images/blue-right-arrow.png");
port2.setVertex(true);
mxGeometry geo3 = new mxGeometry(0.5, 1, PORT_DIAMETER,
PORT_DIAMETER);
geo3.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS));
geo3.setRelative(true);
mxCell port3 = new mxCell(null, geo3,
"port;image=/Images/blue-up-arrow.png");
port3.setVertex(true);
graph.addCell(port1, this);
graph.addCell(port2, this);
graph.addCell(port3, this);
graph.cellsOrdered(new Object[]{port1,port2,port3}, true);
graph.cellsOrdered(new Object[]{this}, false);
I am not sure whether this is possible: the ports have a parent p1 (in your case some 'this') and p1 has a parent e.g. p2 (or the default parent). when you bring to front or back a cell you just alter the index of the cell on the same parent. In your case, if you change the parent of the ports, you will loose them because of the relative geometry (that is necessary).
You can verify this in the example GraphEditor class: create a large rectangle, then right click on the rectangle->shape->ender group, then create a second shape, then right click shape->exit group. Then try to bring to back the second shape: right click on to the shape shape->to back. Nothing changes.
One solution, if it suits you, is to create another cell on top of your 'this' cell (the one with the ports):
double w = this.getGeometry().getWidth();
double h = this.getGeometry().getHeight();
mxGeometry geo4 = new mxGeometry(0, 0, w, h);
geo4.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS));
String s = this.getStyle(); //set the same style
mxCell c = new mxCell(null, geo4, style);
c.setVertex(true);
graph.addCell(c, this); //set this to be the patern
Another solution is to edit your blue-right-arrow.png (port) and paint the half of it with the style of your parent vertex, in order to create the illusion that they are at the back.
Hope it helps.
var snapshot:ImageSnapshot = ImageSnapshot.captureImage(someSprite);
var file:FileReference = new FileReference();
file.save(snapshot.data,'abc.png');
In the above code I am able to capture an image.
But I also want to apply a scalingMatrix(for zoomIn/Out) and a clipping rectangle to it.
How to do it?
I tried capturebitmapdata too, but with that I can't even get a proper image. See here. So I don't want to use that.
sw = someSprite.stage.stageWidth;
sh = someSprite.stage.stageHeight;
var cr:Rectangle = new Rectangle(x,y,cw,ch);//you have to check that this clip rectangle should not overshoot your stage
//cr is the clip rectangle
var bmp:BitmapData = new BitmapData(sw,sh);
bmp.draw(someSprite,null,null,null,cr);
var bmp1:BitmapData = new BitmapData(cw,ch);
bmp1.copyPixels(bmp,cr,new Point(0,0));
var enc:JPEGEncoder = new JPEGEncoder();
var data:ByteArray = encoder.encode(bmd1);
new FileReference().save(data,'image.jpeg');
The above code allows you to draw only the portion inside the clip rectangle.
In my case I didn't have to take into account a scaling matrix, even though
I was using zoom In/Out features.
I have created a Sprite in Actionscript and rendered it to a Flex Canvas. Suppose:
var fooShape:Sprite = new FooSpriteSubclass();
fooCanvas.rawChildren.addChild(myshape);
//Sprite shape renders on screen
fooShape.rotation = fooNumber;
This will rotate my shape, but seems to rotate it around the upper-left
point of its parent container(the canvas).
How can I force the Sprite to rotate about is own center point? I could obviously
write code to calculate the rotation, and then have it re-render, but I think there
must be a built-in way to do this, and certainly do not want to 'reinvent the wheel'
if possible.
I am using FlexBuilder, and therefore do not have access to the full Flash API.
Thank you much!
The following steps are required to rotate objects based on a reference point (using Matrix object and getBounds):
Matrix translation (moving to the reference point)
Matrix rotation
Matrix translation (back to original position)
For example to rotate an object 90 degrees around its center:
// Get the matrix of the object
var matrix:Matrix = myObject.transform.matrix;
// Get the rect of the object (to know the dimension)
var rect:Rectangle = myObject.getBounds(parentOfMyObject);
// Translating the desired reference point (in this case, center)
matrix.translate(- (rect.left + (rect.width/2)), - (rect.top + (rect.height/2)));
// Rotation (note: the parameter is in radian)
matrix.rotate((90/180)*Math.PI);
// Translating the object back to the original position.
matrix.translate(rect.left + (rect.width/2), rect.top + (rect.height/2));
Key methods used:
Matrix.rotate
Matrix.translate
DisplayObject.getBounds
Didn't have much luck with the other examples. This one worked for me. I used it on a UIComponent.
http://www.selikoff.net/2010/03/17/solution-to-flex-image-rotation-and-flipping-around-center/
private static function rotateImage(image:Image, degrees:Number):void {
// Calculate rotation and offsets
var radians:Number = degrees * (Math.PI / 180.0);
var offsetWidth:Number = image.contentWidth/2.0;
var offsetHeight:Number = image.contentHeight/2.0;
// Perform rotation
var matrix:Matrix = new Matrix();
matrix.translate(-offsetWidth, -offsetHeight);
matrix.rotate(radians);
matrix.translate(+offsetWidth, +offsetHeight);
matrix.concat(image.transform.matrix);
image.transform.matrix = matrix;
}
Actually I had to add this code to make above solutions work for me.
private var _rotateCount = 0;
var _origginalMatrix:Matrix=new Matrix();
.........
if (_rotateCount++ >= 360 / angleDegrees)
{
myObject.transform.matrix = _origginalMatrix;
_rotateCount = 0;
return;
}
var matrix:Matrix = myObject.transform.matrix;
....
Without that after some long time rotated object slowly moves somewhere right top.
An alternative solution is to put your object inside another View, move it so that your image's center is at the container's top-left corner, and then rotate the container.
import spark.components.*;
var myContainer:View = new View();
var myImage:Image = new Image();
myContainer.addElement(myImage);
myImage.x = myImage.width / -2;
myImage.y = myImage.height / -2;
addElement(myContainer);
myContainer.rotation = whateverAngle;
One issue might be that the width of the image isn't know at the moment it is created, so you might want to find a way around that. (Hardcode it, or see if myImage.preliminaryWidth works)
/**
* Rotates the object based on its center
* Parameters: #obj => the object to rotate
* # rotation => angle to rotate
* */
public function RotateAroundCenter(obj:Object, rotation:Number):void
{
var bound:Rectangle = new Rectangle();
// get the bounded rectangle of objects
bound = obj.getRect(this);
// calculate mid poits
var midx1:Number = bound.x + bound.width/2;
var midy1:Number = bound.y + bound.height/2;
// assign the rotation
obj.rotation = rotation;
// assign the previous mid point as (x,y)
obj.x = midx1;
obj.y = midy1;
// get the new bounded rectangle of objects
bound = obj.getRect(this);
// calculate new mid points
var midx2:Number = bound.x + bound.width/2;
var midy2:Number = bound.y + bound.height/2;
// calculate differnece between the current mid and (x,y) and subtract
//it to position the object in the previous bound.
var diff:Number = midx2 - obj.x;
obj.x -= diff;
diff = midy2 - obj.y;
obj.y -= diff;
}
//////////////////
Usage:
you can use the above function as described below,
var img:Canvas = new Canvas()
RotateAroundCenter(img, rotation);
This will help you
REf: http://subashflash.blogspot.in/2010/08/rotation-of-object-based-on-center.html
If you want to rotate around the center, merely center the asset inside your sprite by setting the internal assets x and y to half of the width and height of the asset. This swill center your content and allow it to rotate around a center point.
An example of runtime loaded assets is as follows:
var loader:Loader = new Loader():
var request:URLRequest = new URLRequest(path/to/asset.ext);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, _onLoaderComplete);
loader.load(request);
private function _onLoaderComplete(e:Event):void
{
var mc:MovieClip = e.target.content as MovieClip;
mc.x = -mc.width * 0.5;
mc.y = -mc.height * 0.5;
mc.rotation = 90;
addChild(mc);
}