Package jcgp.gui.population

Source Code of jcgp.gui.population.GUINode

package jcgp.gui.population;

import javafx.event.EventHandler;
import javafx.scene.control.Label;
import javafx.scene.input.MouseDragEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import jcgp.backend.function.Function;
import jcgp.backend.population.Connection;
import jcgp.backend.population.Input;
import jcgp.backend.population.Node;
import jcgp.backend.resources.Resources;
import jcgp.gui.GUI;
import jcgp.gui.constants.Constants;

public class GUINode extends GUIGene {

  private Line[] lines;
  private Node node;
  private Resources resources;
  private int connectionIndex = 0;
 

  public GUINode(ChromosomePane parentRef, final Node node, Line[] connectionLines, final GUI gui) {
    super();
   
    // store references
    this.parent = parentRef;
    this.node = node;
    this.lines = connectionLines;
    this.resources = gui.getExperiment().getResources();
   
    // move the GUIGene to the right position
    relocate(((node.getColumn() + 1) * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + Constants.NODE_RADIUS,
        (node.getRow() * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + Constants.NODE_RADIUS);

    // set the line ends correctly
    updateLines();

    final Label connectionNumber = new Label();
    connectionNumber.setStyle("-fx-background-color:rgb(255, 255, 255); -fx-border-color:rgba(0, 0, 0, 0.5); ");
    connectionNumber.setVisible(false);

    Circle output = new Circle(Constants.NODE_RADIUS, 0, Constants.SOCKET_RADIUS, Paint.valueOf("white"));
    output.setStroke(Paint.valueOf("black"));

    updateText();

    Circle[] sockets = new Circle[resources.arity()];
    double angle, xPos, yPos;
    for (int l = 0; l < sockets.length; l++) {
      angle = (((l + 1) / ((double) (resources.arity() + 1))) * Constants.THETA) - (Constants.THETA / 2);
      xPos = -Math.cos(angle) * Constants.NODE_RADIUS;
      yPos = Math.sin(angle) * Constants.NODE_RADIUS;

      sockets[l] = new Circle(xPos, yPos, Constants.SOCKET_RADIUS, Paint.valueOf("white"));
      sockets[l].setId(String.valueOf(l));
      sockets[l].setStroke(Paint.valueOf("black"));

      final Circle s = sockets[l];
      final int index = l;

      /*
       * Mouse event handlers on sockets
       *
       */
      s.addEventFilter(MouseEvent.DRAG_DETECTED, new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
          // the mouse has been dragged out of the socket, this means a full drag is in progress
          startFullDrag();
        }
      });

      s.addEventFilter(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
          // user is hovering over connection socket
          connectionNumber.setText("C: " + s.getId());
          connectionNumber.relocate(s.getCenterX() + 5, s.getCenterY() - 10);
          connectionNumber.setVisible(true);
        }
      });

