Изменение размера и символа точек точечной диаграммы в ScalaFX - PullRequest
0 голосов
/ 09 февраля 2019

Я хочу создать программу линейной регрессии, которая визуализирует данные для пользователя.Я использую EJML для расчетов и ScalaFX для внешнего интерфейса.Все идет хорошо, но когда я строю данные, используя точечную диаграмму, линия, нарисованная из данных, устанавливается в виде прямоугольников, которые покрывают исходные точки данных.Я хотел бы знать, как я могу изменить размер, форму, прозрачность и т. Д. Нанесенных точек.

Почти все руководства по JavaFX говорят, что я должен изменить файл CSS (который не существует автоматически), чтобы стилизовать мою диаграмму.Я не знаю, как это сделать в ScalaFX или даже это возможно сделать так.Мой результат поиска всех возможных уроков оказался бесплодным.

import scalafx.application.JFXApp
import scalafx.scene.Scene
import scalafx.scene.chart.ScatterChart
import scalafx.collections.ObservableBuffer
import scalafx.scene.chart.NumberAxis
import scalafx.scene.chart.XYChart
import scalafx.scene.shape.Line
import org.ejml.simple.SimpleMatrix
import scala.math.pow
import scala.collection.mutable.Buffer


object Plotting extends JFXApp {
  /*
   * Below are some arbitrary x and y values for a regression line
   */
  val xValues = Array(Array(1.0, 1.0, 1.0, 1.0, 1.0, 1.0), Array(14.0, 19.0, 22.0, 26.0, 31.0, 43.0))
  val yValues = Array(Array(51.0, 57.0, 66.0, 71.0, 72.0, 84.0))
  val temp    = yValues.flatten
  val wrapper = xValues(1).zip(temp)
  /*
   * In the lines before stage what happens is that matrices for the x and y values are created, coefficients
   * for the regression line are calculated with matrix operations and (x, y) points are calculated for the 
   * regression line.
   */
  val X       = new SimpleMatrix(xValues).transpose
  val Y       = new SimpleMatrix(yValues).transpose
  val secondX = new SimpleMatrix(xValues(0).size, 2)
  for (i <- 0 until xValues(0).size) {
    secondX.set(i, 0, xValues(0)(i))
    secondX.set(i, 1, xValues(1)(i))
  }
  val invertedSecondX = secondX.pseudoInverse()
  val B = invertedSecondX.mult(Y)
  val graphPoints = Buffer[(Double, Double)]()
  for (i <- 0 to xValues(1).max.toInt) {
    graphPoints.append((i.toDouble, B.get(0, 0) + i * B.get(1, 0)))
  }

  stage = new JFXApp.PrimaryStage {
    title = "Demo"
    scene = new Scene(400, 400) {
      val xAxis = NumberAxis()
      val yAxis = NumberAxis()
      val pData = XYChart.Series[Number, Number](
        "Data",
        ObservableBuffer(wrapper.map(z => XYChart.Data[Number, Number](z._1, z._2)): _*))
      val graph = XYChart.Series[Number, Number](
        "RegressionLine",
        ObservableBuffer(graphPoints.map(z => XYChart.Data[Number, Number](z._1, z._2)): _*))
      val plot = new ScatterChart(xAxis, yAxis, ObservableBuffer(graph, pData))
      root     = plot
    }
  }
}

Ответы [ 2 ]

0 голосов
/ 28 марта 2019

Я почти не смею добавить что-нибудь в решение Майка Алленса (которое, как всегда, очень хорошо), но у меня это не сработало, потому что я не мог заставить свою scala найти и / или обработать файл .cssфайл.Я бы сделал это таким образом, если это возможно, но я просто не мог заставить его работать.Вот что я придумал:

Предположим, у меня есть данные для отображения:

val xyExampleData: ObservableBuffer[(Double, Double)] = ObservableBuffer(Seq(
    1 -> 1,
    2 -> 4,
    3 -> 9))

Затем я преобразую это в Серию для LineChart:

val DataPoints = ObservableBuffer(xyExampleData map { case (x, y) => XYChart.Data[Number, Number](x, y) })
val PointsToDisplay = XYChart.Series[Number, Number]("Points", DataPoints)

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

 val lineChartBuffer = ObservableBuffer(PointsToDisplay, ...)

и, наконец, я создаю свой lineChart, который я называю (с недостатком творчества) lineChart:

val lineChart = new LineChart(xAxis, yAxis, lineChartBuffer) {...}

Теперь можно легко перекрасить линии между точками данных с помощью:

lineChart.lookup(".default-color0.chart-series-line").setStyle("-fx-stroke: blue;") 

Это изменит цвет линии набора данных FIRST в LineChartBuffer.Если вы хотите изменить свойства линии для секунды, которую вы называете

lineChart.lookup(".default-color1.chart-series-line")...

Также существует "-fx-stroke-width: 3px;"установить с помощью строки.

"- fx-opacity: 0.1;"

"- fx-stroke-dash-array: 10;"

-fx-fill: blue; "

также полезны, но не вызывайте вышеуказанную строку повторно, потому что второй вызов переопределит первый. Вместо этого объедините все строки в одну:

lineChart.lookup(".default-color0.chart-series-line").setStyle("-fx-stroke: blue;-fx-opacity: 0.1;-fx-stroke-dash-array: 10;-fx-fill: blue;")

Теперь для форматирования символов в каждой точке данных:

, к сожалению, кажется, чтоне иначе, как отдельно отформатировать каждый символ:

lineChart.lookupAll(".default-color0.chart-line-symbol").asScala foreach { node => node.setStyle("-fx-background-color: blue, white;") }

