Exemple #1
0
  public void doTrackStyle() {
    if (null == stylist) {
      return;
    }
    gui.logger.setStatus("Setting style.");

    graph.getModel().beginUpdate();
    try {

      // Collect edges
      Set<Integer> trackIDs = model.getTrackModel().trackIDs(true);
      HashMap<Integer, Set<mxCell>> edgeMap = new HashMap<Integer, Set<mxCell>>(trackIDs.size());
      for (Integer trackID : trackIDs) {
        Set<DefaultWeightedEdge> edges = model.getTrackModel().trackEdges(trackID);
        HashSet<mxCell> set = new HashSet<mxCell>(edges.size());
        for (DefaultWeightedEdge edge : edges) {
          set.add(graph.getCellFor(edge));
        }
        edgeMap.put(trackID, set);
      }

      // Give them style
      Set<mxICell> verticesUpdated = stylist.execute(edgeMap);

      // Take care of vertices
      HashSet<mxCell> missedVertices = new HashSet<mxCell>(graph.getVertexCells());
      missedVertices.removeAll(verticesUpdated);
      stylist.updateVertexStyle(missedVertices);

    } finally {
      graph.getModel().endUpdate();
    }
  }
Exemple #2
0
 /**
  * Import a whole track from the {@link Model} and make it visible
  *
  * @param trackIndex the index of the track to show in TrackScheme
  */
 private void importTrack(int trackIndex) {
   model.beginUpdate();
   graph.getModel().beginUpdate();
   try {
     // Flag original track as visible
     model.setTrackVisibility(trackIndex, true);
     // Find adequate column
     int targetColumn = getUnlaidSpotColumn();
     // Create cells for track
     Set<Spot> trackSpots = model.getTrackModel().trackSpots(trackIndex);
     for (Spot trackSpot : trackSpots) {
       int frame = trackSpot.getFeature(Spot.FRAME).intValue();
       int column = Math.max(targetColumn, getNextFreeColumn(frame));
       insertSpotInGraph(trackSpot, column);
       rowLengths.put(frame, column);
     }
     Set<DefaultWeightedEdge> trackEdges = model.getTrackModel().trackEdges(trackIndex);
     for (DefaultWeightedEdge trackEdge : trackEdges) {
       graph.addJGraphTEdge(trackEdge);
     }
   } finally {
     model.endUpdate();
     graph.getModel().endUpdate();
   }
 }
Exemple #3
0
  /**
   * Updates or creates a cell for the target spot. Is called after the user modified a spot
   * (location, radius, ...) somewhere else.
   *
   * @param spot the spot that was modified.
   */
  private mxICell updateCellOf(final Spot spot) {

    mxICell cell = graph.getCellFor(spot);
    graph.getModel().beginUpdate();
    try {
      if (DEBUG) System.out.println("[TrackScheme] modelChanged: updating cell for spot " + spot);
      if (null == cell) {
        // mxCell not present in graph. Most likely because the corresponding spot belonged
        // to an invisible track, and a cell was not created for it when TrackScheme was
        // launched. So we create one on the fly now.
        int row = getUnlaidSpotColumn();
        cell = insertSpotInGraph(spot, row);
        int frame = spot.getFeature(Spot.FRAME).intValue();
        rowLengths.put(frame, row + 1);
      }

      // Update cell look
      if (spotImageUpdater != null && doThumbnailCapture) {
        String style = cell.getStyle();
        String imageStr = spotImageUpdater.getImageString(spot);
        style =
            mxStyleUtils.setStyle(style, mxConstants.STYLE_IMAGE, "data:image/base64," + imageStr);
        graph.getModel().setStyle(cell, style);
      }
    } finally {
      graph.getModel().endUpdate();
    }
    return cell;
  }
