2017-01-26 16 views
1

Looking this post, я попытался реализовать в javaFX, со многими трудностями, диаграмму рассеяния 3D, где сетка - это мои оси x, y и z, а сферы - мои очки.Диаграмма рассеяния 3D JavaFX: как показать легенду и измерения вблизи оси

Как можно разместить легенду, метки осей и номера диапазонов вдоль оси? Я могу использовать только javaFX без внешней библиотеки. Я в отчаянии .. Я пытаюсь на несколько дней .. без результатов Пожалуйста: помогите мне Спасибо.

Код

import java.util.ArrayList; 
import java.util.List; 
import java.util.Random; 
import javafx.application.Application; 
import javafx.event.EventHandler; 
import javafx.scene.Group; 
import javafx.scene.PerspectiveCamera; 
import javafx.scene.Scene; 
import javafx.scene.SceneAntialiasing; 
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.Line; 
import javafx.scene.shape.Rectangle; 
import javafx.scene.shape.Sphere; 
import javafx.scene.transform.Rotate; 
import javafx.stage.Stage; 

public class GraphingData extends Application { 

    private static Random rnd = new Random(); 

    // size of graph 
    int graphSize = 400; 

    // variables for mouse interaction 
    private double mousePosX, mousePosY; 
    private double mouseOldX, mouseOldY; 

    private final Rotate rotateX = new Rotate(150, Rotate.X_AXIS); 
    private final Rotate rotateY = new Rotate(120, Rotate.Y_AXIS); 

    @Override 
    public void start(Stage primaryStage) { 

     // create axis walls 
     Group grid = createGrid(graphSize); 

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

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

     root.setStyle("-fx-border-color: red;"); 
     // create bars 
     double gridSizeHalf = graphSize/2; 
     double size = 30; 

     //Drawing a Sphere 
     Sphere sphere = new Sphere(); 

     //Setting the properties of the Sphere 
     sphere.setRadius(10.0); 

     sphere.setTranslateX(-50); 
     sphere.setTranslateY(-50);  

     //Preparing the phong material of type specular color 
     PhongMaterial material6 = new PhongMaterial(); 

     //setting the specular color map to the material 
     material6.setDiffuseColor(Color.GREEN); 

     sphere.setMaterial(material6); 

     grid.getChildren().addAll(sphere); 

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

    } 