, чтобы запустить его, вам нужно import scala.collection.JavaConverters._ для преобразования из набора Java в набор Scala.

Можно такжесделайте невидимыми все точки данных только из одного набора данных, например:

lineChart.lookupAll(".default-color1.chart-line-symbol").asScala foreach { node => node.setVisible(false) }

Сказать, что это хорошее решение, было бы преувеличено, и у него есть большой недостаток: вам придется перекрашивать или переформатироватькаждый символ после добавления нового Datapoint к одному из рядов в LineChartBuffer. Если вы этого не сделаете, новые символы будут иметь стандартные цвета и настройки. Линии остаются, те, которые перекрашиваются, я не могу сказать, почему.

Но хорошая сторона этого, одинвпоследствии всегда можно переформатировать кривые в линейной диаграмме, как это!

0 голосов
/ 10 февраля 2019

Это, конечно, не так хорошо документировано, как могло бы быть ...: - (

Таблицы стилей обычно помещаются в каталог ресурсов вашего проекта. Если вы используете SBT (рекомендуется), это будет src/main/resources.

. В этом примере я добавил в этот каталог таблицу стилей MyCharts.css со следующим содержимым:

/* Blue semi-transparent 4-pointed star, using SVG path. */
.default-color0.chart-symbol {
    -fx-background-color: blue;
    -fx-scale-shape: true;
    -fx-shape: "M 0.0 10.0 L 3.0 3.0 L 10.0 0.0 L 3.0 -3.0 L 0.0 -10.0 L -3.0 -3.0 L -10.0 0.0 L -3.0 3.0 Z ";
    -fx-opacity: 0.5;
}

/* Default shape is a rectangle. Here, we round it to become a red circle with a white
 * center. Change the radius to control the size.
 */
.default-color1.chart-symbol {
    -fx-background-color: red, white;
    -fx-background-insets: 0, 2;
    -fx-background-radius: 3px;
    -fx-padding: 3px;
}

color0будет использоваться для первого ряда данных (линия регрессии), color1 для второго (ваши точечные данные). Все остальные ряды используют стиль по умолчанию, JavaFX стиль.

(Дляболее подробную информацию об использовании масштабируемой векторной графики ( SVG ) для определения пользовательских фигур см. в соответствующем разделе SVG спецификации .)

Чтобы эта таблица стилей использовалась ScalaFX ( JavaFX ), у вас есть выбор вариантов. Чтобы они применялись глобально, добавьте его в основную сцену (это то, что я сделал ниже). Альтернативно, если каждый график нуждается в различном стиле, вы можетедобавить различные таблицы стилей в конкретные диаграммы.(Кстати, я также добавил стандарт, включающий импорт, поскольку это предотвращает многие проблемы JavaFX - ScalaFX преобразования элементов; в противном случае я не внес изменения в ваши источники.)

import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.scene.Scene
import scalafx.scene.chart.ScatterChart
import scalafx.collections.ObservableBuffer
import scalafx.scene.chart.NumberAxis
import scalafx.scene.chart.XYChart
import scalafx.scene.shape.Line
import org.ejml.simple.SimpleMatrix
import scala.math.pow
import scala.collection.mutable.Buffer

object Plotting extends JFXApp {
  /*
   * Below are some arbitrary x and y values for a regression line
   */
  val xValues = Array(Array(1.0, 1.0, 1.0, 1.0, 1.0, 1.0), Array(14.0, 19.0, 22.0, 26.0, 31.0, 43.0))
  val yValues = Array(Array(51.0, 57.0, 66.0, 71.0, 72.0, 84.0))
  val temp    = yValues.flatten
  val wrapper = xValues(1).zip(temp)
  /*
   * In the lines before stage what happens is that matrices for the x and y values are created, coefficients
   * for the regression line are calculated with matrix operations and (x, y) points are calculated for the 
   * regression line.
   */
  val X       = new SimpleMatrix(xValues).transpose
  val Y       = new SimpleMatrix(yValues).transpose
  val secondX = new SimpleMatrix(xValues(0).size, 2)
  for (i <- 0 until xValues(0).size) {
    secondX.set(i, 0, xValues(0)(i))
    secondX.set(i, 1, xValues(1)(i))
  }
  val invertedSecondX = secondX.pseudoInverse()
  val B = invertedSecondX.mult(Y)
  val graphPoints = Buffer[(Double, Double)]()
  for (i <- 0 to xValues(1).max.toInt) {
    graphPoints.append((i.toDouble, B.get(0, 0) + i * B.get(1, 0)))
  }

  stage = new JFXApp.PrimaryStage {
    title = "Demo"
    scene = new Scene(400, 400) {

      // Add our stylesheet.
      stylesheets.add("MyCharts.css")

      val xAxis = NumberAxis()
      val yAxis = NumberAxis()
      val pData = XYChart.Series[Number, Number](
        "Data",
        ObservableBuffer(wrapper.map(z => XYChart.Data[Number, Number](z._1, z._2)): _*))
      val graph = XYChart.Series[Number, Number](
        "RegressionLine",
        ObservableBuffer(graphPoints.map(z => XYChart.Data[Number, Number](z._1, z._2)): _*))
      val plot = new ScatterChart(xAxis, yAxis, ObservableBuffer(graph, pData))
      root     = plot
    }
  }
}

Для получения дополнительной информации о доступных параметрах форматирования CSS (изменение форм, цветов, прозрачности и т. Д.) См. Справочное руководство по CSS .

Результат выглядит так: Chart image, with customized points.

...