/*
* Licensed Materials - Property of Rogue Wave Software, Inc.
* © Copyright Rogue Wave Software, Inc. 2014, 2017
* © 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.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.SystemColor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import ilog.views.IlvApplyObject;
import ilog.views.IlvDirection;
import ilog.views.IlvGraphic;
import ilog.views.IlvGraphicEnumeration;
import ilog.views.IlvHandlesSelection;
import ilog.views.IlvManager;
import ilog.views.IlvManagerView;
import ilog.views.IlvPoint;
import ilog.views.IlvRect;
import ilog.views.IlvTransformer;
import ilog.views.event.ManagerContentChangedEvent;
import ilog.views.event.ManagerContentChangedListener;
import ilog.views.event.ManagerSelectionChangedEvent;
import ilog.views.event.ManagerSelectionListener;
import ilog.views.event.TransformerChangedEvent;
import ilog.views.event.TransformerListener;
import ilog.views.graphic.IlvEllipse;
import ilog.views.graphic.IlvIcon;
import ilog.views.graphic.IlvLine;
import ilog.views.graphic.IlvRectangle;
import ilog.views.graphic.IlvZoomableLabel;
import ilog.views.graphlayout.labellayout.IlvDefaultLabelingModel;
import ilog.views.graphlayout.labellayout.IlvLabelLayout;
import ilog.views.graphlayout.labellayout.IlvLabelLayoutReport;
import ilog.views.graphlayout.labellayout.LabelLayoutEvent;
import ilog.views.graphlayout.labellayout.LabelLayoutEventListener;
import ilog.views.graphlayout.labellayout.annealing.IlvAnnealingLabelLayout;
import ilog.views.graphlayout.labellayout.annealing.IlvAnnealingPointLabelDescriptor;
import ilog.views.graphlayout.labellayout.random.IlvRandomLabelLayout;
import ilog.views.interactor.IlvSelectInteractor;
import ilog.views.interactor.IlvZoomViewInteractor;
import ilog.views.io.IlvReadFileException;
import ilog.views.swing.IlvJManagerViewControlBar;
import ilog.views.swing.IlvJScrollManagerView;
import ilog.views.util.IlvProductUtil;
import ilog.views.util.swing.IlvSwingUtil;
/**
* The main class PointLabelLayoutApp.
*/
public class PointLabelLayoutApp extends JRootPane
implements ManagerContentChangedListener, ManagerSelectionListener, TransformerListener {
{
// This sample uses JViews Diagrammer features. When deploying an
// application that includes this code, you need to be in possession
// of a JViews Diagrammer Deployment license.
IlvProductUtil.DeploymentLicenseRequired(IlvProductUtil.JViews_Diagrammer_Deployment);
}
/** The manager */
IlvManager manager = new IlvManager();
/** The labeling model for the manager */
IlvDefaultLabelingModel labelingModel;
/** The view of the manager */
IlvManagerView mgrview = new PointLabelingManagerView(manager);
/** An instance of the Simulates Annealing Label Layout algorithm */
IlvAnnealingLabelLayout layout = new IlvAnnealingLabelLayout();
/** An instance of the Random Label Layout algorithm */
IlvRandomLabelLayout randomLayout = new IlvRandomLabelLayout();
/** A label layout event listener */
PointLabelingListener layoutListener = new PointLabelingListener(this);
/** A text field to display messages */
JTextField msgLine = new JTextField();
/** The preferred direction of all labels */
int preferredDirection = IlvDirection.Right;
/** Offset distances */
static final double minDistToOtherObstacles = 5;
double prefDistFromOwnObstacle = 1;
/** Text fields for the offset parameters */
JTextField minOffset = new JTextField("" + minDistToOtherObstacles);
JTextField prefOffset = new JTextField("" + prefDistFromOwnObstacle);
/**
* Whether we want to see the label bounding box or not.
*/
boolean noLabelBackground = true;
/**
* In this demo, the labels are assigned to circles (IlvEllipse) or rectangles
* (IlvRectangle) representing cities. The labels should be placed close to
* the cities. Which label belongs to which city is in this sample randomly
* decided, and stored in the assignment table, such that we can show the
* assignment by the auxiliry links when necessary.
*/
Hashtable<Object, IlvGraphic> assignmentTable;
Object[] obstacles;
Object[] labels;
int[] labelsPerObstacle;
/** The assignment lines */
IlvLine[] assignmentLines;
boolean labelAssignmentVisible = false;
/**
* A counter to block the event handling of the auxiliary lines. Otherise, the
* updating of the auxiliary lines on movements would cause cycles of events.
*/
int blockEventsCounter = 0;
/**
* Initializes the application.
*/
public void init() {
showMessage("layout sample initialization...");
JPanel panel;
// register this application as listener to the manager
manager.addManagerContentChangedListener(this);
// register this application as selection listener to the manager
manager.addManagerSelectionListener(this);
// 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.setSize(new Dimension(488, 357));
mgrview.addTransformerListener(this);
// Settings parameters for selection handles
IlvHandlesSelection.defaultHandleColor = Color.black;
IlvHandlesSelection.defaultHandleBackgroundColor = Color.white;
IlvHandlesSelection.defaultHandleShape = IlvHandlesSelection.SQUARE_SHAPE;
// create a new labeling model
// We do this after adding the app as transformer listener, because
// the default labeling model will register itself as transformer listener
// as well when using nonzoomable labels. The aux lines mechanism of this
// app requires that the app receives the event at last.
labelingModel = new IlvDefaultLabelingModel(manager);
// attach the manager to the layout instances
layout.attach(labelingModel);
randomLayout.attach(labelingModel);
layout.setObstacleOffset(minDistToOtherObstacles);
layout.setAutoUpdate(false);
// install the layout iteration listener on the layout instance
layout.addLabelLayoutEventListener(layoutListener);
// set the layout manager
getContentPane().setLayout(new BorderLayout(0, 0));
// fit so far all together
getContentPane().add("Center", scrollManView);
getContentPane().add("North", panel = new JPanel());
panel.setLayout(new FlowLayout(FlowLayout.LEFT));
// create the standard control bar
IlvJManagerViewControlBar controlBar = new IlvJManagerViewControlBar();
controlBar.setView(mgrview);
panel.add(controlBar);
// modify the interactors such that the demo looks better
((IlvSelectInteractor) controlBar.getSelectInteractor()).setOpaqueMove(true);
((IlvZoomViewInteractor) controlBar.getZoomViewInteractor()).setPermanent(true);
// set the initial interactor
mgrview.setInteractor(controlBar.getSelectInteractor());
// create the graph selector
panel.add(new JLabel("Select sample"));
JComboBox<String> chooser;
panel.add(chooser = new JComboBox<String>());
chooser.setToolTipText("Select a sample");
chooser.addItemListener(new ItemListener() {
Override
public void itemStateChanged(ItemEvent event) {
if (event.getStateChange() == ItemEvent.DESELECTED)
return;
try {
// disable autoupdate until we did at least one layout
layout.setAutoUpdate(false);
// just to be sure: store where the aux lines are visible and
// hide the aux lines
boolean f = labelAssignmentVisible;
hideLabelAssignment();
// refill the manager from file
manager.deleteAll(false);
readManager((String) event.getItem());
// restore whether the aux lines were visible
labelAssignmentVisible = f;
// postprocessing after reading a file
afterRead();
} catch (Exception e) {
// e.printStackTrace();
showMessage(e.getMessage());
}
}
});
chooser.addItem("small1");
chooser.addItem("small2");
chooser.addItem("medium");
chooser.addItem("large");
chooser.addItem("huge");
// create the panel on bottom
JPanel bottomPanel = new JPanel();
bottomPanel.setLayout(new BorderLayout());
getContentPane().add("South", bottomPanel);
// create the layout buttons
JButton b;
JPanel layoutPanel = new JPanel();
bottomPanel.add("North", layoutPanel);
layoutPanel.add(b = new JButton("Random"));
b.setToolTipText("Randomize the label positions");
b.addActionListener(new ActionListener() {
Override
public void actionPerformed(ActionEvent evt) {
preprocess();
layout(randomLayout, manager);
}
});
layoutPanel.add(b = new JButton("Layout"));
b.setToolTipText("Perform a label layout");
b.addActionListener(new ActionListener() {
Override
public void actionPerformed(ActionEvent evt) {
try {
float value;
// validate the min offset
value = Float.valueOf(minOffset.getText().trim()).floatValue();
setMinimumOffset(value);
// validate the preferred offset
value = Float.valueOf(prefOffset.getText().trim()).floatValue();
setPreferredOffset(value);
} catch (Exception ex) {
// ex.printStackTrace();
showMessage("Illegal offset: " + ex.getMessage());
return;
}
// now the layout
preprocess();
layout(layout, manager);
// from now on, the labels follow the obstacles
layout.setAutoUpdate(true);
}
});
// create the layout parameters panel
JPanel layoutParameters = new JPanel();
layoutParameters.setLayout(new BorderLayout());
bottomPanel.add("Center", layoutParameters);
JPanel layoutParameters1 = new JPanel();
layoutParameters1.setLayout(new GridLayout(2, 1));
JPanel layoutParameters2 = new JPanel();
layoutParameters2.setLayout(new GridLayout(2, 2));
// pref direction parameter
JPanel auxPanel = new JPanel();
auxPanel.setLayout(new BorderLayout(0, 0));
layoutParameters1.add(auxPanel);
JComboBox<String> prefDirectionChooser = new JComboBox<String>();
prefDirectionChooser.setToolTipText("Select the preferred direction for all labels");
auxPanel.add("West", new JLabel("Preferred Direction:"));
auxPanel.add("Center", prefDirectionChooser);
prefDirectionChooser.addItemListener(new ItemListener() {
Override
public void itemStateChanged(ItemEvent event) {
if (event.getStateChange() == ItemEvent.DESELECTED)
return;
int oldDir = preferredDirection;
if ((String) event.getItem() == "top") {
preferredDirection = IlvDirection.Top;
}
if ((String) event.getItem() == "bottom") {
preferredDirection = IlvDirection.Bottom;
}
if ((String) event.getItem() == "left") {
preferredDirection = IlvDirection.Left;
}
if ((String) event.getItem() == "right") {
preferredDirection = IlvDirection.Right;
}
if (oldDir != preferredDirection)
setLayoutParameters();
}
});
prefDirectionChooser.addItem("top");
prefDirectionChooser.addItem("bottom");
prefDirectionChooser.addItem("left");
prefDirectionChooser.addItem("right");
prefDirectionChooser.setSelectedIndex(3);
// quadtree
auxPanel = new JPanel();
auxPanel.setLayout(new BorderLayout(0, 0));
layoutParameters1.add(auxPanel);
JCheckBox useQuadtree = new JCheckBox("Quadtree", true);
useQuadtree.setToolTipText("Enable or disable the quadtree as speedup mechanism");
auxPanel.add("West", useQuadtree);
useQuadtree.addItemListener(new ItemListener() {
Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED)
layout.setUseQuadtree(true);
else if (e.getStateChange() == ItemEvent.DESELECTED)
layout.setUseQuadtree(false);
}
});
// label box
JCheckBox showLabelBox = new JCheckBox("Label Box", false);
showLabelBox.setToolTipText("Show or hide the bounding box of the labels");
auxPanel.add("Center", showLabelBox);
showLabelBox.addItemListener(new ItemListener() {
Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED)
noLabelBackground = false;
else if (e.getStateChange() == ItemEvent.DESELECTED)
noLabelBackground = true;
setNoLabelBackground(noLabelBackground);
// corner may have changed
if (labelAssignmentVisible)
showLabelAssignment();
manager.reDraw();
}
});
// auxiliary lines
JCheckBox showAuxLines = new JCheckBox("Auxiliary Lines", false);
showAuxLines.setToolTipText("Show or hide the auxiliary lines of the labels");
auxPanel.add("East", showAuxLines);
showAuxLines.addItemListener(new ItemListener() {
Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED)
showLabelAssignment();
else if (e.getStateChange() == ItemEvent.DESELECTED)
hideLabelAssignment();
}
});
// minimum offset to other obstacles
layoutParameters2.add(new JLabel("Min. Offset:"));
layoutParameters2.add(minOffset);
minOffset.setToolTipText("Set the minimum offset to unrelated obstacles");
minOffset.addActionListener(new ActionListener() {
Override
public void actionPerformed(ActionEvent e) {
try {
JTextField textfield = (JTextField) e.getSource();
float value = Float.valueOf(textfield.getText().trim()).floatValue();
setMinimumOffset(value);
} catch (Exception ex) {
showMessage("Illegal minimum offset: " + ex.getMessage());
}
}
});
// preferred offset to own obstacle
layoutParameters2.add(new JLabel("Pref. Offset:"));
layoutParameters2.add(prefOffset);
prefOffset.setToolTipText("Set the preferred offset between label and related obstacle");
prefOffset.addActionListener(new ActionListener() {
Override
public void actionPerformed(ActionEvent e) {
try {
JTextField textfield = (JTextField) e.getSource();
float value = Float.valueOf(textfield.getText().trim()).floatValue();
setPreferredOffset(value);
} catch (Exception ex) {
showMessage("Illegal preferred offset: " + ex.getMessage());
}
}
});
layoutParameters.add("West", layoutParameters1);
layoutParameters.add("East", layoutParameters2);
// add the message line to the bottom panel
bottomPanel.add("South", msgLine);
msgLine.setEditable(false);
// load a sample manager
try {
manager.deleteAll(false);
readManager("small1");
afterRead();
} catch (Exception e) {
// e.printStackTrace();
showMessage(e.getMessage());
}
}
/**
* Allows you to run the demo as a standalone application.
*/
public static void main(String[] arg) {
// Sun recommends that to put the entire GUI initialization into the
// AWT thread
SwingUtilities.invokeLater(new Runnable() {
Override
public void run() {
PointLabelLayoutApp app = new PointLabelLayoutApp();
app.init();
JFrame frame = new JFrame("Label Layout Demo");
// EXIT_ON_CLOSE is not defined in jdk1.2, hence this way:
frame.setDefaultCloseOperation(3 /* EXIT_ON_CLOSE, >= jdk1.2.2 */);
frame.setSize(600, 600);
frame.getContentPane().add(app);
frame.setVisible(true);
}
});
}
/**
* Read the grapher.
*/
private void readManager(String fileNameBase) throws IOException, IlvReadFileException {
showMessage("Reading " + fileNameBase + ".ivl ...");
manager.read(IlvSwingUtil.getRelativeURL(this, "data/" + fileNameBase + ".ivl"));
showMessage("Reading " + fileNameBase + ".ivl done.");
}
/**
* Performs the layout of the graph.
*/
private void layout(IlvLabelLayout layout, IlvManager manager) {
// the layout report instance; this is an object in which
// the layout algorithm stores information about its behavior
IlvLabelLayoutReport layoutReport = null;
showMessage("layout started...");
// hide the label assignment lines because they should not be obstacles
boolean labelAssignmentWasVisible = labelAssignmentVisible;
hideLabelAssignment();
// initialize the iteration listener
layoutListener.initialize();
try {
// perform the layout and get the layout report
layoutReport = layout.performLayout(false, false);
// print the code from the layout report
showMessage(layoutReport.codeToString(layoutReport.getCode()) + "." + " Time: "
+ (0.001f * layoutReport.getLayoutTime()) + " sec." + " Overlap: " + getOverlapAsString());
} catch (Exception e) {
// e.printStackTrace();
showMessage(e.getMessage());
} finally {
if (layoutReport != null && layoutReport.getCode() != IlvLabelLayoutReport.NOT_NEEDED) {
if (labelAssignmentWasVisible)
showLabelAssignment();
mgrview.repaint();
}
}
}
/**
* Displays a message.
*/
void showMessage(String message) {
// the message is displayed in the message line
msgLine.setText(message);
msgLine.paintImmediately(0, 0, msgLine.getWidth(), msgLine.getHeight());
}
/**
* Some special handling after read. Here we determine dynamically which label
* belongs to which rectangle or ellipse obstacle (representing for instance a
* city). Polyline obstacles have no labels in this sample.
*/
void afterRead() {
int numObstacles = labelingModel.getObstaclesCount();
int numLabels = labelingModel.getLabelsCount();
obstacles = new Object[numObstacles];
labels = new Object[numLabels];
labelsPerObstacle = new int[numObstacles];
assignmentLines = new IlvLine[numLabels];
// initialize the table of obstacles
Enumeration<?> e = labelingModel.getObstacles();
int numUsableObstacles = 0;
int i = 0;
while (e.hasMoreElements()) {
obstacles[i] = e.nextElement();
if (obstacles[i] instanceof IlvIcon || obstacles[i] instanceof IlvEllipse || obstacles[i] instanceof IlvRectangle)
numUsableObstacles++;
labelsPerObstacle[i] = 0;
i++;
}
int maxLabelsPerObstacle;
if (numLabels <= numUsableObstacles)
maxLabelsPerObstacle = 1;
else if (numLabels <= 3 * numUsableObstacles)
maxLabelsPerObstacle = 3;
else
maxLabelsPerObstacle = 10000;
// initialize the table of labels
e = labelingModel.getLabels();
i = 0;
while (e.hasMoreElements()) {
labels[i] = e.nextElement();
assignmentLines[i] = new IlvLine(0, 0, 0, 0);
i++;
}
// assign to each label any obstacle randomly. But no obstacle should get
// more than maxLabelsPerObstacle labels.
Random rand = new Random(0);
assignmentTable = new Hashtable<Object, IlvGraphic>();
e = labelingModel.getLabels();
while (e.hasMoreElements()) {
Object label = e.nextElement();
while (label != null) {
i = rand.nextInt();
if (i < 0)
i = -i;
i = (i % numObstacles);
if ((obstacles[i] instanceof IlvIcon || obstacles[i] instanceof IlvEllipse
|| obstacles[i] instanceof IlvRectangle) && labelsPerObstacle[i] < maxLabelsPerObstacle) {
assignmentTable.put(label, (IlvGraphic) obstacles[i]);
labelsPerObstacle[i]++;
label = null;
}
}
}
// if necessary, make the labels transparent
if (noLabelBackground)
setNoLabelBackground(true);
setLayoutParameters();
if (labelAssignmentVisible)
showLabelAssignment();
mgrview.fitTransformerToContent();
manager.reDraw();
}
/**
* Set the layout parameters for simulated annealing.
*/
void setLayoutParameters() {
// now set the label descriptors.
try {
Object label;
Object obstacle;
int shape;
for (int i = 0; i < labels.length; i++) {
label = labels[i];
obstacle = assignmentTable.get(label);
if (obstacle instanceof IlvEllipse)
shape = IlvAnnealingPointLabelDescriptor.ELLIPTIC;
else
shape = IlvAnnealingPointLabelDescriptor.RECTANGULAR;
layout.setLabelDescriptor(label, new IlvAnnealingPointLabelDescriptor(label, obstacle, null, shape, 0, 0,
prefDistFromOwnObstacle, prefDistFromOwnObstacle, preferredDirection));
}
layout.setAllowedTime(240000);
} catch (Exception ex) {
// ex.printStackTrace();
showMessage("Illegal label descriptor: " + ex.getMessage());
}
}
/**
* Set the minimum offset between label and obstacle. We also take 20% of this
* value as the minimum offset between pairs of labels.
*/
void setMinimumOffset(double value) {
if (value > 100.0)
value = 100.0;
if (value < 0.0)
value = 0.0;
// write again to be sure the right value is shown
minOffset.setText("" + value);
// set the new value of the parameter
layout.setObstacleOffset(value);
// also, increase the offset between the labels a little bit
layout.setLabelOffset(0.2 * value);
}
/**
* Set the preferred offset for all labels.
*/
void setPreferredOffset(double value) {
if (value > 30.0)
value = 30.0;
if (value < 0.0)
value = 0.0;
// write again to be sure the right value is shown
prefOffset.setText("" + value);
// set the new value of the parameter
double oldval = prefDistFromOwnObstacle;
prefDistFromOwnObstacle = value;
// need to update the layout parameters only if something changed
if (oldval != value)
setLayoutParameters();
}
/**
* Update the label assignment for all labels. This means, actually it update
* the auxiliary line that shows the label assignment.
*/
void updateLabelAssignment() {
for (int i = 0; i < labels.length; i++) {
IlvLine line = assignmentLines[i];
IlvZoomableLabel label = (IlvZoomableLabel) labels[i];
Object obstacle = assignmentTable.get(label);
IlvRect lr = labelingModel.boundingBox(label);
IlvRect or = labelingModel.boundingBox(obstacle);
updateLabelAssignment(line, lr, or);
}
}
/**
* Update the auxiliary line showing label assignment for one label.
*/
void updateLabelAssignment(IlvLine line, IlvRect labelRect, IlvRect obstacleRect) {
double ox = obstacleRect.x + 0.5 * obstacleRect.width;
double oy = obstacleRect.y + 0.5 * obstacleRect.height;
double bestX, bestY;
if (noLabelBackground) {
// for transparent labels, show the aux line from label center
bestX = labelRect.x + 0.5 * labelRect.width;
bestY = labelRect.y + 0.5 * labelRect.height;
} else {
// for nonstransparent labels, show the aux line from the closest
// corner of the label to (ox, oy)
double bestDistSquare;
double distSquare;
double x, y;
bestX = labelRect.x;
bestY = labelRect.y;
bestDistSquare = (bestX - ox) * (bestX - ox) + (bestY - oy) * (bestY - oy);
x = labelRect.x + labelRect.width;
y = labelRect.y;
distSquare = (x - ox) * (x - ox) + (y - oy) * (y - oy);
if (distSquare < bestDistSquare) {
bestX = x;
bestY = y;
bestDistSquare = distSquare;
}
x = labelRect.x + labelRect.width;
y = labelRect.y + labelRect.height;
distSquare = (x - ox) * (x - ox) + (y - oy) * (y - oy);
if (distSquare < bestDistSquare) {
bestX = x;
bestY = y;
bestDistSquare = distSquare;
}
x = labelRect.x;
y = labelRect.y + labelRect.height;
distSquare = (x - ox) * (x - ox) + (y - oy) * (y - oy);
if (distSquare < bestDistSquare) {
bestX = x;
bestY = y;
bestDistSquare = distSquare;
}
}
// set coordinates and color of the line
line.setFrom(new IlvPoint(ox, oy));
line.setTo(new IlvPoint(bestX, bestY));
line.setForeground(Color.red);
}
/**
* Show the label assignment.
*/
void showLabelAssignment() {
blockEventsCounter++;
try {
manager.setContentsAdjusting(true);
// always update the label assignment when the lines are hidden
if (labelAssignmentVisible) {
hideLabelAssignment();
}
updateLabelAssignment();
if (!labelAssignmentVisible) {
for (int i = 0; i < labels.length; i++) {
manager.addObject(assignmentLines[i], 0, false);
manager.setSelectable(assignmentLines[i], false);
}
labelAssignmentVisible = true;
}
manager.setContentsAdjusting(false);
manager.reDraw();
} finally {
blockEventsCounter--;
}
}
/**
* Hide the label assignment.
*/
void hideLabelAssignment() {
if (labelAssignmentVisible) {
blockEventsCounter++;
try {
manager.setContentsAdjusting(true);
for (int i = 0; i < labels.length; i++)
if (assignmentLines[i].getGraphicBag() != null)
manager.removeObject(assignmentLines[i], false);
manager.setContentsAdjusting(false);
manager.reDraw();
labelAssignmentVisible = false;
} finally {
blockEventsCounter--;
}
}
}
/**
* Show or hide the background of all labels.
*/
void setNoLabelBackground(boolean flag) {
IlvZoomableLabel label;
IlvApplyObject apply;
if (flag)
apply = new IlvApplyObject() {
Override
public void apply(IlvGraphic obj, Object arg) {
IlvZoomableLabel l = (IlvZoomableLabel) obj;
l.setBorderOn(false);
l.setBackgroundOn(false);
}
};
else
apply = new IlvApplyObject() {
Override
public void apply(IlvGraphic obj, Object arg) {
IlvZoomableLabel l = (IlvZoomableLabel) obj;
l.setBorderOn(true);
l.setBackgroundOn(true);
}
};
for (int i = 0; i < labels.length; i++) {
label = (IlvZoomableLabel) labels[i];
manager.applyToObject(label, apply, null, true);
}
}
/**
* Calculate the overlap amount.
*/
String getOverlapAsString() {
double overlap = 0.0;
double maxOverlap = 0.0;
int i, j;
IlvRect r1, r2;
IlvPoint[] pts;
double w;
Object ownerObstacle;
// calculate the actual overlap value, and the hypothetical maximum
// overlap. The hypothetical maximum overlap is used to print some
// percentage value.
for (i = 0; i < labels.length; i++) {
r1 = labelingModel.boundingBox(labels[i]);
for (j = i + 1; j < labels.length; j++) {
r2 = labelingModel.boundingBox(labels[j]);
overlap += labelingModel.getLabelOverlap(labels[i], r1, labels[j], r2, 0.0);
maxOverlap += 0.5 * r1.width * r1.height;
maxOverlap += 0.5 * r2.width * r2.height;
}
}
for (i = 0; i < labels.length; i++) {
r1 = labelingModel.boundingBox(labels[i]);
ownerObstacle = assignmentTable.get(labels[i]);
for (j = 0; j < obstacles.length; j++) {
if (obstacles[j] != ownerObstacle) {
if (labelingModel.isPolylineObstacle(obstacles[j])) {
r2 = labelingModel.boundingBox(obstacles[j]);
pts = labelingModel.getPolylinePoints(obstacles[j]);
w = labelingModel.getPolylineWidth(obstacles[j]);
overlap += labelingModel.getPolylineObstacleOverlap(labels[i], r1, obstacles[j], pts, w, 0.0);
maxOverlap += 0.5 * r1.width * r1.height;
maxOverlap += 0.5 * r2.width * r2.height;
} else {
r2 = labelingModel.boundingBox(obstacles[j]);
overlap += labelingModel.getObstacleOverlap(labels[i], r1, obstacles[j], r2, 0.0);
maxOverlap += 0.5 * r1.width * r1.height;
maxOverlap += 0.5 * r2.width * r2.height;
}
}
}
}
// the percentage of overlap
double perc = 100 * (overlap / maxOverlap);
// calculate the absolute overlap, with 2 digits after dot.
overlap = ((int) (overlap * 100)) / 100.0;
// calculate the logarithmic overlap percentage (with 2 digits after dot)
// The logarithmic percentage is here more useful because it represents
// better how much the overlaps disturb the user.
perc = 100 / Math.log(101) * Math.log(perc + 1);
float logPerc = ((int) (100 * perc)) / 100.0f;
return ("" + overlap + " (Log. Perc.: " + logPerc + "%)");
}
/**
* Preprocess graph: Add some local parameters for demonstration purpose.
* Here, it tells the random layout about the layout region.
*/
void preprocess() {
IlvGraphic obj;
// calculate the layout region
IlvGraphicEnumeration selectedObjects = manager.getObjects();
double minX = Double.MAX_VALUE;
double maxX = -Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
double maxY = -Double.MAX_VALUE;
while (selectedObjects.hasMoreElements()) {
obj = selectedObjects.nextElement();
if (labelingModel.isObstacle(obj)) {
IlvRect r = labelingModel.boundingBox(obj);
if (r.x < minX)
minX = r.x;
if (r.x + r.width > maxX)
maxX = r.x + r.width;
if (r.y < minY)
minY = r.y;
if (r.y + r.height > maxY)
maxY = r.y + r.height;
}
}
randomLayout.setLayoutRegion(new IlvRect(minX, minY, (maxX - minX), (maxY - minY)));
}
/**
* Listen to changes of the manager.
*/
Override
public void contentsChanged(ManagerContentChangedEvent evt) {
// the label assignment, if visible, should be kept up to date
if (!evt.isAdjusting() && blockEventsCounter == 0) {
if (labelAssignmentVisible)
showLabelAssignment();
}
}
/**
* Listen to selections of the manager.
*/
Override
public void selectionChanged(ManagerSelectionChangedEvent event) {
if (labelingModel != null && layout.isAutoUpdate()) {
// to make sure a deselected object is always moveable
if (event.getGraphic() != null)
manager.setMovable(event.getGraphic(), true);
// we need to make sure that, if a label and its related obstacle are
// both selected, that the label is registered nonmovable. Otherwise
// we get weird effects because the label tries to follow the obstacle
// by itself.
IlvGraphicEnumeration e = manager.getSelectedObjects(false);
while (e.hasMoreElements()) {
IlvGraphic g = e.nextElement();
if (labelingModel.isLabel(g)) {
IlvGraphic obstacle = assignmentTable.get(g);
if (obstacle != null && manager.isSelected(obstacle))
manager.setMovable(g, false);
else
manager.setMovable(g, true);
}
}
}
}
/**
* Listen to changes of the transformer.
*/
Override
public void transformerChanged(TransformerChangedEvent event) {
IlvTransformer t = event.getNewValue();
// limit the zoom factor. Otherwise, we may run into serious rounding
// problems in the arithmetic formulas of the auxiliary lines.
if (t.zoomFactor() < 0.01) {
t.setValues(0.01, t.getx12(), t.getx21(), 0.01);
mgrview.setTransformer(t);
} else if (t.zoomFactor() > 20.0) {
t.setValues(20.0, t.getx12(), t.getx21(), 20.0);
mgrview.setTransformer(t);
} else if (labelAssignmentVisible) {
// update the label assignment just for sure.
showLabelAssignment();
manager.reDraw();
}
}
}
/**
* A label layout iteration listener. Implementing the interface
* LabelLayoutEventListener gives you the possibility to receive during the
* layout information about the behavior of the layout algorithm. This
* information is contained in the label layout event.
*
* In our case, we will simply print the percentage completed by the layout each
* time the method layoutStepPerformed is called, that is after each iteration
* of the Annealing Label Layout algorithm.
*/
class PointLabelingListener implements LabelLayoutEventListener {
private float oldPercentage;
private PointLabelLayoutApp app;
PointLabelingListener(PointLabelLayoutApp app) {
this.app = app;
}
/**
* This method is automatically called by the layout algorithm.
*/
Override
public void layoutStepPerformed(LabelLayoutEvent event) {
float percentage = event.getLayoutReport().getPercentageComplete();
if (percentage != oldPercentage) {
app.showMessage("" + percentage + "%");
oldPercentage = percentage;
}
}
/**
* Initialize the listener by reseting the old percentage variable. This
* method must be called before the layout is started.
*/
void initialize() {
oldPercentage = -1.0f;
}
}
/**
* A manager view with limited range for zooming. It doesn't allow to set the
* zoom level below 0.001 or above 30.0, so that the demo runs smoothly even if
* the user stress-tests the zoomin and zoomout button.
*/
class PointLabelingManagerView extends IlvManagerView {
boolean insideVerifyTransformer = false;
public PointLabelingManagerView(IlvManager manager) {
super(manager);
}
/**
* Checks a transformer. This method is called every time the transformer of
* the view is changed by calling <code>setTransformer</code> or
* <code>addTransformer</code>. It makes sure that the zoomFactor is between
* 0.001 and 30.0.
*/
Override
public void verifyTransformer() {
super.verifyTransformer();
if (insideVerifyTransformer)
return;
IlvTransformer t = getTransformer();
double zoomFactor = t.zoomFactor();
if (zoomFactor < 0.001) {
double x0 = t.getx0() * 0.999 / (1 - zoomFactor);
double y0 = t.gety0() * 0.999 / (1 - zoomFactor);
t.setValues(0.001, t.getx12(), t.getx21(), 0.001, x0, y0);
insideVerifyTransformer = true;
setTransformer(t);
insideVerifyTransformer = false;
} else if (zoomFactor > 30.0) {
double x0 = t.getx0() * 29.0 / (zoomFactor - 1);
double y0 = t.gety0() * 29.0 / (zoomFactor - 1);
t.setValues(30.0, t.getx12(), t.getx21(), 30.0, x0, y0);
insideVerifyTransformer = true;
setTransformer(t);
insideVerifyTransformer = false;
}
}
}