    /** 
    * 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.RED; 

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

      } 

     } 

     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 createGrid(int size) { 

     Group cube = new Group(); 

     // size of the cube 
     Color color = Color.LIGHTGRAY; 

     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 Color randomColor() { 
     return Color.rgb(rnd.nextInt(255), rnd.nextInt(255), rnd.nextInt(255)); 
    } 

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

Идентично для http: // stackoverflow.com/questions/41873073/javafx-3d-how-to-put-a-legend-axis-labels-and-numbers-вдоль оси? –

+0

@James_D мы вместе делаем проект. Для этого вопросы идентичны –

+0

@James_D вы можете нам помочь? –

ответ

1

Вот основная идея создать некоторые меры по осям. Он не готов к производству, но должен дать вам достаточно для начала.

private Group createGrid(int size) { 

    // existing code omitted... 

    cube.getChildren().addAll(cubeFaces); 


    double gridSizeHalf = size/2; 
    double labelOffset = 30 ; 
    double labelPos = gridSizeHalf - labelOffset ; 

    for (double coord = -gridSizeHalf ; coord < gridSizeHalf ; coord+=50) { 
     Text xLabel = new Text(coord, labelPos, String.format("%.0f", coord)); 
     xLabel.setTranslateZ(labelPos); 
     xLabel.setScaleX(-1); 
     Text yLabel = new Text(labelPos, coord, String.format("%.0f", coord)); 
     yLabel.setTranslateZ(labelPos); 
     yLabel.setScaleX(-1); 
     Text zLabel = new Text(labelPos, labelPos, String.format("%.0f", coord)); 
     zLabel.setTranslateZ(coord); 
     cube.getChildren().addAll(xLabel, yLabel, zLabel); 
     zLabel.setScaleX(-1); 
    } 

    return cube; 
} 

я бы просто поместить легенду за пределами графика, который бы просто панель 2D сетки не вращается ...

+0

Спасибо вам за помощь! Это было очень полезно. Я попытался разместить 2D-легенду и [прочитать дискуссию] (http://stackoverflow.com/questions/28628702/javafx-2d-part-in-3d-application). К сожалению, это один из моих первых projct в 3D. Это ссылка на мои тесты отказов: [link1] (http://pastebin.com/vG16c4QV) и [ink2] (http://pastebin.com/tU3UMhQX). Как я могу редактировать и исправлять? –

+0

Перевод этикеток, как это будет переводить в 3D-пространстве 2D-метку, которая будет выглядеть правильно, пока вы не переместите камеру. Предполагая, что вам нужна диаграмма рассеяния, которая не перемещается и не поворачивается, это будет хорошо. Иными словами, вам нужно будет автоматически преобразовывать двумерные метки всякий раз, когда вы перемещаете камеру. (т.е. ... обработчик мыши). – Birdasaur

+0

@Birdasaur Да, справедливая точка. Если я правильно помню, что более ранние версии «Camera» не могли быть перемещены (это был не подкласс «Node», и вы не могли добавить его на график сцены): вы просто установили бы камеру на сцену и если вы хотите, чтобы что-то перемещалось относительно него, вы применяете преобразования к корню сцены (или ее частям). Также не было класса SubScene. Большинство моих экспериментов с 3D в JavaFX были до того, как камера могла быть перемещена, поэтому вы были вынуждены самостоятельно преобразовывать разные части графика сцены. Но ваш подход выглядит корректно для текущих версий. –

0

Я знаю, этот вопрос становится старым, но 2D этикетки в JavaFX 3D сцены тема, которая появляется много, и я никогда не видел, чтобы она отвечала «правильно».

Перевод этикеток, как в ответе James_D, переведёт в 3D-пространство 2D-метку, которая будет выглядеть правильно, пока вы не переместите камеру. Предполагая, что вам нужна диаграмма рассеяния, которая не перемещается и не поворачивается, это будет хорошо. Иными словами, вам нужно будет автоматически преобразовывать двумерные метки всякий раз, когда вы перемещаете камеру. (т.е. ... обработчик мыши). Вы могли бы удалить свою диаграмму рассеяния и каждый раз считывать все это на сцену, но это будет убийство в вашей памяти кучи и не будет выполнимо для наборов данных любого реального полезного размера.

Правильный способ сделать это - использовать текстовые рендеринги OpenGL или DirectDraw, которые перерисовывают метки на каждом проходе цикла визуализации, но JavaFX 3D не дает вам доступа (в настоящее время). Таким образом, «правильный путь в JavaFX» заключается в том, чтобы плавать 2D-метки поверх 3D-подсети, а затем переводить их всякий раз, когда камера перемещается. Это требует, чтобы вы преобразовали трехмерную координатную проекцию 3D-местоположения, в которой вы хотите, чтобы метка была направлена ​​на 2D-экран.

родовым управлять 2D метки, связанные с Point3D в JavaFX 3D вам нужно сделать преобразование по следующим:

Point3D coordinates = node.localToScene(javafx.geometry.Point3D.ZERO); 
SubScene oldSubScene = NodeHelper.getSubScene(node); 
coordinates = SceneUtils.subSceneToScene(oldSubScene, coordinates); 
double x = coordinates.getX(); 
double y = coordinates.getY(); 
label.getTransforms().setAll(new Translate(x, y)); 

Если узел некоторые фактические 3D объект уже в 3D subscene. Для моих приложений я просто использую Сфера чрезвычайно маленького размера, которую она не может видеть. Если бы вы следовали примеру James_D, вы могли бы перевести сферу (ы) в те же места, где вы переводили метки основной оси. Этикетка представляет собой стандартную метку JavaFX 2D, которую вы добавляете к своей сцене ... обычно через StackPane, так что метки плавают поверх 3D-подсети.

Теперь, когда камера перемещается/вращается, это приводит к тому, что это преобразование будет вызываться, которое сместит метку на 2D-слое. Без прямого доступа к базовым вызовам GL или DD это практически единственный способ сделать что-то подобное в JavaFX 3D, но он работает очень хорошо.

Работает video example. Вот open source example реализации простой версии плавающих 2D-меток. (Предупреждение, я являюсь автором автора, не пытаясь продвигать библиотеку.)

 Смежные вопросы

  • Нет связанных вопросов^_^