/*
 * Licensed Materials - Property of Perforce Software, Inc. 
 * © Copyright Perforce Software, Inc. 2014, 2021 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.GradientPaint;
import java.awt.SystemColor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.JTextField;

import ilog.views.IlvApplyObject;
import ilog.views.IlvBufferedGraphicEnumeration;
import ilog.views.IlvConstantModeManagerFrame;
import ilog.views.IlvDefaultManagerFrame;
import ilog.views.IlvDirection;
import ilog.views.IlvGraphic;
import ilog.views.IlvGraphicEnumeration;
import ilog.views.IlvGrid;
import ilog.views.IlvHandlesSelection;
import ilog.views.IlvManagerView;
import ilog.views.IlvPoint;
import ilog.views.IlvRect;
import ilog.views.accelerator.IlvDeleteSelectionAccelerator;
import ilog.views.accelerator.IlvFitToSizeAccelerator;
import ilog.views.accelerator.IlvIdentityAccelerator;
import ilog.views.accelerator.IlvRotateAccelerator;
import ilog.views.accelerator.IlvScrollDownAccelerator;
import ilog.views.accelerator.IlvScrollLeftAccelerator;
import ilog.views.accelerator.IlvScrollRightAccelerator;
import ilog.views.accelerator.IlvScrollUpAccelerator;
import ilog.views.accelerator.IlvZoomInAccelerator;
import ilog.views.accelerator.IlvZoomOutAccelerator;
import ilog.views.graphic.IlvGeneralPath;
import ilog.views.graphic.IlvPolygon;
import ilog.views.hypergraph.IlvHyperEdge;
import ilog.views.hypergraph.IlvHyperEdgeEnd;
import ilog.views.hypergraph.IlvHyperGrapher;
import ilog.views.hypergraph.IlvSegmentedHyperEdge;
import ilog.views.hypergraph.crossing.IlvCrossingCalculation;
import ilog.views.hypergraph.edgeconnector.IlvHyperEdgeCenterConnector;
import ilog.views.hypergraph.edgeconnector.IlvHyperEdgeClippingConnector;
import ilog.views.hypergraph.edgeconnector.IlvHyperEdgeConnector;
import ilog.views.hypergraph.edgeconnector.IlvHyperEdgeFixedConnector;
import ilog.views.hypergraph.edgeconnector.IlvHyperEdgePinConnector;
import ilog.views.hypergraph.edgeconnector.IlvHyperGrapherPin;
import ilog.views.hypergraph.interactor.IlvMakeHyperEdgeInteractor;
import ilog.views.hypergraph.interactor.IlvMakeHyperEdgePinConnectorInteractor;
import ilog.views.hypergraph.interactor.IlvMakeSegmentedHyperEdgeInteractor;
import ilog.views.interactor.IlvSelectInteractor;
import ilog.views.interactor.IlvZoomViewInteractor;
import ilog.views.swing.IlvJManagerViewControlBar;
import ilog.views.swing.IlvJScrollManagerView;
import ilog.views.util.IlvProductUtil;
import ilog.views.util.swing.IlvSwingUtil;
import interactor.HyperEdgeConnectorInteractor;
import interactor.InteractorButton;
import interactor.MakeNodeInteractor;
import util.ResourceUtil;

/**
 * This is a very simple application that uses the
 * <code>IlvHyperGrapher</code>.
 */
public class HyperGrapherDemo extends JRootPane {
  {
    // This sample uses JViews Diagrammer features. When deploying an
    // application that includes this code, you need to be in possession
    // of a Perforce JViews Diagrammer Deployment license.
    IlvProductUtil.DeploymentLicenseRequired(IlvProductUtil.JViews_Diagrammer_Deployment);
  }

  /** The grapher */
  IlvHyperGrapher grapher = new IlvHyperGrapher();

  /** The view of the grapher */
  IlvManagerView mgrview = new IlvManagerView(grapher);

  /** The sample connectors */
  IlvHyperEdgeClippingConnector sampleConnector1 = new IlvHyperEdgeClippingConnector();
  IlvHyperEdgeCenterConnector sampleConnector2 = new IlvHyperEdgeCenterConnector();
  IlvHyperEdgeFixedConnector sampleConnector3 = new IlvHyperEdgeFixedConnector();
  IlvMakeHyperEdgePinConnectorInteractor makePinInteractor =
      new IlvMakeHyperEdgePinConnectorInteractor();
  IlvMakeSegmentedHyperEdgeInteractor makeSegEdgeInteractor =
      new IlvMakeSegmentedHyperEdgeInteractor();

  /**
   * Initializes the application.
   */
  public void init() {
    // create the scroll manager view
    IlvJScrollManagerView scrollManView = new IlvJScrollManagerView(mgrview);

    // Some settings on the manager view and on the scroll manager view
    mgrview.setAntialiasing(true);
    mgrview.setKeepingAspectRatio(true);
    mgrview.setBackground(Color.white);
    mgrview.setForeground(SystemColor.windowText);
    Color xc = SystemColor.windowText;
    xc = new Color(255 - xc.getRed(), 255 - xc.getGreen(), 255 - xc.getBlue());
    mgrview.setDefaultXORColor(xc);
    mgrview.setDefaultGhostColor(SystemColor.windowText);
    mgrview.setZoomFactorRange(0.02, 10.0);
    mgrview.setWheelZoomingEnabled(true);
    scrollManView.setWheelScrollingEnabled(true);

    // Settings parameters for selection handles
    IlvHandlesSelection.defaultHandleColor = Color.black;
    IlvHandlesSelection.defaultHandleBackgroundColor = Color.white;
    IlvHandlesSelection.defaultHandleShape = IlvHandlesSelection.SQUARE_SHAPE;

    // menu
    JMenuBar menuBar = new JMenuBar();
    createMenus(menuBar);
    setJMenuBar(menuBar);

    // create the standard control bar
    IlvJManagerViewControlBar controlBar = new IlvJManagerViewControlBar();
    controlBar.setView(mgrview);
    createToolbarButtons(controlBar);

    // modify the interactors such that the demo looks better
    IlvSelectInteractor selInter = (IlvSelectInteractor) controlBar.getSelectInteractor();
    selInter.setOpaqueMove(true);
    selInter.setOpaqueResize(true);
    ((IlvZoomViewInteractor) controlBar.getZoomViewInteractor()).setPermanent(true);
    // set the initial interactor
    mgrview.setInteractor(controlBar.getSelectInteractor());

    // init interactors
    makePinInteractor.setPinSize(4);

    // fit things together
    getContentPane().add(BorderLayout.NORTH, controlBar);
    getContentPane().add(BorderLayout.CENTER, scrollManView);

    // configure the grapher
    initAccelerators();

    // create the contents of a sample grapher
    fillGrapher();
  }

