/**
  * Overlay a single disease reaction onto a normal reaction.
  *
  * @param normalReaction
  * @param diseaseReaction
  * @param overlaidObjects
  */
 private void overlayDiseaseReaction(HyperEdge normalReaction, GKInstance diseaseReaction)
     throws Exception {
   // Make a copy of the HyperEdge for future process that is related to Vertex and JSON generation
   HyperEdge reactionCopy = normalReaction.shallowCopy();
   reactionCopy.setReactomeId(diseaseReaction.getDBID());
   reactionCopy.setDisplayName(diseaseReaction.getDisplayName());
   reactionCopy.setLineColor(DefaultRenderConstants.DEFAULT_DISEASE_BACKGROUND);
   displayedObject.addComponent(reactionCopy);
   overlaidObjects.add(reactionCopy);
   // Want to handle inputs, outputs and catalysts since regulators can
   // be ignored
   List<Node> nodes = new ArrayList<Node>();
   nodes.addAll(normalReaction.getInputNodes());
   nodes.addAll(normalReaction.getOutputNodes());
   nodes.addAll(normalReaction.getHelperNodes());
   // List objects not listed in the disease reaction as crossed objects
   Set<GKInstance> participants = InstanceUtilities.getReactionParticipants(diseaseReaction);
   Set<Long> diseaseIds = new HashSet<Long>();
   for (GKInstance participant : participants) {
     diseaseIds.add(participant.getDBID());
     if (participant.getSchemClass().isValidAttribute(ReactomeJavaConstants.hasMember)) {
       List<GKInstance> list = participant.getAttributeValuesList(ReactomeJavaConstants.hasMember);
       if (list != null && list.size() > 0) {
         for (GKInstance inst : list) diseaseIds.add(inst.getDBID());
       }
     }
     if (participant.getSchemClass().isValidAttribute(ReactomeJavaConstants.hasCandidate)) {
       List<GKInstance> list =
           participant.getAttributeValuesList(ReactomeJavaConstants.hasCandidate);
       if (list != null && list.size() > 0) {
         for (GKInstance inst : list) diseaseIds.add(inst.getDBID());
       }
     }
   }
   Set<GKInstance> lofInstances = new HashSet<GKInstance>();
   Map<Node, GKInstance> normalToDiseaseEntity =
       mapMutatedToNormalNodes(diseaseReaction, normalReaction, nodes, lofInstances);
   for (Node node : nodes) {
     if (!diseaseIds.contains(node.getReactomeId())) {
       // Check if it should be mapped to a normal entity
       GKInstance diseaseEntity = normalToDiseaseEntity.get(node);
       if (diseaseEntity == null) crossedObjects.add(node); // Just crossed out
       else {
         Node diseaseNode =
             replaceNormalNode(node, diseaseEntity, contains(diseaseEntity, lofInstances));
         if (diseaseNode == null) continue; // Just in case
         // Re-link to diseaseNode
         ConnectInfo connectInfo = reactionCopy.getConnectInfo();
         List<?> widgets = connectInfo.getConnectWidgets();
         for (Object obj : widgets) {
           ConnectWidget widget = (ConnectWidget) obj;
           if (widget.getConnectedNode() == node) widget.replaceConnectedNode(diseaseNode);
         }
       }
     } else overlaidObjects.add(node);
   }
 }
 protected void validateConnectWidget(ConnectWidget widget) {
   Point point = widget.getPoint();
   if (widget.getRole() == HyperEdge.INPUT) {
     // Should have fixed position
     Point p = getLinkPoint();
     point.x = p.x + 1;
     point.y = p.y;
     return;
   }
   // Always in the north
   Rectangle bounds = getBounds();
   int fixedY = bounds.y + RenderableGene.GENE_SYMBOL_WIDTH / 2 - 2;
   if (point.y != fixedY) point.y = fixedY;
   double ratio = widget.getLinkRatio();
   // Don't let x goes out of the bounds
   if (ratio > 0.0) {
     point.x = (int) (bounds.x + bounds.width * ratio);
   } else if (point.x < bounds.x) point.x = bounds.x;
   else if (point.x > bounds.x + bounds.width) point.x = bounds.x + bounds.width;
 }