      s.addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
          // user exits the connection socket
          connectionNumber.setVisible(false);
        }
      });

      s.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
          // mouse was pressed on the socket
          setState(GUIGeneState.SOURCE);
          connectionIndex = index;
        }
      });

      s.addEventFilter(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
          if (!parent.isTarget()) {
            lines[connectionIndex].setEndX(event.getX() + ((Circle) event.getSource()).getParent().getLayoutX());
            lines[connectionIndex].setEndY(event.getY() + ((Circle) event.getSource()).getParent().getLayoutY());
          }
        }
      })

      s.addEventFilter(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {         
          if (event.isStillSincePress()) {
            // mouse was released before dragging out of the socket
            updateLine(index);
            setState(GUIGeneState.HOVER);
          } else if (getState() == GUIGeneState.SOURCE) {
            // no connection has been made, fallback
            resetState();
            updateLines();
          }
        }
      });
    }

    /*
     * Mouse event handlers on whole gene
     */
    addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
      @Override
      public void handle(MouseEvent event) {
        gui.bringFunctionSelector(event, (GUINode) event.getSource());
      }
    });
   
    addEventFilter(MouseDragEvent.MOUSE_DRAG_ENTERED, new EventHandler<MouseDragEvent>() {
      @Override
      public void handle(MouseDragEvent event) {
        // the drag has entered this node, react appropriately
        // this happens even if we are the source of the drag
        if (isAllowed((GUIGene) event.getGestureSource(), (GUIGene) event.getSource())) {
          ((GUIGene) event.getGestureSource()).setConnectionLine((GUIGene) event.getSource());
         
          Connection source = ((GUIGene) event.getGestureSource()).getChangingConnection();
          if (node == source) {
            setState(GUIGeneState.NO_CHANGE_TARGET);
          } else {
            setState(GUIGeneState.VALID_TARGET);
          }
        } else {
          setState(GUIGeneState.INVALID_TARGET);
        }
      }
    });

    addEventFilter(MouseDragEvent.MOUSE_DRAG_EXITED, new EventHandler<MouseDragEvent>() {
      @Override
      public void handle(MouseDragEvent event) {
        // the drag has exited this node, react appropriately
        // this happens even if we are the source of the drag
        parent.setTarget(false);
        if (event.isPrimaryButtonDown()) {
          if (event.getGestureSource() == event.getSource()) {
            setState(GUIGeneState.SOURCE);
          } else {
            if (getState() == GUIGeneState.NO_CHANGE_TARGET) {
              setState(GUIGeneState.INDIRECT_HOVER);
            } else {
              setState(GUIGeneState.NEUTRAL);
              ((GUIGene) event.getGestureSource()).setConnectionStates(GUIGeneState.INDIRECT_HOVER);
            }
          }
        }
      }
    });

    addEventFilter(MouseDragEvent.MOUSE_DRAG_RELEASED, new EventHandler<MouseDragEvent>() {
      @Override
      public void handle(MouseDragEvent event) {
        GUIGene source = ((GUIGene) event.getGestureSource());
        // set states to reflect the new situation
        if (source.isLocked()) {
          source.setState(GUIGeneState.HOVER);
          source.setConnectionStates(GUIGeneState.HOVER);
        } else {
          source.setState(GUIGeneState.NEUTRAL);
          source.setConnectionStates(GUIGeneState.NEUTRAL);
        }

        // the user released the drag gesture on this node, react appropriately
        if (isAllowed((GUIGene) event.getGestureSource(), (GUIGene) event.getSource())) {
          if (source.isLocked()) {
            // remove locks from the old connection, add the to setConnethe new
            // note that the old connection may still have locks after this
            parent.getGuiGene(source.getChangingConnection()).removeLocks(source.getLocks());
            addLocks(source.getLocks());
          } else {
            if (source instanceof GUIOutput) {
              source.resetState();
            }
          }
          source.setChangingConnection(node);

        }
        source.updateLines();
        setState(GUIGeneState.HOVER);
      }
    });

    addEventFilter(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() {
      @Override
      public void handle(MouseEvent event) {
        // cursor has entered this node without dragging, or it is dragging and this is the source
        if (getState() == GUIGeneState.NEUTRAL) {
          setState(GUIGeneState.HOVER);
        } else if (locked > 0) {
          setConnectionStates(GUIGeneState.LOCKED_HOVER);
        }
      }
    });

    addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {
      @Override
      public void handle(MouseEvent event) {
        // cursor has left this node without dragging, or it is dragging and this is the source
        if (getState() == GUIGeneState.HOVER && locked <= 0) {
          setState(GUIGeneState.NEUTRAL);
          setConnectionStates(GUIGeneState.NEUTRAL);
        } else if (locked > 0) {
          if (getState() == GUIGeneState.SOURCE || getState() == GUIGeneState.INVALID_TARGET) {
            setConnectionStates(GUIGeneState.INDIRECT_HOVER);
          } else {
            setConnectionStates(GUIGeneState.HOVER);
          }
         
        }
      }
    });

    getChildren().addAll(mainCircle, text);
    getChildren().addAll(sockets);
    getChildren().addAll(output, connectionNumber);

  }

  @Override
  public void setState(GUIGeneState newState) {
    switch (newState) {
    case ACTIVE_HOVER:
      if (locked > 0) {
        setState(GUIGeneState.LOCKED_HOVER);
      } else {
        mainCircle.setFill(Paint.valueOf(Constants.SOFT_HIGHLIGHT_COLOUR));
        showLines(true);
      }
      setConnectionStates(GUIGeneState.ACTIVE_HOVER);
      break;
    case LOCKED_HOVER:
      mainCircle.setFill(Paint.valueOf(Constants.SOFT_HIGHLIGHT_COLOUR));
      break;
    case INVALID_TARGET:
      mainCircle.setFill(Paint.valueOf(Constants.BAD_SELECTION_COLOUR));
      break;
    case HOVER:
      mainCircle.setFill(Paint.valueOf(Constants.MEDIUM_HIGHLIGHT_COLOUR));
      showLines(true);
      if (locked <= 0) {
        setConnectionStates(GUIGeneState.INDIRECT_HOVER);
      } else {
        setConnectionStates(GUIGeneState.HOVER);
      }
      break;
    case INDIRECT_HOVER:
      mainCircle.setFill(Paint.valueOf(Constants.SOFT_HIGHLIGHT_COLOUR));
      break;
    case NEUTRAL:
      if (locked > 0) {
        setState(GUIGeneState.HOVER);
      } else {
        mainCircle.setFill(Paint.valueOf(Constants.NEUTRAL_COLOUR));
        showLines(false);
        if (getState() == GUIGeneState.ACTIVE_HOVER) {
          setConnectionStates(GUIGeneState.NEUTRAL);
        }
      }
      break;
    case NO_CHANGE_TARGET:
      parent.setTarget(true);
      mainCircle.setFill(Paint.valueOf(Constants.NEUTRAL_SELECTION_COLOUR));
      break;
    case SOURCE:
      mainCircle.setFill(Paint.valueOf(Constants.HARD_HIGHLIGHT_COLOUR));
      break;
    case VALID_TARGET:
      parent.setTarget(true);
      mainCircle.setFill(Paint.valueOf(Constants.GOOD_SELECTION_COLOUR));
      break;
    default:
      break;
    }

    super.setState(newState);
  }
 
  @Override
  public Connection getChangingConnection() {
    return node.getConnection(connectionIndex);
  }

  private boolean isAllowed(GUIGene source, GUIGene target) {
    if (source instanceof GUINode) {
      // if the source is a node, all inputs and some nodes are valid
      if (target instanceof GUIInput) {
        return true;
      } else if (target instanceof GUINode) {
        // target and source are nodes, let's look at levels back
        Node t = ((GUINode) target).getNode(), s = ((GUINode) source).getNode();
        if (s.getColumn() - t.getColumn() > 0 && s.getColumn() - t.getColumn() <= resources.levelsBack()) {
          return true;
        }
        return false;
      } else if (target instanceof GUIOutput) {
        return false;
      } else {
        throw new ClassCastException("Target was neither GUINode nor GUIInput nor GUIOutput.");
      }
    } else if (source instanceof GUIOutput) {
      // if the source is an output, any node or input is valid
      if (target instanceof GUINode || target instanceof GUIInput) {
        return true;
      } else if (target instanceof GUIOutput) {
        return false;
      } else {
        throw new ClassCastException("Target was neither GUINode nor GUIInput nor GUIOutput.");
      }
    }
    // if the source was neither node nor output, something bad is happening
    throw new ClassCastException("Source was neither GUINode nor GUIOutput.");
  }


  public Node getNode() {
    return node;
  }

  /**
   * Place the end of the specified line on the output of the associated connection.
   *
   * @param index the line to be updated.
   */
  public void updateLine(int index) {
    if (node.getConnection(index) instanceof Node) {
      int row = ((Node) node.getConnection(index)).getRow(),
          column = ((Node) node.getConnection(index)).getColumn();
      lines[index].setEndX(((column + 1) * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + 2 * Constants.NODE_RADIUS);
      lines[index].setEndY((row * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + Constants.NODE_RADIUS);
    } else if (node.getConnection(index) instanceof Input) {
      int inputIndex = ((Input) node.getConnection(index)).getIndex();
      lines[index].setEndX(2 * Constants.NODE_RADIUS);
      lines[index].setEndY(inputIndex * (2 * Constants.NODE_RADIUS + Constants.SPACING) + Constants.NODE_RADIUS);
    }
  }

  /**
   * Updates the end of all lines to match the associated connections.
   */
  @Override
  public void updateLines() {
    for (int c = 0; c < lines.length; c++) {
      updateLine(c);
    }
  }

  /**
   * Toggle visibility of all connection lines.
   *
   * @param value whether to show the lines or not.
   */
  private void showLines(boolean value) {
    for (int i = 0; i < lines.length; i++) {
      lines[i].setVisible(value);
    }
  }

  @Override
  public void setConnectionStates(GUIGeneState newState) {
    for (int i = 0; i < lines.length; i++) {
      parent.getGuiGene(node.getConnection(i)).setState(newState);
    }
  }

  @Override
  public void setChangingConnection(Connection newConnection) {
    node.setConnection(connectionIndex, newConnection);
    if (parent.isEvaluating()) {
      parent.updateValues();
    }
  }


  @Override
  public void resetState() {
    if (locked > 0) {
      setState(GUIGeneState.HOVER);
    } else {
      setState(GUIGeneState.NEUTRAL);
      setConnectionStates(GUIGeneState.NEUTRAL);
    }

  }

  @Override
  protected void setLocked(boolean value) {
    locked += value ? 1 : -1;
    setState(locked > 0 ? GUIGeneState.HOVER : GUIGeneState.ACTIVE_HOVER);

    for (int i = 0; i < lines.length; i++) {
      parent.getGuiGene(node.getConnection(i)).setLocked(value);
    }
  }

  @Override
  public void addLocks(int value) {
    locked += value;
    setState(locked > 0 ? GUIGeneState.HOVER : GUIGeneState.ACTIVE_HOVER);

    for (int i = 0; i < lines.length; i++) {
      parent.getGuiGene(node.getConnection(i)).addLocks(value);
    }
  }

  @Override
  public void removeLocks(int value) {
    locked -= value;
    setState(locked > 0 ? GUIGeneState.HOVER : GUIGeneState.NEUTRAL);

    for (int i = 0; i < lines.length; i++) {
      parent.getGuiGene(node.getConnection(i)).removeLocks(value);
    }
  }

  @Override
  public void setConnectionLine(GUIGene gene) {
    lines[connectionIndex].setEndX(gene.getLayoutX() + Constants.NODE_RADIUS);
    lines[connectionIndex].setEndY(gene.getLayoutY());
  }

  public void updateText() {
    if (parent.isEvaluating()) {
      text.setText(node.getFunction() + "\n" + node.getValue().toString());
    } else {
      text.setText(node.getFunction().toString());
    }
  }
 
  public void setFunction(Function function) {
    node.setFunction(function);
    if (parent.isEvaluating()) {
      parent.updateValues();
    } else {
      updateText();
    }
  }

  public void setNode(Node newNode) {
    node = newNode;
  }
}
TOP

Related Classes of jcgp.gui.population.GUINode

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.