  /**
   * Create the additional toolbar buttons.
   */
  private void createToolbarButtons(IlvJManagerViewControlBar controlBar) {
    InteractorButton b;
    controlBar.addSeparator();

    /*
     * b = new InteractorButton(new ExpandInteractor(), "gif/Expand.gif", mgrview);
     * b.setToolTipText(ResourceUtil.getString("Expand.tooltip")); controlBar.add(b);
     */

    IlvGeneralPath sample1 = new IlvGeneralPath(new Rectangle2D.Double(0, 0, 40, 40));
    sample1.setFillPaint(new GradientPaint(0, 0, Color.white, 20, 30, Color.yellow));
    sample1.setStrokePaint(Color.black);
    sample1.setFillOn(true);
    sample1.setStrokeOn(true);
    sample1.setPaintAbsolute(false);
    b = new InteractorButton(new MakeNodeInteractor(sample1), mgrview);
    b.setToolTipText(ResourceUtil.getString("CreateRectangle.tooltip"));
    controlBar.add(b);

    IlvGeneralPath sample2 = new IlvGeneralPath(new Ellipse2D.Double(0, 0, 40, 40));
    sample2.setFillPaint(new GradientPaint(0, 30, Color.yellow, 30, 30, new Color(150, 255, 255)));
    sample2.setStrokePaint(Color.black);
    sample2.setFillOn(true);
    sample2.setStrokeOn(true);
    sample2.setPaintAbsolute(false);
    b = new InteractorButton(new MakeNodeInteractor(sample2), mgrview);
    b.setToolTipText(ResourceUtil.getString("CreateCircle.tooltip"));
    controlBar.add(b);

    IlvPoint[] pts = new IlvPoint[10];
    for (int i = 0; i < 10; i++) {
      int r = (i % 2 == 0 ? 25 : 10);
      double angle = Math.PI + i * 2 * Math.PI / 10;
      double s = r * Math.sin(angle);
      double c = r * Math.cos(angle);
      pts[i] = new IlvPoint(s, c);
    }
    IlvPolygon sample3 = new IlvPolygon(pts);
    sample3.setBackground(Color.black);
    sample3.setForeground(new Color(255, 220, 130));
    sample3.setFillOn(true);
    sample3.setStrokeOn(true);
    b = new InteractorButton(new MakeNodeInteractor(sample3), mgrview);
    b.setToolTipText(ResourceUtil.getString("CreateStar.tooltip"));
    controlBar.add(b);

    IlvHyperGrapher sample4 = new IlvHyperGrapher();
    IlvConstantModeManagerFrame frame = new IlvConstantModeManagerFrame();
    frame.setOpaque(true);
    frame.setMinimumMargin(5);
    frame.setForeground(Color.black);
    frame.setBackground(new Color(200, 200, 255, 128));
    frame.setShowingTitle(false);
    sample4.setFrame(frame);
    sample4.addNode(sample1, true);
    IlvGraphic n2 = sample1.copy();
    n2.move(0, 70);
    sample4.addNode(n2, true);
    IlvGraphic n3 = sample1.copy();
    n3.move(70, 0);
    sample4.addNode(n3, true);
    b = new InteractorButton(new MakeNodeInteractor(sample4), mgrview);
    b.setToolTipText(ResourceUtil.getString("CreateSubgraphConstant.tooltip"));
    controlBar.add(b);

    IlvHyperGrapher sample5 = new IlvHyperGrapher();
    IlvDefaultManagerFrame frame2 = new IlvDefaultManagerFrame();
    frame2.setOpaque(true);
    frame2.setTopMargin(5);
    frame2.setBottomMargin(5);
    frame2.setLeftMargin(5);
    frame2.setRightMargin(5);
    frame2.setForeground(Color.black);
    frame2.setBackground(new Color(255, 220, 200, 128));
    frame2.setShowingTitle(false);
    sample5.setFrame(frame2);
    sample5.addNode(sample1.copy(), true);
    n2 = sample1.copy();
    n2.move(0, 70);
    sample5.addNode(n2, true);
    n3 = sample1.copy();
    n3.move(70, 70);
    sample5.addNode(n3, true);
    b = new InteractorButton(new MakeNodeInteractor(sample5), mgrview);
    b.setToolTipText(ResourceUtil.getString("CreateSubgraphVariable.tooltip"));
    controlBar.add(b);

    controlBar.addSeparator();

    b = new InteractorButton(new IlvMakeHyperEdgeInteractor(), "gif/HyperEdge.gif", mgrview);
    b.setToolTipText(ResourceUtil.getString("CreateHyperEdge.tooltip"));
    controlBar.add(b);

    b = new InteractorButton(makeSegEdgeInteractor, "gif/SegmentedHyperEdge.gif", mgrview);
    b.setToolTipText(ResourceUtil.getString("CreateSegHyperEdge.tooltip"));
    controlBar.add(b);

    controlBar.addSeparator();

    b = new InteractorButton(makePinInteractor, "gif/PinConnector.gif", mgrview);
    b.setToolTipText(ResourceUtil.getString("InstallPinConnector.tooltip"));
    controlBar.add(b);

    b = new InteractorButton(new HyperEdgeConnectorInteractor(sampleConnector1),
        "gif/ClipConnector.gif", mgrview);
    b.setToolTipText(ResourceUtil.getString("InstallClipConnector.tooltip"));
    controlBar.add(b);

    b = new InteractorButton(new HyperEdgeConnectorInteractor(sampleConnector2),
        "gif/CenterConnector.gif", mgrview);
    b.setToolTipText(ResourceUtil.getString("InstallCenterConnector.tooltip"));
    controlBar.add(b);

    b = new InteractorButton(new HyperEdgeConnectorInteractor(null), "gif/NoConnector.gif",
        mgrview);
    b.setToolTipText(ResourceUtil.getString("DeinstallConnector.tooltip"));
    controlBar.add(b);

    // alternative implementation of "no connector"
    /*
     * b = new InteractorButton(new HyperEdgeConnectorInteractor(sampleConnector3),
     * "gif/NoConnector.gif", mgrview);
     * b.setToolTipText(ResourceUtil.getString("DeinstallConnector.tooltip")); controlBar.add(b);
     */
  }

  /**
   * Create the menu.
   */
  private void createMenus(JMenuBar menuBar) {
    JMenu menu = new JMenu(ResourceUtil.getString("File.menu.title"));
    menuBar.add(menu);

    JMenuItem mitem = new JMenuItem(ResourceUtil.getString("ClearWindow.menuitem.title"));
    mitem.setActionCommand("ClearWindow");
    mitem.addActionListener(actionListener);
    menu.add(mitem);

    mitem = new JMenuItem(ResourceUtil.getString("LoadFile.menuitem.title"));
    mitem.setActionCommand("LoadFile");
    mitem.addActionListener(actionListener);
    menu.add(mitem);

    mitem = new JMenuItem(ResourceUtil.getString("SaveFile.menuitem.title"));
    mitem.setActionCommand("SaveFile");
    mitem.addActionListener(actionListener);
    menu.add(mitem);

    mitem = new JMenuItem(ResourceUtil.getString("Quit.menuitem.title"));
    mitem.setActionCommand("Quit");
    mitem.addActionListener(actionListener);
    menu.add(mitem);

    menu = new JMenu(ResourceUtil.getString("Options.menu.title"));
    menuBar.add(menu);

    mitem = new JMenuItem(ResourceUtil.getString("ConnectorOptions.menuitem.title"));
    mitem.setActionCommand("ConnectorOptions");
    mitem.addActionListener(actionListener);
    menu.add(mitem);

    mitem = new JMenuItem(ResourceUtil.getString("GridOptions.menuitem.title"));
    mitem.setActionCommand("GridOptions");
    mitem.addActionListener(actionListener);
    menu.add(mitem);

    mitem = new JMenuItem(ResourceUtil.getString("CrossingOptions.menuitem.title"));
    mitem.setActionCommand("CrossingOptions");
    mitem.addActionListener(actionListener);
    menu.add(mitem);
  }

