How to create a 3d / surface chart with JavaFX? -
problem
i tried create 3d chart javafx seems more difficult 1 expect.
my current way of doing create trianglemesh, that's rather circumstantial. i'd provide list<point3d>
chart , chart should rendered surface.
however, simple pyramid 5 data points turns out rather complicated:
float h = 200; // height float s = 200; // side trianglemesh pyramidmesh = new trianglemesh(); pyramidmesh.gettexcoords().addall(0,0); pyramidmesh.getpoints().addall( 0, 0, 0, // point 0 - top 0, h, -s/2, // point 1 - front -s/2, h, 0, // point 2 - left s/2, h, 0, // point 3 - 0, h, s/2 // point 4 - right ); pyramidmesh.getfaces().addall( 0,0, 2,0, 1,0, // front left face 0,0, 1,0, 3,0, // front right face 0,0, 3,0, 4,0, // right face 0,0, 4,0, 2,0, // left face 4,0, 1,0, 2,0, // bottom rear face 4,0, 3,0, 1,0 // bottom front face );
questions
- does know how create 3d chart javafx?
- is trianglemesh proper way it?
- how convert
list<point3d>
trianglemesh?
code
import javafx.animation.timeline; import javafx.application.application; import javafx.event.eventhandler; import javafx.scene.group; import javafx.scene.node; import javafx.scene.perspectivecamera; import javafx.scene.scene; import javafx.scene.input.keyevent; import javafx.scene.input.mouseevent; import javafx.scene.paint.color; import javafx.scene.paint.phongmaterial; import javafx.scene.shape.box; import javafx.scene.shape.drawmode; import javafx.scene.shape.meshview; import javafx.scene.shape.trianglemesh; import javafx.scene.transform.rotate; import javafx.scene.transform.scale; import javafx.scene.transform.translate; import javafx.stage.stage; import javafx.util.duration; public class chart3dsampleapp extends application { final group root = new group(); final group axisgroup = new group(); final xform world = new xform(); final perspectivecamera camera = new perspectivecamera(true); final xform cameraxform = new xform(); final xform cameraxform2 = new xform(); final xform cameraxform3 = new xform(); final double cameradistance = 1450; final xform moleculegroup = new xform(); private timeline timeline; boolean timelineplaying = false; double one_frame = 1.0 / 24.0; double delta_multiplier = 200.0; double control_multiplier = 10.1; double shift_multiplier = 0.1; double alt_multiplier = 0.5; double mouseposx; double mouseposy; double mouseoldx; double mouseoldy; double mousedeltax; double mousedeltay; private void buildscene() { root.getchildren().add(world); } private void buildcamera() { root.getchildren().add(cameraxform); cameraxform.getchildren().add(cameraxform2); cameraxform2.getchildren().add(cameraxform3); cameraxform3.getchildren().add(camera); cameraxform3.setrotatez(0); camera.setnearclip(0.1); camera.setfarclip(10000.0); camera.settranslatez(-cameradistance); cameraxform.ry.setangle(0); cameraxform.rx.setangle(0); } private void buildaxes() { final phongmaterial redmaterial = new phongmaterial(); redmaterial.setdiffusecolor(color.darkred); redmaterial.setspecularcolor(color.red); final phongmaterial greenmaterial = new phongmaterial(); greenmaterial.setdiffusecolor(color.darkgreen); greenmaterial.setspecularcolor(color.green); final phongmaterial bluematerial = new phongmaterial(); bluematerial.setdiffusecolor(color.darkblue); bluematerial.setspecularcolor(color.blue); final box xaxis = new box(300, 1, 300); final box yaxis = new box(1, 300, 300); final box zaxis = new box(300, 300, 1); yaxis.settranslatey(-150); yaxis.settranslatex(150); zaxis.settranslatey(-150); zaxis.settranslatez(150); xaxis.setmaterial(redmaterial); yaxis.setmaterial(greenmaterial); zaxis.setmaterial(bluematerial); axisgroup.getchildren().addall(xaxis, yaxis, zaxis); world.getchildren().addall(axisgroup); } private void buildchart() { final phongmaterial whitematerial = new phongmaterial(); whitematerial.setdiffusecolor(color.white); whitematerial.setspecularcolor(color.lightblue); float h = 200; // height float s = 200; // side trianglemesh pyramidmesh = new trianglemesh(); pyramidmesh.gettexcoords().addall(0,0); pyramidmesh.getpoints().addall( 0, 0, 0, // point 0 - top 0, h, -s/2, // point 1 - front -s/2, h, 0, // point 2 - left s/2, h, 0, // point 3 - 0, h, s/2 // point 4 - right ); pyramidmesh.getfaces().addall( 0,0, 2,0, 1,0, // front left face 0,0, 1,0, 3,0, // front right face 0,0, 3,0, 4,0, // right face 0,0, 4,0, 2,0, // left face 4,0, 1,0, 2,0, // bottom rear face 4,0, 3,0, 1,0 // bottom front face ); meshview pyramid = new meshview(pyramidmesh); pyramid.setdrawmode(drawmode.fill); pyramid.setmaterial(whitematerial); pyramid.settranslatey(-h); world.getchildren().addall(pyramid); } private void handlemouse(scene scene, final node root) { scene.setonmousepressed(new eventhandler<mouseevent>() { @override public void handle(mouseevent me) { mouseposx = me.getscenex(); mouseposy = me.getsceney(); mouseoldx = me.getscenex(); mouseoldy = me.getsceney(); } }); scene.setonmousedragged(new eventhandler<mouseevent>() { @override public void handle(mouseevent me) { mouseoldx = mouseposx; mouseoldy = mouseposy; mouseposx = me.getscenex(); mouseposy = me.getsceney(); mousedeltax = (mouseposx - mouseoldx); mousedeltay = (mouseposy - mouseoldy); double modifier = 1.0; double modifierfactor = 0.1; if (me.iscontroldown()) { modifier = 0.1; } if (me.isshiftdown()) { modifier = 10.0; } if (me.isprimarybuttondown()) { cameraxform.ry.setangle(cameraxform.ry.getangle() - mousedeltax * modifierfactor * modifier * 2.0); // + cameraxform.rx.setangle(cameraxform.rx.getangle() + mousedeltay * modifierfactor * modifier * 2.0); // - } else if (me.issecondarybuttondown()) { double z = camera.gettranslatez(); double newz = z + mousedeltax * modifierfactor * modifier; camera.settranslatez(newz); } else if (me.ismiddlebuttondown()) { cameraxform2.t.setx(cameraxform2.t.getx() + mousedeltax * modifierfactor * modifier * 0.3); // - cameraxform2.t.sety(cameraxform2.t.gety() + mousedeltay * modifierfactor * modifier * 0.3); // - } } }); } private void handlekeyboard(scene scene, final node root) { final boolean movecamera = true; scene.setonkeypressed(new eventhandler<keyevent>() { @override public void handle(keyevent event) { duration currenttime; switch (event.getcode()) { case z: if (event.isshiftdown()) { cameraxform.ry.setangle(0.0); cameraxform.rx.setangle(0.0); camera.settranslatez(-300.0); } cameraxform2.t.setx(0.0); cameraxform2.t.sety(0.0); break; case x: if (event.iscontroldown()) { if (axisgroup.isvisible()) { axisgroup.setvisible(false); } else { axisgroup.setvisible(true); } } break; case s: if (event.iscontroldown()) { if (moleculegroup.isvisible()) { moleculegroup.setvisible(false); } else { moleculegroup.setvisible(true); } } break; case space: if (timelineplaying) { timeline.pause(); timelineplaying = false; } else { timeline.play(); timelineplaying = true; } break; case up: if (event.iscontroldown() && event.isshiftdown()) { cameraxform2.t.sety(cameraxform2.t.gety() - 10.0 * control_multiplier); } else if (event.isaltdown() && event.isshiftdown()) { cameraxform.rx.setangle(cameraxform.rx.getangle() - 10.0 * alt_multiplier); } else if (event.iscontroldown()) { cameraxform2.t.sety(cameraxform2.t.gety() - 1.0 * control_multiplier); } else if (event.isaltdown()) { cameraxform.rx.setangle(cameraxform.rx.getangle() - 2.0 * alt_multiplier); } else if (event.isshiftdown()) { double z = camera.gettranslatez(); double newz = z + 5.0 * shift_multiplier; camera.settranslatez(newz); } break; case down: if (event.iscontroldown() && event.isshiftdown()) { cameraxform2.t.sety(cameraxform2.t.gety() + 10.0 * control_multiplier); } else if (event.isaltdown() && event.isshiftdown()) { cameraxform.rx.setangle(cameraxform.rx.getangle() + 10.0 * alt_multiplier); } else if (event.iscontroldown()) { cameraxform2.t.sety(cameraxform2.t.gety() + 1.0 * control_multiplier); } else if (event.isaltdown()) { cameraxform.rx.setangle(cameraxform.rx.getangle() + 2.0 * alt_multiplier); } else if (event.isshiftdown()) { double z = camera.gettranslatez(); double newz = z - 5.0 * shift_multiplier; camera.settranslatez(newz); } break; case right: if (event.iscontroldown() && event.isshiftdown()) { cameraxform2.t.setx(cameraxform2.t.getx() + 10.0 * control_multiplier); } else if (event.isaltdown() && event.isshiftdown()) { cameraxform.ry.setangle(cameraxform.ry.getangle() - 10.0 * alt_multiplier); } else if (event.iscontroldown()) { cameraxform2.t.setx(cameraxform2.t.getx() + 1.0 * control_multiplier); } else if (event.isaltdown()) { cameraxform.ry.setangle(cameraxform.ry.getangle() - 2.0 * alt_multiplier); } break; case left: if (event.iscontroldown() && event.isshiftdown()) { cameraxform2.t.setx(cameraxform2.t.getx() - 10.0 * control_multiplier); } else if (event.isaltdown() && event.isshiftdown()) { cameraxform.ry.setangle(cameraxform.ry.getangle() + 10.0 * alt_multiplier); // - } else if (event.iscontroldown()) { cameraxform2.t.setx(cameraxform2.t.getx() - 1.0 * control_multiplier); } else if (event.isaltdown()) { cameraxform.ry.setangle(cameraxform.ry.getangle() + 2.0 * alt_multiplier); // - } break; } } }); } @override public void start(stage primarystage) { buildscene(); buildcamera(); buildaxes(); buildchart(); scene scene = new scene(root, 1600, 900, true); scene.setfill(color.grey); handlekeyboard(scene, world); handlemouse(scene, world); primarystage.setscene(scene); primarystage.show(); scene.setcamera(camera); } /** * main() method ignored in correctly deployed javafx application. * main() serves fallback in case application can not * launched through deployment artifacts, e.g., in ides limited fx * support. netbeans ignores main(). * * @param args command line arguments */ public static void main(string[] args) { system.setproperty("prism.dirtyopts", "false"); launch(args); } public static class xform extends group { public enum rotateorder { xyz, xzy, yxz, yzx, zxy, zyx } public translate t = new translate(); public translate p = new translate(); public translate ip = new translate(); 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); } public scale s = new scale(); public xform() { super(); gettransforms().addall(t, rz, ry, rx, s); } public xform(rotateorder rotateorder) { super(); // choose order of rotations based on rotateorder switch (rotateorder) { case xyz: gettransforms().addall(t, p, rz, ry, rx, s, ip); break; case xzy: gettransforms().addall(t, p, ry, rz, rx, s, ip); break; case yxz: gettransforms().addall(t, p, rz, rx, ry, s, ip); break; case yzx: gettransforms().addall(t, p, rx, rz, ry, s, ip); // camera break; case zxy: gettransforms().addall(t, p, ry, rx, rz, s, ip); break; case zyx: gettransforms().addall(t, p, rx, ry, rz, s, ip); break; } } public void settranslate(double x, double y, double z) { t.setx(x); t.sety(y); t.setz(z); } public void settranslate(double x, double y) { t.setx(x); t.sety(y); } // cannot override these methods final: // public void settranslatex(double x) { t.setx(x); } // public void settranslatey(double y) { t.sety(y); } // public void settranslatez(double z) { t.setz(z); } // use these methods instead: public void settx(double x) { t.setx(x); } public void setty(double y) { t.sety(y); } public void settz(double z) { t.setz(z); } public void setrotate(double x, double y, double z) { rx.setangle(x); ry.setangle(y); rz.setangle(z); } public void setrotatex(double x) { rx.setangle(x); } public void setrotatey(double y) { ry.setangle(y); } public void setrotatez(double z) { rz.setangle(z); } public void setrx(double x) { rx.setangle(x); } public void setry(double y) { ry.setangle(y); } public void setrz(double z) { rz.setangle(z); } public void setscale(double scalefactor) { s.setx(scalefactor); s.sety(scalefactor); s.setz(scalefactor); } public void setscale(double x, double y, double z) { s.setx(x); s.sety(y); s.setz(z); } // cannot override these methods final: // public void setscalex(double x) { s.setx(x); } // public void setscaley(double y) { s.sety(y); } // public void setscalez(double z) { s.setz(z); } // use these methods instead: public void setsx(double x) { s.setx(x); } public void setsy(double y) { s.sety(y); } public void setsz(double z) { s.setz(z); } public void setpivot(double x, double y, double z) { p.setx(x); p.sety(y); p.setz(z); ip.setx(-x); ip.sety(-y); ip.setz(-z); } public void reset() { t.setx(0.0); t.sety(0.0); t.setz(0.0); rx.setangle(0.0); ry.setangle(0.0); rz.setangle(0.0); s.setx(1.0); s.sety(1.0); s.setz(1.0); p.setx(0.0); p.sety(0.0); p.setz(0.0); ip.setx(0.0); ip.sety(0.0); ip.setz(0.0); } public void resettsp() { t.setx(0.0); t.sety(0.0); t.setz(0.0); s.setx(1.0); s.sety(1.0); s.setz(1.0); p.setx(0.0); p.sety(0.0); p.setz(0.0); ip.setx(0.0); ip.sety(0.0); ip.setz(0.0); } } }
the chart should e. g. this:
or this:
in end should possible display e. g. the result of perlin noise, instead of perlin noise value being color value, it's height value.
thank help!
thanks nwdx's answer managed create useful. it's not complete chart application , hope more know how can provide better answer, i'll post result nontheless.
you can use mouse dragging rotation , mouse wheel zooming. example shows perlin noise graph diffuse map used on mesh.
the core isn't code. it's turning 2d-array mesh:
// perlin noise float[][] noisearray = createnoise( size); // mesh trianglemesh mesh = new trianglemesh(); // create points x/z float amplification = 100; // amplification of noise (int x = 0; x < size; x++) { (int z = 0; z < size; z++) { mesh.getpoints().addall(x, noisearray[x][z] * amplification, z); } } // texture int length = size; float total = length; (float x = 0; x < length - 1; x++) { (float y = 0; y < length - 1; y++) { float x0 = x / total; float y0 = y / total; float x1 = (x + 1) / total; float y1 = (y + 1) / total; mesh.gettexcoords().addall( // x0, y0, // 0, top-left x0, y1, // 1, bottom-left x1, y1, // 2, top-right x1, y1 // 3, bottom-right ); } } // faces (int x = 0; x < length - 1; x++) { (int z = 0; z < length - 1; z++) { int tl = x * length + z; // top-left int bl = x * length + z + 1; // bottom-left int tr = (x + 1) * length + z; // top-right int br = (x + 1) * length + z + 1; // bottom-right int offset = (x * (length - 1) + z ) * 8 / 2; // div 2 because have u , v in 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); } }
if has better algorithm, please share it. don't mind if reuse code.
the full working example:
import java.util.arraylist; import java.util.list; import javafx.application.application; import javafx.event.eventhandler; import javafx.geometry.point3d; import javafx.scene.depthtest; import javafx.scene.group; import javafx.scene.perspectivecamera; import javafx.scene.scene; import javafx.scene.sceneantialiasing; import javafx.scene.canvas.canvas; import javafx.scene.image.image; import javafx.scene.image.imageview; import javafx.scene.image.pixelwriter; import javafx.scene.image.writableimage; import javafx.scene.input.scrollevent; import javafx.scene.layout.pane; import javafx.scene.layout.stackpane; import javafx.scene.paint.color; import javafx.scene.paint.cyclemethod; import javafx.scene.paint.lineargradient; import javafx.scene.paint.paint; import javafx.scene.paint.phongmaterial; import javafx.scene.paint.stop; import javafx.scene.shape.cullface; import javafx.scene.shape.drawmode; import javafx.scene.shape.line; import javafx.scene.shape.meshview; import javafx.scene.shape.rectangle; import javafx.scene.shape.trianglemesh; import javafx.scene.transform.rotate; import javafx.stage.stage; public class chart3ddemo extends application { // size of graph int size = 400; // variables mouse interaction private double mouseposx, mouseposy; private double mouseoldx, mouseoldy; private final rotate rotatex = new rotate(20, rotate.x_axis); private final rotate rotatey = new rotate(-45, rotate.y_axis); @override public void start(stage primarystage) { // create axis walls group cube = createcube(size); // initial cube rotation cube.gettransforms().addall(rotatex, rotatey); // add objects scene stackpane root = new stackpane(); root.getchildren().add(cube); // perlin noise float[][] noisearray = createnoise( size); // mesh trianglemesh mesh = new trianglemesh(); // create points x/z float amplification = 100; // amplification of noise (int x = 0; x < size; x++) { (int z = 0; z < size; z++) { mesh.getpoints().addall(x, noisearray[x][z] * amplification, z); } } // texture int length = size; float total = length; (float x = 0; x < length - 1; x++) { (float y = 0; y < length - 1; y++) { float x0 = x / total; float y0 = y / total; float x1 = (x + 1) / total; float y1 = (y + 1) / total; mesh.gettexcoords().addall( // x0, y0, // 0, top-left x0, y1, // 1, bottom-left x1, y1, // 2, top-right x1, y1 // 3, bottom-right ); } } // faces (int x = 0; x < length - 1; x++) { (int z = 0; z < length - 1; z++) { int tl = x * length + z; // top-left int bl = x * length + z + 1; // bottom-left int tr = (x + 1) * length + z; // top-right int br = (x + 1) * length + z + 1; // bottom-right int offset = (x * (length - 1) + z ) * 8 / 2; // div 2 because have u , v in 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); } } // material image diffusemap = createimage(size, noisearray); phongmaterial material = new phongmaterial(); material.setdiffusemap(diffusemap); material.setspecularcolor(color.white); // mesh view meshview meshview = new meshview(mesh); meshview.settranslatex(-0.5 * size); meshview.settranslatez(-0.5 * size); meshview.setmaterial(material); meshview.setcullface(cullface.none); meshview.setdrawmode(drawmode.fill); meshview.setdepthtest(depthtest.enable); cube.getchildren().addall(meshview); // testing / debugging stuff: show diffuse map on chart imageview iv = new imageview(diffusemap); iv.settranslatex(-0.5 * size); iv.settranslatey(-0.10 * size); iv.setrotate(90); iv.setrotationaxis(new point3d(1, 0, 0)); cube.getchildren().add(iv); // scene scene scene = new scene(root, 1600, 900, true, sceneantialiasing.balanced); scene.setcamera(new perspectivecamera()); scene.setonmousepressed(me -> { mouseoldx = me.getscenex(); mouseoldy = me.getsceney(); }); scene.setonmousedragged(me -> { mouseposx = me.getscenex(); mouseposy = me.getsceney(); rotatex.setangle(rotatex.getangle() - (mouseposy - mouseoldy)); rotatey.setangle(rotatey.getangle() + (mouseposx - mouseoldx)); mouseoldx = mouseposx; mouseoldy = mouseposy; }); makezoomable(root); primarystage.setresizable(false); primarystage.setscene(scene); primarystage.show(); } /** * create texture uv mapping * @param size * @param noise * @return */ public image createimage(double size, float[][] noise) { int width = (int) size; int height = (int) size; writableimage wr = new writableimage(width, height); pixelwriter pw = wr.getpixelwriter(); (int x = 0; x < width; x++) { (int y = 0; y < height; y++) { float value = noise[x][y]; double gray = normalizevalue(value, -.5, .5, 0., 1.); gray = clamp(gray, 0, 1); color color = color.red.interpolate(color.yellow, gray); pw.setcolor(x, y, color); } } return wr; } /** * axis wall */ public static class axis extends pane { rectangle wall; public axis(double size) { // wall // first wall, lines => overlapping of lines on walls // works wall = new rectangle(size, size); getchildren().add(wall); // grid double ztranslate = 0; double linewidth = 1.0; color gridcolor = color.white; (int y = 0; y <= size; y += size / 10) { line line = new line(0, 0, size, 0); line.setstroke(gridcolor); line.setfill(gridcolor); line.settranslatey(y); line.settranslatez(ztranslate); line.setstrokewidth(linewidth); getchildren().addall(line); } (int x = 0; x <= size; x += size / 10) { line line = new line(0, 0, 0, size); line.setstroke(gridcolor); line.setfill(gridcolor); line.settranslatex(x); line.settranslatez(ztranslate); line.setstrokewidth(linewidth); getchildren().addall(line); } // labels // todo: reason text makes wall have offset // for( int y=0; y <= size; y+=size/10) { // // text text = new text( ""+y); // text.settranslatex(size + 10); // // text.settranslatey(y); // text.settranslatez(ztranslate); // // getchildren().addall(text); // // } } public void setfill(paint paint) { wall.setfill(paint); } } public void makezoomable(stackpane control) { final double max_scale = 20.0; final double min_scale = 0.1; control.addeventfilter(scrollevent.any, new eventhandler<scrollevent>() { @override public void handle(scrollevent event) { double delta = 1.2; double scale = control.getscalex(); if (event.getdeltay() < 0) { scale /= delta; } else { scale *= delta; } scale = clamp(scale, min_scale, max_scale); control.setscalex(scale); control.setscaley(scale); event.consume(); } }); } /** * create axis walls * @param size * @return */ private group createcube(int size) { group cube = new group(); // size of cube color color = color.darkcyan; list<axis> cubefaces = new arraylist<>(); axis r; // face r = new axis(size); r.setfill(color.derivecolor(0.0, 1.0, (1 - 0.5 * 1), 1.0)); r.settranslatex(-0.5 * size); r.settranslatey(-0.5 * size); r.settranslatez(0.5 * size); cubefaces.add(r); // bottom face r = new axis(size); r.setfill(color.derivecolor(0.0, 1.0, (1 - 0.4 * 1), 1.0)); r.settranslatex(-0.5 * size); r.settranslatey(0); r.setrotationaxis(rotate.x_axis); r.setrotate(90); cubefaces.add(r); // right face r = new axis(size); r.setfill(color.derivecolor(0.0, 1.0, (1 - 0.3 * 1), 1.0)); r.settranslatex(-1 * size); r.settranslatey(-0.5 * size); r.setrotationaxis(rotate.y_axis); r.setrotate(90); // cubefaces.add( r); // left face r = new axis(size); r.setfill(color.derivecolor(0.0, 1.0, (1 - 0.2 * 1), 1.0)); r.settranslatex(0); r.settranslatey(-0.5 * size); r.setrotationaxis(rotate.y_axis); r.setrotate(90); cubefaces.add(r); // top face r = new axis(size); r.setfill(color.derivecolor(0.0, 1.0, (1 - 0.1 * 1), 1.0)); r.settranslatex(-0.5 * size); r.settranslatey(-1 * size); r.setrotationaxis(rotate.x_axis); r.setrotate(90); // cubefaces.add( r); // front face r = new axis(size); r.setfill(color.derivecolor(0.0, 1.0, (1 - 0.1 * 1), 1.0)); r.settranslatex(-0.5 * size); r.settranslatey(-0.5 * size); r.settranslatez(-0.5 * size); // cubefaces.add( r); cube.getchildren().addall(cubefaces); return cube; } /** * create array of given size values of perlin noise * @param size * @return */ private float[][] createnoise( int size) { float[][] noisearray = new float[(int) size][(int) size]; (int x = 0; x < size; x++) { (int y = 0; y < size; y++) { double frequency = 10.0 / (double) size; double noise = improvednoise.noise(x * frequency, y * frequency, 0); noisearray[x][y] = (float) noise; } } return noisearray; } public static double normalizevalue(double value, double min, double max, double newmin, double newmax) { return (value - min) * (newmax - newmin) / (max - min) + newmin; } public static double clamp(double value, double min, double max) { if (double.compare(value, min) < 0) return min; if (double.compare(value, max) > 0) return max; return value; } /** * perlin noise generator * * // java reference implementation of improved noise - copyright 2002 ken perlin. * // http://mrl.nyu.edu/~perlin/paper445.pdf * // http://mrl.nyu.edu/~perlin/noise/ */ public final static class improvednoise { static public double noise(double x, double y, double z) { int x = (int)math.floor(x) & 255, // find unit cube y = (int)math.floor(y) & 255, // contains point. z = (int)math.floor(z) & 255; x -= math.floor(x); // find relative x,y,z y -= math.floor(y); // of point in cube. z -= math.floor(z); double u = fade(x), // compute fade curves v = fade(y), // each of x,y,z. w = fade(z); int = p[x ]+y, aa = p[a]+z, ab = p[a+1]+z, // hash coordinates of b = p[x+1]+y, ba = p[b]+z, bb = p[b+1]+z; // 8 cube corners, return lerp(w, lerp(v, lerp(u, grad(p[aa ], x , y , z ), // , add grad(p[ba ], x-1, y , z )), // blended lerp(u, grad(p[ab ], x , y-1, z ), // results grad(p[bb ], x-1, y-1, z ))),// 8 lerp(v, lerp(u, grad(p[aa+1], x , y , z-1 ), // corners grad(p[ba+1], x-1, y , z-1 )), // of cube lerp(u, grad(p[ab+1], x , y-1, z-1 ), grad(p[bb+1], x-1, y-1, z-1 )))); } static double fade(double t) { return t * t * t * (t * (t * 6 - 15) + 10); } static double lerp(double t, double a, double b) { return + t * (b - a); } static double grad(int hash, double x, double y, double z) { int h = hash & 15; // convert lo 4 bits of hash code double u = h<8 ? x : y, // 12 gradient directions. v = h<4 ? y : h==12||h==14 ? x : z; return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v); } static final int p[] = new int[512], permutation[] = { 151,160,137,91,90,15, 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 }; static { (int i=0; < 256 ; i++) p[256+i] = p[i] = permutation[i]; } } public static void main(string[] args) { launch(args); } }
screenshot:
Comments
Post a Comment