How to draw a filled path built of arcs - gdi+

Might be a simple question:
how can I draw a filled graphic path built with lines and arcs? The problem is, that GDI+ defines an arc from starting angle to end angle with clockwize orientation only.
Image to be made
A sample (any language) would be great!
It should be made by setting the end point of an arc is the starting point of the next line and so on.

A way =>
Result :
Code (C#, VS 2015) :
(drawing on screen DC for testing) :
using (Graphics gr = Graphics.FromHwnd(IntPtr.Zero))
{
RectangleF rect = new Rectangle(500, 100, 400, 400 / 2);
RectangleF rectArc;
float nXradius = 60;
float nYradius = 60;
PointF point1, point2;
GraphicsPath gp = new GraphicsPath();
// Upper Left
rectArc = new RectangleF(rect.X, rect.Y, 2 * nXradius, 2 * nYradius);
gp.AddArc(rectArc, 180, 90);
point1 = new PointF(rect.X + nXradius, rect.Y);
// Top Border
point2 = new PointF(rect.Right - nXradius, rect.Y);
gp.AddLine(point1, point2);
// Upper Right.
rectArc = new RectangleF(rect.Right - 2 * nXradius, rect.Y, 2 * nXradius, 2 * nYradius);
gp.AddArc(rectArc, 270, 90);
point1 = new PointF(rect.Right, rect.Y + nYradius);
// Right Border
point2 = new PointF(rect.Right, rect.Bottom - nYradius);
gp.AddLine(point1, point2);
// Lower Right
rectArc = new RectangleF(rect.Right, rect.Bottom - 2 * nYradius, 2 * nXradius, 2 * nYradius);
gp.AddArc(rectArc, -180, -90);
point1 = new PointF(rect.Right + nXradius, rect.Bottom);
// Bottom Border
point2 = new PointF(rect.X - nXradius, rect.Bottom);
gp.AddLine(point1, point2);
// Lower Left
rectArc = new RectangleF(rect.X - 2 * nXradius, rect.Bottom - 2 * nYradius, 2 * nXradius, 2 * nYradius);
gp.AddArc(rectArc, 90, -90);
point1 = new PointF(rect.X, rect.Bottom - nYradius);
// Left Border
point2 = new PointF(rect.X, rect.Y + nYradius);
gp.AddLine(point1, point2);
gp.CloseFigure();
System.Drawing.Pen pen = new System.Drawing.Pen(System.Drawing.Color.Black, 10);
pen.LineJoin = LineJoin.Round;
gr.SmoothingMode = SmoothingMode.AntiAlias;
gr.DrawPath(pen, gp);
gr.FillPath(System.Drawing.Brushes.Orange, gp);
}

Related

Rotation in react application

I am trying to make a game where I have to move the plane in circular motion. However, when it reaches 90 degree or certain angle it still points in same direction as it is still image. I want to update the position on a circle with the state of react but image should also move and not point in same direction. aeroplane move circular
Currently the head is in east direction, at 0 degree. Now if the react state has value 90 degree i want it to move on this circle 90 degree but also now point upwards in north direction . Anyhelp?
Yes I tried it but does not seem to be working. I was using canvas earlier.....................
class Louie extends Component {
constructor(props) {
super(props);
this.update = this.update.bind(this);
this.rotateImage = this.rotateImage.bind(this);
}
componentDidMount() {
this.props.dispatch(getStats());
this.canvas = this.refs.canvas;
this.ctx = this.canvas.getContext("2d");
this.image = new Image();
this.image.src = require("../../Images/louie-front.png");
this.image.id = "lpl-head";
this.image.style.transform = "rotate(90deg)";
this.camera = {};
this.camera.x = 0;
this.camera.y = 0;
this.scale = 1.0;
this.obj = [];
this.t = {};
this.t.angle = Math.random() * Math.PI * 2; //start angle
this.t.radius = document.getElementById("louie-canvas").offsetHeight - 100;
this.t.x = Math.cos(this.t.angle) * this.t.radius; // start position x
this.t.y = Math.sin(this.t.angle) * this.t.radius; //start position y
this.t.circumference = this.t.radius * 2 * Math.PI; //curcumfrence
this.t.start = Date.now();
this.obj.push(this.t);
this.draw();
}
draw = () => {
this.update();
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.save();
this.ctx.translate(
0 - (this.camera.x - this.canvas.width / 2),
0 - (this.camera.y - this.canvas.height / 2)
);
this.ctx.scale(this.scale, this.scale);
this.ctx.fillStyle = "blue";
for (var i = 0; i < this.obj.length; i++) {
this.ctx.beginPath();
this.ctx.arc(0, 0, this.obj[i].radius, 0, Math.PI * 2);
this.ctx.lineWidth = 5;
this.ctx.strokeStyle = "#949494";
this.ctx.shadowBlur = 15;
this.ctx.shadowColor = "#080808";
this.ctx.shadowOffsetX = 10;
this.ctx.shadowOffsetY = 5;
this.ctx.stroke();
this.ctx.drawImage(
this.image,
this.obj[i].x - (window.innerWidth * 4) / 100,
this.obj[i].y - (window.innerWidth * 3) / 100,
(window.innerWidth * 8) / 100,
(window.innerHeight * 10) / 100
);
}
this.ctx.fillStyle = "#ab377a";
this.ctx.beginPath();
this.ctx.arc(0, 0, 10, 0, 2 * Math.PI);
this.ctx.closePath();
this.ctx.fill();
this.ctx.restore();
requestAnimationFrame(this.draw);
};
update = () => {
for (var i = 0; i < this.obj.length; i++) {
var angle = this.props.headDegree;
this.obj[i].x = this.obj[i].radius * Math.cos((angle * Math.PI) / 180);
this.obj[i].y = this.obj[i].radius * Math.sin((angle * Math.PI) / 180);
}
};
rotateImage = (angle) => {
this.image.style.transform = `rotateZ(${angle * 3}deg)`;
};

What is the most practical way to create coordinate grid with JavaFX 3D?

I would like to create a 3D demo application with JavaFX to visualize movements of points in 3D space and first I need to set up a coordinate grid for visual reference. Unfortunately, I was not able to find a sample code for a grid like in this picture:
Does anyone know what is the most practical way to create something like it?
There are a few solutions out there already.
FXyz3D library has a CubeWorld class, that gives you precisely a reference grid.
It is quite easy to use. Just import the 'org.fxyz3d:fxyz3d:0.3.0' dependency from JCenter and use it:
CubeWorld cubeWorld = new CubeWorld(5000, 500, true);
Sphere sphere = new Sphere(100);
sphere.setMaterial(new PhongMaterial(Color.FIREBRICK));
sphere.getTransforms().add(new Translate(100, 200, 300));
Scene scene = new Scene(new Group(cubeWorld, sphere), 800, 800, true, SceneAntialiasing.BALANCED);
As you can see, the solution is based on using 2D rectangles for each face, and the grid lines are created with 3D cylinders. It has very nice features (like self lightning or frontal faces according to camera don't show grid), but it is quite intensive in nodes (sample above has 168 nodes).
There are other solutions that use a lower number of nodes. For instance, for this sample, that also happens to be related to Leap Motion, I used a TriangleMesh.
This is an easy solution, and with just two meshes. However, you see the triangles, instead of squares.
So let's try to get rid of the triangles. For that I'll use a PolygonMesh, as in this other question, based on the 3DViewer project that is available at the OpenJFX repository, contains already a PolygonalMesh implementation, that allows any number of points per face, so any polygon can be a face.
This will give you a plane grid based in square faces:
private PolygonMesh createQuadrilateralMesh(float width, float height, int subDivX, int subDivY) {
final float minX = - width / 2f;
final float minY = - height / 2f;
final float maxX = width / 2f;
final float maxY = height / 2f;
final int pointSize = 3;
final int texCoordSize = 2;
// 4 point indices and 4 texCoord indices per face
final int faceSize = 8;
int numDivX = subDivX + 1;
int numVerts = (subDivY + 1) * numDivX;
float points[] = new float[numVerts * pointSize];
float texCoords[] = new float[numVerts * texCoordSize];
int faceCount = subDivX * subDivY;
int faces[][] = new int[faceCount][faceSize];
// Create points and texCoords
for (int y = 0; y <= subDivY; y++) {
float dy = (float) y / subDivY;
double fy = (1 - dy) * minY + dy * maxY;
for (int x = 0; x <= subDivX; x++) {
float dx = (float) x / subDivX;
double fx = (1 - dx) * minX + dx * maxX;
int index = y * numDivX * pointSize + (x * pointSize);
points[index] = (float) fx;
points[index + 1] = (float) fy;
points[index + 2] = 0.0f;
index = y * numDivX * texCoordSize + (x * texCoordSize);
texCoords[index] = dx;
texCoords[index + 1] = dy;
}
}
// Create faces
int index = 0;
for (int y = 0; y < subDivY; y++) {
for (int x = 0; x < subDivX; x++) {
int p00 = y * numDivX + x;
int p01 = p00 + 1;
int p10 = p00 + numDivX;
int p11 = p10 + 1;
int tc00 = y * numDivX + x;
int tc01 = tc00 + 1;
int tc10 = tc00 + numDivX;
int tc11 = tc10 + 1;
faces[index][0] = p00;
faces[index][1] = tc00;
faces[index][2] = p10;
faces[index][3] = tc10;
faces[index][4] = p11;
faces[index][5] = tc11;
faces[index][6] = p01;
faces[index++][7] = tc01;
}
}
int[] smooth = new int[faceCount];
PolygonMesh mesh = new PolygonMesh(points, texCoords, faces);
mesh.getFaceSmoothingGroups().addAll(smooth);
return mesh;
}
So you can use 2 or 3 of them to create a coordinate system like this:
public Group createGrid(float size, float delta) {
if (delta < 1) {
delta = 1;
}
final PolygonMesh plane = createQuadrilateralMesh(size, size, (int) (size / delta), (int) (size / delta));
final PolygonMesh plane2 = createQuadrilateralMesh(size, size, (int) (size / delta / 5), (int) (size / delta / 5));
PolygonMeshView meshViewXY = new PolygonMeshView(plane);
meshViewXY.setDrawMode(DrawMode.LINE);
meshViewXY.setCullFace(CullFace.NONE);
PolygonMeshView meshViewXZ = new PolygonMeshView(plane);
meshViewXZ.setDrawMode(DrawMode.LINE);
meshViewXZ.setCullFace(CullFace.NONE);
meshViewXZ.getTransforms().add(new Rotate(90, Rotate.X_AXIS));
PolygonMeshView meshViewYZ = new PolygonMeshView(plane);
meshViewYZ.setDrawMode(DrawMode.LINE);
meshViewYZ.setCullFace(CullFace.NONE);
meshViewYZ.getTransforms().add(new Rotate(90, Rotate.Y_AXIS));
PolygonMeshView meshViewXY2 = new PolygonMeshView(plane2);
meshViewXY2.setDrawMode(DrawMode.LINE);
meshViewXY2.setCullFace(CullFace.NONE);
meshViewXY2.getTransforms().add(new Translate(size / 1000f, size / 1000f, 0));
PolygonMeshView meshViewXZ2 = new PolygonMeshView(plane2);
meshViewXZ2.setDrawMode(DrawMode.LINE);
meshViewXZ2.setCullFace(CullFace.NONE);
meshViewXZ2.getTransforms().add(new Translate(size / 1000f, size / 1000f, 0));
meshViewXZ2.getTransforms().add(new Rotate(90, Rotate.X_AXIS));
PolygonMeshView meshViewYZ2 = new PolygonMeshView(plane2);
meshViewYZ2.setDrawMode(DrawMode.LINE);
meshViewYZ2.setCullFace(CullFace.NONE);
meshViewYZ2.getTransforms().add(new Translate(size / 1000f, size / 1000f, 0));
meshViewYZ2.getTransforms().add(new Rotate(90, Rotate.Y_AXIS));
return new Group(meshViewXY, meshViewXY2, meshViewXZ, meshViewXZ2 /*, meshViewYZ, meshViewYZ2 */);
}
Note that I've duplicated the plane to mock a wider stroke every 5 lines.
Finally adding axes:
public Group getAxes(double scale) {
Cylinder axisX = new Cylinder(1, 200);
axisX.getTransforms().addAll(new Rotate(90, Rotate.Z_AXIS), new Translate(0, -100, 0));
axisX.setMaterial(new PhongMaterial(Color.RED));
Cylinder axisY = new Cylinder(1, 200);
axisY.getTransforms().add(new Translate(0, 100, 0));
axisY.setMaterial(new PhongMaterial(Color.GREEN));
Cylinder axisZ = new Cylinder(1, 200);
axisZ.setMaterial(new PhongMaterial(Color.BLUE));
axisZ.getTransforms().addAll(new Rotate(90, Rotate.X_AXIS), new Translate(0, 100, 0));
Group group = new Group(axisX, axisY, axisZ);
group.getTransforms().add(new Scale(scale, scale, scale));
return group;
}
Now you have:
final Group axes = getAxes(0.5);
final Group grid = createGrid(200, 10);
final Sphere sphere = new Sphere(5);
sphere.getTransforms().add(new Translate(20, 15, 40));
Scene scene = new Scene(new Group(axes, grid, sphere), 800, 800, true, SceneAntialiasing.BALANCED);
The total amount of nodes of this sample is 14.
Of course, it can be improved to add labels and many other features.

Placing circles in a pane with javafx

Trying to create dynamically a series of circles with Javafx. After typing the number of circles i got this:
But actually i want that my circles be in that position:
Here is my code and thanks for any hints!!
int k = 5;
for (int i = 0; i < nbNoeuds; i++) {
Noeudfx circle = new Noeudfx(k * 2, k * 2, 1, String.valueOf(i));
Label id = new Label(String.valueOf(i));
noeuds.getChildren().add(id);
id.setLayoutX(k * 2 - 20);
id.setLayoutY(k * 2 - 20);
id.setBlendMode(BlendMode.DIFFERENCE);
k += 10;
FillTransition ft1 = new FillTransition(Duration.millis(300), circle, Color.RED, Color.BLACK);
ft1.play();
noeuds.getChildren().add(circle);
ScaleTransition tr = new ScaleTransition(Duration.millis(100), circle);
tr.setByX(10f);
tr.setByY(10f);
tr.setInterpolator(Interpolator.EASE_OUT);
tr.play();
}
}
public class Noeudfx extends Circle {
Noeud noeud;
Point point;
Label distance = new Label("distance : infinite");
boolean isSelected = false;
List<Noeudfx> circles = new ArrayList<>();
public Noeudfx(double a, double b, double c, String nom) {
super(a, b, c);
noeud = new Noeud(nom, this);
point = new Point((int) a, (int) b);
circles.add(this);
}
}
Here is my solution:
int nbNoeuds = Integer.parseInt(nodeID.getText());
System.out.println("nnnnn"
+ nbNoeuds);
final Timeline animation = new Timeline(
new KeyFrame(Duration.seconds(.5), (ActionEvent actionEvent) -> {
while (noeuds.getChildren().size() <= nbNoeuds) {
// noeuds.getChildren().remove(0);
int radius =10 ;
noeuds.getChildren().add(
new Circle(
rnd.nextInt(SCENE_SIZE - radius * 2) + radius, rnd.nextInt(SCENE_SIZE - radius * 2) + radius,
radius,
Color.GRAY
)
);
}
})
);
animation.setCycleCount(Animation.INDEFINITE);
animation.play();
animation.setOnFinished((ActionEvent actionevent) -> {
animation.stop();
});
Update: i tried to add label to each circle, the problem was that the number of circles in the screen is not correct i don't know why!
Label id = new Label(String.valueOf(i));
id.setTextFill(Color.CADETBLUE);
id.setAlignment(Pos.CENTER);
Circle circle = new Circle(
rnd.nextInt(SCENE_SIZE - radius * 2) + radius, rnd.nextInt(SCENE_SIZE - radius * 2) + radius,
radius,
Color.GRAY
);
Double a = circle.getCenterX();
Double b = circle.getCenterY();
id.setLayoutX(a - 20);
id.setLayoutY(b - 20);
id.setBlendMode(BlendMode.DIFFERENCE);
noeuds.getChildren().add(id);
noeuds.getChildren().add(circle);

How to create hollow cylinder and truncated cone with JavaFX?

I'm learning JavaFX 3D. So far I have not found a way how I can create the following objects:
Hollow cylinder
Truncated cone
Can someone please give me a short code example?
Any help will be appreciated. :-)
I know it's an old question but I've been trying to solve similar problem so here is my solution for truncated cone:
public class Cone extends Group{
int rounds = 360;
int r1 = 100;
int r2 = 50;
int h = 100;
public Cone() {
Group cone = new Group();
PhongMaterial material = new PhongMaterial(Color.BLUE);
float[] points = new float[rounds *12];
float[] textCoords = {
0.5f, 0,
0, 1,
1, 1
};
int[] faces = new int[rounds *12];
for(int i= 0; i<rounds; i++){
int index = i*12;
//0
points[index] = (float)Math.cos(Math.toRadians(i))*r2;
points[index+1] = (float)Math.sin(Math.toRadians(i))*r2;
points[index+2] = h/2;
//1
points[index+3] = (float)Math.cos(Math.toRadians(i))*r1;
points[index+4] = (float)Math.sin(Math.toRadians(i))*r1;
points[index+5] = -h/2;
//2
points[index+6] = (float)Math.cos(Math.toRadians(i+1))*r1;
points[index+7] = (float)Math.sin(Math.toRadians(i+1))*r1;
points[index+8] = -h/2;
//3
points[index+9] = (float)Math.cos(Math.toRadians(i+1))*r2;
points[index+10] = (float)Math.sin(Math.toRadians(i+1))*r2;
points[index+11] = h/2;
}
for(int i = 0; i<rounds ; i++){
int index = i*12;
faces[index]=i*4;
faces[index+1]=0;
faces[index+2]=i*4+1;
faces[index+3]=1;
faces[index+4]=i*4+2;
faces[index+5]=2;
faces[index+6]=i*4;
faces[index+7]=0;
faces[index+8]=i*4+2;
faces[index+9]=1;
faces[index+10]=i*4+3;
faces[index+11]=2;
}
TriangleMesh mesh = new TriangleMesh();
mesh.getPoints().addAll(points);
mesh.getTexCoords().addAll(textCoords);
mesh.getFaces().addAll(faces);
Cylinder circle1 = new Cylinder(r1, 0.1);
circle1.setMaterial(material);
circle1.setTranslateZ( -h / 2);
circle1.setRotationAxis(Rotate.X_AXIS);
circle1.setRotate(90);
Cylinder circle2 = new Cylinder(r2, 0.1);
circle2.setMaterial(material);
circle2.setTranslateZ( h / 2);
circle2.setRotationAxis(Rotate.X_AXIS);
circle2.setRotate(90);
MeshView meshView = new MeshView();
meshView.setMesh(mesh);
meshView.setMaterial(material);
//meshView.setDrawMode(DrawMode.LINE);
cone.getChildren().addAll(meshView);
Rotate r1 = new Rotate(90, Rotate.X_AXIS);
cone.getTransforms().add(r1);
getChildren().addAll(cone);
}
Hope this helps someone in the future!
I started using the code supplied in an other answer on this page, but I wanted to make it as a single mesh without the cylinders in the ends. I also calculated better texture coordinates and normals. (I use Vecmath for the normal calculation. This should probably be changed to Apache commons math or something more modern...)
/**
* Create a cone shape. Origin is center of base (bottom circle). Height is along the Y axis
* #param m A given TirangleMesh
* #param res The resolution of the circles
* #param radius The base radius
* #param topRadius The top radius. If this is > 0 the cone is capped
* #param height The height of the cone along the Y axis
* #param texture The texture
*/
public static void createCone(TriangleMesh m, int res, float radius, float topRadius, float height, TextureMapping texture)
{
if (texture == null)
texture = defaultTextureMapping;
m.setVertexFormat(VertexFormat.POINT_NORMAL_TEXCOORD);
float[] v = new float[res * 6]; //vertices
float[] n = new float[(res+2)*3]; //face normals
float[] uv = new float[(res * 8) + 4]; //texture coordinates
int[] f = new int[18*(2*res-2)]; // faces ((divisions * 18) + ((divisions-2)*18))
float radPerDiv = ((float)Math.PI * 2f) / res;
int tv = res * 3; //top plane vertices start index
int tuv = (res+1) * 2; //top plane uv start index
int bcuv = tuv * 2;//(res * 4) + 4; //bottom cap uv start index
int tcuv = bcuv + (res * 2); //bottom cap uv start index
for(int i = 0; i < res; i++)
{
int vi = i*3;
float cos = (float) Math.cos(radPerDiv*(i));
float sin = (float) Math.sin(radPerDiv*(i));
//bottom plane vertices
v[vi] = cos * radius; //X
v[vi + 1] = 0; //Y
v[vi + 2] = sin * radius; //Z
//top plane vertices
v[tv + vi] = cos * topRadius; //X
v[tv + vi + 1] = height; //Y
v[tv + vi + 2] = sin * topRadius; //Z
int uvi = i*2;
//texture coordinate side down
uv[uvi] = 1f-((float)i/(float)res);
uv[uvi + 1] = 1f;
//texture coordinate side up
uv[tuv + uvi] = uv[uvi];
uv[tuv + uvi + 1] = 0;
//texture coordinate bottom cap
uv[bcuv + uvi] = (1f+cos)/2f;
uv[bcuv + uvi + 1] = (1f+sin)/2f;
//texture coordinate top cap
uv[tcuv + uvi] = (1f-cos)/2f;
uv[tcuv + uvi + 1] = (1f+sin)/2f;
//face normals
if(i>0)
{
Vector3f p0 = new Vector3f(v[vi - 3], v[vi - 2], v[vi - 1]);
Vector3f p1 = new Vector3f(v[vi], v[vi + 1], v[vi + 2]);
Vector3f p2 = new Vector3f(v[tv + vi], v[tv + vi + 1], v[tv + vi + 2]);
p1.sub(p0);
p2.sub(p0);
p0.cross(p2, p1);
p0.normalize();
n[vi - 3] = p0.x;
n[vi - 2] = p0.y;
n[vi - 1] = p0.z;
}
if(i==res-1)
{
Vector3f p0 = new Vector3f(v[vi], v[vi + 1], v[vi + 2]);
Vector3f p1 = new Vector3f(v[0], v[1], v[2]);
Vector3f p2 = new Vector3f(v[tv], v[tv + 1], v[tv + 2]);
p1.sub(p0);
p2.sub(p0);
p0.cross(p2, p1);
p0.normalize();
n[vi] = p0.x;
n[vi + 1] = p0.y;
n[vi + 2] = p0.z;
}
//faces around
int fi = i*18;
//first triangle of face
f[fi] = i; //vertex
f[fi+1] = i; //normal
f[fi+2] = i; //uv
f[fi+3] = res+i; //vertex
f[fi+4] = i; //normal
f[fi+5] = res+1+i; //uv
f[fi+6] = i+1; //vertex
f[fi+7] = i+1; //normal
f[fi+8] = i+1; //uv
//second triangle of face
f[fi+9] = i+1; //vertex
f[fi+10] = i+1; //normal
f[fi+11] = i+1; //uv
f[fi+12] = res+i; //vertex
f[fi+13] = i; //normal
f[fi+14] = res+1+i; //uv
f[fi+15] = res+i+1; //vertex
f[fi+16] = i+1; //normal
f[fi+17] = res+2+i; //uv
//wrap around, use the first vertices/normals
if(i==res-1)
{
f[fi+6] = 0; //vertex
f[fi+9] = 0; //vertex
f[fi+15] = res; //vertex
f[fi+7] = 0; //normal
f[fi+10] = 0; //normal
f[fi+16] = 0; //normal
}
//top and bottom caps
int fi2 = (i*9)+(res*18); //start index for bottom cap. Start after cone side is done
int fi3 = fi2 + (res*9) - 18; //fi2 + ((divisions - 2) * 9) //start index for top cap. Start after the bottom cap is done
int uv2 = (res*2)+2; //start index of bottom cap texture coordinate
int uv3 = (res*3)+2; //start index of top cap texture coordinate
if(i<res-2)
{
//bottom cap
f[fi2] = 0;
f[fi2+1] = res; //normal
f[fi2+2] = uv2; //uv
f[fi2+3] = i+1;
f[fi2+4] = res; //normal
f[fi2+5] = uv2 + i+1; //uv
f[fi2+6] = i+2;
f[fi2+7] = res; //normal
f[fi2+8] = uv2 + i+2; //uv
//top cap
f[fi3] = res;
f[fi3+1] = res + 1; //normal
f[fi3+2] = uv3; //uv
f[fi3+3] = res+i+2;
f[fi3+4] = res + 1; //normal
f[fi3+5] = uv3 + i+2; //uv
f[fi3+6] = res+i+1;
f[fi3+7] = res + 1; //normal
f[fi3+8] = uv3 + i+1; //uv
}
}
//smooth normals
float[] ns = new float[n.length];
Vector3f n0 = new Vector3f();
Vector3f n1 = new Vector3f();
for(int i = 0; i < res; i++)
{
int p0 = i*3;
int p1 = (i-1)*3;
if(i==0)
p1 = (res-1)*3;
n0.set(n[p0], n[p0+1], n[p0+2]);
n1.set(n[p1], n[p1+1], n[p1+2]);
n0.add(n1);
n0.normalize();
ns[p0] = n0.x;
ns[p0+1] = n0.y;
ns[p0+2] = n0.z;
}
int ni = res * 3;
ns[ni + 1] = -1; //bottom cap normal Y axis
ns[ni + 4] = 1; //top cap normal Y axis
uv[tuv-1] = 1; //bottom ring end uv coordinate
//set all data to mesh
m.getPoints().setAll(v);
m.getNormals().setAll(ns);
m.getTexCoords().setAll(uv);
m.getFaces().setAll(f);
}
Edit:
And here is code for a hollow cone (or cylinder, just set both rads to same value) Here I am using JavaFX vectors in stead of Vecmath
/**
* Create a hollow cone shape. Origin is center of base (bottom circle). Height is along the Y axis
* #param m A given TirangleMesh
* #param res The resolution of the circles
* #param radius The base radius
* #param topRadius The top radius. If this is > 0 the cone is capped
* #param height The height of the cone along the Y axis
* #param thickness The thickness of the cone wall
* #param center If the origo shall be in the center, False
* #param texture The texture
*/
public static void createHollowCone(TriangleMesh m, int res, float radius, float topRadius, float height, float thickness, boolean center, TextureMapping texture)
{
if (texture == null)
texture = defaultTextureMapping;
m.setVertexFormat(VertexFormat.POINT_NORMAL_TEXCOORD);
float[] v = new float[res * 12]; //vertices
float[] n = new float[res*3]; //face normals (raw)
float[] uv = new float[(res * 24) + 8]; //texture coordinates
int[] f = new int[res*72]; // faces
float radPerDiv = ((float)Math.PI * 2f) / res;
float buvf = 1-(thickness/radius);
float tuvf = 1-(thickness/topRadius);
int otcvsi = res * 3; //outside top circle vertices start index
int ibcvsi = res * 6; //inside bottom circle vertices start index
int itcvsi = res * 9; //inside top circle vertices start index
int ifsi = res*18; //inside faces start index
int bfsi = res*36; //bottom faces start index
int tfsi = res*54; //bottom faces start index
int tuvsi = (res+1) * 2; //top plane uv start index
int bcuvsi = tuvsi * 2;//(res * 4) + 4; //bottom cap uv start index
int tcuvsi = bcuvsi + (res * 4); //bottom cap uv start index
int bcfuvsi = bcuvsi/2; //bottom cap faces uv start index
int tcfuvsi = tcuvsi/2; //top cap faces uv start index
for(int i = 0; i < res; i++)
{
int vi = i*3;
float cos = (float) Math.cos(radPerDiv*(i));
float sin = (float) Math.sin(radPerDiv*(i));
//outside bottom circle vertices
v[vi] = cos * radius; //X
v[vi + 1] = center?-height/2f:0; //Y
v[vi + 2] = sin * radius; //Z
//outside top circle vertices
v[otcvsi + vi] = cos * topRadius; //X
v[otcvsi + vi + 1] = center?height/2f:height; //Y
v[otcvsi + vi + 2] = sin * topRadius; //Z
//inside bottom circle vertices
v[ibcvsi + vi] = cos * (radius-thickness); //X
v[ibcvsi + vi + 1] = center?-height/2f:0; //Y
v[ibcvsi + vi + 2] = sin * (radius-thickness); //Z
//inside top circle vertices
v[itcvsi + vi] = cos * (topRadius-thickness); //X
v[itcvsi + vi + 1] = center?height/2f:height; //Y
v[itcvsi + vi + 2] = sin * (topRadius-thickness); //Z
int uvi = i*2;
//texture coordinate outer side down
uv[uvi] = 1f-((float)i/(float)res);
uv[uvi + 1] = 1f;
//texture coordinate outer side up
uv[tuvsi + uvi] = uv[uvi];
uv[tuvsi + uvi + 1] = 0;
//texture coordinate bottom
uv[bcuvsi + uvi] = (1f+cos)/2f;
uv[bcuvsi + uvi + 1] = (1f-sin)/2f;
uv[(res*2)+bcuvsi + uvi] = (1f+(cos*buvf))/2f;
uv[(res*2)+bcuvsi + uvi + 1] = (1f-(sin*buvf))/2f;
//texture coordinate top cap
uv[tcuvsi + uvi] = (1f+cos)/2f;
uv[tcuvsi + uvi + 1] = (1f+sin)/2f;
uv[(res*2)+tcuvsi + uvi] = (1f+(cos*tuvf))/2f;
uv[(res*2)+tcuvsi + uvi + 1] = (1f+(sin*tuvf))/2f;
//face normals
if(i>0)
{
Point3D p0 = new Point3D(v[vi - 3], v[vi - 2], v[vi - 1]);
Point3D p1 = new Point3D(v[vi], v[vi + 1], v[vi + 2]);
Point3D p2 = new Point3D(v[otcvsi + vi], v[otcvsi + vi + 1], v[otcvsi + vi + 2]);
p1 = p1.subtract(p0);
p2 = p2.subtract(p0);
p0 = p2.crossProduct(p1);
p0 = p0.normalize();
n[vi - 3] = (float)p0.getX();
n[vi - 2] = (float)p0.getY();
n[vi - 1] = (float)p0.getZ();
}
if(i==res-1)
{
Point3D p0 = new Point3D(v[vi], v[vi + 1], v[vi + 2]);
Point3D p1 = new Point3D(v[0], v[1], v[2]);
Point3D p2 = new Point3D(v[otcvsi], v[otcvsi + 1], v[otcvsi + 2]);
p1 = p1.subtract(p0);
p2 = p2.subtract(p0);
p0 = p2.crossProduct(p1);
p0 = p0.normalize();
n[vi] = (float)p0.getX();
n[vi + 1] = (float)p0.getY();
n[vi + 2] = (float)p0.getZ();
}
int fi = i*18;
//faces around outside
//first triangle of face
f[fi] = i; //vertex
f[fi+1] = i; //normal
f[fi+2] = i; //uv
f[fi+3] = res+i; //vertex
f[fi+4] = i; //normal
f[fi+5] = res+1+i; //uv
f[fi+6] = i+1; //vertex
f[fi+7] = i+1; //normal
f[fi+8] = i+1; //uv
//second triangle of face
f[fi+9] = i+1; //vertex
f[fi+10] = i+1; //normal
f[fi+11] = i+1; //uv
f[fi+12] = res+i; //vertex
f[fi+13] = i; //normal
f[fi+14] = res+1+i; //uv
f[fi+15] = res+i+1; //vertex
f[fi+16] = i+1; //normal
f[fi+17] = res+2+i; //uv
//faces around inside
//first triangle of face
f[ifsi+fi] = (res*2)+i; //vertex
f[ifsi+fi+1] = res+i; //normal
f[ifsi+fi+2] = res-i; //uv
f[ifsi+fi+3] = (res*2)+i+1; //vertex
f[ifsi+fi+4] = res+i+1; //normal
f[ifsi+fi+5] = res-1-i; //uv
f[ifsi+fi+6] = (res*3)+i; //vertex
f[ifsi+fi+7] = res+i; //normal
f[ifsi+fi+8] = res+res+1-i; //uv
//second triangle of face
f[ifsi+fi+9] = (res*2)+i+1; //vertex
f[ifsi+fi+10] = res+i+1; //normal
f[ifsi+fi+11] = res-1-i; //uv
f[ifsi+fi+12] = (res*3)+i+1; //vertex
f[ifsi+fi+13] = res+i+1; //normal
f[ifsi+fi+14] = res+res-i; //uv
f[ifsi+fi+15] = (res*3)+i; //vertex
f[ifsi+fi+16] = res+i; //normal
f[ifsi+fi+17] = res+res+1-i; //uv
//faces on bottom
//first triangle of face
f[bfsi+fi] = i; //vertex 0
f[bfsi+fi+1] = res*2; //normal
f[bfsi+fi+2] = bcfuvsi+i; //uv
f[bfsi+fi+3] = i+1; //vertex 1
f[bfsi+fi+4] = res*2; //normal
f[bfsi+fi+5] = bcfuvsi+i+1; //uv
f[bfsi+fi+6] = (res*2)+i+1; //vertex n+1
f[bfsi+fi+7] = res*2; //normal
f[bfsi+fi+8] = bcfuvsi+res+i+1; //uv
//second triangle of face
f[bfsi+fi+9] = (res*2)+i+1; //vertex n+1
f[bfsi+fi+10] = res*2; //normal
f[bfsi+fi+11] = bcfuvsi+res+i+1; //uv
f[bfsi+fi+12] = (res*2)+i; //vertex n
f[bfsi+fi+13] = res*2; //normal
f[bfsi+fi+14] = bcfuvsi+res+i; //uv
f[bfsi+fi+15] = i; //vertex 0
f[bfsi+fi+16] = res*2; //normal
f[bfsi+fi+17] = bcfuvsi+i; //uv
//faces on top
//first triangle of face
f[tfsi+fi] = res+i; //vertex 0
f[tfsi+fi+1] = res*2+1; //normal
f[tfsi+fi+2] = tcfuvsi+i; //uv
f[tfsi+fi+3] = (res*3)+i; //vertex n
f[tfsi+fi+4] = res*2+1; //normal
f[tfsi+fi+5] = tcfuvsi+res+i; //uv
f[tfsi+fi+6] = (res*3)+i+1; //vertex n+1
f[tfsi+fi+7] = res*2+1; //normal
f[tfsi+fi+8] = tcfuvsi+res+i+1; //uv
//second triangle of face
f[tfsi+fi+9] = (res*3)+i+1; //vertex n+1
f[tfsi+fi+10] = res*2+1; //normal
f[tfsi+fi+11] = tcfuvsi+res+i+1; //uv
f[tfsi+fi+12] = res+i+1; //vertex 1
f[tfsi+fi+13] = res*2+1; //normal
f[tfsi+fi+14] = tcfuvsi+i+1; //uv
f[tfsi+fi+15] = res+i; //vertex 0
f[tfsi+fi+16] = res*2+1; //normal
f[tfsi+fi+17] = tcfuvsi+i; //uv
//wrap around, use the first vertices/normals
if(i==res-1)
{
f[fi+6] = 0; //vertex
f[fi+9] = 0; //vertex
f[fi+15] = res; //vertex
f[ifsi+fi+3] = res*2; //vertex
f[ifsi+fi+9] = res*2; //vertex
f[ifsi+fi+12] = res*3; //vertex
f[bfsi+fi+3] = 0; //vertex
f[bfsi+fi+6] = res*2; //vertex
f[bfsi+fi+9] = res*2; //vertex
f[tfsi+fi+6] = res*3; //vertex
f[tfsi+fi+9] = res*3; //vertex
f[tfsi+fi+12] = res; //vertex
f[fi+7] = 0; //normal
f[fi+10] = 0; //normal
f[fi+16] = 0; //normal
f[ifsi+fi+4] = res; //normal
f[ifsi+fi+10] = res; //normal
f[ifsi+fi+13] = res; //normal
f[bfsi+fi+5] = bcfuvsi; //uv
f[bfsi+fi+8] = bcfuvsi+res; //uv
f[bfsi+fi+11] = bcfuvsi+res; //uv
f[tfsi+fi+8] = tcfuvsi+res; //uv
f[tfsi+fi+11] = tcfuvsi+res; //uv
f[tfsi+fi+14] = tcfuvsi; //uv
}
}
//smooth normals
float[] ns = new float[(n.length*2)+6];
int ni = res * 3;
for(int i = 0; i < res; i++)
{
int p0 = i*3;
int p1 = (i-1)*3;
if(i==0)
p1 = (res-1)*3;
Point3D n0 = new Point3D(n[p0], n[p0+1], n[p0+2]);
Point3D n1 = new Point3D(n[p1], n[p1+1], n[p1+2]);
n0 = n0.add(n1);
n0 = n0.normalize();
ns[p0] = (float)n0.getX();
ns[p0+1] = (float)n0.getY();
ns[p0+2] = (float)n0.getZ();
n0 = n0.multiply(-1);
ns[ni+p0] = (float)n0.getX();
ns[ni+p0+1] = (float)n0.getY();
ns[ni+p0+2] = (float)n0.getZ();
}
ni = res * 6;
ns[ni + 1] = -1; //bottom cap normal Y axis
ns[ni + 4] = 1; //top cap normal Y axis
uv[tuvsi-1] = 1; //bottom ring end uv coordinate
//set all data to mesh
m.getPoints().setAll(v);
m.getNormals().setAll(ns);
m.getTexCoords().setAll(uv);
m.getFaces().setAll(f);
}
The first - create a cylinder, and use two different appearance objects - one for cylinder, side, and one for the two bases. For the two bases, use an invisible appearance (rendering attributes). For info on how to set different appearances to bases, set thread:
http://forum.java.sun.com/thread.jspa?threadID=663825&tstart=0
The second - use a clipping plane to clip of the bottom and top of the cylinder, and you have a hollow cylinder.
Those two methods will give you a hollow cylinder with a COMPLETELY empty interior.
If you're looking to have walls with a given thickness, there is a boolean operations set available on the internet. Create a large cylinder, and subtract a smaller cylinder. For info on the boolean op set, set this thread: http://forum.java.sun.com/thread.jspa?threadID=658612&tstart=0
Finally, it's really pretty easy to create a cylinder's geometry yourself, and it can be automated really easily with a loop. I would create four separate geometries: one for the inner cylinder with the rendered faces on the inside, one for the outer cylinder, with the rendered faces on the outside, one for the base, a disc with a hole in the middle and bottom surfaces rendered, and one for the top, another disc with a hole in the middle, and top surfaces rendered.
That can all be done pretty easily with a triangle strip array.

Javafx 8 3D Complex Shape

I want to create complex shape like human bodys for my game project. Can I create complex shape in javafx 8 with trianglemesh. and also i read, it is possible to import 3ds model to javafx. can any one tell me how to import them and do things like rotate move by javafx code
thanks for your help
Also, If you go to Oracle, a 3DViewer is in the jfx samples which will export the 3d file to FXML, thus creating appropriate Groups ... Also being able to re-import the FXML into the Viewer...
Samples ...
Although as the above comment stated, InteractiveMesh has some nice tools.
Thats the easy way, if you already have a 3d file... otherwise the point,texCoord, and face arrays can be quite daunting.
It took me quite a while to create a torus .. points were fine, texCoords were fine, the faces are a pain...
this was just for my customizable torus
/*
Let the radius from the center of the hole to the center of the torus tube be "c",
and the radius of the tube be "a".
Then the equation in Cartesian coordinates for a torus azimuthally symmetric about the z-axis is
(c-sqrt(x^2+y^2))^2+z^2=a^2
and the parametric equations are
x = (c + a * cos(v)) * cos(u)
y = (c + a * cos(v)) * sin(u)
z = a * sin(v)
(for u,v in [0,2pi).
Three types of torus, known as the standard tori, are possible,
depending on the relative sizes of a and c. c>a corresponds to the ring torus (shown above),
c=a corresponds to a horn torus which is tangent to itself at the point (0, 0, 0),
and c<a corresponds to a self-intersecting spindle torus (Pinkall 1986).
*/
public static TriangleMesh createToroidMesh(float radius, float tRadius, int tubeDivisions, int radiusDivisions) {
int POINT_SIZE = 3, TEXCOORD_SIZE = 2, FACE_SIZE = 6;
int numVerts = tubeDivisions * radiusDivisions;
int faceCount = numVerts * 2;
float[] points = new float[numVerts * POINT_SIZE],
texCoords = new float[numVerts * TEXCOORD_SIZE];
int[] faces = new int[faceCount * FACE_SIZE],
smoothingGroups;
int pointIndex = 0, texIndex = 0, faceIndex = 0, smoothIndex = 0;
float tubeFraction = 1.0f / tubeDivisions;
float radiusFraction = 1.0f / radiusDivisions;
float x, y, z;
int p0 = 0, p1 = 0, p2 = 0, p3 = 0, t0 = 0, t1 = 0, t2 = 0, t3 = 0;
// create points
for (int tubeIndex = 0; tubeIndex < tubeDivisions; tubeIndex++) {
float radian = tubeFraction * tubeIndex * 2.0f * 3.141592653589793f;
for (int radiusIndex = 0; radiusIndex < radiusDivisions; radiusIndex++) {
float localRadian = radiusFraction * radiusIndex * 2.0f * 3.141592653589793f;
points[pointIndex] = x = (radius + tRadius * ((float) Math.cos(radian))) * ((float) Math.cos(localRadian));
points[pointIndex + 1] = y = (radius + tRadius * ((float) Math.cos(radian))) * ((float) Math.sin(localRadian));
points[pointIndex + 2] = z = (tRadius * (float) Math.sin(radian));
pointIndex += 3;
float r = radiusIndex < tubeDivisions ? tubeFraction * radiusIndex * 2.0F * 3.141592653589793f : 0.0f;
texCoords[texIndex] = (0.5F + (float) (Math.sin(r) * 0.5D));;
texCoords[texIndex + 1] = ((float) (Math.cos(r) * 0.5D) + 0.5F);
texIndex += 2;
}
}
//create faces
for (int point = 0; point < (tubeDivisions) ; point++) {
for (int crossSection = 0; crossSection < (radiusDivisions) ; crossSection++) {
p0 = point * radiusDivisions + crossSection;
p1 = p0 >= 0 ? p0 + 1 : p0 - (radiusDivisions);
p1 = p1 % (radiusDivisions) != 0 ? p0 + 1 : p0 - (radiusDivisions - 1);
p2 = (p0 + radiusDivisions) < ((tubeDivisions * radiusDivisions)) ? p0 + radiusDivisions : p0 - (tubeDivisions * radiusDivisions) + radiusDivisions ;
p3 = p2 < ((tubeDivisions * radiusDivisions) - 1) ? p2 + 1 : p2 - (tubeDivisions * radiusDivisions) + 1;
p3 = p3 % (radiusDivisions) != 0 ? p2 + 1 : p2 - (radiusDivisions - 1);
t0 = point * (radiusDivisions) + crossSection;
t1 = t0 >= 0 ? t0 + 1 : t0 - (radiusDivisions);
t1 = t1 % (radiusDivisions) != 0 ? t0 + 1 : t0 - (radiusDivisions - 1);
t2 = (t0 + radiusDivisions) < ((tubeDivisions * radiusDivisions)) ? t0 + radiusDivisions : t0 - (tubeDivisions * radiusDivisions) + radiusDivisions ;
t3 = t2 < ((tubeDivisions * radiusDivisions) - 1) ? t2 + 1 : t2 - (tubeDivisions * radiusDivisions) + 1;
t3 = t3 % (radiusDivisions) != 0 ? t2 + 1 : t2 - (radiusDivisions - 1);
try {
faces[faceIndex] = (p2);
faces[faceIndex + 1] = (t3);
faces[faceIndex + 2] = (p0);
faces[faceIndex + 3] = (t2);
faces[faceIndex + 4] = (p1);
faces[faceIndex + 5] = (t0);
faceIndex += FACE_SIZE;
faces[faceIndex] = (p2);
faces[faceIndex + 1] = (t3);
faces[faceIndex + 2] = (p1);
faces[faceIndex + 3] = (t0);
faces[faceIndex + 4] = (p3);
faces[faceIndex + 5] = (t1);
faceIndex += FACE_SIZE;
} catch (Exception e) {
e.printStackTrace();
}
}
}
TriangleMesh localTriangleMesh = new TriangleMesh();
localTriangleMesh.getPoints().setAll(points);
localTriangleMesh.getTexCoords().setAll(texCoords);
localTriangleMesh.getFaces().setAll(faces);
return localTriangleMesh;
}
Consider it to be a personal opinion.
Though, I don't question the power of Javafx and the possibility to build complex 3d models using Javafx, I would not suggest building such complex structures using it, when you can easily achieve them using AutoDesk and other softwares, with less effort !
You have options to import them in your Javafx applications using early access available here
http://www.interactivemesh.org/models/jfx3dimporter.html
This seems to be very promising !
have a look at this as well
http://www.interactivemesh.org/models/jfx3dbrowser.html
I made a 2x6 in sketchup and imported it along with the chick/dude/androgyne. He/she/it is only 2D. You need to get the jar file (jimColModelImporterJFX.jar) here.
import com.interactivemesh.jfx.importer.Viewpoint;
import com.interactivemesh.jfx.importer.col.ColAsset;
import com.interactivemesh.jfx.importer.col.ColModelImporter;
//import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import java.io.File;
import java.util.Map;
public class Importer3D extends Application {
//you'll have to make your own file in sketchup and save it as *.dae
//http://help.sketchup.com/en/article/114347
private static final String DAE_FILENAME = ("2x6stud-1.dae");
private static final int VIEWPORT_SIZE = 800;
private Group root;
public Rotate rx = new Rotate();
{ rx.setAxis(Rotate.X_AXIS); }
public Rotate ry = new Rotate();
{ ry.setAxis(Rotate.Y_AXIS); }
public Rotate rz = new Rotate();
{ rz.setAxis(Rotate.Z_AXIS); }
Translate t = new Translate();
static Node[] loadNodes() {
File file = new File(DAE_FILENAME);
ColModelImporter importer = new ColModelImporter();
importer.read(file);
Node[] nodes = importer.getImport();
ColAsset colAsset = importer.getAsset();
System.out.println("asset title " + colAsset.getTitle());
System.out.println("asset unit name " + colAsset.getUnitName());
System.out.println("asset unit meter " + colAsset.getUnitMeter());
System.out.println("asset up axis " + colAsset.getUpAxis());
Map<String, PhongMaterial> materials = importer.getNamedMaterials();
for (Map.Entry<String, PhongMaterial> e : materials.entrySet()) {
System.out.println("phong material " + e.getKey() + " -> " + e.getValue());
}
Map<String, Node> namedNodes = importer.getNamedNodes();
for (Map.Entry<String, Node> e : namedNodes.entrySet()) {
System.out.println("nodes " + e.getKey() + " -> " + e.getValue());
}
Viewpoint[] viewpoints = importer.getViewpoints();
if (viewpoints != null) for (Viewpoint v : viewpoints) {
System.out.println("viewpoint " + v);
}
return nodes;
}
private Group buildScene() {
Node[] nodes = loadNodes();
root = new Group(nodes);
return root;
}
private PerspectiveCamera addCamera(Scene scene) {
PerspectiveCamera camera = new PerspectiveCamera();
camera.getTransforms().addAll(t, rz, ry, rx);
camera.setVerticalFieldOfView(true);
camera.setFieldOfView(10d);
System.out.println("Near Clip: " + camera.getNearClip());
System.out.println("Far Clip: " + camera.getFarClip());
System.out.println("FOV: " + camera.getFieldOfView());
scene.setCamera(camera);
return camera;
}
#Override
public void start(Stage stage) {
Group group = buildScene();
group.setScaleX(10);
group.setScaleY(10);
group.setScaleZ(10);
group.setTranslateX(VIEWPORT_SIZE / 2);
group.setTranslateY(VIEWPORT_SIZE / 2);
Scene scene = new Scene(group, VIEWPORT_SIZE, VIEWPORT_SIZE, true);
scene.setFill(Color.rgb(10, 10, 40));
addCamera(scene);
stage.setTitle("Collada importer");
stage.setScene(scene);
stage.show();
scene.setOnKeyPressed((evt) -> {
switch (evt.getCode()) {
case UP:
rx.setAngle(rx.getAngle() + 5);
break;
case DOWN:
rx.setAngle(rx.getAngle() - 5);
break;
case RIGHT:
t.setX(t.getX() + 10);
//camera.setTranslateX(camera.getTranslateX()+10);
break;
case LEFT:
t.setX(t.getX() - 10);
//camera.setTranslateX(camera.getTranslateX()-10);
break;
case Z:
double zoom = evt.isShortcutDown() ? -10 : +10;
t.setZ(t.getZ() + zoom);
//camera.setTranslateZ(camera.getTranslateZ()+zoom);
break;
}
});
}
public static void main(String[] args) {
System.setProperty("prism.dirtyopts", "false");
launch(args);
}
}

Resources