JavaFX 3d и панель инструментов - PullRequest
0 голосов
/ 09 мая 2018

Я столкнулся с некоторым странным поведением после добавления 3D-контента в мое приложение JavaFX, которое содержит панель инструментов.

Я создал небольшой проект, который повторяет эту проблему. В основном приложении я использую SubScene, но поведение абсолютно одинаковое. Вот как должно выглядеть приложение (без панели инструментов) enter image description here

После добавления этой единственной строки

ToolBar toolBar = new ToolBar();

Содержание становится таким enter image description here

Вот код

import java.util.*;

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.DepthTest;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.control.ToolBar;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.paint.PhongMaterial;
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;
import javafx.util.Pair;
import org.apache.commons.math3.analysis.interpolation.PiecewiseBicubicSplineInterpolatingFunction;

public class Main extends Application {

    double[] xMetricData = {1 * 1000, 10 * 1000, 20 * 1000, 30 * 1000, 40 * 1000};
    double[] yMetricData = {1 * 1000, 10 * 1000, 20 * 1000, 30 * 1000, 40 * 1000, 50 * 1000};
    double[][] fMetricData = {{25, 5, 10, 15, 30, 30}, {15, -5, 5, 15, 25, 25}, {35, 5, 15, 10, 15, 15}, {15, 25, 15, 25, 35, 35}, {20, 30, 25, 30, 40, 40}};

    int size = 50;
    int cubeSize = 100;

    // variables for 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(cubeSize);

        // initial cube rotation
        cube.getTransforms().addAll(rotateX, rotateY);

        //ToolBar toolBar = new ToolBar();

        // add objects to scene
        StackPane root = new StackPane();
        root.getChildren().add(cube);


        TriangleMesh mesh = new TriangleMesh();

        int xLength = 0, zLength = 0;
        for (int x = (int)xMetricData[0]; x < xMetricData[xMetricData.length - 1]; x+= 1000)
        {
            zLength = 0;
            for (int z = (int) yMetricData[0]; z < yMetricData[yMetricData.length - 1]; z+= 1000)
            {
                float y = (float) apacheInterpolation(x, z);

                //System.out.println(x +"," + z + ", " + y);
                mesh.getPoints().addAll(x / 1000.0f, y, z / 1000.0f);
                zLength += 1;
            }
            xLength += 1;
        }


        for (float x = 0; x < xLength - 1; x++)
        {
            for (float y = 0; y < zLength - 1; y++)
            {
                float x0 = x / xLength;
                float y0 = y / zLength;
                float x1 = (x + 1) / xLength;
                float y1 = (y + 1) / zLength;

                mesh.getTexCoords().addAll( //
                        x0, y0, // 0, top-left
                        x0, y1, // 1, bottom-left
                        x1, y1, // 2, top-right
                        x1, y1 // 3, bottom-right
                );
            }
        }

        // faces
        for (int x = 0; x < xLength - 1; x++)
        {
            for (int z = 0; z < zLength - 1; z++)
            {
                int tl = x * zLength + z; // top-left
                int bl = x * zLength + z + 1; // bottom-left

                int tr = (x + 1) * zLength + z; // top-right
                int br = (x + 1) * zLength + z + 1; // bottom-right

                int offset = (x * (zLength - 1) + z ) * 8 / 2; // div 2 because we have u AND v in the 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);
                System.out.println(tl +", " + tr);
            }
        }

        System.out.println(mesh.getPoints().size());

        PhongMaterial material = new PhongMaterial();
        //material.setDiffuseMap(diffuseMap);
        material.setSpecularColor(Color.BLACK);


        MeshView meshView = new MeshView(mesh);
//        meshView.setTranslateX(-0.5 * size);
//        meshView.setTranslateZ(-0.5 * size);
        meshView.setMaterial(material);
        meshView.setCullFace(CullFace.BACK);
        meshView.setDrawMode(DrawMode.FILL);
        meshView.setDepthTest(DepthTest.ENABLE);

        cube.getChildren().addAll(meshView);

