1

I am using a Java FX line chart to draw protein structures but I am having trouble trying to represent "fitness bonds" on this graph (this is when two hydrophobic acids are a distance of 1 unit away from each other but aren't sequentially connected, shown in the links below).

This is what my chart looks like now.

This is what I am trying to get my chart to look like.

My fitness bonds are held in a List<Pair<Point, Point>>.

For example, I need to do something like:

for (Pair<Point, Point> pair : pointPairs) {
   //draw red line from pair.getKey() to pair.getValue()
}

A fitness bond can either be a horizontal or vertical line.

How can I take this list of point pairs and draw custom lines directly on the chart between each point pair?

Jenna
  • 11
  • 2
  • 2
    A `LineChart` simply doesn't seem like the correct thing to use here. I would recommend creating this using shapes (lines, circles, etc) added to a pane. – James_D Mar 01 '18 at 16:37

1 Answers1

0

This is achievable by using a separate Series per line segment. For future reference; it would be most helpful if you would provide a minimal, runnable example of the problem.

Here is an example based on your requirements:

FitnessBnids.java:

package application;

import static java.util.stream.Collectors.toList;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

public class FitnessBonds extends Application
{

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

  @Override
  public void start( final Stage primaryStage )
  {
    final XYChart.Series<Number, Number> series1 = new XYChart.Series<>();
    series1.setData( FXCollections.observableArrayList(
        createData( 0, 0 ),
        createData( 1, 0 ),

        createData( 1, -1 ),
        createData( 0, -1 ),

        createData( 0, -2 ),
        createData( 1, -2 ),

        createData( 2, -2 ),
        createData( 2, -1 ),

        createData( 3, -1 ),
        createData( 3, 0 ),

        createData( 4, 0 ),
        createData( 4, -1 ),

        createData( 4, -2 ),
        createData( 3, -2 ),

        createData( 3, -3 ),
        createData( 3, -4 ),

        createData( 2, -4 ),
        createData( 1, -4 ),

        createData( 1, -3 ),
        createData( 2, -3 ) ) );

    final List<XYChart.Series<Number, Number>> bonds = bruteForceSearchUnconnectedNodesOneDistance( series1.getData().stream()
        .map( data -> new Point2D( data.getXValue().doubleValue(), data
            .getYValue().doubleValue() ) )
        .collect( toList() ) );


    final ObservableList<XYChart.Series<Number, Number>> series = FXCollections
        .observableArrayList( series1 );

    series.addAll( bonds );

    final NumberAxis xAxis = new NumberAxis();
    xAxis.setLowerBound( -2 );
    xAxis.setUpperBound( 5 );
    xAxis.setAutoRanging( false );
    final NumberAxis yAxis = new NumberAxis();
    yAxis.setLowerBound( -5 );
    yAxis.setUpperBound( 5 );
    yAxis.setAutoRanging( false );

    final LineChart lineChart = new LineChart<>( xAxis, yAxis,
        series );

    lineChart.setCreateSymbols( true );
    lineChart.setAnimated( true );
    lineChart.setAxisSortingPolicy( LineChart.SortingPolicy.NONE );

    lineChart.setPadding( new Insets( 32 ) );
    final Scene scene = new Scene( new StackPane( lineChart ) );

    scene.getStylesheets().add( FitnessBonds.class.getResource( "fit.css" ).toExternalForm() );

    primaryStage.setScene( scene );
    primaryStage.show();
  }

  private List<XYChart.Series<Number, Number>> bruteForceSearchUnconnectedNodesOneDistance( final List<Point2D> data )
  {
    if ( data.size() < 2 )
    {
      return Collections.emptyList();
    }

    final List<XYChart.Series<Number, Number>> list = new ArrayList<>();

    for ( int i = 1; i < data.size(); ++i )
    {
      try
      {
        final Point2D previousNode = data.get( i - 1 );
        final Point2D nextNode = data.get( i + 1 );
        final Point2D node = data.get( i );

        final List<Point2D> allAround = getAllAroundDistanceOne( node );
        allAround.stream()
            .filter( data::contains )
            .filter( point -> !nextNode.equals( point ) )
            .filter( point -> !previousNode.equals( point ) )
            //FIXME this does not eliminate duplicates
            .forEach( point -> list.add( new XYChart.Series<>( FXCollections.observableArrayList(
                createBondData( node.getX(), node.getY() ),
                createBondData( point.getX(), point.getY() ) ) ) ) );
      }
      catch ( final IndexOutOfBoundsException exception )
      {
        continue;
      }
    }

    return list;
  }

  private List<Point2D> getAllAroundDistanceOne( final Point2D node )
  {
    return Stream.of(
        new Point2D( 0, 1 ),
        new Point2D( 1, 0 ),
        new Point2D( 0, -1 ),
        new Point2D( -1, 0 ) )
        .map( node::add )
        .collect( toList() );
  }

  private XYChart.Data<Number, Number> createData( final double x, final double y )
  {
    final XYChart.Data<Number, Number> data = new XYChart.Data<>( x, y );
    data.setNode( new Circle( 4, Color.BLUE ) );
    return data;
  }

  private XYChart.Data<Number, Number> createBondData( final double x, final double y )
  {
    final XYChart.Data<Number, Number> data = new XYChart.Data<>( x, y );
    data.setNode( new Circle( 6, Color.GREEN ) );
    return data;
  }
}

fit.css:

.default-color0.chart-series-line { -fx-stroke: black; }

Please note, that, based on your description

this is when two hydrophobic acids are a distance of 1 unit away from each other but aren't sequentially connected, shown in the links below)

there should be a lot more links in your example. I can only assume that the constraints for what constitutes a "fitness bond" are more strict. But in my example you will see more links.

Oliver Jan Krylow
  • 1,758
  • 15
  • 22