Exemple #4
0
  public void selectTrack(
      final Collection<mxCell> vertices, final Collection<mxCell> edges, final int direction) {

    // Look for spot and edges matching given mxCells
    HashSet<Spot> inspectionSpots = new HashSet<Spot>(vertices.size());
    for (mxCell cell : vertices) {
      Spot spot = graph.getSpotFor(cell);
      if (null == spot) {
        if (DEBUG) {
          System.out.println(
              "[TrackScheme] selectWholeTrack: tried to retrieve cell "
                  + cell
                  + ", unknown to spot map.");
        }
        continue;
      }
      inspectionSpots.add(spot);
    }
    HashSet<DefaultWeightedEdge> inspectionEdges = new HashSet<DefaultWeightedEdge>(edges.size());
    for (mxCell cell : edges) {
      DefaultWeightedEdge dwe = graph.getEdgeFor(cell);
      if (null == dwe) {
        if (DEBUG) {
          System.out.println(
              "[TrackScheme] select whole track: tried to retrieve cell "
                  + cell
                  + ", unknown to edge map.");
        }
        continue;
      }
      inspectionEdges.add(dwe);
    }
    // Forward to selection model
    selectionModel.selectTrack(inspectionSpots, inspectionEdges, direction);
  }
Exemple #5
0
 /** Removes the cell selected by the user in the GUI. */
 public void removeSelectedCells() {
   graph.getModel().beginUpdate();
   try {
     graph.removeCells(graph.getSelectionCells());
     // Will be caught by the graph listeners
   } finally {
     graph.getModel().endUpdate();
   }
 }
Exemple #6
0
  @Override
  public void selectionChanged(SelectionChangeEvent event) {
    if (DEBUG_SELECTION)
      System.out.println(
          "[TrackSchemeFrame] selectionChanged: received event "
              + event.hashCode()
              + " from "
              + event.getSource()
              + ". Fire flag is "
              + doFireSelectionChangeEvent);
    if (!doFireSelectionChangeEvent) return;
    doFireSelectionChangeEvent = false;

    {
      ArrayList<Object> newSelection =
          new ArrayList<Object>(
              selectionModel.getSpotSelection().size() + selectionModel.getEdgeSelection().size());
      Iterator<DefaultWeightedEdge> edgeIt = selectionModel.getEdgeSelection().iterator();
      while (edgeIt.hasNext()) {
        mxICell cell = graph.getCellFor(edgeIt.next());
        if (null != cell) {
          newSelection.add(cell);
        }
      }
      Iterator<Spot> spotIt = selectionModel.getSpotSelection().iterator();
      while (spotIt.hasNext()) {
        mxICell cell = graph.getCellFor(spotIt.next());
        if (null != cell) {
          newSelection.add(cell);
        }
      }
      mxGraphSelectionModel mGSmodel = graph.getSelectionModel();
      mGSmodel.setCells(newSelection.toArray());
    }

    // Center on selection if we added one spot exactly
    Map<Spot, Boolean> spotsAdded = event.getSpots();
    if (spotsAdded != null && spotsAdded.size() == 1) {
      boolean added = spotsAdded.values().iterator().next();
      if (added) {
        Spot spot = spotsAdded.keySet().iterator().next();
        centerViewOn(spot);
      }
    }
    doFireSelectionChangeEvent = true;
  }
