/*
* Copyright (C) 2013-2015 F(X)yz,
* Sean Phillips, Jason Pollastrini and Jose Pereda
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.fxyz.shapes.primitives;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.DepthTest;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
/**
*
* @author jDub1581
*/
public class TorusMesh extends MeshView {
private static final int DEFAULT_DIVISIONS = 64;
private static final int DEFAULT_T_DIVISIONS = 64;
private static final double DEFAULT_RADIUS = 12.5D;
private static final double DEFAULT_T_RADIUS = 5.0D;
private static final double DEFAULT_START_ANGLE = 0.0D;
private static final double DEFAULT_X_OFFSET = 0.0D;
private static final double DEFAULT_Y_OFFSET = 0.0D;
private static final double DEFAULT_Z_OFFSET = 1.0D;
public TorusMesh() {
this(DEFAULT_DIVISIONS, DEFAULT_T_DIVISIONS, DEFAULT_RADIUS, DEFAULT_T_RADIUS);
}
public TorusMesh(double radius, double tRadius) {
this(DEFAULT_DIVISIONS, DEFAULT_T_DIVISIONS, radius, tRadius);
}
public TorusMesh(int rDivs, int tDivs, double radius, double tRadius) {
setRadiusDivisions(rDivs);
setTubeDivisions(tDivs);
setRadius(radius);
setTubeRadius(tRadius);
setDepthTest(DepthTest.ENABLE);
updateMesh();
}
private void updateMesh(){
setMesh(createTorus(
getRadiusDivisions(),
getTubeDivisions(),
(float) getRadius(),
(float) getTubeRadius(),
(float) getTubeStartAngleOffset(),
(float)getxOffset(),
(float)getyOffset(),
(float)getzOffset()));
}
private TriangleMesh createTorus(
int radiusDivisions,
int tubeDivisions,
float radius,
float tRadius,
float tubeStartAngle,
float xOffset,
float yOffset,
float zOffset) {
int numVerts = tubeDivisions * radiusDivisions;
int faceCount = numVerts * 2;
float[] points = new float[numVerts * 3],
texCoords = new float[numVerts * 2];
int[] faces = new int[faceCount * 6];
int pointIndex = 0, texIndex = 0, faceIndex = 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 = tubeStartAngle + tubeFraction * tubeIndex * 2.0f * 3.141592653589793f;
for (int radiusIndex = 0; radiusIndex < radiusDivisions; radiusIndex++) {
float localRadian = radiusFraction * (radiusIndex) * 2.0f * 3.141592653589793f;
points[pointIndex + 0] = x = (radius + tRadius * ((float) Math.cos(radian))) * ((float) Math.cos(localRadian) + xOffset);
points[pointIndex + 1] = y = (radius + tRadius * ((float) Math.cos(radian))) * ((float) Math.sin(localRadian) + yOffset);
points[pointIndex + 2] = z = (tRadius * (float) Math.sin(radian) * zOffset);
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 += 6;
faces[faceIndex] = (p2);
faces[faceIndex + 1] = (t3);
faces[faceIndex + 2] = (p1);
faces[faceIndex + 3] = (t0);
faces[faceIndex + 4] = (p3);
faces[faceIndex + 5] = (t1);
faceIndex += 6;
} catch (Exception e) {
e.printStackTrace();
}
}
}
TriangleMesh localTriangleMesh = new TriangleMesh();
localTriangleMesh.getPoints().setAll(points);
localTriangleMesh.getTexCoords().setAll(texCoords);
localTriangleMesh.getFaces().setAll(faces);
return localTriangleMesh;
}
private final IntegerProperty radiusDivisions = new SimpleIntegerProperty(DEFAULT_DIVISIONS) {
@Override
protected void invalidated() {
setMesh(createTorus(
getRadiusDivisions(),
getTubeDivisions(),
(float) getRadius(),
(float) getTubeRadius(),
(float) getTubeStartAngleOffset(),
(float)getxOffset(),
(float)getyOffset(),
(float)getzOffset()));
}
};
public final int getRadiusDivisions() {
return radiusDivisions.get();
}
public final void setRadiusDivisions(int value) {
radiusDivisions.set(value);
}
public IntegerProperty radiusDivisionsProperty() {
return radiusDivisions;
}
private final IntegerProperty tubeDivisions = new SimpleIntegerProperty(DEFAULT_T_DIVISIONS) {
@Override
protected void invalidated() {
updateMesh();
}
};
public final int getTubeDivisions() {
return tubeDivisions.get();
}
public final void setTubeDivisions(int value) {
tubeDivisions.set(value);
}
public IntegerProperty tubeDivisionsProperty() {
return tubeDivisions;
}
private final DoubleProperty radius = new SimpleDoubleProperty(DEFAULT_RADIUS) {
@Override
protected void invalidated() {
updateMesh();
}
};
public final double getRadius() {
return radius.get();
}
public final void setRadius(double value) {
radius.set(value);
}
public DoubleProperty radiusProperty() {
return radius;
}
private final DoubleProperty tubeRadius = new SimpleDoubleProperty(DEFAULT_T_RADIUS) {
@Override
protected void invalidated() {
updateMesh();
}
};
public final double getTubeRadius() {
return tubeRadius.get();
}
public final void setTubeRadius(double value) {
tubeRadius.set(value);
}
public DoubleProperty tubeRadiusProperty() {
return tubeRadius;
}
private final DoubleProperty tubeStartAngleOffset = new SimpleDoubleProperty(DEFAULT_START_ANGLE) {
@Override
protected void invalidated() {
updateMesh();
}
};
public final double getTubeStartAngleOffset() {
return tubeStartAngleOffset.get();
}
public void setTubeStartAngleOffset(double value) {
tubeStartAngleOffset.set(value);
}
public DoubleProperty tubeStartAngleOffsetProperty() {
return tubeStartAngleOffset;
}
private final DoubleProperty xOffset = new SimpleDoubleProperty(DEFAULT_X_OFFSET) {
@Override
protected void invalidated() {
updateMesh();
}
};
public final double getxOffset() {
return xOffset.get();
}
public void setxOffset(double value) {
xOffset.set(value);
}
public DoubleProperty xOffsetProperty() {
return xOffset;
}
private final DoubleProperty yOffset = new SimpleDoubleProperty(DEFAULT_Y_OFFSET) {
@Override
protected void invalidated() {
updateMesh();
}
};
public final double getyOffset() {
return yOffset.get();
}
public void setyOffset(double value) {
yOffset.set(value);
}
public DoubleProperty yOffsetProperty() {
return yOffset;
}
private final DoubleProperty zOffset = new SimpleDoubleProperty(DEFAULT_Z_OFFSET) {
@Override
protected void invalidated() {
updateMesh();
}
};
public final double getzOffset() {
return zOffset.get();
}
public void setzOffset(double value) {
zOffset.set(value);
}
public DoubleProperty zOffsetProperty() {
return zOffset;
}
}