/*
* 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.SimpleDoubleProperty;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
/**
*
* @author Dub
*/
public class CapsuleMesh extends MeshView{
/*
Field vars
*/
private static final int DEFAULT_DIVISIONS = 64;
private static final double DEFAULT_RADIUS = 25.0D;
private static final double DEFAULT_HEIGHT = 50.0D;
/*
Constructors
*/
public CapsuleMesh() {
this(DEFAULT_RADIUS, DEFAULT_HEIGHT);
}
public CapsuleMesh(double radius, double height){
this(DEFAULT_DIVISIONS, radius, height);
}
public CapsuleMesh(int divisions, double radius, double height) {
setRadius(radius);
setHeight(height);
setMesh(createCapsule(DEFAULT_DIVISIONS, (float)getRadius(), (float)getHeight()));
}
/*
Methods
*/
private static int correctDivisions(int div) {
return ((div + 3) / 4) * 4;
}
static TriangleMesh createCapsule(int sphereDivisions, float radius, float height) {
TriangleMesh m = new TriangleMesh();
sphereDivisions = correctDivisions(sphereDivisions);
final int halfDivisions = sphereDivisions / 2;
final float fDivisions = 1.f / sphereDivisions;
final int numPoints = sphereDivisions * (halfDivisions - 1) + 2;
final int numTexCoords = (sphereDivisions + 1) * (halfDivisions - 1) + sphereDivisions * 2;
final int numFaces = sphereDivisions * (halfDivisions - 2) * 2 + sphereDivisions * 2;
float points[] = new float[numPoints * m.getPointElementSize()];
float texCoords[] = new float[numTexCoords * m.getTexCoordElementSize()];
int faces[] = new int[numFaces * m.getFaceElementSize()];
int pointIndex = 0, texIndex = 0;
float x, y, z;
for (int i = 0; i < halfDivisions - 1; ++i) {
float va = fDivisions * (i + 1 - halfDivisions / 2) * 2 * (float) Math.PI;
float hdY = (float) Math.sin(va);
float hdX = (float) Math.cos(va);
float thetaY = 0.5f + hdY * 0.5f;
for (int point = 0; point < sphereDivisions; ++point) {
double localTheta = fDivisions * point * 2 * (float) Math.PI;
float ly = (float) Math.sin(localTheta);
float lx = (float) Math.cos(localTheta);
if(i >= (halfDivisions - 1) / 2){
points[pointIndex + 0] = x = ly * hdX * (radius); //X
points[pointIndex + 1] = y = hdY * (radius) + height;//Y
points[pointIndex + 2] = z = lx * hdX * radius; //Z
}else{
points[pointIndex + 0] = x = ly * hdX * (radius); //X
points[pointIndex + 1] = y = hdY * (radius); //Y
points[pointIndex + 2] = z = lx * hdX * radius; //Z
}
texCoords[texIndex + 0] = 1 - fDivisions * point;
texCoords[texIndex + 1] = thetaY;
pointIndex += 3;
texIndex += 2;
}
texCoords[texIndex + 0] = 0;
texCoords[texIndex + 1] = thetaY;
texIndex += 2;
}
points[pointIndex + 0] = 0;
points[pointIndex + 1] = -(radius);
points[pointIndex + 2] = 0;
points[pointIndex + 3] = 0;
points[pointIndex + 4] = radius + height;
points[pointIndex + 5] = 0;
pointIndex += 6;
int pS = (halfDivisions - 1) * sphereDivisions;
float textureDelta = 1.f / 256;
for (int i = 0; i < sphereDivisions; ++i) {
texCoords[texIndex + 0] = fDivisions * (0.5f + i);
texCoords[texIndex + 1] = textureDelta;
texIndex += 2;
}
for (int i = 0; i < sphereDivisions; ++i) {
texCoords[texIndex + 0] = fDivisions * (0.5f + i);
texCoords[texIndex + 1] = 1 - textureDelta;
texIndex += 2;
}
int faceIndex = 0;
for (int i = 0; i < halfDivisions - 2; ++i) {
for (int j = 0; j < sphereDivisions; ++j) {
int p0 = i * sphereDivisions + j;
int p1 = p0 + 1;
int p2 = p0 + sphereDivisions;
int p3 = p1 + sphereDivisions;
int t0 = p0 + i;
int t1 = t0 + 1;
int t2 = t0 + (sphereDivisions + 1);
int t3 = t1 + (sphereDivisions + 1);
// add p0, p1, p2
faces[faceIndex + 0] = p0;
faces[faceIndex + 1] = t0;
faces[faceIndex + 2] = p1 % sphereDivisions == 0 ? p1 - sphereDivisions : p1;
faces[faceIndex + 3] = t1;
faces[faceIndex + 4] = p2;
faces[faceIndex + 5] = t2;
faceIndex += 6;
// add p3, p2, p1
faces[faceIndex + 0] = p3 % sphereDivisions == 0 ? p3 - sphereDivisions : p3;
faces[faceIndex + 1] = t3;
faces[faceIndex + 2] = p2;
faces[faceIndex + 3] = t2;
faces[faceIndex + 4] = p1 % sphereDivisions == 0 ? p1 - sphereDivisions : p1;
faces[faceIndex + 5] = t1;
faceIndex += 6;
}
}
int p0 = pS;
int tB = (halfDivisions - 1) * (sphereDivisions + 1);
for (int i = 0; i < sphereDivisions; ++i) {
int p2 = i, p1 = i + 1, t0 = tB + i;
faces[faceIndex + 0] = p0;
faces[faceIndex + 1] = t0;
faces[faceIndex + 2] = p1 == sphereDivisions ? 0 : p1;
faces[faceIndex + 3] = p1;
faces[faceIndex + 4] = p2;
faces[faceIndex + 5] = p2;
faceIndex += 6;
}
p0 = p0 + 1;
tB = tB + sphereDivisions;
int pB = (halfDivisions - 2) * sphereDivisions;
for (int i = 0; i < sphereDivisions; ++i) {
int p1 = pB + i, p2 = pB + i + 1, t0 = tB + i;
int t1 = (halfDivisions - 2) * (sphereDivisions + 1) + i, t2 = t1 + 1;
faces[faceIndex + 0] = p0;
faces[faceIndex + 1] = t0;
faces[faceIndex + 2] = p1;
faces[faceIndex + 3] = t1;
faces[faceIndex + 4] = p2 % sphereDivisions == 0 ? p2 - sphereDivisions : p2;
faces[faceIndex + 5] = t2;
faceIndex += 6;
}
m.getPoints().setAll(points);
m.getTexCoords().setAll(texCoords);
m.getFaces().setAll(faces);
return m;
}
/*
Properties
*/
private final DoubleProperty radius = new SimpleDoubleProperty(){
@Override
protected void invalidated() {
setMesh(createCapsule(DEFAULT_DIVISIONS, (float)getRadius(), (float)getHeight()));
}
};
public final double getRadius() {
return radius.get();
}
public final void setRadius(double value) {
radius.set(value);
}
public DoubleProperty radiusProperty() {
return radius;
}
private final DoubleProperty height = new SimpleDoubleProperty(){
@Override
protected void invalidated() {
setMesh(createCapsule(DEFAULT_DIVISIONS, (float)getRadius(), (float)getHeight()));
}
};
public final double getHeight() {
return height.get();
}
public final void setHeight(double value) {
height.set(value);
}
public DoubleProperty heightProperty() {
return height;
}
}