Exemple #7
0
  /**
   * Captures and stores the thumbnail image that will be displayed in each spot cell, when using
   * styles that can display images.
   */
  private void createThumbnails() {
    // Group spots per frame
    Set<Integer> frames = model.getSpots().keySet();
    final HashMap<Integer, HashSet<Spot>> spotPerFrame =
        new HashMap<Integer, HashSet<Spot>>(frames.size());
    for (Integer frame : frames) {
      spotPerFrame.put(
          frame, new HashSet<Spot>(model.getSpots().getNSpots(frame, true))); // max size
    }
    for (Integer trackID : model.getTrackModel().trackIDs(true)) {
      for (Spot spot : model.getTrackModel().trackSpots(trackID)) {
        int frame = spot.getFeature(Spot.FRAME).intValue();
        spotPerFrame.get(frame).add(spot);
      }
    }
    // Set spot image to cell style
    if (null != spotImageUpdater) {
      gui.logger.setStatus("Collecting spot thumbnails.");
      int index = 0;
      try {
        graph.getModel().beginUpdate();

        // Iterate per frame
        for (Integer frame : frames) {
          for (Spot spot : spotPerFrame.get(frame)) {

            mxICell cell = graph.getCellFor(spot);
            String imageStr = spotImageUpdater.getImageString(spot);
            String style = cell.getStyle();
            style =
                mxStyleUtils.setStyle(
                    style, mxConstants.STYLE_IMAGE, "data:image/base64," + imageStr);
            graph.getModel().setStyle(cell, style);
          }
          gui.logger.setProgress((double) index++ / frames.size());
        }
      } finally {
        graph.getModel().endUpdate();
        gui.logger.setProgress(0d);
        gui.logger.setStatus("");
        thumbnailCaptured = true; // After that they will be kept in synch thanks to #modelChanged
      }
    }
  }
Exemple #8
0
 /**
  * Insert a spot in the {@link TrackSchemeFrame}, by creating a {@link mxCell} in the graph model
  * of this frame and position it according to its feature.
  */
 private mxICell insertSpotInGraph(Spot spot, int targetColumn) {
   mxICell cellAdded = graph.getCellFor(spot);
   if (cellAdded != null) {
     // cell for spot already exist, do nothing and return original spot
     return cellAdded;
   }
   // Instantiate JGraphX cell
   cellAdded = graph.addJGraphTVertex(spot);
   // Position it
   int row = spot.getFeature(Spot.FRAME).intValue() + 1;
   double x = (targetColumn - 1) * X_COLUMN_SIZE - DEFAULT_CELL_WIDTH / 2;
   double y = (0.5 + row) * Y_COLUMN_SIZE - DEFAULT_CELL_HEIGHT / 2;
   mxGeometry geometry = new mxGeometry(x, y, DEFAULT_CELL_WIDTH, DEFAULT_CELL_HEIGHT);
   cellAdded.setGeometry(geometry);
   // Set its style
   if (null != spotImageUpdater && doThumbnailCapture) {
     String imageStr = spotImageUpdater.getImageString(spot);
     graph
         .getModel()
         .setStyle(cellAdded, mxConstants.STYLE_IMAGE + "=" + "data:image/base64," + imageStr);
   }
   return cellAdded;
 }