        // scene
        Scene scene = new Scene(root, 800, 700, 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(true);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static double getFirmwareOffset(List<int[]> matrix, int x, int y)
    {
        Comparator<Pair<Integer, Long>> idComparator = (p1, p2) -> (int) (p2.getKey() - p1.getValue());

        Queue<Pair<Integer, Long>> integerPriorityQueue = new PriorityQueue<>(5, idComparator);

        long minDistance = -1;
        long sumDistance = 0;
        for (int i = 0; i < matrix.size(); ++i)
        {
            long difX = (long)Math.abs(matrix.get(i)[0] - x);
            long difY = (long)Math.abs(matrix.get(i)[1] - y);

            // in case accuracy is not enough we coudl go back to the traditional formula
            //long distance = sqrt(difX * difX + difY * difY);
            long distance = difX + difY;

            if (distance == 0)
                return matrix.get(i)[2];

            if (integerPriorityQueue.size() < 15)
            {
                integerPriorityQueue.add(new Pair<>(i, distance));
                sumDistance += distance;
            }
            else
            {
                Pair<Integer, Long> top = integerPriorityQueue.peek();
                if (top.getValue() > distance)
                {
                    integerPriorityQueue.poll();
                    integerPriorityQueue.add(new Pair<>(i, distance));

                    sumDistance -= top.getValue();
                    sumDistance += distance;
                }
            }
            if (minDistance == -1 || distance < minDistance)
                minDistance = distance;
        }

        double sumRatios = 0;

        List<Pair<Integer, Double>> ratios = new ArrayList<>();

        while(integerPriorityQueue.size() > 0)
        {
            Pair<Integer, Long> entry = integerPriorityQueue.poll();

            double ratio = (double)sumDistance / entry.getValue();
            ratios.add(new Pair<>(entry.getKey(), ratio));
            sumRatios += ratio;
        }

        if (sumRatios == 0)
            return 0;


        double offset = 0;
        for (int i = 0; i < ratios.size(); ++i)
        {
            Pair<Integer, Double> entry = ratios.get(i);
            offset += (matrix.get(entry.getKey())[2] * entry.getValue() / sumRatios);
        }

        return offset;
    }

        /**
         * Axis wall
         */
    public static class Axis extends Pane {

        Rectangle wall;

        public Axis(double size) {

            // wall
            // first the wall, then the lines => overlapping of lines over walls
            // works
            wall = new Rectangle(size, size);
            getChildren().add(wall);

            // grid
            double zTranslate = 0;
            double lineWidth = 1.0;
            Color gridColor = Color.WHITE;

            for (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);

            }

            for (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: for some reason the text makes the wall have an 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 = 4;

        control.setScaleX(MIN_SCALE);
        control.setScaleY(MIN_SCALE);

        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 the cube
        Color color = Color.DARKCYAN;

        List<Axis> cubeFaces = new ArrayList<>();
        Axis r;

        // back 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;
    }

    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;
    }

    public static void main(String[] args) {
        launch(args);
    }

    public double apacheInterpolation(float px, float py)
    {
        PiecewiseBicubicSplineInterpolatingFunction t = new PiecewiseBicubicSplineInterpolatingFunction(xMetricData, yMetricData, fMetricData);
        return t.value(px, py);
    }
}

UPD: после небольшого расследования любой контроль приводит к такому поведению.

Ответы [ 2 ]

0 голосов
/ 09 мая 2018

Используйте SubScene и установите nearClip камеры на минимально возможное значение:

SubScene scene = new SubScene(root, 800, 700, true, SceneAntialiasing.BALANCED);
PerspectiveCamera camera = new PerspectiveCamera();
camera.setNearClip(Double.MIN_VALUE);
scene.setCamera(camera);

...

BorderPane sRoot = new BorderPane(scene);
sRoot.setTop(new ToolBar());
Scene mainScene = new Scene(sRoot);

primaryStage.setResizable(true);
primaryStage.setScene(mainScene);
primaryStage.show();
0 голосов
/ 09 мая 2018

Как сказал @Fabian, я продолжал экспериментировать с SubScene. Хитрость заключается в том, чтобы не использовать какие-либо панели при добавлении 3d-элементов в SubScene.

...