  /**
   * Action listener for the menu.
   */
  private ActionListener actionListener = new ActionListener() {
    Override
    public void actionPerformed(ActionEvent action) {
      String command = action.getActionCommand();
      if (command.equals("ClearWindow")) {
        grapher.deleteAll(true);
      } else if (command.equals("LoadFile")) {
        JFileChooser chooser = new JFileChooser(".");
        chooser.addChoosableFileFilter(new javax.swing.filechooser.FileFilter() {
          Override
          public boolean accept(File f) {
            return f.isDirectory() || f.getName().endsWith(".ivl");
          }

          Override
          public String getDescription() {
            return "*.ivl";
          }
        });
        chooser.setDialogTitle(ResourceUtil.getString("Load.filechooser.title"));
        int returnVal = chooser.showOpenDialog(HyperGrapherDemo.this);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
          String fileName = chooser.getSelectedFile().getPath();
          try {
            grapher.deleteAll(true);
            grapher.read(fileName);
            grapher.reDraw();
          } catch (Exception ex) {
            JOptionPane.showMessageDialog(HyperGrapherDemo.this,
                "Cannot load file '" + fileName + "'", "Read Error", JOptionPane.ERROR_MESSAGE);
            ex.printStackTrace();
          }
        }
      } else if (command.equals("SaveFile")) {
        JFileChooser chooser = new JFileChooser(".");
        chooser.addChoosableFileFilter(new javax.swing.filechooser.FileFilter() {
          Override
          public boolean accept(File f) {
            return f.isDirectory() || f.getName().endsWith(".ivl");
          }

          Override
          public String getDescription() {
            return "*.ivl";
          }
        });
        chooser.setDialogTitle(ResourceUtil.getString("Save.filechooser.title"));
        int returnVal = chooser.showSaveDialog(HyperGrapherDemo.this);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
          String fileName = chooser.getSelectedFile().getPath();
          if (!fileName.endsWith(".ivl"))
            fileName = fileName + ".ivl";
          try {
            grapher.write(fileName);
          } catch (IOException ex) {
            JOptionPane.showMessageDialog(HyperGrapherDemo.this,
                "Cannot save file '" + fileName + "'", "Write Error", JOptionPane.ERROR_MESSAGE);
            ex.printStackTrace();
          }
        }
      } else if (command.equals("Quit")) {
        System.exit(0);
      } else if (command.equals("ConnectorOptions")) {
        showConnectorOptionsDialog();
      } else if (command.equals("GridOptions")) {
        showGridOptionsDialog();
      } else if (command.equals("CrossingOptions")) {
        showCrossingOptionsDialog();
      }
    }
  };

  /**
   * Initialize the accelerators.
   */
  private void initAccelerators() {
    grapher.addAccelerator(
        new IlvDeleteSelectionAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_DELETE, 0));
    grapher.addAccelerator(
        new IlvIdentityAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_I, KeyEvent.CTRL_MASK));
    grapher.addAccelerator(
        new IlvZoomOutAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_U, KeyEvent.CTRL_MASK));
    grapher.addAccelerator(
        new IlvZoomInAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_Z, KeyEvent.CTRL_MASK));
    grapher.addAccelerator(
        new IlvFitToSizeAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_F, KeyEvent.CTRL_MASK));
    grapher.addAccelerator(
        new IlvRotateAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_R, KeyEvent.CTRL_MASK));
    grapher.addAccelerator(new IlvScrollUpAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_UP, 0));
    grapher.addAccelerator(new IlvScrollDownAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_DOWN, 0));
    grapher
        .addAccelerator(new IlvScrollRightAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_RIGHT, 0));
    grapher.addAccelerator(new IlvScrollLeftAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_LEFT, 0));
  }

  /**
   * Fill the contents of a grapher.
   */
  private void fillGrapher() {
    grapher.setLinksInsertionLayer(3);

    IlvHyperEdgeEnd end;
    IlvRect bbox;

    IlvGeneralPath sample = new IlvGeneralPath(new Rectangle2D.Double(0, 0, 40, 40));
    sample.setFillPaint(new GradientPaint(0, 0, Color.white, 20, 30, Color.yellow));
    sample.setStrokePaint(Color.black);
    sample.setFillOn(true);
    sample.setStrokeOn(true);
    sample.setPaintAbsolute(false);

    // first create the nodes
    IlvGraphic[] nodes = new IlvGraphic[24];
    IlvHyperGrapher subgraph1 = new IlvHyperGrapher();
    IlvHyperGrapher subgraph2 = new IlvHyperGrapher();

    IlvDefaultManagerFrame frame = new IlvDefaultManagerFrame();
    frame.setOpaque(true);
    frame.setTopMargin(5);
    frame.setBottomMargin(5);
    frame.setLeftMargin(5);
    frame.setRightMargin(5);
    frame.setForeground(Color.black);
    frame.setBackground(new Color(255, 220, 200, 128));
    frame.setShowingTitle(false);
    subgraph1.setFrame(frame);

    frame = new IlvDefaultManagerFrame();
    frame.setOpaque(true);
    frame.setTopMargin(5);
    frame.setBottomMargin(5);
    frame.setLeftMargin(5);
    frame.setRightMargin(5);
    frame.setForeground(Color.black);
    frame.setBackground(new Color(225, 220, 255, 128));
    frame.setShowingTitle(false);
    subgraph2.setFrame(frame);

    for (int i = 0; i < 4; i++) {
      for (int j = 0; j < 6; j++) {
        if (i == 1 && j == 3) {
          nodes[j * 4 + i] = nodes[(j - 1) * 4 + i];
          nodes[j * 4 + i].resize(40, 120);
        } else if (i == 3 && j == 2) {
          nodes[j * 4 + i] = nodes[(j - 1) * 4 + i];
          nodes[j * 4 + i].resize(40, 120);
        } else if (i == 3 && j == 4) {
          nodes[j * 4 + i] = nodes[(j - 1) * 4 + i];
          nodes[j * 4 + i].resize(40, 120);
        } else {
          nodes[j * 4 + i] = sample.copy();
          nodes[j * 4 + i].moveResize(new IlvRect(10 + i * 120, 10 + j * 80, 40, 40));
        }
      }
    }

    // add the nodes

    IlvHyperGrapher owner;
    for (int i = 0; i < 4; i++) {
      for (int j = 0; j < 6; j++) {
        owner = grapher;
        if (i < 2 && j >= 4)
          owner = subgraph2;
        if (i == 2 && j >= 4)
          owner = subgraph1;
        if (nodes[j * 4 + i].getGraphicBag() == null)
          owner.addNode(nodes[j * 4 + i], true);
      }
    }

    subgraph1.addNode(subgraph2, true);
    grapher.addNode(subgraph1, true);

    // add a simple hyperedge

    IlvHyperEdge hyperEdge = new IlvHyperEdge();
    hyperEdge.setLineWidth(2);
    for (int i = 0; i < 3; i++) {
      end = hyperEdge.addFrom(nodes[i * 4]);
      bbox = nodes[i * 4].boundingBox(null);
      end.setPosition(new IlvPoint(bbox.x + bbox.width, bbox.y + 0.5 * bbox.height), null);
    }
    for (int i = 0; i < 2; i++) {
      end = hyperEdge.addTo(nodes[i * 4 + 1]);
      bbox = nodes[i * 4 + 1].boundingBox(null);
      end.setPosition(new IlvPoint(bbox.x, bbox.y + 0.5 * bbox.height), null);
    }

    grapher.addHyperEdge(hyperEdge, true);

    // add a segmented hyperedge

    IlvSegmentedHyperEdge shyperEdge = new IlvSegmentedHyperEdge();
    IlvSegmentedHyperEdge.Segment seg1, seg2, seg3, seg4, seg5, seg6, seg7, seg8, seg9, seg10;
    shyperEdge.setLineWidth(2);
    shyperEdge.setAutoConnect(false);

    end = shyperEdge.addFrom(nodes[8]);
    bbox = nodes[8].boundingBox(null);
    end.setPosition(new IlvPoint(bbox.x + bbox.width, bbox.y + 0.5 * bbox.height), null);
    seg1 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[9]);
    bbox = nodes[9].boundingBox(null);
    end.setPosition(new IlvPoint(bbox.x, bbox.y + 0.3 * bbox.height), null);
    seg2 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[9]);
    end.setPosition(new IlvPoint(bbox.x, bbox.y + 0.7 * bbox.height), null);
    seg3 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addFrom(nodes[12]);
    bbox = nodes[12].boundingBox(null);
    end.setPosition(new IlvPoint(bbox.x + bbox.width, bbox.y + 0.5 * bbox.height), null);
    seg4 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[17]);
    bbox = nodes[17].boundingBox(null);
    end.setPosition(new IlvPoint(bbox.x, bbox.y + 0.3 * bbox.height), null);
    seg5 = shyperEdge.addSegment(end, 0);

    seg6 = shyperEdge.addSegment(seg5, 90, 110, 0, null);
    shyperEdge.connectSegments(seg1, seg6);
    shyperEdge.connectSegments(seg2, seg6);
    shyperEdge.connectSegments(seg3, seg6);
    shyperEdge.connectSegments(seg4, seg6);

    shyperEdge.setAutoConnect(true);
    grapher.addHyperEdge(shyperEdge, true);

    // another segmented hyperedge

    shyperEdge = new IlvSegmentedHyperEdge();
    shyperEdge.setLineWidth(2);
    shyperEdge.setAutoConnect(false);

    end = shyperEdge.addFrom(nodes[12]);
    bbox = nodes[12].boundingBox(null);
    end.setPosition(new IlvPoint(bbox.x + 0.5 * bbox.width, bbox.y + bbox.height), null);
    seg1 = shyperEdge.addSegment(end, 90);

    end = shyperEdge.addTo(nodes[16]);
    bbox = nodes[16].boundingBox(null);
    end.setPosition(new IlvPoint(bbox.x + 0.5 * bbox.width, bbox.y), null);
    seg2 = shyperEdge.addSegment(end, 90);

    end = shyperEdge.addTo(nodes[17]);
    bbox = nodes[17].boundingBox(null);
    end.setPosition(new IlvPoint(bbox.x, bbox.y + 0.7 * bbox.height), null);
    seg3 = shyperEdge.addSegment(end, 0);

    seg4 = shyperEdge.addSegment(seg3, 90, 90, 0, null);
    seg5 = shyperEdge.addSegment(seg4, 0, 0, 310, null);
    shyperEdge.connectSegments(seg1, seg5);
    shyperEdge.connectSegments(seg2, seg5);

    shyperEdge.setAutoConnect(true);
    grapher.addHyperEdge(shyperEdge, true);

    // yet another segmented hyperedge

    shyperEdge = new IlvSegmentedHyperEdge();
    shyperEdge.setLineWidth(2);
    shyperEdge.setAutoConnect(false);

    end = shyperEdge.addFrom(nodes[13]);
    bbox = nodes[13].boundingBox(null);
    end.setPosition(new IlvPoint(bbox.x + 0.5 * bbox.width, bbox.y + bbox.height), null);
    seg1 = shyperEdge.addSegment(end, 90);

    end = shyperEdge.addTo(nodes[17]);
    bbox = nodes[17].boundingBox(null);
    end.setPosition(new IlvPoint(bbox.x + 0.5 * bbox.width, bbox.y), null);
    seg2 = shyperEdge.addSegment(end, 90);

    end = shyperEdge.addTo(nodes[22]);
    bbox = nodes[22].boundingBox(null);
    end.setPosition(new IlvPoint(bbox.x, bbox.y + 0.5 * bbox.height), null);
    seg3 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[21]);
    bbox = nodes[21].boundingBox(null);
    end.setPosition(new IlvPoint(bbox.x, bbox.y + 0.5 * bbox.height), null);
    seg4 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[20]);
    bbox = nodes[20].boundingBox(null);
    end.setPosition(new IlvPoint(bbox.x + bbox.width, bbox.y + 0.5 * bbox.height), null);
    seg5 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[16]);
    bbox = nodes[16].boundingBox(null);
    end.setPosition(new IlvPoint(bbox.x + bbox.width, bbox.y + 0.5 * bbox.height), null);
    seg6 = shyperEdge.addSegment(end, 0);

    seg7 = shyperEdge.addSegment(seg6, 90, 70, 0, null);
    shyperEdge.connectSegments(seg7, seg5);
    shyperEdge.connectSegments(seg7, seg4);

    seg8 = shyperEdge.addSegment(seg7, 0, 0, 390, null);
    seg9 = shyperEdge.addSegment(seg8, 90, 190, 0, null);
    shyperEdge.connectSegments(seg9, seg3);

    seg10 = shyperEdge.addSegment(seg9, 0, 0, 310, null);
    shyperEdge.connectSegments(seg10, seg2);
    shyperEdge.connectSegments(seg10, seg1);

    shyperEdge.setAutoConnect(true);
    grapher.addHyperEdge(shyperEdge, true);

    // yet another segmented hyperedge

    shyperEdge = new IlvSegmentedHyperEdge();
    shyperEdge.setLineWidth(2);
    shyperEdge.setAutoConnect(false);

    end = shyperEdge.addFrom(nodes[18]);
    bbox = nodes[18].boundingBox(null);
    end.setPosition(new IlvPoint(bbox.x + 0.5 * bbox.width, bbox.y + bbox.height), null);
    seg1 = shyperEdge.addSegment(end, 90);

    end = shyperEdge.addTo(nodes[22]);
    bbox = nodes[22].boundingBox(null);
    end.setPosition(new IlvPoint(bbox.x + 0.2 * bbox.width, bbox.y), null);
    seg2 = shyperEdge.addSegment(end, 90);

    end = shyperEdge.addTo(nodes[22]);
    end.setPosition(new IlvPoint(bbox.x + 0.4 * bbox.width, bbox.y), null);
    seg3 = shyperEdge.addSegment(end, 90);

    end = shyperEdge.addTo(nodes[22]);
    end.setPosition(new IlvPoint(bbox.x + 0.6 * bbox.width, bbox.y), null);
    seg4 = shyperEdge.addSegment(end, 90);

    end = shyperEdge.addTo(nodes[22]);
    end.setPosition(new IlvPoint(bbox.x + 0.8 * bbox.width, bbox.y), null);
    seg5 = shyperEdge.addSegment(end, 90);

    seg6 = shyperEdge.addSegment(seg1, 0, 0, 390, null);
    shyperEdge.connectSegments(seg6, seg2);
    shyperEdge.connectSegments(seg6, seg3);
    shyperEdge.connectSegments(seg6, seg4);
    shyperEdge.connectSegments(seg6, seg5);

    shyperEdge.setAutoConnect(true);
    grapher.addHyperEdge(shyperEdge, true);

    // create pins

    IlvHyperEdgePinConnector conn;

    for (int i = 2; i < 4; i++) {
      for (int j = 0; j < 6; j++) {
        if (i == 2 && j > 3)
          continue;
        bbox = nodes[j * 4 + i].boundingBox(null);
        conn = new IlvHyperEdgePinConnector(nodes[j * 4 + i]);
        int n = 2;
        if (bbox.height > 40)
          n = 8;

        for (int k = 0; k < n; k++) {
          conn.addPin(new IlvHyperGrapherPin(new IlvPoint(0, (k + 1) / (n + 1.)),
              new IlvPoint(-4, 0), 4, 0), true);
        }
        for (int k = 0; k < n; k++) {
          conn.addPin(
              new IlvHyperGrapherPin(new IlvPoint(1, (k + 1) / (n + 1.)), new IlvPoint(4, 0), 4, 0),
              true);
        }
      }
    }

    // hyperedge from pins to pins

    shyperEdge = new IlvSegmentedHyperEdge();
    shyperEdge.setLineWidth(2);
    shyperEdge.setAutoConnect(false);

    end = shyperEdge.addFrom(nodes[2]);
    bbox = nodes[2].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 0.7 * bbox.height), null);
    seg1 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[3]);
    bbox = nodes[3].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 0.3 * bbox.height), null);
    seg2 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[3]);
    bbox = nodes[3].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 0.3 * bbox.height), null);
    seg3 = shyperEdge.addSegment(end, 0);

    seg4 = shyperEdge.addSegment(seg1, 90, 330, 0, null);
    shyperEdge.connectSegments(seg4, seg2);

    seg5 = shyperEdge.addSegment(seg4, 0, 0, 70, null);
    seg6 = shyperEdge.addSegment(seg5, 90, 450, 0, null);
    shyperEdge.connectSegments(seg6, seg3);

    shyperEdge.setAutoConnect(true);
    grapher.addHyperEdge(shyperEdge, true);

    // another hyperedge from pins to pins

    shyperEdge = new IlvSegmentedHyperEdge();
    shyperEdge.setLineWidth(2);
    shyperEdge.setAutoConnect(false);

    end = shyperEdge.addFrom(nodes[14]);
    bbox = nodes[14].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 0.3 * bbox.height), null);
    seg1 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[15]);
    bbox = nodes[15].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 53), null);
    seg2 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[7]);
    bbox = nodes[7].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 106), null);
    seg3 = shyperEdge.addSegment(end, 0);

    seg4 = shyperEdge.addSegment(seg1, 90, 295 + 4 * 14, 0, null);
    shyperEdge.connectSegments(seg4, seg2);
    shyperEdge.connectSegments(seg4, seg3);

    shyperEdge.setAutoConnect(true);
    grapher.addHyperEdge(shyperEdge, true);

    // another hyperedge from pins to pins

    shyperEdge = new IlvSegmentedHyperEdge();
    shyperEdge.setLineWidth(2);
    shyperEdge.setAutoConnect(false);

    end = shyperEdge.addFrom(nodes[14]);
    bbox = nodes[14].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 0.7 * bbox.height), null);
    seg1 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[15]);
    bbox = nodes[15].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 66), null);
    seg2 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[7]);
    bbox = nodes[7].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 93), null);
    seg3 = shyperEdge.addSegment(end, 0);

    seg4 = shyperEdge.addSegment(seg1, 90, 295 + 3 * 14, 0, null);
    shyperEdge.connectSegments(seg4, seg2);
    shyperEdge.connectSegments(seg4, seg3);

    shyperEdge.setAutoConnect(true);
    grapher.addHyperEdge(shyperEdge, true);

    // another hyperedge from pins to pins

    shyperEdge = new IlvSegmentedHyperEdge();
    shyperEdge.setLineWidth(2);
    shyperEdge.setAutoConnect(false);

    end = shyperEdge.addFrom(nodes[10]);
    bbox = nodes[10].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 0.3 * bbox.height), null);
    seg1 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[15]);
    bbox = nodes[15].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 80), null);
    seg2 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[7]);
    bbox = nodes[7].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 80), null);
    seg3 = shyperEdge.addSegment(end, 0);

    seg4 = shyperEdge.addSegment(seg1, 90, 295 + 2 * 14, 0, null);
    shyperEdge.connectSegments(seg4, seg2);
    shyperEdge.connectSegments(seg4, seg3);

    shyperEdge.setAutoConnect(true);
    grapher.addHyperEdge(shyperEdge, true);

    // another hyperedge from pins to pins

    shyperEdge = new IlvSegmentedHyperEdge();
    shyperEdge.setLineWidth(2);
    shyperEdge.setAutoConnect(false);

    end = shyperEdge.addFrom(nodes[10]);
    bbox = nodes[10].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 0.7 * bbox.height), null);
    seg1 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[15]);
    bbox = nodes[15].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 93), null);
    seg2 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[7]);
    bbox = nodes[7].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 66), null);
    seg3 = shyperEdge.addSegment(end, 0);

    seg4 = shyperEdge.addSegment(seg1, 90, 295 + 14, 0, null);
    shyperEdge.connectSegments(seg4, seg2);
    shyperEdge.connectSegments(seg4, seg3);

    end = shyperEdge.addTo(nodes[23]);
    bbox = nodes[23].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 13), null);
    seg5 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[23]);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 26), null);
    seg6 = shyperEdge.addSegment(end, 0);

    shyperEdge.connectSegments(seg4, seg5);
    shyperEdge.connectSegments(seg4, seg6);

    end = shyperEdge.addTo(nodes[10]);
    bbox = nodes[10].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 13), null);
    seg7 = shyperEdge.addSegment(end, 0);
    seg8 = shyperEdge.addSegment(seg7, 90, 175 + 3 * 14, 0, null);

    shyperEdge.connectSegments(seg8, seg3);

    shyperEdge.setAutoConnect(true);
    grapher.addHyperEdge(shyperEdge, true);

    // another hyperedge from pins to pins

    shyperEdge = new IlvSegmentedHyperEdge();
    shyperEdge.setLineWidth(2);
    shyperEdge.setAutoConnect(false);

    end = shyperEdge.addFrom(nodes[6]);
    bbox = nodes[6].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 0.3 * bbox.height), null);
    seg1 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[2]);
    bbox = nodes[2].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 0.7 * bbox.height), null);
    seg2 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[10]);
    bbox = nodes[10].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 0.7 * bbox.height), null);
    seg3 = shyperEdge.addSegment(end, 0);

    seg4 = shyperEdge.addSegment(seg1, 90, 175 + 2 * 14, 0, null);
    shyperEdge.connectSegments(seg4, seg2);
    shyperEdge.connectSegments(seg4, seg3);

    shyperEdge.setAutoConnect(true);
    grapher.addHyperEdge(shyperEdge, true);

    // another hyperedge from pins to pins

    shyperEdge = new IlvSegmentedHyperEdge();
    shyperEdge.setLineWidth(2);
    shyperEdge.setAutoConnect(false);

    end = shyperEdge.addFrom(nodes[6]);
    bbox = nodes[6].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 0.7 * bbox.height), null);
    seg1 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[2]);
    bbox = nodes[2].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 0.3 * bbox.height), null);
    seg2 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[14]);
    bbox = nodes[14].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 0.3 * bbox.height), null);
    seg3 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[14]);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 0.7 * bbox.height), null);
    seg4 = shyperEdge.addSegment(end, 0);

    seg5 = shyperEdge.addSegment(seg1, 90, 175 + 14, 0, null);
    shyperEdge.connectSegments(seg5, seg2);
    shyperEdge.connectSegments(seg5, seg3);
    shyperEdge.connectSegments(seg5, seg4);

    shyperEdge.setAutoConnect(true);
    grapher.addHyperEdge(shyperEdge, true);

    // another hyperedge from pins to pins

    shyperEdge = new IlvSegmentedHyperEdge();
    shyperEdge.setLineWidth(2);
    shyperEdge.setAutoConnect(false);

    end = shyperEdge.addFrom(nodes[23]);
    bbox = nodes[23].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 0.3 * bbox.height), null);
    seg1 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[19]);
    bbox = nodes[19].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 106), null);
    seg2 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[19]);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 93), null);
    seg3 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[19]);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 80), null);
    seg4 = shyperEdge.addSegment(end, 0);

    seg5 = shyperEdge.addSegment(seg1, 90, 450, 0, null);
    shyperEdge.connectSegments(seg5, seg2);
    shyperEdge.connectSegments(seg5, seg3);
    shyperEdge.connectSegments(seg5, seg4);

    shyperEdge.setAutoConnect(true);
    grapher.addHyperEdge(shyperEdge, true);

    // another hyperedge from pins to pins

    shyperEdge = new IlvSegmentedHyperEdge();
    shyperEdge.setLineWidth(2);
    shyperEdge.setAutoConnect(false);

    end = shyperEdge.addFrom(nodes[23]);
    bbox = nodes[23].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 0.7 * bbox.height), null);
    seg1 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[19]);
    bbox = nodes[19].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 67), null);
    seg2 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[19]);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 53), null);
    seg3 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[19]);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 40), null);
    seg4 = shyperEdge.addSegment(end, 0);

    seg5 = shyperEdge.addSegment(seg1, 90, 470, 0, null);
    shyperEdge.connectSegments(seg5, seg2);
    shyperEdge.connectSegments(seg5, seg3);
    shyperEdge.connectSegments(seg5, seg4);

    shyperEdge.setAutoConnect(true);
    grapher.addHyperEdge(shyperEdge, true);

    // hyperedge with 45 degree segment

    shyperEdge = new IlvSegmentedHyperEdge();
    shyperEdge.setLineWidth(2);
    shyperEdge.setAutoConnect(false);

    end = shyperEdge.addFrom(nodes[6]);
    bbox = nodes[6].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 0.7 * bbox.height), null);
    seg1 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[7]);
    bbox = nodes[7].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 13), null);
    seg2 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[7]);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x, bbox.y + 40), null);
    seg3 = shyperEdge.addSegment(end, 0);

    seg4 = shyperEdge.addSegment(seg1, -45, 320, 116, null);
    seg5 = shyperEdge.addSegment(seg1, 45, 320, 116, null);

    shyperEdge.connectSegments(seg4, seg2);
    shyperEdge.connectSegments(seg5, seg3);

    shyperEdge.setAutoConnect(true);
    grapher.addHyperEdge(shyperEdge, true);

    // hyperedge with variable angle segments

    shyperEdge = new IlvSegmentedHyperEdge();
    shyperEdge.setLineWidth(2);
    shyperEdge.setAutoConnect(false);

    end = shyperEdge.addFrom(nodes[15]);
    bbox = nodes[15].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 12), null);
    seg1 = shyperEdge.addSegment(end, 0);

    end = shyperEdge.addTo(nodes[7]);
    bbox = nodes[7].boundingBox(null);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 13), null);
    seg2 = shyperEdge.addSegment(end, 450, 100, null);

    end = shyperEdge.addTo(nodes[7]);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 26), null);
    seg3 = shyperEdge.addSegment(end, 450, 100, null);

    end = shyperEdge.addTo(nodes[7]);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 40), null);
    seg4 = shyperEdge.addSegment(end, 450, 100, null);

    end = shyperEdge.addTo(nodes[7]);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 53), null);
    seg5 = shyperEdge.addSegment(end, 450, 100, null);

    end = shyperEdge.addTo(nodes[7]);
    conn = (IlvHyperEdgePinConnector) IlvHyperEdgeConnector.Get(end);
    conn.connect(end, new IlvPoint(bbox.x + bbox.width, bbox.y + 66), null);
    seg6 = shyperEdge.addSegment(end, 450, 100, null);

    seg7 = shyperEdge.addSegment(seg1, 90, 470, 0, null);
    seg8 = shyperEdge.addSegment(seg7, 0, 0, bbox.y + 40, null);

    shyperEdge.connectSegments(seg8, seg2);
    shyperEdge.connectSegments(seg8, seg3);
    shyperEdge.connectSegments(seg8, seg4);
    shyperEdge.connectSegments(seg8, seg5);
    shyperEdge.connectSegments(seg8, seg6);

    shyperEdge.setAutoConnect(true);
    grapher.addHyperEdge(shyperEdge, true);
  }

  // ---------------------------------------------------------------------------
  // Options for hyperedge connectors

  // whether we allow moving connection points
  private boolean allowMovingConnPts = true;

  // whether IlvHyperEdgeClippingConnector clips to center
  private boolean clipToCenter = false;

  // whether IlvHyperGrapherPins are visible
  private boolean pinsVisible = true;

  // whether IlvHyperGrapherPins are movable
  private boolean pinsMovable = true;

  // whether IlvHyperGrapherPins allow multiple hyperedge ends
  private boolean allowMultiEnds = false;

  // whether pins are in node layer, in front of node, or behind node
  private int layerModeIndex = 0;
  private static final int[] layerModes = new int[] {IlvHyperEdgeConnector.IN_NODE_LAYER,
      IlvHyperEdgeConnector.IN_FRONT, IlvHyperEdgeConnector.IN_BACK};

  // Whether pin dragging is restricted to the border of the bounding box
  private boolean restrictToNodeBorder = true;

  // The offset of the pin
  private int pinOffsetIndex = 0;
  private static final int[] pinOffsets = new int[] {3, 0, -3};

  /**
   * Shows the hyperedge connector options dialog.
   */
  private void showConnectorOptionsDialog() {
    Frame frame = JOptionPane.getFrameForComponent(this);
    JDialog dialog = new ConnectorOptionsDialog(frame, this);
    dialog.setVisible(true);
    evaluateConnectorOptions();
  }

  /**
   * Evaluates the hyperedge connector options.
   */
  private void evaluateConnectorOptions() {
    // use buffered enumeration since setLayerMode modifies the content
    // of the grapher
    IlvGraphicEnumeration e = new IlvBufferedGraphicEnumeration(grapher.getObjects(true));
    while (e.hasMoreElements()) {
      IlvGraphic g = e.nextElement();
      final IlvHyperEdgeConnector conn = IlvHyperEdgeConnector.GetAttached(g);
      if (conn != null) {
        g.getGraphicBag().applyToObject(g, new IlvApplyObject() {
          Override
          public void apply(IlvGraphic g, Object arg) {
            conn.setConnectionPointMoveAllowed(allowMovingConnPts);
            conn.setLayerMode(layerModes[layerModeIndex]);
            if (conn instanceof IlvHyperEdgeClippingConnector) {
              IlvHyperEdgeClippingConnector cconn = (IlvHyperEdgeClippingConnector) conn;
              cconn.setClipToCenter(clipToCenter);
            }
            if (conn instanceof IlvHyperEdgePinConnector) {
              IlvHyperEdgePinConnector pconn = (IlvHyperEdgePinConnector) conn;
              pconn.setPinDraggingRestrictedToNodeBorder(restrictToNodeBorder);
              Iterator<?> iter = pconn.getPins();
              while (iter.hasNext()) {
                IlvHyperGrapherPin pin = (IlvHyperGrapherPin) iter.next();
                pin.setVisible(pinsVisible);
                pin.setMovable(pinsMovable);
                // can only change this option if nothing is yet connected
                if (!pin.getConnectedEnds().hasNext())
                  pin.setAllowMultiConnection(allowMultiEnds);
                if (!pinsVisible)
                  pin.setDirection(IlvDirection.Center);
                else
                  pin.setDirection(0);
                if (restrictToNodeBorder) {
                  int offs = pinOffsets[pinOffsetIndex];
                  IlvHyperEdgePinConnector.snapPinToNodeBorder(pin, offs);
                } else {
                  IlvPoint p = pin.getPosition(null);
                  pin.setAbsoluteOffset(0, 0);
                  pin.setPosition(p, null);
                }
              }
            }
          }
        }, null, true);
      }
    }

    // the sample connectors are not attached to any graphic, so they can be
    // changed directly.
    sampleConnector1.setConnectionPointMoveAllowed(allowMovingConnPts);
    sampleConnector2.setConnectionPointMoveAllowed(allowMovingConnPts);
    sampleConnector3.setConnectionPointMoveAllowed(allowMovingConnPts);
    sampleConnector1.setClipToCenter(clipToCenter);
    sampleConnector1.setLayerMode(layerModes[layerModeIndex]);
    sampleConnector2.setLayerMode(layerModes[layerModeIndex]);
    sampleConnector3.setLayerMode(layerModes[layerModeIndex]);
    makePinInteractor.setConnectionPointMoveAllowed(allowMovingConnPts);
    makePinInteractor.setPinLayerMode(layerModes[layerModeIndex]);
    makePinInteractor.setPinVisible(pinsVisible);
    makePinInteractor.setPinMovable(pinsMovable);
    if (!restrictToNodeBorder)
      makePinInteractor.setPinOffset(0);
    else
      makePinInteractor.setPinOffset(pinOffsets[pinOffsetIndex]);
    makePinInteractor.setPinDraggingRestrictedToNodeBorder(restrictToNodeBorder);
    if (!pinsVisible)
      makePinInteractor.setPinDirection(IlvDirection.Center);
    else
      makePinInteractor.setPinDirection(0);
    makePinInteractor.setPinAllowMultiConnection(allowMultiEnds);
  }

  /**
   * The dialog class to show the hyperedge connector options.
   */
  class ConnectorOptionsDialog extends JDialog implements ActionListener {
    public ConnectorOptionsDialog(Frame frame, Component locationComp) {
      super(frame, ResourceUtil.getString("ConnectorOptions.dialog.title"), true);
      Container contentPane = getContentPane();
      contentPane.setLayout(new BorderLayout());

      JPanel panel = new JPanel();
      panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
      contentPane.add(BorderLayout.CENTER, panel);

      addCheckBox(panel, "AllowMovingConnPts", this, allowMovingConnPts);
      addCheckBox(panel, "ClipToCenter", this, clipToCenter);
      addCheckBox(panel, "PinsWithMultiEnds", this, allowMultiEnds);
      addCheckBox(panel, "PinsVisible", this, pinsVisible);
      addCheckBox(panel, "PinsMovable", this, pinsMovable);
      addCheckBox(panel, "PinsAtBorder", this, restrictToNodeBorder);
      addListBox(panel, "PinLayer", new String[] {"InNodeLayer", "InFront", "InBack"}, this,
          layerModeIndex);
      addListBox(panel, "PinOffset", new String[] {"Outside", "Center", "Inside"}, this,
          pinOffsetIndex);

      layoutDialogPanel(panel);

      JButton okayButton = new JButton(ResourceUtil.getString("Okay.button"));
      okayButton.setActionCommand("Okay");
      okayButton.addActionListener(this);
      JPanel aux = new JPanel(new FlowLayout(FlowLayout.CENTER));
      aux.add(okayButton);
      contentPane.add(BorderLayout.SOUTH, aux);
      pack();
      setLocationRelativeTo(locationComp);
    }

    Override
    public void actionPerformed(ActionEvent e) {
      if ("AllowMovingConnPts".equals(e.getActionCommand())) {
        JCheckBox box = (JCheckBox) e.getSource();
        allowMovingConnPts = box.isSelected();
      } else if ("ClipToCenter".equals(e.getActionCommand())) {
        JCheckBox box = (JCheckBox) e.getSource();
        clipToCenter = box.isSelected();
      } else if ("PinsWithMultiEnds".equals(e.getActionCommand())) {
        JCheckBox box = (JCheckBox) e.getSource();
        allowMultiEnds = box.isSelected();
      } else if ("PinsVisible".equals(e.getActionCommand())) {
        JCheckBox box = (JCheckBox) e.getSource();
        pinsVisible = box.isSelected();
      } else if ("PinsMovable".equals(e.getActionCommand())) {
        JCheckBox box = (JCheckBox) e.getSource();
        pinsMovable = box.isSelected();
      } else if ("PinsAtBorder".equals(e.getActionCommand())) {
        JCheckBox box = (JCheckBox) e.getSource();
        restrictToNodeBorder = box.isSelected();
      } else if ("PinLayer".equals(e.getActionCommand())) {
        SuppressWarnings("unchecked")
        JComboBox<String> box = (JComboBox<String>) e.getSource();
        layerModeIndex = box.getSelectedIndex();
      } else if ("PinOffset".equals(e.getActionCommand())) {
        SuppressWarnings("unchecked")
        JComboBox<String> box = (JComboBox<String>) e.getSource();
        pinOffsetIndex = box.getSelectedIndex();
      } else if ("Okay".equals(e.getActionCommand())) {
        setVisible(false);
      }
    }
  }

  // ---------------------------------------------------------------------------
  // Options for grid

  private boolean gridVisible = false;
  private boolean gridActive = false;
  private float gridSpacingX = 10;
  private float gridSpacingY = 10;
  private float gridBaseX = 0;
  private float gridBaseY = 0;

  /**
   * Shows the grid options dialog.
   */
  private void showGridOptionsDialog() {
    Frame frame = JOptionPane.getFrameForComponent(this);
    JDialog dialog = new GridOptionsDialog(frame, this);
    dialog.setVisible(true);
    evaluateGridOptions();
  }

  /**
   * Evaluates the hyperedge connector options.
   */
  private void evaluateGridOptions() {
    if (gridVisible || gridActive) {
      mgrview.setGrid(new IlvGrid(Color.black, new IlvPoint(gridBaseX, gridBaseY), gridSpacingX,
          gridSpacingY, gridVisible, gridActive));
    } else {
      mgrview.setGrid(null);
    }
    grapher.reDraw();
  }

  /**
   * The dialog class to show the grid options.
   */
  class GridOptionsDialog extends JDialog implements ActionListener {
    public GridOptionsDialog(Frame frame, Component locationComp) {
      super(frame, ResourceUtil.getString("GridOptions.dialog.title"), true);
      Container contentPane = getContentPane();
      contentPane.setLayout(new BorderLayout());

      JPanel panel = new JPanel();
      panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
      contentPane.add(BorderLayout.CENTER, panel);

      addCheckBox(panel, "GridVisible", this, gridVisible);
      addCheckBox(panel, "GridActive", this, gridActive);
      addTextBox(panel, "GridSpacingX", 8, this, "" + gridSpacingX);
      addTextBox(panel, "GridSpacingY", 8, this, "" + gridSpacingY);
      addTextBox(panel, "GridBaseX", 8, this, "" + gridBaseX);
      addTextBox(panel, "GridBaseY", 8, this, "" + gridBaseY);

      layoutDialogPanel(panel);

      JButton okayButton = new JButton(ResourceUtil.getString("Okay.button"));
      okayButton.setActionCommand("Okay");
      okayButton.addActionListener(this);
      JPanel aux = new JPanel(new FlowLayout(FlowLayout.CENTER));
      aux.add(okayButton);
      contentPane.add(BorderLayout.SOUTH, aux);
      pack();
      setLocationRelativeTo(locationComp);
    }

    Override
    public void actionPerformed(ActionEvent e) {
      if ("GridVisible".equals(e.getActionCommand())) {
        JCheckBox box = (JCheckBox) e.getSource();
        gridVisible = box.isSelected();
      } else if ("GridActive".equals(e.getActionCommand())) {
        JCheckBox box = (JCheckBox) e.getSource();
        gridActive = box.isSelected();
      } else if ("GridSpacingX".equals(e.getActionCommand())) {
        JTextField box = (JTextField) e.getSource();
        gridSpacingX = parseFloat(box.getText(), 10);
      } else if ("GridSpacingY".equals(e.getActionCommand())) {
        JTextField box = (JTextField) e.getSource();
        gridSpacingY = parseFloat(box.getText(), 10);
      } else if ("GridBaseX".equals(e.getActionCommand())) {
        JTextField box = (JTextField) e.getSource();
        gridBaseX = parseFloat(box.getText(), 0);
      } else if ("GridBaseY".equals(e.getActionCommand())) {
        JTextField box = (JTextField) e.getSource();
        gridBaseY = parseFloat(box.getText(), 0);
      } else if ("Okay".equals(e.getActionCommand())) {
        setVisible(false);
      }
    }
  }

  // ---------------------------------------------------------------------------
  // Options for crossings

  private boolean crossingEnabled = false;
  private float gapSize = 2;
  private boolean gapZoomable = true;
  private int layerOfCrossingGraphic = -1;

  /**
   * Shows the grid options dialog.
   */
  private void showCrossingOptionsDialog() {
    Frame frame = JOptionPane.getFrameForComponent(this);
    JDialog dialog = new CrossingOptionsDialog(frame, this);
    dialog.setVisible(true);
    evaluateCrossingOptions();
  }

  /**
   * Evaluates the hyperedge crossing options.
   */
  private void evaluateCrossingOptions() {
    IlvCrossingCalculation.setCrossingEnabled(grapher, true, null, crossingEnabled, true);
    IlvCrossingCalculation.setGap(grapher, true, null, gapSize, true);
    IlvCrossingCalculation.setGapZoomable(grapher, true, null, gapZoomable, true);
    IlvCrossingCalculation.setLayerOfCrossingGraphic(grapher, true, null, layerOfCrossingGraphic,
        true);
    makeSegEdgeInteractor.setCrossingEnabled(crossingEnabled);
    makeSegEdgeInteractor.setGap(gapSize);
    makeSegEdgeInteractor.setGapZoomable(gapZoomable);
    makeSegEdgeInteractor.setLayerOfCrossingGraphic(layerOfCrossingGraphic);
  }

  /**
   * The dialog class to show the crossing options.
   */
  class CrossingOptionsDialog extends JDialog implements ActionListener {
    public CrossingOptionsDialog(Frame frame, Component locationComp) {
      super(frame, ResourceUtil.getString("CrossingOptions.dialog.title"), true);
      Container contentPane = getContentPane();
      contentPane.setLayout(new BorderLayout());

      JPanel panel = new JPanel();
      panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
      contentPane.add(BorderLayout.CENTER, panel);

      addCheckBox(panel, "CrossingEnabled", this, crossingEnabled);
      addCheckBox(panel, "GapZoomable", this, gapZoomable);
      addTextBox(panel, "GapSize", 8, this, "" + gapSize);
      addTextBox(panel, "LayerOfCrossGraphic", 8, this, "" + layerOfCrossingGraphic);

      layoutDialogPanel(panel);

      JButton okayButton = new JButton(ResourceUtil.getString("Okay.button"));
      okayButton.setActionCommand("Okay");
      okayButton.addActionListener(this);
      JPanel aux = new JPanel(new FlowLayout(FlowLayout.CENTER));
      aux.add(okayButton);
      contentPane.add(BorderLayout.SOUTH, aux);
      pack();
      setLocationRelativeTo(locationComp);
    }

    Override
    public void actionPerformed(ActionEvent e) {
      if ("CrossingEnabled".equals(e.getActionCommand())) {
        JCheckBox box = (JCheckBox) e.getSource();
        crossingEnabled = box.isSelected();
      } else if ("GapSize".equals(e.getActionCommand())) {
        JTextField box = (JTextField) e.getSource();
        gapSize = parseFloat(box.getText(), 2);
      } else if ("GapZoomable".equals(e.getActionCommand())) {
        JCheckBox box = (JCheckBox) e.getSource();
        gapZoomable = box.isSelected();
      } else if ("LayerOfCrossGraphic".equals(e.getActionCommand())) {
        JTextField box = (JTextField) e.getSource();
        layerOfCrossingGraphic = (int) parseFloat(box.getText(), -1);
      } else if ("Okay".equals(e.getActionCommand())) {
        setVisible(false);
      }
    }
  }

  // ---------------------------------------------------------------------------
  // Simple dialog build options

  /**
   * Adds a check box to the panel.
   */
  private static void addCheckBox(JPanel panel, String labelKey, ActionListener actionListener,
      boolean defaultValue) {
    JPanel aux = new JPanel();
    aux.setLayout(new FlowLayout(FlowLayout.LEADING));
    JCheckBox cbox = new JCheckBox(ResourceUtil.getString(labelKey + ".title"), defaultValue);
    cbox.setToolTipText(ResourceUtil.getString(labelKey + ".tooltip"));
    cbox.setActionCommand(labelKey);
    cbox.addActionListener(actionListener);
    aux.add(cbox);
    panel.add(aux);

  }

  /**
   * Adds a list box to the panel.
   */
  private static void addListBox(JPanel panel, String labelKey, String[] optionKeys,
      ActionListener actionListener, int defaultIndex) {
    JPanel aux = new JPanel();
    aux.setLayout(new FlowLayout(FlowLayout.LEADING));
    JLabel label = new JLabel(ResourceUtil.getString(labelKey + ".title"));
    label.setToolTipText(ResourceUtil.getString(labelKey + ".tooltip"));
    JComboBox<String> cbox = new JComboBox<String>();
    for (int i = 0; i < optionKeys.length; i++) {
      String s = ResourceUtil.getString(optionKeys[i] + ".option");
      cbox.addItem(s);
    }
    cbox.setSelectedIndex(defaultIndex);
    cbox.setActionCommand(labelKey);
    cbox.addActionListener(actionListener);
    aux.add(label);
    aux.add(cbox);
    panel.add(aux);
  }

  /**
   * Adds a text field to the panel.
   */
  private static void addTextBox(JPanel panel, String labelKey, int columns,
      ActionListener actionListener, String defaultText) {
    JPanel aux = new JPanel();
    aux.setLayout(new FlowLayout(FlowLayout.LEADING));
    JLabel label = new JLabel(ResourceUtil.getString(labelKey + ".title"));
    label.setToolTipText(ResourceUtil.getString(labelKey + ".tooltip"));
    final JTextField tbox = new JTextField(columns);
    tbox.setText(defaultText);
    tbox.setActionCommand(labelKey);
    tbox.addActionListener(actionListener);
    tbox.addFocusListener(new FocusListener() {
      Override
      public void focusGained(FocusEvent e) {}

      Override
      public void focusLost(FocusEvent e) {
        tbox.postActionEvent();
      }
    });
    aux.add(label);
    aux.add(tbox);
    panel.add(aux);
  }

  /**
   * Layout a panel that was filled with {@link #addCheckBox},
   * {@link #addListBox} etc.
   */
  private static void layoutDialogPanel(JPanel panel) {
    int maxWidth1 = 0;
    int maxWidth2 = 0;
    for (int i = 0; i < panel.getComponentCount(); i++) {
      Component c = panel.getComponent(i);
      if (c instanceof JPanel) {
        JPanel aux = (JPanel) c;
        if (aux.getComponentCount() > 1) {
          Component c1 = aux.getComponent(0);
          Component c2 = aux.getComponent(1);
          Dimension d1 = c1.getPreferredSize();
          Dimension d2 = c2.getPreferredSize();
          maxWidth1 = Math.max(maxWidth1, d1.width);
          maxWidth2 = Math.max(maxWidth2, d2.width);
        }
      }
    }
    for (int i = 0; i < panel.getComponentCount(); i++) {
      Component c = panel.getComponent(i);
      if (c instanceof JPanel) {
        JPanel aux = (JPanel) c;
        if (aux.getComponentCount() > 1) {
          JComponent c1 = (JComponent) aux.getComponent(0);
          JComponent c2 = (JComponent) aux.getComponent(1);
          Dimension d1 = c1.getPreferredSize();
          Dimension d2 = c2.getPreferredSize();
          d1.width = maxWidth1;
          d2.width = maxWidth2;
          c1.setPreferredSize(d1);
          c2.setPreferredSize(d2);
        }
      }
    }
  }

  /**
   * Parse a string to float.
   */
  private static float parseFloat(String txt, float defValue) {
    try {
      return Float.parseFloat(txt.trim());
    } catch (NumberFormatException ex) {
      return defValue;
    }
  }

  // ---------------------------------------------------------------------------

  /**
   * Allows you to run the demo as a standalone application.
   */
  public static void main(String[] arg) {
    HyperGrapherDemo app = new HyperGrapherDemo();
    app.init();

    JFrame frame = new JFrame("Hypergrapher Example");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(600, 600);
    frame.getContentPane().add(app);
    frame.setVisible(true);
  }
}