Exemple #9
0
  /**
   * Called when the user makes a selection change in the graph. Used to forward this event to the
   * {@link InfoPane} and to other {@link SelectionChangeListener}s.
   *
   * @param model the selection model
   * @param added the cells <b>removed</b> from selection (careful, inverted)
   * @param removed the cells <b>added</b> to selection (careful, inverted)
   */
  private void userChangedSelection(
      mxGraphSelectionModel mxGSmodel,
      Collection<Object> added,
      Collection<Object> removed) { // Seems to be inverted
    if (!doFireSelectionChangeEvent) return;
    Collection<Spot> spotsToAdd = new ArrayList<Spot>();
    Collection<Spot> spotsToRemove = new ArrayList<Spot>();
    Collection<DefaultWeightedEdge> edgesToAdd = new ArrayList<DefaultWeightedEdge>();
    Collection<DefaultWeightedEdge> edgesToRemove = new ArrayList<DefaultWeightedEdge>();

    if (null != added) {
      for (Object obj : added) {
        mxCell cell = (mxCell) obj;

        if (cell.getChildCount() > 0) {

          for (int i = 0; i < cell.getChildCount(); i++) {
            mxICell child = cell.getChildAt(i);
            if (child.isVertex()) {
              Spot spot = graph.getSpotFor(child);
              spotsToRemove.add(spot);
            } else {
              DefaultWeightedEdge edge = graph.getEdgeFor(child);
              edgesToRemove.add(edge);
            }
          }

        } else {

          if (cell.isVertex()) {
            Spot spot = graph.getSpotFor(cell);
            spotsToRemove.add(spot);
          } else {
            DefaultWeightedEdge edge = graph.getEdgeFor(cell);
            edgesToRemove.add(edge);
          }
        }
      }
    }

    if (null != removed) {
      for (Object obj : removed) {
        mxCell cell = (mxCell) obj;

        if (cell.getChildCount() > 0) {

          for (int i = 0; i < cell.getChildCount(); i++) {
            mxICell child = cell.getChildAt(i);
            if (child.isVertex()) {
              Spot spot = graph.getSpotFor(child);
              spotsToAdd.add(spot);
            } else {
              DefaultWeightedEdge edge = graph.getEdgeFor(child);
              edgesToAdd.add(edge);
            }
          }

        } else {

          if (cell.isVertex()) {
            Spot spot = graph.getSpotFor(cell);
            spotsToAdd.add(spot);
          } else {
            DefaultWeightedEdge edge = graph.getEdgeFor(cell);
            edgesToAdd.add(edge);
          }
        }
      }
    }
    if (DEBUG_SELECTION)
      System.out.println("[TrackScheme] userChangeSelection: sending selection change to model.");
    doFireSelectionChangeEvent = false;
    if (!edgesToAdd.isEmpty()) selectionModel.addEdgeToSelection(edgesToAdd);
    if (!spotsToAdd.isEmpty()) selectionModel.addSpotToSelection(spotsToAdd);
    if (!edgesToRemove.isEmpty()) selectionModel.removeEdgeFromSelection(edgesToRemove);
    if (!spotsToRemove.isEmpty()) selectionModel.removeSpotFromSelection(spotsToRemove);
    doFireSelectionChangeEvent = true;
  }
