216 217 @Override protected void dataItemRemoved(final Data<X, Y> item, final Series<X, Y> series) { 218 final Node bar = item.getNode(); 219 220 if (bar != null) { 221 bar.focusTraversableProperty().unbind(); 222 } 223 224 if (shouldAnimate()) { 225 Timeline t = createDataRemoveTimeline(item, bar, series); 226 t.setOnFinished(event -> { 227 removeDataItemFromDisplay(series, item); 228 }); 229 t.play(); 230 } else { 231 processDataRemove(series, item); 232 removeDataItemFromDisplay(series, item); 233 } 234 } 235 236 /** @inheritDoc */ 237 @Override protected void dataItemChanged(Data<X, Y> item) { 238 double barVal; 239 double currentVal; 240 if (orientation == Orientation.VERTICAL) { 241 barVal = ((Number) item.getYValue()).doubleValue(); 242 currentVal = ((Number) getCurrentDisplayedYValue(item)).doubleValue(); 243 } else { 244 barVal = ((Number) item.getXValue()).doubleValue(); 245 currentVal = ((Number) getCurrentDisplayedXValue(item)).doubleValue(); 246 } 247 if (currentVal > 0 && barVal < 0) { // going from positive to negative 248 // add style class negative 249 item.getNode().getStyleClass().add("negative"); 250 } else if (currentVal < 0 && barVal > 0) { // going from negative to positive 251 // remove style class negative 252 item.getNode().getStyleClass().remove("negative"); 253 } 254 } 255 256 @Override protected void seriesChanged(ListChangeListener.Change<? extends Series> c) { 257 // Update style classes for all series lines and symbols 258 // Note: is there a more efficient way of doing this? 259 for (int i = 0; i < getDataSize(); i++) { 260 final Series<X,Y> series = getData().get(i); 261 for (int j=0; j<series.getData().size(); j++) { 262 Data<X,Y> item = series.getData().get(j); 263 Node bar = item.getNode(); 264 bar.getStyleClass().setAll("chart-bar", "series" + i, "data" + j, series.defaultColorStyleClass); 265 } 266 } 267 } 268 269 /** @inheritDoc */ 270 @Override protected void seriesAdded(Series<X, Y> series, int seriesIndex) { 271 // handle any data already in series 272 // create entry in the map 273 Map<String, List<Data<X, Y>>> categoryMap = new HashMap<String, List<Data<X, Y>>>(); 274 for (int j = 0; j < series.getData().size(); j++) { 275 Data<X, Y> item = series.getData().get(j); 276 Node bar = createBar(series, seriesIndex, item, j); 277 String category; 278 if (orientation == Orientation.VERTICAL) { 279 category = (String) item.getXValue(); 280 } else { 281 category = (String) item.getYValue(); 282 } 283 // list of two item positive and negative 284 List<Data<X, Y>> itemList = categoryMap.get(category) != null ? categoryMap.get(category) : new ArrayList<Data<X, Y>>(); 285 itemList.add(item); 286 categoryMap.put(category, itemList); 287 if (shouldAnimate()) { 288 animateDataAdd(item, bar); 289 } else { 319 FadeTransition ft = new FadeTransition(Duration.millis(700), bar); 320 ft.setFromValue(1); 321 ft.setToValue(0); 322 ft.setOnFinished(actionEvent -> { 323 processDataRemove(series, d); 324 bar.setOpacity(1.0); 325 }); 326 pt.getChildren().add(ft); 327 } 328 } 329 pt.play(); 330 } else { 331 for (Data<X, Y> d : series.getData()) { 332 processDataRemove(series, d); 333 } 334 removeSeriesFromDisplay(series); 335 requestChartLayout(); 336 } 337 } 338 339 /** @inheritDoc */ 340 @Override protected void updateAxisRange() { 341 // This override is necessary to update axis range based on cumulative Y value for the 342 // Y axis instead of the inherited way where the max value in the data range is used. 343 boolean categoryIsX = categoryAxis == getXAxis(); 344 if (categoryAxis.isAutoRanging()) { 345 List cData = new ArrayList(); 346 for (Series<X, Y> series : getData()) { 347 for (Data<X, Y> data : series.getData()) { 348 if (data != null) cData.add(categoryIsX ? data.getXValue() : data.getYValue()); 349 } 350 } 351 categoryAxis.invalidateRange(cData); 352 } 353 if (valueAxis.isAutoRanging()) { 354 List<Number> vData = new ArrayList<>(); 355 for (String category : categoryAxis.getAllDataCategories()) { 356 double totalXN = 0; 357 double totalXP = 0; 358 Iterator<Series<X, Y>> seriesIterator = getDisplayedSeriesIterator(); 359 while (seriesIterator.hasNext()) { 360 Series<X, Y> series = seriesIterator.next(); 361 for (final Data<X, Y> item : getDataItem(series, category)) { 362 if (item != null) { 363 boolean isNegative = item.getNode().getStyleClass().contains("negative"); 364 Number value = (Number) (categoryIsX ? item.getYValue() : item.getXValue()); 365 if (!isNegative) { 366 totalXP += valueAxis.toNumericValue(value); 367 } else { 368 totalXN += valueAxis.toNumericValue(value); 369 } 370 } 371 } 372 } 373 vData.add(totalXP); 374 vData.add(totalXN); 375 } 376 valueAxis.invalidateRange(vData); 377 } 378 } 379 380 /** @inheritDoc */ 381 @Override protected void layoutPlotChildren() { 382 double catSpace = categoryAxis.getCategorySpacing(); 383 // calculate bar spacing 384 final double availableBarSpace = catSpace - getCategoryGap(); 385 final double barWidth = availableBarSpace; 386 final double barOffset = -((catSpace - getCategoryGap()) / 2); 387 // update bar positions and sizes 388 for (String category : categoryAxis.getCategories()) { 389 double currentPositiveValue = 0; 390 double currentNegativeValue = 0; 391 Iterator<Series<X, Y>> seriesIterator = getDisplayedSeriesIterator(); 392 while (seriesIterator.hasNext()) { 393 Series<X, Y> series = seriesIterator.next(); 394 for (final Data<X, Y> item : getDataItem(series, category)) { 395 if (item != null) { 396 final Node bar = item.getNode(); 397 final double categoryPos; 398 final double valNumber; 399 final X xValue = getCurrentDisplayedXValue(item); 400 final Y yValue = getCurrentDisplayedYValue(item); | 216 217 @Override protected void dataItemRemoved(final Data<X, Y> item, final Series<X, Y> series) { 218 final Node bar = item.getNode(); 219 220 if (bar != null) { 221 bar.focusTraversableProperty().unbind(); 222 } 223 224 if (shouldAnimate()) { 225 Timeline t = createDataRemoveTimeline(item, bar, series); 226 t.setOnFinished(event -> { 227 removeDataItemFromDisplay(series, item); 228 }); 229 t.play(); 230 } else { 231 processDataRemove(series, item); 232 removeDataItemFromDisplay(series, item); 233 } 234 } 235 236 /** {@inheritDoc} */ 237 @Override protected void dataItemChanged(Data<X, Y> item) { 238 double barVal; 239 double currentVal; 240 if (orientation == Orientation.VERTICAL) { 241 barVal = ((Number) item.getYValue()).doubleValue(); 242 currentVal = ((Number) getCurrentDisplayedYValue(item)).doubleValue(); 243 } else { 244 barVal = ((Number) item.getXValue()).doubleValue(); 245 currentVal = ((Number) getCurrentDisplayedXValue(item)).doubleValue(); 246 } 247 if (currentVal > 0 && barVal < 0) { // going from positive to negative 248 // add style class negative 249 item.getNode().getStyleClass().add("negative"); 250 } else if (currentVal < 0 && barVal > 0) { // going from negative to positive 251 // remove style class negative 252 item.getNode().getStyleClass().remove("negative"); 253 } 254 } 255 256 @Override protected void seriesChanged(ListChangeListener.Change<? extends Series> c) { 257 // Update style classes for all series lines and symbols 258 // Note: is there a more efficient way of doing this? 259 for (int i = 0; i < getDataSize(); i++) { 260 final Series<X,Y> series = getData().get(i); 261 for (int j=0; j<series.getData().size(); j++) { 262 Data<X,Y> item = series.getData().get(j); 263 Node bar = item.getNode(); 264 bar.getStyleClass().setAll("chart-bar", "series" + i, "data" + j, series.defaultColorStyleClass); 265 } 266 } 267 } 268 269 /** {@inheritDoc} */ 270 @Override protected void seriesAdded(Series<X, Y> series, int seriesIndex) { 271 // handle any data already in series 272 // create entry in the map 273 Map<String, List<Data<X, Y>>> categoryMap = new HashMap<String, List<Data<X, Y>>>(); 274 for (int j = 0; j < series.getData().size(); j++) { 275 Data<X, Y> item = series.getData().get(j); 276 Node bar = createBar(series, seriesIndex, item, j); 277 String category; 278 if (orientation == Orientation.VERTICAL) { 279 category = (String) item.getXValue(); 280 } else { 281 category = (String) item.getYValue(); 282 } 283 // list of two item positive and negative 284 List<Data<X, Y>> itemList = categoryMap.get(category) != null ? categoryMap.get(category) : new ArrayList<Data<X, Y>>(); 285 itemList.add(item); 286 categoryMap.put(category, itemList); 287 if (shouldAnimate()) { 288 animateDataAdd(item, bar); 289 } else { 319 FadeTransition ft = new FadeTransition(Duration.millis(700), bar); 320 ft.setFromValue(1); 321 ft.setToValue(0); 322 ft.setOnFinished(actionEvent -> { 323 processDataRemove(series, d); 324 bar.setOpacity(1.0); 325 }); 326 pt.getChildren().add(ft); 327 } 328 } 329 pt.play(); 330 } else { 331 for (Data<X, Y> d : series.getData()) { 332 processDataRemove(series, d); 333 } 334 removeSeriesFromDisplay(series); 335 requestChartLayout(); 336 } 337 } 338 339 /** {@inheritDoc} */ 340 @Override protected void updateAxisRange() { 341 // This override is necessary to update axis range based on cumulative Y value for the 342 // Y axis instead of the inherited way where the max value in the data range is used. 343 boolean categoryIsX = categoryAxis == getXAxis(); 344 if (categoryAxis.isAutoRanging()) { 345 List cData = new ArrayList(); 346 for (Series<X, Y> series : getData()) { 347 for (Data<X, Y> data : series.getData()) { 348 if (data != null) cData.add(categoryIsX ? data.getXValue() : data.getYValue()); 349 } 350 } 351 categoryAxis.invalidateRange(cData); 352 } 353 if (valueAxis.isAutoRanging()) { 354 List<Number> vData = new ArrayList<>(); 355 for (String category : categoryAxis.getAllDataCategories()) { 356 double totalXN = 0; 357 double totalXP = 0; 358 Iterator<Series<X, Y>> seriesIterator = getDisplayedSeriesIterator(); 359 while (seriesIterator.hasNext()) { 360 Series<X, Y> series = seriesIterator.next(); 361 for (final Data<X, Y> item : getDataItem(series, category)) { 362 if (item != null) { 363 boolean isNegative = item.getNode().getStyleClass().contains("negative"); 364 Number value = (Number) (categoryIsX ? item.getYValue() : item.getXValue()); 365 if (!isNegative) { 366 totalXP += valueAxis.toNumericValue(value); 367 } else { 368 totalXN += valueAxis.toNumericValue(value); 369 } 370 } 371 } 372 } 373 vData.add(totalXP); 374 vData.add(totalXN); 375 } 376 valueAxis.invalidateRange(vData); 377 } 378 } 379 380 /** {@inheritDoc} */ 381 @Override protected void layoutPlotChildren() { 382 double catSpace = categoryAxis.getCategorySpacing(); 383 // calculate bar spacing 384 final double availableBarSpace = catSpace - getCategoryGap(); 385 final double barWidth = availableBarSpace; 386 final double barOffset = -((catSpace - getCategoryGap()) / 2); 387 // update bar positions and sizes 388 for (String category : categoryAxis.getCategories()) { 389 double currentPositiveValue = 0; 390 double currentNegativeValue = 0; 391 Iterator<Series<X, Y>> seriesIterator = getDisplayedSeriesIterator(); 392 while (seriesIterator.hasNext()) { 393 Series<X, Y> series = seriesIterator.next(); 394 for (final Data<X, Y> item : getDataItem(series, category)) { 395 if (item != null) { 396 final Node bar = item.getNode(); 397 final double categoryPos; 398 final double valNumber; 399 final X xValue = getCurrentDisplayedXValue(item); 400 final Y yValue = getCurrentDisplayedYValue(item); |