/*
* 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 java.util.function.Function;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Point2D;
import javafx.scene.DepthTest;
import javafx.scene.shape.CullFace;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.TriangleMesh;
import org.fxyz.geometry.Face3;
import org.fxyz.geometry.Point3D;
/**
* SurfacePlotMesh to plot 2D functions z = f(x,y)
*/
public class SurfacePlotMesh extends TexturedMesh {
private static final Function<Point2D,Number> DEFAULT_FUNCTION = p->Math.sin(p.magnitude())/p.magnitude();
private static final double DEFAULT_X_RANGE = 10; // -5 +5
private static final double DEFAULT_Y_RANGE = 10; // -5 +5
private static final int DEFAULT_X_DIVISIONS = 64;
private static final int DEFAULT_Y_DIVISIONS = 64;
private static final double DEFAULT_FUNCTION_SCALE = 1.0D;
public SurfacePlotMesh() {
this(DEFAULT_FUNCTION,DEFAULT_X_RANGE,DEFAULT_Y_RANGE,DEFAULT_X_DIVISIONS, DEFAULT_Y_DIVISIONS,DEFAULT_FUNCTION_SCALE);
}
public SurfacePlotMesh(Function<Point2D,Number> function) {
this(function,DEFAULT_X_RANGE,DEFAULT_Y_RANGE,DEFAULT_X_DIVISIONS, DEFAULT_Y_DIVISIONS,DEFAULT_FUNCTION_SCALE);
}
public SurfacePlotMesh(Function<Point2D,Number> function, double rangeX, double rangeY) {
this(function,rangeX,rangeY,DEFAULT_X_DIVISIONS, DEFAULT_Y_DIVISIONS,DEFAULT_FUNCTION_SCALE);
}
public SurfacePlotMesh(Function<Point2D,Number> function, double rangeX, double rangeY, double functionScale) {
this(function,rangeX,rangeY,DEFAULT_X_DIVISIONS, DEFAULT_Y_DIVISIONS,functionScale);
}
public SurfacePlotMesh(Function<Point2D,Number> function, double rangeX, double rangeY, int divisionsX, int divisionsY, double functionScale) {
setFunction2D(function);
setRangeX(rangeX);
setRangeY(rangeY);
setDivisionsX(divisionsX);
setDivisionsY(divisionsY);
setFunctionScale(functionScale);
updateMesh();
setCullFace(CullFace.BACK);
setDrawMode(DrawMode.FILL);
setDepthTest(DepthTest.ENABLE);
}
@Override
protected final void updateMesh(){
setMesh(null);
mesh=createPlotMesh(
getFunction2D(),
getRangeX(),getRangeY(),
getDivisionsX(),getDivisionsY(),
getFunctionScale());
setMesh(mesh);
}
private final ObjectProperty<Function<Point2D, Number>> function2D = new SimpleObjectProperty<Function<Point2D, Number>>(DEFAULT_FUNCTION){
@Override
protected void invalidated() {
if(mesh!=null){
updateMesh();
}
}
};
public Function<Point2D, Number> getFunction2D() {
return function2D.get();
}
public final void setFunction2D(Function<Point2D, Number> value) {
function2D.set(value);
}
public ObjectProperty function2DProperty() {
return function2D;
}
private final DoubleProperty rangeX = new SimpleDoubleProperty(DEFAULT_X_RANGE){
@Override
protected void invalidated() {
if(mesh!=null){
updateMesh();
}
}
};
public double getRangeX() {
return rangeX.get();
}
public final void setRangeX(double value) {
rangeX.set(value);
}
public DoubleProperty rangeXProperty() {
return rangeX;
}
private final DoubleProperty rangeY = new SimpleDoubleProperty(DEFAULT_Y_RANGE){
@Override
protected void invalidated() {
if(mesh!=null){
updateMesh();
}
}
};
public double getRangeY() {
return rangeY.get();
}
public final void setRangeY(double value) {
rangeY.set(value);
}
public DoubleProperty rangeYProperty() {
return rangeY;
}
private final IntegerProperty divisionsX = new SimpleIntegerProperty(DEFAULT_X_DIVISIONS){
@Override
protected void invalidated() {
if(mesh!=null){
updateMesh();
}
}
};
public int getDivisionsX() {
return divisionsX.get();
}
public final void setDivisionsX(int value) {
divisionsX.set(value);
}
public IntegerProperty divisionsXProperty() {
return divisionsX;
}
private final IntegerProperty divisionsY = new SimpleIntegerProperty(DEFAULT_Y_DIVISIONS){
@Override
protected void invalidated() {
if(mesh!=null){
updateMesh();
}
}
};
public int getDivisionsY() {
return divisionsY.get();
}
public final void setDivisionsY(int value) {
divisionsY.set(value);
}
public IntegerProperty divisionsYProperty() {
return divisionsY;
}
private final DoubleProperty functionScale = new SimpleDoubleProperty(DEFAULT_FUNCTION_SCALE);
public double getFunctionScale() {
return functionScale.get();
}
public final void setFunctionScale(double value) {
functionScale.set(value);
}
public DoubleProperty functionScaleProperty() {
return functionScale;
}
private TriangleMesh createPlotMesh(Function<Point2D,Number> function2D, double rangeX, double rangeY, int divisionsX, int divisionsY, double scale) {
listVertices.clear();
listTextures.clear();
listFaces.clear();
int numDivX = divisionsX + 1;
float pointY;
areaMesh.setWidth(rangeX);
areaMesh.setHeight(rangeY);
// Create points
for (int y = 0; y <= divisionsY; y++) {
float dy = (float)(-rangeY/2d + ((float)y /(float)divisionsY)*rangeY);
for (int x = 0; x <= divisionsX; x++) {
float dx = (float)(-rangeX/2d + ((float)x /(float)divisionsX)*rangeX);
pointY = (float)scale*function2D.apply(new Point2D(dx,dy)).floatValue();
listVertices.add(new Point3D(dx, pointY, dy));
}
}
// Create texture coordinates
createTexCoords(divisionsX,divisionsY);
// Create textures indices
for (int y = 0; y < divisionsY; y++) {
for (int x = 0; x < divisionsX; x++) {
int p00 = y * numDivX + x;
int p01 = p00 + 1;
int p10 = p00 + numDivX;
int p11 = p10 + 1;
listTextures.add(new Face3(p00,p10,p11));
listTextures.add(new Face3(p11,p01,p00));
}
}
// Create faces indices
for (int y = 0; y < divisionsY; y++) {
for (int x = 0; x < divisionsX; x++) {
int p00 = y * numDivX + x;
int p01 = p00 + 1;
int p10 = p00 + numDivX;
int p11 = p10 + 1;
listFaces.add(new Face3(p00,p10,p11));
listFaces.add(new Face3(p11,p01,p00));
}
}
return createMesh();
}
}