Exemple #10
0
  /**
   * Used to catch spot creation events that occurred elsewhere, for instance by manual editing in
   * the {@link AbstractTrackMateModelView}.
   *
   * <p>We have to deal with the graph modification ourselves here, because the {@link Model} model
   * holds a non-listenable JGraphT instance. A modification made to the model would not be
   * reflected on the graph here.
   */
  @Override
  public void modelChanged(final ModelChangeEvent event) {

    // Only catch model changes
    if (event.getEventID() != ModelChangeEvent.MODEL_MODIFIED) return;

    graph.getModel().beginUpdate();
    try {
      ArrayList<mxICell> cellsToRemove = new ArrayList<mxICell>();

      final int targetColumn = getUnlaidSpotColumn();

      // Deal with spots
      if (!event.getSpots().isEmpty()) {

        Collection<mxCell> spotsWithStyleToUpdate = new HashSet<mxCell>();

        for (Spot spot : event.getSpots()) {

          if (event.getSpotFlag(spot) == ModelChangeEvent.FLAG_SPOT_ADDED) {

            int frame = spot.getFeature(Spot.FRAME).intValue();
            // Put in the graph
            int column = Math.max(targetColumn, getNextFreeColumn(frame));
            mxICell newCell = insertSpotInGraph(spot, column); // move in right+1 free column
            rowLengths.put(frame, column);
            spotsWithStyleToUpdate.add((mxCell) newCell);

          } else if (event.getSpotFlag(spot) == ModelChangeEvent.FLAG_SPOT_MODIFIED) {

            // Change the look of the cell
            mxICell cell = updateCellOf(spot);
            spotsWithStyleToUpdate.add((mxCell) cell);

          } else if (event.getSpotFlag(spot) == ModelChangeEvent.FLAG_SPOT_REMOVED) {

            mxICell cell = graph.getCellFor(spot);
            cellsToRemove.add(cell);
          }
        }
        graph.removeCells(cellsToRemove.toArray(), true);
        stylist.updateVertexStyle(spotsWithStyleToUpdate);
      }

    } finally {
      graph.getModel().endUpdate();
    }

    // Deal with edges
    if (!event.getEdges().isEmpty()) {

      graph.getModel().beginUpdate();
      try {

        if (event.getEdges().size() > 0) {

          Map<Integer, Set<mxCell>> edgesToUpdate = new HashMap<Integer, Set<mxCell>>();

          for (DefaultWeightedEdge edge : event.getEdges()) {

            if (event.getEdgeFlag(edge) == ModelChangeEvent.FLAG_EDGE_ADDED) {

              mxCell edgeCell = graph.getCellFor(edge);
              if (null == edgeCell) {

                // Make sure target & source cells exist

                Spot source = model.getTrackModel().getEdgeSource(edge);
                mxCell sourceCell = graph.getCellFor(source);
                if (sourceCell == null) {
                  int frame = source.getFeature(Spot.FRAME).intValue();
                  // Put in the graph
                  int targetColumn = getUnlaidSpotColumn();
                  int column = Math.max(targetColumn, getNextFreeColumn(frame));
                  insertSpotInGraph(source, column); // move in right+1 free column
                  rowLengths.put(frame, column);
                }

                Spot target = model.getTrackModel().getEdgeTarget(edge);
                mxCell targetCell = graph.getCellFor(target);
                if (targetCell == null) {
                  int frame = target.getFeature(Spot.FRAME).intValue();
                  // Put in the graph
                  int targetColumn = getUnlaidSpotColumn();
                  int column = Math.max(targetColumn, getNextFreeColumn(frame));
                  insertSpotInGraph(target, column); // move in right+1 free column
                  rowLengths.put(frame, column);
                }

                // And finally create the edge cell
                edgeCell = graph.addJGraphTEdge(edge);
              }

              graph.getModel().add(graph.getDefaultParent(), edgeCell, 0);

              // Add it to the map of cells to recolor
              Integer trackID = model.getTrackModel().trackIDOf(edge);
              Set<mxCell> edgeSet = edgesToUpdate.get(trackID);
              if (edgesToUpdate.get(trackID) == null) {
                edgeSet = new HashSet<mxCell>();
                edgesToUpdate.put(trackID, edgeSet);
              }
              edgeSet.add(edgeCell);

            } else if (event.getEdgeFlag(edge) == ModelChangeEvent.FLAG_EDGE_MODIFIED) {
              // Add it to the map of cells to recolor
              Integer trackID = model.getTrackModel().trackIDOf(edge);
              Set<mxCell> edgeSet = edgesToUpdate.get(trackID);
              if (edgesToUpdate.get(trackID) == null) {
                edgeSet = new HashSet<mxCell>();
                edgesToUpdate.put(trackID, edgeSet);
              }
              edgeSet.add(graph.getCellFor(edge));

            } else if (event.getEdgeFlag(edge) == ModelChangeEvent.FLAG_EDGE_REMOVED) {

              mxCell cell = graph.getCellFor(edge);
              graph.removeCells(new Object[] {cell});
            }
          }

          stylist.execute(edgesToUpdate);
          SwingUtilities.invokeLater(
              new Runnable() {
                public void run() {
                  gui.graphComponent.refresh();
                  gui.graphComponent.repaint();
                }
              });
        }
      } finally {
        graph.getModel().endUpdate();
      }
    }
  }
Exemple #11
0
 @Override
 public void centerViewOn(Spot spot) {
   gui.centerViewOn(graph.getCellFor(spot));
 }
