I have written my own routines for manipulating vectors and matrices including a number of math utilities.
I'd far rather use JavaFX without its scene graph and use my own routines, is it possible to manipulate just a single (4x4) matrix for each Shape3D you wish to render?
In order to Transform a Node directly rather than making it a child of a scene graph of transforms (for example and X,Y,Z axis rotation and a translation etc) you can use a single Affine matrix to do all the transformations (orientation/translation)
For example parent a Shape3D to this class then parent the Pivot to the root of the scene
package uk.co.bedroomcoders.jfx3dedit;
import javafx.scene.Group;
import javafx.scene.transform.Affine;
import javafx.scene.transform.MatrixType;
import com.sun.javafx.geom.Vec3d;
public class Pivot extends Group {
private final double[] idt={1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};
public double rx,ry,rz;
public Affine matrix=new Affine(idt,MatrixType.MT_3D_4x4,0);
public Pivot() {
super();
getTransforms().setAll(matrix);
}
public Pivot(double x, double y, double z) {
this();
matrix.setTx(x);
matrix.setTy(y);
matrix.setTz(z);
}
public void setPosition(double x, double y, double z) {
matrix.setTx(x);
matrix.setTy(y);
matrix.setTz(z);
}
public Vec3d getPosition() {
return new Vec3d(matrix.getTx(),matrix.getTy(),matrix.getTz());
}
// set to eular rotation retaining translation (TODO reimplement fromQuat)
public void updateFromEular(double rx, double ry, double rz) {
double cx = Math.cos(rx);
double cy = Math.cos(ry);
double cz = Math.cos(rz);
double sx = Math.sin(rx);
double sy = Math.sin(ry);
double sz = Math.sin(rz);
matrix.setMxx(cy*cz); matrix.setMxy((sx * sy * cz) + (cx * sz)); matrix.setMxz(-(cx * sy * cz) + (sx * sz));
matrix.setMyx(-cy*sz); matrix.setMyy(-(sx * sy * sz) + (cx * cz)); matrix.setMyz((cx * sy * sz) + (sx * cz));
matrix.setMzx(sy); matrix.setMzy(-sx*cy); matrix.setMzz(cx*cy);
}
// make this pivot face the a point
public void lookAt(Vec3d centre, Vec3d up) {
final Vec3d f = new Vec3d(), s = new Vec3d(), u = new Vec3d();
final Vec3d t = new Vec3d(), eye = new Vec3d();
eye.set(matrix.getTx(),matrix.getTy(),matrix.getTz());
f.set(centre);
f.sub(eye);
f.normalize();
up.normalize();
t.set(f);
s.cross(t,up);
s.normalize();
t.set(s);
u.cross(t,f);
u.normalize();
matrix.setMxx( s.x); matrix.setMxy( u.x); matrix.setMxz( f.x);
matrix.setMyx( s.y); matrix.setMyy( u.y); matrix.setMyz( f.y);
matrix.setMzx( s.z); matrix.setMzy( u.z); matrix.setMzz( f.z);
}
}
Related
those are 2 example cases of what I need to solve, it is just finding the coordinate of D, given position of A, and the direction vector of red and green line
red/green line vector (or direction) is known
point A is an intersection between the red line and red circle tangent point
point B is the center of the red circle with radius = R (known)
point C is an intersection between the green line and the green circle tangent point
point D is unknown and this one that needs to be calculated
point D will always located in green circle (radius of 2R from point B)
both red and green line has the same radius of R
V is the angle of the red line relative to north up
W is the angle of the green line relative to north up
the distance between point B and D is always 2R since the circle adjacent (touching each other)
much help and hint appreciated, preferred in some code instead of math equation
Having coordinates A,B,C, we can write two vector equations using scalar (dot) product:
AC.dot.DC = 0
DB.dot.DB = 4*R^2
The first one refers to perpendicularity between tangent to circle and radius to tangency point, the second one - just squared distance between circle centers.
In coordinates:
(cx-ax)*(cx-dx) + (cy-ay)*(cy-dy) = 0
(bx-dx)*(bx-dx) + (by-dy)*(by-dy) = 4*R^2
Solve this system for unknown dx, dy - two solutions in general case.
If A and C are not known, as #Mike 'Pomax' Kamermans noticed:
Let
cr = sin(v) sr = cos(v)
cg = sin(w) sg = cos(w)
So
ax = bx + R * cr
ay = by + R * sr
and
dx = cx - R * cg
dy = cy + R * sg
Substituting expressions into the system above we have:
(dx+R*cg-bx-R*cr)*cg - (dy-R*sg-by-R*sr)*sg = 0
(bx-dx)*(bx-dx) + (by-dy)*(by-dy) = 4*R^2
Again - solve system for unknowns dx, dy
As a hint: draw it out some more:
We can construct D by constructing the line segment AG, for which we know both the angle and length, because AC⟂AG, and the segment has length R.
We can then construct a line perpendicular to AG, through G, which gives us a chord on the blue circle, one endpoint of which is D. We know the distance from B to GD (because we know trigonometry) and we know that the distance BD is 2R (because that's a given). Pythagoras then trivially gives us D.
You know where A is and the angle θ it makes from vertical.
So specify the line though C called line(C) above and the offset the line by R in order to get line(D) above that goes through point D.
In C# code this is
Line line_C = Line.ThroughPointAtAngle(A, theta);
Line line_D = line_C.Offset(radius);
Now find the intersection of this line to the greater circle
Circle circle = new Circle(B, 2 * radius);
if (circle.Intersect(line_D, out Point D, alternate: false))
{
Console.WriteLine(D);
float d_BD = B.DistanceTo(D);
Console.WriteLine(d_BD);
}
else
{
Console.WriteLine("Does not intersect.");
}
This produces point D either above line(C) or below line(C) depending on the bool argument alternate.
The code example below produces the following output:
D=Point(-0.4846499,-1.94039)
|BD|=2
The source code is
Program.cs
using static Float;
static class Program
{
static void Main(string[] args)
{
float radius = 1;
Point A = new Point(-radius, 0);
Point B = new Point(0, 0);
float theta = deg(15);
Line line_C = Line.ThroughPointAtAngle(A, theta);
Line line_D = line_C.Offset(radius);
Circle circle = new Circle(B, 2 * radius);
if (circle.Intersect(line_D, out Point D, alternate: false))
{
Console.WriteLine($"D={D}");
float d_BD = B.DistanceTo(D);
Console.WriteLine($"|BD|={d_BD}");
}
else
{
Console.WriteLine("Does not intersect.");
}
}
}
Point.cs
Describes a point in cartesian space using two coordinates (x,y)
using static Float;
public readonly struct Point
{
readonly (float x, float y) data;
public Point(float x, float y)
{
this.data = (x, y);
}
public static Point Origin { get; } = new Point(0, 0);
public static Point FromTwoLines(Line line1, Line line2)
{
float x = line1.B * line2.C - line1.C * line2.B;
float y = line1.C * line2.A - line1.A * line2.C;
float w = line1.A * line2.B - line1.B * line2.A;
return new Point(x / w, y / w);
}
public float X => data.x;
public float Y => data.y;
public float SumSquares => data.x * data.x + data.y * data.y;
#region Algebra
public static Point Negate(Point a)
=> new Point(
-a.data.x,
-a.data.y);
public static Point Scale(float factor, Point a)
=> new Point(
factor * a.data.x,
factor * a.data.y);
public static Point Add(Point a, Point b)
=> new Point(
a.data.x + b.data.x,
a.data.y + b.data.y);
public static Point Subtract(Point a, Point b)
=> new Point(
a.data.x - b.data.x,
a.data.y - b.data.y);
public static float Dot(Point point, Line line)
=> line.A * point.data.x + line.B * point.data.y + line.C;
public static Point operator +(Point a, Point b) => Add(a, b);
public static Point operator -(Point a) => Negate(a);
public static Point operator -(Point a, Point b) => Subtract(a, b);
public static Point operator *(float f, Point a) => Scale(f, a);
public static Point operator *(Point a, float f) => Scale(f, a);
public static Point operator /(Point a, float d) => Scale(1 / d, a);
#endregion
#region Geometry
public Point Offset(float dx, float dy)
=> new Point(data.x + dx, data.y + dy);
public Point Offset(Vector2 delta) => Offset(delta.X, delta.Y);
public float DistanceTo(Point point)
=> sqrt(sqr(data.x - point.data.x) + sqr(data.y - point.data.y));
#endregion
#region Formatting
public string ToString(string formatting, IFormatProvider provider)
{
return $"Point({data.x.ToString(formatting, provider)},{data.y.ToString(formatting, provider)})";
}
public string ToString(string formatting)
=> ToString(formatting, null);
public override string ToString()
=> ToString("g");
#endregion
}
Line.cs
Describes a line in cartesian space using the coefficients (a,b,c) such that the equation of the line is a x + b y + c = 0
using static Float;
public readonly struct Line
{
readonly (float a, float b, float c) data;
public Line(float a, float b, float c) : this()
{
data = (a, b, c);
}
public static Line AlongX { get; } = new Line(0, 1, 0);
public static Line AlongY { get; } = new Line(-1, 0, 0);
public static Line ThroughPointAtAngle(Point point, float angle)
{
return new Line(cos(angle), -sin(angle), point.Y * sin(angle) - point.X * cos(angle));
}
public static Line ThroughTwoPoints(Point point1, Point point2)
=> new Line(
point1.Y - point2.Y,
point2.X - point1.X,
point1.X * point2.Y - point1.Y * point2.X);
public float A => data.a;
public float B => data.b;
public float C => data.c;
#region Algebra
public static float Dot(Line line, Point point)
=> line.data.a * point.X + line.data.b * point.Y + line.data.c;
#endregion
#region Geometry
public Line ParallelThrough(Point point)
{
return new Line(data.a, data.b, -data.a * point.X - data.b * point.Y);
}
public Line PerpendicularThrough(Point point)
{
return new Line(data.b, -data.a, -data.b * point.X + data.a * point.Y);
}
public Line Offset(float amount)
=> new Line(data.a, data.b, data.c - amount * sqrt(sqr(data.a) + sqr(data.b)));
public Line Offset(float dx, float dy)
=> new Line(data.a, data.b, data.c + data.a * dx + data.b * dy);
public Line Offset(Vector2 delta) => Offset(delta.X, delta.Y);
public float DistanceTo(Point point)
=> Dot(this, point) / (data.a * data.a + data.b * data.b);
#endregion
#region Formatting
public string ToString(string formatting, IFormatProvider provider)
{
return $"Line({data.a.ToString(formatting, provider)}x+{data.b.ToString(formatting, provider)}y+{data.c.ToString(formatting, provider)}=0)";
}
public string ToString(string formatting)
=> ToString(formatting, null);
public override string ToString()
=> ToString("g");
#endregion
}
Circle.cs
Describes a circle using the center and radius.
using static Float;
public readonly struct Circle
{
readonly (Point center, float radius) data;
public Circle(Point center, float radius)
{
this.data = (center, radius);
}
public static Circle FromTwoPoints(Point point1, Point point2)
{
float radius = point1.DistanceTo(point2) / 2;
Point center = (point1 + point2) / 2;
return new Circle(center, radius);
}
public static Circle FromThreePoints(Point point1, Point point2, Point point3)
{
float k_1 = point1.SumSquares / 2;
float k_2 = point2.SumSquares / 2;
float k_3 = point3.SumSquares / 2;
float dx_12 = point2.X - point1.X;
float dy_12 = point2.Y - point1.Y;
float dx_23 = point3.X - point2.X;
float dy_23 = point3.Y - point2.Y;
float det = dx_12 * dy_23 - dx_23 * dy_12;
Point center = new Point(
(dy_12 * (k_2 - k_3) + dy_23 * (k_2 - k_1)) / det,
(dx_12 * (k_3 - k_2) + dx_23 * (k_1 - k_2)) / det);
float radius = center.DistanceTo(point1);
return new Circle(center, radius);
}
public Point Center => data.center;
public float Radius => data.radius;
#region Geometry
public float DistanceTo(Point point)
=> data.center.DistanceTo(point) - data.radius;
public float DistanceTo(Line line)
{
float d = line.DistanceTo(Center);
if (d > 0)
{
return d - data.radius;
}
else
{
return d + data.radius;
}
}
public bool Intersect(Line line, out Point point, bool alternate = false)
{
line = line.Offset(-Center.X, -Center.Y);
int sign = alternate ? -1 : 1;
float discr = sqr(line.A * data.radius) + sqr(line.B * data.radius) - sqr(line.C);
if (discr >= 0)
{
float d = sign * sqrt(discr);
float ab = line.A * line.A + line.B * line.B;
point = new Point((line.B * d - line.A * line.C) / ab, -(line.A * d + line.B * line.C) / ab);
point += Center;
return true;
}
else
{
float ab = line.A * line.A + line.B * line.B;
point = new Point((-line.A * line.C) / ab, -(+line.B * line.C) / ab);
point += Center;
return false;
}
}
#endregion
#region Formatting
public string ToString(string formatting, IFormatProvider provider)
{
return $"Circle({data.center.ToString(formatting, provider)},{data.radius.ToString(formatting, provider)})";
}
public string ToString(string formatting)
=> ToString(formatting, null);
public override string ToString()
=> ToString("g");
#endregion
}
Float.cs
Helper functions dealing with float math which is lacking from System.Math.
public static class Float
{
/// <summary>
/// A factor of π.
/// </summary>
/// <param name="x">The factor.</param>
public static float pi(float x) => (float)(Math.PI * x);
/// <summary>
/// Degree to Radian conversion
/// </summary>
/// <param name="x">The angle in degrees.</param>
/// <returns>Angle in radians</returns>
public static float deg(float x) => pi(x) / 180;
/// <summary>
/// Radian to Degree conversion
/// </summary>
/// <param name="x">The angle in radians.</param>
/// <returns>Angle in degrees</returns>
public static float rad(float x) => x * 180 / pi(1);
public static float sqr(float x) => x * x;
public static float sqrt(float x) => (float)Math.Sqrt(x);
public static float sin(float x) => (float)Math.Sin(x);
public static float cos(float x) => (float)Math.Cos(x);
}
thx for all the answer (will upvote them), I did the work on it myself, I will share my result :
and here is length of d, to solve the equation :
I am creating a 3D plot in JavaFX using the same technique discussed in this answer, with a MeshView and a PhongMaterial to provide the colours. However, only the top side of the material is coloured, and if the user rotates the camera to view from below, it's impossible to determine the shape of the plot because it is all black.
My questions:
Is there any way to set the material of the reverse side of the mesh?
If not, is there a good approach to "faking" it? I would imagine creating a new mesh upside-down in exactly the same position would cause rendering issues; is the best approach to do that but apply a very small offset so that the two meshes are not exactly on top of each other?
Edit: I have included some example code below, which is cut down from my real code but contains enough to illustrate the problem. By default it displays the top of the mesh, which is coloured red in this example. If you change the line that reads new Rotate(-30, Rotate.X_AXIS) so that the angle becomes +30 rather than -30, it will rotate the camera to show the underside of the mesh, which you will see appears black.
package test;
import javafx.application.Application;
import javafx.scene.DepthTest;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.image.Image;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.CullFace;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
public class TestApp extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
TestPlot tp = new TestPlot();
tp.setPrefSize(600, 400);
Scene scene = new Scene(tp);
stage.setScene(scene);
stage.show();
}
class TestPlot extends Region {
private final PerspectiveCamera camera = new PerspectiveCamera(true);
private double[][] data = new double[500][500];
private final StackPane root = new StackPane();
private final SubScene subscene;
public TestPlot() {
subscene = new SubScene(root, 1, 1, true, SceneAntialiasing.BALANCED);
subscene.setCamera(camera);
getChildren().add(subscene);
widthProperty().addListener((obs, oldVal, newVal) -> refreshPlot());
heightProperty().addListener((obs, oldVal, newVal) -> refreshPlot());
refreshPlot();
}
private void refreshPlot() {
// Set the subscene bounds to match the plot bounds, in case the plot was
// resized
subscene.setHeight(this.getHeight());
subscene.setWidth(this.getWidth());
// Clear any existing stuff
root.getChildren().clear();
root.setStyle("-fx-background-color: rgba(0, 0, 0, 0);");
root.getChildren().add(camera);
camera.getTransforms().clear();
int xDataPoints = data.length;
int zDataPoints = data[0].length;
// Create data mesh
TriangleMesh mesh = new TriangleMesh();
for (int x = 0; x < xDataPoints; x++) {
for (int z = 0; z < zDataPoints; z++) {
// Invert the data as JavaFX meshes are positive-down, whereas we expect
// the plot to be positive-up
mesh.getPoints().addAll(x, (float) (-data[x][z]), z);
}
}
// Create faces from data mesh
for (int x = 0; x < xDataPoints - 1; x++) {
for (int z = 0; z < zDataPoints - 1; z++) {
int tl = x * zDataPoints + z; // top-left
int bl = x * zDataPoints + z + 1; // bottom-left
int tr = (x + 1) * zDataPoints + z; // top-right
int br = (x + 1) * zDataPoints + z + 1; // bottom-right
int offset = (x * (zDataPoints - 1) + z) * 8 / 2; // div 2 because we have u AND v in the list
// working
mesh.getFaces().addAll(bl, offset + 1, tl, offset + 0, tr, offset + 2);
mesh.getFaces().addAll(tr, offset + 2, br, offset + 3, bl, offset + 1);
}
}
// Create data mesh texture map
for (float x = 0; x < xDataPoints - 1; x++) {
for (float z = 0; z < zDataPoints - 1; z++) {
float x0 = x / xDataPoints;
float z0 = z / zDataPoints;
float x1 = (x + 1) / xDataPoints;
float z1 = (z + 1) / zDataPoints;
mesh.getTexCoords().addAll( //
x0, z0, // 0, top-left
x0, z1, // 1, bottom-left
x1, z1, // 2, top-right
x1, z1 // 3, bottom-right
);
}
}
// Create texture material
Image diffuseMap = createTexture(data);
PhongMaterial material = new PhongMaterial();
material.setDiffuseMap(diffuseMap);
// Create & add mesh view
MeshView meshView = new MeshView(mesh);
meshView.setTranslateZ(-zDataPoints);
meshView.setMaterial(material);
meshView.setCullFace(CullFace.NONE);
meshView.setDrawMode(DrawMode.FILL);
meshView.setDepthTest(DepthTest.ENABLE);
root.getChildren().addAll(meshView);
double biggestAxisSize = xDataPoints;
double z = -(0.5 * biggestAxisSize) / Math.tan(0.5 * Math.toRadians(camera.getFieldOfView()));
camera.getTransforms().addAll(
new Translate(0, 0, -zDataPoints / 3.0),
new Rotate(-30, Rotate.X_AXIS),
new Translate(0, 0.5, z)
);
camera.setFarClip(biggestAxisSize * 200.0);
}
private Image createTexture(double[][] data) {
int width = data.length;
int height = data[0].length;
WritableImage wr = new WritableImage(width, height);
PixelWriter pw = wr.getPixelWriter();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
pw.setColor(x, y, Color.RED);
}
}
return wr;
}
}
}
Modify the cullFace property of your MeshView:
meshView.setCullFace(CullFace.NONE);
Also you need to add ambient light to the scene. The normals of the surface are automatiacally determined and the scalar product used with won't be positive, if the normal is facing away from the light source...
root.getChildren().add(new AmbientLight(Color.WHITE));
I have a more mathematical than programming question, sorry if I'm not in the right section. In my 2D game, we can move the camera on a map where there are objects that can emit sound, and this sound volume (defined by a float from 0 to 1) must increase when the screen center is near this object. For example, when the object is at the screen center, the sound volume is 1, and when we move away, the volume must decrease. Each object has its own scope value. (for example 1000 pixels).
I don't know how to write a method that can calculate it.
Here is some of my code (which is not the right calculation) :
private function setVolumeWithDistance():Void
{
sound.volume = getDistanceFromScreenCenter() / range;
// So the volume is a 0 to 1 float, the range is the scope in pixels and
// and the getDistanceFromScreenCenter() is the distance in pixels
}
I already have the method which calculates the distance of the object from the center screen :
public function getDistanceFromScreenCenter():Float
{
return Math.sqrt(Math.pow((Cameraman.getInstance().getFocusPosition().x - position.x), 2) +
Math.pow((Cameraman.getInstance().getFocusPosition().y - position.y), 2));
Simple acoustics can help.
Here is the formula for sound intensity from a point source. It follows an inverse square of distance rule. Build that into your code.
You need to consider the mapping between global and screen coordinates. You have to map pixel location on the screen to physical coordinates and back.
Your distance code is flawed. No one should use pow() to square numbers. Yours is susceptible to round off errors.
This code combines the distance calculation, done properly, and attempts to solve the inverse square intensity calculation. Note: Inverse square is singular for zero distance.
package physics;
/**
* Simple model for an acoustic point source
* Created by Michael
* Creation date 1/16/2016.
* #link https://stackoverflow.com/questions/34827629/calculate-sound-value-with-distance/34828300?noredirect=1#comment57399595_34828300
*/
public class AcousticPointSource {
// Units matter here....
private static final double DEFAULT_REFERENCE_INTENSITY = 0.01;
private static final double DEFAULT_REFERENCE_DISTANCE = 1.0;
// Units matter here...
private double referenceDistance;
private double referenceIntensity;
public static void main(String[] args) {
int numPoints = 20;
double x = 0.0;
double dx = 0.05;
AcousticPointSource source = new AcousticPointSource();
for (int i = 0; i < numPoints; ++i) {
x += dx;
Point p = new Point(x);
System.out.println(String.format("point %s intensity %-10.6f", p, source.intensity(p)));
}
}
public AcousticPointSource() {
this(DEFAULT_REFERENCE_DISTANCE, DEFAULT_REFERENCE_INTENSITY);
}
public AcousticPointSource(double referenceDistance, double referenceIntensity) {
if (referenceDistance <= 0.0) throw new IllegalArgumentException("distance must be positive");
if (referenceIntensity <= 0.0) throw new IllegalArgumentException("intensity must be positive");
this.referenceDistance = referenceDistance;
this.referenceIntensity = referenceIntensity;
}
public double distance2D(Point p1) {
return distance2D(p1, Point.ZERO);
}
public double distance2D(Point p1, Point p2) {
double distance = 0.0;
if ((p1 != null) && (p2 != null)) {
double dx = Math.abs(p1.x - p2.x);
double dy = Math.abs(p1.y - p2.y);
double ratio;
if (dx > dy) {
ratio = dy/dx;
distance = dx;
} else {
ratio = dx/dy;
distance = dy;
}
distance *= Math.sqrt(1.0 + ratio*ratio);
if (Double.isNaN(distance)) {
distance = 0.0;
}
}
return distance;
}
public double intensity(Point p) {
double intensity = 0.0;
if (p != null) {
double distance = distance2D(p);
if (distance != 0.0) {
double ratio = this.referenceDistance/distance;
intensity = this.referenceIntensity*ratio*ratio;
}
}
return intensity;
}
}
class Point {
public static final Point ZERO = new Point(0.0, 0.0, 0.0);
public final double x;
public final double y;
public final double z;
public Point(double x) {
this(x, 0.0, 0.0);
}
public Point(double x, double y) {
this(x, y, 0.0);
}
public Point(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
#Override
public String toString() {
return String.format("(%-10.4f,%-10.4f,%-10.4f)", x, y, z);
}
}
I would like to know how to draw a semi circle in JavaFX. I tried to use Shape and QuadCurve but I couldn't make a perfect semicircle.
Here is a picture of what I'm trying to draw :
The picture you linked is actually a semi-ring. You can get it in JavaFX by drawing nested 2 arcs and some lines. But my preferred way is to use the 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);
}
}
Refer to Shape API of JavaFX for more info about the shapes used in the code.
Screenshot:
Suggestions:
If you don't need a full outlining path, you can just use an Arc.
If you don't need the arc filled and just want to trace the outline path of the arc, then set the fill of the arc to null.
If you want the outline path of the arc thick, then set the stroke parameters on the arc.
If you need the a thick arc which is also outlined, then it is best to define a full arc as in Uluk's answer.
Sample code:
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); }
}
As an experiment, I tried to do the same thing on a Canvas. This is what I came up with, making use of a RadialGradient and the function 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
);
}
Key points here are the arc type as ArcType.ROUND and the use of Color.TRANSPARENT as the first color.
Then it can be used something along the line:
drawSemiCircle(100, 100, 100, .5f, -45, 270);
It's not a perfect solution but it worked for me.
Path.arcTo() the parameter SweepAngle refers to the rotation degree, if sweepAngle is positive the arc is clockwise, if sweepAngle is negative the arc is counterclockwise.
This code is used in my production environment, it draws a semi-circle ring using a bitmap image, the path goes clockwise on the outer radius, and counter-clockwise on the inner radius:
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);
I have an accelerometer and gyroscope embedded in a chip, from which I get the x,y,z axis (from accelerometer), pitch, roll and yaw (from gyroscope). Using these values, I have to rotate the 3D image. glRotatef(Angle,Xvector,Yvector,Zvector) How to find the Angle from pitch, roll and yaw? Any equation for this?
Those rotational values you receive from the gyroscope should not be treated as individual rotations about different axes, but time differential the compund rotation. You problem is of the form that you want to find the differential rotation matrix dR for which the differential components can be multiplied in arbitrary order to yield that matrix
dR = dR_x * dR_y * dR_z
= dR_y * dR_x * dR_z
= dR_y * dR_z * dR_x
= dR_z * dR_x * dR_y
= dR_z * dR_y * dR_x
The most easy way to do this, is calculate each of those permutations and take the average matrix of them being dR' and apply orthonormalization.
Then just use this matrix. Forget about glRotate, you don't need it and it got removed from later OpenGL versions anyway.
your answer for pitch & Yaw angle by android acceleration_sensor is:
double angle = Math.atan2(zVal, yVal)/(Math.PI/180) //for pitch
double angle = Math.atan2(xVal, yVal)/(Math.PI/180) //for Yaw
they are tested.
private SensorManager sensorManager;
TextView x;
TextView y;
TextView z;
TextView ang;
String sx, sy, sz;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.l1);
x = (TextView) findViewById (R.id.textView2);
y = (TextView) findViewById (R.id.textView3);
z = (TextView) findViewById (R.id.textView4);
ang = (TextView) findViewById(R.id.textView5);
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
sensorManager.registerListener(this, sensorManager.getDefaultSensor
(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
public void onAccuracyChanged(Sensor arg0, int arg1) {
}
#Override
public void onSensorChanged(SensorEvent event) {
if(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER){
float xVal = event.values[0];
float yVal = event.values[1];
float zVal = event.values[2];
sx = "X Value : <font color = '#800080'> " + xVal + "</font>";
sy = "Y Value : <font color = '#800080'> " + yVal + "</font>";
sz = "Z Value : <font color = '#800080'> " + zVal + "</font>";
x.setText(Html.fromHtml(sx));
y.setText(Html.fromHtml(sy));
z.setText(Html.fromHtml(sz));
double angle = Math.atan2(zVal, xVal)/(Math.PI/180);
ang.setText(String.valueOf(angle));
}
}