Exemple #12
0
  /**
   * This method is called when the user has created manually an edge in the graph, by dragging a
   * link between two spot cells. It checks whether the matching edge in the model exists, and tune
   * what should be done accordingly.
   *
   * @param cell the mxCell of the edge that has been manually created.
   */
  protected void addEdgeManually(mxCell cell) {
    if (cell.isEdge()) {
      final mxIGraphModel graphModel = graph.getModel();
      cell.setValue("New");
      model.beginUpdate();
      graphModel.beginUpdate();
      try {

        Spot source = graph.getSpotFor(cell.getSource());
        Spot target = graph.getSpotFor(cell.getTarget());

        if (DEBUG) {
          System.out.println(
              "[TrackScheme] #addEdgeManually: edge is between 2 spots belonging to the same frame. Removing it.");
          System.out.println(
              "[TrackScheme] #addEdgeManually: adding edge between source "
                  + source
                  + " at frame "
                  + source.getFeature(Spot.FRAME).intValue()
                  + " and target "
                  + target
                  + " at frame "
                  + target.getFeature(Spot.FRAME).intValue());
        }

        if (Spot.frameComparator.compare(source, target) == 0) {
          // Prevent adding edges between spots that belong to the same frame

          if (DEBUG) {
            System.out.println(
                "[TrackScheme] addEdgeManually: edge is between 2 spots belonging to the same frame. Removing it.");
          }
          graph.removeCells(new Object[] {cell});

        } else {
          // We can add it to the model

          // Put them right in order: since we use a oriented graph,
          // we want the source spot to precede in time.
          if (Spot.frameComparator.compare(source, target) > 0) {

            if (DEBUG) {
              System.out.println(
                  "[TrackScheme] #addEdgeManually: Source "
                      + source
                      + " succeed target "
                      + target
                      + ". Inverting edge direction.");
            }

            Spot tmp = source;
            source = target;
            target = tmp;
          }
          // We add a new jGraphT edge to the underlying model, if it does not exist yet.
          DefaultWeightedEdge edge = model.getTrackModel().getEdge(source, target);
          if (null == edge) {
            edge = model.addEdge(source, target, -1);
            if (DEBUG) {
              System.out.println(
                  "[TrackScheme] #addEdgeManually: Creating new edge: " + edge + ".");
            }
          } else {
            // Ah. There was an existing edge in the model we were trying to re-add there, from the
            // graph.
            // We remove the graph edge we have added,
            if (DEBUG) {
              System.out.println("[TrackScheme] #addEdgeManually: Edge pre-existed. Retrieve it.");
            }
            graph.removeCells(new Object[] {cell});
            // And re-create a graph edge from the model edge.
            cell = graph.addJGraphTEdge(edge);
            cell.setValue(String.format("%.1f", model.getTrackModel().getEdgeWeight(edge)));
            // We also need now to check if the edge belonged to a visible track. If not,
            // we make it visible.
            int ID = model.getTrackModel().trackIDOf(edge);
            // This will work, because track indices will be reprocessed only after the
            // graphModel.endUpdate()
            // reaches 0. So now, it's like we are dealing with the track indices priori to
            // modification.
            if (model.getTrackModel().isVisible(ID)) {
              if (DEBUG) {
                System.out.println(
                    "[TrackScheme] #addEdgeManually: Track was visible. Do nothing.");
              }
            } else {
              if (DEBUG) {
                System.out.println(
                    "[TrackScheme] #addEdgeManually: Track was invisible. Make it visible.");
              }
              importTrack(ID);
            }
          }
          graph.mapEdgeToCell(edge, cell);
        }

      } finally {
        graphModel.endUpdate();
        model.endUpdate();
        selectionModel.clearEdgeSelection();
      }
    }
  }
Exemple #13
0
  /** Used to instantiate and configure the {@link JGraphXAdapter} that will be used for display. */
  private JGraphXAdapter createGraph() {

    gui.logger.setStatus("Creating graph adapter.");

    final JGraphXAdapter graph = new JGraphXAdapter(model);
    graph.setAllowLoops(false);
    graph.setAllowDanglingEdges(false);
    graph.setCellsCloneable(false);
    graph.setCellsSelectable(true);
    graph.setCellsDisconnectable(false);
    graph.setCellsMovable(true);
    graph.setGridEnabled(false);
    graph.setLabelsVisible(true);
    graph.setDropEnabled(false);

    // Cells removed from JGraphX
    graph.addListener(mxEvent.CELLS_REMOVED, new CellRemovalListener());

    // Cell selection change
    graph.getSelectionModel().addListener(mxEvent.CHANGE, new SelectionChangeListener());

    // Return graph
    return graph;
  }
Exemple #14
0
  /**
   * Create links between all the spots currently in the {@link Model} selection. We update
   * simultaneously the {@link Model} and the {@link JGraphXAdapter}.
   */
  public void linkSpots() {

    // Sort spots by time
    TreeMap<Integer, Spot> spotsInTime = new TreeMap<Integer, Spot>();
    for (Spot spot : selectionModel.getSpotSelection()) {
      spotsInTime.put(spot.getFeature(Spot.FRAME).intValue(), spot);
    }

    // Find adequate column
    int targetColumn = getUnlaidSpotColumn();

    // Then link them in this order
    model.beginUpdate();
    graph.getModel().beginUpdate();
    try {
      Iterator<Integer> it = spotsInTime.keySet().iterator();
      Integer previousTime = it.next();
      Spot previousSpot = spotsInTime.get(previousTime);
      // If this spot belong to an invisible track, we make it visible
      Integer ID = model.getTrackModel().trackIDOf(previousSpot);
      if (ID != null && !model.getTrackModel().isVisible(ID)) {
        importTrack(ID);
      }

      while (it.hasNext()) {
        Integer currentTime = it.next();
        Spot currentSpot = spotsInTime.get(currentTime);
        // If this spot belong to an invisible track, we make it visible
        ID = model.getTrackModel().trackIDOf(currentSpot);
        if (ID != null && !model.getTrackModel().isVisible(ID)) {
          importTrack(ID);
        }
        // Check that the cells matching the 2 spots exist in the graph
        mxICell currentCell = graph.getCellFor(currentSpot);
        if (null == currentCell) {
          currentCell = insertSpotInGraph(currentSpot, targetColumn);
          if (DEBUG) {
            System.out.println(
                "[TrackScheme] linkSpots: creating cell "
                    + currentCell
                    + " for spot "
                    + currentSpot);
          }
        }
        mxICell previousCell = graph.getCellFor(previousSpot);
        if (null == previousCell) {
          int frame = previousSpot.getFeature(Spot.FRAME).intValue();
          int column = Math.max(targetColumn, getNextFreeColumn(frame));
          rowLengths.put(frame, column);
          previousCell = insertSpotInGraph(previousSpot, column);
          if (DEBUG) {
            System.out.println(
                "[TrackScheme] linkSpots: creating cell "
                    + previousCell
                    + " for spot "
                    + previousSpot);
          }
        }
        // Check if the model does not have already a edge for these 2 spots (that is
        // the case if the 2 spot are in an invisible track, which track scheme does not
        // know of).
        DefaultWeightedEdge edge = model.getTrackModel().getEdge(previousSpot, currentSpot);
        if (null == edge) {
          // We create a new edge between 2 spots, and pair it with a new cell edge.
          edge = model.addEdge(previousSpot, currentSpot, -1);
          mxCell cell = (mxCell) graph.addJGraphTEdge(edge);
          cell.setValue("New");
        } else {
          // We retrieve the edge, and pair it with a new cell edge.
          mxCell cell = (mxCell) graph.addJGraphTEdge(edge);
          cell.setValue(String.format("%.1f", model.getTrackModel().getEdgeWeight(edge)));
          // Also, if the existing edge belonged to an existing invisible track, we make it visible.
          ID = model.getTrackModel().trackIDOf(edge);
          if (ID != null && !model.getTrackModel().isVisible(ID)) {
            importTrack(ID);
          }
        }
        previousSpot = currentSpot;
      }
    } finally {
      graph.getModel().endUpdate();
      model.endUpdate();
    }
  }