summaryrefslogtreecommitdiff
path: root/src/de/lmu/ifi/dbs/elki/visualization
diff options
context:
space:
mode:
Diffstat (limited to 'src/de/lmu/ifi/dbs/elki/visualization')
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/ExportVisualizations.java88
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/VisualizationTask.java107
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/VisualizerContext.java101
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/VisualizerParameterizer.java110
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/batikutil/CSSHoverClass.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/batikutil/CloneInlineImages.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/batikutil/JSVGUpdateSynchronizer.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/batikutil/ThumbnailRegistryEntry.java22
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/colors/ColorLibrary.java19
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/css/CSSClass.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/css/CSSClassManager.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/gui/ResultVisualizer.java17
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/gui/ResultWindow.java11
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/gui/SelectionTableWindow.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/gui/detail/DetailView.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/gui/overview/OverviewPlot.java163
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/gui/overview/RectangleArranger.java99
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/gui/overview/package-info.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/opticsplot/OPTICSPlot.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/package-info.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/AbstractFullProjection.java20
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/AffineProjection.java32
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/FullProjection.java33
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/OPTICSProjection.java94
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/Projection1D.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/Projection2D.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/ProjectionParallel.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/Simple1D.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/Simple2D.java20
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projections/SimpleParallel.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramFactory.java15
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramProjector.java18
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projector/OPTICSProjector.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotFactory.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotProjector.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotFactory.java17
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotProjector.java48
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/savedialog/SVGSaveDialog.java79
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/savedialog/SaveOptionsPanel.java7
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/ClusterStylingPolicy.java9
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/PropertiesBasedStyleLibrary.java104
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/StyleResult.java26
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/lines/DashedLineStyleLibrary.java14
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/lines/LineStyleLibrary.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/lines/SolidLineStyleLibrary.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/marker/CircleMarkers.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/marker/MinimalMarkers.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/marker/PrettyMarkers.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/style/presentation.properties4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/svg/SVGArrow.java114
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperCube.java20
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperSphere.java10
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPath.java22
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPlot.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/svg/SVGScoreBar.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/svg/SVGSimpleLinearAxis.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/svg/SVGUtil.java84
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/svg/VoronoiDraw.java3
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisFactory.java4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisualization.java1
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/StaticVisualizationInstance.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/StaticVisualization.java)4
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisualizerUtil.java133
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/histogram/ColoredHistogramVisualizer.java557
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/AbstractOPTICSVisualization.java12
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSClusterVisualization.java229
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotCutVisualization.java395
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotSelectionVisualization.java489
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotVisualizer.java114
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSSteepAreaVisualization.java233
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/package-info.java5
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/CircleSegmentsVisualizer.java1062
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AbstractParallelVisualization.java7
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AxisReorderVisualization.java296
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AxisVisibilityVisualization.java293
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/LineVisualization.java245
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/ParallelAxisVisualization.java214
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterOutlineVisualization.java297
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterParallelMeanVisualization.java226
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/index/RTreeParallelVisualization.java278
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionAxisRangeVisualization.java180
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionLineVisualization.java148
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolAxisRangeVisualization.java387
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolLineVisualization.java423
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractScatterplotVisualization.java6
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractTooltipVisualization.java30
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AxisVisualization.java175
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/MarkerVisualization.java167
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/PolygonVisualization.java170
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ReferencePointsVisualization.java127
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ToolBox2DVisualization.java393
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipScoreVisualization.java281
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipStringVisualization.java243
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterHullVisualization.java306
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterMeanVisualization.java345
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterOrderVisualization.java126
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/EMClusterVisualization.java659
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/VoronoiVisualization.java358
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/density/DensityEstimationOverlay.java298
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeMBRVisualization.java258
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeSphereVisualization.java333
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/BubbleVisualization.java374
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/COPVectorVisualization.java190
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/DistanceFunctionVisualization.java370
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/MoveObjectsToolVisualization.java270
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionConvexHullVisualization.java161
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionCubeVisualization.java293
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionDotVisualization.java143
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolCubeVisualization.java373
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolDotVisualization.java330
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailVisualization.java42
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/ClusterEvaluationVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/ClusterEvaluationVisFactory.java)15
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisFactory.java)78
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/KeyVisualization.java249
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/LabelVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/LabelVisFactory.java)20
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/PixmapVisualizer.java136
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SettingsVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SettingsVisFactory.java)18
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SimilarityMatrixVisualizer.java184
-rw-r--r--src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/XYCurveVisualization.java (renamed from src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/XYCurveVisFactory.java)28
118 files changed, 8383 insertions, 7096 deletions
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/ExportVisualizations.java b/src/de/lmu/ifi/dbs/elki/visualization/ExportVisualizations.java
index da0a5c85..f0a26325 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/ExportVisualizations.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/ExportVisualizations.java
@@ -48,20 +48,21 @@ import de.lmu.ifi.dbs.elki.visualization.gui.overview.PlotItem;
import de.lmu.ifi.dbs.elki.visualization.projector.Projector;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.VisualizerUtil;
/**
* Class that automatically generates all visualizations and exports them into
* SVG files.
*
* @author Erich Schubert
+ *
+ * @apiviz.composedOf VisualizerParameterizer
*/
// TODO: make more parameterizable, wrt. what to skip
public class ExportVisualizations implements ResultHandler {
/**
* Get a logger for this class.
*/
- protected final static Logging logger = Logging.getLogger(ExportVisualizations.class);
+ private static final Logging LOG = Logging.getLogger(ExportVisualizations.class);
/**
* Parameter to specify the canvas ratio
@@ -72,7 +73,7 @@ public class ExportVisualizations implements ResultHandler {
* Default value: 1.33
* </p>
*/
- public static final OptionID RATIO_ID = OptionID.getOrCreateOptionID("vis.ratio", "The width/heigh ratio of the output.");
+ public static final OptionID RATIO_ID = new OptionID("vis.ratio", "The width/heigh ratio of the output.");
/**
* Parameter to specify the output folder
@@ -80,7 +81,7 @@ public class ExportVisualizations implements ResultHandler {
* Key: {@code -vis.output}
* </p>
*/
- public static final OptionID FOLDER_ID = OptionID.getOrCreateOptionID("vis.output", "The output folder.");
+ public static final OptionID FOLDER_ID = new OptionID("vis.output", "The output folder.");
/**
* Output folder
@@ -128,44 +129,44 @@ public class ExportVisualizations implements ResultHandler {
@Override
public void processNewResult(HierarchicalResult baseResult, Result newResult) {
- if(output.isFile()) {
+ if (output.isFile()) {
throw new AbortException("Output folder cannot be an existing file.");
}
- if(!output.exists()) {
- if(!output.mkdirs()) {
+ if (!output.exists()) {
+ if (!output.mkdirs()) {
throw new AbortException("Could not create output directory.");
}
}
- if(this.baseResult != baseResult) {
+ if (this.baseResult != baseResult) {
this.baseResult = baseResult;
context = null;
counter = 0;
- logger.verbose("Note: Reusing visualization exporter for more than one result is untested.");
+ LOG.verbose("Note: Reusing visualization exporter for more than one result is untested.");
}
- if(context == null) {
+ if (context == null) {
context = manager.newContext(baseResult);
}
// Projected visualizations
ArrayList<Projector> projectors = ResultUtil.filterResults(baseResult, Projector.class);
- for(Projector proj : projectors) {
+ for (Projector proj : projectors) {
// TODO: allow selecting individual projections only.
Collection<PlotItem> items = proj.arrange();
- for(PlotItem item : items) {
+ for (PlotItem item : items) {
processItem(item);
}
}
ResultHierarchy hier = baseResult.getHierarchy();
ArrayList<VisualizationTask> tasks = ResultUtil.filterResults(baseResult, VisualizationTask.class);
- for(VisualizationTask task : tasks) {
+ for (VisualizationTask task : tasks) {
boolean isprojected = false;
- for(Result parent : hier.getParents(task)) {
- if(parent instanceof Projector) {
+ for (Result parent : hier.getParents(task)) {
+ if (parent instanceof Projector) {
isprojected = true;
break;
}
}
- if(isprojected) {
+ if (isprojected) {
continue;
}
PlotItem pi = new PlotItem(ratio, 1.0, null);
@@ -178,11 +179,11 @@ public class ExportVisualizations implements ResultHandler {
final double height = 1;
final double width = ratio * height;
// Descend into subitems
- for(Iterator<PlotItem> iter = item.subitems.iterator(); iter.hasNext();) {
+ for (Iterator<PlotItem> iter = item.subitems.iterator(); iter.hasNext();) {
PlotItem subitem = iter.next();
processItem(subitem);
}
- if(item.taskSize() <= 0) {
+ if (item.taskSize() <= 0) {
return;
}
item.sort();
@@ -193,44 +194,29 @@ public class ExportVisualizations implements ResultHandler {
svgp.getRoot().setAttribute(SVGConstants.SVG_VIEW_BOX_ATTRIBUTE, "0 0 " + width + " " + height);
ArrayList<Visualization> layers = new ArrayList<Visualization>();
- for(Iterator<VisualizationTask> iter = item.tasks.iterator(); iter.hasNext();) {
+ for (Iterator<VisualizationTask> iter = item.tasks.iterator(); iter.hasNext();) {
VisualizationTask task = iter.next();
- {
- Boolean dis = task.getGenerics(VisualizationTask.META_NODETAIL, Boolean.class);
- if(dis != null && dis == true) {
- continue;
- }
- }
- {
- Boolean dis = task.getGenerics(VisualizationTask.META_NOEXPORT, Boolean.class);
- if(dis != null && dis == true) {
- continue;
- }
- }
- if(!VisualizerUtil.isVisible(task)) {
+ if (task.nodetail || task.noexport || !task.visible) {
continue;
}
try {
Visualization v = task.getFactory().makeVisualization(task.clone(svgp, context, item.proj, width, height));
layers.add(v);
- }
- catch(Exception e) {
- if(Logging.getLogger(task.getFactory().getClass()).isDebugging()) {
+ } catch (Exception e) {
+ if (Logging.getLogger(task.getFactory().getClass()).isDebugging()) {
LoggingUtil.exception("Visualization failed.", e);
- }
- else {
+ } else {
LoggingUtil.warning("Visualizer " + task.getFactory().getClass().getName() + " failed - enable debugging to see details.");
}
}
}
- if(layers.size() <= 0) {
+ if (layers.size() <= 0) {
return;
}
- for(Visualization layer : layers) {
- if(layer.getLayer() != null) {
+ for (Visualization layer : layers) {
+ if (layer.getLayer() != null) {
svgp.getRoot().appendChild(layer.getLayer());
- }
- else {
+ } else {
LoggingUtil.warning("NULL layer seen.");
}
}
@@ -240,11 +226,10 @@ public class ExportVisualizations implements ResultHandler {
File outname = new File(output, "plot-" + counter + ".svg");
try {
svgp.saveAsSVG(outname);
+ } catch (Exception e) {
+ LOG.warning("Export of visualization failed.", e);
}
- catch(Exception e) {
- logger.warning("Export of visualization failed.", e);
- }
- for(Visualization layer : layers) {
+ for (Visualization layer : layers) {
layer.destroy();
}
counter++;
@@ -277,13 +262,14 @@ public class ExportVisualizations implements ResultHandler {
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
FileParameter outputP = new FileParameter(FOLDER_ID, FileType.OUTPUT_FILE);
- if(config.grab(outputP)) {
+ if (config.grab(outputP)) {
output = outputP.getValue();
}
- DoubleParameter ratioP = new DoubleParameter(RATIO_ID, new GreaterConstraint(0.0), 1.33);
- if(config.grab(ratioP)) {
- ratio = ratioP.getValue();
+ DoubleParameter ratioP = new DoubleParameter(RATIO_ID, 1.33);
+ ratioP.addConstraint(new GreaterConstraint(0.0));
+ if (config.grab(ratioP)) {
+ ratio = ratioP.doubleValue();
}
manager = config.tryInstantiate(VisualizerParameterizer.class);
@@ -294,4 +280,4 @@ public class ExportVisualizations implements ResultHandler {
return new ExportVisualizations(output, manager, ratio);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/VisualizationTask.java b/src/de/lmu/ifi/dbs/elki/visualization/VisualizationTask.java
index 39d6e359..050c7b63 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/VisualizationTask.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/VisualizationTask.java
@@ -25,7 +25,7 @@ package de.lmu.ifi.dbs.elki.visualization;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.result.Result;
-import de.lmu.ifi.dbs.elki.utilities.datastructures.AnyMap;
+import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
import de.lmu.ifi.dbs.elki.visualization.projections.Projection;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.visualizers.VisFactory;
@@ -41,12 +41,7 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.VisFactory;
* @apiviz.has VisFactory
* @apiviz.has Projection oneway - 0:1
*/
-public class VisualizationTask extends AnyMap<String> implements Cloneable, Result, Comparable<VisualizationTask> {
- /**
- * Serial number
- */
- private static final long serialVersionUID = 1L;
-
+public class VisualizationTask implements Cloneable, Result, Comparable<VisualizationTask> {
/**
* Constant for using thumbnail
*/
@@ -60,83 +55,81 @@ public class VisualizationTask extends AnyMap<String> implements Cloneable, Resu
/**
* Meta data key: Level for visualizer ordering
*
- * Returns an integer indicating the "temporal position" of this Visualizer.
- * It is intended to impose an ordering on the execution of Visualizers as a
+ * Returns an integer indicating the "height" of this Visualizer. It is
+ * intended to impose an ordering on the execution of Visualizers as a
* Visualizer may depend on another Visualizer running earlier. <br>
* Lower numbers should result in a earlier use of this Visualizer, while
* higher numbers should result in a later use. If more Visualizers have the
* same level, no ordering is guaranteed. <br>
* Note that this value is only a recommendation, as it is totally up to the
* framework to ignore it.
- *
- * Type: Integer
*/
- public final static String META_LEVEL = "level";
+ public int level = 0;
/**
- * Flag to control visibility. Type: Boolean
+ * Flag to control visibility.
*/
- public final static String META_VISIBLE = "visible";
+ public boolean visible = true;
/**
- * Flag to signal there is no thumbnail needed. Type: Boolean
+ * Flag to signal there is no thumbnail needed.
*/
- public final static String META_NOTHUMB = "no-thumbnail";
+ public boolean thumbnail = true;
/**
* Mark as not having a (sensible) detail view.
*/
- public static final String META_NODETAIL = "no-detail";
+ public boolean nodetail = false;
/**
- * Flag to signal the visualizer should not be exported. Type: Boolean
+ * Flag to signal the visualizer should not be exported.
*/
- public final static String META_NOEXPORT = "no-export";
+ public boolean noexport = false;
/**
- * Flag to signal the visualizer should not be embed. Type: Boolean
+ * Flag to signal the visualizer should not be embedded.
*/
- public final static String META_NOEMBED = "no-embed";
+ public boolean noembed = false;
/**
- * Flag to signal default visibility of a visualizer. Type: Boolean
+ * Flag to signal default visibility of a visualizer.
*/
- public static final String META_VISIBLE_DEFAULT = "visible-default";
+ public boolean default_visibility = true;
/**
- * Flag to mark the visualizer as tool. Type: Boolean
+ * Flag to mark the visualizer as tool.
*/
- public static final String META_TOOL = "tool";
+ public boolean tool = false;
/**
* Indicate whether this task has options.
*/
- public static final String META_HAS_OPTIONS = "has-options";
+ public boolean hasoptions = false;
/**
* Background layer
*/
- public final static int LEVEL_BACKGROUND = 0;
+ public static final int LEVEL_BACKGROUND = 0;
/**
* Data layer
*/
- public final static int LEVEL_DATA = 100;
+ public static final int LEVEL_DATA = 100;
/**
* Static plot layer
*/
- public final static int LEVEL_STATIC = 200;
+ public static final int LEVEL_STATIC = 200;
/**
* Passive foreground layer
*/
- public final static int LEVEL_FOREGROUND = 300;
+ public static final int LEVEL_FOREGROUND = 300;
/**
* Active foreground layer (interactive elements)
*/
- public final static int LEVEL_INTERACTIVE = 1000;
+ public static final int LEVEL_INTERACTIVE = 1000;
/**
* Name
@@ -184,6 +177,11 @@ public class VisualizationTask extends AnyMap<String> implements Cloneable, Resu
public double height;
/**
+ * Thumbnail size
+ */
+ public int thumbsize;
+
+ /**
* Visualization task.
*
* @param name Name
@@ -270,18 +268,23 @@ public class VisualizationTask extends AnyMap<String> implements Cloneable, Resu
return height;
}
+ /**
+ * Init the default visibility of a task.
+ *
+ * @param vis Visibility.
+ */
+ public void initDefaultVisibility(boolean vis) {
+ visible = vis;
+ default_visibility = vis;
+ }
+
@Override
public VisualizationTask clone() {
- VisualizationTask obj = (VisualizationTask) super.clone();
- obj.name = name;
- obj.context = context;
- obj.result = result;
- obj.proj = proj;
- obj.factory = factory;
- obj.svgp = svgp;
- obj.width = width;
- obj.height = height;
- return obj;
+ try {
+ return (VisualizationTask) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AbortException("Cloneable interface was removed.", e);
+ }
}
/**
@@ -330,15 +333,13 @@ public class VisualizationTask extends AnyMap<String> implements Cloneable, Resu
@Override
public int compareTo(VisualizationTask other) {
// sort by levels first
- Integer level1 = this.get(VisualizationTask.META_LEVEL, Integer.class);
- Integer level2 = other.get(VisualizationTask.META_LEVEL, Integer.class);
- if(level1 != null && level2 != null && level1 != level2) {
- return level1 - level2;
+ if (this.level != other.level) {
+ return this.level - other.level;
}
// sort by name otherwise.
String name1 = this.getShortName();
String name2 = other.getShortName();
- if(name1 != null && name2 != null && name1 != name2) {
+ if (name1 != null && name2 != null && name1 != name2) {
return name1.compareTo(name2);
}
return 0;
@@ -346,13 +347,13 @@ public class VisualizationTask extends AnyMap<String> implements Cloneable, Resu
@Override
public String toString() {
- StringBuffer buf = new StringBuffer();
- buf.append("VisTask: ").append(factory.getClass().getName()).append(" ");
- if(result != null) {
- buf.append("Result: ").append(result.getLongName()).append(" ");
+ StringBuilder buf = new StringBuilder();
+ buf.append("VisTask: ").append(factory.getClass().getName()).append(' ');
+ if (result != null) {
+ buf.append("Result: ").append(result.getLongName()).append(' ');
}
- if(proj != null) {
- buf.append("Proj: ").append(proj.toString()).append(" ");
+ if (proj != null) {
+ buf.append("Proj: ").append(proj.toString()).append(' ');
}
buf.append(super.toString());
return buf.toString();
@@ -369,4 +370,4 @@ public class VisualizationTask extends AnyMap<String> implements Cloneable, Resu
// Also don't inherit equals based on list contents!
return (this == o);
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/VisualizerContext.java b/src/de/lmu/ifi/dbs/elki/visualization/VisualizerContext.java
index c4e6c839..417d6d07 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/VisualizerContext.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/VisualizerContext.java
@@ -49,11 +49,9 @@ import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.SelectionResult;
import de.lmu.ifi.dbs.elki.visualization.projector.ProjectorFactory;
import de.lmu.ifi.dbs.elki.visualization.style.ClusterStylingPolicy;
-import de.lmu.ifi.dbs.elki.visualization.style.PropertiesBasedStyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.style.StyleResult;
import de.lmu.ifi.dbs.elki.visualization.visualizers.VisFactory;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.VisualizerUtil;
/**
* Map to store context information for the visualizer. This can be any data
@@ -76,7 +74,7 @@ public class VisualizerContext implements DataStoreListener, Result {
/**
* Logger.
*/
- private static final Logging logger = Logging.getLogger(VisualizerContext.class);
+ private static final Logging LOG = Logging.getLogger(VisualizerContext.class);
/**
* The full result object
@@ -89,11 +87,6 @@ public class VisualizerContext implements DataStoreListener, Result {
private EventListenerList listenerList = new EventListenerList();
/**
- * The style library of this context
- */
- private StyleLibrary stylelib;
-
- /**
* Projectors to use
*/
private Collection<ProjectorFactory> projectors;
@@ -117,14 +110,12 @@ public class VisualizerContext implements DataStoreListener, Result {
* Constructor. We currently require a Database and a Result.
*
* @param result Result
- * @param stylelib Style library
* @param projectors Projectors to use
* @param factories Visualizer Factories to use
*/
public VisualizerContext(HierarchicalResult result, StyleLibrary stylelib, Collection<ProjectorFactory> projectors, Collection<VisFactory> factories) {
super();
this.result = result;
- this.stylelib = stylelib;
this.projectors = projectors;
this.factories = factories;
@@ -133,16 +124,16 @@ public class VisualizerContext implements DataStoreListener, Result {
final Database db = ResultUtil.findDatabase(result);
ResultUtil.ensureClusteringResult(db, result);
this.selection = ResultUtil.ensureSelectionResult(db);
- for(Relation<?> rel : ResultUtil.getRelations(result)) {
+ for (Relation<?> rel : ResultUtil.getRelations(result)) {
ResultUtil.getSamplingResult(rel);
// FIXME: this is a really ugly workaround. :-(
- if(TypeUtil.NUMBER_VECTOR_FIELD.isAssignableFromType(rel.getDataTypeInformation())) {
+ if (TypeUtil.NUMBER_VECTOR_FIELD.isAssignableFromType(rel.getDataTypeInformation())) {
@SuppressWarnings("unchecked")
- Relation<? extends NumberVector<?, ?>> vrel = (Relation<? extends NumberVector<?, ?>>) rel;
+ Relation<? extends NumberVector<?>> vrel = (Relation<? extends NumberVector<?>>) rel;
ResultUtil.getScalesResult(vrel);
}
}
- getStyleResult();
+ makeStyleResult(stylelib);
// result.getHierarchy().add(result, this);
@@ -151,7 +142,8 @@ public class VisualizerContext implements DataStoreListener, Result {
// For proxying events.
ResultUtil.findDatabase(result).addDataStoreListener(this);
- // Add a result listener. Don't expose these methods to avoid inappropriate use.
+ // Add a result listener. Don't expose these methods to avoid inappropriate
+ // use.
addResultListener(new ResultListener() {
@Override
public void resultAdded(Result child, Result parent) {
@@ -171,6 +163,24 @@ public class VisualizerContext implements DataStoreListener, Result {
}
/**
+ * Generate a new style result for the given style library.
+ *
+ * @param stylelib Style library
+ */
+ protected void makeStyleResult(StyleLibrary stylelib) {
+ styleresult = new StyleResult();
+ styleresult.setStyleLibrary(stylelib);
+ List<Clustering<? extends Model>> clusterings = ResultUtil.getClusteringResults(result);
+ if (clusterings.size() > 0) {
+ styleresult.setStylingPolicy(new ClusterStylingPolicy(clusterings.get(0), stylelib));
+ } else {
+ Clustering<Model> c = generateDefaultClustering();
+ styleresult.setStylingPolicy(new ClusterStylingPolicy(c, stylelib));
+ }
+ result.getHierarchy().add(result, styleresult);
+ }
+
+ /**
* Get the full result object
*
* @return result object
@@ -189,36 +199,11 @@ public class VisualizerContext implements DataStoreListener, Result {
}
/**
- * Get the style library
- *
- * @return style library
- */
- public StyleLibrary getStyleLibrary() {
- if(stylelib == null) {
- stylelib = new PropertiesBasedStyleLibrary();
- }
- return stylelib;
- }
-
- /**
* Get the style result.
*
* @return Style result
*/
public StyleResult getStyleResult() {
- if(styleresult == null) {
- styleresult = new StyleResult();
- List<Clustering<? extends Model>> clusterings = ResultUtil.getClusteringResults(result);
- if(clusterings.size() > 0) {
- styleresult.setStylingPolicy(new ClusterStylingPolicy(clusterings.get(0), stylelib));
- result.getHierarchy().add(result, styleresult);
- return styleresult;
- }
- Clustering<Model> c = generateDefaultClustering();
- styleresult.setStylingPolicy(new ClusterStylingPolicy(c, stylelib));
- result.getHierarchy().add(result, styleresult);
- return styleresult;
- }
return styleresult;
}
@@ -234,8 +219,7 @@ public class VisualizerContext implements DataStoreListener, Result {
// Try to cluster by labels
ByLabelHierarchicalClustering split = new ByLabelHierarchicalClustering();
c = split.run(db);
- }
- catch(NoSupportedDataTypeException e) {
+ } catch (NoSupportedDataTypeException e) {
// Put everything into one
c = new TrivialAllInOne().run(db);
}
@@ -264,21 +248,6 @@ public class VisualizerContext implements DataStoreListener, Result {
}
/**
- * Change a visualizers visibility.
- *
- * When a Tool visualizer is made visible, other tools are hidden.
- *
- * @param task Visualization task
- * @param visibility new visibility
- *
- * @deprecated Use {@link VisualizerUtil#setVisible}
- */
- @Deprecated
- public void setVisualizationVisibility(VisualizationTask task, boolean visibility) {
- VisualizerUtil.setVisible(this, task, visibility);
- }
-
- /**
* Adds a listener for the <code>DataStoreEvent</code> posted after the
* content changes.
*
@@ -304,7 +273,7 @@ public class VisualizerContext implements DataStoreListener, Result {
*/
@Override
public void contentChanged(DataStoreEvent e) {
- for(DataStoreListener listener : listenerList.getListeners(DataStoreListener.class)) {
+ for (DataStoreListener listener : listenerList.getListeners(DataStoreListener.class)) {
listener.contentChanged(e);
}
}
@@ -316,21 +285,19 @@ public class VisualizerContext implements DataStoreListener, Result {
* @param newResult Newly added Result
*/
private void processNewResult(HierarchicalResult baseResult, Result newResult) {
- for(ProjectorFactory p : projectors) {
+ for (ProjectorFactory p : projectors) {
try {
p.processNewResult(baseResult, newResult);
- }
- catch(Throwable e) {
- logger.warning("ProjectorFactory " + p.getClass().getCanonicalName() + " failed:", e);
+ } catch (Throwable e) {
+ LOG.warning("ProjectorFactory " + p.getClass().getCanonicalName() + " failed:", e);
}
}
// Collect all visualizers.
- for(VisFactory f : factories) {
+ for (VisFactory f : factories) {
try {
f.processNewResult(baseResult, newResult);
- }
- catch(Throwable e) {
- logger.warning("VisFactory " + f.getClass().getCanonicalName() + " failed:", e);
+ } catch (Throwable e) {
+ LOG.warning("VisFactory " + f.getClass().getCanonicalName() + " failed:", e);
}
}
}
@@ -362,4 +329,4 @@ public class VisualizerContext implements DataStoreListener, Result {
public String getShortName() {
return "vis-context";
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/VisualizerParameterizer.java b/src/de/lmu/ifi/dbs/elki/visualization/VisualizerParameterizer.java
index 36bbb823..1d1a7569 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/VisualizerParameterizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/VisualizerParameterizer.java
@@ -27,7 +27,6 @@ import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-import java.util.Random;
import java.util.regex.Pattern;
import de.lmu.ifi.dbs.elki.algorithm.AbstractDistanceBasedAlgorithm;
@@ -43,6 +42,7 @@ import de.lmu.ifi.dbs.elki.result.SamplingResult;
import de.lmu.ifi.dbs.elki.result.SettingsResult;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
import de.lmu.ifi.dbs.elki.utilities.InspectionUtil;
+import de.lmu.ifi.dbs.elki.utilities.RandomFactory;
import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
@@ -75,7 +75,7 @@ public class VisualizerParameterizer implements Parameterizable {
/**
* Get a logger for this class.
*/
- protected final static Logging logger = Logging.getLogger(VisualizerParameterizer.class);
+ private static final Logging LOG = Logging.getLogger(VisualizerParameterizer.class);
/**
* Parameter to get the style properties file.
@@ -86,12 +86,12 @@ public class VisualizerParameterizer implements Parameterizable {
* Default: default properties file
* </p>
*/
- public final static OptionID STYLELIB_ID = OptionID.getOrCreateOptionID("visualizer.stylesheet", "Style properties file to use");
+ public static final OptionID STYLELIB_ID = new OptionID("visualizer.stylesheet", "Style properties file to use");
/**
* Default pattern for visualizer enabling.
*/
- public final static String DEFAULT_ENABLEVIS = "^" + Pattern.quote(VisualizerParameterizer.class.getPackage().getName()) + "\\..*";
+ public static final String DEFAULT_ENABLEVIS = "^" + Pattern.quote(VisualizerParameterizer.class.getPackage().getName()) + "\\..*";
/**
* Parameter to enable visualizers
@@ -102,7 +102,7 @@ public class VisualizerParameterizer implements Parameterizable {
* Default: ELKI core
* </p>
*/
- public final static OptionID ENABLEVIS_ID = OptionID.getOrCreateOptionID("vis.enable", "Visualizers to enable by default.");
+ public static final OptionID ENABLEVIS_ID = new OptionID("vis.enable", "Visualizers to enable by default.");
/**
* Parameter to set the sampling level
@@ -111,7 +111,7 @@ public class VisualizerParameterizer implements Parameterizable {
* Key: -vis.sampling
* </p>
*/
- public final static OptionID SAMPLING_ID = OptionID.getOrCreateOptionID("vis.sampling", "Maximum number of objects to visualize by default (for performance reasons).");
+ public static final OptionID SAMPLING_ID = new OptionID("vis.sampling", "Maximum number of objects to visualize by default (for performance reasons).");
/**
* Style library to use.
@@ -138,7 +138,7 @@ public class VisualizerParameterizer implements Parameterizable {
*
* FIXME: make parameterizable.
*/
- private long seed = new Random().nextLong();
+ private RandomFactory rnd = RandomFactory.DEFAULT;
/**
* Constructor.
@@ -164,16 +164,16 @@ public class VisualizerParameterizer implements Parameterizable {
* @return New context
*/
public VisualizerContext newContext(HierarchicalResult result) {
- if(samplesize > 0) {
+ if (samplesize > 0) {
Collection<Relation<?>> rels = ResultUtil.filterResults(result, Relation.class);
- for(Relation<?> rel : rels) {
- if(!ResultUtil.filterResults(rel, SamplingResult.class).isEmpty()) {
+ for (Relation<?> rel : rels) {
+ if (!ResultUtil.filterResults(rel, SamplingResult.class).isEmpty()) {
continue;
}
int size = rel.size();
- if(size > samplesize) {
+ if (size > samplesize) {
SamplingResult sample = new SamplingResult(rel);
- sample.setSample(DBIDUtil.randomSample(sample.getSample(), samplesize, seed));
+ sample.setSample(DBIDUtil.randomSample(sample.getSample(), samplesize, rnd));
ResultUtil.addChildResult(rel, sample);
}
}
@@ -190,54 +190,54 @@ public class VisualizerParameterizer implements Parameterizable {
* @return generated title
*/
public static String getTitle(Database db, Result result) {
- List<Pair<Object, Parameter<?, ?>>> settings = new ArrayList<Pair<Object, Parameter<?, ?>>>();
- for(SettingsResult sr : ResultUtil.getSettingsResults(result)) {
+ List<Pair<Object, Parameter<?>>> settings = new ArrayList<Pair<Object, Parameter<?>>>();
+ for (SettingsResult sr : ResultUtil.getSettingsResults(result)) {
settings.addAll(sr.getSettings());
}
String algorithm = null;
String distance = null;
String dataset = null;
- for(Pair<Object, Parameter<?, ?>> setting : settings) {
- if(setting.second.equals(OptionID.ALGORITHM)) {
+ for (Pair<Object, Parameter<?>> setting : settings) {
+ if (setting.second.equals(OptionID.ALGORITHM)) {
algorithm = setting.second.getValue().toString();
}
- if(setting.second.equals(AbstractDistanceBasedAlgorithm.DISTANCE_FUNCTION_ID)) {
+ if (setting.second.equals(AbstractDistanceBasedAlgorithm.DISTANCE_FUNCTION_ID)) {
distance = setting.second.getValue().toString();
}
- if(setting.second.equals(FileBasedDatabaseConnection.INPUT_ID)) {
+ if (setting.second.equals(FileBasedDatabaseConnection.INPUT_ID)) {
dataset = setting.second.getValue().toString();
}
}
StringBuilder buf = new StringBuilder();
- if(algorithm != null) {
+ if (algorithm != null) {
// shorten the algorithm
- if(algorithm.contains(".")) {
- algorithm = algorithm.substring(algorithm.lastIndexOf(".") + 1);
+ if (algorithm.contains(".")) {
+ algorithm = algorithm.substring(algorithm.lastIndexOf('.') + 1);
}
buf.append(algorithm);
}
- if(distance != null) {
+ if (distance != null) {
// shorten the distance
- if(distance.contains(".")) {
- distance = distance.substring(distance.lastIndexOf(".") + 1);
+ if (distance.contains(".")) {
+ distance = distance.substring(distance.lastIndexOf('.') + 1);
}
- if(buf.length() > 0) {
+ if (buf.length() > 0) {
buf.append(" using ");
}
buf.append(distance);
}
- if(dataset != null) {
+ if (dataset != null) {
// shorten the data set filename
- if(dataset.contains(File.separator)) {
+ if (dataset.contains(File.separator)) {
dataset = dataset.substring(dataset.lastIndexOf(File.separator) + 1);
}
- if(buf.length() > 0) {
+ if (buf.length() > 0) {
buf.append(" on ");
}
buf.append(dataset);
}
- if(buf.length() > 0) {
+ if (buf.length() > 0) {
return buf.toString();
}
return null;
@@ -260,27 +260,27 @@ public class VisualizerParameterizer implements Parameterizable {
protected Collection<ProjectorFactory> projectors = null;
protected int samplesize = -1;
-
+
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter samplingP = new IntParameter(SAMPLING_ID, new GreaterEqualConstraint(-1), 10000);
- if(config.grab(samplingP)) {
- samplesize = samplingP.getValue();
+ IntParameter samplingP = new IntParameter(SAMPLING_ID, 10000);
+ samplingP.addConstraint(new GreaterEqualConstraint(-1));
+ if (config.grab(samplingP)) {
+ samplesize = samplingP.intValue();
}
StringParameter stylelibP = new StringParameter(STYLELIB_ID, PropertiesBasedStyleLibrary.DEFAULT_SCHEME_FILENAME);
- if(config.grab(stylelibP)) {
+ if (config.grab(stylelibP)) {
String filename = stylelibP.getValue();
try {
stylelib = new PropertiesBasedStyleLibrary(filename, "Command line style");
- }
- catch(AbortException e) {
+ } catch (AbortException e) {
config.reportError(new WrongParameterValueException(stylelibP, filename, e));
}
}
PatternParameter enablevisP = new PatternParameter(ENABLEVIS_ID, DEFAULT_ENABLEVIS);
- if(config.grab(enablevisP)) {
- if(!"all".equals(enablevisP.getValueAsString())) {
+ if (config.grab(enablevisP)) {
+ if (!"all".equals(enablevisP.getValueAsString())) {
enableVisualizers = enablevisP.getValue();
}
}
@@ -298,21 +298,19 @@ public class VisualizerParameterizer implements Parameterizable {
*/
private static <O> Collection<ProjectorFactory> collectProjectorFactorys(MergedParameterization config, Pattern filter) {
ArrayList<ProjectorFactory> factories = new ArrayList<ProjectorFactory>();
- for(Class<?> c : InspectionUtil.cachedFindAllImplementations(ProjectorFactory.class)) {
- if(filter != null && !filter.matcher(c.getCanonicalName()).find()) {
+ for (Class<?> c : InspectionUtil.cachedFindAllImplementations(ProjectorFactory.class)) {
+ if (filter != null && !filter.matcher(c.getCanonicalName()).find()) {
continue;
}
try {
config.rewind();
ProjectorFactory a = ClassGenericsUtil.tryInstantiate(ProjectorFactory.class, c, config);
factories.add(a);
- }
- catch(Throwable e) {
- if(logger.isDebugging()) {
- logger.exception("Error instantiating visualization factory " + c.getName(), e.getCause());
- }
- else {
- logger.warning("Error instantiating visualization factory " + c.getName() + ": " + e.getMessage());
+ } catch (Throwable e) {
+ if (LOG.isDebugging()) {
+ LOG.exception("Error instantiating visualization factory " + c.getName(), e.getCause());
+ } else {
+ LOG.warning("Error instantiating visualization factory " + c.getName() + ": " + e.getMessage());
}
}
}
@@ -328,21 +326,19 @@ public class VisualizerParameterizer implements Parameterizable {
*/
private static <O> Collection<VisFactory> collectVisFactorys(MergedParameterization config, Pattern filter) {
ArrayList<VisFactory> factories = new ArrayList<VisFactory>();
- for(Class<?> c : InspectionUtil.cachedFindAllImplementations(VisFactory.class)) {
- if(filter != null && !filter.matcher(c.getCanonicalName()).find()) {
+ for (Class<?> c : InspectionUtil.cachedFindAllImplementations(VisFactory.class)) {
+ if (filter != null && !filter.matcher(c.getCanonicalName()).find()) {
continue;
}
try {
config.rewind();
VisFactory a = ClassGenericsUtil.tryInstantiate(VisFactory.class, c, config);
factories.add(a);
- }
- catch(Throwable e) {
- if(logger.isDebugging()) {
- logger.exception("Error instantiating visualization factory " + c.getName(), e.getCause());
- }
- else {
- logger.warning("Error instantiating visualization factory " + c.getName() + ": " + e.getMessage());
+ } catch (Throwable e) {
+ if (LOG.isDebugging()) {
+ LOG.exception("Error instantiating visualization factory " + c.getName(), e.getCause());
+ } else {
+ LOG.warning("Error instantiating visualization factory " + c.getName() + ": " + e.getMessage());
}
}
}
@@ -354,4 +350,4 @@ public class VisualizerParameterizer implements Parameterizable {
return new VisualizerParameterizer(samplesize, stylelib, projectors, factories, enableVisualizers);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/batikutil/CSSHoverClass.java b/src/de/lmu/ifi/dbs/elki/visualization/batikutil/CSSHoverClass.java
index 3e9f355a..54d8e7e3 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/batikutil/CSSHoverClass.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/batikutil/CSSHoverClass.java
@@ -30,12 +30,11 @@ import org.w3c.dom.events.EventListener;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
-
/**
* Do a hover effect using a CSS class.
*
* @author Erich Schubert
- *
+ *
*/
public class CSSHoverClass implements EventListener {
/**
@@ -47,7 +46,7 @@ public class CSSHoverClass implements EventListener {
* Class to set when out
*/
private String outclass;
-
+
/**
* Consider a click as 'out'?
*/
@@ -55,6 +54,7 @@ public class CSSHoverClass implements EventListener {
/**
* Constructor
+ *
* @param overclass class to set when over
* @param outclass class to set when out
* @param clickisout consider a click to be an 'out' event
@@ -82,7 +82,7 @@ public class CSSHoverClass implements EventListener {
@Override
public void handleEvent(Event evt) {
Element e = (Element) evt.getTarget();
- if (evt.getType() == SVGConstants.SVG_EVENT_MOUSEOVER) {
+ if (SVGConstants.SVG_EVENT_MOUSEOVER.equals(evt.getType())) {
if (overclass != null) {
SVGUtil.addCSSClass(e, overclass);
}
@@ -90,7 +90,7 @@ public class CSSHoverClass implements EventListener {
SVGUtil.removeCSSClass(e, outclass);
}
}
- if (evt.getType() == SVGConstants.SVG_EVENT_MOUSEOUT) {
+ if (SVGConstants.SVG_EVENT_MOUSEOUT.equals(evt.getType())) {
if (overclass != null) {
SVGUtil.removeCSSClass(e, overclass);
}
@@ -98,7 +98,7 @@ public class CSSHoverClass implements EventListener {
SVGUtil.addCSSClass(e, outclass);
}
}
- if (clickisout && evt.getType() == SVGConstants.SVG_EVENT_CLICK) {
+ if (clickisout && SVGConstants.SVG_EVENT_CLICK.equals(evt.getType())) {
if (overclass != null) {
SVGUtil.removeCSSClass(e, overclass);
}
@@ -107,4 +107,4 @@ public class CSSHoverClass implements EventListener {
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/batikutil/CloneInlineImages.java b/src/de/lmu/ifi/dbs/elki/visualization/batikutil/CloneInlineImages.java
index 30456ba9..9c53d72b 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/batikutil/CloneInlineImages.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/batikutil/CloneInlineImages.java
@@ -46,6 +46,8 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGCloneVisible;
* Clone an SVG document, inlining temporary and in-memory linked images.
*
* @author Erich Schubert
+ *
+ * @apiviz.has ThumbnailRegistryEntry
*/
public class CloneInlineImages extends SVGCloneVisible {
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/batikutil/JSVGUpdateSynchronizer.java b/src/de/lmu/ifi/dbs/elki/visualization/batikutil/JSVGUpdateSynchronizer.java
index 953c39da..110d38ec 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/batikutil/JSVGUpdateSynchronizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/batikutil/JSVGUpdateSynchronizer.java
@@ -24,6 +24,7 @@ package de.lmu.ifi.dbs.elki.visualization.batikutil;
*/
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.List;
import org.apache.batik.bridge.UpdateManager;
@@ -50,7 +51,7 @@ class JSVGUpdateSynchronizer implements UpdateSynchronizer {
/**
* The UpdateRunner we are put into
*/
- private List<WeakReference<UpdateRunner>> updaterunner = new java.util.Vector<WeakReference<UpdateRunner>>();
+ private List<WeakReference<UpdateRunner>> updaterunner = new ArrayList<WeakReference<UpdateRunner>>();
/**
* Adapter to track component changes
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/batikutil/ThumbnailRegistryEntry.java b/src/de/lmu/ifi/dbs/elki/visualization/batikutil/ThumbnailRegistryEntry.java
index dd844165..83c101e3 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/batikutil/ThumbnailRegistryEntry.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/batikutil/ThumbnailRegistryEntry.java
@@ -75,7 +75,7 @@ public class ThumbnailRegistryEntry extends AbstractRegistryEntry implements URL
/**
* The logger class.
*/
- private static final Logging logger = Logging.getLogger(ThumbnailRegistryEntry.class);
+ private static final Logging LOG = Logging.getLogger(ThumbnailRegistryEntry.class);
/**
* The image cache.
@@ -95,8 +95,8 @@ public class ThumbnailRegistryEntry extends AbstractRegistryEntry implements URL
*/
public ThumbnailRegistryEntry() {
super("Internal", PRIORITY, new String[0], new String[] { INTERNAL_MIME_TYPE });
- if(logger.isDebuggingFiner()) {
- logger.debugFiner("Registry initialized.");
+ if(LOG.isDebuggingFiner()) {
+ LOG.debugFiner("Registry initialized.");
}
}
@@ -122,8 +122,8 @@ public class ThumbnailRegistryEntry extends AbstractRegistryEntry implements URL
}
}
}
- if(logger.isDebuggingFiner()) {
- logger.debugFiner("Registered image: " + key);
+ if(LOG.isDebuggingFiner()) {
+ LOG.debugFiner("Registered image: " + key);
}
return key;
}
@@ -162,8 +162,8 @@ public class ThumbnailRegistryEntry extends AbstractRegistryEntry implements URL
* @return Image, or null
*/
public static Filter handleURL(ParsedURL url) {
- if(logger.isDebuggingFiner()) {
- logger.debugFiner("handleURL " + url.toString());
+ if(LOG.isDebuggingFiner()) {
+ LOG.debugFiner("handleURL " + url.toString());
}
if(!isCompatibleURLStatic(url)) {
return null;
@@ -179,7 +179,7 @@ public class ThumbnailRegistryEntry extends AbstractRegistryEntry implements URL
if(ref != null) {
RenderedImage ri = ref.get();
if(ri == null) {
- logger.warning("Referenced image has expired from the cache!");
+ LOG.warning("Referenced image has expired from the cache!");
}
else {
return new RedRable(GraphicsUtil.wrap(ri));
@@ -193,6 +193,8 @@ public class ThumbnailRegistryEntry extends AbstractRegistryEntry implements URL
* URL representation for internal URLs.
*
* @author Erich Schubert
+ *
+ * @apiviz.exclude
*/
class InternalParsedURLData extends ParsedURLData {
/**
@@ -225,8 +227,8 @@ public class ThumbnailRegistryEntry extends AbstractRegistryEntry implements URL
@Override
public ParsedURLData parseURL(String urlStr) {
- if(logger.isDebuggingFinest()) {
- logger.debugFinest("parseURL: " + urlStr);
+ if(LOG.isDebuggingFinest()) {
+ LOG.debugFinest("parseURL: " + urlStr);
}
if(urlStr.startsWith(INTERNAL_PREFIX)) {
InternalParsedURLData ret = new InternalParsedURLData(urlStr.substring(INTERNAL_PREFIX.length()));
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/colors/ColorLibrary.java b/src/de/lmu/ifi/dbs/elki/visualization/colors/ColorLibrary.java
index 2693bc6b..2d7512c8 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/colors/ColorLibrary.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/colors/ColorLibrary.java
@@ -23,7 +23,6 @@ package de.lmu.ifi.dbs.elki.visualization.colors;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
/**
* Color scheme interface
*
@@ -34,49 +33,61 @@ public interface ColorLibrary {
* List of line colors
*/
final static String COLOR_LINE_COLORS = "line.colors";
+
/**
* Named color for the page background
*/
final static String COLOR_PAGE_BACKGROUND = "page.background";
+
/**
* Named color for a typical axis
*/
final static String COLOR_AXIS_LINE = "axis.line";
+
/**
* Named color for a typical axis tick mark
*/
final static String COLOR_AXIS_TICK = "axis.tick";
+
/**
* Named color for a typical axis tick mark
*/
final static String COLOR_AXIS_MINOR_TICK = "axis.tick.minor";
+
/**
* Named color for a typical axis label
*/
final static String COLOR_AXIS_LABEL = "axis.label";
+
/**
* Named color for the background of the key box
*/
final static String COLOR_KEY_BACKGROUND = "key.background";
+
/**
* Named color for a label in the key part
*/
final static String COLOR_KEY_LABEL = "key.label";
+
/**
* Background color for plot area
*/
final static String COLOR_PLOT_BACKGROUND = "plot.background";
+
/**
- * Return the number of native colors available. These are guaranteed to be unique.
+ * Return the number of native colors available. These are guaranteed to be
+ * unique.
*
* @return number of native colors
*/
public int getNumberOfNativeColors();
+
/**
* Return the i'th color.
*
* @param index color index
- * @return color in hexadecimal notation (#aabbcc) or color name ("red") as valid in CSS and SVG.
+ * @return color in hexadecimal notation (#aabbcc) or color name ("red") as
+ * valid in CSS and SVG.
*/
public String getColor(int index);
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/css/CSSClass.java b/src/de/lmu/ifi/dbs/elki/visualization/css/CSSClass.java
index 5c89f638..bb62b7ef 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/css/CSSClass.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/css/CSSClass.java
@@ -265,13 +265,13 @@ public class CSSClass {
*
* @param buf String buffer to append to.
*/
- public void appendCSSDefinition(StringBuffer buf) {
+ public void appendCSSDefinition(StringBuilder buf) {
buf.append("\n.");
buf.append(name);
- buf.append("{");
+ buf.append('{');
for (Pair<String, String> pair : statements) {
buf.append(pair.getFirst());
- buf.append(":");
+ buf.append(':');
buf.append(pair.getSecond());
buf.append(";\n");
}
@@ -305,12 +305,12 @@ public class CSSClass {
* @return string rendition of CSS for inline use
*/
public String inlineCSS() {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
for (Pair<String, String> pair : statements) {
buf.append(pair.getFirst());
- buf.append(":");
+ buf.append(':');
buf.append(pair.getSecond());
- buf.append(";");
+ buf.append(';');
}
return buf.toString();
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/css/CSSClassManager.java b/src/de/lmu/ifi/dbs/elki/visualization/css/CSSClassManager.java
index 22e53c55..34debe56 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/css/CSSClassManager.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/css/CSSClassManager.java
@@ -119,7 +119,7 @@ public class CSSClassManager {
*
* @param buf String buffer
*/
- public void serialize(StringBuffer buf) {
+ public void serialize(StringBuilder buf) {
for (CSSClass clss : store.values()) {
clss.appendCSSDefinition(buf);
}
@@ -193,7 +193,7 @@ public class CSSClassManager {
* @param style Style element
*/
public void updateStyleElement(Document document, Element style) {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
serialize(buf);
Text cont = document.createTextNode(buf.toString());
while (style.hasChildNodes()) {
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/gui/ResultVisualizer.java b/src/de/lmu/ifi/dbs/elki/visualization/gui/ResultVisualizer.java
index 2c3db308..635a0466 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/gui/ResultVisualizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/gui/ResultVisualizer.java
@@ -25,6 +25,7 @@ package de.lmu.ifi.dbs.elki.visualization.gui;
import javax.swing.JFrame;
+import de.lmu.ifi.dbs.elki.gui.GUIUtil;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
@@ -51,7 +52,7 @@ public class ResultVisualizer implements ResultHandler {
/**
* Get a logger for this class.
*/
- protected final static Logging logger = Logging.getLogger(ResultVisualizer.class);
+ private static final Logging LOG = Logging.getLogger(ResultVisualizer.class);
/**
* Parameter to specify the window title
@@ -62,7 +63,7 @@ public class ResultVisualizer implements ResultHandler {
* Default value: "ELKI Result Visualization"
* </p>
*/
- public static final OptionID WINDOW_TITLE_ID = OptionID.getOrCreateOptionID("vis.window.title", "Title to use for visualization window.");
+ public static final OptionID WINDOW_TITLE_ID = new OptionID("vis.window.title", "Title to use for visualization window.");
/**
* Flag to set single display
@@ -71,7 +72,7 @@ public class ResultVisualizer implements ResultHandler {
* Key: -vis.single
* </p>
*/
- public final static OptionID SINGLE_ID = OptionID.getOrCreateOptionID("vis.window.single", "Embed visualizers in a single window, not using thumbnails and detail views.");
+ public static final OptionID SINGLE_ID = new OptionID("vis.window.single", "Embed visualizers in a single window, not using thumbnails and detail views.");
/**
* Stores the set title.
@@ -81,7 +82,7 @@ public class ResultVisualizer implements ResultHandler {
/**
* Default title
*/
- protected final static String DEFAULT_TITLE = "ELKI Result Visualization";
+ protected static final String DEFAULT_TITLE = "ELKI Result Visualization";
/**
* Visualization manager.
@@ -124,13 +125,14 @@ public class ResultVisualizer implements ResultHandler {
@Override
public void run() {
try {
+ GUIUtil.setLookAndFeel();
ResultWindow window = new ResultWindow(title, top, context, single);
window.setVisible(true);
window.setExtendedState(window.getExtendedState() | JFrame.MAXIMIZED_BOTH);
window.showOverview();
}
catch(Throwable e) {
- logger.exception("Error in starting visualizer window.", e);
+ LOG.exception("Error in starting visualizer window.", e);
}
}
});
@@ -162,13 +164,14 @@ public class ResultVisualizer implements ResultHandler {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- StringParameter titleP = new StringParameter(WINDOW_TITLE_ID, true);
+ StringParameter titleP = new StringParameter(WINDOW_TITLE_ID);
+ titleP.setOptional(true);
if(config.grab(titleP)) {
title = titleP.getValue();
}
Flag singleF = new Flag(SINGLE_ID);
if (config.grab(singleF)) {
- single = singleF.getValue();
+ single = singleF.isTrue();
}
manager = config.tryInstantiate(VisualizerParameterizer.class);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/gui/ResultWindow.java b/src/de/lmu/ifi/dbs/elki/visualization/gui/ResultWindow.java
index 38476d7d..609932cf 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/gui/ResultWindow.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/gui/ResultWindow.java
@@ -87,7 +87,7 @@ public class ResultWindow extends JFrame implements ResultListener {
/**
* Get a logger for this class.
*/
- protected final static Logging logger = Logging.getLogger(ResultWindow.class);
+ private static final Logging LOG = Logging.getLogger(ResultWindow.class);
/**
* The "Overview" button, which goes to the overview view.
@@ -348,7 +348,7 @@ public class ResultWindow extends JFrame implements ResultListener {
SVGSaveDialog.showSaveDialog(currentPlot, 512, 512);
}
else {
- logger.warning("saveCurrentPlot() called without a visible plot!");
+ LOG.warning("saveCurrentPlot() called without a visible plot!");
}
}
@@ -431,8 +431,8 @@ public class ResultWindow extends JFrame implements ResultListener {
// Currently enabled?
final String name = v.getLongName();
- boolean enabled = VisualizerUtil.isVisible(v);
- boolean istool = VisualizerUtil.isTool(v);
+ boolean enabled = v.visible;
+ boolean istool = v.tool;
if(!istool) {
final JCheckBoxMenuItem visItem = new JCheckBoxMenuItem(name, enabled);
visItem.addItemListener(new ItemListener() {
@@ -465,8 +465,7 @@ public class ResultWindow extends JFrame implements ResultListener {
});
item = visItem;
}
- boolean hasoptions = VisualizerUtil.hasOptions(v);
- if(hasoptions) {
+ if(v.hasoptions) {
final JMenu menu = new JMenu(name);
menu.add(item);
// TODO: build a menu for the visualizer!
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/gui/SelectionTableWindow.java b/src/de/lmu/ifi/dbs/elki/visualization/gui/SelectionTableWindow.java
index 754779fe..4fea630a 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/gui/SelectionTableWindow.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/gui/SelectionTableWindow.java
@@ -99,7 +99,7 @@ public class SelectionTableWindow extends JFrame implements DataStoreListener, R
/**
* The logger
*/
- static final Logging logger = Logging.getLogger(SelectionTableWindow.class);
+ private static final Logging LOG = Logging.getLogger(SelectionTableWindow.class);
/**
* The DBIDs to display
@@ -229,7 +229,7 @@ public class SelectionTableWindow extends JFrame implements DataStoreListener, R
context.setSelection(new DBIDSelection(remain));
// Now delete them.
for(DBIDIter iter = todel.iter(); iter.valid(); iter.advance()) {
- upd.delete(iter.getDBID());
+ upd.delete(iter);
}
}
@@ -248,7 +248,7 @@ public class SelectionTableWindow extends JFrame implements DataStoreListener, R
@Override
public int getColumnCount() {
- return 3; //DatabaseUtil.dimensionality(database) + 3;
+ return 3; //RelationUtil.dimensionality(database) + 3;
}
@Override
@@ -301,7 +301,7 @@ public class SelectionTableWindow extends JFrame implements DataStoreListener, R
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if(columnIndex == 0) {
- logger.warning("Tried to edit DBID, this is not allowed.");
+ LOG.warning("Tried to edit DBID, this is not allowed.");
return;
}
final DBID id = dbids.get(rowIndex);
@@ -314,7 +314,7 @@ public class SelectionTableWindow extends JFrame implements DataStoreListener, R
crep.set(id, lbl);
}
if(!(aValue instanceof String)) {
- logger.warning("Was expecting a String value from the input element, got: " + aValue.getClass());
+ LOG.warning("Was expecting a String value from the input element, got: " + aValue.getClass());
return;
}
throw new AbortException("FIXME: INCOMPLETE TRANSITION");
@@ -323,7 +323,7 @@ public class SelectionTableWindow extends JFrame implements DataStoreListener, R
logger.warning("Tried to edit removed object?");
return;
}
- final int dimensionality = DatabaseUtil.dimensionality(database);
+ final int dimensionality = RelationUtil.dimensionality(database);
double[] vals = new double[dimensionality];
for(int d = 0; d < dimensionality; d++) {
if(d == columnIndex - 3) {
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/gui/detail/DetailView.java b/src/de/lmu/ifi/dbs/elki/visualization/gui/detail/DetailView.java
index 3e1348ac..091bc8ca 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/gui/detail/DetailView.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/gui/detail/DetailView.java
@@ -46,7 +46,6 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGEffects;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.VisualizerUtil;
/**
* Manages a detail view.
@@ -122,7 +121,7 @@ public class DetailView extends SVGPlot implements ResultListener {
private void addBackground(VisualizerContext context) {
// Make a background
CSSClass cls = new CSSClass(this, "background");
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, context.getStyleLibrary().getBackgroundColor(StyleLibrary.PAGE));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, context.getStyleResult().getStyleLibrary().getBackgroundColor(StyleLibrary.PAGE));
Element bg = this.svgElement(SVGConstants.SVG_RECT_TAG);
SVGUtil.setAtt(bg, SVGConstants.SVG_X_ATTRIBUTE, "0");
SVGUtil.setAtt(bg, SVGConstants.SVG_Y_ATTRIBUTE, "0");
@@ -146,10 +145,10 @@ public class DetailView extends SVGPlot implements ResultListener {
// TODO: center/arrange visualizations?
for(Iterator<VisualizationTask> tit = visi.tasks.iterator(); tit.hasNext();) {
VisualizationTask task = tit.next();
- if(VisualizerUtil.isVisible(task)) {
+ if(task.visible) {
try {
Visualization v = task.getFactory().makeVisualization(task.clone(this, context, visi.proj, width, height));
- if (VisualizerUtil.isNoExport(task)) {
+ if (task.noexport) {
v.getLayer().setAttribute(NO_EXPORT_ATTRIBUTE, NO_EXPORT_ATTRIBUTE);
}
layers.add(v);
@@ -277,8 +276,8 @@ public class DetailView extends SVGPlot implements ResultListener {
Visualization vis = layermap.get(task);
if(vis != null) {
// Ensure visibility is as expected
- boolean isHidden = vis.getLayer().getAttribute(SVGConstants.CSS_VISIBILITY_PROPERTY) == SVGConstants.CSS_HIDDEN_VALUE;
- if(VisualizerUtil.isVisible(task)) {
+ boolean isHidden = SVGConstants.CSS_HIDDEN_VALUE.equals(vis.getLayer().getAttribute(SVGConstants.CSS_VISIBILITY_PROPERTY));
+ if(task.visible) {
if(isHidden) {
this.scheduleUpdate(new AttributeModifier(vis.getLayer(), SVGConstants.CSS_VISIBILITY_PROPERTY, SVGConstants.CSS_VISIBLE_VALUE));
}
@@ -291,10 +290,10 @@ public class DetailView extends SVGPlot implements ResultListener {
}
else {
// Only materialize when becoming visible
- if(VisualizerUtil.isVisible(task)) {
+ if(task.visible) {
// LoggingUtil.warning("Need to recreate a missing layer for " + v);
vis = task.getFactory().makeVisualization(task.clone(this, context, visi.proj, width, height));
- if (VisualizerUtil.isNoExport(task)) {
+ if (task.noexport) {
vis.getLayer().setAttribute(NO_EXPORT_ATTRIBUTE, NO_EXPORT_ATTRIBUTE);
}
layermap.put(task, vis);
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/OverviewPlot.java b/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/OverviewPlot.java
index 92ddb0b8..271a62ff 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/OverviewPlot.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/OverviewPlot.java
@@ -55,7 +55,6 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGEffects;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.VisualizerUtil;
/**
* Generate an overview plot for a set of visualizations.
@@ -76,7 +75,7 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
/**
* Our logging class
*/
- private static final Logging logger = Logging.getLogger(OverviewPlot.class);
+ private static final Logging LOG = Logging.getLogger(OverviewPlot.class);
/**
* Visualizer context
@@ -91,7 +90,7 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
/**
* Action listeners for this plot.
*/
- private java.util.Vector<ActionListener> actionListeners = new java.util.Vector<ActionListener>();
+ private ArrayList<ActionListener> actionListeners = new ArrayList<ActionListener>();
/**
* Single view mode
@@ -163,7 +162,7 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
// Add a background element:
{
CSSClass cls = new CSSClass(this, "background");
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, context.getStyleLibrary().getBackgroundColor(StyleLibrary.PAGE));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, context.getStyleResult().getStyleLibrary().getBackgroundColor(StyleLibrary.PAGE));
addCSSClassOrLogError(cls);
Element background = this.svgElement(SVGConstants.SVG_RECT_TAG);
background.setAttribute(SVGConstants.SVG_X_ATTRIBUTE, "0");
@@ -174,7 +173,7 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
getRoot().appendChild(background);
}
- if(single) {
+ if (single) {
setDisableInteractions(true);
}
SVGEffects.addShadowFilter(this);
@@ -186,19 +185,22 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
/**
* Recompute the layout of visualizations.
+ *
+ * @param width Initial width
+ * @param height Initial height
+ * @return Arrangement
*/
- private void arrangeVisualizations() {
- RectangleArranger<PlotItem> plotmap = new RectangleArranger<PlotItem>(ratio);
+ private RectangleArranger<PlotItem> arrangeVisualizations(double width, double height) {
+ RectangleArranger<PlotItem> plotmap = new RectangleArranger<PlotItem>(width, height);
ArrayList<Projector> projectors = ResultUtil.filterResults(context.getResult(), Projector.class);
// Rectangle layout
- for(Projector p : projectors) {
+ for (Projector p : projectors) {
Collection<PlotItem> projs = p.arrange();
- for(PlotItem it : projs) {
- if(it.w <= 0.0 || it.h <= 0.0) {
- logger.warning("Plot item with improper size information: " + it);
- }
- else {
+ for (PlotItem it : projs) {
+ if (it.w <= 0.0 || it.h <= 0.0) {
+ LOG.warning("Plot item with improper size information: " + it);
+ } else {
plotmap.put(it.w, it.h, it);
}
}
@@ -206,22 +208,21 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
ResultHierarchy hier = context.getHierarchy();
ArrayList<VisualizationTask> tasks = ResultUtil.filterResults(context.getResult(), VisualizationTask.class);
- nextTask: for(VisualizationTask task : tasks) {
- for(Result parent : hier.getParents(task)) {
- if(parent instanceof Projector) {
+ nextTask: for (VisualizationTask task : tasks) {
+ for (Result parent : hier.getParents(task)) {
+ if (parent instanceof Projector) {
continue nextTask;
}
}
- if(task.getWidth() <= 0.0 || task.getHeight() <= 0.0) {
- logger.warning("Task with improper size information: " + task);
- }
- else {
+ if (task.getWidth() <= 0.0 || task.getHeight() <= 0.0) {
+ LOG.warning("Task with improper size information: " + task);
+ } else {
PlotItem it = new PlotItem(task.getWidth(), task.getHeight(), null);
it.tasks.add(task);
plotmap.put(it.w, it.h, it);
}
}
- this.plotmap = plotmap;
+ return plotmap;
}
/**
@@ -229,13 +230,19 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
*/
private void reinitialize() {
setupHoverer();
- arrangeVisualizations();
+ plotmap = arrangeVisualizations(ratio, 1.0);
+ double s = plotmap.relativeFill();
+ if (s < 0.9) {
+ // Retry, sometimes this yields better results
+ plotmap = arrangeVisualizations(plotmap.getWidth() * s, plotmap.getHeight() * s);
+ }
+
recalcViewbox();
final int thumbsize = (int) Math.max(screenwidth / plotmap.getWidth(), screenheight / plotmap.getHeight());
// TODO: cancel pending thumbnail requests!
// Detach existing elements:
- for(Pair<Element, Visualization> pair : vistoelem.values()) {
+ for (Pair<Element, Visualization> pair : vistoelem.values()) {
SVGUtil.removeFromParent(pair.first);
}
// Replace the layer map
@@ -249,10 +256,10 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
hoverlayer = this.svgElement(SVGConstants.SVG_G_TAG);
// Redo the layout
- for(Entry<PlotItem, double[]> e : plotmap.entrySet()) {
+ for (Entry<PlotItem, double[]> e : plotmap.entrySet()) {
final double basex = e.getValue()[0];
final double basey = e.getValue()[1];
- for(Iterator<PlotItem> iter = e.getKey().itemIterator(); iter.hasNext();) {
+ for (Iterator<PlotItem> iter = e.getKey().itemIterator(); iter.hasNext();) {
PlotItem it = iter.next();
boolean hasDetails = false;
@@ -262,27 +269,24 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
plotlayer.appendChild(g);
vistoelem.put(it, null, g, null);
// Add the actual tasks:
- for(VisualizationTask task : it.tasks) {
- if(!visibleInOverview(task)) {
+ for (VisualizationTask task : it.tasks) {
+ if (!visibleInOverview(task)) {
continue;
}
- if(VisualizerUtil.detailsEnabled(task)) {
- hasDetails = true;
- // TODO: not updatable
- }
+ hasDetails |= !task.nodetail;
Pair<Element, Visualization> pair = oldlayers.remove(it, task);
- if(pair == null) {
+ if (pair == null) {
pair = new Pair<Element, Visualization>(null, null);
pair.first = svgElement(SVGConstants.SVG_G_TAG);
}
- if(pair.second == null) {
+ if (pair.second == null) {
pair.second = embedOrThumbnail(thumbsize, it, task, pair.first);
}
g.appendChild(pair.first);
vistoelem.put(it, task, pair);
}
// When needed, add a hover effect
- if(hasDetails && !single) {
+ if (hasDetails && !single) {
Element hover = this.svgRect(basex + it.x, basey + it.y, it.w, it.h);
SVGUtil.addCSSClass(hover, selcss.getName());
// link hoverer.
@@ -296,8 +300,8 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
}
}
}
- for(Pair<Element, Visualization> pair : oldlayers.values()) {
- if(pair.second != null) {
+ for (Pair<Element, Visualization> pair : oldlayers.values()) {
+ if (pair.second != null) {
pair.second.destroy();
}
}
@@ -315,29 +319,26 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
* @param parent Parent element to draw to
*/
private Visualization embedOrThumbnail(final int thumbsize, PlotItem it, VisualizationTask task, Element parent) {
- if(single) {
+ if (single) {
VisualizationTask thumbtask = task.clone(this, context, it.proj, it.w, it.h);
final Visualization vis = thumbtask.getFactory().makeVisualization(thumbtask);
- if(vis.getLayer() == null) {
+ if (vis.getLayer() == null) {
LoggingUtil.warning("Visualization returned empty layer: " + vis);
- }
- else {
- if (VisualizerUtil.isNoExport(task)) {
+ } else {
+ if (task.noexport) {
vis.getLayer().setAttribute(NO_EXPORT_ATTRIBUTE, NO_EXPORT_ATTRIBUTE);
}
parent.appendChild(vis.getLayer());
}
return vis;
- }
- else {
+ } else {
VisualizationTask thumbtask = task.clone(this, context, it.proj, it.w, it.h);
- thumbtask.put(VisualizationTask.THUMBNAIL, true);
- thumbtask.put(VisualizationTask.THUMBNAIL_RESOLUTION, thumbsize);
+ thumbtask.thumbnail = true;
+ thumbtask.thumbsize = thumbsize;
final Visualization vis = thumbtask.getFactory().makeVisualizationOrThumbnail(thumbtask);
- if(vis.getLayer() == null) {
+ if (vis.getLayer() == null) {
LoggingUtil.warning("Visualization returned empty layer: " + vis);
- }
- else {
+ } else {
parent.appendChild(vis.getLayer());
}
return vis;
@@ -349,25 +350,24 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
*/
synchronized void refresh() {
pendingRefresh = null;
- if(reinitOnRefresh) {
- logger.debug("Reinitialize");
+ if (reinitOnRefresh) {
+ LOG.debug("Reinitialize");
reinitialize();
reinitOnRefresh = false;
- }
- else {
- logger.debug("Incremental refresh");
+ } else {
+ LOG.debug("Incremental refresh");
boolean refreshcss = false;
final int thumbsize = (int) Math.max(screenwidth / plotmap.getWidth(), screenheight / plotmap.getHeight());
- for(PlotItem pi : plotmap.keySet()) {
- for(Iterator<PlotItem> iter = pi.itemIterator(); iter.hasNext();) {
+ for (PlotItem pi : plotmap.keySet()) {
+ for (Iterator<PlotItem> iter = pi.itemIterator(); iter.hasNext();) {
PlotItem it = iter.next();
- for(Iterator<VisualizationTask> tit = it.tasks.iterator(); tit.hasNext();) {
+ for (Iterator<VisualizationTask> tit = it.tasks.iterator(); tit.hasNext();) {
VisualizationTask task = tit.next();
Pair<Element, Visualization> pair = vistoelem.get(it, task);
// New task?
- if(pair == null) {
- if(visibleInOverview(task)) {
+ if (pair == null) {
+ if (visibleInOverview(task)) {
pair = new Pair<Element, Visualization>(null, null);
pair.first = svgElement(SVGConstants.SVG_G_TAG);
pair.second = embedOrThumbnail(thumbsize, it, task, pair.first);
@@ -375,24 +375,22 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
vistoelem.put(it, task, pair);
refreshcss = true;
}
- }
- else {
- if(visibleInOverview(task)) {
+ } else {
+ if (visibleInOverview(task)) {
// unhide if hidden.
- if(pair.first.hasAttribute(SVGConstants.CSS_VISIBILITY_PROPERTY)) {
+ if (pair.first.hasAttribute(SVGConstants.CSS_VISIBILITY_PROPERTY)) {
pair.first.removeAttribute(SVGConstants.CSS_VISIBILITY_PROPERTY);
}
// if not yet rendered, add a thumbnail
- if(!pair.first.hasChildNodes()) {
- logger.warning("This codepath should no longer be needed.");
+ if (!pair.first.hasChildNodes()) {
+ LOG.warning("This codepath should no longer be needed.");
Visualization visualization = embedOrThumbnail(thumbsize, it, task, pair.first);
vistoelem.put(it, task, pair.first, visualization);
refreshcss = true;
}
- }
- else {
+ } else {
// hide if there is anything to hide.
- if(pair.first != null && pair.first.hasChildNodes()) {
+ if (pair.first != null && pair.first.hasChildNodes()) {
pair.first.setAttribute(SVGConstants.CSS_VISIBILITY_PROPERTY, SVGConstants.CSS_HIDDEN_VALUE);
}
}
@@ -401,7 +399,7 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
}
}
}
- if(refreshcss) {
+ if (refreshcss) {
updateStyleElement();
}
}
@@ -414,15 +412,10 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
* @return visibility
*/
protected boolean visibleInOverview(VisualizationTask task) {
- if(!VisualizerUtil.isVisible(task)) {
- return false;
- }
- if(single) {
- Boolean nothumb = task.getGenerics(VisualizationTask.META_NOEMBED, Boolean.class);
- return (nothumb == null) || !nothumb;
- }
- else {
- return VisualizerUtil.thumbnailEnabled(task);
+ if (single) {
+ return task.visible && !task.noembed;
+ } else {
+ return task.visible && task.thumbnail;
}
}
@@ -483,7 +476,7 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
*/
protected void triggerSubplotSelectEvent(PlotItem it) {
// forward event to all listeners.
- for(ActionListener actionListener : actionListeners) {
+ for (ActionListener actionListener : actionListeners) {
actionListener.actionPerformed(new DetailViewSelectedEvent(this, ActionEvent.ACTION_PERFORMED, null, 0, it));
}
}
@@ -544,11 +537,11 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
* Trigger a redraw, but avoid excessive redraws.
*/
public final void lazyRefresh() {
- logger.debug("Scheduling refresh.");
+ LOG.debug("Scheduling refresh.");
Runnable pr = new Runnable() {
@Override
public void run() {
- if(OverviewPlot.this.pendingRefresh == this) {
+ if (OverviewPlot.this.pendingRefresh == this) {
OverviewPlot.this.refresh();
}
}
@@ -559,8 +552,8 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
@Override
public void resultAdded(Result child, Result parent) {
- logger.debug("result added: " + child);
- if(child instanceof VisualizationTask) {
+ LOG.debug("result added: " + child);
+ if (child instanceof VisualizationTask) {
reinitOnRefresh = true;
}
lazyRefresh();
@@ -568,13 +561,13 @@ public class OverviewPlot extends SVGPlot implements ResultListener {
@Override
public void resultChanged(Result current) {
- logger.debug("result changed: " + current);
+ LOG.debug("result changed: " + current);
lazyRefresh();
}
@Override
public void resultRemoved(Result child, Result parent) {
- logger.debug("result removed: " + child);
+ LOG.debug("result removed: " + child);
lazyRefresh();
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/RectangleArranger.java b/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/RectangleArranger.java
index a70393a5..b5e6031e 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/RectangleArranger.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/RectangleArranger.java
@@ -23,6 +23,8 @@ package de.lmu.ifi.dbs.elki.visualization.gui.overview;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+import gnu.trove.list.array.TDoubleArrayList;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -47,7 +49,7 @@ public class RectangleArranger<T> {
/**
* Logging class
*/
- private static final Logging logger = Logging.getLogger(RectangleArranger.class);
+ private static final Logging LOG = Logging.getLogger(RectangleArranger.class);
/**
* Target height/width ratio
@@ -67,15 +69,15 @@ public class RectangleArranger<T> {
/**
* Column widths
*/
- private ArrayList<Double> widths = new ArrayList<Double>();
+ private TDoubleArrayList widths = new TDoubleArrayList();
/**
* Column heights
*/
- private ArrayList<Double> heights = new ArrayList<Double>();
+ private TDoubleArrayList heights = new TDoubleArrayList();
/**
- * Bit sets to store usage. ArrayList = y, BitSet = x
+ * Map indicating which cells are used.
*/
private ArrayList<ArrayList<Object>> usage = new ArrayList<ArrayList<Object>>();
@@ -120,7 +122,9 @@ public class RectangleArranger<T> {
* @param data Data object to add (key)
*/
public void put(double w, double h, T data) {
- logger.finest("Add: " + w + "x" + h);
+ if(LOG.isDebuggingFinest()) {
+ LOG.finest("Add: " + w + "x" + h);
+ }
final int cols = widths.size();
final int rows = heights.size();
@@ -233,7 +237,9 @@ public class RectangleArranger<T> {
double hinc = Math.max(0.0, h - avh);
double inc = computeIncreaseArea(winc, hinc);
- logger.debugFinest("Candidate: " + sx + "," + sy + " - " + ex + "," + ey + ": " + avw + "x" + avh + " " + inc);
+ if(LOG.isDebuggingFinest()) {
+ LOG.debugFinest("Candidate: " + sx + "," + sy + " - " + ex + "," + ey + ": " + avw + "x" + avh + " " + inc);
+ }
if(inc < bestinc) {
bestinc = inc;
bestsx = sx;
@@ -251,7 +257,9 @@ public class RectangleArranger<T> {
}
assert assertConsistent();
}
- logger.debugFinest("Best: " + bestsx + "," + bestsy + " - " + bestex + "," + bestey + " inc: " + bestwi + "x" + besthi + " " + bestinc);
+ if(LOG.isDebuggingFinest()) {
+ LOG.debugFinest("Best: " + bestsx + "," + bestsy + " - " + bestex + "," + bestey + " inc: " + bestwi + "x" + besthi + " " + bestinc);
+ }
// Need to increase the total area
if(bestinc > 0) {
assert (bestex == cols - 1 || bestey == rows - 1);
@@ -290,7 +298,7 @@ public class RectangleArranger<T> {
}
}
map.put(data, new double[] { xpos, ypos, w, h });
- if(logger.isDebuggingFinest()) {
+ if(LOG.isDebuggingFinest()) {
logSizes();
}
}
@@ -302,25 +310,29 @@ public class RectangleArranger<T> {
}
protected void splitRow(int bestey, double besthi) {
- assert(bestey < heights.size());
- if (heights.get(bestey) - besthi <= Double.MIN_NORMAL) {
+ assert (bestey < heights.size());
+ if(heights.get(bestey) - besthi <= Double.MIN_NORMAL) {
return;
}
- logger.debugFine("Split row " + bestey);
- heights.add(bestey + 1, besthi);
+ if(LOG.isDebuggingFine()) {
+ LOG.debugFine("Split row " + bestey);
+ }
+ heights.insert(bestey + 1, besthi);
heights.set(bestey, heights.get(bestey) - besthi);
// Update used map
usage.add(bestey + 1, new ArrayList<Object>(usage.get(bestey)));
}
protected void splitCol(int bestex, double bestwi) {
- assert(bestex < widths.size());
- if (widths.get(bestex) - bestwi <= Double.MIN_NORMAL) {
+ assert (bestex < widths.size());
+ if(widths.get(bestex) - bestwi <= Double.MIN_NORMAL) {
return;
}
final int rows = heights.size();
- logger.debugFine("Split column " + bestex);
- widths.add(bestex + 1, bestwi);
+ if(LOG.isDebuggingFine()) {
+ LOG.debugFine("Split column " + bestex);
+ }
+ widths.insert(bestex + 1, bestwi);
widths.set(bestex, widths.get(bestex) - bestwi);
// Update used map
for(int y = 0; y < rows; y++) {
@@ -332,9 +344,11 @@ public class RectangleArranger<T> {
private void resize(double inc) {
final int cols = widths.size();
final int rows = heights.size();
- logger.debugFine("Resize by " + inc + "x" + (inc / ratio));
- if(logger.isDebuggingFinest()) {
- logSizes();
+ if(LOG.isDebuggingFine()) {
+ LOG.debugFine("Resize by " + inc + "x" + (inc / ratio));
+ if(LOG.isDebuggingFinest()) {
+ logSizes();
+ }
}
// TODO: if the last row or column is empty, we can do this simpler
widths.add(inc);
@@ -354,7 +368,7 @@ public class RectangleArranger<T> {
usage.add(row);
}
assert assertConsistent();
- if(logger.isDebuggingFinest()) {
+ if(LOG.isDebuggingFinest()) {
logSizes();
}
}
@@ -379,7 +393,7 @@ public class RectangleArranger<T> {
{
double wsum = 0.0;
for(int x = 0; x < cols; x++) {
- assert (widths.get(x) > 0) : "Non-positive width: "+widths.get(x);
+ assert (widths.get(x) > 0) : "Non-positive width: " + widths.get(x);
wsum += widths.get(x);
}
assert (Math.abs(wsum - twidth) < 1E-10);
@@ -387,7 +401,7 @@ public class RectangleArranger<T> {
{
double hsum = 0.0;
for(int y = 0; y < rows; y++) {
- assert (heights.get(y) > 0) : "Non-positive height: "+heights.get(y);
+ assert (heights.get(y) > 0) : "Non-positive height: " + heights.get(y);
hsum += heights.get(y);
}
assert (Math.abs(hsum - theight) < 1E-10);
@@ -401,8 +415,11 @@ public class RectangleArranger<T> {
return true;
}
- public void logSizes() {
- StringBuffer buf = new StringBuffer();
+ /**
+ * Debug logging
+ */
+ protected void logSizes() {
+ StringBuilder buf = new StringBuilder();
final int cols = widths.size();
final int rows = heights.size();
{
@@ -413,7 +430,7 @@ public class RectangleArranger<T> {
}
buf.append(widths.get(x));
}
- buf.append("\n");
+ buf.append('\n');
}
{
buf.append("Heights: ");
@@ -423,7 +440,7 @@ public class RectangleArranger<T> {
}
buf.append(heights.get(y));
}
- buf.append("\n");
+ buf.append('\n');
}
{
for(int y = 0; y < rows; y++) {
@@ -433,11 +450,33 @@ public class RectangleArranger<T> {
buf.append("|\n");
}
for(int x = 0; x < cols; x++) {
- buf.append("-");
+ buf.append('-');
}
buf.append("+\n");
}
- logger.debug(buf);
+ LOG.debug(buf);
+ }
+
+ /**
+ * Compute the relative fill. Useful for triggering a relayout if the relative
+ * fill is not satisfactory.
+ *
+ * @return relative fill
+ */
+ public double relativeFill() {
+ double acc = 0.0;
+ final int cols = widths.size();
+ final int rows = heights.size();
+ {
+ for(int y = 0; y < rows; y++) {
+ for(int x = 0; x < cols; x++) {
+ if(usage.get(y).get(x) != null) {
+ acc += widths.get(x) * heights.get(y);
+ }
+ }
+ }
+ }
+ return acc / (twidth * theight);
}
/**
@@ -482,7 +521,7 @@ public class RectangleArranger<T> {
* @param args
*/
public static void main(String[] args) {
- logger.getWrappedLogger().setLevel(Level.FINEST);
+ LOG.getWrappedLogger().setLevel(Level.FINEST);
RectangleArranger<String> r = new RectangleArranger<String>(1.3);
r.put(4., 1., "Histogram");
r.put(4., 4., "3D view");
@@ -505,4 +544,4 @@ public class RectangleArranger<T> {
r.put(4., 1., "C");
r.put(1., .1, "D");
}
-}
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/package-info.java b/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/package-info.java
index f4690d0a..013f86ca 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/gui/overview/package-info.java
@@ -1,6 +1,7 @@
/**
* <p>Classes for managing the overview plot.</p>
*
+ * @apiviz.exclude java.awt.event.*
*/
/*
This file is part of ELKI:
@@ -24,4 +25,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.visualization.gui.overview; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.visualization.gui.overview;
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/opticsplot/OPTICSPlot.java b/src/de/lmu/ifi/dbs/elki/visualization/opticsplot/OPTICSPlot.java
index e0aac5ca..b6b6fb31 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/opticsplot/OPTICSPlot.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/opticsplot/OPTICSPlot.java
@@ -56,7 +56,7 @@ public class OPTICSPlot<D extends Distance<D>> implements Result {
/**
* Logger
*/
- protected static final Logging logger = Logging.getLogger(OPTICSPlot.class);
+ private static final Logging LOG = Logging.getLogger(OPTICSPlot.class);
/**
* Scale to use
@@ -200,7 +200,7 @@ public class OPTICSPlot<D extends Distance<D>> implements Result {
}
}
catch(ArrayIndexOutOfBoundsException e) {
- logger.error("Plotting out of range: " + x + "," + y + " >= " + width + "x" + height);
+ LOG.error("Plotting out of range: " + x + "," + y + " >= " + width + "x" + height);
}
x++;
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/package-info.java b/src/de/lmu/ifi/dbs/elki/visualization/package-info.java
index b331cc0d..f4e854e0 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/package-info.java
@@ -1,6 +1,8 @@
/**
* <p>Visualization package of ELKI.</p>
*
+ * @apiviz.exclude elki.utilities
+ * @apiviz.exclude java.lang.*
*/
/*
This file is part of ELKI:
@@ -24,4 +26,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.visualization; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.visualization;
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/AbstractFullProjection.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/AbstractFullProjection.java
index 6b08f033..e8f7a91a 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projections/AbstractFullProjection.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/AbstractFullProjection.java
@@ -52,12 +52,12 @@ public abstract class AbstractFullProjection extends AbstractProjection implemen
* @return vector in scaled space
*/
@Override
- public Vector projectDataToScaledSpace(NumberVector<?, ?> data) {
+ public Vector projectDataToScaledSpace(NumberVector<?> data) {
final int dim = data.getDimensionality();
Vector vec = new Vector(dim);
double[] ds = vec.getArrayRef();
for(int d = 0; d < dim; d++) {
- ds[d] = scales[d].getScaled(data.doubleValue(d + 1));
+ ds[d] = scales[d].getScaled(data.doubleValue(d));
}
return vec;
}
@@ -86,12 +86,12 @@ public abstract class AbstractFullProjection extends AbstractProjection implemen
* @return relative vector in scaled space
*/
@Override
- public Vector projectRelativeDataToScaledSpace(NumberVector<?, ?> data) {
+ public Vector projectRelativeDataToScaledSpace(NumberVector<?> data) {
final int dim = data.getDimensionality();
Vector vec = new Vector(dim);
double[] ds = vec.getArrayRef();
for(int d = 0; d < dim; d++) {
- ds[d] = scales[d].getRelativeScaled(data.doubleValue(d + 1));
+ ds[d] = scales[d].getRelativeScaled(data.doubleValue(d));
}
return vec;
}
@@ -120,7 +120,7 @@ public abstract class AbstractFullProjection extends AbstractProjection implemen
* @return vector in rendering space
*/
@Override
- public Vector projectDataToRenderSpace(NumberVector<?, ?> data) {
+ public Vector projectDataToRenderSpace(NumberVector<?> data) {
return projectScaledToRender(projectDataToScaledSpace(data));
}
@@ -142,7 +142,7 @@ public abstract class AbstractFullProjection extends AbstractProjection implemen
* @return relative vector in rendering space
*/
@Override
- public Vector projectRelativeDataToRenderSpace(NumberVector<?, ?> data) {
+ public Vector projectRelativeDataToRenderSpace(NumberVector<?> data) {
return projectRelativeScaledToRender(projectRelativeDataToScaledSpace(data));
}
@@ -166,7 +166,7 @@ public abstract class AbstractFullProjection extends AbstractProjection implemen
* @return vector in data space
*/
@Override
- public <NV extends NumberVector<NV, ?>> NV projectScaledToDataSpace(Vector v, NV factory) {
+ public <NV extends NumberVector<?>> NV projectScaledToDataSpace(Vector v, NumberVector.Factory<NV, ?> factory) {
final int dim = v.getDimensionality();
Vector vec = v.copy();
double[] ds = vec.getArrayRef();
@@ -185,7 +185,7 @@ public abstract class AbstractFullProjection extends AbstractProjection implemen
* @return vector in data space
*/
@Override
- public <NV extends NumberVector<NV, ?>> NV projectRenderToDataSpace(Vector v, NV prototype) {
+ public <NV extends NumberVector<?>> NV projectRenderToDataSpace(Vector v, NumberVector.Factory<NV, ?> prototype) {
final int dim = v.getDimensionality();
Vector vec = projectRenderToScaled(v);
double[] ds = vec.getArrayRef();
@@ -206,7 +206,7 @@ public abstract class AbstractFullProjection extends AbstractProjection implemen
* @return relative vector in data space
*/
@Override
- public <NV extends NumberVector<NV, ?>> NV projectRelativeScaledToDataSpace(Vector v, NV prototype) {
+ public <NV extends NumberVector<?>> NV projectRelativeScaledToDataSpace(Vector v, NumberVector.Factory<NV, ?> prototype) {
final int dim = v.getDimensionality();
Vector vec = v.copy();
double[] ds = vec.getArrayRef();
@@ -225,7 +225,7 @@ public abstract class AbstractFullProjection extends AbstractProjection implemen
* @return relative vector in data space
*/
@Override
- public <NV extends NumberVector<NV, ?>> NV projectRelativeRenderToDataSpace(Vector v, NV prototype) {
+ public <NV extends NumberVector<?>> NV projectRelativeRenderToDataSpace(Vector v, NumberVector.Factory<NV, ?> prototype) {
final int dim = v.getDimensionality();
Vector vec = projectRelativeRenderToScaled(v);
double[] ds = vec.getArrayRef();
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/AffineProjection.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/AffineProjection.java
index 10533ee0..0e184e32 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projections/AffineProjection.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/AffineProjection.java
@@ -109,7 +109,7 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
@Override
public CanvasSize estimateViewport() {
- if(viewport == null) {
+ if (viewport == null) {
final int dim = proj.getDimensionality();
DoubleMinMax minmaxx = new DoubleMinMax();
DoubleMinMax minmaxy = new DoubleMinMax();
@@ -121,14 +121,14 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
minmaxy.put(orig.get(1));
// Diagonal point
Vector diag = new Vector(dim);
- for(int d2 = 0; d2 < dim; d2++) {
+ for (int d2 = 0; d2 < dim; d2++) {
diag.set(d2, 1);
}
diag = projectScaledToRender(diag);
minmaxx.put(diag.get(0));
minmaxy.put(diag.get(1));
// Axis end points
- for(int d = 0; d < dim; d++) {
+ for (int d = 0; d < dim; d++) {
Vector v = new Vector(dim);
v.set(d, 1);
Vector ax = projectScaledToRender(v);
@@ -154,7 +154,7 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
AffineTransformation proj = AffineTransformation.reorderAxesTransformation(dim, new int[] { ax1, ax2 });
// Assuming that the data was normalized on [0:1], center it:
double[] trans = new double[dim];
- for(int i = 0; i < dim; i++) {
+ for (int i = 0; i < dim; i++) {
trans[i] = -.5;
}
proj.addTranslation(new Vector(trans));
@@ -173,7 +173,7 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
}
@Override
- public double[] fastProjectDataToRenderSpace(NumberVector<?, ?> data) {
+ public double[] fastProjectDataToRenderSpace(NumberVector<?> data) {
return fastProjectScaledToRenderSpace(fastProjectDataToScaledSpace(data));
}
@@ -184,7 +184,7 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
}
@Override
- public double[] fastProjectDataToScaledSpace(NumberVector<?, ?> data) {
+ public double[] fastProjectDataToScaledSpace(NumberVector<?> data) {
// FIXME: implement with less objects?
return projectDataToScaledSpace(data).getArrayRef();
}
@@ -201,7 +201,7 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
final double[] cols = matrix[vr.length];
assert (colx.length == coly.length && colx.length == cols.length && cols.length == vr.length + 1);
- for(int k = 0; k < vr.length; k++) {
+ for (int k = 0; k < vr.length; k++) {
x += colx[k] * vr[k];
y += coly[k] * vr[k];
s += cols[k] * vr[k];
@@ -210,7 +210,7 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
x += colx[vr.length];
y += coly[vr.length];
s += cols[vr.length];
- assert (s != 0.0);
+ assert (s > 0.0 || s < 0.0);
return new double[] { x / s, y / s };
}
@@ -221,7 +221,7 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
}
@Override
- public double[] fastProjectRelativeDataToRenderSpace(NumberVector<?, ?> data) {
+ public double[] fastProjectRelativeDataToRenderSpace(NumberVector<?> data) {
// FIXME: implement with less objects?
return fastProjectRelativeScaledToRenderSpace(projectRelativeDataToScaledSpace(data).getArrayRef());
}
@@ -236,7 +236,7 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
final double[] coly = matrix[1];
assert (colx.length == coly.length);
- for(int k = 0; k < vr.length; k++) {
+ for (int k = 0; k < vr.length; k++) {
x += colx[k] * vr[k];
y += coly[k] * vr[k];
}
@@ -246,7 +246,7 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
@Override
public double[] fastProjectRenderToDataSpace(double[] data) {
double[] ret = fastProjectRenderToScaledSpace(data);
- for(int d = 0; d < scales.length; d++) {
+ for (int d = 0; d < scales.length; d++) {
ret[d] = scales[d].getUnscaled(ret[d]);
}
return ret;
@@ -254,11 +254,11 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
@Override
public double[] fastProjectRenderToScaledSpace(double[] v) {
- if(v.length == scales.length) {
+ if (v.length == scales.length) {
return projectRenderToScaled(new Vector(v)).getArrayRef();
}
double[] c = Arrays.copyOf(v, scales.length);
- for(int d = v.length; d < scales.length; d++) {
+ for (int d = v.length; d < scales.length; d++) {
c[d] = 0.5;
}
return projectRenderToScaled(new Vector(c)).getArrayRef();
@@ -269,16 +269,16 @@ public class AffineProjection extends AbstractFullProjection implements Projecti
final int dim = proj.getDimensionality();
BitSet actDim = new BitSet(dim);
Vector vScale = new Vector(dim);
- for(int d = 0; d < dim; d++) {
+ for (int d = 0; d < dim; d++) {
vScale.setZero();
vScale.set(d, 1);
double[] vRender = fastProjectScaledToRenderSpace(vScale.getArrayRef());
// TODO: Can't we do this by inspecting the projection matrix directly?
- if(vRender[0] != 0.0 || vRender[1] != 0) {
+ if (vRender[0] > 0.0 || vRender[0] < 0.0 || vRender[1] != 0) {
actDim.set(d);
}
}
return actDim;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/FullProjection.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/FullProjection.java
index cc1433aa..02705355 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projections/FullProjection.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/FullProjection.java
@@ -24,7 +24,6 @@ package de.lmu.ifi.dbs.elki.visualization.projections;
*/
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
-import de.lmu.ifi.dbs.elki.visualization.projections.Projection;
/**
* Full vector space projections.
@@ -44,7 +43,7 @@ public interface FullProjection extends Projection {
* @param v vector in scaled space
* @return vector in rendering space
*/
- public Vector projectScaledToRender(Vector v);
+ Vector projectScaledToRender(Vector v);
/**
* Project a vector from rendering space to scaled space.
@@ -52,7 +51,7 @@ public interface FullProjection extends Projection {
* @param v vector in rendering space
* @return vector in scaled space
*/
- public Vector projectRenderToScaled(Vector v);
+ Vector projectRenderToScaled(Vector v);
/**
* Project a relative vector from scaled space to rendering space.
@@ -60,7 +59,7 @@ public interface FullProjection extends Projection {
* @param v relative vector in scaled space
* @return relative vector in rendering space
*/
- public Vector projectRelativeScaledToRender(Vector v);
+ Vector projectRelativeScaledToRender(Vector v);
/**
* Project a relative vector from rendering space to scaled space.
@@ -68,7 +67,7 @@ public interface FullProjection extends Projection {
* @param v relative vector in rendering space
* @return relative vector in scaled space
*/
- public Vector projectRelativeRenderToScaled(Vector v);
+ Vector projectRelativeRenderToScaled(Vector v);
/**
* Project a data vector from data space to scaled space.
@@ -76,7 +75,7 @@ public interface FullProjection extends Projection {
* @param data vector in data space
* @return vector in scaled space
*/
- public Vector projectDataToScaledSpace(NumberVector<?, ?> data);
+ Vector projectDataToScaledSpace(NumberVector<?> data);
/**
* Project a data vector from data space to scaled space.
@@ -84,7 +83,7 @@ public interface FullProjection extends Projection {
* @param data vector in data space
* @return vector in scaled space
*/
- public Vector projectDataToScaledSpace(Vector data);
+ Vector projectDataToScaledSpace(Vector data);
/**
* Project a relative data vector from data space to scaled space.
@@ -92,7 +91,7 @@ public interface FullProjection extends Projection {
* @param data relative vector in data space
* @return relative vector in scaled space
*/
- public Vector projectRelativeDataToScaledSpace(NumberVector<?, ?> data);
+ Vector projectRelativeDataToScaledSpace(NumberVector<?> data);
/**
* Project a relative data vector from data space to scaled space.
@@ -100,7 +99,7 @@ public interface FullProjection extends Projection {
* @param data relative vector in data space
* @return relative vector in scaled space
*/
- public Vector projectRelativeDataToScaledSpace(Vector data);
+ Vector projectRelativeDataToScaledSpace(Vector data);
/**
* Project a data vector from data space to rendering space.
@@ -108,7 +107,7 @@ public interface FullProjection extends Projection {
* @param data vector in data space
* @return vector in rendering space
*/
- public Vector projectDataToRenderSpace(NumberVector<?, ?> data);
+ Vector projectDataToRenderSpace(NumberVector<?> data);
/**
* Project a data vector from data space to rendering space.
@@ -116,7 +115,7 @@ public interface FullProjection extends Projection {
* @param data vector in data space
* @return vector in rendering space
*/
- public Vector projectDataToRenderSpace(Vector data);
+ Vector projectDataToRenderSpace(Vector data);
/**
* Project a vector from scaled space to data space.
@@ -126,7 +125,7 @@ public interface FullProjection extends Projection {
* @param factory Object factory
* @return vector in data space
*/
- public <NV extends NumberVector<NV, ?>> NV projectScaledToDataSpace(Vector v, NV factory);
+ <NV extends NumberVector<?>> NV projectScaledToDataSpace(Vector v, NumberVector.Factory<NV, ?> factory);
/**
* Project a vector from rendering space to data space.
@@ -136,7 +135,7 @@ public interface FullProjection extends Projection {
* @param prototype Object factory
* @return vector in data space
*/
- public <NV extends NumberVector<NV, ?>> NV projectRenderToDataSpace(Vector v, NV prototype);
+ <NV extends NumberVector<?>> NV projectRenderToDataSpace(Vector v, NumberVector.Factory<NV, ?> prototype);
/**
* Project a relative data vector from data space to rendering space.
@@ -144,7 +143,7 @@ public interface FullProjection extends Projection {
* @param data relative vector in data space
* @return relative vector in rendering space
*/
- public Vector projectRelativeDataToRenderSpace(NumberVector<?, ?> data);
+ Vector projectRelativeDataToRenderSpace(NumberVector<?> data);
/**
* Project a relative data vector from data space to rendering space.
@@ -152,7 +151,7 @@ public interface FullProjection extends Projection {
* @param data relative vector in data space
* @return relative vector in rendering space
*/
- public Vector projectRelativeDataToRenderSpace(Vector data);
+ Vector projectRelativeDataToRenderSpace(Vector data);
/**
* Project a relative vector from scaled space to data space.
@@ -162,7 +161,7 @@ public interface FullProjection extends Projection {
* @param prototype Object factory
* @return relative vector in data space
*/
- public <NV extends NumberVector<NV, ?>> NV projectRelativeScaledToDataSpace(Vector v, NV prototype);
+ <NV extends NumberVector<?>> NV projectRelativeScaledToDataSpace(Vector v, NumberVector.Factory<NV, ?> prototype);
/**
* Project a relative vector from rendering space to data space.
@@ -172,5 +171,5 @@ public interface FullProjection extends Projection {
* @param prototype Object factory
* @return relative vector in data space
*/
- public <NV extends NumberVector<NV, ?>> NV projectRelativeRenderToDataSpace(Vector v, NV prototype);
+ <NV extends NumberVector<?>> NV projectRelativeRenderToDataSpace(Vector v, NumberVector.Factory<NV, ?> prototype);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/OPTICSProjection.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/OPTICSProjection.java
new file mode 100644
index 00000000..9a6dd9b8
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/OPTICSProjection.java
@@ -0,0 +1,94 @@
+package de.lmu.ifi.dbs.elki.visualization.projections;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
+import de.lmu.ifi.dbs.elki.math.scales.LinearScale;
+import de.lmu.ifi.dbs.elki.result.AbstractHierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.optics.ClusterOrderResult;
+import de.lmu.ifi.dbs.elki.visualization.VisualizerContext;
+import de.lmu.ifi.dbs.elki.visualization.opticsplot.OPTICSPlot;
+import de.lmu.ifi.dbs.elki.visualization.projector.OPTICSProjector;
+
+/**
+ * OPTICS projection. This is not really needed, but a quick hack to have more
+ * consistency in the visualizer API.
+ *
+ * @author Erich Schubert
+ */
+public class OPTICSProjection<D extends Distance<D>> extends AbstractHierarchicalResult implements Projection {
+ /**
+ * The projector we were generated from.
+ */
+ OPTICSProjector<D> projector;
+
+ /**
+ * Constructor.
+ *
+ * @param opticsProjector OPTICS projector
+ */
+ public OPTICSProjection(OPTICSProjector<D> opticsProjector) {
+ super();
+ this.projector = opticsProjector;
+ }
+
+ @Override
+ public String getLongName() {
+ return "OPTICS projection";
+ }
+
+ @Override
+ public String getShortName() {
+ return "OPTICSproj";
+ }
+
+ @Override
+ public int getInputDimensionality() {
+ return -1;
+ }
+
+ @Override
+ public LinearScale getScale(int d) {
+ return null;
+ }
+
+ /**
+ * Get or produce the actual OPTICS plot.
+ *
+ * @param context Context to use
+ * @return Plot
+ */
+ public OPTICSPlot<D> getOPTICSPlot(VisualizerContext context) {
+ return projector.getOPTICSPlot(context);
+ }
+
+ /**
+ * Get the OPTICS cluster order.
+ *
+ * @return Cluster oder result.
+ */
+ public ClusterOrderResult<D> getResult() {
+ return projector.getResult();
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/Projection1D.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/Projection1D.java
index 2232e0b9..82c9483d 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projections/Projection1D.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/Projection1D.java
@@ -48,7 +48,7 @@ public interface Projection1D extends Projection {
* @param data vector in data space
* @return vector in rendering space
*/
- public double fastProjectDataToRenderSpace(NumberVector<?, ?> data);
+ public double fastProjectDataToRenderSpace(NumberVector<?> data);
/**
* Project a vector from scaled space to rendering space.
@@ -72,7 +72,7 @@ public interface Projection1D extends Projection {
* @param data vector in data space
* @return vector in rendering space
*/
- public double fastProjectRelativeDataToRenderSpace(NumberVector<?, ?> data);
+ public double fastProjectRelativeDataToRenderSpace(NumberVector<?> data);
/**
* Project a vector from scaled space to rendering space.
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/Projection2D.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/Projection2D.java
index 66518c8f..acd4a829 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projections/Projection2D.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/Projection2D.java
@@ -34,6 +34,8 @@ import de.lmu.ifi.dbs.elki.data.NumberVector;
* @author Erich Schubert
*
* @apiviz.landmark
+ *
+ * @apiviz.has CanvasSize
*/
public interface Projection2D extends Projection {
/**
@@ -50,7 +52,7 @@ public interface Projection2D extends Projection {
* @param data vector in data space
* @return vector in rendering space
*/
- public double[] fastProjectDataToRenderSpace(NumberVector<?, ?> data);
+ public double[] fastProjectDataToRenderSpace(NumberVector<?> data);
/**
* Project a data vector from data space to scaled space.
@@ -66,7 +68,7 @@ public interface Projection2D extends Projection {
* @param data vector in data space
* @return vector in scaled space
*/
- public double[] fastProjectDataToScaledSpace(NumberVector<?, ?> data);
+ public double[] fastProjectDataToScaledSpace(NumberVector<?> data);
/**
* Project a vector from scaled space to rendering space.
@@ -91,7 +93,7 @@ public interface Projection2D extends Projection {
* @param prototype Prototype to create vector from
* @return vector in data space
*/
- // public <V extends NumberVector<V, ?>> V fastProjectRenderToDataSpace(double[] data, V prototype);
+ // public <V extends NumberVector<?>> V fastProjectRenderToDataSpace(double[] data, V prototype);
/**
* Project a vector from rendering space to scaled space.
@@ -115,7 +117,7 @@ public interface Projection2D extends Projection {
* @param data vector in data space
* @return vector in rendering space
*/
- public double[] fastProjectRelativeDataToRenderSpace(NumberVector<?, ?> data);
+ public double[] fastProjectRelativeDataToRenderSpace(NumberVector<?> data);
/**
* Project a vector from scaled space to rendering space.
@@ -140,4 +142,4 @@ public interface Projection2D extends Projection {
* @return Bit set, first dimension is bit 0.
*/
public BitSet getVisibleDimensions2D();
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/ProjectionParallel.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/ProjectionParallel.java
index 03e8a245..98c2ac33 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projections/ProjectionParallel.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/ProjectionParallel.java
@@ -166,7 +166,7 @@ public interface ProjectionParallel extends Projection {
* @param v Input vector
* @return Vector with reordering, inversions and scales applied.
*/
- public double[] fastProjectDataToRenderSpace(NumberVector<?, ?> v);
+ public double[] fastProjectDataToRenderSpace(NumberVector<?> v);
/**
* Project the value of a single axis to its display value
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/Simple1D.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/Simple1D.java
index b701c934..ef97fe15 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projections/Simple1D.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/Simple1D.java
@@ -42,11 +42,11 @@ public class Simple1D extends AbstractSimpleProjection implements Projection1D {
* Simple 1D projection using scaling only.
*
* @param scales Scales to use
- * @param dnum Dimension (starting at 1)
+ * @param dnum Dimension (starting at 0)
*/
public Simple1D(LinearScale[] scales, int dnum) {
super(scales);
- this.dnum = dnum - 1;
+ this.dnum = dnum;
}
@Override
@@ -55,8 +55,8 @@ public class Simple1D extends AbstractSimpleProjection implements Projection1D {
}
@Override
- public double fastProjectDataToRenderSpace(NumberVector<?, ?> data) {
- return (scales[dnum].getScaled(data.doubleValue(dnum + 1)) - 0.5) * SCALE;
+ public double fastProjectDataToRenderSpace(NumberVector<?> data) {
+ return (scales[dnum].getScaled(data.doubleValue(dnum)) - 0.5) * SCALE;
}
@Override
@@ -70,7 +70,7 @@ public class Simple1D extends AbstractSimpleProjection implements Projection1D {
}
@Override
- public double fastProjectRelativeDataToRenderSpace(NumberVector<?, ?> data) {
+ public double fastProjectRelativeDataToRenderSpace(NumberVector<?> data) {
return (data.doubleValue(dnum) - 0.5) * SCALE;
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/Simple2D.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/Simple2D.java
index 1964f12b..81821a7c 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projections/Simple2D.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/Simple2D.java
@@ -54,8 +54,8 @@ public class Simple2D extends AbstractSimpleProjection implements Projection2D {
*/
public Simple2D(LinearScale[] scales, int ax1, int ax2) {
super(scales);
- this.dim1 = ax1 - 1;
- this.dim2 = ax2 - 1;
+ this.dim1 = ax1;
+ this.dim2 = ax2;
}
@Override
@@ -66,9 +66,9 @@ public class Simple2D extends AbstractSimpleProjection implements Projection2D {
}
@Override
- public double[] fastProjectDataToRenderSpace(NumberVector<?, ?> data) {
- double x = (scales[dim1].getScaled(data.doubleValue(dim1 + 1)) - 0.5) * SCALE;
- double y = (scales[dim2].getScaled(data.doubleValue(dim2 + 1)) - 0.5) * -SCALE;
+ public double[] fastProjectDataToRenderSpace(NumberVector<?> data) {
+ double x = (scales[dim1].getScaled(data.doubleValue(dim1)) - 0.5) * SCALE;
+ double y = (scales[dim2].getScaled(data.doubleValue(dim2)) - 0.5) * -SCALE;
return new double[] { x, y };
}
@@ -83,11 +83,11 @@ public class Simple2D extends AbstractSimpleProjection implements Projection2D {
}
@Override
- public double[] fastProjectDataToScaledSpace(NumberVector<?, ?> data) {
+ public double[] fastProjectDataToScaledSpace(NumberVector<?> data) {
final int dim = data.getDimensionality();
double[] ds = new double[dim];
for(int d = 0; d < dim; d++) {
- ds[d] = scales[d].getScaled(data.doubleValue(d + 1));
+ ds[d] = scales[d].getScaled(data.doubleValue(d));
}
return ds;
}
@@ -141,9 +141,9 @@ public class Simple2D extends AbstractSimpleProjection implements Projection2D {
}
@Override
- public double[] fastProjectRelativeDataToRenderSpace(NumberVector<?, ?> data) {
- double x = scales[dim1].getRelativeScaled(data.doubleValue(dim1 + 1)) * SCALE;
- double y = scales[dim2].getRelativeScaled(data.doubleValue(dim2 + 1)) * -SCALE;
+ public double[] fastProjectRelativeDataToRenderSpace(NumberVector<?> data) {
+ double x = scales[dim1].getRelativeScaled(data.doubleValue(dim1)) * SCALE;
+ double y = scales[dim2].getRelativeScaled(data.doubleValue(dim2)) * -SCALE;
return new double[] { x, y };
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projections/SimpleParallel.java b/src/de/lmu/ifi/dbs/elki/visualization/projections/SimpleParallel.java
index c843d61d..6b07895d 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projections/SimpleParallel.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projections/SimpleParallel.java
@@ -182,12 +182,13 @@ public class SimpleParallel extends BasicResult implements ProjectionParallel {
@Override
public int getDimForVisibleAxis(int pos) {
for(int i = 0; i < scales.length; i++) {
- if (isAxisVisible(i)) {
- if (pos == 0) {
- return dimOrder[i];
- }
- pos--;
+ if (isDimHidden(dimOrder[i])) {
+ continue;
+ }
+ if (pos == 0) {
+ return dimOrder[i];
}
+ pos--;
}
return -1;
}
@@ -214,14 +215,14 @@ public class SimpleParallel extends BasicResult implements ProjectionParallel {
}
@Override
- public double[] fastProjectDataToRenderSpace(NumberVector<?, ?> data) {
+ public double[] fastProjectDataToRenderSpace(NumberVector<?> data) {
double[] v = new double[visDims];
for(int j = 0, o = 0; j < scales.length; j++) {
if(isDimHidden(j)) {
continue;
}
int i = dimOrder[j];
- v[o] = scales[i].getScaled(data.doubleValue(i + 1));
+ v[o] = scales[i].getScaled(data.doubleValue(i));
if(!isDimInverted(i)) {
v[o] = 1 - v[o];
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramFactory.java
index 3f487e0e..24defc7b 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramFactory.java
@@ -28,10 +28,10 @@ import java.util.ArrayList;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
@@ -46,7 +46,7 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
*/
public class HistogramFactory implements ProjectorFactory {
/**
- * Maximum dimensionality
+ * Maximum dimensionality.
*/
private int maxdim = ScatterPlotFactory.MAX_DIMENSIONS_DEFAULT;
@@ -66,9 +66,9 @@ public class HistogramFactory implements ProjectorFactory {
for(Relation<?> rel : rels) {
if(TypeUtil.NUMBER_VECTOR_FIELD.isAssignableFromType(rel.getDataTypeInformation())) {
@SuppressWarnings("unchecked")
- Relation<NumberVector<?, ?>> vrel = (Relation<NumberVector<?, ?>>) rel;
- final int dim = DatabaseUtil.dimensionality(vrel);
- HistogramProjector<NumberVector<?, ?>> proj = new HistogramProjector<NumberVector<?, ?>>(vrel, Math.min(dim, maxdim));
+ Relation<NumberVector<?>> vrel = (Relation<NumberVector<?>>) rel;
+ final int dim = RelationUtil.dimensionality(vrel);
+ HistogramProjector<NumberVector<?>> proj = new HistogramProjector<NumberVector<?>>(vrel, Math.min(dim, maxdim));
baseResult.getHierarchy().add(vrel, proj);
}
}
@@ -90,9 +90,10 @@ public class HistogramFactory implements ProjectorFactory {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter maxdimP = new IntParameter(ScatterPlotFactory.Parameterizer.MAXDIM_ID, new GreaterEqualConstraint(1), ScatterPlotFactory.MAX_DIMENSIONS_DEFAULT);
+ IntParameter maxdimP = new IntParameter(ScatterPlotFactory.Parameterizer.MAXDIM_ID, ScatterPlotFactory.MAX_DIMENSIONS_DEFAULT);
+ maxdimP.addConstraint(new GreaterEqualConstraint(1));
if(config.grab(maxdimP)) {
- maxdim = maxdimP.getValue();
+ maxdim = maxdimP.intValue();
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramProjector.java b/src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramProjector.java
index 030ea954..9153476b 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramProjector.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projector/HistogramProjector.java
@@ -29,15 +29,15 @@ import java.util.List;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.result.AbstractHierarchicalResult;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.ScalesResult;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.gui.overview.PlotItem;
import de.lmu.ifi.dbs.elki.visualization.projections.Projection1D;
import de.lmu.ifi.dbs.elki.visualization.projections.Simple1D;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.LabelVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.LabelVisualization;
/**
* ScatterPlotProjector is responsible for producing a set of scatterplot
@@ -50,14 +50,14 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.LabelVisFactory;
*
* @param <V> Vector type
*/
-public class HistogramProjector<V extends NumberVector<?, ?>> extends AbstractHierarchicalResult implements Projector {
+public class HistogramProjector<V extends NumberVector<?>> extends AbstractHierarchicalResult implements Projector {
/**
- * Relation we project
+ * Relation we project.
*/
Relation<V> rel;
/**
- * Database dimensionality
+ * Database dimensionality.
*/
int dmax;
@@ -71,7 +71,7 @@ public class HistogramProjector<V extends NumberVector<?, ?>> extends AbstractHi
super();
this.rel = rel;
this.dmax = maxdim;
- assert (maxdim <= DatabaseUtil.dimensionality(rel)) : "Requested dimensionality larger than data dimensionality?!?";
+ assert (maxdim <= RelationUtil.dimensionality(rel)) : "Requested dimensionality larger than data dimensionality?!?";
}
@Override
@@ -85,7 +85,7 @@ public class HistogramProjector<V extends NumberVector<?, ?>> extends AbstractHi
PlotItem master = new PlotItem(dmax + xoff, hheight + lheight, null);
ScalesResult scales = ResultUtil.getScalesResult(rel);
for(int d1 = 0; d1 < dmax; d1++) {
- Projection1D proj = new Simple1D(scales.getScales(), d1 + 1);
+ Projection1D proj = new Simple1D(scales.getScales(), d1);
final PlotItem it = new PlotItem(d1 + xoff, lheight, 1., hheight, proj);
it.tasks = tasks;
master.subitems.add(it);
@@ -94,11 +94,11 @@ public class HistogramProjector<V extends NumberVector<?, ?>> extends AbstractHi
// Add labels
for(int d1 = 0; d1 < dmax; d1++) {
PlotItem it = new PlotItem(d1 + xoff, 0, 1., lheight, null);
- LabelVisFactory lbl = new LabelVisFactory(DatabaseUtil.getColumnLabel(rel, d1 + 1));
+ LabelVisualization lbl = new LabelVisualization(RelationUtil.getColumnLabel(rel, d1));
final VisualizationTask task = new VisualizationTask("", null, null, lbl);
task.height = lheight;
task.width = 1;
- task.put(VisualizationTask.META_NODETAIL, true);
+ task.nodetail = true;
it.tasks.add(task);
master.subitems.add(it);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projector/OPTICSProjector.java b/src/de/lmu/ifi/dbs/elki/visualization/projector/OPTICSProjector.java
index 297b7b12..5aa43cd9 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projector/OPTICSProjector.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projector/OPTICSProjector.java
@@ -35,6 +35,7 @@ import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.VisualizerContext;
import de.lmu.ifi.dbs.elki.visualization.gui.overview.PlotItem;
import de.lmu.ifi.dbs.elki.visualization.opticsplot.OPTICSPlot;
+import de.lmu.ifi.dbs.elki.visualization.projections.OPTICSProjection;
/**
* Projection for OPTICS plots.
@@ -77,7 +78,7 @@ public class OPTICSProjector<D extends Distance<D>> extends AbstractHierarchical
List<PlotItem> col = new ArrayList<PlotItem>(1);
List<VisualizationTask> tasks = ResultUtil.filterResults(this, VisualizationTask.class);
if (tasks.size() > 0) {
- final PlotItem it = new PlotItem(4., 1., null);
+ final PlotItem it = new PlotItem(4., 1., new OPTICSProjection<D>(this));
it.tasks = tasks;
col.add(it);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotFactory.java
index 8b110967..23ba6d3f 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotFactory.java
@@ -54,8 +54,8 @@ public class ParallelPlotFactory implements ProjectorFactory {
// TODO: multi-relational parallel plots
if(TypeUtil.NUMBER_VECTOR_FIELD.isAssignableFromType(rel.getDataTypeInformation())) {
@SuppressWarnings("unchecked")
- Relation<NumberVector<?, ?>> vrel = (Relation<NumberVector<?, ?>>) rel;
- ParallelPlotProjector<NumberVector<?, ?>> proj = new ParallelPlotProjector<NumberVector<?, ?>>(vrel);
+ Relation<NumberVector<?>> vrel = (Relation<NumberVector<?>>) rel;
+ ParallelPlotProjector<NumberVector<?>> proj = new ParallelPlotProjector<NumberVector<?>>(vrel);
baseResult.getHierarchy().add(vrel, proj);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotProjector.java b/src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotProjector.java
index 2076655b..3e172ff9 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotProjector.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projector/ParallelPlotProjector.java
@@ -47,9 +47,9 @@ import de.lmu.ifi.dbs.elki.visualization.projections.SimpleParallel;
* @param <V> Vector type
*/
// TODO: support categorical features, and multiple relations too
-public class ParallelPlotProjector<V extends NumberVector<?, ?>> extends AbstractHierarchicalResult implements Projector {
+public class ParallelPlotProjector<V extends NumberVector<?>> extends AbstractHierarchicalResult implements Projector {
/**
- * Relation we project
+ * Relation we project.
*/
Relation<V> rel;
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotFactory.java
index 6e1a899b..0367d54b 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotFactory.java
@@ -28,10 +28,10 @@ import java.util.ArrayList;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualConstraint;
@@ -74,9 +74,9 @@ public class ScatterPlotFactory implements ProjectorFactory {
for(Relation<?> rel : rels) {
if(TypeUtil.NUMBER_VECTOR_FIELD.isAssignableFromType(rel.getDataTypeInformation())) {
@SuppressWarnings("unchecked")
- Relation<NumberVector<?, ?>> vrel = (Relation<NumberVector<?, ?>>) rel;
- final int dim = DatabaseUtil.dimensionality(vrel);
- ScatterPlotProjector<NumberVector<?, ?>> proj = new ScatterPlotProjector<NumberVector<?, ?>>(vrel, Math.min(maxdim, dim));
+ Relation<NumberVector<?>> vrel = (Relation<NumberVector<?>>) rel;
+ final int dim = RelationUtil.dimensionality(vrel);
+ ScatterPlotProjector<NumberVector<?>> proj = new ScatterPlotProjector<NumberVector<?>>(vrel, Math.min(maxdim, dim));
baseResult.getHierarchy().add(vrel, proj);
}
}
@@ -91,13 +91,13 @@ public class ScatterPlotFactory implements ProjectorFactory {
*/
public static class Parameterizer extends AbstractParameterizer {
/**
- * Parameter for the maximum number of dimensions,
+ * Parameter for the maximum number of dimensions.
*
* <p>
* Code: -vis.maxdim
* </p>
*/
- public static final OptionID MAXDIM_ID = OptionID.getOrCreateOptionID("vis.maxdim", "Maximum number of dimensions to display.");
+ public static final OptionID MAXDIM_ID = new OptionID("vis.maxdim", "Maximum number of dimensions to display.");
/**
* Stores the maximum number of dimensions to show.
@@ -107,9 +107,10 @@ public class ScatterPlotFactory implements ProjectorFactory {
@Override
protected void makeOptions(Parameterization config) {
super.makeOptions(config);
- IntParameter maxdimP = new IntParameter(MAXDIM_ID, new GreaterEqualConstraint(1), MAX_DIMENSIONS_DEFAULT);
+ IntParameter maxdimP = new IntParameter(MAXDIM_ID, MAX_DIMENSIONS_DEFAULT);
+ maxdimP.addConstraint(new GreaterEqualConstraint(1));
if(config.grab(maxdimP)) {
- maxdim = maxdimP.getValue();
+ maxdim = maxdimP.intValue();
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotProjector.java b/src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotProjector.java
index f251733e..e97ad653 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotProjector.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/projector/ScatterPlotProjector.java
@@ -29,17 +29,17 @@ import java.util.List;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.math.linearalgebra.AffineTransformation;
import de.lmu.ifi.dbs.elki.result.AbstractHierarchicalResult;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.ScalesResult;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.gui.overview.PlotItem;
import de.lmu.ifi.dbs.elki.visualization.projections.AffineProjection;
import de.lmu.ifi.dbs.elki.visualization.projections.Projection2D;
import de.lmu.ifi.dbs.elki.visualization.projections.Simple2D;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.LabelVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.LabelVisualization;
/**
* ScatterPlotProjector is responsible for producing a set of scatterplot
@@ -52,14 +52,14 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.visunproj.LabelVisFactory;
*
* @param <V> Vector type
*/
-public class ScatterPlotProjector<V extends NumberVector<?, ?>> extends AbstractHierarchicalResult implements Projector {
+public class ScatterPlotProjector<V extends NumberVector<?>> extends AbstractHierarchicalResult implements Projector {
/**
- * Relation we project
+ * Relation we project.
*/
Relation<V> rel;
/**
- * Database dimensionality
+ * Database dimensionality.
*/
int dmax;
@@ -73,7 +73,7 @@ public class ScatterPlotProjector<V extends NumberVector<?, ?>> extends Abstract
super();
this.rel = rel;
this.dmax = maxdim;
- assert (maxdim <= DatabaseUtil.dimensionality(rel)) : "Requested dimensionality larger than data dimensionality?!?";
+ assert (maxdim <= RelationUtil.dimensionality(rel)) : "Requested dimensionality larger than data dimensionality?!?";
}
@Override
@@ -87,7 +87,7 @@ public class ScatterPlotProjector<V extends NumberVector<?, ?>> extends Abstract
// In 2d, make the plot twice as big.
master = new PlotItem(2 + .1, 2 + .1, null);
{
- Projection2D proj = new Simple2D(scales.getScales(), 1, 2);
+ Projection2D proj = new Simple2D(scales.getScales(), 0, 1);
PlotItem it = new PlotItem(.1, 0, 2., 2., proj);
it.tasks = tasks;
master.subitems.add(it);
@@ -95,38 +95,38 @@ public class ScatterPlotProjector<V extends NumberVector<?, ?>> extends Abstract
// Label at bottom
{
PlotItem it = new PlotItem(.1, 2., 2., .1, null);
- final VisualizationTask task = new VisualizationTask("", null, null, new LabelVisFactory(DatabaseUtil.getColumnLabel(rel, 1)));
+ final VisualizationTask task = new VisualizationTask("", null, null, new LabelVisualization(RelationUtil.getColumnLabel(rel, 0)));
task.height = .1;
task.width = 2.;
- task.put(VisualizationTask.META_NODETAIL, true);
+ task.nodetail = true;
it.tasks.add(task);
master.subitems.add(it);
}
// Label on left
{
PlotItem it = new PlotItem(0, 0, .1, 2, null);
- final VisualizationTask task = new VisualizationTask("", null, null, new LabelVisFactory(DatabaseUtil.getColumnLabel(rel, 2), true));
+ final VisualizationTask task = new VisualizationTask("", null, null, new LabelVisualization(RelationUtil.getColumnLabel(rel, 1), true));
task.height = 2.;
task.width = .1;
- task.put(VisualizationTask.META_NODETAIL, true);
+ task.nodetail = true;
it.tasks.add(task);
master.subitems.add(it);
}
}
else {
final double sizeh = Math.ceil((dmax - 1) / 2.0);
- master = new PlotItem(sizeh * 2 + .1, dmax - 1 + .1, null);
+ master = new PlotItem(sizeh * 2. + .1, dmax - 1 + .1, null);
- for(int d1 = 1; d1 < dmax; d1++) {
- for(int d2 = d1 + 1; d2 <= dmax; d2++) {
+ for(int d1 = 0; d1 < dmax - 1; d1++) {
+ for(int d2 = d1 + 1; d2 < dmax; d2++) {
Projection2D proj = new Simple2D(scales.getScales(), d1, d2);
- PlotItem it = new PlotItem(d1 - 1 + .1, d2 - 2, 1., 1., proj);
+ PlotItem it = new PlotItem(d1 + .1, d2 - 1, 1., 1., proj);
it.tasks = tasks;
master.subitems.add(it);
}
}
if(dmax >= 3) {
- AffineTransformation p = AffineProjection.axisProjection(DatabaseUtil.dimensionality(rel), 1, 2);
+ AffineTransformation p = AffineProjection.axisProjection(RelationUtil.dimensionality(rel), 1, 2);
p.addRotation(0, 2, Math.PI / 180 * -10.);
p.addRotation(1, 2, Math.PI / 180 * 15.);
// Wanna try 4d? go ahead:
@@ -138,22 +138,22 @@ public class ScatterPlotProjector<V extends NumberVector<?, ?>> extends Abstract
master.subitems.add(it);
}
// Labels at bottom
- for(int d1 = 1; d1 < dmax; d1++) {
- PlotItem it = new PlotItem(d1 - 1 + .1, dmax - 1, 1., .1, null);
- final VisualizationTask task = new VisualizationTask("", null, null, new LabelVisFactory(DatabaseUtil.getColumnLabel(rel, d1)));
+ for(int d1 = 0; d1 < dmax - 1; d1++) {
+ PlotItem it = new PlotItem(d1 + .1, dmax - 1, 1., .1, null);
+ final VisualizationTask task = new VisualizationTask("", null, null, new LabelVisualization(RelationUtil.getColumnLabel(rel, d1)));
task.height = .1;
task.width = 1;
- task.put(VisualizationTask.META_NODETAIL, true);
+ task.nodetail = true;
it.tasks.add(task);
master.subitems.add(it);
}
// Labels on left
- for(int d2 = 2; d2 <= dmax; d2++) {
- PlotItem it = new PlotItem(0, d2 - 2, .1, 1, null);
- final VisualizationTask task = new VisualizationTask("", null, null, new LabelVisFactory(DatabaseUtil.getColumnLabel(rel, d2), true));
+ for(int d2 = 1; d2 < dmax; d2++) {
+ PlotItem it = new PlotItem(0, d2 - 1, .1, 1, null);
+ final VisualizationTask task = new VisualizationTask("", null, null, new LabelVisualization(RelationUtil.getColumnLabel(rel, d2), true));
task.height = 1;
task.width = .1;
- task.put(VisualizationTask.META_NODETAIL, true);
+ task.nodetail = true;
it.tasks.add(task);
master.subitems.add(it);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/savedialog/SVGSaveDialog.java b/src/de/lmu/ifi/dbs/elki/visualization/savedialog/SVGSaveDialog.java
index f895c46e..8f56c562 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/savedialog/SVGSaveDialog.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/savedialog/SVGSaveDialog.java
@@ -57,10 +57,10 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
*/
public class SVGSaveDialog {
/** The default title. "Save as ...". */
- public final static String DEFAULT_TITLE = "Save as ...";
+ public static final String DEFAULT_TITLE = "Save as ...";
/** Static logger reference */
- private final static Logging logger = Logging.getLogger(SVGSaveDialog.class);
+ private static final Logging LOG = Logging.getLogger(SVGSaveDialog.class);
/** Automagic file format */
final static String automagic_format = "automatic";
@@ -73,11 +73,10 @@ public class SVGSaveDialog {
static {
// FOP installed?
- if(SVGPlot.hasFOPInstalled()) {
+ if (SVGPlot.hasFOPInstalled()) {
formats = new String[] { "svg", "png", "jpeg", "jpg", "pdf", "ps", "eps" };
visibleformats = new String[] { automagic_format, "svg", "png", "jpeg", "pdf", "ps", "eps" };
- }
- else {
+ } else {
formats = new String[] { "svg", "png", "jpeg", "jpg" };
visibleformats = new String[] { automagic_format, "svg", "png", "jpeg" };
}
@@ -95,7 +94,7 @@ public class SVGSaveDialog {
double quality = 1.0;
int ret = -1;
- JFileChooser fc = new JFileChooser();
+ JFileChooser fc = new JFileChooser(new File("."));
fc.setDialogTitle(DEFAULT_TITLE);
// fc.setFileFilter(new ImageFilter());
SaveOptionsPanel optionsPanel = new SaveOptionsPanel(fc, width, height);
@@ -103,70 +102,54 @@ public class SVGSaveDialog {
ret = fc.showSaveDialog(null);
fc.setDialogTitle("Saving... Please wait.");
- if(ret == JFileChooser.APPROVE_OPTION) {
+ if (ret == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
String format = optionsPanel.getSelectedFormat();
- if(format == null || format == automagic_format) {
+ if (format == null || automagic_format.equals(format)) {
format = guessFormat(file.getName());
}
try {
- if(format == null) {
+ if (format == null) {
showError(fc, "Error saving image.", "File format not recognized.");
- }
- else if(format.equals("jpeg") || format.equals("jpg")) {
+ } else if ("jpeg".equals(format) || "jpg".equals(format)) {
quality = optionsPanel.getJPEGQuality();
plot.saveAsJPEG(file, width, height, quality);
- }
- else if(format.equals("png")) {
+ } else if ("png".equals(format)) {
plot.saveAsPNG(file, width, height);
- }
- else if(format.equals("ps")) {
+ } else if ("ps".equals(format)) {
plot.saveAsPS(file);
- }
- else if(format.equals("eps")) {
+ } else if ("eps".equals(format)) {
plot.saveAsEPS(file);
- }
- else if(format.equals("pdf")) {
+ } else if ("pdf".equals(format)) {
plot.saveAsPDF(file);
- }
- else if(format.equals("svg")) {
+ } else if ("svg".equals(format)) {
plot.saveAsSVG(file);
- }
- else {
+ } else {
showError(fc, "Error saving image.", "Unsupported format: " + format);
}
- }
- catch(java.lang.IncompatibleClassChangeError e) {
+ } catch (java.lang.IncompatibleClassChangeError e) {
showError(fc, "Error saving image.", "It seems that your Java version is incompatible with this version of Batik and Jpeg writing. Sorry.");
- }
- catch(ClassNotFoundException e) {
+ } catch (ClassNotFoundException e) {
showError(fc, "Error saving image.", "A class was not found when saving this image. Maybe installing Apache FOP will help (for PDF, PS and EPS output).\n" + e.toString());
- }
- catch(IOException e) {
- logger.exception(e);
+ } catch (IOException e) {
+ LOG.exception(e);
showError(fc, "Error saving image.", e.toString());
- }
- catch(TranscoderException e) {
- logger.exception(e);
+ } catch (TranscoderException e) {
+ LOG.exception(e);
showError(fc, "Error saving image.", e.toString());
- }
- catch(TransformerFactoryConfigurationError e) {
- logger.exception(e);
+ } catch (TransformerFactoryConfigurationError e) {
+ LOG.exception(e);
showError(fc, "Error saving image.", e.toString());
- }
- catch(TransformerException e) {
- logger.exception(e);
+ } catch (TransformerException e) {
+ LOG.exception(e);
showError(fc, "Error saving image.", e.toString());
- }
- catch(Exception e) {
- logger.exception(e);
+ } catch (Exception e) {
+ LOG.exception(e);
showError(fc, "Error saving image.", e.toString());
}
- }
- else if(ret == JFileChooser.ERROR_OPTION) {
+ } else if (ret == JFileChooser.ERROR_OPTION) {
showError(fc, "Error in file dialog.", "Unknown Error.");
- }
- else if(ret == JFileChooser.CANCEL_OPTION) {
+ } else if (ret == JFileChooser.CANCEL_OPTION) {
// do nothing - except return result
}
return ret;
@@ -180,8 +163,8 @@ public class SVGSaveDialog {
*/
public static String guessFormat(String name) {
String ext = FileUtil.getFilenameExtension(name);
- for(String format : formats) {
- if(format.equals(ext)) {
+ for (String format : formats) {
+ if (format.equals(ext)) {
return ext;
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/savedialog/SaveOptionsPanel.java b/src/de/lmu/ifi/dbs/elki/visualization/savedialog/SaveOptionsPanel.java
index 6193310b..7e77c79f 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/savedialog/SaveOptionsPanel.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/savedialog/SaveOptionsPanel.java
@@ -206,8 +206,8 @@ public class SaveOptionsPanel extends JPanel {
resetSizeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
- modelWidth.setValue(width);
- modelHeight.setValue(height);
+ modelWidth.setValue(Integer.valueOf(width));
+ modelHeight.setValue(Integer.valueOf(height));
aspectRatioLock.setSelected(true);
}
});
@@ -312,8 +312,7 @@ public class SaveOptionsPanel extends JPanel {
* @return Quality value for JPEG.
*/
public double getJPEGQuality() {
- Double qual = 0.7;
- qual = modelQuality.getNumber().doubleValue();
+ double qual =modelQuality.getNumber().doubleValue();
if(qual > 1.0) {
qual = 1.0;
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/ClusterStylingPolicy.java b/src/de/lmu/ifi/dbs/elki/visualization/style/ClusterStylingPolicy.java
index 1d329736..7adecc68 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/ClusterStylingPolicy.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/ClusterStylingPolicy.java
@@ -77,14 +77,15 @@ public class ClusterStylingPolicy implements ClassStylingPolicy {
colors = new TIntArrayList(clusters.size());
Iterator<? extends Cluster<?>> ci = clusters.iterator();
- for(int i = 0;; i++) {
+ for(int i = 0; ci.hasNext(); i++) {
Cluster<?> c = ci.next();
ids.add(DBIDUtil.ensureSet(c.getIDs()));
Color col = SVGUtil.stringToColor(colorset.getColor(i));
- if (col != null) {
+ if(col != null) {
colors.add(col.getRGB());
- } else {
- LoggingUtil.warning("Unrecognized color name: "+colorset.getColor(i));
+ }
+ else {
+ LoggingUtil.warning("Unrecognized color name: " + colorset.getColor(i));
}
if(!ci.hasNext()) {
break;
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/PropertiesBasedStyleLibrary.java b/src/de/lmu/ifi/dbs/elki/visualization/style/PropertiesBasedStyleLibrary.java
index 2bdae5e6..6b0869fb 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/PropertiesBasedStyleLibrary.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/PropertiesBasedStyleLibrary.java
@@ -49,7 +49,7 @@ public class PropertiesBasedStyleLibrary implements StyleLibrary {
/**
* Logger
*/
- protected static final Logging logger = Logging.getLogger(PropertiesBasedStyleLibrary.class);
+ private static final Logging LOG = Logging.getLogger(PropertiesBasedStyleLibrary.class);
/**
* Name of the default color scheme.
@@ -130,24 +130,20 @@ public class PropertiesBasedStyleLibrary implements StyleLibrary {
InputStream stream = null;
try {
stream = FileUtil.openSystemFile(filename);
- }
- catch(FileNotFoundException e) {
+ } catch (FileNotFoundException e) {
try {
stream = FileUtil.openSystemFile(filename + DEFAULT_PROPERTIES_EXTENSION);
- }
- catch(FileNotFoundException e2) {
+ } catch (FileNotFoundException e2) {
try {
stream = FileUtil.openSystemFile(DEFAULT_PROPERTIES_PATH + filename + DEFAULT_PROPERTIES_EXTENSION);
- }
- catch(FileNotFoundException e3) {
+ } catch (FileNotFoundException e3) {
throw new AbortException("Could not find style scheme file '" + filename + "' for scheme '" + name + "'!");
}
}
}
try {
properties.load(stream);
- }
- catch(Exception e) {
+ } catch (Exception e) {
throw new AbortException("Error loading properties file " + filename + ".\n", e);
}
}
@@ -171,7 +167,7 @@ public class PropertiesBasedStyleLibrary implements StyleLibrary {
* @return Resulting value
*/
private <T> T getCached(String prefix, String postfix, Class<T> cls) {
- return cache.get(prefix + "." + postfix, cls);
+ return cache.get(prefix + '.' + postfix, cls);
}
/**
@@ -183,7 +179,7 @@ public class PropertiesBasedStyleLibrary implements StyleLibrary {
* @param data Data
*/
private <T> void setCached(String prefix, String postfix, T data) {
- cache.put(prefix + "." + postfix, data);
+ cache.put(prefix + '.' + postfix, data);
}
/**
@@ -195,26 +191,26 @@ public class PropertiesBasedStyleLibrary implements StyleLibrary {
*/
protected String getPropertyValue(String prefix, String postfix) {
String ret = properties.getProperty(prefix + "." + postfix);
- if(ret != null) {
+ if (ret != null) {
// logger.debugFine("Found property: "+prefix + "." +
// postfix+" for "+prefix);
return ret;
}
int pos = prefix.length();
- while(pos > 0) {
- pos = prefix.lastIndexOf(".", pos - 1);
- if(pos <= 0) {
+ while (pos > 0) {
+ pos = prefix.lastIndexOf('.', pos - 1);
+ if (pos <= 0) {
break;
}
- ret = properties.getProperty(prefix.substring(0, pos) + "." + postfix);
- if(ret != null) {
+ ret = properties.getProperty(prefix.substring(0, pos) + '.' + postfix);
+ if (ret != null) {
// logger.debugFine("Found property: "+prefix.substring(0, pos) + "." +
// postfix+" for "+prefix);
return ret;
}
}
ret = properties.getProperty(postfix);
- if(ret != null) {
+ if (ret != null) {
// logger.debugFine("Found property: "+postfix+" for "+prefix);
return ret;
}
@@ -239,7 +235,7 @@ public class PropertiesBasedStyleLibrary implements StyleLibrary {
@Override
public ColorLibrary getColorSet(String key) {
ColorLibrary cl = getCached(key, COLORSET, ColorLibrary.class);
- if(cl == null) {
+ if (cl == null) {
String[] colors = getPropertyValue(key, COLORSET).split(LIST_SEPARATOR);
cl = new ListBasedColorLibrary(colors, "Default");
setCached(key, COLORSET, cl);
@@ -250,29 +246,27 @@ public class PropertiesBasedStyleLibrary implements StyleLibrary {
@Override
public double getLineWidth(String key) {
Double lw = getCached(key, LINE_WIDTH, Double.class);
- if(lw == null) {
+ if (lw == null) {
try {
- lw = Double.parseDouble(getPropertyValue(key, LINE_WIDTH)) * SCALE;
- }
- catch(NullPointerException e) {
- throw new AbortException("Missing/invalid value in style library: " + key + "." + LINE_WIDTH);
+ lw = Double.valueOf(Double.parseDouble(getPropertyValue(key, LINE_WIDTH)) * SCALE);
+ } catch (NullPointerException e) {
+ throw new AbortException("Missing/invalid value in style library: " + key + '.' + LINE_WIDTH);
}
}
- return lw;
+ return lw.doubleValue();
}
@Override
public double getTextSize(String key) {
Double lw = getCached(key, TEXT_SIZE, Double.class);
- if(lw == null) {
+ if (lw == null) {
try {
- lw = Double.parseDouble(getPropertyValue(key, TEXT_SIZE)) * SCALE;
- }
- catch(NullPointerException e) {
- throw new AbortException("Missing/invalid value in style library: " + key + "." + TEXT_SIZE);
+ lw = Double.valueOf(Double.parseDouble(getPropertyValue(key, TEXT_SIZE)) * SCALE);
+ } catch (NullPointerException e) {
+ throw new AbortException("Missing/invalid value in style library: " + key + '.' + TEXT_SIZE);
}
}
- return lw;
+ return lw.doubleValue();
}
@Override
@@ -283,47 +277,43 @@ public class PropertiesBasedStyleLibrary implements StyleLibrary {
@Override
public double getSize(String key) {
Double lw = getCached(key, GENERIC_SIZE, Double.class);
- if(lw == null) {
+ if (lw == null) {
try {
- lw = Double.parseDouble(getPropertyValue(key, GENERIC_SIZE)) * SCALE;
- }
- catch(NullPointerException e) {
- throw new AbortException("Missing/invalid value in style library: " + key + "." + GENERIC_SIZE);
+ lw = Double.valueOf(Double.parseDouble(getPropertyValue(key, GENERIC_SIZE)) * SCALE);
+ } catch (NullPointerException e) {
+ throw new AbortException("Missing/invalid value in style library: " + key + '.' + GENERIC_SIZE);
}
}
- return lw;
+ return lw.doubleValue();
}
@Override
public double getOpacity(String key) {
Double lw = getCached(key, OPACITY, Double.class);
- if(lw == null) {
+ if (lw == null) {
try {
- lw = Double.parseDouble(getPropertyValue(key, OPACITY));
- }
- catch(NullPointerException e) {
- throw new AbortException("Missing/invalid value in style library: " + key + "." + OPACITY);
+ lw = Double.valueOf(Double.parseDouble(getPropertyValue(key, OPACITY)));
+ } catch (NullPointerException e) {
+ throw new AbortException("Missing/invalid value in style library: " + key + '.' + OPACITY);
}
}
- return lw;
+ return lw.doubleValue();
}
@Override
public LineStyleLibrary lines() {
- if(linelib == null) {
+ if (linelib == null) {
String libname = properties.getProperty(PROP_LINES_LIBRARY, SolidLineStyleLibrary.class.getName());
try {
Class<?> cls;
try {
cls = Class.forName(libname);
- }
- catch(ClassNotFoundException e) {
- cls = Class.forName(LineStyleLibrary.class.getPackage().getName() + "." + libname);
+ } catch (ClassNotFoundException e) {
+ cls = Class.forName(LineStyleLibrary.class.getPackage().getName() + '.' + libname);
}
linelib = (LineStyleLibrary) cls.getConstructor(StyleLibrary.class).newInstance(this);
- }
- catch(Exception e) {
- logger.exception(e);
+ } catch (Exception e) {
+ LOG.exception(e);
linelib = new SolidLineStyleLibrary(this);
}
}
@@ -332,23 +322,21 @@ public class PropertiesBasedStyleLibrary implements StyleLibrary {
@Override
public MarkerLibrary markers() {
- if(markerlib == null) {
+ if (markerlib == null) {
String libname = properties.getProperty(PROP_MARKER_LIBRARY, PrettyMarkers.class.getName());
try {
Class<?> cls;
try {
cls = Class.forName(libname);
- }
- catch(ClassNotFoundException e) {
- cls = Class.forName(MarkerLibrary.class.getPackage().getName() + "." + libname);
+ } catch (ClassNotFoundException e) {
+ cls = Class.forName(MarkerLibrary.class.getPackage().getName() + '.' + libname);
}
markerlib = (MarkerLibrary) cls.getConstructor(StyleLibrary.class).newInstance(this);
- }
- catch(Exception e) {
- logger.exception(e);
+ } catch (Exception e) {
+ LOG.exception(e);
markerlib = new PrettyMarkers(this);
}
}
return markerlib;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/StyleResult.java b/src/de/lmu/ifi/dbs/elki/visualization/style/StyleResult.java
index fe8a73ce..e8f0d3fb 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/StyleResult.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/StyleResult.java
@@ -32,7 +32,6 @@ import de.lmu.ifi.dbs.elki.result.Result;
* @apiviz.landmark
* @apiviz.composedOf StylingPolicy
*/
-// TODO: pull style library etc. from VisualizerContext here?
public class StyleResult implements Result {
/**
* Styling policy
@@ -40,6 +39,11 @@ public class StyleResult implements Result {
StylingPolicy policy;
/**
+ * Style library
+ */
+ StyleLibrary library;
+
+ /**
* Get the active styling policy
*
* @return Styling policy
@@ -57,6 +61,24 @@ public class StyleResult implements Result {
this.policy = policy;
}
+ /**
+ * Get the style library
+ *
+ * @return Style library
+ */
+ public StyleLibrary getStyleLibrary() {
+ return library;
+ }
+
+ /**
+ * Get the style library
+ *
+ * @param library Style library
+ */
+ public void setStyleLibrary(StyleLibrary library) {
+ this.library = library;
+ }
+
@Override
public String getLongName() {
return "Style policy";
@@ -66,4 +88,4 @@ public class StyleResult implements Result {
public String getShortName() {
return "style-policy";
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/lines/DashedLineStyleLibrary.java b/src/de/lmu/ifi/dbs/elki/visualization/style/lines/DashedLineStyleLibrary.java
index 5dbfe67a..e3abd7c1 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/lines/DashedLineStyleLibrary.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/lines/DashedLineStyleLibrary.java
@@ -107,14 +107,14 @@ public class DashedLineStyleLibrary implements LineStyleLibrary {
boolean interpolated = false;
// process flavoring flags
for(Object flag : flags) {
- if(flag == LineStyleLibrary.FLAG_STRONG) {
+ if(LineStyleLibrary.FLAG_STRONG.equals(flag)) {
width = width * 1.5;
}
- else if(flag == LineStyleLibrary.FLAG_WEAK) {
+ else if(LineStyleLibrary.FLAG_WEAK.equals(flag)) {
cls.setStatement(CSSConstants.CSS_STROKE_OPACITY_PROPERTY, ".50");
width = width * 0.75;
}
- else if(flag == LineStyleLibrary.FLAG_INTERPOLATED) {
+ else if(LineStyleLibrary.FLAG_INTERPOLATED.equals(flag)) {
interpolated = true;
}
}
@@ -125,10 +125,10 @@ public class DashedLineStyleLibrary implements LineStyleLibrary {
double[] pat = dashpatterns[styleflav];
assert (pat.length % 2 == 0);
if(pat.length > 0) {
- StringBuffer pattern = new StringBuffer();
+ StringBuilder pattern = new StringBuilder();
for(int i = 0; i < pat.length; i++) {
if(i > 0) {
- pattern.append(",");
+ pattern.append(',');
}
pattern.append(SVGUtil.fmt(pat[i] * width * 30));
// pattern.append("%");
@@ -144,10 +144,10 @@ public class DashedLineStyleLibrary implements LineStyleLibrary {
assert (pat.length % 2 == 0);
// TODO: add dotting.
if(pat.length > 0) {
- StringBuffer pattern = new StringBuffer();
+ StringBuilder pattern = new StringBuilder();
for(int i = 0; i < pat.length; i++) {
if(i > 0) {
- pattern.append(",");
+ pattern.append(',');
}
pattern.append(SVGUtil.fmt(pat[i] * width));
// pattern.append("%");
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/lines/LineStyleLibrary.java b/src/de/lmu/ifi/dbs/elki/visualization/style/lines/LineStyleLibrary.java
index aff1e0fc..79f3eda0 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/lines/LineStyleLibrary.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/lines/LineStyleLibrary.java
@@ -50,17 +50,17 @@ public interface LineStyleLibrary {
/**
* Meta flag to request a 'stronger' version of the style
*/
- public final static String FLAG_STRONG = "strong";
+ public static final String FLAG_STRONG = "strong";
/**
* Meta flag to request a 'weaker' version of the style
*/
- public final static String FLAG_WEAK = "weak";
+ public static final String FLAG_WEAK = "weak";
/**
* Meta flag to request an 'interpolated' version of the style
*/
- public final static String FLAG_INTERPOLATED = "interpolated";
+ public static final String FLAG_INTERPOLATED = "interpolated";
/**
* Add the formatting statements to the given CSS class.
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/lines/SolidLineStyleLibrary.java b/src/de/lmu/ifi/dbs/elki/visualization/style/lines/SolidLineStyleLibrary.java
index 051bff0a..51491db2 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/lines/SolidLineStyleLibrary.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/lines/SolidLineStyleLibrary.java
@@ -87,20 +87,20 @@ public class SolidLineStyleLibrary implements LineStyleLibrary {
boolean interpolated = false;
// process flavoring flags
for(Object flag : flags) {
- if(flag == LineStyleLibrary.FLAG_STRONG) {
+ if(LineStyleLibrary.FLAG_STRONG.equals(flag)) {
width = width * 1.5;
}
- else if(flag == LineStyleLibrary.FLAG_WEAK) {
+ else if(LineStyleLibrary.FLAG_WEAK.equals(flag)) {
cls.setStatement(CSSConstants.CSS_STROKE_OPACITY_PROPERTY, ".50");
width = width * 0.75;
}
- else if(flag == LineStyleLibrary.FLAG_INTERPOLATED) {
+ else if(LineStyleLibrary.FLAG_INTERPOLATED.equals(flag)) {
interpolated = true;
}
}
cls.setStatement(CSSConstants.CSS_STROKE_WIDTH_PROPERTY, SVGUtil.fmt(width));
if(interpolated) {
- cls.setStatement(CSSConstants.CSS_STROKE_DASHARRAY_PROPERTY, "" + SVGUtil.fmt(width / StyleLibrary.SCALE * 2) + "," + SVGUtil.fmt(width / StyleLibrary.SCALE * 2));
+ cls.setStatement(CSSConstants.CSS_STROKE_DASHARRAY_PROPERTY, "" + SVGUtil.fmt(width / StyleLibrary.SCALE * 2.) + "," + SVGUtil.fmt(width / StyleLibrary.SCALE * 2.));
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/marker/CircleMarkers.java b/src/de/lmu/ifi/dbs/elki/visualization/style/marker/CircleMarkers.java
index b847d571..ae066c9e 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/marker/CircleMarkers.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/marker/CircleMarkers.java
@@ -72,7 +72,7 @@ public class CircleMarkers implements MarkerLibrary {
*/
@Override
public Element useMarker(SVGPlot plot, Element parent, double x, double y, int stylenr, double size) {
- Element marker = plot.svgCircle(x, y, size / 2);
+ Element marker = plot.svgCircle(x, y, size * .5);
final String col;
if(stylenr == -1) {
col = dotcolor;
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/marker/MinimalMarkers.java b/src/de/lmu/ifi/dbs/elki/visualization/style/marker/MinimalMarkers.java
index 2f5a8c2a..b7ad6274 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/marker/MinimalMarkers.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/marker/MinimalMarkers.java
@@ -72,7 +72,7 @@ public class MinimalMarkers implements MarkerLibrary {
*/
@Override
public Element useMarker(SVGPlot plot, Element parent, double x, double y, int stylenr, double size) {
- Element marker = plot.svgRect(x - size / 2, y - size / 2, size, size);
+ Element marker = plot.svgRect(x - size * .5, y - size * .5, size, size);
final String col;
if(stylenr == -1) {
col = dotcolor;
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/marker/PrettyMarkers.java b/src/de/lmu/ifi/dbs/elki/visualization/style/marker/PrettyMarkers.java
index d868bb06..7352fea3 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/marker/PrettyMarkers.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/marker/PrettyMarkers.java
@@ -48,7 +48,7 @@ public class PrettyMarkers implements MarkerLibrary {
/**
* Default prefix to use.
*/
- private final static String DEFAULT_PREFIX = "s";
+ private static final String DEFAULT_PREFIX = "s";
/**
* Prefix for the IDs generated.
@@ -159,7 +159,7 @@ public class PrettyMarkers implements MarkerLibrary {
}
case 5: {
// O filled circle
- Element circ = plot.svgCircle(x, y, size / 2);
+ Element circ = plot.svgCircle(x, y, size * .5);
SVGUtil.setStyle(circ, SVGConstants.CSS_FILL_PROPERTY + ":" + colorstr);
parent.appendChild(circ);
break;
@@ -192,7 +192,7 @@ public class PrettyMarkers implements MarkerLibrary {
* @param size Size
*/
protected void plotGray(SVGPlot plot, Element parent, double x, double y, double size) {
- Element marker = plot.svgCircle(x, y, size / 2);
+ Element marker = plot.svgCircle(x, y, size * .5);
SVGUtil.setStyle(marker, SVGConstants.CSS_FILL_PROPERTY + ":" + greycolor);
parent.appendChild(marker);
}
@@ -207,7 +207,7 @@ public class PrettyMarkers implements MarkerLibrary {
* @param size Size
*/
protected void plotUncolored(SVGPlot plot, Element parent, double x, double y, double size) {
- Element marker = plot.svgCircle(x, y, size / 2);
+ Element marker = plot.svgCircle(x, y, size * .5);
SVGUtil.setStyle(marker, SVGConstants.CSS_FILL_PROPERTY + ":" + dotcolor);
parent.appendChild(marker);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/style/presentation.properties b/src/de/lmu/ifi/dbs/elki/visualization/style/presentation.properties
index 8c597066..01264673 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/style/presentation.properties
+++ b/src/de/lmu/ifi/dbs/elki/visualization/style/presentation.properties
@@ -74,9 +74,9 @@ xycurve.text-size=0.04
overview.labels.text-size=0.08
## Selection colors
-plot.selection.color=darkblue
+plot.selection.color=red
plot.selection.opacity=0.4
-plot.selection.size=0.015
+plot.selection.size=0.03
## Circle segment colors. Will be interpolated to produce extra classes.
segments.border.color=#FF0073
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGArrow.java b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGArrow.java
new file mode 100644
index 00000000..3859deb5
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGArrow.java
@@ -0,0 +1,114 @@
+package de.lmu.ifi.dbs.elki.visualization.svg;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import org.w3c.dom.Element;
+
+/**
+ * Static class for drawing simple arrows
+ *
+ * @author Erich Schubert
+ * @author Robert Rödler
+ *
+ * @apiviz.uses SVGPath
+ */
+public final class SVGArrow {
+ /**
+ * Direction constants
+ *
+ * @author Erich Schubert
+ * @author Robert Rödler
+ *
+ * @apiviz.exclude
+ */
+ public static enum Direction {
+ LEFT, DOWN, RIGHT, UP, // SWAPWITH, INSERT
+ }
+
+ /**
+ * Constant for "up"
+ */
+ public static final Direction UP = Direction.UP;
+
+ /**
+ * Constant for "down"
+ */
+ public static final Direction DOWN = Direction.DOWN;
+
+ /**
+ * Constant for "right"
+ */
+ public static final Direction RIGHT = Direction.RIGHT;
+
+ /**
+ * Constant for "left"
+ */
+ public static final Direction LEFT = Direction.LEFT;
+
+ /**
+ * Draw an arrow at the given position.
+ *
+ * Note: the arrow is an unstyled svg path. You need to apply style afterwards.
+ *
+ * @param svgp Plot to draw to
+ * @param dir Direction to draw
+ * @param x Center x coordinate
+ * @param y Center y coordinate
+ * @param size Arrow size
+ * @return SVG Element
+ */
+ public static Element makeArrow(SVGPlot svgp, Direction dir, double x, double y, double size) {
+ final SVGPath path = new SVGPath();
+ final double hs = size / 2.;
+
+ switch(dir){
+ case LEFT:
+ path.drawTo(x + hs, y + hs);
+ path.drawTo(x - hs, y);
+ path.drawTo(x + hs, y - hs);
+ path.drawTo(x + hs, y + hs);
+ break;
+ case DOWN:
+ path.drawTo(x - hs, y - hs);
+ path.drawTo(x + hs, y - hs);
+ path.drawTo(x, y + hs);
+ path.drawTo(x - hs, y - hs);
+ break;
+ case RIGHT:
+ path.drawTo(x - hs, y - hs);
+ path.drawTo(x + hs, y);
+ path.drawTo(x - hs, y + hs);
+ path.drawTo(x - hs, y - hs);
+ break;
+ case UP:
+ path.drawTo(x - hs, y + hs);
+ path.drawTo(x, y - hs);
+ path.drawTo(x + hs, y + hs);
+ path.drawTo(x - hs, y + hs);
+ break;
+ }
+ path.close();
+ return path.makeElement(svgp);
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperCube.java b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperCube.java
index 723f13df..656e42da 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperCube.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperCube.java
@@ -70,7 +70,7 @@ public class SVGHyperCube {
* @param max Opposite corner
* @return path element
*/
- public static <V extends NumberVector<V, ?>> Element drawFrame(SVGPlot svgp, Projection2D proj, V min, V max) {
+ public static <V extends NumberVector<?>> Element drawFrame(SVGPlot svgp, Projection2D proj, V min, V max) {
SVGPath path = new SVGPath();
ArrayList<double[]> edges = getVisibleEdges(proj, min.getColumnVector().getArrayRef(), max.getColumnVector().getArrayRef());
double[] rv_min = proj.fastProjectDataToRenderSpace(min);
@@ -107,7 +107,7 @@ public class SVGHyperCube {
* @param max Opposite corner
* @return group element
*/
- public static <V extends NumberVector<V, ?>> Element drawFilled(SVGPlot svgp, String cls, Projection2D proj, V min, V max) {
+ public static <V extends NumberVector<?>> Element drawFilled(SVGPlot svgp, String cls, Projection2D proj, V min, V max) {
Element group = svgp.svgElement(SVGConstants.SVG_G_TAG);
ArrayList<double[]> edges = getVisibleEdges(proj, min.getColumnVector().getArrayRef(), max.getColumnVector().getArrayRef());
double[] rv_min = proj.fastProjectDataToRenderSpace(min);
@@ -184,14 +184,14 @@ public class SVGHyperCube {
for(int j = i + 1; j < r_edges.size(); j++) {
if(!b.get(i) && !b.get(j)) {
double[] deltaj = r_edges.get(j);
- StringBuffer pbuf = new StringBuffer();
- pbuf.append(SVGUtil.fmt(r_min[0])).append(",");
- pbuf.append(SVGUtil.fmt(r_min[1])).append(" ");
- pbuf.append(SVGUtil.fmt(r_min[0] + deltai[0])).append(",");
- pbuf.append(SVGUtil.fmt(r_min[1] + deltai[1])).append(" ");
- pbuf.append(SVGUtil.fmt(r_min[0] + deltai[0] + deltaj[0])).append(",");
- pbuf.append(SVGUtil.fmt(r_min[1] + deltai[1] + deltaj[1])).append(" ");
- pbuf.append(SVGUtil.fmt(r_min[0] + deltaj[0])).append(",");
+ StringBuilder pbuf = new StringBuilder();
+ pbuf.append(SVGUtil.fmt(r_min[0])).append(',');
+ pbuf.append(SVGUtil.fmt(r_min[1])).append(' ');
+ pbuf.append(SVGUtil.fmt(r_min[0] + deltai[0])).append(',');
+ pbuf.append(SVGUtil.fmt(r_min[1] + deltai[1])).append(' ');
+ pbuf.append(SVGUtil.fmt(r_min[0] + deltai[0] + deltaj[0])).append(',');
+ pbuf.append(SVGUtil.fmt(r_min[1] + deltai[1] + deltaj[1])).append(' ');
+ pbuf.append(SVGUtil.fmt(r_min[0] + deltaj[0])).append(',');
pbuf.append(SVGUtil.fmt(r_min[1] + deltaj[1]));
Element poly = plot.svgElement(SVGConstants.SVG_POLYGON_TAG);
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperSphere.java b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperSphere.java
index 29b437f9..33daa56b 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperSphere.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGHyperSphere.java
@@ -45,7 +45,7 @@ public class SVGHyperSphere {
*
* kappa = 4 * (Math.sqrt(2)-1)/3
*/
- public final static double EUCLIDEAN_KAPPA = 0.5522847498;
+ public static final double EUCLIDEAN_KAPPA = 0.5522847498;
/**
* Wireframe "manhattan" hypersphere
@@ -57,7 +57,7 @@ public class SVGHyperSphere {
* @param rad radius
* @return path element
*/
- public static <D extends NumberDistance<?, ?>> Element drawManhattan(SVGPlot svgp, Projection2D proj, NumberVector<?, ?> mid, D rad) {
+ public static <D extends NumberDistance<?, ?>> Element drawManhattan(SVGPlot svgp, Projection2D proj, NumberVector<?> mid, D rad) {
final double radius = rad.doubleValue();
final double[] v_mid = mid.getColumnVector().getArrayRef(); // a copy
final BitSet dims = proj.getVisibleDimensions2D();
@@ -104,7 +104,7 @@ public class SVGHyperSphere {
* @param rad radius
* @return path element
*/
- public static <D extends NumberDistance<?, ?>> Element drawEuclidean(SVGPlot svgp, Projection2D proj, NumberVector<?, ?> mid, D rad) {
+ public static <D extends NumberDistance<?, ?>> Element drawEuclidean(SVGPlot svgp, Projection2D proj, NumberVector<?> mid, D rad) {
final double radius = rad.doubleValue();
double[] v_mid = mid.getColumnVector().getArrayRef(); // a copy
BitSet dims = proj.getVisibleDimensions2D();
@@ -157,7 +157,7 @@ public class SVGHyperSphere {
* @param p L_p value
* @return path element
*/
- public static <D extends NumberDistance<?, ?>> Element drawLp(SVGPlot svgp, Projection2D proj, NumberVector<?, ?> mid, D rad, double p) {
+ public static <D extends NumberDistance<?, ?>> Element drawLp(SVGPlot svgp, Projection2D proj, NumberVector<?> mid, D rad, double p) {
final double radius = rad.doubleValue();
final double[] v_mid = mid.getColumnVector().getArrayRef();
final BitSet dims = proj.getVisibleDimensions2D();
@@ -277,7 +277,7 @@ public class SVGHyperSphere {
* @param rad radius
* @return path element
*/
- public static <D extends NumberDistance<?, ?>> Element drawCross(SVGPlot svgp, Projection2D proj, NumberVector<?, ?> mid, D rad) {
+ public static <D extends NumberDistance<?, ?>> Element drawCross(SVGPlot svgp, Projection2D proj, NumberVector<?> mid, D rad) {
final double radius = rad.doubleValue();
final double[] v_mid = mid.getColumnVector().getArrayRef();
final BitSet dims = proj.getVisibleDimensions2D();
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPath.java b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPath.java
index 2b86870b..7c3e97e9 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPath.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPath.java
@@ -40,7 +40,7 @@ public class SVGPath {
/**
* String buffer for building the path.
*/
- private StringBuffer buf = new StringBuffer();
+ private StringBuilder buf = new StringBuilder();
/**
* The last action we did, to not add unnecessary commands
@@ -56,48 +56,48 @@ public class SVGPath {
/**
* The lower case version (relative) line to command.
*/
- public final static String PATH_LINE_TO_RELATIVE = SVGConstants.PATH_LINE_TO.toLowerCase();
+ public static final String PATH_LINE_TO_RELATIVE = SVGConstants.PATH_LINE_TO.toLowerCase();
/**
* The lower case version (relative) move command.
*/
- public final static String PATH_MOVE_RELATIVE = SVGConstants.PATH_MOVE.toLowerCase();
+ public static final String PATH_MOVE_RELATIVE = SVGConstants.PATH_MOVE.toLowerCase();
/**
* The lower case version (relative) horizontal line to command.
*/
- public final static String PATH_HORIZONTAL_LINE_TO_RELATIVE = SVGConstants.PATH_HORIZONTAL_LINE_TO.toLowerCase();
+ public static final String PATH_HORIZONTAL_LINE_TO_RELATIVE = SVGConstants.PATH_HORIZONTAL_LINE_TO.toLowerCase();
/**
* The lower case version (relative) vertical line to command.
*/
- public final static String PATH_VERTICAL_LINE_TO_RELATIVE = SVGConstants.PATH_VERTICAL_LINE_TO.toLowerCase();
+ public static final String PATH_VERTICAL_LINE_TO_RELATIVE = SVGConstants.PATH_VERTICAL_LINE_TO.toLowerCase();
/**
* The lower case version (relative) cubic line to command.
*/
- public final static String PATH_CUBIC_TO_RELATIVE = SVGConstants.PATH_CUBIC_TO.toLowerCase();
+ public static final String PATH_CUBIC_TO_RELATIVE = SVGConstants.PATH_CUBIC_TO.toLowerCase();
/**
* The lower case version (relative) smooth cubic to command.
*/
- public final static String PATH_SMOOTH_CUBIC_TO_RELATIVE = PATH_SMOOTH_CUBIC_TO.toLowerCase();
+ public static final String PATH_SMOOTH_CUBIC_TO_RELATIVE = PATH_SMOOTH_CUBIC_TO.toLowerCase();
/**
* The lower case version (relative) quadratic interpolation to command.
*/
- public final static String PATH_QUAD_TO_RELATIVE = SVGConstants.PATH_QUAD_TO.toLowerCase();
+ public static final String PATH_QUAD_TO_RELATIVE = SVGConstants.PATH_QUAD_TO.toLowerCase();
/**
* The lower case version (relative) smooth quadratic interpolation to
* command.
*/
- public final static String PATH_SMOOTH_QUAD_TO_RELATIVE = SVGConstants.PATH_SMOOTH_QUAD_TO.toLowerCase();
+ public static final String PATH_SMOOTH_QUAD_TO_RELATIVE = SVGConstants.PATH_SMOOTH_QUAD_TO.toLowerCase();
/**
* The lower case version (relative) path arc command.
*/
- public final static String PATH_ARC_RELATIVE = SVGConstants.PATH_ARC.toLowerCase();
+ public static final String PATH_ARC_RELATIVE = SVGConstants.PATH_ARC.toLowerCase();
/**
* Empty path constructor.
@@ -794,7 +794,7 @@ public class SVGPath {
}
for(double d : ds) {
buf.append(SVGUtil.FMT.format(d));
- buf.append(" ");
+ buf.append(' ');
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPlot.java b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPlot.java
index e45c7b5a..43244047 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPlot.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGPlot.java
@@ -24,6 +24,7 @@ package de.lmu.ifi.dbs.elki.visualization.svg;
*/
import java.awt.image.BufferedImage;
+import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -331,7 +332,7 @@ public class SVGPlot {
* @throws TransformerException Transformation error
*/
public void saveAsSVG(File file) throws IOException, TransformerFactoryConfigurationError, TransformerException {
- OutputStream out = new FileOutputStream(file);
+ OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
// TODO embed linked images.
javax.xml.transform.Result result = new StreamResult(out);
SVGDocument doc = cloneDocument();
@@ -356,7 +357,7 @@ public class SVGPlot {
transcoder.addTranscodingHint(XMLAbstractTranscoder.KEY_XML_PARSER_VALIDATING, Boolean.FALSE);
SVGDocument doc = cloneDocument();
TranscoderInput input = new TranscoderInput(doc);
- OutputStream out = new FileOutputStream(file);
+ OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
TranscoderOutput output = new TranscoderOutput(out);
transcoder.transcode(input, output);
out.flush();
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGScoreBar.java b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGScoreBar.java
index ac352169..3af2ea41 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGScoreBar.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGScoreBar.java
@@ -111,7 +111,7 @@ public class SVGScoreBar {
Element bar = svgp.svgRect(x, y, width, height);
bar.setAttribute(SVGConstants.SVG_FILL_ATTRIBUTE, "#a0a0a0");
bar.setAttribute(SVGConstants.SVG_STROKE_ATTRIBUTE, "#a0a0a0");
- bar.setAttribute(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, "" + height * 0.01);
+ bar.setAttribute(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, String.valueOf(height * 0.01));
barchart.appendChild(bar);
if(fill >= 0 && fill <= size + 1) {
@@ -119,7 +119,7 @@ public class SVGScoreBar {
Element chart = svgp.svgRect(x + 0.02 * height, y + 0.02 * height, fpos, height - 0.04 * height);
chart.setAttribute(SVGConstants.SVG_FILL_ATTRIBUTE, "#d4e4f1");
chart.setAttribute(SVGConstants.SVG_STROKE_ATTRIBUTE, "#a0a0a0");
- chart.setAttribute(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, "" + height * 0.01);
+ chart.setAttribute(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, String.valueOf(height * 0.01));
barchart.appendChild(chart);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGSimpleLinearAxis.java b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGSimpleLinearAxis.java
index 987915b7..930e9e8e 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGSimpleLinearAxis.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGSimpleLinearAxis.java
@@ -66,17 +66,17 @@ public class SVGSimpleLinearAxis {
/**
* CSS class name for the axes
*/
- private final static String CSS_AXIS = "axis";
+ private static final String CSS_AXIS = "axis";
/**
* CSS class name for the axes
*/
- private final static String CSS_AXIS_TICK = "axis-tick";
+ private static final String CSS_AXIS_TICK = "axis-tick";
/**
* CSS class name for the axes
*/
- private final static String CSS_AXIS_LABEL = "axis-label";
+ private static final String CSS_AXIS_LABEL = "axis-label";
/**
* Register CSS classes with a {@link CSSClassManager}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGUtil.java b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGUtil.java
index 4e40d1b0..633cb3e5 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/svg/SVGUtil.java
@@ -79,7 +79,7 @@ public final class SVGUtil {
* SVG color names conversion.
*/
final private static TObjectIntHashMap<String> SVG_COLOR_NAMES;
-
+
/**
* Key not found value. Not a reasonable color, fully transparent!
*/
@@ -244,7 +244,7 @@ public final class SVGUtil {
/**
* CSS Stylesheet from Javax, to parse color values.
*/
- private final static StyleSheet colorLookupStylesheet = new StyleSheet();
+ private static final StyleSheet colorLookupStylesheet = new StyleSheet();
/**
* Format a double according to the SVG specs.
@@ -329,13 +329,13 @@ public final class SVGUtil {
*/
public static void addCSSClass(Element e, String cssclass) {
String oldval = e.getAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE);
- if(oldval == null || oldval.length() == 0) {
+ if (oldval == null || oldval.length() == 0) {
setAtt(e, SVGConstants.SVG_CLASS_ATTRIBUTE, cssclass);
return;
}
String[] classes = oldval.split(" ");
- for(String c : classes) {
- if(c.equals(cssclass)) {
+ for (String c : classes) {
+ if (c.equals(cssclass)) {
return;
}
}
@@ -350,22 +350,36 @@ public final class SVGUtil {
*/
public static void removeCSSClass(Element e, String cssclass) {
String oldval = e.getAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE);
- if(oldval == null) {
+ if (oldval == null) {
return;
}
String[] classes = oldval.split(" ");
- String joined = "";
- for(String c : classes) {
- if(!c.equals(cssclass)) {
- if(joined.length() > 0) {
- joined = joined + " " + c;
+ if (classes.length == 1) {
+ if (cssclass.equals(classes[0])) {
+ e.removeAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE);
+ }
+ } else if (classes.length == 2) {
+ if (cssclass.equals(classes[0])) {
+ if (cssclass.equals(classes[1])) {
+ e.removeAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE);
+ } else {
+ e.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, classes[1]);
}
- else {
- joined = c;
+ } else if (cssclass.equals(classes[1])) {
+ e.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, classes[0]);
+ }
+ } else {
+ StringBuilder joined = new StringBuilder();
+ for (String c : classes) {
+ if (!c.equals(cssclass)) {
+ if (joined.length() > 0) {
+ joined.append(' ');
+ }
+ joined.append(c);
}
}
+ e.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, joined.toString());
}
- SVGUtil.setAtt(e, SVGConstants.SVG_CLASS_ATTRIBUTE, joined);
}
/**
@@ -376,7 +390,7 @@ public final class SVGUtil {
*/
public static Element makeStyleElement(Document document) {
Element style = SVGUtil.svgElement(document, SVGConstants.SVG_STYLE_TAG);
- SVGUtil.setAtt(style, SVGConstants.SVG_TYPE_ATTRIBUTE, SVGConstants.CSS_MIME_TYPE);
+ style.setAttribute(SVGConstants.SVG_TYPE_ATTRIBUTE, SVGConstants.CSS_MIME_TYPE);
return style;
}
@@ -479,7 +493,7 @@ public final class SVGUtil {
*/
public static Color stringToColor(String str) {
int icol = SVG_COLOR_NAMES.get(str.toLowerCase());
- if(icol != NO_VALUE) {
+ if (icol != NO_VALUE) {
return new Color(icol, false);
}
return colorLookupStylesheet.stringToColor(str);
@@ -494,7 +508,7 @@ public final class SVGUtil {
* @return Color string
*/
public static String colorToString(Color col) {
- return String.format("#%02x%02x%02x", col.getRed(), col.getGreen(), col.getBlue());
+ return colorToString(col.getRGB());
}
/**
@@ -506,7 +520,12 @@ public final class SVGUtil {
* @return Color string
*/
public static String colorToString(int col) {
- return String.format("#%02x%02x%02x", (col >>> 16) & 0xFF, (col >>> 8) & 0xFF, col & 0xFF);
+ char[] buf = new char[] { '#', '0', '0', '0', '0', '0', '0' };
+ for (int i = 7; i > 0; i--) {
+ buf[i] += (col & 0xF);
+ col >>= 4;
+ }
+ return new String(buf);
}
/**
@@ -526,8 +545,8 @@ public final class SVGUtil {
double swidth = iwidth + lmargin + rmargin;
double sheight = iheight + tmargin + bmargin;
double scale = Math.max(swidth / owidth, sheight / oheight);
- double offx = (scale * owidth - swidth) / 2 + lmargin;
- double offy = (scale * oheight - sheight) / 2 + tmargin;
+ double offx = (scale * owidth - swidth) * .5 + lmargin;
+ double offy = (scale * oheight - sheight) * .5 + tmargin;
return "scale(" + fmt(1 / scale) + ") translate(" + fmt(offx) + " " + fmt(offy) + ")";
}
@@ -578,8 +597,7 @@ public final class SVGUtil {
cPt.setX(gnme.getClientX());
cPt.setY(gnme.getClientY());
return cPt.matrixTransform(imat);
- }
- catch(Exception e) {
+ } catch (Exception e) {
LoggingUtil.warning("Error getting coordinates from SVG event.", e);
return null;
}
@@ -592,7 +610,7 @@ public final class SVGUtil {
*/
public static void removeLastChild(Element tag) {
final Node last = tag.getLastChild();
- if(last != null) {
+ if (last != null) {
tag.removeChild(last);
}
}
@@ -603,8 +621,8 @@ public final class SVGUtil {
* @param elem Element to remove
*/
public static void removeFromParent(Element elem) {
- if(elem != null) {
- if(elem.getParentNode() != null) {
+ if (elem != null) {
+ if (elem.getParentNode() != null) {
elem.getParentNode().removeChild(elem);
}
}
@@ -625,25 +643,25 @@ public final class SVGUtil {
public static Element svgCircleSegment(SVGPlot svgp, double centerx, double centery, double angleStart, double angleDelta, double innerRadius, double outerRadius) {
double sin1st = Math.sin(angleStart);
double cos1st = Math.cos(angleStart);
-
+
double sin2nd = Math.sin(angleStart + angleDelta);
double cos2nd = Math.cos(angleStart + angleDelta);
-
+
double inner1stx = centerx + (innerRadius * sin1st);
double inner1sty = centery - (innerRadius * cos1st);
double outer1stx = centerx + (outerRadius * sin1st);
double outer1sty = centery - (outerRadius * cos1st);
-
+
double inner2ndx = centerx + (innerRadius * sin2nd);
double inner2ndy = centery - (innerRadius * cos2nd);
double outer2ndx = centerx + (outerRadius * sin2nd);
double outer2ndy = centery - (outerRadius * cos2nd);
-
+
double largeArc = 0;
- if(angleDelta >= Math.PI) {
+ if (angleDelta >= Math.PI) {
largeArc = 1;
}
-
+
SVGPath path = new SVGPath(inner1stx, inner1sty);
path.lineTo(outer1stx, outer1sty);
path.ellipticalArc(outerRadius, outerRadius, 0, largeArc, 1, outer2ndx, outer2ndy);
@@ -651,7 +669,7 @@ public final class SVGUtil {
if (innerRadius > 0) {
path.ellipticalArc(innerRadius, innerRadius, 0, largeArc, 0, inner1stx, inner1sty);
}
-
+
return path.makeElement(svgp);
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/svg/VoronoiDraw.java b/src/de/lmu/ifi/dbs/elki/visualization/svg/VoronoiDraw.java
index 317a44ae..2997456d 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/svg/VoronoiDraw.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/svg/VoronoiDraw.java
@@ -35,6 +35,9 @@ import de.lmu.ifi.dbs.elki.visualization.projections.Projection2D;
*
* @author Robert Rödler
* @author Erich Schubert
+ *
+ * @apiviz.uses de.lmu.ifi.dbs.elki.math.geometry.SweepHullDelaunay2D.Triangle
+ * @apiviz.uses Projection2D
*/
public class VoronoiDraw {
/**
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisFactory.java
index 40ae5cdf..565cf784 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisFactory.java
@@ -32,6 +32,7 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
* @author Remigius Wojdanowski
*
* @apiviz.uses ThumbnailVisualization oneway - - «create»
+ * @apiviz.excludeSubtypes
*/
public abstract class AbstractVisFactory implements VisFactory {
/**
@@ -49,8 +50,7 @@ public abstract class AbstractVisFactory implements VisFactory {
@Override
public Visualization makeVisualizationOrThumbnail(VisualizationTask task) {
// Is this a thumbnail request?
- Boolean isthumb = task.get(VisualizationTask.THUMBNAIL, Boolean.class);
- if (isthumb != null && isthumb.booleanValue() && allowThumbnails(task)) {
+ if (task.thumbnail && allowThumbnails(task)) {
return new ThumbnailVisualization(this, task, thumbmask);
}
return makeVisualization(task);
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisualization.java
index bbff0117..b023827d 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/AbstractVisualization.java
@@ -37,6 +37,7 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
* Abstract base class for visualizations.
*
* @author Erich Schubert
+ * @apiviz.excludeSubtypes
*/
public abstract class AbstractVisualization implements Visualization, ResultListener {
/**
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/StaticVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/StaticVisualizationInstance.java
index 3a4be45b..3cd24289 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/StaticVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/StaticVisualizationInstance.java
@@ -32,14 +32,14 @@ import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
*
* @author Erich Schubert
*/
-public class StaticVisualization extends AbstractVisualization {
+public class StaticVisualizationInstance extends AbstractVisualization {
/**
* Unchanging precomputed visualization.
*
* @param task Task to visualize
* @param element Element containing the resulting visualization
*/
- public StaticVisualization(VisualizationTask task, Element element) {
+ public StaticVisualizationInstance(VisualizationTask task, Element element) {
super(task);
this.layer = element;
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisualizerUtil.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisualizerUtil.java
index 1bc9b332..389aec52 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisualizerUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/VisualizerUtil.java
@@ -47,6 +47,14 @@ import de.lmu.ifi.dbs.elki.visualization.VisualizerContext;
*/
public final class VisualizerUtil {
/**
+ * Fake constructor: do not instantiate.
+ *
+ */
+ private VisualizerUtil() {
+ // Do not instantiate.
+ }
+
+ /**
* Find the visualizer context in a result tree.
*
* @param baseResult base result to start searching at.
@@ -54,33 +62,14 @@ public final class VisualizerUtil {
*/
public static VisualizerContext getContext(HierarchicalResult baseResult) {
List<VisualizerContext> contexts = ResultUtil.filterResults(baseResult, VisualizerContext.class);
- if(!contexts.isEmpty()) {
+ if (!contexts.isEmpty()) {
return contexts.get(0);
- }
- else {
+ } else {
return null;
}
}
/**
- * Utility function to test for Visualizer visibility.
- *
- * @param task Visualization task
- * @return true when visible
- */
- public static boolean isVisible(VisualizationTask task) {
- // Currently enabled?
- Boolean enabled = task.getGenerics(VisualizationTask.META_VISIBLE, Boolean.class);
- if(enabled == null) {
- enabled = task.getGenerics(VisualizationTask.META_VISIBLE_DEFAULT, Boolean.class);
- }
- if(enabled == null) {
- enabled = true;
- }
- return enabled;
- }
-
- /**
* Utility function to change Visualizer visibility.
*
* @param task Visualization task
@@ -88,10 +77,9 @@ public final class VisualizerUtil {
*/
public static void setVisible(VisualizationTask task, boolean visibility) {
VisualizerContext context = task.getContext();
- if(context != null) {
+ if (context != null) {
setVisible(context, task, visibility);
- }
- else {
+ } else {
LoggingUtil.warning("setVisible called without context in task.", new Throwable());
}
}
@@ -105,78 +93,49 @@ public final class VisualizerUtil {
*/
public static void setVisible(VisualizerContext context, VisualizationTask task, boolean visibility) {
// Hide other tools
- if(visibility && VisualizerUtil.isTool(task)) {
+ if (visibility && task.tool) {
final List<VisualizationTask> visualizers = ResultUtil.filterResults(context.getResult(), VisualizationTask.class);
- for(VisualizationTask other : visualizers) {
- if(other != task && VisualizerUtil.isTool(other) && VisualizerUtil.isVisible(other)) {
- other.put(VisualizationTask.META_VISIBLE, false);
+ for (VisualizationTask other : visualizers) {
+ if (other != task && other.tool && other.visible) {
+ other.visible = false;
context.getHierarchy().resultChanged(other);
}
}
}
- task.put(VisualizationTask.META_VISIBLE, visibility);
+ task.visible = visibility;
context.getHierarchy().resultChanged(task);
}
/**
- * Utility function to test for a visualizer being a "tool".
- *
- * @param vis Visualizer to test
- * @return true for a tool
- */
- public static boolean isTool(VisualizationTask vis) {
- // Currently enabled?
- Boolean tool = vis.getGenerics(VisualizationTask.META_TOOL, Boolean.class);
- return (tool != null) && tool;
- }
-
- /**
- * Utility function to test for a visualizer being "no export".
- *
- * @param vis Visualizer to test
- * @return true when not to export
- */
- public static boolean isNoExport(VisualizationTask vis) {
- // Currently enabled?
- Boolean noexport = vis.getGenerics(VisualizationTask.META_NOEXPORT, Boolean.class);
- return (noexport != null) && noexport;
- }
-
- /**
- * Utility function to test for a visualizer having options.
- *
- * @param vis Visualizer to test
- * @return true when it has options
- */
- public static boolean hasOptions(VisualizationTask vis) {
- // Currently enabled?
- Boolean hasoptions = vis.getGenerics(VisualizationTask.META_HAS_OPTIONS, Boolean.class);
- return (hasoptions != null) && hasoptions;
- }
-
- /**
- * Filter for number vector field representations
+ * Filter for number vector field representations.
*
* @param result Result to filter
* @return Iterator over suitable representations
*/
// TODO: move to DatabaseUtil?
- public static Iterator<Relation<? extends NumberVector<?, ?>>> iterateVectorFieldRepresentations(final Result result) {
+ public static Iterator<Relation<? extends NumberVector<?>>> iterateVectorFieldRepresentations(final Result result) {
List<Relation<?>> parent = ResultUtil.filterResults(result, Relation.class);
return new VectorspaceIterator(parent.iterator());
}
-
+
/**
- * Iterate over vectorspace
+ * Iterate over vectorspace.
*
* @author Erich Schubert
*
* @apiviz.exclude
*/
- private static class VectorspaceIterator extends AbstractFilteredIterator<Relation<?>, Relation<? extends NumberVector<?, ?>>> {
- /** Parent iterator */
+ private static class VectorspaceIterator extends AbstractFilteredIterator<Relation<?>, Relation<? extends NumberVector<?>>> {
+ /**
+ * Parent iterator.
+ */
private Iterator<Relation<?>> parent;
+ /**
+ * Constructor.
+ *
+ * @param parent Parent iterator
+ */
public VectorspaceIterator(Iterator<Relation<?>> parent) {
super();
this.parent = parent;
@@ -189,37 +148,15 @@ public final class VisualizerUtil {
@SuppressWarnings("unchecked")
@Override
- protected Relation<? extends NumberVector<?, ?>> testFilter(Relation<?> nextobj) {
+ protected Relation<? extends NumberVector<?>> testFilter(Relation<?> nextobj) {
final SimpleTypeInformation<?> type = nextobj.getDataTypeInformation();
- if(!NumberVector.class.isAssignableFrom(type.getRestrictionClass())) {
+ if (!NumberVector.class.isAssignableFrom(type.getRestrictionClass())) {
return null;
}
- if(!(type instanceof VectorFieldTypeInformation)) {
+ if (!(type instanceof VectorFieldTypeInformation)) {
return null;
}
- return (Relation<? extends NumberVector<?, ?>>) nextobj;
+ return (Relation<? extends NumberVector<?>>) nextobj;
}
};
-
- /**
- * Test whether a thumbnail is enabled for this visualizer.
- *
- * @param vis Visualizer
- * @return boolean
- */
- public static boolean thumbnailEnabled(VisualizationTask vis) {
- Boolean nothumb = vis.getGenerics(VisualizationTask.META_NOTHUMB, Boolean.class);
- return (nothumb == null) || !nothumb;
- }
-
- /**
- * Test whether a detail plot is available for this task.
- *
- * @param vis Task
- * @return boolean
- */
- public static boolean detailsEnabled(VisualizationTask vis) {
- Boolean nodetail = vis.getGenerics(VisualizationTask.META_NODETAIL, Boolean.class);
- return (nodetail == null) || !nodetail;
- }
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/histogram/ColoredHistogramVisualizer.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/histogram/ColoredHistogramVisualizer.java
index e300a3c4..7a52c8b3 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/histogram/ColoredHistogramVisualizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/histogram/ColoredHistogramVisualizer.java
@@ -28,19 +28,20 @@ import java.util.Collection;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
+import de.lmu.ifi.dbs.elki.data.DoubleVector;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
-import de.lmu.ifi.dbs.elki.math.histograms.AggregatingHistogram;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.math.scales.LinearScale;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.SamplingResult;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
+import de.lmu.ifi.dbs.elki.utilities.datastructures.histogram.DoubleArrayStaticHistogram;
import de.lmu.ifi.dbs.elki.utilities.exceptions.ObjectNotFoundException;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
@@ -48,7 +49,6 @@ import de.lmu.ifi.dbs.elki.utilities.optionhandling.constraints.GreaterEqualCons
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.IntParameter;
-import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleObjPair;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.colors.ColorLibrary;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
@@ -72,279 +72,301 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
* of the database's objects.
*
* @author Remigius Wojdanowski
+ * @author Erich Schubert
*
- * @apiviz.has NumberVector oneway - - visualizes
- *
- * @param <NV> Type of the DatabaseObject being visualized.
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-// FIXME: make non-static, react to database changes!
-// FIXME: cache histogram instead of recomputing it.
-public class ColoredHistogramVisualizer<NV extends NumberVector<NV, ?>> extends AbstractHistogramVisualization {
+public class ColoredHistogramVisualizer extends AbstractVisFactory {
/**
* Name for this visualizer.
*/
private static final String CNAME = "Histograms";
/**
- * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * Settings
*/
- public static final String BIN = "bin";
+ protected Parameterizer settings;
/**
- * Internal storage of the curves flag.
+ * Number of bins to use in histogram.
*/
- private boolean curves;
+ private static final int DEFAULT_BINS = 50;
/**
- * Number of bins to use in the histogram.
+ * Constructor.
+ *
+ * @param settings Settings
*/
- private int bins;
+ public ColoredHistogramVisualizer(Parameterizer settings) {
+ super();
+ this.settings = settings;
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_STYLE;
+ }
- /**
- * The database we visualize
- */
- private Relation<NV> relation;
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<DoubleVector>(task);
+ }
- /**
- * The style policy
- */
- private StyleResult style;
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find a style result to visualize:
+ Collection<StyleResult> styleres = ResultUtil.filterResults(result, StyleResult.class);
+ for (StyleResult c : styleres) {
+ Collection<HistogramProjector<?>> ps = ResultUtil.filterResults(baseResult, HistogramProjector.class);
+ for (HistogramProjector<?> p : ps) {
+ // register self
+ final VisualizationTask task = new VisualizationTask(CNAME, c, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA;
+ baseResult.getHierarchy().add(c, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
- /**
- * Sampling result
- */
- private SamplingResult sample;
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
+ }
/**
- * Constructor.
+ * Instance
+ *
+ * @author Remigius Wojdanowski
*
- * @param task Visualization task
- * @param curves Curves flag
- * @param bins Number of bins
+ * @apiviz.has NumberVector oneway - - visualizes
+ *
+ * @param <NV> Type of the DatabaseObject being visualized.
*/
- public ColoredHistogramVisualizer(VisualizationTask task, boolean curves, int bins) {
- super(task);
- this.curves = curves;
- this.bins = bins;
- this.relation = task.getRelation();
- this.style = task.getResult();
- this.sample = ResultUtil.getSamplingResult(relation);
- context.addResultListener(this);
- }
+ // FIXME: make non-static, react to database changes!
+ // FIXME: cache histogram instead of recomputing it.
+ public class Instance<NV extends NumberVector<?>> extends AbstractHistogramVisualization {
+ /**
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String BIN = "bin";
- @Override
- public void destroy() {
- context.removeResultListener(this);
- super.destroy();
- }
+ /**
+ * The database we visualize
+ */
+ private Relation<NV> relation;
- @Override
- protected void redraw() {
- double margin = context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
- layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
- double xsize = Projection.SCALE * task.getWidth() / task.getHeight();
- double ysize = Projection.SCALE;
-
- final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), xsize, ysize, margin);
- SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
-
- // Styling policy
- final StylingPolicy spol = style.getStylingPolicy();
- final ClassStylingPolicy cspol;
- if(spol instanceof ClassStylingPolicy) {
- cspol = (ClassStylingPolicy) spol;
+ /**
+ * The style policy
+ */
+ private StyleResult style;
+
+ /**
+ * Sampling result
+ */
+ private SamplingResult sample;
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.relation = task.getRelation();
+ this.style = task.getResult();
+ this.sample = ResultUtil.getSamplingResult(relation);
+ context.addResultListener(this);
}
- else {
- cspol = null;
+
+ @Override
+ public void destroy() {
+ context.removeResultListener(this);
+ super.destroy();
}
- // TODO also use min style?
- setupCSS(svgp, (cspol != null) ? cspol.getMaxStyle() : 0);
-
- // Create histograms
- final int off = (cspol != null) ? cspol.getMinStyle() : 0;
- final int numc = (cspol != null) ? (cspol.getMaxStyle() - cspol.getMinStyle()) : 0;
- DoubleMinMax minmax = new DoubleMinMax();
- final double frac = 1. / relation.size(); // TODO: sampling?
- final int cols = numc + 1;
- AggregatingHistogram<double[], double[]> histogram = new AggregatingHistogram<double[], double[]>(bins, -.5, .5, new AggregatingHistogram.Adapter<double[], double[]>() {
- @Override
- public double[] aggregate(double[] existing, double[] data) {
- for(int i = 0; i < existing.length; i++) {
- existing[i] += data[i];
- }
- return existing;
- }
- @Override
- public double[] make() {
- return new double[cols];
+ @Override
+ protected void redraw() {
+ double margin = style.getStyleLibrary().getSize(StyleLibrary.MARGIN);
+ layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
+ double xsize = Projection.SCALE * task.getWidth() / task.getHeight();
+ double ysize = Projection.SCALE;
+
+ final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), xsize, ysize, margin);
+ SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
+
+ // Styling policy
+ final StylingPolicy spol = style.getStylingPolicy();
+ final ClassStylingPolicy cspol;
+ if (spol instanceof ClassStylingPolicy) {
+ cspol = (ClassStylingPolicy) spol;
+ } else {
+ cspol = null;
}
- });
-
- if(cspol != null) {
- for(int snum = 0; snum < numc; snum++) {
+ // TODO also use min style?
+ setupCSS(svgp, (cspol != null) ? cspol.getMaxStyle() : 0);
+
+ // Create histograms
+ final int off = (cspol != null) ? cspol.getMinStyle() : 0;
+ final int numc = (cspol != null) ? (cspol.getMaxStyle() - cspol.getMinStyle()) : 0;
+ DoubleMinMax minmax = new DoubleMinMax();
+ final double frac = 1. / relation.size(); // TODO: sampling?
+ final int cols = numc + 1;
+ DoubleArrayStaticHistogram histogram = new DoubleArrayStaticHistogram(settings.bins, -.5, .5, cols);
+
+ if (cspol != null) {
+ for (int snum = 0; snum < numc; snum++) {
+ double[] inc = new double[cols];
+ inc[0] = frac;
+ inc[snum + 1] = frac;
+ for (DBIDIter iter = cspol.iterateClass(snum + off); iter.valid(); iter.advance()) {
+ if (!sample.getSample().contains(iter)) {
+ continue; // TODO: can we test more efficiently than this?
+ }
+ try {
+ double pos = proj.fastProjectDataToRenderSpace(relation.get(iter)) / Projection.SCALE;
+ histogram.increment(pos, inc);
+ } catch (ObjectNotFoundException e) {
+ // Ignore. The object was probably deleted from the database
+ }
+ }
+ }
+ } else {
+ // Actual data distribution.
double[] inc = new double[cols];
inc[0] = frac;
- inc[snum + 1] = frac;
- for(DBIDIter iter = cspol.iterateClass(snum + off); iter.valid(); iter.advance()) {
- if(!sample.getSample().contains(iter)) {
- continue; // TODO: can we test more efficiently than this?
- }
- try {
- double pos = proj.fastProjectDataToRenderSpace(relation.get(iter)) / Projection.SCALE;
- histogram.aggregate(pos, inc);
- }
- catch(ObjectNotFoundException e) {
- // Ignore. The object was probably deleted from the database
- }
+ for (DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ double pos = proj.fastProjectDataToRenderSpace(relation.get(iditer)) / Projection.SCALE;
+ histogram.increment(pos, inc);
}
}
- }
- else {
- // Actual data distribution.
- double[] inc = new double[cols];
- inc[0] = frac;
- for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- double pos = proj.fastProjectDataToRenderSpace(relation.get(iditer)) / Projection.SCALE;
- histogram.aggregate(pos, inc);
- }
- }
- // for scaling, get the maximum occurring value in the bins:
- for(DoubleObjPair<double[]> bin : histogram) {
- for(double val : bin.second) {
- minmax.put(val);
+ // for scaling, get the maximum occurring value in the bins:
+ for (DoubleArrayStaticHistogram.Iter iter = histogram.iter(); iter.valid(); iter.advance()) {
+ for (double val : iter.getValue()) {
+ minmax.put(val);
+ }
}
- }
- LinearScale yscale = new LinearScale(0, minmax.getMax());
- LinearScale xscale = new LinearScale(histogram.getCoverMinimum(), histogram.getCoverMaximum());
-
- // Axis. TODO: Add an AxisVisualizer for this?
- try {
- SVGSimpleLinearAxis.drawAxis(svgp, layer, yscale, 0, ysize, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, context.getStyleLibrary());
-
- // draw axes that are non-trivial
- final int dimensionality = DatabaseUtil.dimensionality(relation);
- double orig = proj.fastProjectScaledToRender(new Vector(dimensionality));
- for(int d = 0; d < dimensionality; d++) {
- Vector v = new Vector(dimensionality);
- v.set(d, 1);
- // projected endpoint of axis
- double ax = proj.fastProjectScaledToRender(v);
- if(ax != orig) {
- final double left = (orig / Projection.SCALE + 0.5) * xsize;
- final double right = (ax / Projection.SCALE + 0.5) * xsize;
- SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getScale(d), left, ysize, right, ysize, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, context.getStyleLibrary());
+ LinearScale yscale = new LinearScale(0, minmax.getMax());
+ LinearScale xscale = new LinearScale(histogram.getCoverMinimum(), histogram.getCoverMaximum());
+
+ // Axis. TODO: Add an AxisVisualizer for this?
+ try {
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, yscale, 0, ysize, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, style.getStyleLibrary());
+
+ // draw axes that are non-trivial
+ final int dimensionality = RelationUtil.dimensionality(relation);
+ double orig = proj.fastProjectScaledToRender(new Vector(dimensionality));
+ for (int d = 0; d < dimensionality; d++) {
+ Vector v = new Vector(dimensionality);
+ v.set(d, 1);
+ // projected endpoint of axis
+ double ax = proj.fastProjectScaledToRender(v);
+ if (ax < orig || ax > orig) {
+ final double left = (orig / Projection.SCALE + 0.5) * xsize;
+ final double right = (ax / Projection.SCALE + 0.5) * xsize;
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getScale(d), left, ysize, right, ysize, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, style.getStyleLibrary());
+ }
}
+ } catch (CSSNamingConflict e) {
+ LoggingUtil.exception("CSS class exception in axis class.", e);
}
- }
- catch(CSSNamingConflict e) {
- LoggingUtil.exception("CSS class exception in axis class.", e);
- }
- double binwidth = histogram.getBinsize();
- // Visualizing
- if(!curves) {
- for(DoubleObjPair<double[]> bin : histogram) {
- double lpos = xscale.getScaled(bin.first - binwidth / 2);
- double rpos = xscale.getScaled(bin.first + binwidth / 2);
- double stack = 0.0;
- final int start = numc > 0 ? 1 : 0;
- for(int key = start; key < cols; key++) {
- double val = yscale.getScaled(bin.getSecond()[key]);
- Element row = SVGUtil.svgRect(svgp.getDocument(), xsize * lpos, ysize * (1 - (val + stack)), xsize * (rpos - lpos), ysize * val);
- stack = stack + val;
- SVGUtil.addCSSClass(row, BIN + (off + key - 1));
- layer.appendChild(row);
+ // Visualizing
+ if (!settings.curves) {
+ for (DoubleArrayStaticHistogram.Iter iter = histogram.iter(); iter.valid(); iter.advance()) {
+ double lpos = xscale.getScaled(iter.getLeft());
+ double rpos = xscale.getScaled(iter.getRight());
+ double stack = 0.0;
+ final int start = numc > 0 ? 1 : 0;
+ for (int key = start; key < cols; key++) {
+ double val = yscale.getScaled(iter.getValue()[key]);
+ Element row = SVGUtil.svgRect(svgp.getDocument(), xsize * lpos, ysize * (1 - (val + stack)), xsize * (rpos - lpos), ysize * val);
+ stack = stack + val;
+ SVGUtil.addCSSClass(row, BIN + (off + key - 1));
+ layer.appendChild(row);
+ }
+ }
+ } else {
+ double left = xscale.getScaled(histogram.getCoverMinimum());
+ double right = left;
+
+ SVGPath[] paths = new SVGPath[cols];
+ double[] lasty = new double[cols];
+ for (int i = 0; i < cols; i++) {
+ paths[i] = new SVGPath(xsize * left, ysize * 1);
+ lasty[i] = 0;
}
- }
- }
- else {
- double left = xscale.getScaled(histogram.getCoverMinimum());
- double right = left;
-
- SVGPath[] paths = new SVGPath[cols];
- double[] lasty = new double[cols];
- for(int i = 0; i < cols; i++) {
- paths[i] = new SVGPath(xsize * left, ysize * 1);
- lasty[i] = 0;
- }
- // draw histogram lines
- for(DoubleObjPair<double[]> bin : histogram) {
- left = xscale.getScaled(bin.first - binwidth / 2);
- right = xscale.getScaled(bin.first + binwidth / 2);
- for(int i = 0; i < cols; i++) {
- double val = yscale.getScaled(bin.getSecond()[i]);
- if(lasty[i] != val) {
- paths[i].lineTo(xsize * left, ysize * (1 - lasty[i]));
- paths[i].lineTo(xsize * left, ysize * (1 - val));
- paths[i].lineTo(xsize * right, ysize * (1 - val));
- lasty[i] = val;
+ // draw histogram lines
+ for (DoubleArrayStaticHistogram.Iter iter = histogram.iter(); iter.valid(); iter.advance()) {
+ left = xscale.getScaled(iter.getLeft());
+ right = xscale.getScaled(iter.getRight());
+ for (int i = 0; i < cols; i++) {
+ double val = yscale.getScaled(iter.getValue()[i]);
+ if (lasty[i] > val || lasty[i] < val) {
+ paths[i].lineTo(xsize * left, ysize * (1 - lasty[i]));
+ paths[i].lineTo(xsize * left, ysize * (1 - val));
+ paths[i].lineTo(xsize * right, ysize * (1 - val));
+ lasty[i] = val;
+ }
}
}
- }
- // close and insert all lines.
- for(int i = 0; i < cols; i++) {
- if(lasty[i] != 0) {
- paths[i].lineTo(xsize * right, ysize * (1 - lasty[i]));
+ // close and insert all lines.
+ for (int i = 0; i < cols; i++) {
+ if (lasty[i] != 0) {
+ paths[i].lineTo(xsize * right, ysize * (1 - lasty[i]));
+ }
+ paths[i].lineTo(xsize * right, ysize * 1);
+ Element elem = paths[i].makeElement(svgp);
+ SVGUtil.addCSSClass(elem, BIN + (off + i - 1));
+ layer.appendChild(elem);
}
- paths[i].lineTo(xsize * right, ysize * 1);
- Element elem = paths[i].makeElement(svgp);
- SVGUtil.addCSSClass(elem, BIN + (off + i - 1));
- layer.appendChild(elem);
}
}
- }
- /**
- * Generate the needed CSS classes.
- *
- * @param svgp Plot context
- * @param numc Number of classes we need.
- */
- private void setupCSS(SVGPlot svgp, int numc) {
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
+ /**
+ * Generate the needed CSS classes.
+ *
+ * @param svgp Plot context
+ * @param numc Number of classes we need.
+ */
+ private void setupCSS(SVGPlot svgp, int numc) {
+ ColorLibrary colors = style.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
+
+ CSSClass allInOne = new CSSClass(svgp, BIN + -1);
+ if (!settings.curves) {
+ allInOne.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
+ allInOne.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 1.0);
+ } else {
+ allInOne.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
+ allInOne.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
+ allInOne.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ }
+ svgp.addCSSClassOrLogError(allInOne);
- CSSClass allInOne = new CSSClass(svgp, BIN + -1);
- if(!curves) {
- allInOne.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
- allInOne.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 1.0);
- }
- else {
- allInOne.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
- allInOne.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- allInOne.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- }
- svgp.addCSSClassOrLogError(allInOne);
+ for (int clusterID = 0; clusterID < numc; clusterID++) {
+ CSSClass bin = new CSSClass(svgp, BIN + clusterID);
- for(int clusterID = 0; clusterID < numc; clusterID++) {
- CSSClass bin = new CSSClass(svgp, BIN + clusterID);
+ if (!settings.curves) {
+ bin.setStatement(SVGConstants.CSS_FILL_PROPERTY, colors.getColor(clusterID));
+ } else {
+ bin.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(clusterID));
+ bin.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
+ bin.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ }
- if(!curves) {
- bin.setStatement(SVGConstants.CSS_FILL_PROPERTY, colors.getColor(clusterID));
- }
- else {
- bin.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(clusterID));
- bin.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- bin.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(bin);
}
-
- svgp.addCSSClassOrLogError(bin);
}
}
/**
- * Visualizer factory for 1D histograms
+ * Parameterization class.
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses ColoredHistogramVisualizer oneway - - «create»
- *
- * @param <NV> Number vector type
+ * @apiviz.exclude
*/
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
+ public static class Parameterizer extends AbstractParameterizer {
/**
* Flag to specify the "curves" rendering style.
*
@@ -352,7 +374,7 @@ public class ColoredHistogramVisualizer<NV extends NumberVector<NV, ?>> extends
* Key: {@code -histogram.curves}
* </p>
*/
- public static final OptionID STYLE_CURVES_ID = OptionID.getOrCreateOptionID("projhistogram.curves", "Use curves instead of the stacked histogram style.");
+ public static final OptionID STYLE_CURVES_ID = new OptionID("projhistogram.curves", "Use curves instead of the stacked histogram style.");
/**
* Parameter to specify the number of bins to use in histogram.
@@ -361,98 +383,35 @@ public class ColoredHistogramVisualizer<NV extends NumberVector<NV, ?>> extends
* Key: {@code -projhistogram.bins} Default: 20
* </p>
*/
- public static final OptionID HISTOGRAM_BINS_ID = OptionID.getOrCreateOptionID("projhistogram.bins", "Number of bins in the distribution histogram");
+ public static final OptionID HISTOGRAM_BINS_ID = new OptionID("projhistogram.bins", "Number of bins in the distribution histogram");
/**
* Internal storage of the curves flag.
*/
- private boolean curves;
-
- /**
- * Number of bins to use in histogram.
- */
- private static final int DEFAULT_BINS = 50;
+ protected boolean curves = false;
/**
* Number of bins to use in the histogram.
*/
- private int bins = DEFAULT_BINS;
-
- /**
- * Constructor.
- *
- * @param curves
- * @param bins
- */
- public Factory(boolean curves, int bins) {
- super();
- this.curves = curves;
- this.bins = bins;
- thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_STYLE;
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ColoredHistogramVisualizer<NV>(task, curves, bins);
- }
+ protected int bins = DEFAULT_BINS;
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // Find a style result to visualize:
- Collection<StyleResult> styleres = ResultUtil.filterResults(result, StyleResult.class);
- for(StyleResult c : styleres) {
- Collection<HistogramProjector<?>> ps = ResultUtil.filterResults(baseResult, HistogramProjector.class);
- for(HistogramProjector<?> p : ps) {
- // register self
- final VisualizationTask task = new VisualizationTask(CNAME, c, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
- baseResult.getHierarchy().add(c, task);
- baseResult.getHierarchy().add(p, task);
- }
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ Flag curvesF = new Flag(STYLE_CURVES_ID);
+ if (config.grab(curvesF)) {
+ curves = curvesF.isTrue();
+ }
+ IntParameter binsP = new IntParameter(HISTOGRAM_BINS_ID, DEFAULT_BINS);
+ binsP.addConstraint(new GreaterEqualConstraint(2));
+ if (config.grab(binsP)) {
+ bins = binsP.intValue();
}
}
@Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
- }
-
- /**
- * Parameterization class.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- public static class Parameterizer<NV extends NumberVector<NV, ?>> extends AbstractParameterizer {
- /**
- * Internal storage of the curves flag.
- */
- private boolean curves;
-
- /**
- * Number of bins to use in the histogram.
- */
- private int bins = DEFAULT_BINS;
-
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- Flag STYLE_CURVES_FLAG = new Flag(STYLE_CURVES_ID);
- if(config.grab(STYLE_CURVES_FLAG)) {
- curves = STYLE_CURVES_FLAG.getValue();
- }
- IntParameter HISTOGRAM_BINS_PARAM = new IntParameter(HISTOGRAM_BINS_ID, new GreaterEqualConstraint(2), DEFAULT_BINS);
- if(config.grab(HISTOGRAM_BINS_PARAM)) {
- bins = HISTOGRAM_BINS_PARAM.getValue();
- }
- }
-
- @Override
- protected Factory<NV> makeInstance() {
- return new Factory<NV>(curves, bins);
- }
+ protected ColoredHistogramVisualizer makeInstance() {
+ return new ColoredHistogramVisualizer(this);
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/AbstractOPTICSVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/AbstractOPTICSVisualization.java
index a04a4f7f..329fc64a 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/AbstractOPTICSVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/AbstractOPTICSVisualization.java
@@ -30,7 +30,7 @@ import org.apache.batik.util.SVGConstants;
import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance;
import de.lmu.ifi.dbs.elki.result.optics.ClusterOrderEntry;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
-import de.lmu.ifi.dbs.elki.visualization.projector.OPTICSProjector;
+import de.lmu.ifi.dbs.elki.visualization.projections.OPTICSProjection;
import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisualization;
@@ -40,7 +40,7 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisualization;
*
* @author Erich Schubert
*
- * @apiviz.uses OPTICSProjector
+ * @apiviz.uses OPTICSProjection
*
* @param <D>
*/
@@ -48,7 +48,7 @@ public abstract class AbstractOPTICSVisualization<D extends Distance<D>> extends
/**
* The plot
*/
- final protected OPTICSProjector<D> optics;
+ final protected OPTICSProjection<D> optics;
/**
* Width of plot (in display units)
@@ -67,7 +67,7 @@ public abstract class AbstractOPTICSVisualization<D extends Distance<D>> extends
*/
public AbstractOPTICSVisualization(VisualizationTask task) {
super(task);
- this.optics = task.getResult();
+ this.optics = task.getProj();
}
/**
@@ -76,9 +76,9 @@ public abstract class AbstractOPTICSVisualization<D extends Distance<D>> extends
protected void makeLayerElement() {
plotwidth = StyleLibrary.SCALE;
plotheight = StyleLibrary.SCALE / optics.getOPTICSPlot(context).getRatio();
- final double margin = context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
+ final double margin = context.getStyleResult().getStyleLibrary().getSize(StyleLibrary.MARGIN);
layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
- final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), plotwidth, plotheight, margin / 2);
+ final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), plotwidth, plotheight, margin * .5);
SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSClusterVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSClusterVisualization.java
index 2369588b..e771e380 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSClusterVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSClusterVisualization.java
@@ -55,15 +55,14 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
*
* @author Erich Schubert
*
- * @apiviz.uses Clustering oneway - - «visualizes»
- *
- * @param <D> Distance type (actually unused)
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class OPTICSClusterVisualization<D extends Distance<D>> extends AbstractOPTICSVisualization<D> {
+public class OPTICSClusterVisualization extends AbstractVisFactory {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(OPTICSClusterVisualization.class);
+ private static final Logging LOG = Logging.getLogger(OPTICSClusterVisualization.class);
/**
* A short name characterizing this Visualizer.
@@ -71,30 +70,37 @@ public class OPTICSClusterVisualization<D extends Distance<D>> extends AbstractO
private static final String NAME = "OPTICS Cluster Ranges";
/**
- * CSS class for markers
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- protected static final String CSS_BRACKET = "opticsBracket";
+ public OPTICSClusterVisualization() {
+ super();
+ }
- /**
- * Optics clustering we visualize
- */
- public static final String CLUSTERING = "OPTICSClustering";
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
+ for(OPTICSProjector<?> p : ops) {
+ final Clustering<OPTICSModel> ocl = findOPTICSClustering(baseResult);
+ if(ocl != null) {
+ final VisualizationTask task = new VisualizationTask(NAME, ocl, null, this);
+ task.level = VisualizationTask.LEVEL_DATA;
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ // TODO: also run when a new clustering is added, instead of just new
+ // projections?
+ }
- /**
- * Our clustering
- */
- Clustering<OPTICSModel> clus;
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<DoubleDistance>(task);
+ }
- /**
- * Constructor.
- *
- * @param task Visualization task
- */
- public OPTICSClusterVisualization(VisualizationTask task) {
- super(task);
- this.clus = task.getGenerics(CLUSTERING, Clustering.class);
- context.addResultListener(this);
- incrementalRedraw();
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
}
/**
@@ -115,118 +121,109 @@ public class OPTICSClusterVisualization<D extends Distance<D>> extends AbstractO
if(firstcluster.getModel() instanceof OPTICSModel) {
return (Clustering<OPTICSModel>) clus;
}
- } catch(Exception e) {
+ }
+ catch(Exception e) {
// Empty clustering? Shouldn't happen.
- logger.warning("Clustering with no cluster detected.", e);
+ LOG.warning("Clustering with no cluster detected.", e);
}
}
return null;
}
- @Override
- protected void redraw() {
- makeLayerElement();
- addCSSClasses();
-
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
- HashMap<Cluster<?>, String> colormap = new HashMap<Cluster<?>, String>();
- int cnum = 0;
- for (Cluster<?> c : clus.getAllClusters()) {
- colormap.put(c, colors.getColor(cnum));
- cnum++;
- }
- drawClusters(clus.getToplevelClusters(), 1, colormap);
- }
-
/**
- * Recursively draw clusters
- *
- * @param clusters Current set of clusters
- * @param depth Recursion depth
- * @param colormap Color mapping
- */
- private void drawClusters(List<Cluster<OPTICSModel>> clusters, int depth, Map<Cluster<?>,String> colormap) {
- final double scale = StyleLibrary.SCALE;
-
- for(Cluster<OPTICSModel> cluster : clusters) {
- try {
- OPTICSModel model = cluster.getModel();
- final double x1 = plotwidth * ((model.getStartIndex() + .25) / this.optics.getResult().getClusterOrder().size());
- final double x2 = plotwidth * ((model.getEndIndex() + .75) / this.optics.getResult().getClusterOrder().size());
- final double y = plotheight + depth * scale * 0.01;
- Element e = svgp.svgLine(x1, y, x2, y);
- SVGUtil.addCSSClass(e, CSS_BRACKET);
- String color = colormap.get(cluster);
- if (color != null) {
- SVGUtil.setAtt(e, SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_STROKE_PROPERTY+":"+color);
- }
- layer.appendChild(e);
- }
- catch(ClassCastException e) {
- logger.warning("Expected OPTICSModel, got: " + cluster.getModel().getClass().getSimpleName());
- }
- // Descend
- final List<Cluster<OPTICSModel>> children = cluster.getChildren();
- if(children != null) {
- drawClusters(children, depth + 1, colormap);
- }
- }
- }
-
- /**
- * Adds the required CSS-Classes
- */
- private void addCSSClasses() {
- // Class for the markers
- if(!svgp.getCSSClassManager().contains(CSS_BRACKET)) {
- final CSSClass cls = new CSSClass(this, CSS_BRACKET);
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, context.getStyleLibrary().getColor(StyleLibrary.PLOT));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- svgp.addCSSClassOrLogError(cls);
- }
- }
-
- /**
- * Factory class for OPTICS plot selections.
+ * Instance.
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses OPTICSPlotSelectionVisualization oneway - - «create»
+ * @apiviz.uses Clustering oneway - - «visualizes»
+ *
+ * @param <D> Distance type (actually unused)
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance<D extends Distance<D>> extends AbstractOPTICSVisualization<D> {
+ /**
+ * CSS class for markers
+ */
+ protected static final String CSS_BRACKET = "opticsBracket";
+
+ /**
+ * Our clustering
+ */
+ Clustering<OPTICSModel> clus;
+
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Constructor.
+ *
+ * @param task Visualization task
*/
- public Factory() {
- super();
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.clus = task.getResult();
+ context.addResultListener(this);
+ incrementalRedraw();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
- for(OPTICSProjector<?> p : ops) {
- final Clustering<OPTICSModel> ocl = findOPTICSClustering(baseResult);
- if(ocl != null) {
- final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
- task.put(CLUSTERING, ocl);
- baseResult.getHierarchy().add(p, task);
- }
+ protected void redraw() {
+ makeLayerElement();
+ addCSSClasses();
+
+ ColorLibrary colors = context.getStyleResult().getStyleLibrary().getColorSet(StyleLibrary.PLOT);
+ HashMap<Cluster<?>, String> colormap = new HashMap<Cluster<?>, String>();
+ int cnum = 0;
+ for(Cluster<?> c : clus.getAllClusters()) {
+ colormap.put(c, colors.getColor(cnum));
+ cnum++;
}
- // TODO: also run when a new clustering is added, instead of just new projections?
+ drawClusters(clus.getToplevelClusters(), 1, colormap);
}
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new OPTICSClusterVisualization<DoubleDistance>(task);
+ /**
+ * Recursively draw clusters
+ *
+ * @param clusters Current set of clusters
+ * @param depth Recursion depth
+ * @param colormap Color mapping
+ */
+ private void drawClusters(List<Cluster<OPTICSModel>> clusters, int depth, Map<Cluster<?>, String> colormap) {
+ final double scale = StyleLibrary.SCALE;
+
+ for(Cluster<OPTICSModel> cluster : clusters) {
+ try {
+ OPTICSModel model = cluster.getModel();
+ final double x1 = plotwidth * ((model.getStartIndex() + .25) / this.optics.getResult().getClusterOrder().size());
+ final double x2 = plotwidth * ((model.getEndIndex() + .75) / this.optics.getResult().getClusterOrder().size());
+ final double y = plotheight + depth * scale * 0.01;
+ Element e = svgp.svgLine(x1, y, x2, y);
+ SVGUtil.addCSSClass(e, CSS_BRACKET);
+ String color = colormap.get(cluster);
+ if(color != null) {
+ SVGUtil.setAtt(e, SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_STROKE_PROPERTY + ":" + color);
+ }
+ layer.appendChild(e);
+ }
+ catch(ClassCastException e) {
+ LOG.warning("Expected OPTICSModel, got: " + cluster.getModel().getClass().getSimpleName());
+ }
+ // Descend
+ final List<Cluster<OPTICSModel>> children = cluster.getChildren();
+ if(children != null) {
+ drawClusters(children, depth + 1, colormap);
+ }
+ }
}
- @Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
+ /**
+ * Adds the required CSS-Classes
+ */
+ private void addCSSClasses() {
+ // Class for the markers
+ if(!svgp.getCSSClassManager().contains(CSS_BRACKET)) {
+ final CSSClass cls = new CSSClass(this, CSS_BRACKET);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, style.getColor(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
+ svgp.addCSSClassOrLogError(cls);
+ }
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotCutVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotCutVisualization.java
index 2bd5c63a..739e0ccd 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotCutVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotCutVisualization.java
@@ -52,247 +52,250 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
/**
* Visualizes a cut in an OPTICS Plot to select an Epsilon value and generate a
- * new clustering result
+ * new clustering result.
*
* @author Heidi Kolb
+ * @author Erich Schubert
*
- * @param <D> distance type
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class OPTICSPlotCutVisualization<D extends Distance<D>> extends AbstractOPTICSVisualization<D> implements DragableArea.DragListener {
+public class OPTICSPlotCutVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "OPTICS Cut";
- /**
- * CSS-Styles
- */
- protected static final String CSS_LINE = "opticsPlotLine";
-
- /**
- * CSS-Styles
- */
- protected final static String CSS_EPSILON = "opticsPlotEpsilonValue";
-
- /**
- * The current epsilon value.
- */
- private double epsilon = 0.0;
-
- /**
- * Sensitive (clickable) area
- */
- private DragableArea eventarea = null;
-
- /**
- * The label element
- */
- private Element elemText = null;
-
- /**
- * The line element
- */
- private Element elementLine = null;
-
- /**
- * The drag handle element
- */
- private Element elementPoint = null;
-
- /**
- * Constructor.
- *
- * @param task Task
- */
- public OPTICSPlotCutVisualization(VisualizationTask task) {
- super(task);
+ public OPTICSPlotCutVisualization() {
+ super();
}
@Override
- protected void redraw() {
- incrementalRedraw();
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
+ for(OPTICSProjector<?> p : ops) {
+ final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ baseResult.getHierarchy().add(p, task);
+ }
}
@Override
- protected void incrementalRedraw() {
- if(layer == null) {
- makeLayerElement();
- addCSSClasses();
- }
-
- // TODO make the number of digits configurable
- final String label = (epsilon != 0.0) ? FormatUtil.format(epsilon, 4) : "";
- // compute absolute y-value of bar
- final double yAct = plotheight - getYFromEpsilon(epsilon);
-
- if(elemText == null) {
- elemText = svgp.svgText(StyleLibrary.SCALE * 1.05, yAct, label);
- SVGUtil.setAtt(elemText, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_EPSILON);
- layer.appendChild(elemText);
- }
- else {
- elemText.setTextContent(label);
- SVGUtil.setAtt(elemText, SVGConstants.SVG_Y_ATTRIBUTE, yAct);
- }
-
- // line and handle
- if(elementLine == null) {
- elementLine = svgp.svgLine(0, yAct, StyleLibrary.SCALE * 1.04, yAct);
- SVGUtil.addCSSClass(elementLine, CSS_LINE);
- layer.appendChild(elementLine);
- }
- else {
- SVGUtil.setAtt(elementLine, SVG12Constants.SVG_Y1_ATTRIBUTE, yAct);
- SVGUtil.setAtt(elementLine, SVG12Constants.SVG_Y2_ATTRIBUTE, yAct);
- }
- if(elementPoint == null) {
- elementPoint = svgp.svgCircle(StyleLibrary.SCALE * 1.04, yAct, StyleLibrary.SCALE * 0.004);
- SVGUtil.addCSSClass(elementPoint, CSS_LINE);
- layer.appendChild(elementPoint);
- }
- else {
- SVGUtil.setAtt(elementPoint, SVG12Constants.SVG_CY_ATTRIBUTE, yAct);
- }
-
- if(eventarea == null) {
- eventarea = new DragableArea(svgp, StyleLibrary.SCALE, 0, StyleLibrary.SCALE * 0.1, plotheight, this);
- layer.appendChild(eventarea.getElement());
- }
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<DoubleDistance>(task);
}
@Override
- public void destroy() {
- super.destroy();
- eventarea.destroy();
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
}
/**
- * Get epsilon from y-value
+ * Instance.
*
- * @param y y-Value
- * @return epsilon
- */
- protected double getEpsilonFromY(double y) {
- if(y < 0) {
- y = 0;
- }
- if(y > plotheight) {
- y = plotheight;
- }
- return optics.getOPTICSPlot(context).getScale().getUnscaled(y / plotheight);
- }
-
- /**
- * Get y-value from epsilon
+ * @author Heidi Kolb
+ * @author Erich Schubert
*
- * @param epsilon epsilon
- * @return y-Value
+ * @param <D> distance type
*/
- protected double getYFromEpsilon(double epsilon) {
- double y = optics.getOPTICSPlot(context).getScale().getScaled(epsilon) * plotheight;
- if(y < 0) {
- y = 0;
+ public class Instance<D extends Distance<D>> extends AbstractOPTICSVisualization<D> implements DragableArea.DragListener {
+ /**
+ * CSS-Styles
+ */
+ protected static final String CSS_LINE = "opticsPlotLine";
+
+ /**
+ * CSS-Styles
+ */
+ protected static final String CSS_EPSILON = "opticsPlotEpsilonValue";
+
+ /**
+ * The current epsilon value.
+ */
+ private double epsilon = 0.0;
+
+ /**
+ * Sensitive (clickable) area
+ */
+ private DragableArea eventarea = null;
+
+ /**
+ * The label element
+ */
+ private Element elemText = null;
+
+ /**
+ * The line element
+ */
+ private Element elementLine = null;
+
+ /**
+ * The drag handle element
+ */
+ private Element elementPoint = null;
+
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
}
- if(y > plotheight) {
- y = plotheight;
+
+ @Override
+ protected void redraw() {
+ incrementalRedraw();
}
- return y;
- }
- @Override
- public boolean startDrag(SVGPoint start, Event evt) {
- epsilon = getEpsilonFromY(plotheight - start.getY());
- // opvis.unsetEpsilonExcept(this);
- synchronizedRedraw();
- return true;
- }
+ @Override
+ protected void incrementalRedraw() {
+ if(layer == null) {
+ makeLayerElement();
+ addCSSClasses();
+ }
- @Override
- public boolean duringDrag(SVGPoint start, SVGPoint end, Event evt, boolean inside) {
- if(inside) {
- epsilon = getEpsilonFromY(plotheight - end.getY());
- }
- // opvis.unsetEpsilonExcept(this);
- synchronizedRedraw();
- return true;
- }
+ // TODO make the number of digits configurable
+ final String label = (epsilon > 0.0) ? FormatUtil.format(epsilon, 4) : "";
+ // compute absolute y-value of bar
+ final double yAct = plotheight - getYFromEpsilon(epsilon);
- @Override
- public boolean endDrag(SVGPoint start, SVGPoint end, Event evt, boolean inside) {
- if(inside) {
- epsilon = getEpsilonFromY(plotheight - end.getY());
- // opvis.unsetEpsilonExcept(this);
+ if(elemText == null) {
+ elemText = svgp.svgText(StyleLibrary.SCALE * 1.05, yAct, label);
+ SVGUtil.setAtt(elemText, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_EPSILON);
+ layer.appendChild(elemText);
+ }
+ else {
+ elemText.setTextContent(label);
+ SVGUtil.setAtt(elemText, SVGConstants.SVG_Y_ATTRIBUTE, yAct);
+ }
- // FIXME: replace an existing optics cut result!
- final ClusterOrderResult<D> order = optics.getResult();
- Clustering<Model> cl = OPTICSCut.makeOPTICSCut(order, optics.getOPTICSPlot(context).getDistanceAdapter(), epsilon);
- order.addChildResult(cl);
+ // line and handle
+ if(elementLine == null) {
+ elementLine = svgp.svgLine(0, yAct, StyleLibrary.SCALE * 1.04, yAct);
+ SVGUtil.addCSSClass(elementLine, CSS_LINE);
+ layer.appendChild(elementLine);
+ }
+ else {
+ SVGUtil.setAtt(elementLine, SVG12Constants.SVG_Y1_ATTRIBUTE, yAct);
+ SVGUtil.setAtt(elementLine, SVG12Constants.SVG_Y2_ATTRIBUTE, yAct);
+ }
+ if(elementPoint == null) {
+ elementPoint = svgp.svgCircle(StyleLibrary.SCALE * 1.04, yAct, StyleLibrary.SCALE * 0.004);
+ SVGUtil.addCSSClass(elementPoint, CSS_LINE);
+ layer.appendChild(elementPoint);
+ }
+ else {
+ SVGUtil.setAtt(elementPoint, SVG12Constants.SVG_CY_ATTRIBUTE, yAct);
+ }
+
+ if(eventarea == null) {
+ eventarea = new DragableArea(svgp, StyleLibrary.SCALE, 0, StyleLibrary.SCALE * 0.1, plotheight, this);
+ layer.appendChild(eventarea.getElement());
+ }
}
- context.getHierarchy().resultChanged(this.task);
- // synchronizedRedraw();
- return true;
- }
- /**
- * Reset the epsilon value.
- */
- public void unsetEpsilon() {
- epsilon = 0.0;
- }
+ @Override
+ public void destroy() {
+ super.destroy();
+ eventarea.destroy();
+ }
- /**
- * Adds the required CSS-Classes
- */
- private void addCSSClasses() {
- // Class for the epsilon-value
- if(!svgp.getCSSClassManager().contains(CSS_EPSILON)) {
- final CSSClass label = new CSSClass(svgp, CSS_EPSILON);
- label.setStatement(SVGConstants.CSS_FILL_PROPERTY, context.getStyleLibrary().getTextColor(StyleLibrary.AXIS_LABEL));
- label.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, context.getStyleLibrary().getFontFamily(StyleLibrary.AXIS_LABEL));
- label.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, context.getStyleLibrary().getTextSize(StyleLibrary.AXIS_LABEL));
- svgp.addCSSClassOrLogError(label);
+ /**
+ * Get epsilon from y-value
+ *
+ * @param y y-Value
+ * @return epsilon
+ */
+ protected double getEpsilonFromY(double y) {
+ if(y < 0) {
+ y = 0;
+ }
+ if(y > plotheight) {
+ y = plotheight;
+ }
+ return optics.getOPTICSPlot(context).getScale().getUnscaled(y / plotheight);
}
- // Class for the epsilon cut line
- if(!svgp.getCSSClassManager().contains(CSS_LINE)) {
- final CSSClass lcls = new CSSClass(svgp, CSS_LINE);
- lcls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, context.getStyleLibrary().getColor(StyleLibrary.PLOT));
- lcls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, 0.5 * context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- svgp.addCSSClassOrLogError(lcls);
+
+ /**
+ * Get y-value from epsilon
+ *
+ * @param epsilon epsilon
+ * @return y-Value
+ */
+ protected double getYFromEpsilon(double epsilon) {
+ double y = optics.getOPTICSPlot(context).getScale().getScaled(epsilon) * plotheight;
+ if(y < 0) {
+ y = 0;
+ }
+ if(y > plotheight) {
+ y = plotheight;
+ }
+ return y;
}
- }
- /**
- * Factory class
- *
- * @author Erich Schubert
- *
- * @apiviz.stereotype factory
- * @apiviz.uses OPTICSPlotCutVisualization oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
- public Factory() {
- super();
+ @Override
+ public boolean startDrag(SVGPoint start, Event evt) {
+ epsilon = getEpsilonFromY(plotheight - start.getY());
+ // opvis.unsetEpsilonExcept(this);
+ synchronizedRedraw();
+ return true;
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
- for(OPTICSProjector<?> p : ops) {
- final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
- baseResult.getHierarchy().add(p, task);
+ public boolean duringDrag(SVGPoint start, SVGPoint end, Event evt, boolean inside) {
+ if(inside) {
+ epsilon = getEpsilonFromY(plotheight - end.getY());
}
+ // opvis.unsetEpsilonExcept(this);
+ synchronizedRedraw();
+ return true;
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new OPTICSPlotCutVisualization<DoubleDistance>(task);
+ public boolean endDrag(SVGPoint start, SVGPoint end, Event evt, boolean inside) {
+ if(inside) {
+ epsilon = getEpsilonFromY(plotheight - end.getY());
+ // opvis.unsetEpsilonExcept(this);
+
+ // FIXME: replace an existing optics cut result!
+ final ClusterOrderResult<D> order = optics.getResult();
+ Clustering<Model> cl = OPTICSCut.makeOPTICSCut(order, optics.getOPTICSPlot(context).getDistanceAdapter(), epsilon);
+ order.addChildResult(cl);
+ }
+ context.getHierarchy().resultChanged(this.task);
+ // synchronizedRedraw();
+ return true;
}
- @Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
+ /**
+ * Reset the epsilon value.
+ */
+ public void unsetEpsilon() {
+ epsilon = 0.0;
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ */
+ private void addCSSClasses() {
+ // Class for the epsilon-value
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ if(!svgp.getCSSClassManager().contains(CSS_EPSILON)) {
+ final CSSClass label = new CSSClass(svgp, CSS_EPSILON);
+ label.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getTextColor(StyleLibrary.AXIS_LABEL));
+ label.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, style.getFontFamily(StyleLibrary.AXIS_LABEL));
+ label.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, style.getTextSize(StyleLibrary.AXIS_LABEL));
+ svgp.addCSSClassOrLogError(label);
+ }
+ // Class for the epsilon cut line
+ if(!svgp.getCSSClassManager().contains(CSS_LINE)) {
+ final CSSClass lcls = new CSSClass(svgp, CSS_LINE);
+ lcls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, style.getColor(StyleLibrary.PLOT));
+ lcls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, 0.5 * style.getLineWidth(StyleLibrary.PLOT));
+ svgp.addCSSClassOrLogError(lcls);
+ }
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotSelectionVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotSelectionVisualization.java
index 87b24df2..bd2d0946 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotSelectionVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotSelectionVisualization.java
@@ -56,17 +56,16 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
/**
* Handle the marker in an OPTICS plot.
*
- * @author Heidi Kolb
+ * @author Erich Schubert
*
- * @apiviz.uses DBIDSelection oneway - 1 visualizes
- *
- * @param <D> distance type
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class OPTICSPlotSelectionVisualization<D extends Distance<D>> extends AbstractOPTICSVisualization<D> implements DragableArea.DragListener {
+public class OPTICSPlotSelectionVisualization extends AbstractVisFactory {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(OPTICSPlotSelectionVisualization.class);
+ private static final Logging LOG = Logging.getLogger(OPTICSPlotSelectionVisualization.class);
/**
* A short name characterizing this Visualizer.
@@ -74,16 +73,6 @@ public class OPTICSPlotSelectionVisualization<D extends Distance<D>> extends Abs
private static final String NAME = "OPTICS Selection";
/**
- * CSS class for markers
- */
- protected static final String CSS_MARKER = "opticsPlotMarker";
-
- /**
- * CSS class for markers
- */
- protected static final String CSS_RANGEMARKER = "opticsPlotRangeMarker";
-
- /**
* Input modes
*
* @apiviz.exclude
@@ -94,278 +83,290 @@ public class OPTICSPlotSelectionVisualization<D extends Distance<D>> extends Abs
}
/**
- * Element for the events
- */
- private Element etag;
-
- /**
- * Element for the marker
- */
- private Element mtag;
-
- /**
- * Constructor.
- *
- * @param task Visualization task
- */
- public OPTICSPlotSelectionVisualization(VisualizationTask task) {
- super(task);
- context.addResultListener(this);
- incrementalRedraw();
- }
-
- @Override
- protected void redraw() {
- makeLayerElement();
- addCSSClasses();
-
- mtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
- addMarker();
-
- DragableArea drag = new DragableArea(svgp, 0 - plotwidth * 0.1, 0, plotwidth * 1.1, plotheight, this);
- etag = drag.getElement();
- // mtag first, etag must be the top Element
- layer.appendChild(mtag);
- layer.appendChild(etag);
- }
-
- /**
- * Add marker for the selected IDs to mtag
- */
- public void addMarker() {
- List<ClusterOrderEntry<D>> order = getClusterOrder();
- // TODO: replace mtag!
- DBIDSelection selContext = context.getSelection();
- if(selContext != null) {
- DBIDs selection = DBIDUtil.ensureSet(selContext.getSelectedIds());
-
- final double width = plotwidth / order.size();
- int begin = -1;
- for(int j = 0; j < order.size(); j++) {
- DBID id = order.get(j).getID();
- if(selection.contains(id)) {
- if(begin == -1) {
- begin = j;
- }
- }
- else {
- if(begin != -1) {
- Element marker = addMarkerRect(begin * width, (j - begin) * width);
- SVGUtil.addCSSClass(marker, CSS_MARKER);
- mtag.appendChild(marker);
- begin = -1;
- }
- }
- }
- // tail
- if(begin != -1) {
- Element marker = addMarkerRect(begin * width, (order.size() - begin) * width);
- SVGUtil.addCSSClass(marker, CSS_MARKER);
- mtag.appendChild(marker);
- }
- }
- }
-
- /**
- * Create a rectangle as marker (Marker higher than plot!)
- *
- * @param x1 X-Value for the marker
- * @param width Width of an entry
- * @return SVG-Element svg-rectangle
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- public Element addMarkerRect(double x1, double width) {
- return svgp.svgRect(x1, 0, width, plotheight);
+ public OPTICSPlotSelectionVisualization() {
+ super();
}
@Override
- public boolean startDrag(SVGPoint startPoint, Event evt) {
- List<ClusterOrderEntry<D>> order = getClusterOrder();
- int mouseActIndex = getSelectedIndex(order, startPoint);
- if(mouseActIndex >= 0 && mouseActIndex < order.size()) {
- double width = plotwidth / order.size();
- double x1 = mouseActIndex * width;
- Element marker = addMarkerRect(x1, width);
- SVGUtil.setCSSClass(marker, CSS_RANGEMARKER);
- mtag.appendChild(marker);
- return true;
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
+ for(OPTICSProjector<?> p : ops) {
+ final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ baseResult.getHierarchy().add(p, task);
}
- return false;
}
@Override
- public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- List<ClusterOrderEntry<D>> order = getClusterOrder();
- int mouseDownIndex = getSelectedIndex(order, startPoint);
- int mouseActIndex = getSelectedIndex(order, dragPoint);
- final int begin = Math.max(Math.min(mouseDownIndex, mouseActIndex), 0);
- final int end = Math.min(Math.max(mouseDownIndex, mouseActIndex), order.size());
- double width = plotwidth / order.size();
- double x1 = begin * width;
- double x2 = (end * width) + width;
- mtag.removeChild(mtag.getLastChild());
- Element marker = addMarkerRect(x1, x2 - x1);
- SVGUtil.setCSSClass(marker, CSS_RANGEMARKER);
- mtag.appendChild(marker);
- return true;
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<DoubleDistance>(task);
}
@Override
- public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- List<ClusterOrderEntry<D>> order = getClusterOrder();
- int mouseDownIndex = getSelectedIndex(order, startPoint);
- int mouseActIndex = getSelectedIndex(order, dragPoint);
- Mode mode = getInputMode(evt);
- final int begin = Math.max(Math.min(mouseDownIndex, mouseActIndex), 0);
- final int end = Math.min(Math.max(mouseDownIndex, mouseActIndex), order.size());
- updateSelection(mode, begin, end);
- return true;
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
}
/**
- * Get the current input mode, on each mouse event.
+ * Instance.
*
- * @param evt Mouse event.
- * @return Input mode
- */
- private Mode getInputMode(Event evt) {
- if(evt instanceof DOMMouseEvent) {
- DOMMouseEvent domme = (DOMMouseEvent) evt;
- // TODO: visual indication of mode possible?
- if(domme.getShiftKey()) {
- return Mode.ADD;
- }
- else if(domme.getCtrlKey()) {
- return Mode.INVERT;
- }
- else {
- return Mode.REPLACE;
- }
- }
- // Default mode is replace.
- return Mode.REPLACE;
- }
-
- /**
- * Gets the Index of the ClusterOrderEntry where the event occurred
+ * @author Heidi Kolb
+ * @author Erich Schubert
*
- * @param order List of ClusterOrderEntries
- * @param cPt clicked point
- * @return Index of the object
- */
- private int getSelectedIndex(List<ClusterOrderEntry<D>> order, SVGPoint cPt) {
- int mouseActIndex = (int) ((cPt.getX() / plotwidth) * order.size());
- return mouseActIndex;
- }
-
- /**
- * Updates the selection for the given ClusterOrderEntry.
+ * @apiviz.uses DBIDSelection oneway - 1 visualizes
*
- * @param mode Input mode
- * @param begin first index to select
- * @param end last index to select
+ * @param <D> distance type
*/
- protected void updateSelection(Mode mode, int begin, int end) {
- List<ClusterOrderEntry<D>> order = getClusterOrder();
- if(begin < 0 || begin > end || end >= order.size()) {
- logger.warning("Invalid range in updateSelection: " + begin + " .. " + end);
- return;
- }
+ public class Instance<D extends Distance<D>> extends AbstractOPTICSVisualization<D> implements DragableArea.DragListener {
+ /**
+ * CSS class for markers
+ */
+ protected static final String CSS_MARKER = "opticsPlotMarker";
- DBIDSelection selContext = context.getSelection();
- HashSetModifiableDBIDs selection;
- if(selContext == null || mode == Mode.REPLACE) {
- selection = DBIDUtil.newHashSet();
+ /**
+ * CSS class for markers
+ */
+ protected static final String CSS_RANGEMARKER = "opticsPlotRangeMarker";
+
+ /**
+ * Element for the events
+ */
+ private Element etag;
+
+ /**
+ * Element for the marker
+ */
+ private Element mtag;
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ context.addResultListener(this);
+ incrementalRedraw();
}
- else {
- selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
+
+ @Override
+ protected void redraw() {
+ makeLayerElement();
+ addCSSClasses();
+
+ mtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ addMarker();
+
+ DragableArea drag = new DragableArea(svgp, 0 - plotwidth * 0.1, 0, plotwidth * 1.1, plotheight, this);
+ etag = drag.getElement();
+ // mtag first, etag must be the top Element
+ layer.appendChild(mtag);
+ layer.appendChild(etag);
}
- for(int i = begin; i <= end; i++) {
- DBID id = order.get(i).getID();
- if(mode == Mode.INVERT) {
- if(!selection.contains(id)) {
- selection.add(id);
+ /**
+ * Add marker for the selected IDs to mtag
+ */
+ public void addMarker() {
+ List<ClusterOrderEntry<D>> order = getClusterOrder();
+ // TODO: replace mtag!
+ DBIDSelection selContext = context.getSelection();
+ if(selContext != null) {
+ DBIDs selection = DBIDUtil.ensureSet(selContext.getSelectedIds());
+
+ final double width = plotwidth / order.size();
+ int begin = -1;
+ for(int j = 0; j < order.size(); j++) {
+ DBID id = order.get(j).getID();
+ if(selection.contains(id)) {
+ if(begin == -1) {
+ begin = j;
+ }
+ }
+ else {
+ if(begin != -1) {
+ Element marker = addMarkerRect(begin * width, (j - begin) * width);
+ SVGUtil.addCSSClass(marker, CSS_MARKER);
+ mtag.appendChild(marker);
+ begin = -1;
+ }
+ }
}
- else {
- selection.remove(id);
+ // tail
+ if(begin != -1) {
+ Element marker = addMarkerRect(begin * width, (order.size() - begin) * width);
+ SVGUtil.addCSSClass(marker, CSS_MARKER);
+ mtag.appendChild(marker);
}
}
- else {
- // In REPLACE and ADD, add objects.
- // The difference was done before by not re-using the selection.
- // Since we are using a set, we can just add in any case.
- selection.add(id);
+ }
+
+ /**
+ * Create a rectangle as marker (Marker higher than plot!)
+ *
+ * @param x1 X-Value for the marker
+ * @param width Width of an entry
+ * @return SVG-Element svg-rectangle
+ */
+ public Element addMarkerRect(double x1, double width) {
+ return svgp.svgRect(x1, 0, width, plotheight);
+ }
+
+ @Override
+ public boolean startDrag(SVGPoint startPoint, Event evt) {
+ List<ClusterOrderEntry<D>> order = getClusterOrder();
+ int mouseActIndex = getSelectedIndex(order, startPoint);
+ if(mouseActIndex >= 0 && mouseActIndex < order.size()) {
+ double width = plotwidth / order.size();
+ double x1 = mouseActIndex * width;
+ Element marker = addMarkerRect(x1, width);
+ SVGUtil.setCSSClass(marker, CSS_RANGEMARKER);
+ mtag.appendChild(marker);
+ return true;
}
+ return false;
}
- context.setSelection(new DBIDSelection(selection));
- }
- /**
- * Adds the required CSS-Classes
- */
- private void addCSSClasses() {
- // Class for the markers
- if(!svgp.getCSSClassManager().contains(CSS_MARKER)) {
- final CSSClass cls = new CSSClass(this, CSS_MARKER);
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLUE_VALUE);
- cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, "0.2");
- svgp.addCSSClassOrLogError(cls);
+ @Override
+ public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ List<ClusterOrderEntry<D>> order = getClusterOrder();
+ int mouseDownIndex = getSelectedIndex(order, startPoint);
+ int mouseActIndex = getSelectedIndex(order, dragPoint);
+ final int begin = Math.max(Math.min(mouseDownIndex, mouseActIndex), 0);
+ final int end = Math.min(Math.max(mouseDownIndex, mouseActIndex), order.size());
+ double width = plotwidth / order.size();
+ double x1 = begin * width;
+ double x2 = (end * width) + width;
+ mtag.removeChild(mtag.getLastChild());
+ Element marker = addMarkerRect(x1, x2 - x1);
+ SVGUtil.setCSSClass(marker, CSS_RANGEMARKER);
+ mtag.appendChild(marker);
+ return true;
}
- // Class for the range marking
- if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
- final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
- rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_RED_VALUE);
- rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, "0.2");
- svgp.addCSSClassOrLogError(rcls);
+ @Override
+ public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ List<ClusterOrderEntry<D>> order = getClusterOrder();
+ int mouseDownIndex = getSelectedIndex(order, startPoint);
+ int mouseActIndex = getSelectedIndex(order, dragPoint);
+ Mode mode = getInputMode(evt);
+ final int begin = Math.max(Math.min(mouseDownIndex, mouseActIndex), 0);
+ final int end = Math.min(Math.max(mouseDownIndex, mouseActIndex), order.size());
+ updateSelection(mode, begin, end);
+ return true;
}
- }
- @Override
- public void resultChanged(Result current) {
- if(current instanceof SelectionResult) {
- synchronizedRedraw();
- return;
+ /**
+ * Get the current input mode, on each mouse event.
+ *
+ * @param evt Mouse event.
+ * @return Input mode
+ */
+ private Mode getInputMode(Event evt) {
+ if(evt instanceof DOMMouseEvent) {
+ DOMMouseEvent domme = (DOMMouseEvent) evt;
+ // TODO: visual indication of mode possible?
+ if(domme.getShiftKey()) {
+ return Mode.ADD;
+ }
+ else if(domme.getCtrlKey()) {
+ return Mode.INVERT;
+ }
+ else {
+ return Mode.REPLACE;
+ }
+ }
+ // Default mode is replace.
+ return Mode.REPLACE;
}
- super.resultChanged(current);
- }
- /**
- * Factory class for OPTICS plot selections.
- *
- * @author Erich Schubert
- *
- * @apiviz.stereotype factory
- * @apiviz.uses OPTICSPlotSelectionVisualization oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Gets the Index of the ClusterOrderEntry where the event occurred
+ *
+ * @param order List of ClusterOrderEntries
+ * @param cPt clicked point
+ * @return Index of the object
*/
- public Factory() {
- super();
+ private int getSelectedIndex(List<ClusterOrderEntry<D>> order, SVGPoint cPt) {
+ int mouseActIndex = (int) ((cPt.getX() / plotwidth) * order.size());
+ return mouseActIndex;
}
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
- for(OPTICSProjector<?> p : ops) {
- final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
- baseResult.getHierarchy().add(p, task);
+ /**
+ * Updates the selection for the given ClusterOrderEntry.
+ *
+ * @param mode Input mode
+ * @param begin first index to select
+ * @param end last index to select
+ */
+ protected void updateSelection(Mode mode, int begin, int end) {
+ List<ClusterOrderEntry<D>> order = getClusterOrder();
+ if(begin < 0 || begin > end || end >= order.size()) {
+ LOG.warning("Invalid range in updateSelection: " + begin + " .. " + end);
+ return;
+ }
+
+ DBIDSelection selContext = context.getSelection();
+ HashSetModifiableDBIDs selection;
+ if(selContext == null || mode == Mode.REPLACE) {
+ selection = DBIDUtil.newHashSet();
+ }
+ else {
+ selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
}
+
+ for(int i = begin; i <= end; i++) {
+ DBID id = order.get(i).getID();
+ if(mode == Mode.INVERT) {
+ if(!selection.contains(id)) {
+ selection.add(id);
+ }
+ else {
+ selection.remove(id);
+ }
+ }
+ else {
+ // In REPLACE and ADD, add objects.
+ // The difference was done before by not re-using the selection.
+ // Since we are using a set, we can just add in any case.
+ selection.add(id);
+ }
+ }
+ context.setSelection(new DBIDSelection(selection));
}
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new OPTICSPlotSelectionVisualization<DoubleDistance>(task);
+ /**
+ * Adds the required CSS-Classes
+ */
+ private void addCSSClasses() {
+ // Class for the markers
+ if(!svgp.getCSSClassManager().contains(CSS_MARKER)) {
+ final CSSClass cls = new CSSClass(this, CSS_MARKER);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLUE_VALUE);
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, "0.2");
+ svgp.addCSSClassOrLogError(cls);
+ }
+
+ // Class for the range marking
+ if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
+ final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
+ rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_RED_VALUE);
+ rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, "0.2");
+ svgp.addCSSClassOrLogError(rcls);
+ }
}
@Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
+ public void resultChanged(Result current) {
+ if(current instanceof SelectionResult) {
+ synchronizedRedraw();
+ return;
+ }
+ super.resultChanged(current);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotVisualizer.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotVisualizer.java
index d93c98cb..cde2d89d 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotVisualizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSPlotVisualizer.java
@@ -38,6 +38,7 @@ import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClassManager.CSSNamingConflict;
import de.lmu.ifi.dbs.elki.visualization.opticsplot.OPTICSPlot;
import de.lmu.ifi.dbs.elki.visualization.projector.OPTICSProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGSimpleLinearAxis;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
@@ -48,87 +49,88 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
*
* @author Erich Schubert
*
- * @param <D> Distance type
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class OPTICSPlotVisualizer<D extends Distance<D>> extends AbstractOPTICSVisualization<D> {
+public class OPTICSPlotVisualizer extends AbstractVisFactory {
/**
* Name for this visualizer.
*/
private static final String NAME = "OPTICS Plot";
/**
- * Constructor.
- *
- * @param task Visualization task
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- public OPTICSPlotVisualizer(VisualizationTask task) {
- super(task);
+ public OPTICSPlotVisualizer() {
+ super();
}
@Override
- protected void redraw() {
- makeLayerElement();
- // addCSSClasses();
-
- OPTICSPlot<D> opticsplot = optics.getOPTICSPlot(context);
- String ploturi = opticsplot.getSVGPlotURI();
-
- Element itag = svgp.svgElement(SVGConstants.SVG_IMAGE_TAG);
- SVGUtil.setAtt(itag, SVGConstants.SVG_IMAGE_RENDERING_ATTRIBUTE, SVGConstants.SVG_OPTIMIZE_SPEED_VALUE);
- SVGUtil.setAtt(itag, SVGConstants.SVG_X_ATTRIBUTE, 0);
- SVGUtil.setAtt(itag, SVGConstants.SVG_Y_ATTRIBUTE, 0);
- SVGUtil.setAtt(itag, SVGConstants.SVG_WIDTH_ATTRIBUTE, plotwidth);
- SVGUtil.setAtt(itag, SVGConstants.SVG_HEIGHT_ATTRIBUTE, plotheight);
- itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, ploturi);
-
- layer.appendChild(itag);
-
- try {
- SVGSimpleLinearAxis.drawAxis(svgp, layer, opticsplot.getScale(), 0, plotheight, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, context.getStyleLibrary());
- SVGSimpleLinearAxis.drawAxis(svgp, layer, opticsplot.getScale(), plotwidth, plotheight, plotwidth, 0, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, context.getStyleLibrary());
- }
- catch(CSSNamingConflict e) {
- LoggingUtil.exception("CSS naming conflict for axes on OPTICS plot", e);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
+ for(OPTICSProjector<?> p : ops) {
+ // Add plots, attach visualizer
+ final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
+ task.level = VisualizationTask.LEVEL_DATA;
+ baseResult.getHierarchy().add(p, task);
}
}
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<DoubleDistance>(task);
+ }
+
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
+ }
+
/**
- * Factory class for OPTICS plot.
+ * Instance.
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses OPTICSPlotVisualizer oneway - - «create»
+ * @param <D> Distance type
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance<D extends Distance<D>> extends AbstractOPTICSVisualization<D> {
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Constructor.
+ *
+ * @param task Visualization task
*/
- public Factory() {
- super();
+ public Instance(VisualizationTask task) {
+ super(task);
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
- for(OPTICSProjector<?> p : ops) {
- // Add plots, attach visualizer
- final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ makeLayerElement();
+ // addCSSClasses();
+
+ OPTICSPlot<D> opticsplot = optics.getOPTICSPlot(context);
+ String ploturi = opticsplot.getSVGPlotURI();
+
+ Element itag = svgp.svgElement(SVGConstants.SVG_IMAGE_TAG);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_IMAGE_RENDERING_ATTRIBUTE, SVGConstants.SVG_OPTIMIZE_SPEED_VALUE);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_X_ATTRIBUTE, 0);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_Y_ATTRIBUTE, 0);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_WIDTH_ATTRIBUTE, plotwidth);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_HEIGHT_ATTRIBUTE, plotheight);
+ itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, ploturi);
+
+ layer.appendChild(itag);
+
+ try {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, opticsplot.getScale(), 0, plotheight, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, style);
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, opticsplot.getScale(), plotwidth, plotheight, plotwidth, 0, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, style);
+ }
+ catch(CSSNamingConflict e) {
+ LoggingUtil.exception("CSS naming conflict for axes on OPTICS plot", e);
}
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new OPTICSPlotVisualizer<DoubleDistance>(task);
- }
-
- @Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSSteepAreaVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSSteepAreaVisualization.java
index 4e557a74..e6e00960 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSSteepAreaVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/optics/OPTICSSteepAreaVisualization.java
@@ -56,40 +56,47 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
*
* @author Erich Schubert
*
- * @apiviz.uses
- * de.lmu.ifi.dbs.elki.algorithm.clustering.OPTICSXi.SteepAreaResult
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class OPTICSSteepAreaVisualization<D extends Distance<D>> extends AbstractOPTICSVisualization<D> {
+public class OPTICSSteepAreaVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "OPTICS Steep Areas";
/**
- * CSS class for markers
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- protected static final String CSS_STEEP_UP = "opticsSteepUp";
+ public OPTICSSteepAreaVisualization() {
+ super();
+ }
- /**
- * CSS class for markers
- */
- protected static final String CSS_STEEP_DOWN = "opticsSteepDown";
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
+ for(OPTICSProjector<?> p : ops) {
+ final SteepAreaResult steep = findSteepAreaResult(p.getResult());
+ if(steep != null) {
+ final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
+ task.level = VisualizationTask.LEVEL_DATA + 1;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(p, task);
+ baseResult.getHierarchy().add(steep, task);
+ }
+ }
+ }
- /**
- * Our clustering
- */
- OPTICSXi.SteepAreaResult areas;
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<DoubleDistance>(task);
+ }
- /**
- * Constructor.
- *
- * @param task Visualization task
- */
- public OPTICSSteepAreaVisualization(VisualizationTask task) {
- super(task);
- this.areas = findSteepAreaResult(this.optics.getResult());
- context.addResultListener(this);
- incrementalRedraw();
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
}
/**
@@ -107,116 +114,110 @@ public class OPTICSSteepAreaVisualization<D extends Distance<D>> extends Abstrac
return null;
}
- @Override
- protected void redraw() {
- makeLayerElement();
- addCSSClasses();
-
- final OPTICSPlot<D> opticsplot = optics.getOPTICSPlot(context);
- final List<ClusterOrderEntry<D>> co = getClusterOrder();
- final OPTICSDistanceAdapter<D> adapter = opticsplot.getDistanceAdapter();
- final LinearScale scale = opticsplot.getScale();
-
- for(OPTICSXi.SteepArea area : areas) {
- final int st = area.getStartIndex();
- final int en = area.getEndIndex();
- // Note: make sure we are using doubles!
- final double x1 = (st + .25) / co.size();
- final double x2 = (en + .75) / co.size();
- final double d1 = adapter.getDoubleForEntry(co.get(st));
- final double d2 = adapter.getDoubleForEntry(co.get(en));
- final double y1 = (!Double.isInfinite(d1) && !Double.isNaN(d1)) ? (1. - scale.getScaled(d1)) : 0.;
- final double y2 = (!Double.isInfinite(d2) && !Double.isNaN(d2)) ? (1. - scale.getScaled(d2)) : 0.;
- Element e = svgp.svgLine(plotwidth * x1, plotheight * y1, plotwidth * x2, plotheight * y2);
- if(area instanceof OPTICSXi.SteepDownArea) {
- SVGUtil.addCSSClass(e, CSS_STEEP_DOWN);
- }
- else {
- SVGUtil.addCSSClass(e, CSS_STEEP_UP);
- }
- layer.appendChild(e);
- }
- }
-
/**
- * Adds the required CSS-Classes
- */
- private void addCSSClasses() {
- // Class for the markers
- if(!svgp.getCSSClassManager().contains(CSS_STEEP_DOWN)) {
- final CSSClass cls = new CSSClass(this, CSS_STEEP_DOWN);
- Color color = SVGUtil.stringToColor(context.getStyleLibrary().getColor(StyleLibrary.PLOT));
- if(color == null) {
- color = Color.BLACK;
- }
- color = new Color((int) (color.getRed() * 0.8), (int) (color.getGreen() * 0.8 + 0.2 * 256), (int) (color.getBlue() * 0.8));
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGUtil.colorToString(color));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- svgp.addCSSClassOrLogError(cls);
- }
- if(!svgp.getCSSClassManager().contains(CSS_STEEP_UP)) {
- final CSSClass cls = new CSSClass(this, CSS_STEEP_UP);
- Color color = SVGUtil.stringToColor(context.getStyleLibrary().getColor(StyleLibrary.PLOT));
- if(color == null) {
- color = Color.BLACK;
- }
- color = new Color((int) (color.getRed() * 0.8 + 0.2 * 256), (int) (color.getGreen() * 0.8), (int) (color.getBlue() * 0.8));
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGUtil.colorToString(color));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- svgp.addCSSClassOrLogError(cls);
- }
- }
-
- @Override
- public void resultChanged(Result current) {
- if(current instanceof SelectionResult) {
- synchronizedRedraw();
- return;
- }
- super.resultChanged(current);
- }
-
- /**
- * Factory class for OPTICS plot selections.
+ * Instance
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses OPTICSPlotSelectionVisualization oneway - - «create»
+ * @apiviz.uses
+ * de.lmu.ifi.dbs.elki.algorithm.clustering.OPTICSXi.SteepAreaResult
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance<D extends Distance<D>> extends AbstractOPTICSVisualization<D> {
+ /**
+ * CSS class for markers
+ */
+ protected static final String CSS_STEEP_UP = "opticsSteepUp";
+
+ /**
+ * CSS class for markers
+ */
+ protected static final String CSS_STEEP_DOWN = "opticsSteepDown";
+
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Our clustering
*/
- public Factory() {
- super();
+ OPTICSXi.SteepAreaResult areas;
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.areas = findSteepAreaResult(this.optics.getResult());
+ context.addResultListener(this);
+ incrementalRedraw();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<OPTICSProjector<?>> ops = ResultUtil.filterResults(result, OPTICSProjector.class);
- for(OPTICSProjector<?> p : ops) {
- final SteepAreaResult steep = findSteepAreaResult(p.getResult());
- if(steep != null) {
- final VisualizationTask task = new VisualizationTask(NAME, p, null, this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA + 1);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(p, task);
- baseResult.getHierarchy().add(steep, task);
+ protected void redraw() {
+ makeLayerElement();
+ addCSSClasses();
+
+ final OPTICSPlot<D> opticsplot = optics.getOPTICSPlot(context);
+ final List<ClusterOrderEntry<D>> co = getClusterOrder();
+ final OPTICSDistanceAdapter<D> adapter = opticsplot.getDistanceAdapter();
+ final LinearScale scale = opticsplot.getScale();
+
+ for(OPTICSXi.SteepArea area : areas) {
+ final int st = area.getStartIndex();
+ final int en = area.getEndIndex();
+ // Note: make sure we are using doubles!
+ final double x1 = (st + .25) / co.size();
+ final double x2 = (en + .75) / co.size();
+ final double d1 = adapter.getDoubleForEntry(co.get(st));
+ final double d2 = adapter.getDoubleForEntry(co.get(en));
+ final double y1 = (!Double.isInfinite(d1) && !Double.isNaN(d1)) ? (1. - scale.getScaled(d1)) : 0.;
+ final double y2 = (!Double.isInfinite(d2) && !Double.isNaN(d2)) ? (1. - scale.getScaled(d2)) : 0.;
+ Element e = svgp.svgLine(plotwidth * x1, plotheight * y1, plotwidth * x2, plotheight * y2);
+ if(area instanceof OPTICSXi.SteepDownArea) {
+ SVGUtil.addCSSClass(e, CSS_STEEP_DOWN);
}
+ else {
+ SVGUtil.addCSSClass(e, CSS_STEEP_UP);
+ }
+ layer.appendChild(e);
}
}
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new OPTICSSteepAreaVisualization<DoubleDistance>(task);
+ /**
+ * Adds the required CSS-Classes
+ */
+ private void addCSSClasses() {
+ // Class for the markers
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ if(!svgp.getCSSClassManager().contains(CSS_STEEP_DOWN)) {
+ final CSSClass cls = new CSSClass(this, CSS_STEEP_DOWN);
+ Color color = SVGUtil.stringToColor(style.getColor(StyleLibrary.PLOT));
+ if(color == null) {
+ color = Color.BLACK;
+ }
+ color = new Color((int) (color.getRed() * 0.8), (int) (color.getGreen() * 0.8 + 0.2 * 256.), (int) (color.getBlue() * 0.8));
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGUtil.colorToString(color));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
+ svgp.addCSSClassOrLogError(cls);
+ }
+ if(!svgp.getCSSClassManager().contains(CSS_STEEP_UP)) {
+ final CSSClass cls = new CSSClass(this, CSS_STEEP_UP);
+ Color color = SVGUtil.stringToColor(style.getColor(StyleLibrary.PLOT));
+ if(color == null) {
+ color = Color.BLACK;
+ }
+ color = new Color((int) (color.getRed() * 0.8 + 0.2 * 256.), (int) (color.getGreen() * 0.8), (int) (color.getBlue() * 0.8));
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGUtil.colorToString(color));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
+ svgp.addCSSClassOrLogError(cls);
+ }
}
@Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
+ public void resultChanged(Result current) {
+ if(current instanceof SelectionResult) {
+ synchronizedRedraw();
+ return;
+ }
+ super.resultChanged(current);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/package-info.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/package-info.java
index 208d8cb7..d5dad85b 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/package-info.java
@@ -2,7 +2,8 @@
* <p>Visualizers for various results</p>
*
* @apiviz.exclude de.lmu.ifi.dbs.elki.utilities.datastructures.AnyMap
- * @apiviz.exclude de.lmu.ifi.dbs.elki.visualization.visualizers\.vis.*\..*
+ * @apiviz.exclude Visualization.Factory
+ * @apiviz.exclude de.lmu.ifi.dbs.elki.visualization.gui.*
*/
/*
This file is part of ELKI:
@@ -26,4 +27,4 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package de.lmu.ifi.dbs.elki.visualization.visualizers; \ No newline at end of file
+package de.lmu.ifi.dbs.elki.visualization.visualizers;
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/CircleSegmentsVisualizer.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/CircleSegmentsVisualizer.java
index f0655ab6..400b8ebb 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/CircleSegmentsVisualizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/pairsegments/CircleSegmentsVisualizer.java
@@ -73,655 +73,667 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
* In: Proc. 28th International Conference on Data Engineering (ICDE) 2012
* </p>
*
+ * <p>
+ * Details on the experimental setup can be found at: <a
+ * href="http://elki.dbs.ifi.lmu.de/wiki/Examples/ClusterEvaluation"
+ * >wiki/Examples/ClusterEvaluation</a>
+ * </p>
+ *
* @author Sascha Goldhofer
* @author Erich Schubert
*
* @apiviz.landmark
*
- * @apiviz.uses Segments
- * @apiviz.has SegmentsStylingPolicy
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-@Reference(title = "Evaluation of Clusterings – Metrics and Visual Support", authors = "Elke Achtert, Sascha Goldhofer, Hans-Peter Kriegel, Erich Schubert, Arthur Zimek", booktitle = "Proc. 28th International Conference on Data Engineering (ICDE) 2012", url = "http://elki.dbs.ifi.lmu.de/wiki/PairSegments")
-public class CircleSegmentsVisualizer extends AbstractVisualization implements ResultListener {
+@Reference(title = "Evaluation of Clusterings – Metrics and Visual Support", authors = "Elke Achtert, Sascha Goldhofer, Hans-Peter Kriegel, Erich Schubert, Arthur Zimek", booktitle = "Proc. 28th International Conference on Data Engineering (ICDE) 2012", url = "http://dx.doi.org/10.1109/ICDE.2012.128")
+public class CircleSegmentsVisualizer extends AbstractVisFactory {
/**
* Class logger
*/
- private static final Logging logger = Logging.getLogger(CircleSegmentsVisualizer.class);
+ private static final Logging LOG = Logging.getLogger(CircleSegmentsVisualizer.class);
/**
* CircleSegments visualizer name
*/
private static final String NAME = "CircleSegments";
- /** Minimum width (radian) of Segment */
- private final static double SEGMENT_MIN_ANGLE = 0.01;
+ /**
+ * Constructor
+ */
+ public CircleSegmentsVisualizer() {
+ super();
+ this.thumbmask |= ThumbnailVisualization.ON_STYLE;
+ }
- /** Gap (radian) between segments */
- private final static double SEGMENT_MIN_SEP_ANGLE = 0.005;
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
- /** Offset from center to first ring */
- private final static double RADIUS_INNER = 0.04 * StyleLibrary.SCALE;
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // If no comparison result found abort
+ List<Segments> segments = ResultUtil.filterResults(result, Segments.class);
+ for(Segments segmentResult : segments) {
+ SegmentsStylingPolicy policy;
+ List<SegmentsStylingPolicy> styles = ResultUtil.filterResults(segmentResult, SegmentsStylingPolicy.class);
+ if(!styles.isEmpty()) {
+ policy = styles.get(0);
+ }
+ else {
+ policy = new SegmentsStylingPolicy(segmentResult);
+ baseResult.getHierarchy().add(segmentResult, policy);
+ }
+ // create task for visualization
+ final VisualizationTask task = new VisualizationTask(NAME, policy, null, this);
+ task.width = 2.0;
+ task.height = 2.0;
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ baseResult.getHierarchy().add(segmentResult, task);
+ }
+ }
- /** Margin between two rings */
- private final static double RADIUS_DISTANCE = 0.01 * StyleLibrary.SCALE;
+ /**
+ * Instance
+ *
+ * @author Sascha Goldhofer
+ * @author Erich Schubert
+ *
+ * @apiviz.uses Segments
+ * @apiviz.has SegmentsStylingPolicy
+ *
+ */
+ public class Instance extends AbstractVisualization implements ResultListener {
+ /** Minimum width (radian) of Segment */
+ private static final double SEGMENT_MIN_ANGLE = 0.01;
- /** Radius of whole CircleSegments except selection border */
- private final static double RADIUS_OUTER = 0.47 * StyleLibrary.SCALE;
+ /** Gap (radian) between segments */
+ private static final double SEGMENT_MIN_SEP_ANGLE = 0.005;
- /** Radius of highlight selection (outer ring) */
- private final static double RADIUS_SELECTION = 0.02 * StyleLibrary.SCALE;
+ /** Offset from center to first ring */
+ private static final double RADIUS_INNER = 0.04 * StyleLibrary.SCALE;
- /**
- * CSS class name for the clusterings.
- */
- private static final String CLR_CLUSTER_CLASS_PREFIX = "clusterSegment";
+ /** Margin between two rings */
+ private static final double RADIUS_DISTANCE = 0.01 * StyleLibrary.SCALE;
- /**
- * CSS border class of a cluster
- */
- public static final String CLR_BORDER_CLASS = "clusterBorder";
+ /** Radius of whole CircleSegments except selection border */
+ private static final double RADIUS_OUTER = 0.47 * StyleLibrary.SCALE;
- /**
- * CSS hover class for clusters of hovered segment
- */
- public static final String CLR_UNPAIRED_CLASS = "clusterUnpaired";
+ /** Radius of highlight selection (outer ring) */
+ private static final double RADIUS_SELECTION = 0.02 * StyleLibrary.SCALE;
- /**
- * CSS hover class of a segment cluster
- */
- public static final String CLR_HOVER_CLASS = "clusterHover";
+ /**
+ * CSS class name for the clusterings.
+ */
+ private static final String CLR_CLUSTER_CLASS_PREFIX = "clusterSegment";
- /**
- * CSS class of selected Segment
- */
- public static final String SEG_UNPAIRED_SELECTED_CLASS = "unpairedSegmentSelected";
+ /**
+ * CSS border class of a cluster
+ */
+ public static final String CLR_BORDER_CLASS = "clusterBorder";
- /**
- * Style prefix
- */
- public static final String STYLE = "segments";
+ /**
+ * CSS hover class for clusters of hovered segment
+ */
+ public static final String CLR_UNPAIRED_CLASS = "clusterUnpaired";
- /**
- * Style for border lines
- */
- public static final String STYLE_BORDER = STYLE + ".border";
+ /**
+ * CSS hover class of a segment cluster
+ */
+ public static final String CLR_HOVER_CLASS = "clusterHover";
- /**
- * Style for hover effect
- */
- public static final String STYLE_HOVER = STYLE + ".hover";
+ /**
+ * CSS class of selected Segment
+ */
+ public static final String SEG_UNPAIRED_SELECTED_CLASS = "unpairedSegmentSelected";
- /**
- * First color for producing segment-cluster colors
- */
- public static final String STYLE_GRADIENT_FIRST = STYLE + ".cluster.first";
+ /**
+ * Style prefix
+ */
+ public static final String STYLE = "segments";
- /**
- * Second color for producing segment-cluster colors
- */
- public static final String STYLE_GRADIENT_SECOND = STYLE + ".cluster.second";
+ /**
+ * Style for border lines
+ */
+ public static final String STYLE_BORDER = STYLE + ".border";
- /**
- * Segmentation of Clusterings
- */
- protected final Segments segments;
+ /**
+ * Style for hover effect
+ */
+ public static final String STYLE_HOVER = STYLE + ".hover";
- /**
- * The two main layers
- */
- private Element visLayer, ctrlLayer;
+ /**
+ * First color for producing segment-cluster colors
+ */
+ public static final String STYLE_GRADIENT_FIRST = STYLE + ".cluster.first";
- /**
- * Map to connect segments to their visual elements
- */
- public Map<Segment, List<Element>> segmentToElements = new HashMap<Segment, List<Element>>();
+ /**
+ * Second color for producing segment-cluster colors
+ */
+ public static final String STYLE_GRADIENT_SECOND = STYLE + ".cluster.second";
- /**
- * Show unclustered Pairs in CircleSegments
- */
- boolean showUnclusteredPairs = false;
+ /**
+ * Segmentation of Clusterings
+ */
+ protected final Segments segments;
- /**
- * Styling policy
- */
- protected final SegmentsStylingPolicy policy;
+ /**
+ * The two main layers
+ */
+ private Element visLayer, ctrlLayer;
- /**
- * Flag to disallow an incremental redraw
- */
- private boolean noIncrementalRedraw = true;
+ /**
+ * Map to connect segments to their visual elements
+ */
+ public Map<Segment, List<Element>> segmentToElements = new HashMap<Segment, List<Element>>();
- /**
- * Constructor
- */
- public CircleSegmentsVisualizer(VisualizationTask task) {
- super(task);
- policy = task.getResult();
- segments = policy.segments;
- // FIXME: handle this more generally.
- policy.setStyleLibrary(context.getStyleLibrary());
- // Listen for result changes (Selection changed)
- context.addResultListener(this);
- }
+ /**
+ * Show unclustered Pairs in CircleSegments
+ */
+ boolean showUnclusteredPairs = false;
- public void toggleUnclusteredPairs(boolean show) {
- noIncrementalRedraw = true;
- showUnclusteredPairs = show;
- synchronizedRedraw();
- }
+ /**
+ * Styling policy
+ */
+ protected final SegmentsStylingPolicy policy;
- @Override
- public void resultChanged(Result current) {
- super.resultChanged(current);
- // Redraw on style result changes.
- if(current == context.getStyleResult()) {
- // When switching to a different policy, unhighlight segments.
- if(context.getStyleResult().getStylingPolicy() != policy) {
- policy.deselectAllSegments();
- }
- synchronizedRedraw();
+ /**
+ * Flag to disallow an incremental redraw
+ */
+ private boolean noIncrementalRedraw = true;
+
+ /**
+ * Constructor
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ policy = task.getResult();
+ segments = policy.segments;
+ // FIXME: handle this more generally.
+ policy.setStyleLibrary(context.getStyleResult().getStyleLibrary());
+ // Listen for result changes (Selection changed)
+ context.addResultListener(this);
}
- }
- @Override
- protected void incrementalRedraw() {
- if(noIncrementalRedraw) {
- super.incrementalRedraw();
+ public void toggleUnclusteredPairs(boolean show) {
+ noIncrementalRedraw = true;
+ showUnclusteredPairs = show;
+ synchronizedRedraw();
}
- else {
- redrawSelection();
+
+ @Override
+ public void resultChanged(Result current) {
+ super.resultChanged(current);
+ // Redraw on style result changes.
+ if(current == context.getStyleResult()) {
+ // When switching to a different policy, unhighlight segments.
+ if(context.getStyleResult().getStylingPolicy() != policy) {
+ policy.deselectAllSegments();
+ }
+ synchronizedRedraw();
+ }
}
- }
- @Override
- public void redraw() {
- logger.debug("Full redraw");
- noIncrementalRedraw = false; // Done that.
-
- // initialize css (needs clusterSize!)
- addCSSClasses(segments.getHighestClusterCount());
-
- layer = svgp.svgElement(SVGConstants.SVG_G_TAG);
- visLayer = svgp.svgElement(SVGConstants.SVG_G_TAG);
- // Setup scaling for canvas: 0 to StyleLibrary.SCALE (usually 100 to avoid a
- // Java drawing bug!)
- String transform = SVGUtil.makeMarginTransform(task.width, task.height, StyleLibrary.SCALE, StyleLibrary.SCALE, 0) + " translate(" + (StyleLibrary.SCALE * .5) + " " + (StyleLibrary.SCALE * .5) + ")";
- visLayer.setAttribute(SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
- ctrlLayer = svgp.svgElement(SVGConstants.SVG_G_TAG);
-
- // and create svg elements
- drawSegments();
-
- //
- // Build Interface
- //
- SVGCheckbox checkbox = new SVGCheckbox(showUnclusteredPairs, "Show unclustered pairs");
- checkbox.addCheckBoxListener(new ChangeListener() {
- @Override
- public void stateChanged(ChangeEvent e) {
- toggleUnclusteredPairs(((SVGCheckbox) e.getSource()).isChecked());
+ @Override
+ protected void incrementalRedraw() {
+ if(noIncrementalRedraw) {
+ super.incrementalRedraw();
+ }
+ else {
+ redrawSelection();
}
- });
+ }
- // Add ring:clustering info
- Element clrInfo = drawClusteringInfo();
- Element c = checkbox.renderCheckBox(svgp, 1, 5 + Double.parseDouble(clrInfo.getAttribute(SVGConstants.SVG_HEIGHT_ATTRIBUTE)), 11);
- ctrlLayer.appendChild(clrInfo);
- ctrlLayer.appendChild(c);
+ @Override
+ public void redraw() {
+ LOG.debug("Full redraw");
+ noIncrementalRedraw = false; // Done that.
- ctrlLayer.setAttribute(SVGConstants.SVG_TRANSFORM_ATTRIBUTE, "scale(" + (0.25 / StyleLibrary.SCALE) + ")");
+ // initialize css (needs clusterSize!)
+ addCSSClasses(segments.getHighestClusterCount());
- layer.appendChild(visLayer);
- layer.appendChild(ctrlLayer);
- }
+ layer = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ visLayer = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ // Setup scaling for canvas: 0 to StyleLibrary.SCALE (usually 100 to avoid
+ // a
+ // Java drawing bug!)
+ String transform = SVGUtil.makeMarginTransform(task.width, task.height, StyleLibrary.SCALE, StyleLibrary.SCALE, 0) + " translate(" + (StyleLibrary.SCALE * .5) + " " + (StyleLibrary.SCALE * .5) + ")";
+ visLayer.setAttribute(SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
+ ctrlLayer = svgp.svgElement(SVGConstants.SVG_G_TAG);
- /**
- * Define and add required CSS classes
- */
- protected void addCSSClasses(int maxClusterSize) {
- StyleLibrary style = context.getStyleLibrary();
-
- // Cluster separation lines
- CSSClass cssReferenceBorder = new CSSClass(this.getClass(), CLR_BORDER_CLASS);
- cssReferenceBorder.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getColor(STYLE_BORDER));
- svgp.addCSSClassOrLogError(cssReferenceBorder);
-
- // Hover effect for clusters
- CSSClass cluster_hover = new CSSClass(this.getClass(), CLR_HOVER_CLASS);
- // Note: !important is needed to override the regular color assignment
- cluster_hover.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getColor(STYLE_HOVER) + " !important");
- cluster_hover.setStatement(SVGConstants.SVG_CURSOR_TAG, SVGConstants.SVG_POINTER_VALUE);
- svgp.addCSSClassOrLogError(cluster_hover);
-
- // Unpaired cluster segment
- CSSClass cluster_unpaired = new CSSClass(this.getClass(), CLR_UNPAIRED_CLASS);
- cluster_unpaired.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getBackgroundColor(STYLE));
- cluster_unpaired.setStatement(SVGConstants.SVG_STROKE_ATTRIBUTE, SVGConstants.CSS_NONE_VALUE);
- svgp.addCSSClassOrLogError(cluster_unpaired);
-
- // Selected unpaired cluster segment
- CSSClass cluster_unpaired_s = new CSSClass(this.getClass(), SEG_UNPAIRED_SELECTED_CLASS);
- cluster_unpaired_s.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getColor(STYLE_HOVER) + " !important");
- svgp.addCSSClassOrLogError(cluster_unpaired_s);
-
- // create Color shades for clusters
- String firstcol = style.getColor(STYLE_GRADIENT_FIRST);
- String secondcol = style.getColor(STYLE_GRADIENT_SECOND);
- String[] clusterColorShades = makeGradient(maxClusterSize, new String[] { firstcol, secondcol });
-
- for(int i = 0; i < maxClusterSize; i++) {
- CSSClass clusterClasses = new CSSClass(CircleSegmentsVisualizer.class, CLR_CLUSTER_CLASS_PREFIX + "_" + i);
- clusterClasses.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, clusterColorShades[i]);
- clusterClasses.setStatement(SVGConstants.SVG_STROKE_ATTRIBUTE, SVGConstants.SVG_NONE_VALUE);
- svgp.addCSSClassOrLogError(clusterClasses);
- }
- }
+ // and create svg elements
+ drawSegments();
- /**
- * Create the segments
- */
- private void drawSegments() {
- final int clusterings = segments.getClusterings();
+ //
+ // Build Interface
+ //
+ SVGCheckbox checkbox = new SVGCheckbox(showUnclusteredPairs, "Show unclustered pairs");
+ checkbox.addCheckBoxListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ toggleUnclusteredPairs(((SVGCheckbox) e.getSource()).isChecked());
+ }
+ });
- // Reinitialize
- this.segmentToElements.clear();
+ // Add ring:clustering info
+ Element clrInfo = drawClusteringInfo();
+ Element c = checkbox.renderCheckBox(svgp, 1, 5 + Double.parseDouble(clrInfo.getAttribute(SVGConstants.SVG_HEIGHT_ATTRIBUTE)), 11);
+ ctrlLayer.appendChild(clrInfo);
+ ctrlLayer.appendChild(c);
- double angle_pair = (MathUtil.TWOPI - (SEGMENT_MIN_SEP_ANGLE * segments.size())) / segments.getPairCount(showUnclusteredPairs);
- final int pair_min_count = (int) Math.ceil(SEGMENT_MIN_ANGLE / angle_pair);
+ ctrlLayer.setAttribute(SVGConstants.SVG_TRANSFORM_ATTRIBUTE, "scale(" + (0.25 / StyleLibrary.SCALE) + ")");
- // number of segments needed to be resized
- int cluster_min_count = 0;
- for(Segment segment : segments) {
- if(segment.getPairCount() <= pair_min_count) {
- cluster_min_count++;
+ layer.appendChild(visLayer);
+ layer.appendChild(ctrlLayer);
+ }
+
+ /**
+ * Define and add required CSS classes
+ */
+ protected void addCSSClasses(int maxClusterSize) {
+ StyleLibrary style = context.getStyleResult().getStyleLibrary();
+
+ // Cluster separation lines
+ CSSClass cssReferenceBorder = new CSSClass(this.getClass(), CLR_BORDER_CLASS);
+ cssReferenceBorder.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getColor(STYLE_BORDER));
+ svgp.addCSSClassOrLogError(cssReferenceBorder);
+
+ // Hover effect for clusters
+ CSSClass cluster_hover = new CSSClass(this.getClass(), CLR_HOVER_CLASS);
+ // Note: !important is needed to override the regular color assignment
+ cluster_hover.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getColor(STYLE_HOVER) + " !important");
+ cluster_hover.setStatement(SVGConstants.SVG_CURSOR_TAG, SVGConstants.SVG_POINTER_VALUE);
+ svgp.addCSSClassOrLogError(cluster_hover);
+
+ // Unpaired cluster segment
+ CSSClass cluster_unpaired = new CSSClass(this.getClass(), CLR_UNPAIRED_CLASS);
+ cluster_unpaired.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getBackgroundColor(STYLE));
+ cluster_unpaired.setStatement(SVGConstants.SVG_STROKE_ATTRIBUTE, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(cluster_unpaired);
+
+ // Selected unpaired cluster segment
+ CSSClass cluster_unpaired_s = new CSSClass(this.getClass(), SEG_UNPAIRED_SELECTED_CLASS);
+ cluster_unpaired_s.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, style.getColor(STYLE_HOVER) + " !important");
+ svgp.addCSSClassOrLogError(cluster_unpaired_s);
+
+ // create Color shades for clusters
+ String firstcol = style.getColor(STYLE_GRADIENT_FIRST);
+ String secondcol = style.getColor(STYLE_GRADIENT_SECOND);
+ String[] clusterColorShades = makeGradient(maxClusterSize, new String[] { firstcol, secondcol });
+
+ for(int i = 0; i < maxClusterSize; i++) {
+ CSSClass clusterClasses = new CSSClass(CircleSegmentsVisualizer.class, CLR_CLUSTER_CLASS_PREFIX + "_" + i);
+ clusterClasses.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, clusterColorShades[i]);
+ clusterClasses.setStatement(SVGConstants.SVG_STROKE_ATTRIBUTE, SVGConstants.SVG_NONE_VALUE);
+ svgp.addCSSClassOrLogError(clusterClasses);
}
}
- // update width of a pair
- angle_pair = (MathUtil.TWOPI - (SEGMENT_MIN_SEP_ANGLE * segments.size() + cluster_min_count * SEGMENT_MIN_ANGLE)) / (segments.getPairCount(showUnclusteredPairs) - cluster_min_count);
- double radius_delta = (RADIUS_OUTER - RADIUS_INNER - clusterings * RADIUS_DISTANCE) / clusterings;
- double border_width = SEGMENT_MIN_SEP_ANGLE;
+ /**
+ * Create the segments
+ */
+ private void drawSegments() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final int clusterings = segments.getClusterings();
- int refClustering = 0;
- int refSegment = Segment.UNCLUSTERED;
- double offsetAngle = 0.0;
+ // Reinitialize
+ this.segmentToElements.clear();
- for(final Segment segment : segments) {
- long currentPairCount = segment.getPairCount();
+ double angle_pair = (MathUtil.TWOPI - (SEGMENT_MIN_SEP_ANGLE * segments.size())) / segments.getPairCount(showUnclusteredPairs);
+ final int pair_min_count = (int) Math.ceil(SEGMENT_MIN_ANGLE / angle_pair);
- // resize small segments if below minimum
- double alpha = SEGMENT_MIN_ANGLE;
- if(currentPairCount > pair_min_count) {
- alpha = angle_pair * currentPairCount;
+ // number of segments needed to be resized
+ int cluster_min_count = 0;
+ for(Segment segment : segments) {
+ if(segment.getPairCount() <= pair_min_count) {
+ cluster_min_count++;
+ }
}
- // ITERATE OVER ALL SEGMENT-CLUSTERS
-
- ArrayList<Element> elems = new ArrayList<Element>(clusterings);
- segmentToElements.put(segment, elems);
- // draw segment for every clustering
+ // update width of a pair
+ angle_pair = (MathUtil.TWOPI - (SEGMENT_MIN_SEP_ANGLE * segments.size() + cluster_min_count * SEGMENT_MIN_ANGLE)) / (segments.getPairCount(showUnclusteredPairs) - cluster_min_count);
+ double radius_delta = (RADIUS_OUTER - RADIUS_INNER - clusterings * RADIUS_DISTANCE) / clusterings;
+ double border_width = SEGMENT_MIN_SEP_ANGLE;
- for(int i = 0; i < clusterings; i++) {
- double currentRadius = i * (radius_delta + RADIUS_DISTANCE) + RADIUS_INNER;
+ int refClustering = 0;
+ int refSegment = Segment.UNCLUSTERED;
+ double offsetAngle = 0.0;
- // Add border if the next segment is a different cluster in the
- // reference clustering
- if((refSegment != segment.get(refClustering)) && refClustering == i) {
- Element border = SVGUtil.svgCircleSegment(svgp, 0, 0, offsetAngle - SEGMENT_MIN_SEP_ANGLE, border_width, currentRadius, RADIUS_OUTER - RADIUS_DISTANCE);
- border.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_BORDER_CLASS);
- visLayer.appendChild(border);
+ for(final Segment segment : segments) {
+ long currentPairCount = segment.getPairCount();
- if(segment.get(refClustering) == Segment.UNCLUSTERED) {
- refClustering = Math.min(refClustering + 1, clusterings - 1);
- }
- refSegment = segment.get(refClustering);
+ // resize small segments if below minimum
+ double alpha = SEGMENT_MIN_ANGLE;
+ if(currentPairCount > pair_min_count) {
+ alpha = angle_pair * currentPairCount;
}
- int cluster = segment.get(i);
+ // ITERATE OVER ALL SEGMENT-CLUSTERS
- // create ring segment
- Element segelement = SVGUtil.svgCircleSegment(svgp, 0, 0, offsetAngle, alpha, currentRadius, currentRadius + radius_delta);
- elems.add(segelement);
+ ArrayList<Element> elems = new ArrayList<Element>(clusterings);
+ segmentToElements.put(segment, elems);
+ // draw segment for every clustering
- // MouseEvents on segment cluster
- EventListener listener = new SegmentListenerProxy(segment, i);
- EventTarget targ = (EventTarget) segelement;
- targ.addEventListener(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE, listener, false);
- targ.addEventListener(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE, listener, false);
- targ.addEventListener(SVGConstants.SVG_CLICK_EVENT_TYPE, listener, false);
+ for(int i = 0; i < clusterings; i++) {
+ double currentRadius = i * (radius_delta + RADIUS_DISTANCE) + RADIUS_INNER;
- // Coloring based on clusterID
- if(cluster >= 0) {
- segelement.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_CLUSTER_CLASS_PREFIX + "_" + cluster);
- }
- // if its an unpaired cluster set color to white
- else {
- segelement.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_UNPAIRED_CLASS);
- }
+ // Add border if the next segment is a different cluster in the
+ // reference clustering
+ if((refSegment != segment.get(refClustering)) && refClustering == i) {
+ Element border = SVGUtil.svgCircleSegment(svgp, 0, 0, offsetAngle - SEGMENT_MIN_SEP_ANGLE, border_width, currentRadius, RADIUS_OUTER - RADIUS_DISTANCE);
+ border.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_BORDER_CLASS);
+ visLayer.appendChild(border);
- visLayer.appendChild(segelement);
- }
+ if(segment.get(refClustering) == Segment.UNCLUSTERED) {
+ refClustering = Math.min(refClustering + 1, clusterings - 1);
+ }
+ refSegment = segment.get(refClustering);
+ }
- //
- // Add a extended strip for each segment to emphasis selection
- // (easier to track thin segments and their color coding and
- // differentiates them from cluster border lines)
- //
+ int cluster = segment.get(i);
- double currentRadius = clusterings * (radius_delta + RADIUS_DISTANCE) + RADIUS_INNER;
- Element extension = SVGUtil.svgCircleSegment(svgp, 0, 0, offsetAngle, alpha, currentRadius, currentRadius + RADIUS_SELECTION);
- extension.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_UNPAIRED_CLASS);
- elems.add(extension);
+ // create ring segment
+ Element segelement = SVGUtil.svgCircleSegment(svgp, 0, 0, offsetAngle, alpha, currentRadius, currentRadius + radius_delta);
+ elems.add(segelement);
- if(segment.isUnpaired()) {
- if(policy.isSelected(segment)) {
- SVGUtil.addCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
- }
- else {
- // Remove highlight
- SVGUtil.removeCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
- }
- }
- else {
- int idx = policy.indexOfSegment(segment);
- if(idx >= 0) {
- String color = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT).getColor(idx);
- extension.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_FILL_PROPERTY + ":" + color);
- }
- else {
- // Remove styling
- extension.removeAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE);
+ // MouseEvents on segment cluster
+ EventListener listener = new SegmentListenerProxy(segment, i);
+ EventTarget targ = (EventTarget) segelement;
+ targ.addEventListener(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE, listener, false);
+ targ.addEventListener(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE, listener, false);
+ targ.addEventListener(SVGConstants.SVG_CLICK_EVENT_TYPE, listener, false);
+
+ // Coloring based on clusterID
+ if(cluster >= 0) {
+ segelement.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_CLUSTER_CLASS_PREFIX + "_" + cluster);
+ }
+ // if its an unpaired cluster set color to white
+ else {
+ segelement.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_UNPAIRED_CLASS);
+ }
+
+ visLayer.appendChild(segelement);
}
- }
- visLayer.appendChild(extension);
+ //
+ // Add a extended strip for each segment to emphasis selection
+ // (easier to track thin segments and their color coding and
+ // differentiates them from cluster border lines)
+ //
- // calculate angle for next segment
- offsetAngle += alpha + SEGMENT_MIN_SEP_ANGLE;
- }
- }
+ double currentRadius = clusterings * (radius_delta + RADIUS_DISTANCE) + RADIUS_INNER;
+ Element extension = SVGUtil.svgCircleSegment(svgp, 0, 0, offsetAngle, alpha, currentRadius, currentRadius + RADIUS_SELECTION);
+ extension.setAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE, CLR_UNPAIRED_CLASS);
+ elems.add(extension);
- private void redrawSelection() {
- logger.debug("Updating selection only.");
- for(Entry<Segment, List<Element>> entry : segmentToElements.entrySet()) {
- Segment segment = entry.getKey();
- // The selection marker is the extra element in the list
- Element extension = entry.getValue().get(segments.getClusterings());
- if(segment.isUnpaired()) {
- if(policy.isSelected(segment)) {
- SVGUtil.addCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
+ if(segment.isUnpaired()) {
+ if(policy.isSelected(segment)) {
+ SVGUtil.addCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
+ }
+ else {
+ // Remove highlight
+ SVGUtil.removeCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
+ }
}
else {
- // Remove highlight
- SVGUtil.removeCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
+ int idx = policy.indexOfSegment(segment);
+ if(idx >= 0) {
+ String color = style.getColorSet(StyleLibrary.PLOT).getColor(idx);
+ extension.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_FILL_PROPERTY + ":" + color);
+ }
+ else {
+ // Remove styling
+ extension.removeAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE);
+ }
}
+
+ visLayer.appendChild(extension);
+
+ // calculate angle for next segment
+ offsetAngle += alpha + SEGMENT_MIN_SEP_ANGLE;
}
- else {
- int idx = policy.indexOfSegment(segment);
- if(idx >= 0) {
- String color = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT).getColor(idx);
- extension.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_FILL_PROPERTY + ":" + color);
+ }
+
+ private void redrawSelection() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ LOG.debug("Updating selection only.");
+ for(Entry<Segment, List<Element>> entry : segmentToElements.entrySet()) {
+ Segment segment = entry.getKey();
+ // The selection marker is the extra element in the list
+ Element extension = entry.getValue().get(segments.getClusterings());
+ if(segment.isUnpaired()) {
+ if(policy.isSelected(segment)) {
+ SVGUtil.addCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
+ }
+ else {
+ // Remove highlight
+ SVGUtil.removeCSSClass(extension, SEG_UNPAIRED_SELECTED_CLASS);
+ }
}
else {
- // Remove styling
- extension.removeAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE);
+ int idx = policy.indexOfSegment(segment);
+ if(idx >= 0) {
+ String color = style.getColorSet(StyleLibrary.PLOT).getColor(idx);
+ extension.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_FILL_PROPERTY + ":" + color);
+ }
+ else {
+ // Remove styling
+ extension.removeAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE);
+ }
}
}
}
- }
- /**
- * Creates a gradient over a set of colors
- *
- * @param shades number of colors in the gradient
- * @param colors colors for the gradient
- * @return array of colors for CSS
- */
- protected static String[] makeGradient(int shades, String[] colors) {
- if(shades <= colors.length) {
- return colors;
- }
+ /**
+ * Creates a gradient over a set of colors
+ *
+ * @param shades number of colors in the gradient
+ * @param colors colors for the gradient
+ * @return array of colors for CSS
+ */
+ protected String[] makeGradient(int shades, String[] colors) {
+ if(shades <= colors.length) {
+ return colors;
+ }
- // Convert SVG colors into AWT colors for math
- Color[] cols = new Color[colors.length];
- for(int i = 0; i < colors.length; i++) {
- cols[i] = SVGUtil.stringToColor(colors[i]);
- if(cols[i] == null) {
- throw new AbortException("Error parsing color: " + colors[i]);
+ // Convert SVG colors into AWT colors for math
+ Color[] cols = new Color[colors.length];
+ for(int i = 0; i < colors.length; i++) {
+ cols[i] = SVGUtil.stringToColor(colors[i]);
+ if(cols[i] == null) {
+ throw new AbortException("Error parsing color: " + colors[i]);
+ }
}
- }
- // Step size
- double increment = (cols.length - 1.) / shades;
+ // Step size
+ double increment = (cols.length - 1.) / shades;
- String[] colorShades = new String[shades];
+ String[] colorShades = new String[shades];
- for(int s = 0; s < shades; s++) {
- final int ppos = Math.min((int) Math.floor(increment * s), cols.length);
- final int npos = Math.min((int) Math.ceil(increment * s), cols.length);
- if(ppos == npos) {
- colorShades[s] = colors[ppos];
- }
- else {
- Color prev = cols[ppos];
- Color next = cols[npos];
- final double mix = (increment * s - ppos) / (npos - ppos);
- final int r = (int) ((1 - mix) * prev.getRed() + mix * next.getRed());
- final int g = (int) ((1 - mix) * prev.getGreen() + mix * next.getGreen());
- final int b = (int) ((1 - mix) * prev.getBlue() + mix * next.getBlue());
- colorShades[s] = SVGUtil.colorToString(((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF));
+ for(int s = 0; s < shades; s++) {
+ final int ppos = Math.min((int) Math.floor(increment * s), cols.length);
+ final int npos = Math.min((int) Math.ceil(increment * s), cols.length);
+ if(ppos == npos) {
+ colorShades[s] = colors[ppos];
+ }
+ else {
+ Color prev = cols[ppos];
+ Color next = cols[npos];
+ final double mix = (increment * s - ppos) / (npos - ppos);
+ final int r = (int) ((1 - mix) * prev.getRed() + mix * next.getRed());
+ final int g = (int) ((1 - mix) * prev.getGreen() + mix * next.getGreen());
+ final int b = (int) ((1 - mix) * prev.getBlue() + mix * next.getBlue());
+ colorShades[s] = SVGUtil.colorToString(((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF));
+ }
}
+
+ return colorShades;
}
- return colorShades;
- }
+ protected Element drawClusteringInfo() {
+ Element thumbnail = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
- protected Element drawClusteringInfo() {
- Element thumbnail = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
+ // build thumbnail
+ int startRadius = 4;
+ int singleHeight = 12;
+ int margin = 4;
+ int radius = segments.getClusterings() * (singleHeight + margin) + startRadius;
- // build thumbnail
- int startRadius = 4;
- int singleHeight = 12;
- int margin = 4;
- int radius = segments.getClusterings() * (singleHeight + margin) + startRadius;
+ SVGUtil.setAtt(thumbnail, SVGConstants.SVG_HEIGHT_ATTRIBUTE, radius);
- SVGUtil.setAtt(thumbnail, SVGConstants.SVG_HEIGHT_ATTRIBUTE, radius);
+ for(int i = 0; i < segments.getClusterings(); i++) {
+ double innerRadius = i * singleHeight + margin * i + startRadius;
+ Element clr = SVGUtil.svgCircleSegment(svgp, radius - startRadius, radius - startRadius, Math.PI * 1.5, Math.PI * 0.5, innerRadius, innerRadius + singleHeight);
+ // FIXME: Use StyleLibrary
+ clr.setAttribute(SVGConstants.SVG_FILL_ATTRIBUTE, "#d4e4f1");
+ clr.setAttribute(SVGConstants.SVG_STROKE_ATTRIBUTE, "#a0a0a0");
+ clr.setAttribute(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, "1.0");
- for(int i = 0; i < segments.getClusterings(); i++) {
- double innerRadius = i * singleHeight + margin * i + startRadius;
- Element clr = SVGUtil.svgCircleSegment(svgp, radius - startRadius, radius - startRadius, Math.PI * 1.5, Math.PI * 0.5, innerRadius, innerRadius + singleHeight);
- // FIXME: Use StyleLibrary
- clr.setAttribute(SVGConstants.SVG_FILL_ATTRIBUTE, "#d4e4f1");
- clr.setAttribute(SVGConstants.SVG_STROKE_ATTRIBUTE, "#a0a0a0");
- clr.setAttribute(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, "1.0");
+ String labelText = segments.getClusteringDescription(i);
+ Element label = svgp.svgText(radius + startRadius, radius - innerRadius - startRadius, labelText);
+ thumbnail.appendChild(label);
- String labelText = segments.getClusteringDescription(i);
- Element label = svgp.svgText(radius + startRadius, radius - innerRadius - startRadius, labelText);
- thumbnail.appendChild(label);
+ thumbnail.appendChild(clr);
+ }
- thumbnail.appendChild(clr);
+ return thumbnail;
}
- return thumbnail;
- }
-
- protected void segmentHover(Segment segment, int ringid, boolean active) {
- if(active) {
- // abort if this are the unclustered pairs
- if(segment.isNone()) {
- return;
- }
- if(logger.isDebugging()) {
- logger.debug("Hover on segment: " + segment + " unpaired: " + segment.isUnpaired());
- }
+ protected void segmentHover(Segment segment, int ringid, boolean active) {
+ if(active) {
+ // abort if this are the unclustered pairs
+ if(segment.isNone()) {
+ return;
+ }
+ if(LOG.isDebugging()) {
+ LOG.debug("Hover on segment: " + segment + " unpaired: " + segment.isUnpaired());
+ }
- if(!segment.isUnpaired()) {
- //
- // STANDARD CLUSTER SEGMENT
- // highlight all ring segments in this clustering and this cluster
- //
- // highlight all corresponding ring Segments
- for(Entry<Segment, List<Element>> entry : segmentToElements.entrySet()) {
- Segment other = entry.getKey();
- // Same cluster in same clustering?
- if(other.get(ringid) != segment.get(ringid)) {
- continue;
+ if(!segment.isUnpaired()) {
+ //
+ // STANDARD CLUSTER SEGMENT
+ // highlight all ring segments in this clustering and this cluster
+ //
+ // highlight all corresponding ring Segments
+ for(Entry<Segment, List<Element>> entry : segmentToElements.entrySet()) {
+ Segment other = entry.getKey();
+ // Same cluster in same clustering?
+ if(other.get(ringid) != segment.get(ringid)) {
+ continue;
+ }
+ Element ringSegment = entry.getValue().get(ringid);
+ SVGUtil.addCSSClass(ringSegment, CLR_HOVER_CLASS);
}
- Element ringSegment = entry.getValue().get(ringid);
- SVGUtil.addCSSClass(ringSegment, CLR_HOVER_CLASS);
}
- }
- else {
- //
- // UNPAIRED SEGMENT
- // highlight all ring segments in this clustering responsible for
- // unpaired
- // segment
- //
- // get the paired segments corresponding to the unpaired segment
- List<Segment> paired = segments.getPairedSegments(segment);
-
- for(Segment other : paired) {
- Element ringSegment = segmentToElements.get(other).get(ringid);
- SVGUtil.addCSSClass(ringSegment, CLR_HOVER_CLASS);
+ else {
+ //
+ // UNPAIRED SEGMENT
+ // highlight all ring segments in this clustering responsible for
+ // unpaired
+ // segment
+ //
+ // get the paired segments corresponding to the unpaired segment
+ List<Segment> paired = segments.getPairedSegments(segment);
+
+ for(Segment other : paired) {
+ Element ringSegment = segmentToElements.get(other).get(ringid);
+ SVGUtil.addCSSClass(ringSegment, CLR_HOVER_CLASS);
+ }
}
}
- }
- else {
- for(List<Element> elems : segmentToElements.values()) {
- for(Element current : elems) {
- SVGUtil.removeCSSClass(current, CLR_HOVER_CLASS);
+ else {
+ for(List<Element> elems : segmentToElements.values()) {
+ for(Element current : elems) {
+ SVGUtil.removeCSSClass(current, CLR_HOVER_CLASS);
+ }
}
}
}
- }
- protected void segmentClick(Segment segment, Event evt, boolean dblClick) {
- MouseEvent mouse = (MouseEvent) evt;
+ protected void segmentClick(Segment segment, Event evt, boolean dblClick) {
+ MouseEvent mouse = (MouseEvent) evt;
- // CTRL (add) pressed?
- boolean ctrl = false;
- if(mouse.getCtrlKey()) {
- ctrl = true;
- }
+ // CTRL (add) pressed?
+ boolean ctrl = false;
+ if(mouse.getCtrlKey()) {
+ ctrl = true;
+ }
- // Unselect others on double click
- if(dblClick) {
- policy.deselectAllSegments();
+ // Unselect others on double click
+ if(dblClick) {
+ policy.deselectAllSegments();
+ }
+ policy.select(segment, ctrl);
+ // update stylePolicy
+ context.getStyleResult().setStylingPolicy(policy);
+ // fire changed event to trigger redraw
+ context.getHierarchy().resultChanged(context.getStyleResult());
}
- policy.select(segment, ctrl);
- // update stylePolicy
- context.getStyleResult().setStylingPolicy(policy);
- // fire changed event to trigger redraw
- context.getHierarchy().resultChanged(context.getStyleResult());
- }
- /**
- * Proxy element to connect signals.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- private class SegmentListenerProxy implements EventListener {
/**
- * Mouse double click time window in milliseconds
+ * Proxy element to connect signals.
*
- * TODO: does Batik have double click events?
- */
- public static final int EVT_DBLCLICK_DELAY = 350;
-
- /**
- * Segment we are attached to
- */
- private Segment id;
-
- /**
- * Segment ring we are
- */
- private int ringid;
-
- /**
- * For detecting double clicks.
- */
- private long lastClick = 0;
-
- /**
- * Constructor.
+ * @author Erich Schubert
*
- * @param id Segment id
- * @param ringid Ring id
+ * @apiviz.exclude
*/
- public SegmentListenerProxy(Segment id, int ringid) {
- super();
- this.id = id;
- this.ringid = ringid;
- }
-
- @Override
- public void handleEvent(Event evt) {
- if(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE.equals(evt.getType())) {
- segmentHover(id, ringid, true);
+ private class SegmentListenerProxy implements EventListener {
+ /**
+ * Mouse double click time window in milliseconds
+ *
+ * TODO: does Batik have double click events?
+ */
+ public static final int EVT_DBLCLICK_DELAY = 350;
+
+ /**
+ * Segment we are attached to
+ */
+ private Segment id;
+
+ /**
+ * Segment ring we are
+ */
+ private int ringid;
+
+ /**
+ * For detecting double clicks.
+ */
+ private long lastClick = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param id Segment id
+ * @param ringid Ring id
+ */
+ public SegmentListenerProxy(Segment id, int ringid) {
+ super();
+ this.id = id;
+ this.ringid = ringid;
}
- if(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE.equals(evt.getType())) {
- segmentHover(id, ringid, false);
- }
- if(SVGConstants.SVG_CLICK_EVENT_TYPE.equals(evt.getType())) {
- // Check Double Click
- boolean dblClick = false;
- long time = java.util.Calendar.getInstance().getTimeInMillis();
- if(time - lastClick <= EVT_DBLCLICK_DELAY) {
- dblClick = true;
- }
- lastClick = time;
-
- segmentClick(id, evt, dblClick);
- }
- }
- }
-
- /**
- * Factory for visualizers for a circle segment
- *
- * @author Sascha Goldhofer
- *
- * @apiviz.stereotype factory
- * @apiviz.uses CircleSegmentsVisualizer oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
- /**
- * Constructor
- */
- public Factory() {
- super();
- this.thumbmask |= ThumbnailVisualization.ON_STYLE;
- }
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new CircleSegmentsVisualizer(task);
- }
+ @Override
+ public void handleEvent(Event evt) {
+ if(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE.equals(evt.getType())) {
+ segmentHover(id, ringid, true);
+ }
+ if(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE.equals(evt.getType())) {
+ segmentHover(id, ringid, false);
+ }
+ if(SVGConstants.SVG_CLICK_EVENT_TYPE.equals(evt.getType())) {
+ // Check Double Click
+ boolean dblClick = false;
+ long time = java.util.Calendar.getInstance().getTimeInMillis();
+ if(time - lastClick <= EVT_DBLCLICK_DELAY) {
+ dblClick = true;
+ }
+ lastClick = time;
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // If no comparison result found abort
- List<Segments> segments = ResultUtil.filterResults(result, Segments.class);
- for(Segments segmentResult : segments) {
- SegmentsStylingPolicy policy;
- List<SegmentsStylingPolicy> styles = ResultUtil.filterResults(segmentResult, SegmentsStylingPolicy.class);
- if (!styles.isEmpty()) {
- policy = styles.get(0);
- } else {
- policy = new SegmentsStylingPolicy(segmentResult);
- baseResult.getHierarchy().add(segmentResult, policy);
+ segmentClick(id, evt, dblClick);
}
- // create task for visualization
- final VisualizationTask task = new VisualizationTask(NAME, policy, null, this);
- task.width = 2.0;
- task.height = 2.0;
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
- baseResult.getHierarchy().add(segmentResult, task);
}
}
- };
+ }
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AbstractParallelVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AbstractParallelVisualization.java
index babcb864..a2039126 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AbstractParallelVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AbstractParallelVisualization.java
@@ -44,7 +44,7 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisualization;
*
* @param <NV> Vector type in relation
*/
-public abstract class AbstractParallelVisualization<NV extends NumberVector<?, ?>> extends AbstractVisualization {
+public abstract class AbstractParallelVisualization<NV extends NumberVector<?>> extends AbstractVisualization {
/**
* The current projection
*/
@@ -80,9 +80,8 @@ public abstract class AbstractParallelVisualization<NV extends NumberVector<?, ?
this.proj = task.getProj();
this.relation = task.getRelation();
- double ratio = task.width / task.height;
-
- margins = new double[] { 0.05 * StyleLibrary.SCALE, 0.1 * StyleLibrary.SCALE, 0.05 * StyleLibrary.SCALE, 0.4 * StyleLibrary.SCALE };
+ margins = new double[] { 0.05 * StyleLibrary.SCALE, 0.1 * StyleLibrary.SCALE, 0.05 * StyleLibrary.SCALE, 0.1 * StyleLibrary.SCALE };
+ double ratio = (task.width * StyleLibrary.SCALE - margins[0] - margins[2]) / (task.height * StyleLibrary.SCALE - margins[1] - margins[3]);
size = new double[] { ratio * StyleLibrary.SCALE, StyleLibrary.SCALE };
recalcAxisPositions();
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AxisReorderVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AxisReorderVisualization.java
new file mode 100644
index 00000000..6d77f592
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AxisReorderVisualization.java
@@ -0,0 +1,296 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.parallel;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Collection;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+import org.w3c.dom.events.Event;
+import org.w3c.dom.events.EventListener;
+import org.w3c.dom.events.EventTarget;
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.projector.ParallelPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGArrow;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+
+/**
+ * Interactive SVG-Elements for reordering the axes.
+ *
+ * @author Robert Rödler
+ * @author Erich Schubert
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
+ */
+public class AxisReorderVisualization extends AbstractVisFactory {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ private static final String NAME = "Dimension Ordering Tool";
+
+ /**
+ * Constructor, adhering to
+ */
+ public AxisReorderVisualization() {
+ super();
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(result, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, p, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ task.noexport = true;
+ task.thumbnail = false;
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+
+ /**
+ * Instance for a particular plot.
+ *
+ * @author Robert Rödler
+ * @author Erich Schubert
+ */
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> {
+ /**
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String SELECTDIMENSIONORDER = "SelectDimensionOrder";
+
+ /**
+ * CSS class for a tool button
+ */
+ public static final String SDO_BUTTON = "DObutton";
+
+ /**
+ * CSS class for a button border
+ */
+ public static final String SDO_BORDER = "DOborder";
+
+ /**
+ * CSS class for a button cross
+ */
+ public static final String SDO_ARROW = "DOarrow";
+
+ /**
+ * Currently selected dimension. Use -1 to not have a dimension selected.
+ */
+ private int selecteddim = -1;
+
+ /**
+ * Constructor.
+ *
+ * @param task VisualizationTask
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
+ context.addResultListener(this);
+ }
+
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
+ final int dim = proj.getVisibleDimensions();
+
+ final double controlsize = 0.025 * getSizeY();
+ final double buttonsize = 0.75 * controlsize;
+ final double padding = 0.125 * controlsize;
+ final double arrowsize = .75 * buttonsize;
+ final double ypos = getSizeY() + getMarginTop() * .5 + controlsize;
+ final double spacing = 0.9 * controlsize;
+
+ Element back = svgp.svgRect(-controlsize * .5, ypos, getSizeX() + controlsize, controlsize);
+ SVGUtil.addCSSClass(back, SELECTDIMENSIONORDER);
+ layer.appendChild(back);
+
+ if(selecteddim < 0) {
+ // Nothing selected
+ for(int i = 0; i < dim; i++) {
+ final double xpos = getVisibleAxisX(i);
+ if(i > 0) {
+ Element arrow = SVGArrow.makeArrow(svgp, SVGArrow.LEFT, xpos - spacing, ypos + controlsize * .5, arrowsize);
+ SVGUtil.addCSSClass(arrow, SDO_ARROW);
+ layer.appendChild(arrow);
+ Element button = svgp.svgRect(xpos - spacing - buttonsize * .5, ypos + padding, buttonsize, buttonsize);
+ SVGUtil.addCSSClass(button, SDO_BUTTON);
+ addEventListener(button, i, SVGArrow.LEFT);
+ layer.appendChild(button);
+ }
+ {
+ Element arrow = SVGArrow.makeArrow(svgp, SVGArrow.DOWN, xpos, ypos + controlsize * .5, arrowsize);
+ SVGUtil.addCSSClass(arrow, SDO_ARROW);
+ layer.appendChild(arrow);
+ Element button = svgp.svgRect(xpos - buttonsize * .5, ypos + padding, buttonsize, buttonsize);
+ SVGUtil.addCSSClass(button, SDO_BUTTON);
+ addEventListener(button, i, SVGArrow.DOWN);
+ layer.appendChild(button);
+ }
+ if(i < dim - 1) {
+ Element arrow = SVGArrow.makeArrow(svgp, SVGArrow.RIGHT, xpos + spacing, ypos + controlsize * .5, arrowsize);
+ SVGUtil.addCSSClass(arrow, SDO_ARROW);
+ layer.appendChild(arrow);
+ Element button = svgp.svgRect(xpos + spacing - buttonsize * .5, ypos + padding, buttonsize, buttonsize);
+ SVGUtil.addCSSClass(button, SDO_BUTTON);
+ addEventListener(button, i, SVGArrow.RIGHT);
+ layer.appendChild(button);
+ }
+ }
+ }
+ else {
+ for(int i = 0; i < dim; i++) {
+ {
+ Element arrow = SVGArrow.makeArrow(svgp, SVGArrow.DOWN, getVisibleAxisX(i), ypos + controlsize * .5, arrowsize);
+ SVGUtil.addCSSClass(arrow, SDO_ARROW);
+ layer.appendChild(arrow);
+ Element button = svgp.svgRect(getVisibleAxisX(i) - buttonsize * .5, ypos + padding, buttonsize, buttonsize);
+ SVGUtil.addCSSClass(button, SDO_BUTTON);
+ addEventListener(button, i, SVGArrow.DOWN);
+ layer.appendChild(button);
+ }
+ if(i > 0.) {
+ Element arrow = SVGArrow.makeArrow(svgp, SVGArrow.UP, getVisibleAxisX(i - .5), ypos + controlsize * .5, arrowsize);
+ SVGUtil.addCSSClass(arrow, SDO_ARROW);
+ layer.appendChild(arrow);
+ Element button = svgp.svgRect(getVisibleAxisX(i - .5) - buttonsize * .5, ypos + padding, buttonsize, buttonsize);
+ SVGUtil.addCSSClass(button, SDO_BUTTON);
+ addEventListener(button, i, SVGArrow.UP);
+ layer.appendChild(button);
+ }
+ }
+ }
+ }
+
+ /**
+ * Add an event listener to the Element
+ *
+ * @param tag Element to add the listener
+ * @param i represented axis
+ */
+ private void addEventListener(final Element tag, final int i, final SVGArrow.Direction j) {
+ EventTarget targ = (EventTarget) tag;
+ targ.addEventListener(SVGConstants.SVG_EVENT_CLICK, new EventListener() {
+ @Override
+ public void handleEvent(Event evt) {
+ if(selecteddim < 0) {
+ switch(j){
+ case DOWN:
+ selecteddim = i;
+ break;
+ case LEFT:
+ int prev = i - 1;
+ while(prev >= 0 && !proj.isAxisVisible(prev)) {
+ prev -= 1;
+ }
+ proj.swapAxes(i, prev);
+ break;
+ case RIGHT:
+ int next = i + 1;
+ while(next < proj.getInputDimensionality() - 1 && !proj.isAxisVisible(next)) {
+ next += 1;
+ }
+ proj.swapAxes(i, next);
+ break;
+ default:
+ break;
+ }
+ }
+ else {
+ switch(j){
+ case DOWN:
+ proj.swapAxes(selecteddim, i);
+ selecteddim = -1;
+ break;
+ case UP:
+ if(selecteddim != i) {
+ proj.moveAxis(selecteddim, i);
+ }
+ selecteddim = -1;
+ break;
+ default:
+ break;
+ }
+ }
+ // Notify
+ context.getHierarchy().resultChanged(proj);
+ }
+ }, false);
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ if(!svgp.getCSSClassManager().contains(SELECTDIMENSIONORDER)) {
+ CSSClass cls = new CSSClass(this, SELECTDIMENSIONORDER);
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.1);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLUE_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ if(!svgp.getCSSClassManager().contains(SDO_BORDER)) {
+ CSSClass cls = new CSSClass(this, SDO_BORDER);
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_GREY_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) / 3.0);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ if(!svgp.getCSSClassManager().contains(SDO_BUTTON)) {
+ CSSClass cls = new CSSClass(this, SDO_BUTTON);
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.01);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_GREY_VALUE);
+ cls.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ if(!svgp.getCSSClassManager().contains(SDO_ARROW)) {
+ CSSClass cls = new CSSClass(this, SDO_ARROW);
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_DARKGREY_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) / 3);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AxisVisibilityVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AxisVisibilityVisualization.java
new file mode 100644
index 00000000..ea68d660
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/AxisVisibilityVisualization.java
@@ -0,0 +1,293 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.parallel;
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Collection;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+import org.w3c.dom.events.Event;
+import org.w3c.dom.events.EventListener;
+import org.w3c.dom.events.EventTarget;
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.projector.ParallelPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPath;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+
+/**
+ * Layer for controlling axis visbility in parallel coordinates.
+ *
+ * @author Robert Rödler
+ * @author Erich Schubert
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
+ */
+public class AxisVisibilityVisualization extends AbstractVisFactory {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ private static final String NAME = "Axis Visibility";
+
+ /**
+ * Constructor, adhering to
+ */
+ public AxisVisibilityVisualization() {
+ super();
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(result, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, p, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ task.noexport = true;
+ task.thumbnail = false;
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+
+ /**
+ * Instance for a particular data set.
+ *
+ * @author Robert Rödler
+ * @author Erich Schubert
+ */
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> {
+ /**
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String SELECTAXISVISIBILITY = "SelectAxisVisibility";
+
+ /**
+ * CSS class for a tool button
+ */
+ public static final String SAV_BUTTON = "SAVbutton";
+
+ /**
+ * CSS class for a button border
+ */
+ public static final String SAV_BORDER = "SAVborder";
+
+ /**
+ * CSS class for a button cross
+ */
+ public static final String SAV_CROSS = "SAVbuttoncross";
+
+ /**
+ * Active area size
+ */
+ double controlsize;
+
+ /**
+ * Button size
+ */
+ double buttonsize;
+
+ /**
+ * Vertical position
+ */
+ double ypos;
+
+ /**
+ * Constructor.
+ *
+ * @param task VisualizationTask
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
+ context.addResultListener(this);
+ }
+
+ @Override
+ protected void redraw() {
+ final int dim = proj.getInputDimensionality();
+ addCSSClasses(svgp);
+ controlsize = 0.025 * getSizeY();
+ buttonsize = 0.75 * controlsize;
+ ypos = getSizeY() + getMarginTop() * .5;
+
+ // Background
+ Element back = svgp.svgRect(-controlsize * .5, ypos - controlsize * .5 + buttonsize * .5, getSizeX() + controlsize, controlsize);
+ SVGUtil.addCSSClass(back, SELECTAXISVISIBILITY);
+ layer.appendChild(back);
+
+ // Previous visible dimension.
+ for(int i = 0, hidden = 0, vax = 0; i <= dim; i++) {
+ if(i < dim && !proj.isAxisVisible(i)) {
+ hidden += 1;
+ continue;
+ }
+ // Add button for showing hidden dimensions:
+ if(hidden > 0) {
+ makeButtonsForHidden(vax, i - hidden, hidden, dim);
+ hidden = 0;
+ }
+ // Add buttons for current dimension
+ if(i < dim) {
+ makeButtonForVisible(i, vax);
+ vax++;
+ }
+ }
+ }
+
+ /**
+ * Make a button for a visible axis
+ *
+ * @param anum Axis number
+ * @param apos Axis position in plot
+ */
+ protected void makeButtonForVisible(int anum, int apos) {
+ final double xpos = getVisibleAxisX(apos) - buttonsize * .5;
+
+ Element border = svgp.svgRect(xpos, ypos, buttonsize, buttonsize);
+ SVGUtil.addCSSClass(border, SAV_BORDER);
+ layer.appendChild(border);
+
+ SVGPath path = new SVGPath();
+ final double qs = controlsize * .5;
+ final double cs = controlsize * .125;
+ path.moveTo(xpos + cs, ypos + cs);
+ path.relativeLineTo(qs, qs);
+ path.relativeMoveTo(0, -qs);
+ path.relativeLineTo(-qs, qs);
+ Element cross = path.makeElement(svgp);
+ SVGUtil.addCSSClass(cross, SAV_CROSS);
+ layer.appendChild(cross);
+
+ Element rect = svgp.svgRect(xpos, ypos, buttonsize, buttonsize);
+ SVGUtil.addCSSClass(rect, SAV_BUTTON);
+ addEventListener(rect, anum);
+ layer.appendChild(rect);
+ }
+
+ /**
+ * Insert buttons for hidden dimensions.
+ *
+ * @param vnum Column number (= next visible axis number)
+ * @param first First invisible axis
+ * @param count Number of invisible axes
+ * @param dim Number of total dimensions
+ */
+ private void makeButtonsForHidden(final int vnum, final int first, final int count, final int dim) {
+ final double lpos, rpos;
+ if(vnum == 0) {
+ lpos = -getMarginLeft();
+ }
+ else {
+ lpos = getVisibleAxisX(vnum - 1);
+ }
+ if(first + count + 1 >= dim) {
+ rpos = getWidth() + getMarginLeft();
+ }
+ else {
+ rpos = getVisibleAxisX(vnum);
+ }
+ final double step = (rpos - lpos) / (count + 1.0);
+ for(int j = 0; j < count; j++) {
+ final double apos = lpos + (j + 1) * step - buttonsize * .5;
+ Element border = svgp.svgRect(apos, ypos, buttonsize, buttonsize);
+ SVGUtil.addCSSClass(border, SAV_BORDER);
+ layer.appendChild(border);
+
+ Element rect = svgp.svgRect(apos, ypos, buttonsize, buttonsize);
+ SVGUtil.addCSSClass(rect, SAV_BUTTON);
+ addEventListener(rect, first + j);
+ layer.appendChild(rect);
+ }
+ }
+
+ /**
+ * Add an event listener to the Element
+ *
+ * @param tag Element to add the listener
+ * @param axis Axis number (including hidden axes)
+ */
+ private void addEventListener(final Element tag, final int axis) {
+ EventTarget targ = (EventTarget) tag;
+ targ.addEventListener(SVGConstants.SVG_EVENT_CLICK, new EventListener() {
+ @Override
+ public void handleEvent(Event evt) {
+ if(proj.getVisibleDimensions() > 2) {
+ proj.toggleAxisVisible(axis);
+ context.getHierarchy().resultChanged(proj);
+ }
+ }
+ }, false);
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ if(!svgp.getCSSClassManager().contains(SELECTAXISVISIBILITY)) {
+ CSSClass cls = new CSSClass(this, SELECTAXISVISIBILITY);
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.1);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLUE_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ if(!svgp.getCSSClassManager().contains(SAV_BORDER)) {
+ CSSClass cls = new CSSClass(this, SAV_BORDER);
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_GREY_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) * .5);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ if(!svgp.getCSSClassManager().contains(SAV_BUTTON)) {
+ CSSClass cls = new CSSClass(this, SAV_BUTTON);
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.01);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_GREY_VALUE);
+ cls.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ if(!svgp.getCSSClassManager().contains(SAV_CROSS)) {
+ CSSClass cls = new CSSClass(this, SAV_CROSS);
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) * .75);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/LineVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/LineVisualization.java
index 8269d8fe..e9726ba6 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/LineVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/LineVisualization.java
@@ -53,163 +53,164 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
* Generates data lines.
*
* @author Robert Rödler
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class LineVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> implements DataStoreListener {
- /**
- * Generic tags to indicate the type of element. Used in IDs, CSS-Classes etc.
- */
- public static final String DATALINE = "Dataline";
-
+public class LineVisualization extends AbstractVisFactory {
/**
- * Sample we visualize.
+ * A short name characterizing this Visualizer.
*/
- private SamplingResult sample;
+ public static final String NAME = "Data lines";
/**
- * Constructor.
- *
- * @param task VisualizationTask
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- public LineVisualization(VisualizationTask task) {
- super(task);
- this.sample = ResultUtil.getSamplingResult(relation);
- context.addResultListener(this);
- context.addDataStoreListener(this);
- incrementalRedraw();
+ public LineVisualization() {
+ super();
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_STYLE;
}
@Override
- public void destroy() {
- context.removeDataStoreListener(this);
- context.removeResultListener(this);
- super.destroy();
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- public void resultChanged(Result current) {
- super.resultChanged(current);
- if(current == sample || current == context.getStyleResult()) {
- synchronizedRedraw();
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(result, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, p.getRelation(), p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA;
+ baseResult.getHierarchy().add(p, task);
}
}
- @Override
- protected void redraw() {
- StylingPolicy sp = context.getStyleResult().getStylingPolicy();
- addCSSClasses(svgp, sp);
+ /**
+ * Instance for a particular data set.
+ *
+ * @author Robert Rödler
+ */
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> implements DataStoreListener {
+ /**
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String DATALINE = "Dataline";
- DBIDIter ids = sample.getSample().iter();
- if(ids == null || !ids.valid()) {
- ids = relation.iterDBIDs();
+ /**
+ * Sample we visualize.
+ */
+ private SamplingResult sample;
+
+ /**
+ * Constructor.
+ *
+ * @param task VisualizationTask
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.sample = ResultUtil.getSamplingResult(relation);
+ context.addResultListener(this);
+ context.addDataStoreListener(this);
+ incrementalRedraw();
+ }
+
+ @Override
+ public void destroy() {
+ context.removeDataStoreListener(this);
+ context.removeResultListener(this);
+ super.destroy();
+ }
+
+ @Override
+ public void resultChanged(Result current) {
+ super.resultChanged(current);
+ if(current == sample || current == context.getStyleResult()) {
+ synchronizedRedraw();
+ }
}
- if(sp instanceof ClassStylingPolicy) {
- ClassStylingPolicy csp = (ClassStylingPolicy) sp;
- for(int c = csp.getMinStyle(); c < csp.getMaxStyle(); c++) {
- String key = DATALINE + "_" + c;
- for(DBIDIter iter = csp.iterateClass(c); iter.valid(); iter.advance()) {
- if(!sample.getSample().contains(iter)) {
- continue; // TODO: can we test more efficiently than this?
+
+ @Override
+ protected void redraw() {
+ StylingPolicy sp = context.getStyleResult().getStylingPolicy();
+ addCSSClasses(svgp, sp);
+
+ DBIDIter ids = sample.getSample().iter();
+ if(ids == null || !ids.valid()) {
+ ids = relation.iterDBIDs();
+ }
+ if(sp instanceof ClassStylingPolicy) {
+ ClassStylingPolicy csp = (ClassStylingPolicy) sp;
+ for(int c = csp.getMinStyle(); c < csp.getMaxStyle(); c++) {
+ String key = DATALINE + "_" + c;
+ for(DBIDIter iter = csp.iterateClass(c); iter.valid(); iter.advance()) {
+ if(!sample.getSample().contains(iter)) {
+ continue; // TODO: can we test more efficiently than this?
+ }
+ SVGPath path = new SVGPath();
+ double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(iter));
+ for(int i = 0; i < yPos.length; i++) {
+ path.drawTo(getVisibleAxisX(i), yPos[i]);
+ }
+ Element line = path.makeElement(svgp);
+ SVGUtil.addCSSClass(line, key);
+ layer.appendChild(line);
}
+ }
+ }
+ else {
+ for(; ids.valid(); ids.advance()) {
SVGPath path = new SVGPath();
- double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(iter));
+ double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(ids));
for(int i = 0; i < yPos.length; i++) {
path.drawTo(getVisibleAxisX(i), yPos[i]);
}
Element line = path.makeElement(svgp);
- SVGUtil.addCSSClass(line, key);
+ SVGUtil.addCSSClass(line, DATALINE);
+ // assign color
+ line.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_STROKE_PROPERTY + ":" + SVGUtil.colorToString(sp.getColorForDBID(ids)));
layer.appendChild(line);
}
}
}
- else {
- for(; ids.valid(); ids.advance()) {
- SVGPath path = new SVGPath();
- double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(ids));
- for(int i = 0; i < yPos.length; i++) {
- path.drawTo(getVisibleAxisX(i), yPos[i]);
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp, StylingPolicy sp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final LineStyleLibrary lines = style.lines();
+ final double width = .5 * style.getLineWidth(StyleLibrary.PLOT);
+ if(sp instanceof ClassStylingPolicy) {
+ ClassStylingPolicy csp = (ClassStylingPolicy) sp;
+ for(int i = csp.getMinStyle(); i < csp.getMaxStyle(); i++) {
+ String key = DATALINE + "_" + i;
+ if(!svgp.getCSSClassManager().contains(key)) {
+ CSSClass cls = new CSSClass(this, key);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ lines.formatCSSClass(cls, i, width);
+ svgp.addCSSClassOrLogError(cls);
+ }
}
- Element line = path.makeElement(svgp);
- SVGUtil.addCSSClass(line, DATALINE);
- // assign color
- line.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_STROKE_PROPERTY + ":" + SVGUtil.colorToString(sp.getColorForDBID(ids)));
- layer.appendChild(line);
}
- }
- }
-
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp, StylingPolicy sp) {
- final StyleLibrary style = context.getStyleLibrary();
- final LineStyleLibrary lines = style.lines();
- final double width = .5 * style.getLineWidth(StyleLibrary.PLOT);
- if(sp instanceof ClassStylingPolicy) {
- ClassStylingPolicy csp = (ClassStylingPolicy) sp;
- for(int i = csp.getMinStyle(); i < csp.getMaxStyle(); i++) {
- String key = DATALINE + "_" + i;
- if(!svgp.getCSSClassManager().contains(key)) {
- CSSClass cls = new CSSClass(this, key);
+ else {
+ // Class for the distance function
+ if(!svgp.getCSSClassManager().contains(DATALINE)) {
+ CSSClass cls = new CSSClass(this, DATALINE);
cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- lines.formatCSSClass(cls, i, width);
+ lines.formatCSSClass(cls, -1, width);
svgp.addCSSClassOrLogError(cls);
}
}
- }
- else {
- // Class for the distance function
- if(!svgp.getCSSClassManager().contains(DATALINE)) {
- CSSClass cls = new CSSClass(this, DATALINE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- lines.formatCSSClass(cls, -1, width);
- svgp.addCSSClassOrLogError(cls);
- }
- }
- svgp.updateStyleElement();
- }
-
- /**
- * Factory for axis visualizations
- *
- * @author Robert Rödler
- *
- * @apiviz.stereotype factory
- * @apiviz.uses LineVisualization oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
- /**
- * A short name characterizing this Visualizer.
- */
- private static final String NAME = "Data lines";
-
- /**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
- */
- public Factory() {
- super();
- thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_STYLE;
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new LineVisualization(task);
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(result, ParallelPlotProjector.class);
- for(ParallelPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, p.getRelation(), p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
- baseResult.getHierarchy().add(p, task);
- }
+ svgp.updateStyleElement();
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/ParallelAxisVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/ParallelAxisVisualization.java
index 6209db6b..00ef60ed 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/ParallelAxisVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/ParallelAxisVisualization.java
@@ -32,10 +32,10 @@ import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClassManager.CSSNamingConflict;
@@ -52,144 +52,150 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
*
* @author Robert Rödler
*
- * @apiviz.uses SVGSimpleLinearAxis
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-// TODO: split into interactive / non-interactive parts?
-public class ParallelAxisVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> {
+public class ParallelAxisVisualization extends AbstractVisFactory {
/**
- * Axis label class
+ * A short name characterizing this Visualizer.
*/
- public final static String AXIS_LABEL = "paxis-label";
-
- /**
- * Clickable area for the axis.
- */
- public final static String INVERTEDAXIS = "paxis-button";
+ private static final String NAME = "Parallel Axes";
/**
* Constructor.
- *
- * @param task VisualizationTask
*/
- public ParallelAxisVisualization(VisualizationTask task) {
- super(task);
- incrementalRedraw();
- context.addResultListener(this);
+ public ParallelAxisVisualization() {
+ super();
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
- final int dim = proj.getVisibleDimensions();
- try {
- for(int i = 0; i < dim; i++) {
- final int truedim = proj.getDimForVisibleAxis(i);
- final double axisX = getVisibleAxisX(i);
- if(!proj.isAxisInverted(i)) {
- SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getAxisScale(i), axisX, getSizeY(), axisX, 0, SVGSimpleLinearAxis.LabelStyle.ENDLABEL, context.getStyleLibrary());
- }
- else {
- SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getAxisScale(i), axisX, 0, axisX, getSizeY(), SVGSimpleLinearAxis.LabelStyle.ENDLABEL, context.getStyleLibrary());
- }
- // Get axis label
- final String label = DatabaseUtil.getColumnLabel(relation, truedim + 1);
- // Add axis label
- Element text = svgp.svgText(axisX, -.7 * getMarginTop(), label);
- SVGUtil.setCSSClass(text, AXIS_LABEL);
- layer.appendChild(text);
- // TODO: Split into background + clickable layer.
- Element button = svgp.svgRect(axisX - getAxisSep() * .475, -getMarginTop(), .95 * getAxisSep(), .5 * getMarginTop());
- SVGUtil.setCSSClass(button, INVERTEDAXIS);
- addEventListener(button, truedim);
- layer.appendChild(button);
- }
- }
- catch(CSSNamingConflict e) {
- throw new RuntimeException("Conflict in CSS naming for axes.", e);
- }
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
- /**
- * Add the main CSS classes.
- *
- * @param svgp Plot to draw to
- */
- private void addCSSClasses(SVGPlot svgp) {
- final StyleLibrary style = context.getStyleLibrary();
- if(!svgp.getCSSClassManager().contains(AXIS_LABEL)) {
- CSSClass cls = new CSSClass(this, AXIS_LABEL);
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getTextColor(StyleLibrary.AXIS_LABEL));
- cls.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, style.getFontFamily(StyleLibrary.AXIS_LABEL));
- cls.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, style.getTextSize(StyleLibrary.AXIS_LABEL));
- cls.setStatement(SVGConstants.CSS_TEXT_ANCHOR_PROPERTY, SVGConstants.SVG_MIDDLE_VALUE);
- svgp.addCSSClassOrLogError(cls);
- }
- if(!svgp.getCSSClassManager().contains(INVERTEDAXIS)) {
- CSSClass cls = new CSSClass(this, INVERTEDAXIS);
- cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.1);
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_GREY_VALUE);
- svgp.addCSSClassOrLogError(cls);
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(result, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, p, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_BACKGROUND;
+ baseResult.getHierarchy().add(p, task);
}
}
- /**
- * Add an event listener to the Element
- *
- * @param tag Element to add the listener
- * @param i Tool number for the Element
- */
- private void addEventListener(final Element tag, final int i) {
- EventTarget targ = (EventTarget) tag;
- targ.addEventListener(SVGConstants.SVG_EVENT_CLICK, new EventListener() {
- @Override
- public void handleEvent(Event evt) {
- proj.toggleDimInverted(i);
- context.getHierarchy().resultChanged(proj);
- }
- }, false);
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return true;
}
/**
- * Factory for axis visualizations
+ * Instance.
*
* @author Robert Rödler
*
- * @apiviz.stereotype factory
- * @apiviz.uses ParallelAxisVisualization oneway - - «create»
+ * @apiviz.uses SVGSimpleLinearAxis
*/
- public static class Factory extends AbstractVisFactory {
+ // TODO: split into interactive / non-interactive parts?
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> {
+ /**
+ * Axis label class.
+ */
+ public static final String AXIS_LABEL = "paxis-label";
+
/**
- * A short name characterizing this Visualizer.
+ * Clickable area for the axis.
*/
- private static final String NAME = "Parallel Axes";
+ public static final String INVERTEDAXIS = "paxis-button";
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Constructor.
+ *
+ * @param task VisualizationTask
*/
- public Factory() {
- super();
+ public Instance(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
+ context.addResultListener(this);
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ParallelAxisVisualization(task);
+ protected void redraw() {
+ addCSSClasses(svgp);
+ final int dim = proj.getInputDimensionality();
+ try {
+ for(int i = 0, vdim = 0; i < dim; i++) {
+ if(!proj.isAxisVisible(i)) {
+ continue;
+ }
+ final int truedim = proj.getDimForVisibleAxis(vdim);
+ final double axisX = getVisibleAxisX(vdim);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ if(!proj.isAxisInverted(vdim)) {
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getAxisScale(i), axisX, getSizeY(), axisX, 0, SVGSimpleLinearAxis.LabelStyle.ENDLABEL, style);
+ }
+ else {
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getAxisScale(i), axisX, 0, axisX, getSizeY(), SVGSimpleLinearAxis.LabelStyle.ENDLABEL, style);
+ }
+ // Get axis label
+ final String label = RelationUtil.getColumnLabel(relation, truedim);
+ // Add axis label
+ Element text = svgp.svgText(axisX, -.7 * getMarginTop(), label);
+ SVGUtil.setCSSClass(text, AXIS_LABEL);
+ SVGUtil.setAtt(text, SVGConstants.SVG_TEXT_LENGTH_ATTRIBUTE, getAxisSep() * 0.95);
+ SVGUtil.setAtt(text, SVGConstants.SVG_LENGTH_ADJUST_ATTRIBUTE, SVGConstants.SVG_SPACING_AND_GLYPHS_VALUE);
+ layer.appendChild(text);
+ // TODO: Split into background + clickable layer.
+ Element button = svgp.svgRect(axisX - getAxisSep() * .475, -getMarginTop(), .95 * getAxisSep(), .5 * getMarginTop());
+ SVGUtil.setCSSClass(button, INVERTEDAXIS);
+ addEventListener(button, truedim);
+ layer.appendChild(button);
+ vdim++;
+ }
+ }
+ catch(CSSNamingConflict e) {
+ throw new RuntimeException("Conflict in CSS naming for axes.", e);
+ }
}
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(result, ParallelPlotProjector.class);
- for(ParallelPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, p, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_BACKGROUND);
- baseResult.getHierarchy().add(p, task);
+ /**
+ * Add the main CSS classes.
+ *
+ * @param svgp Plot to draw to
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ if(!svgp.getCSSClassManager().contains(AXIS_LABEL)) {
+ CSSClass cls = new CSSClass(this, AXIS_LABEL);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getTextColor(StyleLibrary.AXIS_LABEL));
+ cls.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, style.getFontFamily(StyleLibrary.AXIS_LABEL));
+ cls.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, style.getTextSize(StyleLibrary.AXIS_LABEL));
+ cls.setStatement(SVGConstants.CSS_TEXT_ANCHOR_PROPERTY, SVGConstants.SVG_MIDDLE_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ if(!svgp.getCSSClassManager().contains(INVERTEDAXIS)) {
+ CSSClass cls = new CSSClass(this, INVERTEDAXIS);
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.1);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_GREY_VALUE);
+ svgp.addCSSClassOrLogError(cls);
}
}
- @Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return true;
+ /**
+ * Add an event listener to the Element.
+ *
+ * @param tag Element to add the listener
+ * @param i Tool number for the Element
+ */
+ private void addEventListener(final Element tag, final int i) {
+ EventTarget targ = (EventTarget) tag;
+ targ.addEventListener(SVGConstants.SVG_EVENT_CLICK, new EventListener() {
+ @Override
+ public void handleEvent(Event evt) {
+ proj.toggleDimInverted(i);
+ context.getHierarchy().resultChanged(proj);
+ }
+ }, false);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterOutlineVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterOutlineVisualization.java
index f34b7514..47a6d9a8 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterOutlineVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterOutlineVisualization.java
@@ -34,7 +34,7 @@ import de.lmu.ifi.dbs.elki.data.Clustering;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.model.Model;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
@@ -56,203 +56,206 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.AbstractParallelVi
* Generates a SVG-Element that visualizes the area covered by a cluster.
*
* @author Robert Rödler
- * @author Erich Schubert
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class ClusterOutlineVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> implements DataStoreListener {
+// TODO: make parameterizable: rounded.
+public class ClusterOutlineVisualization extends AbstractVisFactory {
/**
- * Generic tags to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * A short name characterizing this Visualizer.
*/
- public static final String CLUSTERAREA = "Clusteroutline";
+ public static final String NAME = "Cluster Outline";
/**
- * The result we visualize
+ * Currently unused option to enable/disable rounding
*/
- private Clustering<Model> clustering;
+ public static final OptionID ROUNDED_ID = new OptionID("parallel.clusteroutline.rounded", "Draw lines rounded");
/**
- * Flag for using rounded shapes
+ * Currently, always enabled.
*/
- boolean rounded = true;
+ private boolean rounded = true;
/**
- * Constructor.
- *
- * @param task VisualizationTask
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- public ClusterOutlineVisualization(VisualizationTask task, boolean rounded) {
- super(task);
- this.clustering = task.getResult();
- this.rounded = rounded;
- context.addDataStoreListener(this);
- context.addResultListener(this);
- incrementalRedraw();
+ public ClusterOutlineVisualization() {
+ super();
}
@Override
- public void destroy() {
- context.removeDataStoreListener(this);
- context.removeResultListener(this);
- super.destroy();
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task, rounded);
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
- int dim = proj.getVisibleDimensions();
-
- DoubleMinMax[] mms = DoubleMinMax.newArray(dim);
- DoubleMinMax[] midmm = DoubleMinMax.newArray(dim - 1);
-
- Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
- for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
- Cluster<?> clus = ci.next();
- for(int i = 0; i < dim; i++) {
- mms[i].reset();
- if(i < dim - 1) {
- midmm[i].reset();
- }
- }
-
- // Process points
- // TODO: do this just once, cache the result somewhere appropriately?
- for(DBID objId : clus.getIDs()) {
- double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(objId));
- for(int i = 0; i < dim; i++) {
- mms[i].put(yPos[i]);
- if(i > 0) {
- midmm[i - 1].put((yPos[i] + yPos[i - 1]) / 2.);
- }
- }
- }
-
- SVGPath path = new SVGPath();
- if(!rounded) {
- // Straight lines
- for(int i = 0; i < dim; i++) {
- path.drawTo(getVisibleAxisX(i), mms[i].getMax());
- if(i < dim - 1) {
- path.drawTo(getVisibleAxisX(i + .5), midmm[i].getMax());
- }
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find clusterings we can visualize:
+ Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
+ for(Clustering<?> c : clusterings) {
+ if(c.getAllClusters().size() > 0) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA - 1;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(c, task);
+ baseResult.getHierarchy().add(p, task);
}
- for(int i = dim - 1; i >= 0; i--) {
- if(i < dim - 1) {
- path.drawTo(getVisibleAxisX(i + .5), midmm[i].getMin());
- }
- path.drawTo(getVisibleAxisX(i), mms[i].getMin());
- }
- path.close();
- }
- else {
- // Maxima
- path.drawTo(getVisibleAxisX(0), mms[0].getMax());
- for(int i = 1; i < dim; i++) {
- path.quadTo(getVisibleAxisX(i - .5), midmm[i - 1].getMax(), getVisibleAxisX(i), mms[i].getMax());
- }
- // Minima
- path.drawTo(getVisibleAxisX(dim - 1), mms[dim - 1].getMin());
- for(int i = dim - 1; i > 0; i--) {
- path.quadTo(getVisibleAxisX(i - .5), midmm[i - 1].getMin(), getVisibleAxisX(i - 1), mms[i - 1].getMin());
- }
- path.close();
}
-
- Element intervals = path.makeElement(svgp);
- SVGUtil.addCSSClass(intervals, CLUSTERAREA + cnum);
- layer.appendChild(intervals);
}
}
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- if(!svgp.getCSSClassManager().contains(CLUSTERAREA)) {
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
- int clusterID = 0;
-
- for(@SuppressWarnings("unused")
- Cluster<?> cluster : clustering.getAllClusters()) {
- CSSClass cls = new CSSClass(this, CLUSTERAREA + clusterID);
- // cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY,
- // context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT) / 2.0);
- cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.5);
- final String color;
- if(clustering.getAllClusters().size() == 1) {
- color = SVGConstants.CSS_BLACK_VALUE;
- }
- else {
- color = colors.getColor(clusterID);
- }
- // cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
-
- svgp.addCSSClassOrLogError(cls);
- clusterID++;
- }
- }
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
}
/**
- * Factory for axis visualizations
+ * Instance
*
* @author Robert Rödler
- *
- * @apiviz.stereotype factory
- * @apiviz.uses ClusterOutlineVisualization oneway - - «create»
+ * @author Erich Schubert
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> implements DataStoreListener {
/**
- * A short name characterizing this Visualizer.
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
*/
- private static final String NAME = "Cluster Outline";
+ public static final String CLUSTERAREA = "Clusteroutline";
/**
- * Currently unused option to enable/disable rounding
+ * The result we visualize
*/
- public static final OptionID ROUNDED_ID = OptionID.getOrCreateOptionID("parallel.clusteroutline.rounded", "Draw lines rounded");
+ private Clustering<Model> clustering;
/**
- * Currently, always enabled.
+ * Flag for using rounded shapes
*/
- private boolean rounded = true;
+ boolean rounded = true;
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Constructor.
+ *
+ * @param task VisualizationTask
*/
- public Factory() {
- super();
+ public Instance(VisualizationTask task, boolean rounded) {
+ super(task);
+ this.clustering = task.getResult();
+ this.rounded = rounded;
+ context.addDataStoreListener(this);
+ context.addResultListener(this);
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ClusterOutlineVisualization(task, rounded);
+ public void destroy() {
+ context.removeDataStoreListener(this);
+ context.removeResultListener(this);
+ super.destroy();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // Find clusterings we can visualize:
- Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
- for(Clustering<?> c : clusterings) {
- if(c.getAllClusters().size() > 0) {
- Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
- for(ParallelPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 1);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(c, task);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ addCSSClasses(svgp);
+ int dim = proj.getVisibleDimensions();
+
+ DoubleMinMax[] mms = DoubleMinMax.newArray(dim);
+ DoubleMinMax[] midmm = DoubleMinMax.newArray(dim - 1);
+
+ Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
+ for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
+ Cluster<?> clus = ci.next();
+ for(int i = 0; i < dim; i++) {
+ mms[i].reset();
+ if(i < dim - 1) {
+ midmm[i].reset();
+ }
+ }
+
+ // Process points
+ // TODO: do this just once, cache the result somewhere appropriately?
+ for(DBIDIter id = clus.getIDs().iter(); id.valid(); id.advance()) {
+ double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(id));
+ for(int i = 0; i < dim; i++) {
+ mms[i].put(yPos[i]);
+ if(i > 0) {
+ midmm[i - 1].put((yPos[i] + yPos[i - 1]) / 2.);
+ }
+ }
+ }
+
+ SVGPath path = new SVGPath();
+ if(!rounded) {
+ // Straight lines
+ for(int i = 0; i < dim; i++) {
+ path.drawTo(getVisibleAxisX(i), mms[i].getMax());
+ if(i < dim - 1) {
+ path.drawTo(getVisibleAxisX(i + .5), midmm[i].getMax());
+ }
+ }
+ for(int i = dim - 1; i >= 0; i--) {
+ if(i < dim - 1) {
+ path.drawTo(getVisibleAxisX(i + .5), midmm[i].getMin());
+ }
+ path.drawTo(getVisibleAxisX(i), mms[i].getMin());
}
+ path.close();
}
+ else {
+ // Maxima
+ path.drawTo(getVisibleAxisX(0), mms[0].getMax());
+ for(int i = 1; i < dim; i++) {
+ path.quadTo(getVisibleAxisX(i - .5), midmm[i - 1].getMax(), getVisibleAxisX(i), mms[i].getMax());
+ }
+ // Minima
+ path.drawTo(getVisibleAxisX(dim - 1), mms[dim - 1].getMin());
+ for(int i = dim - 1; i > 0; i--) {
+ path.quadTo(getVisibleAxisX(i - .5), midmm[i - 1].getMin(), getVisibleAxisX(i - 1), mms[i - 1].getMin());
+ }
+ path.close();
+ }
+
+ Element intervals = path.makeElement(svgp);
+ SVGUtil.addCSSClass(intervals, CLUSTERAREA + cnum);
+ layer.appendChild(intervals);
}
}
- @Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ if(!svgp.getCSSClassManager().contains(CLUSTERAREA)) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+ int clusterID = 0;
+
+ for(@SuppressWarnings("unused")
+ Cluster<?> cluster : clustering.getAllClusters()) {
+ CSSClass cls = new CSSClass(this, CLUSTERAREA + clusterID);
+ // cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY,
+ // context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT) / 2.0);
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.5);
+ final String color;
+ if(clustering.getAllClusters().size() == 1) {
+ color = SVGConstants.CSS_BLACK_VALUE;
+ }
+ else {
+ color = colors.getColor(clusterID);
+ }
+ // cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
+
+ svgp.addCSSClassOrLogError(cls);
+ clusterID++;
+ }
+ }
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterParallelMeanVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterParallelMeanVisualization.java
index 1808c241..ad3ed503 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterParallelMeanVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/cluster/ClusterParallelMeanVisualization.java
@@ -53,162 +53,166 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.AbstractParallelVi
* Generates a SVG-Element that visualizes cluster means.
*
* @author Robert Rödler
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class ClusterParallelMeanVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> implements DataStoreListener {
+public class ClusterParallelMeanVisualization extends AbstractVisFactory {
/**
- * Generic tags to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * A short name characterizing this Visualizer.
*/
- public static final String CLUSTERMEAN = "Clustermean";
+ private static final String NAME = "Cluster Means";
/**
- * The result we visualize
- */
- private Clustering<MeanModel<? extends NumberVector<?, ?>>> clustering;
-
- /**
- * Constructor.
- *
- * @param task VisualizationTask
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}.
*/
- public ClusterParallelMeanVisualization(VisualizationTask task) {
- super(task);
- this.clustering = task.getResult();
- context.addDataStoreListener(this);
- context.addResultListener(this);
- incrementalRedraw();
+ public ClusterParallelMeanVisualization() {
+ super();
}
@Override
- public void destroy() {
- context.removeDataStoreListener(this);
- context.removeResultListener(this);
- super.destroy();
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
-
- Iterator<Cluster<MeanModel<? extends NumberVector<?, ?>>>> ci = clustering.getAllClusters().iterator();
- for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
- Cluster<MeanModel<? extends NumberVector<?, ?>>> clus = ci.next();
- NumberVector<?, ?> mean = clus.getModel().getMean();
- if(mean == null) {
- continue;
- }
-
- double[] pmean = proj.fastProjectDataToRenderSpace(mean);
-
- SVGPath path = new SVGPath();
- for(int i = 0; i < pmean.length; i++) {
- path.drawTo(getVisibleAxisX(i), pmean[i]);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find clusterings we can visualize:
+ Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
+ for (Clustering<?> c : clusterings) {
+ if (c.getAllClusters().size() > 0) {
+ // Does the cluster have a model with cluster means?
+ Clustering<MeanModel<? extends NumberVector<?>>> mcls = findMeanModel(c);
+ if (mcls != null) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
+ for (ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA + 1;
+ baseResult.getHierarchy().add(c, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
}
-
- Element meanline = path.makeElement(svgp);
- SVGUtil.addCSSClass(meanline, CLUSTERMEAN + cnum);
- layer.appendChild(meanline);
}
}
/**
- * Adds the required CSS-Classes
+ * Test if the given clustering has a mean model.
*
- * @param svgp SVG-Plot
+ * @param c Clustering to inspect
+ * @return the clustering cast to return a mean model, null otherwise.
*/
- private void addCSSClasses(SVGPlot svgp) {
- if(!svgp.getCSSClassManager().contains(CLUSTERMEAN)) {
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
- int clusterID = 0;
-
- for(@SuppressWarnings("unused")
- Cluster<?> cluster : clustering.getAllClusters()) {
- CSSClass cls = new CSSClass(this, CLUSTERMEAN + clusterID);
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT) * 2);
-
- final String color;
- if(clustering.getAllClusters().size() == 1) {
- color = SVGConstants.CSS_BLACK_VALUE;
- }
- else {
- color = colors.getColor(clusterID);
- }
-
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
-
- svgp.addCSSClassOrLogError(cls);
- clusterID++;
- }
+ @SuppressWarnings("unchecked")
+ private static Clustering<MeanModel<? extends NumberVector<?>>> findMeanModel(Clustering<?> c) {
+ if (c.getAllClusters().get(0).getModel() instanceof MeanModel<?>) {
+ return (Clustering<MeanModel<? extends NumberVector<?>>>) c;
}
+ return null;
+ }
+
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
}
/**
- * Factory for axis visualizations
+ * Instance.
*
* @author Robert Rödler
*
- * @apiviz.stereotype factory
- * @apiviz.uses ClusterParallelMeanVisualization oneway - - «create»
- *
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> implements DataStoreListener {
/**
- * A short name characterizing this Visualizer.
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
*/
- private static final String NAME = "Cluster Means";
+ public static final String CLUSTERMEAN = "Clustermean";
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * The result we visualize.
+ */
+ private Clustering<MeanModel<? extends NumberVector<?>>> clustering;
+
+ /**
+ * Constructor.
+ *
+ * @param task VisualizationTask
*/
- public Factory() {
- super();
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.clustering = task.getResult();
+ context.addDataStoreListener(this);
+ context.addResultListener(this);
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ClusterParallelMeanVisualization(task);
+ public void destroy() {
+ context.removeDataStoreListener(this);
+ context.removeResultListener(this);
+ super.destroy();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // Find clusterings we can visualize:
- Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
- for(Clustering<?> c : clusterings) {
- if(c.getAllClusters().size() > 0) {
- // Does the cluster have a model with cluster means?
- Clustering<MeanModel<? extends NumberVector<?, ?>>> mcls = findMeanModel(c);
- if(mcls != null) {
- Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
- for (ParallelPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA + 1);
- baseResult.getHierarchy().add(c, task);
- baseResult.getHierarchy().add(p, task);
- }
- }
+ protected void redraw() {
+ addCSSClasses(svgp);
+
+ Iterator<Cluster<MeanModel<? extends NumberVector<?>>>> ci = clustering.getAllClusters().iterator();
+ for (int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
+ Cluster<MeanModel<? extends NumberVector<?>>> clus = ci.next();
+ if (clus.getModel() == null) {
+ continue;
+ }
+ NumberVector<?> mean = clus.getModel().getMean();
+ if (mean == null) {
+ continue;
+ }
+
+ double[] pmean = proj.fastProjectDataToRenderSpace(mean);
+
+ SVGPath path = new SVGPath();
+ for (int i = 0; i < pmean.length; i++) {
+ path.drawTo(getVisibleAxisX(i), pmean[i]);
}
+
+ Element meanline = path.makeElement(svgp);
+ SVGUtil.addCSSClass(meanline, CLUSTERMEAN + cnum);
+ layer.appendChild(meanline);
}
}
/**
- * Test if the given clustering has a mean model.
+ * Adds the required CSS-Classes.
*
- * @param c Clustering to inspect
- * @return the clustering cast to return a mean model, null otherwise.
+ * @param svgp SVG-Plot
*/
- @SuppressWarnings("unchecked")
- private static Clustering<MeanModel<? extends NumberVector<?, ?>>> findMeanModel(Clustering<?> c) {
- if(c.getAllClusters().get(0).getModel() instanceof MeanModel<?>) {
- return (Clustering<MeanModel<? extends NumberVector<?, ?>>>) c;
- }
- return null;
- }
+ private void addCSSClasses(SVGPlot svgp) {
+ if (!svgp.getCSSClassManager().contains(CLUSTERMEAN)) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+ int clusterID = 0;
+
+ for (@SuppressWarnings("unused")
+ Cluster<?> cluster : clustering.getAllClusters()) {
+ CSSClass cls = new CSSClass(this, CLUSTERMEAN + clusterID);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) * 2.);
+
+ final String color;
+ if (clustering.getAllClusters().size() == 1) {
+ color = SVGConstants.CSS_BLACK_VALUE;
+ } else {
+ color = colors.getColor(clusterID);
+ }
- @Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+
+ svgp.addCSSClassOrLogError(cls);
+ clusterID++;
+ }
+ }
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/index/RTreeParallelVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/index/RTreeParallelVisualization.java
index 499d14a5..7c2c8b9f 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/index/RTreeParallelVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/index/RTreeParallelVisualization.java
@@ -60,13 +60,10 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.index.TreeMBRVi
*
* @author Robert Rödler
*
- * @apiviz.has AbstractRStarTree oneway - - visualizes
- *
- * @param <N> Tree node type
- * @param <E> Tree entry type
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-// TODO: listen for tree changes instead of data changes?
-public class RTreeParallelVisualization<N extends AbstractRStarTreeNode<N, E>, E extends SpatialEntry> extends AbstractParallelVisualization<NumberVector<?, ?>> implements DataStoreListener {
+public class RTreeParallelVisualization extends AbstractVisFactory {
/**
* Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
*/
@@ -78,190 +75,187 @@ public class RTreeParallelVisualization<N extends AbstractRStarTreeNode<N, E>, E
public static final String NAME = "R-Tree Index MBRs";
/**
- * Fill parameter.
- */
- protected boolean fill = true;
-
- /**
- * The tree we visualize
+ * Settings
*/
- protected AbstractRStarTree<N, E> tree;
+ protected Parameterizer settings;
/**
* Constructor.
*
- * @param task Visualization task
- * @param fill Fill flag
+ * @param settings Settings
*/
- @SuppressWarnings("unchecked")
- public RTreeParallelVisualization(VisualizationTask task, boolean fill) {
- super(task);
- this.tree = AbstractRStarTree.class.cast(task.getResult());
- this.fill = fill;
- context.addDataStoreListener(this);
- context.addResultListener(this);
- incrementalRedraw();
+ public RTreeParallelVisualization(Parameterizer settings) {
+ super();
+ this.settings = settings;
}
@Override
- public void destroy() {
- context.removeDataStoreListener(this);
- context.removeResultListener(this);
- super.destroy();
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<RStarTreeNode, SpatialEntry>(task);
}
@Override
- protected void redraw() {
- if(tree != null) {
- addCSSClasses(svgp);
- E root = tree.getRootEntry();
- visualizeRTreeEntry(svgp, layer, proj, tree, root, 0, 0);
- }
- }
-
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
-
- for(int i = 0; i < tree.getHeight(); i++) {
-
- if(!svgp.getCSSClassManager().contains(INDEX + i)) {
- CSSClass cls = new CSSClass(this, INDEX + i);
-
- // Relative depth of this level. 1.0 = toplevel
- final double relDepth = 1. - (((double) i) / tree.getHeight());
- if(fill) {
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, colors.getColor(i));
- cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.2);
- }
- else {
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ ArrayList<AbstractRStarTree<RStarTreeNode, SpatialEntry>> trees = ResultUtil.filterResults(result, AbstractRStarTree.class);
+ for(AbstractRStarTree<RStarTreeNode, SpatialEntry> tree : trees) {
+ if(tree instanceof Result) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, (Result) tree, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_BACKGROUND + 2;
+ baseResult.getHierarchy().add((Result) tree, task);
+ baseResult.getHierarchy().add(p, task);
}
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- svgp.addCSSClassOrLogError(cls);
}
}
- svgp.updateStyleElement();
}
/**
- * Recursively draw the MBR rectangles.
- *
- * @param svgp SVG Plot
- * @param layer Layer
- * @param proj Projection
- * @param rtree Rtree to visualize
- * @param entry Current entry
- * @param depth Current depth
- */
- private void visualizeRTreeEntry(SVGPlot svgp, Element layer, ProjectionParallel proj, AbstractRStarTree<? extends N, E> rtree, E entry, int depth, int step) {
- final int dim = proj.getVisibleDimensions();
- double[] min = proj.fastProjectDataToRenderSpace(SpatialUtil.getMin(entry));
- double[] max = proj.fastProjectDataToRenderSpace(SpatialUtil.getMax(entry));
- assert (min.length == dim && max.length == dim);
- SVGPath path = new SVGPath();
- for(int i = 0; i < dim; i++) {
- path.drawTo(getVisibleAxisX(i), Math.max(min[i], max[i]));
- }
- for(int i = dim - 1; i >= 0; i--) {
- path.drawTo(getVisibleAxisX(i), Math.min(min[i], max[i]));
- }
- path.close();
-
- Element intervals = path.makeElement(svgp);
-
- SVGUtil.addCSSClass(intervals, INDEX + depth);
- layer.appendChild(intervals);
-
- if(!entry.isLeafEntry()) {
- N node = rtree.getNode(entry);
- for(int i = 0; i < node.getNumEntries(); i++) {
- E child = node.getEntry(i);
- if(!child.isLeafEntry()) {
- visualizeRTreeEntry(svgp, layer, proj, rtree, child, depth + 1, ++step);
- }
- }
- }
- }
-
- /**
- * Factory
+ * Instance for a particular data set and tree
*
* @author Robert Rödler
*
- * @apiviz.stereotype factory
- * @apiviz.uses RTreeParallelVisualization oneway - - «create»
+ * @apiviz.has AbstractRStarTree oneway - - visualizes
+ *
+ * @param <N> Tree node type
+ * @param <E> Tree entry type
*/
- public static class Factory extends AbstractVisFactory {
+ // TODO: listen for tree changes instead of data changes?
+ public class Instance<N extends AbstractRStarTreeNode<N, E>, E extends SpatialEntry> extends AbstractParallelVisualization<NumberVector<?>> implements DataStoreListener {
/**
- * Fill parameter.
+ * The tree we visualize
*/
- protected boolean fill = true;
+ protected AbstractRStarTree<N, E> tree;
/**
* Constructor.
*
- * @param fill
+ * @param task Visualization task
*/
- public Factory(boolean fill) {
- super();
- this.fill = fill;
+ @SuppressWarnings("unchecked")
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.tree = AbstractRStarTree.class.cast(task.getResult());
+ context.addDataStoreListener(this);
+ context.addResultListener(this);
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new RTreeParallelVisualization<RStarTreeNode, SpatialEntry>(task, fill);
+ public void destroy() {
+ context.removeDataStoreListener(this);
+ context.removeResultListener(this);
+ super.destroy();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- ArrayList<AbstractRStarTree<RStarTreeNode, SpatialEntry>> trees = ResultUtil.filterResults(result, AbstractRStarTree.class);
- for(AbstractRStarTree<RStarTreeNode, SpatialEntry> tree : trees) {
- if(tree instanceof Result) {
- Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
- for(ParallelPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, (Result) tree, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_BACKGROUND + 2);
- baseResult.getHierarchy().add((Result) tree, task);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ if(tree != null) {
+ addCSSClasses(svgp);
+ E root = tree.getRootEntry();
+ visualizeRTreeEntry(svgp, layer, proj, tree, root, 0, 0);
+ }
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+
+ for(int i = 0; i < tree.getHeight(); i++) {
+ if(!svgp.getCSSClassManager().contains(INDEX + i)) {
+ CSSClass cls = new CSSClass(this, INDEX + i);
+
+ // Relative depth of this level. 1.0 = toplevel
+ final double relDepth = 1. - (((double) i) / tree.getHeight());
+ if(settings.fill) {
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * style.getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.2);
+ }
+ else {
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * style.getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
}
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ svgp.addCSSClassOrLogError(cls);
}
}
+ svgp.updateStyleElement();
}
/**
- * Parameterization class.
+ * Recursively draw the MBR rectangles.
*
- * @author Erich Schubert
- *
- * @apiviz.exclude
+ * @param svgp SVG Plot
+ * @param layer Layer
+ * @param proj Projection
+ * @param rtree Rtree to visualize
+ * @param entry Current entry
+ * @param depth Current depth
*/
- public static class Parameterizer extends AbstractParameterizer {
- protected boolean fill = true;
+ private void visualizeRTreeEntry(SVGPlot svgp, Element layer, ProjectionParallel proj, AbstractRStarTree<? extends N, E> rtree, E entry, int depth, int step) {
+ final int dim = proj.getVisibleDimensions();
+ double[] min = proj.fastProjectDataToRenderSpace(SpatialUtil.getMin(entry));
+ double[] max = proj.fastProjectDataToRenderSpace(SpatialUtil.getMax(entry));
+ assert (min.length == dim && max.length == dim);
+ SVGPath path = new SVGPath();
+ for(int i = 0; i < dim; i++) {
+ path.drawTo(getVisibleAxisX(i), Math.max(min[i], max[i]));
+ }
+ for(int i = dim - 1; i >= 0; i--) {
+ path.drawTo(getVisibleAxisX(i), Math.min(min[i], max[i]));
+ }
+ path.close();
+
+ Element intervals = path.makeElement(svgp);
+
+ SVGUtil.addCSSClass(intervals, INDEX + depth);
+ layer.appendChild(intervals);
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- Flag fillF = new Flag(TreeMBRVisualization.Factory.FILL_ID);
- fillF.setDefaultValue(true);
- if(config.grab(fillF)) {
- fill = fillF.getValue();
+ if(!entry.isLeafEntry()) {
+ N node = rtree.getNode(entry);
+ for(int i = 0; i < node.getNumEntries(); i++) {
+ E child = node.getEntry(i);
+ if(!child.isLeafEntry()) {
+ visualizeRTreeEntry(svgp, layer, proj, rtree, child, depth + 1, ++step);
+ }
}
}
+ }
+
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ protected boolean fill = true;
- @Override
- protected Factory makeInstance() {
- return new Factory(fill);
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ Flag fillF = new Flag(TreeMBRVisualization.Parameterizer.FILL_ID);
+ fillF.setDefaultValue(Boolean.TRUE);
+ if(config.grab(fillF)) {
+ fill = fillF.isTrue();
}
}
+
+ @Override
+ protected RTreeParallelVisualization makeInstance() {
+ return new RTreeParallelVisualization(this);
+ }
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionAxisRangeVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionAxisRangeVisualization.java
index b894a87b..6e2a9329 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionAxisRangeVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionAxisRangeVisualization.java
@@ -48,134 +48,126 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.AbstractParallelVi
import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualization;
/**
- * Visualizer for generating an SVG-Element representing the selected range for
- * each dimension
+ * Visualizer for generating an SVG-Element representing the selected range.
*
* @author Robert Rödler
*
- * @apiviz.has SelectionResult oneway - - visualizes
- * @apiviz.has RangeSelection oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class SelectionAxisRangeVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> {
+public class SelectionAxisRangeVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
- private static final String NAME = "Selection Axis Range";
-
- /**
- * CSS Class for the range marker
- */
- public static final String MARKER = "selectionAxisRange";
+ public static final String NAME = "Selection Axis Range";
/**
* Constructor.
- *
- * @param task Visualization task
*/
- public SelectionAxisRangeVisualization(VisualizationTask task) {
- super(task);
- addCSSClasses(svgp);
- context.addResultListener(this);
- incrementalRedraw();
+ public SelectionAxisRangeVisualization() {
+ super();
+ thumbmask |= ThumbnailVisualization.ON_SELECTION;
}
@Override
- public void destroy() {
- context.removeResultListener(this);
- super.destroy();
- }
-
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- final StyleLibrary style = context.getStyleLibrary();
- // Class for the cube
- if(!svgp.getCSSClassManager().contains(MARKER)) {
- CSSClass cls = new CSSClass(this, MARKER);
- cls.setStatement(SVGConstants.CSS_STROKE_VALUE, style.getColor(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
-
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
-
- svgp.addCSSClassOrLogError(cls);
- }
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- protected void redraw() {
- DBIDSelection selContext = context.getSelection();
- if(selContext == null || !(selContext instanceof RangeSelection)) {
- return;
- }
- DoubleDoublePair[] ranges = ((RangeSelection) selContext).getRanges();
- if(ranges == null) {
- return;
- }
-
- // Project:
- double[] min = new double[ranges.length];
- double[] max = new double[ranges.length];
- for(int d = 0; d < ranges.length; d++) {
- if(ranges[d] != null) {
- min[d] = ranges[d].first;
- max[d] = ranges[d].second;
- }
- }
- min = proj.fastProjectDataToRenderSpace(min);
- max = proj.fastProjectDataToRenderSpace(max);
-
- int dim = proj.getVisibleDimensions();
- for(int d = 0; d < dim; d++) {
- if(ranges[proj.getDimForVisibleAxis(d)] != null) {
- double amin = Math.min(min[d], max[d]);
- double amax = Math.max(min[d], max[d]);
- Element rect = svgp.svgRect(getVisibleAxisX(d) - (0.01 * StyleLibrary.SCALE), amin, 0.02 * StyleLibrary.SCALE, amax - amin);
- SVGUtil.addCSSClass(rect, MARKER);
- layer.appendChild(rect);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA - 1;
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
}
}
}
/**
- * Factory for visualizers to generate an SVG-Element containing a cube as
- * marker representing the selected range for each dimension
+ * Instance
*
* @author Robert Rödler
*
- * @apiviz.stereotype factory
- * @apiviz.uses SelectionAxisRangeVisualization oneway - - «create»
+ * @apiviz.has SelectionResult oneway - - visualizes
+ * @apiviz.has RangeSelection oneway - - visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> {
+ /**
+ * CSS Class for the range marker
+ */
+ public static final String MARKER = "selectionAxisRange";
+
/**
* Constructor.
+ *
+ * @param task Visualization task
*/
- public Factory() {
- super();
- thumbmask |= ThumbnailVisualization.ON_SELECTION;
+ public Instance(VisualizationTask task) {
+ super(task);
+ addCSSClasses(svgp);
+ context.addResultListener(this);
+ incrementalRedraw();
}
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionAxisRangeVisualization(task);
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ // Class for the cube
+ if(!svgp.getCSSClassManager().contains(MARKER)) {
+ CSSClass cls = new CSSClass(this, MARKER);
+ cls.setStatement(SVGConstants.CSS_STROKE_VALUE, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
+
+ svgp.addCSSClassOrLogError(cls);
+ }
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
- for(SelectionResult selres : selectionResults) {
- Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
- for(ParallelPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 1);
- baseResult.getHierarchy().add(selres, task);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ DBIDSelection selContext = context.getSelection();
+ if(!(selContext instanceof RangeSelection)) {
+ return;
+ }
+ DoubleDoublePair[] ranges = ((RangeSelection) selContext).getRanges();
+ if(ranges == null) {
+ return;
+ }
+
+ // Project:
+ double[] min = new double[ranges.length];
+ double[] max = new double[ranges.length];
+ for(int d = 0; d < ranges.length; d++) {
+ if(ranges[d] != null) {
+ min[d] = ranges[d].first;
+ max[d] = ranges[d].second;
+ }
+ }
+ min = proj.fastProjectDataToRenderSpace(min);
+ max = proj.fastProjectDataToRenderSpace(max);
+
+ int dim = proj.getVisibleDimensions();
+ for(int d = 0; d < dim; d++) {
+ if(ranges[proj.getDimForVisibleAxis(d)] != null) {
+ double amin = Math.min(min[d], max[d]);
+ double amax = Math.max(min[d], max[d]);
+ Element rect = svgp.svgRect(getVisibleAxisX(d) - (0.01 * StyleLibrary.SCALE), amin, 0.02 * StyleLibrary.SCALE, amax - amin);
+ SVGUtil.addCSSClass(rect, MARKER);
+ layer.appendChild(rect);
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionLineVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionLineVisualization.java
index b9a9ac48..d7ccb072 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionLineVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionLineVisualization.java
@@ -54,113 +54,113 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
*
* @author Robert Rödler
*
- * @apiviz.has SelectionResult oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class SelectionLineVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> implements DataStoreListener {
+public class SelectionLineVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
- private static final String NAME = "Selection Line";
-
- /**
- * CSS Class for the range marker
- */
- public static final String MARKER = "SelectionLine";
+ public static final String NAME = "Selection Line";
/**
* Constructor.
- *
- * @param task Visualization task
*/
- public SelectionLineVisualization(VisualizationTask task) {
- super(task);
- addCSSClasses(svgp);
- context.addDataStoreListener(this);
- context.addResultListener(this);
- incrementalRedraw();
+ public SelectionLineVisualization() {
+ super();
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
}
-
+
@Override
- public void destroy() {
- context.removeDataStoreListener(this);
- context.removeResultListener(this);
- super.destroy();
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- protected void redraw() {
- DBIDSelection selContext = context.getSelection();
- if(selContext != null) {
- DBIDs selection = selContext.getSelectedIds();
-
- for(DBIDIter iter = selection.iter(); iter.valid(); iter.advance()) {
- double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(iter));
-
- SVGPath path = new SVGPath();
- for(int i = 0; i < proj.getVisibleDimensions(); i++) {
- path.drawTo(getVisibleAxisX(i), yPos[i]);
- }
- Element marker = path.makeElement(svgp);
- SVGUtil.addCSSClass(marker, MARKER);
- layer.appendChild(marker);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA - 1;
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
}
}
}
/**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- final StyleLibrary style = context.getStyleLibrary();
- // Class for the cube
- if(!svgp.getCSSClassManager().contains(MARKER)) {
- CSSClass cls = new CSSClass(this, MARKER);
- cls.setStatement(SVGConstants.CSS_STROKE_VALUE, style.getColor(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) * 2.);
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- svgp.addCSSClassOrLogError(cls);
- }
- }
-
- /**
- * Factory for visualizers to generate an SVG-Element containing a cube as
- * marker representing the selected range for each dimension
+ * Instance
*
* @author Robert Rödler
*
- * @apiviz.stereotype factory
+ * @apiviz.has SelectionResult oneway - - visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> implements DataStoreListener {
+ /**
+ * CSS Class for the range marker
+ */
+ public static final String MARKER = "SelectionLine";
+
/**
* Constructor.
+ *
+ * @param task Visualization task
*/
- public Factory() {
- super();
- thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
+ public Instance(VisualizationTask task) {
+ super(task);
+ addCSSClasses(svgp);
+ context.addDataStoreListener(this);
+ context.addResultListener(this);
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionLineVisualization(task);
+ public void destroy() {
+ context.removeDataStoreListener(this);
+ context.removeResultListener(this);
+ super.destroy();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
- for(SelectionResult selres : selectionResults) {
- Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
- for(ParallelPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA -1);
- baseResult.getHierarchy().add(selres, task);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ DBIDSelection selContext = context.getSelection();
+ if(selContext != null) {
+ DBIDs selection = selContext.getSelectedIds();
+
+ for(DBIDIter iter = selection.iter(); iter.valid(); iter.advance()) {
+ double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(iter));
+
+ SVGPath path = new SVGPath();
+ for(int i = 0; i < proj.getVisibleDimensions(); i++) {
+ path.drawTo(getVisibleAxisX(i), yPos[i]);
+ }
+ Element marker = path.makeElement(svgp);
+ SVGUtil.addCSSClass(marker, MARKER);
+ layer.appendChild(marker);
}
}
}
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ // Class for the cube
+ if(!svgp.getCSSClassManager().contains(MARKER)) {
+ CSSClass cls = new CSSClass(this, MARKER);
+ cls.setStatement(SVGConstants.CSS_STROKE_VALUE, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) * 2.);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ }
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolAxisRangeVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolAxisRangeVisualization.java
index 2f3c6778..4fd00023 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolAxisRangeVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolAxisRangeVisualization.java
@@ -59,14 +59,14 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.AbstractParallelVi
*
* @author Robert Rödler
*
- * @apiviz.has SelectionResult oneway - - updates
- * @apiviz.has RangeSelection oneway - - updates
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class SelectionToolAxisRangeVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> implements DragableArea.DragListener {
+public class SelectionToolAxisRangeVisualization extends AbstractVisFactory {
/**
* The logger for this class.
*/
- protected static final Logging logger = Logging.getLogger(SelectionToolAxisRangeVisualization.class);
+ private static final Logging LOG = Logging.getLogger(SelectionToolAxisRangeVisualization.class);
/**
* A short name characterizing this Visualizer.
@@ -74,237 +74,232 @@ public class SelectionToolAxisRangeVisualization extends AbstractParallelVisuali
private static final String NAME = "Axis Range Selection";
/**
- * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- private static final String CSS_RANGEMARKER = "selectionAxisRangeMarker";
-
- /**
- * Element for selection rectangle
- */
- private Element rtag;
-
- /**
- * Element for the rectangle to add listeners
- */
- private Element etag;
-
- /**
- * Constructor.
- *
- * @param task Task
- */
- public SelectionToolAxisRangeVisualization(VisualizationTask task) {
- super(task);
- incrementalRedraw();
+ public SelectionToolAxisRangeVisualization() {
+ super();
}
@Override
- public void destroy() {
- super.destroy();
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
-
- // rtag: tag for the selected rect
- rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
- SVGUtil.addCSSClass(rtag, CSS_RANGEMARKER);
- layer.appendChild(rtag);
-
- // etag: sensitive area
- DragableArea drag = new DragableArea(svgp, -.1 * getMarginLeft(), -.1 * getMarginTop(), getSizeX() + getMarginLeft() * .2, getSizeY() + getMarginTop() * .2, this);
- etag = drag.getElement();
- layer.appendChild(etag);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ task.tool = true;
+ task.thumbnail = false;
+ task.noexport = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
}
/**
- * Delete the children of the element
+ * Instance
+ *
+ * @author Robert Rödler
*
- * @param container SVG-Element
+ * @apiviz.has SelectionResult oneway - - updates
+ * @apiviz.has RangeSelection oneway - - updates
*/
- private void deleteChildren(Element container) {
- while(container.hasChildNodes()) {
- container.removeChild(container.getLastChild());
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> implements DragableArea.DragListener {
+ /**
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ private static final String CSS_RANGEMARKER = "selectionAxisRangeMarker";
+
+ /**
+ * Element for selection rectangle
+ */
+ private Element rtag;
+
+ /**
+ * Element for the rectangle to add listeners
+ */
+ private Element etag;
+
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
}
- }
- /**
- * Set the selected ranges and the mask for the actual dimensions in the
- * context
- *
- * @param x1 min x-value
- * @param x2 max x-value
- * @param y1 min y-value
- * @param y2 max y-value
- */
- private void updateSelectionRectKoordinates(double x1, double x2, double y1, double y2, DoubleDoublePair[] ranges) {
- final int dim = proj.getVisibleDimensions();
- int minaxis = dim + 1;
- int maxaxis = -1;
- {
- int i = 0;
- while(i < dim) {
- double axx = getVisibleAxisX(i);
- if(x1 < axx || x2 < axx) {
- minaxis = i;
- break;
- }
- i++;
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
+
+ // rtag: tag for the selected rect
+ rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ SVGUtil.addCSSClass(rtag, CSS_RANGEMARKER);
+ layer.appendChild(rtag);
+
+ // etag: sensitive area
+ DragableArea drag = new DragableArea(svgp, -.1 * getMarginLeft(), -.1 * getMarginTop(), getSizeX() + getMarginLeft() * .2, getSizeY() + getMarginTop() * .2, this);
+ etag = drag.getElement();
+ layer.appendChild(etag);
+ }
+
+ /**
+ * Delete the children of the element
+ *
+ * @param container SVG-Element
+ */
+ private void deleteChildren(Element container) {
+ while(container.hasChildNodes()) {
+ container.removeChild(container.getLastChild());
}
- while(i <= dim) {
- double axx = getVisibleAxisX(i);
- if(x2 < axx && x1 < axx) {
- maxaxis = i;
- break;
+ }
+
+ /**
+ * Set the selected ranges and the mask for the actual dimensions in the
+ * context
+ *
+ * @param x1 min x-value
+ * @param x2 max x-value
+ * @param y1 min y-value
+ * @param y2 max y-value
+ */
+ private void updateSelectionRectKoordinates(double x1, double x2, double y1, double y2, DoubleDoublePair[] ranges) {
+ final int dim = proj.getVisibleDimensions();
+ int minaxis = dim + 1;
+ int maxaxis = -1;
+ {
+ int i = 0;
+ while(i < dim) {
+ double axx = getVisibleAxisX(i);
+ if(x1 < axx || x2 < axx) {
+ minaxis = i;
+ break;
+ }
+ i++;
+ }
+ while(i <= dim) {
+ double axx = getVisibleAxisX(i);
+ if(x2 < axx && x1 < axx) {
+ maxaxis = i;
+ break;
+ }
+ i++;
}
- i++;
}
- }
- double z1 = Math.max(Math.min(y1, y2), 0);
- double z2 = Math.min(Math.max(y1, y2), getSizeY());
- for(int i = minaxis; i < maxaxis; i++) {
- double v1 = proj.fastProjectRenderToDataSpace(z1, i);
- double v2 = proj.fastProjectRenderToDataSpace(z2, i);
- if(logger.isDebugging()) {
- logger.debug("Axis " + i + " dimension " + proj.getDimForVisibleAxis(i) + " " + v1 + " to " + v2);
+ double z1 = Math.max(Math.min(y1, y2), 0);
+ double z2 = Math.min(Math.max(y1, y2), getSizeY());
+ for(int i = minaxis; i < maxaxis; i++) {
+ double v1 = proj.fastProjectRenderToDataSpace(z1, i);
+ double v2 = proj.fastProjectRenderToDataSpace(z2, i);
+ if(LOG.isDebugging()) {
+ LOG.debug("Axis " + i + " dimension " + proj.getDimForVisibleAxis(i) + " " + v1 + " to " + v2);
+ }
+ ranges[proj.getDimForVisibleAxis(i)] = new DoubleDoublePair(Math.min(v1, v2), Math.max(v1, v2));
}
- ranges[proj.getDimForVisibleAxis(i)] = new DoubleDoublePair(Math.min(v1, v2), Math.max(v1, v2));
}
- }
-
- @Override
- public boolean startDrag(SVGPoint startPoint, Event evt) {
- return true;
- }
- @Override
- public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- deleteChildren(rtag);
- double x = Math.min(startPoint.getX(), dragPoint.getX());
- double y = Math.min(startPoint.getY(), dragPoint.getY());
- double width = Math.abs(startPoint.getX() - dragPoint.getX());
- double height = Math.abs(startPoint.getY() - dragPoint.getY());
- rtag.appendChild(svgp.svgRect(x, y, width, height));
- return true;
- }
-
- @Override
- public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- deleteChildren(rtag);
- if(startPoint.getX() != dragPoint.getX() || startPoint.getY() != dragPoint.getY()) {
- updateSelection(proj, startPoint, dragPoint);
+ @Override
+ public boolean startDrag(SVGPoint startPoint, Event evt) {
+ return true;
}
- return true;
- }
- /**
- * Update the selection in the context.
- *
- * @param proj The projection
- * @param p1 First Point of the selected rectangle
- * @param p2 Second Point of the selected rectangle
- */
- private void updateSelection(Projection proj, SVGPoint p1, SVGPoint p2) {
- DBIDSelection selContext = context.getSelection();
- ModifiableDBIDs selection;
- if(selContext != null) {
- selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
- }
- else {
- selection = DBIDUtil.newHashSet();
+ @Override
+ public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ deleteChildren(rtag);
+ double x = Math.min(startPoint.getX(), dragPoint.getX());
+ double y = Math.min(startPoint.getY(), dragPoint.getY());
+ double width = Math.abs(startPoint.getX() - dragPoint.getX());
+ double height = Math.abs(startPoint.getY() - dragPoint.getY());
+ rtag.appendChild(svgp.svgRect(x, y, width, height));
+ return true;
}
- DoubleDoublePair[] ranges;
- if(p1 == null || p2 == null) {
- logger.warning("no rect selected: p1: " + p1 + " p2: " + p2);
+ @Override
+ public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ deleteChildren(rtag);
+ if(startPoint.getX() != dragPoint.getX() || startPoint.getY() != dragPoint.getY()) {
+ updateSelection(proj, startPoint, dragPoint);
+ }
+ return true;
}
- else {
- double x1 = Math.min(p1.getX(), p2.getX());
- double x2 = Math.max(p1.getX(), p2.getX());
- double y1 = Math.max(p1.getY(), p2.getY());
- double y2 = Math.min(p1.getY(), p2.getY());
- int dim = proj.getInputDimensionality();
- if(selContext instanceof RangeSelection) {
- ranges = ((RangeSelection) selContext).getRanges();
+ /**
+ * Update the selection in the context.
+ *
+ * @param proj The projection
+ * @param p1 First Point of the selected rectangle
+ * @param p2 Second Point of the selected rectangle
+ */
+ private void updateSelection(Projection proj, SVGPoint p1, SVGPoint p2) {
+ DBIDSelection selContext = context.getSelection();
+ ModifiableDBIDs selection;
+ if(selContext != null) {
+ selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
}
else {
- ranges = new DoubleDoublePair[dim];
+ selection = DBIDUtil.newHashSet();
+ }
+ DoubleDoublePair[] ranges;
+
+ if(p1 == null || p2 == null) {
+ LOG.warning("no rect selected: p1: " + p1 + " p2: " + p2);
}
- updateSelectionRectKoordinates(x1, x2, y1, y2, ranges);
+ else {
+ double x1 = Math.min(p1.getX(), p2.getX());
+ double x2 = Math.max(p1.getX(), p2.getX());
+ double y1 = Math.max(p1.getY(), p2.getY());
+ double y2 = Math.min(p1.getY(), p2.getY());
+
+ int dim = proj.getInputDimensionality();
+ if(selContext instanceof RangeSelection) {
+ ranges = ((RangeSelection) selContext).getRanges();
+ }
+ else {
+ ranges = new DoubleDoublePair[dim];
+ }
+ updateSelectionRectKoordinates(x1, x2, y1, y2, ranges);
- selection.clear();
+ selection.clear();
- candidates: for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
- NumberVector<?, ?> dbTupel = relation.get(iditer);
- for(int i = 0; i < dim; i++) {
- if(ranges != null && ranges[i] != null) {
- if(dbTupel.doubleValue(i + 1) < ranges[i].first || dbTupel.doubleValue(i + 1) > ranges[i].second) {
- continue candidates;
+ candidates: for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ NumberVector<?> dbTupel = relation.get(iditer);
+ for(int i = 0; i < dim; i++) {
+ if(ranges != null && ranges[i] != null) {
+ if(dbTupel.doubleValue(i) < ranges[i].first || dbTupel.doubleValue(i) > ranges[i].second) {
+ continue candidates;
+ }
}
}
+ selection.add(iditer);
}
- selection.add(iditer);
+ context.setSelection(new RangeSelection(selection, ranges));
}
- context.setSelection(new RangeSelection(selection, ranges));
}
- }
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- protected void addCSSClasses(SVGPlot svgp) {
- // Class for the range marking
- if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
- final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
- final StyleLibrary style = context.getStyleLibrary();
- rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
- rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION_ACTIVE));
- svgp.addCSSClassOrLogError(rcls);
- }
- }
-
- /**
- * Factory for tool visualizations for selecting ranges and the inclosed
- * objects
- *
- * @author Robert Rödler
- *
- * @apiviz.stereotype factory
- * @apiviz.uses SelectionToolAxisRangeVisualization oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
*/
- public Factory() {
- super();
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionToolAxisRangeVisualization(task);
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
- for(SelectionResult selres : selectionResults) {
- Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
- for(ParallelPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_NOTHUMB, true);
- task.put(VisualizationTask.META_NOEXPORT, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(selres, task);
- baseResult.getHierarchy().add(p, task);
- }
+ protected void addCSSClasses(SVGPlot svgp) {
+ // Class for the range marking
+ if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
+ final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
+ rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION_ACTIVE));
+ svgp.addCSSClassOrLogError(rcls);
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolLineVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolLineVisualization.java
index 3e66b4cd..b7296492 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolLineVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/parallel/selection/SelectionToolLineVisualization.java
@@ -58,21 +58,16 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.parallel.AbstractParallelVi
*
* @author Robert Rödler
*
- * @apiviz.has SelectionResult oneway - - updates
- * @apiviz.has DBIDSelection oneway - - updates
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance - - «create»
*/
-public class SelectionToolLineVisualization extends AbstractParallelVisualization<NumberVector<?, ?>> implements DragableArea.DragListener {
+public class SelectionToolLineVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Object Selection";
/**
- * CSS class of the selection rectangle while selecting.
- */
- private static final String CSS_RANGEMARKER = "selectionRangeMarker";
-
- /**
* Input modes
*
* @apiviz.exclude
@@ -82,249 +77,253 @@ public class SelectionToolLineVisualization extends AbstractParallelVisualizatio
}
/**
- * Element for selection rectangle
- */
- Element rtag;
-
- /**
- * Element for the rectangle to add listeners
- */
- Element etag;
-
- /**
- * Constructor.
- *
- * @param task Task
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- public SelectionToolLineVisualization(VisualizationTask task) {
- super(task);
- incrementalRedraw();
+ public SelectionToolLineVisualization() {
+ super();
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
-
- rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
- SVGUtil.addCSSClass(rtag, CSS_RANGEMARKER);
- layer.appendChild(rtag);
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
- // etag: sensitive area
- DragableArea drag = new DragableArea(svgp, -.1 * getMarginLeft(), -.5 * getMarginTop(), getSizeX() + .2 * getMarginLeft(), getMarginTop() * 1.5 + getSizeY(), this);
- etag = drag.getElement();
- layer.appendChild(etag);
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
+ for(ParallelPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ task.tool = true;
+ task.thumbnail = false;
+ task.noexport = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
}
/**
- * Delete the children of the element
+ * Instance.
+ *
+ * @author Robert Rödler
*
- * @param container SVG-Element
+ * @apiviz.has SelectionResult oneway - - updates
+ * @apiviz.has DBIDSelection oneway - - updates
*/
- private void deleteChildren(Element container) {
- while(container.hasChildNodes()) {
- container.removeChild(container.getLastChild());
+ public class Instance extends AbstractParallelVisualization<NumberVector<?>> implements DragableArea.DragListener {
+ /**
+ * CSS class of the selection rectangle while selecting.
+ */
+ private static final String CSS_RANGEMARKER = "selectionRangeMarker";
+
+ /**
+ * Element for selection rectangle
+ */
+ Element rtag;
+
+ /**
+ * Element for the rectangle to add listeners
+ */
+ Element etag;
+
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
}
- }
- @Override
- public boolean startDrag(SVGPoint startPoint, Event evt) {
- return true;
- }
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
- @Override
- public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- deleteChildren(rtag);
- double x = Math.min(startPoint.getX(), dragPoint.getX());
- double y = Math.min(startPoint.getY(), dragPoint.getY());
- double width = Math.abs(startPoint.getX() - dragPoint.getX());
- double height = Math.abs(startPoint.getY() - dragPoint.getY());
- rtag.appendChild(svgp.svgRect(x, y, width, height));
- return true;
- }
+ rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ SVGUtil.addCSSClass(rtag, CSS_RANGEMARKER);
+ layer.appendChild(rtag);
- @Override
- public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- Mode mode = getInputMode(evt);
- deleteChildren(rtag);
- if(startPoint.getX() != dragPoint.getX() || startPoint.getY() != dragPoint.getY()) {
- updateSelection(mode, startPoint, dragPoint);
+ // etag: sensitive area
+ DragableArea drag = new DragableArea(svgp, -.1 * getMarginLeft(), -.5 * getMarginTop(), getSizeX() + .2 * getMarginLeft(), getMarginTop() * 1.5 + getSizeY(), this);
+ etag = drag.getElement();
+ layer.appendChild(etag);
}
- return true;
- }
- /**
- * Get the current input mode, on each mouse event.
- *
- * @param evt Mouse event.
- * @return current input mode
- */
- private Mode getInputMode(Event evt) {
- if(evt instanceof DOMMouseEvent) {
- DOMMouseEvent domme = (DOMMouseEvent) evt;
- // TODO: visual indication of mode possible?
- if(domme.getShiftKey()) {
- return Mode.ADD;
- }
- else if(domme.getCtrlKey()) {
- return Mode.INVERT;
- }
- else {
- return Mode.REPLACE;
+ /**
+ * Delete the children of the element
+ *
+ * @param container SVG-Element
+ */
+ private void deleteChildren(Element container) {
+ while(container.hasChildNodes()) {
+ container.removeChild(container.getLastChild());
}
}
- // Default mode is replace.
- return Mode.REPLACE;
- }
- /**
- * Updates the selection in the context.<br>
- *
- * @param mode Input mode
- * @param p1 first point of the selected rectangle
- * @param p2 second point of the selected rectangle
- */
- private void updateSelection(Mode mode, SVGPoint p1, SVGPoint p2) {
- DBIDSelection selContext = context.getSelection();
- // Note: we rely on SET semantics below!
- final HashSetModifiableDBIDs selection;
- if(selContext == null || mode == Mode.REPLACE) {
- selection = DBIDUtil.newHashSet();
+ @Override
+ public boolean startDrag(SVGPoint startPoint, Event evt) {
+ return true;
}
- else {
- selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
+
+ @Override
+ public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ deleteChildren(rtag);
+ double x = Math.min(startPoint.getX(), dragPoint.getX());
+ double y = Math.min(startPoint.getY(), dragPoint.getY());
+ double width = Math.abs(startPoint.getX() - dragPoint.getX());
+ double height = Math.abs(startPoint.getY() - dragPoint.getY());
+ rtag.appendChild(svgp.svgRect(x, y, width, height));
+ return true;
}
- int[] axisrange = getAxisRange(Math.min(p1.getX(), p2.getX()), Math.max(p1.getX(), p2.getX()));
- DBIDs ids = ResultUtil.getSamplingResult(relation).getSample();
- for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(iter));
- if(checkSelected(axisrange, yPos, Math.max(p1.getX(), p2.getX()), Math.min(p1.getX(), p2.getX()), Math.max(p1.getY(), p2.getY()), Math.min(p1.getY(), p2.getY()))) {
- if(mode == Mode.INVERT) {
- if(!selection.contains(iter)) {
- selection.add(iter);
- }
- else {
- selection.remove(iter);
- }
+
+ @Override
+ public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ Mode mode = getInputMode(evt);
+ deleteChildren(rtag);
+ if(startPoint.getX() != dragPoint.getX() || startPoint.getY() != dragPoint.getY()) {
+ updateSelection(mode, startPoint, dragPoint);
+ }
+ return true;
+ }
+
+ /**
+ * Get the current input mode, on each mouse event.
+ *
+ * @param evt Mouse event.
+ * @return current input mode
+ */
+ private Mode getInputMode(Event evt) {
+ if(evt instanceof DOMMouseEvent) {
+ DOMMouseEvent domme = (DOMMouseEvent) evt;
+ // TODO: visual indication of mode possible?
+ if(domme.getShiftKey()) {
+ return Mode.ADD;
+ }
+ else if(domme.getCtrlKey()) {
+ return Mode.INVERT;
}
else {
- // In REPLACE and ADD, add objects.
- // The difference was done before by not re-using the selection.
- // Since we are using a set, we can just add in any case.
- selection.add(iter);
+ return Mode.REPLACE;
}
}
+ // Default mode is replace.
+ return Mode.REPLACE;
}
- context.setSelection(new DBIDSelection(selection));
- }
- private int[] getAxisRange(double x1, double x2) {
- final int dim = proj.getVisibleDimensions();
- int minaxis = 0;
- int maxaxis = 0;
- boolean minx = true;
- boolean maxx = false;
- int count = -1;
- for(int i = 0; i < dim; i++) {
- if(minx && getVisibleAxisX(i) > x1) {
- minaxis = count;
- minx = false;
- maxx = true;
+ /**
+ * Updates the selection in the context.<br>
+ *
+ * @param mode Input mode
+ * @param p1 first point of the selected rectangle
+ * @param p2 second point of the selected rectangle
+ */
+ private void updateSelection(Mode mode, SVGPoint p1, SVGPoint p2) {
+ DBIDSelection selContext = context.getSelection();
+ // Note: we rely on SET semantics below!
+ final HashSetModifiableDBIDs selection;
+ if(selContext == null || mode == Mode.REPLACE) {
+ selection = DBIDUtil.newHashSet();
+ }
+ else {
+ selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
}
- if(maxx && (getVisibleAxisX(i) > x2 || i == dim - 1)) {
- maxaxis = count + 1;
- if(i == dim - 1 && getVisibleAxisX(i) <= x2) {
- maxaxis++;
+ int[] axisrange = getAxisRange(Math.min(p1.getX(), p2.getX()), Math.max(p1.getX(), p2.getX()));
+ DBIDs ids = ResultUtil.getSamplingResult(relation).getSample();
+ for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ double[] yPos = proj.fastProjectDataToRenderSpace(relation.get(iter));
+ if(checkSelected(axisrange, yPos, Math.max(p1.getX(), p2.getX()), Math.min(p1.getX(), p2.getX()), Math.max(p1.getY(), p2.getY()), Math.min(p1.getY(), p2.getY()))) {
+ if(mode == Mode.INVERT) {
+ if(!selection.contains(iter)) {
+ selection.add(iter);
+ }
+ else {
+ selection.remove(iter);
+ }
+ }
+ else {
+ // In REPLACE and ADD, add objects.
+ // The difference was done before by not re-using the selection.
+ // Since we are using a set, we can just add in any case.
+ selection.add(iter);
+ }
}
- break;
}
- count = i;
+ context.setSelection(new DBIDSelection(selection));
}
- return new int[] { minaxis, maxaxis };
- }
- private boolean checkSelected(int[] ar, double[] yPos, double x1, double x2, double y1, double y2) {
- final int dim = proj.getVisibleDimensions();
- if (ar[0] < 0) {
- ar[0] = 0;
- }
- if(ar[1] >= dim) {
- ar[1] = dim - 1;
- }
- for(int i = ar[0] + 1; i <= ar[1] - 1; i++) {
- if(yPos[i] <= y1 && yPos[i] >= y2) {
- return true;
+ private int[] getAxisRange(double x1, double x2) {
+ final int dim = proj.getVisibleDimensions();
+ int minaxis = 0;
+ int maxaxis = 0;
+ boolean minx = true;
+ boolean maxx = false;
+ int count = -1;
+ for(int i = 0; i < dim; i++) {
+ if(minx && getVisibleAxisX(i) > x1) {
+ minaxis = count;
+ minx = false;
+ maxx = true;
+ }
+ if(maxx && (getVisibleAxisX(i) > x2 || i == dim - 1)) {
+ maxaxis = count + 1;
+ if(i == dim - 1 && getVisibleAxisX(i) <= x2) {
+ maxaxis++;
+ }
+ break;
+ }
+ count = i;
}
+ return new int[] { minaxis, maxaxis };
}
- Line2D.Double idline1 = new Line2D.Double(getVisibleAxisX(ar[0]), yPos[ar[0]], getVisibleAxisX(ar[0] + 1), yPos[ar[0] + 1]);
- Line2D.Double idline2 = new Line2D.Double(getVisibleAxisX(ar[1] - 1), yPos[ar[1] - 1], getVisibleAxisX(ar[1]), yPos[ar[1]]);
- Line2D.Double rectline1 = new Line2D.Double(x2, y1, x1, y1);
- Line2D.Double rectline2 = new Line2D.Double(x2, y1, x2, y2);
- Line2D.Double rectline3 = new Line2D.Double(x2, y2, x1, y2);
- if(idline1.intersectsLine(rectline1) || idline1.intersectsLine(rectline2) || idline1.intersectsLine(rectline3)) {
- return true;
- }
- Line2D.Double rectline4 = new Line2D.Double(x1, y1, x1, y2);
- if(idline2.intersectsLine(rectline1) || idline2.intersectsLine(rectline4) || idline2.intersectsLine(rectline3)) {
- return true;
- }
- return false;
- }
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- protected void addCSSClasses(SVGPlot svgp) {
- // Class for the range marking
- if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
- final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
- final StyleLibrary style = context.getStyleLibrary();
- rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
- rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION_ACTIVE));
- svgp.addCSSClassOrLogError(rcls);
+ private boolean checkSelected(int[] ar, double[] yPos, double x1, double x2, double y1, double y2) {
+ final int dim = proj.getVisibleDimensions();
+ if(ar[0] < 0) {
+ ar[0] = 0;
+ }
+ if(ar[1] >= dim) {
+ ar[1] = dim - 1;
+ }
+ for(int i = ar[0] + 1; i <= ar[1] - 1; i++) {
+ if(yPos[i] <= y1 && yPos[i] >= y2) {
+ return true;
+ }
+ }
+ Line2D.Double idline1 = new Line2D.Double(getVisibleAxisX(ar[0]), yPos[ar[0]], getVisibleAxisX(ar[0] + 1), yPos[ar[0] + 1]);
+ Line2D.Double idline2 = new Line2D.Double(getVisibleAxisX(ar[1] - 1), yPos[ar[1] - 1], getVisibleAxisX(ar[1]), yPos[ar[1]]);
+ Line2D.Double rectline1 = new Line2D.Double(x2, y1, x1, y1);
+ Line2D.Double rectline2 = new Line2D.Double(x2, y1, x2, y2);
+ Line2D.Double rectline3 = new Line2D.Double(x2, y2, x1, y2);
+ if(idline1.intersectsLine(rectline1) || idline1.intersectsLine(rectline2) || idline1.intersectsLine(rectline3)) {
+ return true;
+ }
+ Line2D.Double rectline4 = new Line2D.Double(x1, y1, x1, y2);
+ if(idline2.intersectsLine(rectline1) || idline2.intersectsLine(rectline4) || idline2.intersectsLine(rectline3)) {
+ return true;
+ }
+ return false;
}
- }
- /**
- * Factory for tool visualizations for selecting objects
- *
- * @author Robert Rödler
- *
- * @apiviz.stereotype factory
- * @apiviz.uses SelectionToolLineVisualization - - «create»
- *
- */
- public static class Factory extends AbstractVisFactory {
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
*/
- public Factory() {
- super();
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionToolLineVisualization(task);
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
- for(SelectionResult selres : selectionResults) {
- Collection<ParallelPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ParallelPlotProjector.class);
- for(ParallelPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_NOTHUMB, true);
- task.put(VisualizationTask.META_NOEXPORT, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(selres, task);
- baseResult.getHierarchy().add(p, task);
- }
+ protected void addCSSClasses(SVGPlot svgp) {
+ // Class for the range marking
+ if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
+ final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
+ rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION_ACTIVE));
+ svgp.addCSSClassOrLogError(rcls);
}
}
}
-}
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractScatterplotVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractScatterplotVisualization.java
index cce9ea5d..8cedcf0b 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractScatterplotVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractScatterplotVisualization.java
@@ -56,7 +56,7 @@ public abstract class AbstractScatterplotVisualization extends AbstractVisualiza
/**
* The representation we visualize
*/
- final protected Relation<? extends NumberVector<?, ?>> rel;
+ final protected Relation<? extends NumberVector<?>> rel;
/**
* The DBID sample
@@ -73,7 +73,7 @@ public abstract class AbstractScatterplotVisualization extends AbstractVisualiza
this.proj = task.getProj();
this.rel = task.getRelation();
this.sample = ResultUtil.getSamplingResult(rel);
- final double margin = context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
+ final double margin = context.getStyleResult().getStyleLibrary().getSize(StyleLibrary.MARGIN);
this.layer = setupCanvas(svgp, proj, margin, task.getWidth(), task.getHeight());
}
@@ -91,7 +91,7 @@ public abstract class AbstractScatterplotVisualization extends AbstractVisualiza
final CanvasSize canvas = proj.estimateViewport();
final double sizex = canvas.getDiffX();
final double sizey = canvas.getDiffY();
- String transform = SVGUtil.makeMarginTransform(width, height, sizex, sizey, margin) + " translate(" + SVGUtil.fmt(sizex / 2) + " " + SVGUtil.fmt(sizey / 2) + ")";
+ String transform = SVGUtil.makeMarginTransform(width, height, sizex, sizey, margin) + " translate(" + SVGUtil.fmt(sizex * .5) + " " + SVGUtil.fmt(sizey * .5) + ")";
final Element layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractTooltipVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractTooltipVisualization.java
index 8ce6ad67..107af095 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractTooltipVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AbstractTooltipVisualization.java
@@ -31,7 +31,8 @@ import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
@@ -87,18 +88,12 @@ public abstract class AbstractTooltipVisualization extends AbstractScatterplotVi
}
@Override
- public void destroy() {
- super.destroy();
- context.removeDataStoreListener(this);
- }
-
- @Override
public void redraw() {
setupCSS(svgp);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ double dotsize = style.getLineWidth(StyleLibrary.PLOT);
- double dotsize = context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT);
-
- for(DBID id : sample.getSample()) {
+ for(DBIDIter id = sample.getSample().iter(); id.valid(); id.advance()) {
double[] v = proj.fastProjectDataToRenderSpace(rel.get(id));
Element tooltip = makeTooltip(id, v[0], v[1], dotsize);
SVGUtil.addCSSClass(tooltip, TOOLTIP_HIDDEN);
@@ -118,7 +113,16 @@ public abstract class AbstractTooltipVisualization extends AbstractScatterplotVi
}
}
- abstract protected Element makeTooltip(DBID id, double x, double y, double dotsize);
+ /**
+ * Make a tooltip Element for this id.
+ *
+ * @param id Id to make a tooltip for
+ * @param x X position
+ * @param y Y position
+ * @param dotsize Size of a dot
+ * @return Element
+ */
+ protected abstract Element makeTooltip(DBIDRef id, double x, double y, double dotsize);
/**
* Handle the hover events.
@@ -129,7 +133,7 @@ public abstract class AbstractTooltipVisualization extends AbstractScatterplotVi
if(evt.getTarget() instanceof Element) {
Element e = (Element) evt.getTarget();
Node next = e.getNextSibling();
- if(next != null && next instanceof Element) {
+ if(next instanceof Element) {
toggleTooltip((Element) next, evt.getType());
}
else {
@@ -174,7 +178,7 @@ public abstract class AbstractTooltipVisualization extends AbstractScatterplotVi
*
* @param svgp the SVGPlot to register the Tooltip-CSS-Class.
*/
- abstract protected void setupCSS(SVGPlot svgp);
+ protected abstract void setupCSS(SVGPlot svgp);
@Override
public void resultChanged(Result current) {
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AxisVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AxisVisualization.java
index 4173084d..f08c4b2d 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AxisVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/AxisVisualization.java
@@ -28,10 +28,10 @@ import java.util.Collection;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClassManager.CSSNamingConflict;
@@ -47,113 +47,116 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* Generates a SVG-Element containing axes, including labeling.
*
* @author Remigius Wojdanowski
+ * @author Erich Schubert
*
- * @apiviz.uses SVGSimpleLinearAxis
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class AxisVisualization extends AbstractScatterplotVisualization {
+public class AxisVisualization extends AbstractVisFactory {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ private static final String NAME = "Axes";
+
/**
* Constructor.
- *
- * @param task VisualizationTask
*/
- public AxisVisualization(VisualizationTask task) {
- super(task);
- incrementalRedraw();
+ public AxisVisualization() {
+ super();
}
@Override
- protected void redraw() {
- int dim = DatabaseUtil.dimensionality(rel);
-
- // origin
- double[] orig = proj.fastProjectScaledToRenderSpace(new double[dim]);
- // diagonal point opposite to origin
- double[] diag = new double[dim];
- for(int d2 = 0; d2 < dim; d2++) {
- diag[d2] = 1;
- }
- diag = proj.fastProjectScaledToRenderSpace(diag);
- // compute angle to diagonal line, used for axis labeling.
- double diaga = Math.atan2(diag[1] - orig[1], diag[0] - orig[0]);
-
- double alfontsize = 1.1 * context.getStyleLibrary().getTextSize(StyleLibrary.AXIS_LABEL);
- CSSClass alcls = new CSSClass(AxisVisualization.class, "unmanaged");
- alcls.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, SVGUtil.fmt(alfontsize));
- alcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, context.getStyleLibrary().getTextColor(StyleLibrary.AXIS_LABEL));
- alcls.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, context.getStyleLibrary().getFontFamily(StyleLibrary.AXIS_LABEL));
-
- // draw axes
- for(int d = 0; d < dim; d++) {
- double[] v = new double[dim];
- v[d] = 1;
- // projected endpoint of axis
- double[] ax = proj.fastProjectScaledToRenderSpace(v);
- boolean righthand = false;
- double axa = Math.atan2(ax[1] - orig[1], ax[0] - orig[0]);
- if(axa > diaga || (diaga > 0 && axa > diaga + Math.PI)) {
- righthand = true;
- }
- // System.err.println(ax.get(0) + " "+ ax.get(1)+
- // " "+(axa*180/Math.PI)+" "+(diaga*180/Math.PI));
- if(ax[0] != orig[0] || ax[1] != orig[1]) {
- try {
- SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getScale(d), orig[0], orig[1], ax[0], ax[1], righthand ? SVGSimpleLinearAxis.LabelStyle.RIGHTHAND : SVGSimpleLinearAxis.LabelStyle.LEFTHAND, context.getStyleLibrary());
- // TODO: move axis labeling into drawAxis function.
- double offx = (righthand ? 1 : -1) * 0.02 * Projection.SCALE;
- double offy = (righthand ? 1 : -1) * 0.02 * Projection.SCALE;
- Element label = svgp.svgText(ax[0] + offx, ax[1] + offy, DatabaseUtil.getColumnLabel(rel, d + 1));
- SVGUtil.setAtt(label, SVGConstants.SVG_STYLE_ATTRIBUTE, alcls.inlineCSS());
- SVGUtil.setAtt(label, SVGConstants.SVG_TEXT_ANCHOR_ATTRIBUTE, righthand ? SVGConstants.SVG_START_VALUE : SVGConstants.SVG_END_VALUE);
- layer.appendChild(label);
- }
- catch(CSSNamingConflict e) {
- throw new RuntimeException("Conflict in CSS naming for axes.", e);
- }
- }
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(result, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, p, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_BACKGROUND;
+ baseResult.getHierarchy().add(p, task);
}
}
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
+ }
+
/**
- * Factory for axis visualizations
+ * Instance.
*
* @author Erich Schubert
+ * @author Remigius Wojdanowski
+ *
+ * @apiviz.uses SVGSimpleLinearAxis
*
- * @apiviz.stereotype factory
- * @apiviz.uses AxisVisualization oneway - - «create»
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractScatterplotVisualization {
/**
- * A short name characterizing this Visualizer.
+ * Constructor.
+ *
+ * @param task VisualizationTask
*/
- private static final String NAME = "Axes";
-
- /**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
- */
- public Factory() {
- super();
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new AxisVisualization(task);
+ public Instance(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(result, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, p, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_BACKGROUND);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final int dim = RelationUtil.dimensionality(rel);
+
+ // origin
+ double[] orig = proj.fastProjectScaledToRenderSpace(new double[dim]);
+ // diagonal point opposite to origin
+ double[] diag = new double[dim];
+ for(int d2 = 0; d2 < dim; d2++) {
+ diag[d2] = 1;
+ }
+ diag = proj.fastProjectScaledToRenderSpace(diag);
+ // compute angle to diagonal line, used for axis labeling.
+ double diaga = Math.atan2(diag[1] - orig[1], diag[0] - orig[0]);
+
+ double alfontsize = 1.1 * style.getTextSize(StyleLibrary.AXIS_LABEL);
+ CSSClass alcls = new CSSClass(AxisVisualization.class, "unmanaged");
+ alcls.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, SVGUtil.fmt(alfontsize));
+ alcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getTextColor(StyleLibrary.AXIS_LABEL));
+ alcls.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, style.getFontFamily(StyleLibrary.AXIS_LABEL));
+
+ // draw axes
+ for(int d = 0; d < dim; d++) {
+ double[] v = new double[dim];
+ v[d] = 1;
+ // projected endpoint of axis
+ double[] ax = proj.fastProjectScaledToRenderSpace(v);
+ boolean righthand = false;
+ double axa = Math.atan2(ax[1] - orig[1], ax[0] - orig[0]);
+ if(axa > diaga || (diaga > 0 && axa > diaga + Math.PI)) {
+ righthand = true;
+ }
+ // System.err.println(ax.get(0) + " "+ ax.get(1)+
+ // " "+(axa*180/Math.PI)+" "+(diaga*180/Math.PI));
+ if(ax[0] != orig[0] || ax[1] != orig[1]) {
+ try {
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, proj.getScale(d), orig[0], orig[1], ax[0], ax[1], righthand ? SVGSimpleLinearAxis.LabelStyle.RIGHTHAND : SVGSimpleLinearAxis.LabelStyle.LEFTHAND, style);
+ // TODO: move axis labeling into drawAxis function.
+ double offx = (righthand ? 1 : -1) * 0.02 * Projection.SCALE;
+ double offy = (righthand ? 1 : -1) * 0.02 * Projection.SCALE;
+ Element label = svgp.svgText(ax[0] + offx, ax[1] + offy, RelationUtil.getColumnLabel(rel, d));
+ SVGUtil.setAtt(label, SVGConstants.SVG_STYLE_ATTRIBUTE, alcls.inlineCSS());
+ SVGUtil.setAtt(label, SVGConstants.SVG_TEXT_ANCHOR_ATTRIBUTE, righthand ? SVGConstants.SVG_START_VALUE : SVGConstants.SVG_END_VALUE);
+ layer.appendChild(label);
+ }
+ catch(CSSNamingConflict e) {
+ throw new RuntimeException("Conflict in CSS naming for axes.", e);
+ }
+ }
}
- }
-
- @Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/MarkerVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/MarkerVisualization.java
index c98ca83b..f2140502 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/MarkerVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/MarkerVisualization.java
@@ -54,121 +54,122 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
*
* @author Erich Schubert
*
- * @apiviz.uses StyleResult
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class MarkerVisualization extends AbstractScatterplotVisualization implements DataStoreListener {
+public class MarkerVisualization extends AbstractVisFactory {
/**
- * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * A short name characterizing this Visualizer.
*/
- public static final String DOTMARKER = "dot";
+ private static final String NAME = "Markers";
/**
- * The result we visualize
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- private StyleResult style;
-
- /**
- * Constructor.
- *
- * @param task Visualization task
- */
- public MarkerVisualization(VisualizationTask task) {
- super(task);
- this.style = task.getResult();
- context.addDataStoreListener(this);
- context.addResultListener(this);
- incrementalRedraw();
+ public MarkerVisualization() {
+ super();
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_STYLE;
}
@Override
- public void destroy() {
- super.destroy();
- context.removeDataStoreListener(this);
- context.removeResultListener(this);
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- public void redraw() {
- final MarkerLibrary ml = context.getStyleLibrary().markers();
- final double marker_size = context.getStyleLibrary().getSize(StyleLibrary.MARKERPLOT);
- final StylingPolicy spol = style.getStylingPolicy();
-
- if(spol instanceof ClassStylingPolicy) {
- ClassStylingPolicy cspol = (ClassStylingPolicy) spol;
- for(int cnum = cspol.getMinStyle(); cnum < cspol.getMaxStyle(); cnum++) {
- for(DBIDIter iter = cspol.iterateClass(cnum); iter.valid(); iter.advance()) {
- if(!sample.getSample().contains(iter)) {
- continue; // TODO: can we test more efficiently than this?
- }
- try {
- final NumberVector<?, ?> vec = rel.get(iter);
- double[] v = proj.fastProjectDataToRenderSpace(vec);
- ml.useMarker(svgp, layer, v[0], v[1], cnum, marker_size);
- }
- catch(ObjectNotFoundException e) {
- // ignore.
- }
- }
- }
- }
- else {
- final String FILL = SVGConstants.CSS_FILL_PROPERTY + ":";
- // Color-based styling. Fall back to dots
- for(DBIDIter iter = sample.getSample().iter(); iter.valid(); iter.advance()) {
- try {
- double[] v = proj.fastProjectDataToRenderSpace(rel.get(iter));
- Element dot = svgp.svgCircle(v[0], v[1], marker_size);
- SVGUtil.addCSSClass(dot, DOTMARKER);
- int col = spol.getColorForDBID(iter);
- SVGUtil.setAtt(dot, SVGConstants.SVG_STYLE_ATTRIBUTE, FILL + SVGUtil.colorToString(col));
- layer.appendChild(dot);
- }
- catch(ObjectNotFoundException e) {
- // ignore.
- }
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find a style result to visualize:
+ Collection<StyleResult> styleres = ResultUtil.filterResults(result, StyleResult.class);
+ for(StyleResult c : styleres) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA;
+ baseResult.getHierarchy().add(c, task);
+ baseResult.getHierarchy().add(p, task);
}
}
}
/**
- * Visualization factory
+ * Instance.
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses MarkerVisualization oneway - - «create»
+ * @apiviz.uses StyleResult
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractScatterplotVisualization implements DataStoreListener {
/**
- * A short name characterizing this Visualizer.
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
*/
- private static final String NAME = "Markers";
+ public static final String DOTMARKER = "dot";
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * The result we visualize
*/
- public Factory() {
- super();
- thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_STYLE;
+ private StyleResult style;
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.style = task.getResult();
+ context.addDataStoreListener(this);
+ context.addResultListener(this);
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new MarkerVisualization(task);
+ public void destroy() {
+ super.destroy();
+ context.removeDataStoreListener(this);
+ context.removeResultListener(this);
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // Find a style result to visualize:
- Collection<StyleResult> styleres = ResultUtil.filterResults(result, StyleResult.class);
- for(StyleResult c : styleres) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
- baseResult.getHierarchy().add(c, task);
- baseResult.getHierarchy().add(p, task);
+ public void redraw() {
+ final MarkerLibrary ml = style.getStyleLibrary().markers();
+ final double marker_size = style.getStyleLibrary().getSize(StyleLibrary.MARKERPLOT);
+ final StylingPolicy spol = style.getStylingPolicy();
+
+ if(spol instanceof ClassStylingPolicy) {
+ ClassStylingPolicy cspol = (ClassStylingPolicy) spol;
+ for(int cnum = cspol.getMinStyle(); cnum < cspol.getMaxStyle(); cnum++) {
+ for(DBIDIter iter = cspol.iterateClass(cnum); iter.valid(); iter.advance()) {
+ if(!sample.getSample().contains(iter)) {
+ continue; // TODO: can we test more efficiently than this?
+ }
+ try {
+ final NumberVector<?> vec = rel.get(iter);
+ double[] v = proj.fastProjectDataToRenderSpace(vec);
+ ml.useMarker(svgp, layer, v[0], v[1], cnum, marker_size);
+ }
+ catch(ObjectNotFoundException e) {
+ // ignore.
+ }
+ }
+ }
+ }
+ else {
+ final String FILL = SVGConstants.CSS_FILL_PROPERTY + ":";
+ // Color-based styling. Fall back to dots
+ for(DBIDIter iter = sample.getSample().iter(); iter.valid(); iter.advance()) {
+ try {
+ double[] v = proj.fastProjectDataToRenderSpace(rel.get(iter));
+ Element dot = svgp.svgCircle(v[0], v[1], marker_size);
+ SVGUtil.addCSSClass(dot, DOTMARKER);
+ int col = spol.getColorForDBID(iter);
+ SVGUtil.setAtt(dot, SVGConstants.SVG_STYLE_ATTRIBUTE, FILL + SVGUtil.colorToString(col));
+ layer.appendChild(dot);
+ }
+ catch(ObjectNotFoundException e) {
+ // ignore.
+ }
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/PolygonVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/PolygonVisualization.java
index 5b7c13b4..0fc99bbc 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/PolygonVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/PolygonVisualization.java
@@ -34,15 +34,14 @@ import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.exceptions.ObjectNotFoundException;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
-import de.lmu.ifi.dbs.elki.visualization.projections.Projection2D;
import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPath;
@@ -55,126 +54,123 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
*
* @author Erich Schubert
*
- * @apiviz.has PolygonsObject - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class PolygonVisualization extends AbstractScatterplotVisualization implements DataStoreListener {
+public class PolygonVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Polygons";
/**
- * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * Constructor
*/
- public static final String POLYS = "polys";
-
- /**
- * The current projection
- */
- final protected Projection2D proj;
-
- /**
- * The representation we visualize
- */
- final protected Relation<PolygonsObject> rep;
-
- /**
- * Constructor.
- *
- * @param task Task to visualize
- */
- public PolygonVisualization(VisualizationTask task) {
- super(task);
- this.proj = task.getProj();
- this.rep = task.getResult(); // Note: relation was used for projection
- context.addDataStoreListener(this);
- incrementalRedraw();
+ public PolygonVisualization() {
+ super();
}
@Override
- public void destroy() {
- super.destroy();
- context.removeDataStoreListener(this);
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- public void redraw() {
- CSSClass css = new CSSClass(svgp, POLYS);
- // TODO: separate fill and line colors?
- css.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.POLYGONS));
- css.setStatement(SVGConstants.CSS_STROKE_PROPERTY, context.getStyleLibrary().getColor(StyleLibrary.POLYGONS));
- css.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- svgp.addCSSClassOrLogError(css);
- svgp.updateStyleElement();
-
- // draw data
- for(DBIDIter iditer = rep.iterDBIDs(); iditer.valid(); iditer.advance()) {
- try {
- PolygonsObject poly = rep.get(iditer);
- if(poly == null) {
- continue;
- }
- SVGPath path = new SVGPath();
- for(Polygon ppoly : poly.getPolygons()) {
- Vector first = ppoly.get(0);
- double[] f = proj.fastProjectDataToRenderSpace(first.getArrayRef());
- path.moveTo(f[0], f[1]);
- for(Vector v : ppoly) {
- if(v == first) {
- continue;
- }
- double[] p = proj.fastProjectDataToRenderSpace(v.getArrayRef());
- path.drawTo(p[0], p[1]);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<Relation<?>> results = ResultUtil.filterResults(result, Relation.class);
+ for(Relation<?> rel : results) {
+ if(TypeUtil.POLYGON_TYPE.isAssignableFromType(rel.getDataTypeInformation())) {
+ // Assume that a 2d projector is using the same coordinates as the
+ // polygons.
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ if(RelationUtil.dimensionality(p.getRelation()) == 2) {
+ final VisualizationTask task = new VisualizationTask(NAME, rel, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA - 10;
+ baseResult.getHierarchy().add(rel, task);
+ baseResult.getHierarchy().add(p, task);
}
- // close path.
- path.drawTo(f[0], f[1]);
}
- Element e = path.makeElement(svgp);
- SVGUtil.addCSSClass(e, POLYS);
- layer.appendChild(e);
- }
- catch(ObjectNotFoundException e) {
- // ignore.
}
}
}
/**
- * The visualization factory
+ * Instance
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses PolygonVisualization oneway - - «create»
+ * @apiviz.has PolygonsObject - - visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractScatterplotVisualization implements DataStoreListener {
+ /**
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String POLYS = "polys";
+
/**
- * Constructor
+ * The representation we visualize
*/
- public Factory() {
- super();
+ final protected Relation<PolygonsObject> rep;
+
+ /**
+ * Constructor.
+ *
+ * @param task Task to visualize
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.rep = task.getResult(); // Note: relation was used for projection
+ context.addDataStoreListener(this);
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new PolygonVisualization(task);
+ public void destroy() {
+ super.destroy();
+ context.removeDataStoreListener(this);
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<Relation<?>> results = ResultUtil.filterResults(result, Relation.class);
- for(Relation<?> rel : results) {
- if(TypeUtil.POLYGON_TYPE.isAssignableFromType(rel.getDataTypeInformation())) {
- // Assume that a 2d projector is using the same coordinates as the polygons.
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- if(DatabaseUtil.dimensionality(p.getRelation()) == 2) {
- final VisualizationTask task = new VisualizationTask(NAME, rel, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 10);
- baseResult.getHierarchy().add(rel, task);
- baseResult.getHierarchy().add(p, task);
+ public void redraw() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ CSSClass css = new CSSClass(svgp, POLYS);
+ // TODO: separate fill and line colors?
+ css.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.POLYGONS));
+ css.setStatement(SVGConstants.CSS_STROKE_PROPERTY, style.getColor(StyleLibrary.POLYGONS));
+ css.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(css);
+ svgp.updateStyleElement();
+
+ // draw data
+ for(DBIDIter iditer = rep.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ try {
+ PolygonsObject poly = rep.get(iditer);
+ if(poly == null) {
+ continue;
+ }
+ SVGPath path = new SVGPath();
+ for(Polygon ppoly : poly.getPolygons()) {
+ Vector first = ppoly.get(0);
+ double[] f = proj.fastProjectDataToRenderSpace(first.getArrayRef());
+ path.moveTo(f[0], f[1]);
+ for(Vector v : ppoly) {
+ if(v == first) {
+ continue;
+ }
+ double[] p = proj.fastProjectDataToRenderSpace(v.getArrayRef());
+ path.drawTo(p[0], p[1]);
}
+ // close path.
+ path.drawTo(f[0], f[1]);
}
+ Element e = path.makeElement(svgp);
+ SVGUtil.addCSSClass(e, POLYS);
+ layer.appendChild(e);
+ }
+ catch(ObjectNotFoundException e) {
+ // ignore.
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ReferencePointsVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ReferencePointsVisualization.java
index 056b788b..94c1c8d2 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ReferencePointsVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ReferencePointsVisualization.java
@@ -46,98 +46,103 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
/**
* The actual visualization instance, for a single projection
*
+ * @author Remigius Wojdanowski
* @author Erich Schubert
*
- * @apiviz.has ReferencePointsResult oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-// TODO: add a result listener for the reference points.
-public class ReferencePointsVisualization extends AbstractScatterplotVisualization {
- /**
- * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
- */
- public static final String REFPOINT = "refpoint";
-
+public class ReferencePointsVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Reference Points";
/**
- * Serves reference points.
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- protected ReferencePointsResult<? extends NumberVector<?, ?>> result;
-
- /**
- * Constructor.
- *
- * @param task Visualization task
- */
- public ReferencePointsVisualization(VisualizationTask task) {
- super(task);
- this.result = task.getResult();
- incrementalRedraw();
+ public ReferencePointsVisualization() {
+ super();
}
@Override
- public void redraw() {
- setupCSS(svgp);
- Iterator<? extends NumberVector<?, ?>> iter = result.iterator();
-
- final double dotsize = context.getStyleLibrary().getSize(StyleLibrary.REFERENCE_POINTS);
- while(iter.hasNext()) {
- NumberVector<?, ?> v = iter.next();
- double[] projected = proj.fastProjectDataToRenderSpace(v);
- Element dot = svgp.svgCircle(projected[0], projected[1], dotsize);
- SVGUtil.addCSSClass(dot, REFPOINT);
- layer.appendChild(dot);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ReferencePointsResult<?>> rps = ResultUtil.filterResults(result, ReferencePointsResult.class);
+ for(ReferencePointsResult<?> rp : rps) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, rp, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA;
+ baseResult.getHierarchy().add(rp, task);
+ baseResult.getHierarchy().add(p, task);
+ }
}
}
- /**
- * Registers the Reference-Point-CSS-Class at a SVGPlot.
- *
- * @param svgp the SVGPlot to register the -CSS-Class.
- */
- private void setupCSS(SVGPlot svgp) {
- CSSClass refpoint = new CSSClass(svgp, REFPOINT);
- refpoint.setStatement(SVGConstants.CSS_FILL_PROPERTY, context.getStyleLibrary().getColor(StyleLibrary.REFERENCE_POINTS));
- svgp.addCSSClassOrLogError(refpoint);
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
/**
- * Generates a SVG-Element visualizing reference points.
+ * Instance.
*
* @author Remigius Wojdanowski
+ * @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses ReferencePointsVisualization oneway - - «create»
+ * @apiviz.has ReferencePointsResult oneway - - visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ // TODO: add a result listener for the reference points.
+ public class Instance extends AbstractScatterplotVisualization {
+ /**
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String REFPOINT = "refpoint";
+
+ /**
+ * Serves reference points.
+ */
+ protected ReferencePointsResult<? extends NumberVector<?>> result;
+
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Constructor.
+ *
+ * @param task Visualization task
*/
- public Factory() {
- super();
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.result = task.getResult();
+ incrementalRedraw();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<ReferencePointsResult<?>> rps = ResultUtil.filterResults(result, ReferencePointsResult.class);
- for(ReferencePointsResult<?> rp : rps) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, rp, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
- baseResult.getHierarchy().add(rp, task);
- baseResult.getHierarchy().add(p, task);
- }
+ public void redraw() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ setupCSS(svgp);
+ Iterator<? extends NumberVector<?>> iter = result.iterator();
+
+ final double dotsize = style.getSize(StyleLibrary.REFERENCE_POINTS);
+ while(iter.hasNext()) {
+ NumberVector<?> v = iter.next();
+ double[] projected = proj.fastProjectDataToRenderSpace(v);
+ Element dot = svgp.svgCircle(projected[0], projected[1], dotsize);
+ SVGUtil.addCSSClass(dot, REFPOINT);
+ layer.appendChild(dot);
}
}
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ReferencePointsVisualization(task);
+ /**
+ * Registers the Reference-Point-CSS-Class at a SVGPlot.
+ *
+ * @param svgp the SVGPlot to register the -CSS-Class.
+ */
+ private void setupCSS(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ CSSClass refpoint = new CSSClass(svgp, REFPOINT);
+ refpoint.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.REFERENCE_POINTS));
+ svgp.addCSSClassOrLogError(refpoint);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ToolBox2DVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ToolBox2DVisualization.java
index d0c05cc7..352e79de 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ToolBox2DVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/ToolBox2DVisualization.java
@@ -54,9 +54,10 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.VisualizerUtil;
*
* @author Heidi Kolb
*
- * @apiviz.has VisualizationTask oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class ToolBox2DVisualization extends AbstractScatterplotVisualization {
+public class ToolBox2DVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
@@ -65,241 +66,241 @@ public class ToolBox2DVisualization extends AbstractScatterplotVisualization {
/**
* The logger for this class.
*/
- private static final Logging logger = Logging.getLogger(Factory.class);
+ private static final Logging LOG = Logging.getLogger(ToolBox2DVisualization.class);
/**
- * CSS class for a tool button
+ * Constructor
*/
- public static final String CSS_TOOL_BUTTON = "toolButton";
-
- /**
- * CSS class for the tool button caption
- */
- public static final String CSS_TOOL_CAPTION = "toolCaption";
-
- /**
- * CSS class for a tool button
- */
- public static final String CSS_TOOL_BUTTON_SELECTED = "toolButtonSelected";
-
- /**
- * The container
- */
- private Element container;
-
- /**
- * Constructor.
- *
- * @param task Task
- */
- public ToolBox2DVisualization(VisualizationTask task) {
- super(task);
- // TODO: which result do we best attach to?
- context.addResultListener(this);
- incrementalRedraw();
+ public ToolBox2DVisualization() {
+ super();
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
- container = svgp.svgElement(SVGConstants.SVG_G_TAG);
- buildToolBox();
- layer.appendChild(container);
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
- /**
- * Deletes the children of the container
- *
- * @param container Element to delete children
- */
- private void deleteChildren(Element container) {
- while(container.hasChildNodes()) {
- container.removeChild(container.getLastChild());
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(result, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, p, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ task.thumbnail = false;
+ task.noexport = true;
+ task.noembed = true;
+ baseResult.getHierarchy().add(p, task);
}
}
/**
- * Build the toolbox
+ * Instance.
+ *
+ * @author Heidi Kolb
+ *
+ * @apiviz.has VisualizationTask oneway - - visualizes
*/
- private void buildToolBox() {
- double scale = StyleLibrary.SCALE;
- deleteChildren(container);
-
- ArrayList<VisualizationTask> vis = new ArrayList<VisualizationTask>();
- Collection<VisualizationTask> visualizers = ResultUtil.filterResults(task.getResult(), VisualizationTask.class);
- for(VisualizationTask task : visualizers) {
- if(VisualizerUtil.isTool(task) && !vis.contains(task)) {
- vis.add(task);
- }
- }
+ public class Instance extends AbstractScatterplotVisualization {
+ /**
+ * CSS class for a tool button
+ */
+ public static final String CSS_TOOL_BUTTON = "toolButton";
- // calculate the position of the first tool
- CanvasSize viewport = proj.estimateViewport();
- double x = viewport.getMinX() - 0.17 * scale;
- double width = 0.07 * scale;
- double height = 0.06 * scale;
- double miny = viewport.getMinY();
- double maxy = viewport.getMaxY();
- double y = (miny + maxy) / 2 - (vis.size() * height * 1.4) / 2;
- if(y < miny) {
- logger.warning("Too many Tools");
- }
+ /**
+ * CSS class for the tool button caption
+ */
+ public static final String CSS_TOOL_CAPTION = "toolCaption";
- // add tools
- Element[] toolTags = new Element[vis.size()];
- for(int i = 0; i < vis.size(); i++) {
- VisualizationTask v = vis.get(i);
- toolTags[i] = svgp.svgRect(x, y, width, height);
- String name = v.getLongName();
- // Split
- List<String> lines = FormatUtil.splitAtLastBlank(name, 8);
- // Generate label objects.
- for(int l = 0; l < lines.size(); l++) {
- Element selectRangeText = svgp.svgText(x + 0.01 * scale, y + (0.02 + 0.05 * l / lines.size()) * scale, lines.get(l));
- SVGUtil.setAtt(selectRangeText, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_TOOL_CAPTION);
- container.appendChild(selectRangeText);
- }
+ /**
+ * CSS class for a tool button
+ */
+ public static final String CSS_TOOL_BUTTON_SELECTED = "toolButtonSelected";
- if(VisualizerUtil.isVisible(v)) {
- SVGUtil.addCSSClass(toolTags[i], CSS_TOOL_BUTTON_SELECTED);
- }
- else {
- SVGUtil.addCSSClass(toolTags[i], CSS_TOOL_BUTTON);
- }
- addEventListener(toolTags[i], v);
+ /**
+ * The container
+ */
+ private Element container;
- container.appendChild(toolTags[i]);
- y = y + 0.1 * scale;
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ // TODO: which result do we best attach to?
+ context.addResultListener(this);
+ incrementalRedraw();
}
- }
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- // Class for the not selected tool
- if(!svgp.getCSSClassManager().contains(CSS_TOOL_BUTTON)) {
- final CSSClass modeCls = new CSSClass(this, CSS_TOOL_BUTTON);
- modeCls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.4);
- modeCls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_GREEN_VALUE);
- modeCls.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
-
- svgp.addCSSClassOrLogError(modeCls);
- }
- // Class for the selected tool
- if(!svgp.getCSSClassManager().contains(CSS_TOOL_BUTTON_SELECTED)) {
- final CSSClass modeCls = new CSSClass(this, CSS_TOOL_BUTTON_SELECTED);
- modeCls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.4);
- modeCls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLUE_VALUE);
- modeCls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, 0.4);
- modeCls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_BLUE_VALUE);
- modeCls.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
-
- svgp.addCSSClassOrLogError(modeCls);
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
+ container = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ buildToolBox();
+ layer.appendChild(container);
}
- // Class for the text of the tools
- if(!svgp.getCSSClassManager().contains(CSS_TOOL_CAPTION)) {
- final CSSClass label = new CSSClass(svgp, CSS_TOOL_CAPTION);
- label.setStatement(SVGConstants.CSS_FILL_PROPERTY, context.getStyleLibrary().getTextColor(StyleLibrary.AXIS_LABEL));
- label.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, context.getStyleLibrary().getFontFamily(StyleLibrary.AXIS_LABEL));
- label.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, context.getStyleLibrary().getTextSize(StyleLibrary.AXIS_LABEL) * .8);
-
- svgp.addCSSClassOrLogError(label);
+
+ /**
+ * Deletes the children of the container
+ *
+ * @param container Element to delete children
+ */
+ private void deleteChildren(Element container) {
+ while(container.hasChildNodes()) {
+ container.removeChild(container.getLastChild());
+ }
}
- }
- /**
- * Add an event listener to the Element
- *
- * @param tag Element to add the listener
- * @param tool Tool represented by the Element
- */
- private void addEventListener(final Element tag, final VisualizationTask tool) {
- EventTarget targ = (EventTarget) tag;
- targ.addEventListener(SVGConstants.SVG_EVENT_CLICK, new EventListener() {
- @Override
- public void handleEvent(Event evt) {
- handleMouseClick(tool);
+ /**
+ * Build the toolbox
+ */
+ private void buildToolBox() {
+ double scale = StyleLibrary.SCALE;
+ deleteChildren(container);
+
+ ArrayList<VisualizationTask> vis = new ArrayList<VisualizationTask>();
+ Collection<VisualizationTask> visualizers = ResultUtil.filterResults(task.getResult(), VisualizationTask.class);
+ for(VisualizationTask task : visualizers) {
+ if(task.tool && !vis.contains(task)) {
+ vis.add(task);
+ }
}
- }, false);
- }
- /**
- * Handle the mouseClick - change the selected tool in the context
- *
- * @param tool Selected Tool
- */
- protected void handleMouseClick(VisualizationTask tool) {
- // TODO: Move this to the selected tool instead?
- if(VisualizerUtil.isVisible(tool)) {
- context.setSelection(null);
- }
- VisualizerUtil.setVisible(context, tool, true);
- }
+ // calculate the position of the first tool
+ CanvasSize viewport = proj.estimateViewport();
+ double x = viewport.getMinX() - 0.17 * scale;
+ double width = 0.07 * scale;
+ double height = 0.06 * scale;
+ double miny = viewport.getMinY();
+ double maxy = viewport.getMaxY();
+ double y = (miny + maxy - vis.size() * height * 1.4) * .5;
+ if(y < miny) {
+ LOG.warning("Too many Tools");
+ }
- @Override
- public void resultAdded(Result child, Result parent) {
- if(child instanceof VisualizationTask) {
- VisualizationTask task = (VisualizationTask) child;
- if(VisualizerUtil.isTool(task)) {
- synchronizedRedraw();
+ // add tools
+ Element[] toolTags = new Element[vis.size()];
+ for(int i = 0; i < vis.size(); i++) {
+ VisualizationTask v = vis.get(i);
+ toolTags[i] = svgp.svgRect(x, y, width, height);
+ String name = v.getLongName();
+ // Split
+ List<String> lines = FormatUtil.splitAtLastBlank(name, 8);
+ // Generate label objects.
+ for(int l = 0; l < lines.size(); l++) {
+ Element selectRangeText = svgp.svgText(x + 0.01 * scale, y + (0.02 + 0.05 * l / lines.size()) * scale, lines.get(l));
+ SVGUtil.setAtt(selectRangeText, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_TOOL_CAPTION);
+ container.appendChild(selectRangeText);
+ }
+
+ if(v.visible) {
+ SVGUtil.addCSSClass(toolTags[i], CSS_TOOL_BUTTON_SELECTED);
+ }
+ else {
+ SVGUtil.addCSSClass(toolTags[i], CSS_TOOL_BUTTON);
+ }
+ addEventListener(toolTags[i], v);
+
+ container.appendChild(toolTags[i]);
+ y = y + 0.1 * scale;
}
}
- }
- @Override
- public void resultRemoved(Result child, Result parent) {
- if(child instanceof VisualizationTask) {
- VisualizationTask task = (VisualizationTask) child;
- if(VisualizerUtil.isTool(task)) {
- synchronizedRedraw();
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ // Class for the not selected tool
+ if(!svgp.getCSSClassManager().contains(CSS_TOOL_BUTTON)) {
+ final CSSClass modeCls = new CSSClass(this, CSS_TOOL_BUTTON);
+ modeCls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.4);
+ modeCls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_GREEN_VALUE);
+ modeCls.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
+
+ svgp.addCSSClassOrLogError(modeCls);
+ }
+ // Class for the selected tool
+ if(!svgp.getCSSClassManager().contains(CSS_TOOL_BUTTON_SELECTED)) {
+ final CSSClass modeCls = new CSSClass(this, CSS_TOOL_BUTTON_SELECTED);
+ modeCls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, 0.4);
+ modeCls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLUE_VALUE);
+ modeCls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, 0.4);
+ modeCls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_BLUE_VALUE);
+ modeCls.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
+
+ svgp.addCSSClassOrLogError(modeCls);
+ }
+ // Class for the text of the tools
+ if(!svgp.getCSSClassManager().contains(CSS_TOOL_CAPTION)) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final CSSClass label = new CSSClass(svgp, CSS_TOOL_CAPTION);
+ label.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getTextColor(StyleLibrary.AXIS_LABEL));
+ label.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, style.getFontFamily(StyleLibrary.AXIS_LABEL));
+ label.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, style.getTextSize(StyleLibrary.AXIS_LABEL) * .8);
+
+ svgp.addCSSClassOrLogError(label);
}
}
- }
- @Override
- public void resultChanged(Result current) {
- if(current instanceof VisualizationTask) {
- VisualizationTask task = (VisualizationTask) current;
- if(VisualizerUtil.isTool(task)) {
- synchronizedRedraw();
- }
+ /**
+ * Add an event listener to the Element
+ *
+ * @param tag Element to add the listener
+ * @param tool Tool represented by the Element
+ */
+ private void addEventListener(final Element tag, final VisualizationTask tool) {
+ EventTarget targ = (EventTarget) tag;
+ targ.addEventListener(SVGConstants.SVG_EVENT_CLICK, new EventListener() {
+ @Override
+ public void handleEvent(Event evt) {
+ handleMouseClick(tool);
+ }
+ }, false);
}
- }
- /**
- * Factory for visualizers for a toolbox
- *
- * @author Heidi Kolb
- *
- * @apiviz.stereotype factory
- * @apiviz.uses ToolBox2DVisualization oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
/**
- * Constructor
+ * Handle the mouseClick - change the selected tool in the context
+ *
+ * @param tool Selected Tool
*/
- public Factory() {
- super();
+ protected void handleMouseClick(VisualizationTask tool) {
+ // TODO: Move this to the selected tool instead?
+ if(tool.visible) {
+ context.setSelection(null);
+ }
+ VisualizerUtil.setVisible(context, tool, true);
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ToolBox2DVisualization(task);
+ public void resultAdded(Result child, Result parent) {
+ if(child instanceof VisualizationTask) {
+ VisualizationTask task = (VisualizationTask) child;
+ if(task.tool) {
+ synchronizedRedraw();
+ }
+ }
+ }
+
+ @Override
+ public void resultRemoved(Result child, Result parent) {
+ if(child instanceof VisualizationTask) {
+ VisualizationTask task = (VisualizationTask) child;
+ if(task.tool) {
+ synchronizedRedraw();
+ }
+ }
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(result, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, p, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
- task.put(VisualizationTask.META_NOTHUMB, true);
- task.put(VisualizationTask.META_NOEXPORT, true);
- task.put(VisualizationTask.META_NOEMBED, true);
- baseResult.getHierarchy().add(p, task);
+ public void resultChanged(Result current) {
+ if(current instanceof VisualizationTask) {
+ VisualizationTask task = (VisualizationTask) current;
+ if(task.tool) {
+ synchronizedRedraw();
+ }
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipScoreVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipScoreVisualization.java
index 139fa1ed..f3f35002 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipScoreVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipScoreVisualization.java
@@ -31,7 +31,7 @@ import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
@@ -56,8 +56,12 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* as the cursor lingers on the marker.
*
* @author Remigius Wojdanowski
+ * @author Erich Schubert
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class TooltipScoreVisualization extends AbstractTooltipVisualization {
+public class TooltipScoreVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
@@ -69,185 +73,182 @@ public class TooltipScoreVisualization extends AbstractTooltipVisualization {
public static final String NAME_GEN = " Tooltips";
/**
- * Number format.
+ * Settings
*/
- NumberFormat nf;
+ protected Parameterizer settings;
/**
- * Number value to visualize
- */
- private Relation<? extends Number> result;
-
- /**
- * Font size to use.
- */
- private double fontsize;
-
- /**
- * Constructor
+ * Constructor.
*
- * @param task Task
- * @param nf Number Format
+ * @param settings Settings
*/
- public TooltipScoreVisualization(VisualizationTask task, NumberFormat nf) {
- super(task);
- this.result = task.getResult();
- this.nf = nf;
- this.fontsize = 3 * context.getStyleLibrary().getTextSize(StyleLibrary.PLOT);
- synchronizedRedraw();
+ public TooltipScoreVisualization(Parameterizer settings) {
+ super();
+ this.settings = settings;
}
@Override
- protected Element makeTooltip(DBID id, double x, double y, double dotsize) {
- return svgp.svgText(x + dotsize, y + fontsize * 0.07, nf.format(result.get(id).doubleValue()));
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
- /**
- * Registers the Tooltip-CSS-Class at a SVGPlot.
- *
- * @param svgp the SVGPlot to register the Tooltip-CSS-Class.
- */
@Override
- protected void setupCSS(SVGPlot svgp) {
- final StyleLibrary style = context.getStyleLibrary();
- final double fontsize = style.getTextSize(StyleLibrary.PLOT);
- final String fontfamily = style.getFontFamily(StyleLibrary.PLOT);
-
- CSSClass tooltiphidden = new CSSClass(svgp, TOOLTIP_HIDDEN);
- tooltiphidden.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
- tooltiphidden.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
- tooltiphidden.setStatement(SVGConstants.CSS_DISPLAY_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- svgp.addCSSClassOrLogError(tooltiphidden);
-
- CSSClass tooltipvisible = new CSSClass(svgp, TOOLTIP_VISIBLE);
- tooltipvisible.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
- tooltipvisible.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
- svgp.addCSSClassOrLogError(tooltipvisible);
-
- CSSClass tooltipsticky = new CSSClass(svgp, TOOLTIP_STICKY);
- tooltipsticky.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
- tooltipsticky.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
- svgp.addCSSClassOrLogError(tooltipsticky);
-
- // invisible but sensitive area for the tooltip activator
- CSSClass tooltiparea = new CSSClass(svgp, TOOLTIP_AREA);
- tooltiparea.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_RED_VALUE);
- tooltiparea.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- tooltiparea.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, "0");
- tooltiparea.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
- svgp.addCSSClassOrLogError(tooltiparea);
-
- svgp.updateStyleElement();
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // TODO: we can also visualize other scores!
+ Collection<OutlierResult> ors = ResultUtil.filterResults(result, OutlierResult.class);
+ for(OutlierResult o : ors) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, o.getScores(), p.getRelation(), this);
+ task.tool = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(o.getScores(), task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ Collection<Relation<?>> rrs = ResultUtil.filterResults(result, Relation.class);
+ for(Relation<?> r : rrs) {
+ if(!TypeUtil.DOUBLE.isAssignableFromType(r.getDataTypeInformation()) && !TypeUtil.INTEGER.isAssignableFromType(r.getDataTypeInformation())) {
+ continue;
+ }
+ // Skip if we already considered it above
+ boolean add = true;
+ for(Result p : baseResult.getHierarchy().getChildren(r)) {
+ if(p instanceof VisualizationTask && ((VisualizationTask) p).getFactory() instanceof TooltipScoreVisualization) {
+ add = false;
+ break;
+ }
+ }
+ if(add) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(r.getLongName() + NAME_GEN, r, p.getRelation(), this);
+ task.tool = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(r, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
}
/**
- * Factory for tooltip visualizers
+ * Instance
*
+ * @author Remigius Wojdanowski
* @author Erich Schubert
- *
- * @apiviz.stereotype factory
- * @apiviz.uses TooltipScoreVisualization oneway - - «create»
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractTooltipVisualization {
/**
- * Parameter for the gamma-correction.
- *
- * <p>
- * Key: {@code -tooltip.digits}
- * </p>
- *
- * <p>
- * Default value: 4
- * </p>
+ * Number value to visualize
*/
- public static final OptionID DIGITS_ID = OptionID.getOrCreateOptionID("tooltip.digits", "Number of digits to show (e.g. when visualizing outlier scores)");
+ private Relation<? extends Number> result;
/**
- * Number formatter used for visualization
+ * Font size to use.
*/
- NumberFormat nf = null;
+ private double fontsize;
/**
- * Constructor.
+ * Constructor
*
- * @param digits number of digits
+ * @param task Task
*/
- public Factory(int digits) {
- super();
- nf = NumberFormat.getInstance(Locale.ROOT);
- nf.setGroupingUsed(false);
- nf.setMaximumFractionDigits(digits);
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.result = task.getResult();
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ this.fontsize = 3 * style.getTextSize(StyleLibrary.PLOT);
+ synchronizedRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new TooltipScoreVisualization(task, nf);
+ protected Element makeTooltip(DBIDRef id, double x, double y, double dotsize) {
+ return svgp.svgText(x + dotsize, y + fontsize * 0.07, settings.nf.format(result.get(id).doubleValue()));
}
+ /**
+ * Registers the Tooltip-CSS-Class at a SVGPlot.
+ *
+ * @param svgp the SVGPlot to register the Tooltip-CSS-Class.
+ */
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // TODO: we can also visualize other scores!
- Collection<OutlierResult> ors = ResultUtil.filterResults(result, OutlierResult.class);
- for(OutlierResult o : ors) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, o.getScores(), p.getRelation(), this);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(o.getScores(), task);
- baseResult.getHierarchy().add(p, task);
- }
- }
- Collection<Relation<?>> rrs = ResultUtil.filterResults(result, Relation.class);
- for(Relation<?> r : rrs) {
- if(!TypeUtil.DOUBLE.isAssignableFromType(r.getDataTypeInformation()) && !TypeUtil.INTEGER.isAssignableFromType(r.getDataTypeInformation())) {
- continue;
- }
- // Skip if we already considered it above
- boolean add = true;
- for(Result p : baseResult.getHierarchy().getChildren(r)) {
- if(p instanceof VisualizationTask && ((VisualizationTask) p).getFactory() instanceof Factory) {
- add = false;
- break;
- }
- }
- if(add) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(r.getLongName() + NAME_GEN, r, p.getRelation(), this);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(r, task);
- baseResult.getHierarchy().add(p, task);
- }
- }
- }
+ protected void setupCSS(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final double fontsize = style.getTextSize(StyleLibrary.PLOT);
+ final String fontfamily = style.getFontFamily(StyleLibrary.PLOT);
+
+ CSSClass tooltiphidden = new CSSClass(svgp, TOOLTIP_HIDDEN);
+ tooltiphidden.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
+ tooltiphidden.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
+ tooltiphidden.setStatement(SVGConstants.CSS_DISPLAY_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(tooltiphidden);
+
+ CSSClass tooltipvisible = new CSSClass(svgp, TOOLTIP_VISIBLE);
+ tooltipvisible.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
+ tooltipvisible.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
+ svgp.addCSSClassOrLogError(tooltipvisible);
+
+ CSSClass tooltipsticky = new CSSClass(svgp, TOOLTIP_STICKY);
+ tooltipsticky.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
+ tooltipsticky.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
+ svgp.addCSSClassOrLogError(tooltipsticky);
+
+ // invisible but sensitive area for the tooltip activator
+ CSSClass tooltiparea = new CSSClass(svgp, TOOLTIP_AREA);
+ tooltiparea.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_RED_VALUE);
+ tooltiparea.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ tooltiparea.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, "0");
+ tooltiparea.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
+ svgp.addCSSClassOrLogError(tooltiparea);
+
+ svgp.updateStyleElement();
}
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ /**
+ * Number formatter used for visualization
+ */
+ NumberFormat nf = null;
/**
- * Parameterization class.
+ * Parameter for the gamma-correction.
*
- * @author Erich Schubert
+ * <p>
+ * Key: {@code -tooltip.digits}
+ * </p>
*
- * @apiviz.exclude
+ * <p>
+ * Default value: 4
+ * </p>
*/
- public static class Parameterizer extends AbstractParameterizer {
- protected int digits = 4;
+ public static final OptionID DIGITS_ID = new OptionID("tooltip.digits", "Number of digits to show (e.g. when visualizing outlier scores)");
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- IntParameter DIGITS_PARAM = new IntParameter(DIGITS_ID, new GreaterEqualConstraint(0), 4);
-
- if(config.grab(DIGITS_PARAM)) {
- digits = DIGITS_PARAM.getValue();
- }
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ IntParameter digitsP = new IntParameter(DIGITS_ID, 4);
+ digitsP.addConstraint(new GreaterEqualConstraint(0));
+
+ if(config.grab(digitsP)) {
+ int digits = digitsP.intValue();
+ nf = NumberFormat.getInstance(Locale.ROOT);
+ nf.setGroupingUsed(false);
+ nf.setMaximumFractionDigits(digits);
}
+ }
- @Override
- protected Factory makeInstance() {
- return new Factory(digits);
- }
+ @Override
+ protected TooltipScoreVisualization makeInstance() {
+ return new TooltipScoreVisualization(this);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipStringVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipStringVisualization.java
index d015793c..979ce905 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipStringVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/TooltipStringVisualization.java
@@ -32,6 +32,7 @@ import de.lmu.ifi.dbs.elki.data.ClassLabel;
import de.lmu.ifi.dbs.elki.data.ExternalID;
import de.lmu.ifi.dbs.elki.data.LabelList;
import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
@@ -52,9 +53,10 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @author Remigius Wojdanowski
* @author Erich Schubert
*
- * @apiviz.has Relation oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class TooltipStringVisualization extends AbstractTooltipVisualization {
+public class TooltipStringVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
@@ -76,146 +78,147 @@ public class TooltipStringVisualization extends AbstractTooltipVisualization {
public static final String NAME_EID = "External ID Tooltips";
/**
- * Number value to visualize
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- private Relation<?> result;
-
- /**
- * Font size to use.
- */
- private double fontsize;
-
- /**
- * Constructor.
- *
- * @param task Task
- */
- public TooltipStringVisualization(VisualizationTask task) {
- super(task);
- this.result = task.getResult();
- this.fontsize = 3 * context.getStyleLibrary().getTextSize(StyleLibrary.PLOT);
- synchronizedRedraw();
+ public TooltipStringVisualization() {
+ super();
}
@Override
- protected Element makeTooltip(DBID id, double x, double y, double dotsize) {
- final Object data = result.get(id);
- String label;
- if(data == null) {
- label = "null";
- }
- else {
- label = data.toString();
- }
- if(label == "" || label == null) {
- label = "null";
- }
- return svgp.svgText(x + dotsize, y + fontsize * 0.07, label);
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
- /**
- * Registers the Tooltip-CSS-Class at a SVGPlot.
- *
- * @param svgp the SVGPlot to register the Tooltip-CSS-Class.
- */
@Override
- protected void setupCSS(SVGPlot svgp) {
- final StyleLibrary style = context.getStyleLibrary();
- final double fontsize = style.getTextSize(StyleLibrary.PLOT);
- final String fontfamily = style.getFontFamily(StyleLibrary.PLOT);
-
- CSSClass tooltiphidden = new CSSClass(svgp, TOOLTIP_HIDDEN);
- tooltiphidden.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
- tooltiphidden.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
- tooltiphidden.setStatement(SVGConstants.CSS_DISPLAY_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- svgp.addCSSClassOrLogError(tooltiphidden);
-
- CSSClass tooltipvisible = new CSSClass(svgp, TOOLTIP_VISIBLE);
- tooltipvisible.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
- tooltipvisible.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
- svgp.addCSSClassOrLogError(tooltipvisible);
-
- CSSClass tooltipsticky = new CSSClass(svgp, TOOLTIP_STICKY);
- tooltipsticky.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
- tooltipsticky.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
- svgp.addCSSClassOrLogError(tooltipsticky);
-
- // invisible but sensitive area for the tooltip activator
- CSSClass tooltiparea = new CSSClass(svgp, TOOLTIP_AREA);
- tooltiparea.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_RED_VALUE);
- tooltiparea.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- tooltiparea.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, "0");
- tooltiparea.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
- svgp.addCSSClassOrLogError(tooltiparea);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<Relation<?>> reps = ResultUtil.filterResults(result, Relation.class);
+ for(Relation<?> rep : reps) {
+ if(DBID.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME_ID, rep, p.getRelation(), this);
+ task.tool = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(rep, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ if(ClassLabel.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME_CLASS, rep, p.getRelation(), this);
+ task.tool = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(rep, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ if(LabelList.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME_LABEL, rep, p.getRelation(), this);
+ task.tool = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(rep, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ if(ExternalID.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME_EID, rep, p.getRelation(), this);
+ task.tool = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(rep, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
}
/**
- * Factory
+ * Instance
*
+ * @author Remigius Wojdanowski
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses TooltipStringVisualization oneway - - «create»
+ * @apiviz.has Relation oneway - - visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractTooltipVisualization {
+ /**
+ * Number value to visualize
+ */
+ private Relation<?> result;
+
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Font size to use.
*/
- public Factory() {
- super();
+ private double fontsize;
+
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.result = task.getResult();
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ this.fontsize = 3 * style.getTextSize(StyleLibrary.PLOT);
+ synchronizedRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new TooltipStringVisualization(task);
+ protected Element makeTooltip(DBIDRef id, double x, double y, double dotsize) {
+ final Object data = result.get(id);
+ String label;
+ if(data == null) {
+ label = "null";
+ }
+ else {
+ label = data.toString();
+ }
+ if(label == "" || label == null) {
+ label = "null";
+ }
+ return svgp.svgText(x + dotsize, y + fontsize * 0.07, label);
}
+ /**
+ * Registers the Tooltip-CSS-Class at a SVGPlot.
+ *
+ * @param svgp the SVGPlot to register the Tooltip-CSS-Class.
+ */
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<Relation<?>> reps = ResultUtil.filterResults(result, Relation.class);
- for(Relation<?> rep : reps) {
- if(DBID.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME_ID, rep, p.getRelation(), this);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(rep, task);
- baseResult.getHierarchy().add(p, task);
- }
- }
- if(ClassLabel.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME_CLASS, rep, p.getRelation(), this);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(rep, task);
- baseResult.getHierarchy().add(p, task);
- }
- }
- if(LabelList.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME_LABEL, rep, p.getRelation(), this);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(rep, task);
- baseResult.getHierarchy().add(p, task);
- }
- }
- if(ExternalID.class.isAssignableFrom(rep.getDataTypeInformation().getRestrictionClass())) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME_EID, rep, p.getRelation(), this);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(rep, task);
- baseResult.getHierarchy().add(p, task);
- }
- }
- }
+ protected void setupCSS(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final double fontsize = style.getTextSize(StyleLibrary.PLOT);
+ final String fontfamily = style.getFontFamily(StyleLibrary.PLOT);
+
+ CSSClass tooltiphidden = new CSSClass(svgp, TOOLTIP_HIDDEN);
+ tooltiphidden.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
+ tooltiphidden.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
+ tooltiphidden.setStatement(SVGConstants.CSS_DISPLAY_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(tooltiphidden);
+
+ CSSClass tooltipvisible = new CSSClass(svgp, TOOLTIP_VISIBLE);
+ tooltipvisible.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
+ tooltipvisible.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
+ svgp.addCSSClassOrLogError(tooltipvisible);
+
+ CSSClass tooltipsticky = new CSSClass(svgp, TOOLTIP_STICKY);
+ tooltipsticky.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, fontsize);
+ tooltipsticky.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, fontfamily);
+ svgp.addCSSClassOrLogError(tooltipsticky);
+
+ // invisible but sensitive area for the tooltip activator
+ CSSClass tooltiparea = new CSSClass(svgp, TOOLTIP_AREA);
+ tooltiparea.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_RED_VALUE);
+ tooltiparea.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ tooltiparea.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, "0");
+ tooltiparea.setStatement(SVGConstants.CSS_CURSOR_PROPERTY, SVGConstants.CSS_POINTER_VALUE);
+ svgp.addCSSClassOrLogError(tooltiparea);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterHullVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterHullVisualization.java
index e1817b1e..ebaf9eb9 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterHullVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterHullVisualization.java
@@ -69,207 +69,201 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatter
* @author Robert Rödler
* @author Erich Schubert
*
- * @apiviz.has Clustering oneway - - visualizes
- * @apiviz.uses GrahamScanConvexHull2D
- * @apiviz.uses AlphaShape
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class ClusterHullVisualization extends AbstractScatterplotVisualization {
+public class ClusterHullVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Cluster Hull Visualization";
/**
- * Generic tags to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * Settings
*/
- public static final String CLUSTERHULL = "cluster-hull";
+ Parameterizer settings;
/**
- * The result we work on
- */
- Clustering<Model> clustering;
-
- /**
- * Alpha value
- */
- double alpha = Double.POSITIVE_INFINITY;
-
- /**
- * Constructor
+ * Constructor.
*
- * @param task VisualizationTask
- * @param alpha Alpha value
+ * @param settings Settings
*/
- public ClusterHullVisualization(VisualizationTask task, double alpha) {
- super(task);
- this.clustering = task.getResult();
- this.alpha = alpha;
- incrementalRedraw();
+ public ClusterHullVisualization(Parameterizer settings) {
+ super();
+ this.settings = settings;
}
@Override
- protected void redraw() {
- // Viewport size, for "relative size" computations
- final CanvasSize viewp = proj.estimateViewport();
- double projarea = viewp.getDiffX() * viewp.getDiffY();
-
- double opacity = 0.25;
-
- Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
-
- for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
- Cluster<?> clus = ci.next();
- final DBIDs ids = clus.getIDs();
-
- if(alpha >= Double.POSITIVE_INFINITY) {
- GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
-
- for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- double[] projP = proj.fastProjectDataToRenderSpace(rel.get(iter));
- hull.add(new Vector(projP));
- }
- Polygon chres = hull.getHull();
-
- // Plot the convex hull:
- if(chres != null && chres.size() > 1) {
- SVGPath path = new SVGPath(chres);
- // Approximate area (using bounding box)
- double hullarea = SpatialUtil.volume(chres);
- final double relativeArea = (projarea - hullarea) / projarea;
- final double relativeSize = (double) ids.size() / rel.size();
- opacity = Math.sqrt(relativeSize * relativeArea);
-
- Element hulls = path.makeElement(svgp);
- addCSSClasses(svgp, cnum, opacity);
- SVGUtil.addCSSClass(hulls, CLUSTERHULL + cnum);
- layer.appendChild(hulls);
- }
- }
- else {
- ArrayList<Vector> ps = new ArrayList<Vector>(ids.size());
- for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
- double[] projP = proj.fastProjectDataToRenderSpace(rel.get(iter));
- ps.add(new Vector(projP));
- }
- List<Polygon> polys = (new AlphaShape(ps, alpha * Projection.SCALE)).compute();
- for(Polygon p : polys) {
- SVGPath path = new SVGPath(p);
- Element hulls = path.makeElement(svgp);
- addCSSClasses(svgp, cnum, 0.5);
- SVGUtil.addCSSClass(hulls, CLUSTERHULL + cnum);
- layer.appendChild(hulls);
- }
- }
- }
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp, int clusterID, double opac) {
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
-
- CSSClass cls = new CSSClass(this, CLUSTERHULL + clusterID);
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
-
- final String color;
- if(clustering.getAllClusters().size() == 1) {
- color = "black";
- }
- else {
- color = colors.getColor(clusterID);
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find clusterings we can visualize:
+ Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
+ for(Clustering<?> c : clusterings) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA - 1;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(c, task);
+ baseResult.getHierarchy().add(p, task);
+ }
}
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
- cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, opac);
-
- svgp.addCSSClassOrLogError(cls);
}
/**
- * Factory for visualizers to generate an SVG-Element containing the convex
- * hull or alpha shape of a cluster.
+ * Instance.
*
* @author Robert Rödler
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses ClusterHullVisualization oneway - - «create»
+ * @apiviz.has Clustering oneway - - visualizes
+ * @apiviz.uses GrahamScanConvexHull2D
+ * @apiviz.uses AlphaShape
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractScatterplotVisualization {
/**
- * Alpha value
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
*/
- double alpha = Double.POSITIVE_INFINITY;
+ public static final String CLUSTERHULL = "cluster-hull";
/**
- * Constructor.
- *
- * @param alpha Alpha value
+ * The result we work on
*/
- public Factory(double alpha) {
- super();
- this.alpha = alpha;
- }
+ Clustering<Model> clustering;
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ClusterHullVisualization(task, alpha);
+ /**
+ * Constructor
+ *
+ * @param task VisualizationTask
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.clustering = task.getResult();
+ incrementalRedraw();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // Find clusterings we can visualize:
- Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
- for(Clustering<?> c : clusterings) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 1);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(c, task);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ // Viewport size, for "relative size" computations
+ final CanvasSize viewp = proj.estimateViewport();
+ double projarea = viewp.getDiffX() * viewp.getDiffY();
+
+ double opacity = 0.25;
+
+ Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
+
+ for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
+ Cluster<?> clus = ci.next();
+ final DBIDs ids = clus.getIDs();
+
+ if(settings.alpha >= Double.POSITIVE_INFINITY) {
+ GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
+
+ for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ double[] projP = proj.fastProjectDataToRenderSpace(rel.get(iter));
+ hull.add(new Vector(projP));
+ }
+ Polygon chres = hull.getHull();
+
+ // Plot the convex hull:
+ if(chres != null && chres.size() > 1) {
+ SVGPath path = new SVGPath(chres);
+ // Approximate area (using bounding box)
+ double hullarea = SpatialUtil.volume(chres);
+ final double relativeArea = (projarea - hullarea) / projarea;
+ final double relativeSize = (double) ids.size() / rel.size();
+ opacity = Math.sqrt(relativeSize * relativeArea);
+
+ Element hulls = path.makeElement(svgp);
+ addCSSClasses(svgp, cnum, opacity);
+ SVGUtil.addCSSClass(hulls, CLUSTERHULL + cnum);
+ layer.appendChild(hulls);
+ }
+ }
+ else {
+ ArrayList<Vector> ps = new ArrayList<Vector>(ids.size());
+ for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
+ double[] projP = proj.fastProjectDataToRenderSpace(rel.get(iter));
+ ps.add(new Vector(projP));
+ }
+ List<Polygon> polys = (new AlphaShape(ps, settings.alpha * Projection.SCALE)).compute();
+ for(Polygon p : polys) {
+ SVGPath path = new SVGPath(p);
+ Element hulls = path.makeElement(svgp);
+ addCSSClasses(svgp, cnum, 0.5);
+ SVGUtil.addCSSClass(hulls, CLUSTERHULL + cnum);
+ layer.appendChild(hulls);
+ }
}
}
}
/**
- * Parameterization class.
- *
- * @author Erich Schubert
+ * Adds the required CSS-Classes
*
- * @apiviz.exclude
+ * @param svgp SVG-Plot
*/
- public static class Parameterizer extends AbstractParameterizer {
- /**
- * Alpha-Value for alpha-shapes
- *
- * <p>
- * Key: {@code -hull.alpha}
- * </p>
- */
- public static final OptionID ALPHA_ID = OptionID.getOrCreateOptionID("hull.alpha", "Alpha value for hull drawing (in projected space!).");
-
- /**
- * Alpha value
- */
- double alpha = Double.POSITIVE_INFINITY;
-
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- DoubleParameter alphaP = new DoubleParameter(ALPHA_ID, Double.POSITIVE_INFINITY);
- if(config.grab(alphaP)) {
- alpha = alphaP.getValue();
- }
+ private void addCSSClasses(SVGPlot svgp, int clusterID, double opac) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+
+ CSSClass cls = new CSSClass(this, CLUSTERHULL + clusterID);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
+
+ final String color;
+ if(clustering.getAllClusters().size() == 1) {
+ color = "black";
+ }
+ else {
+ color = colors.getColor(clusterID);
}
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, opac);
+
+ svgp.addCSSClassOrLogError(cls);
+ }
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ /**
+ * Alpha-Value for alpha-shapes
+ *
+ * <p>
+ * Key: {@code -hull.alpha}
+ * </p>
+ */
+ public static final OptionID ALPHA_ID = new OptionID("hull.alpha", "Alpha value for hull drawing (in projected space!).");
+
+ /**
+ * Alpha value
+ */
+ double alpha = Double.POSITIVE_INFINITY;
- @Override
- protected Factory makeInstance() {
- return new Factory(alpha);
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ DoubleParameter alphaP = new DoubleParameter(ALPHA_ID, Double.POSITIVE_INFINITY);
+ if(config.grab(alphaP)) {
+ alpha = alphaP.doubleValue();
}
}
+
+ @Override
+ protected ClusterHullVisualization makeInstance() {
+ return new ClusterHullVisualization(this);
+ }
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterMeanVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterMeanVisualization.java
index 4f14f4ef..c07e5dca 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterMeanVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterMeanVisualization.java
@@ -35,7 +35,7 @@ import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.model.MeanModel;
import de.lmu.ifi.dbs.elki.data.model.MedoidModel;
import de.lmu.ifi.dbs.elki.data.model.Model;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
@@ -61,238 +61,235 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatter
*
* @author Heidi Kolb
*
- * @apiviz.has MeanModel oneway - - visualizes
- * @apiviz.has MedoidModel oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class ClusterMeanVisualization extends AbstractScatterplotVisualization {
+public class ClusterMeanVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Cluster Means";
/**
- * CSS class name for center of the means
+ * Settings
*/
- private final static String CSS_MEAN_CENTER = "mean-center";
-
- /**
- * CSS class name for center of the means
- */
- private final static String CSS_MEAN = "mean-marker";
-
- /**
- * CSS class name for center of the means
- */
- private final static String CSS_MEAN_STAR = "mean-star";
-
- /**
- * Clustering to visualize.
- */
- Clustering<Model> clustering;
-
- /**
- * Draw stars
- */
- boolean stars;
+ protected Parameterizer settings;
/**
* Constructor.
*
- * @param task Visualization task
- * @param stars Draw stars
+ * @param settings Settings
*/
- public ClusterMeanVisualization(VisualizationTask task, boolean stars) {
- super(task);
- this.clustering = task.getResult();
- this.stars = stars;
- incrementalRedraw();
+ public ClusterMeanVisualization(Parameterizer settings) {
+ super();
+ this.settings = settings;
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
-
- MarkerLibrary ml = context.getStyleLibrary().markers();
- double marker_size = context.getStyleLibrary().getSize(StyleLibrary.MARKERPLOT);
-
- Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
- for(int cnum = 0; ci.hasNext(); cnum++) {
- Cluster<Model> clus = ci.next();
- Model model = clus.getModel();
- double[] mean;
- if(model instanceof MeanModel) {
- @SuppressWarnings("unchecked")
- MeanModel<? extends NumberVector<?, ?>> mmodel = (MeanModel<? extends NumberVector<?, ?>>) model;
- mean = proj.fastProjectDataToRenderSpace(mmodel.getMean());
- }
- else if(model instanceof MedoidModel) {
- MedoidModel mmodel = (MedoidModel) model;
- mean = proj.fastProjectDataToRenderSpace(rel.get(mmodel.getMedoid()));
- }
- else {
- continue;
- }
-
- // add a greater Marker for the mean
- Element meanMarker = ml.useMarker(svgp, layer, mean[0], mean[1], cnum, marker_size * 3);
- SVGUtil.setAtt(meanMarker, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_MEAN);
-
- // Add a fine cross to mark the exact location of the mean.
- Element meanMarkerCenter = svgp.svgLine(mean[0] - .7, mean[1], mean[0] + .7, mean[1]);
- SVGUtil.setAtt(meanMarkerCenter, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_MEAN_CENTER);
- Element meanMarkerCenter2 = svgp.svgLine(mean[0], mean[1] - .7, mean[0], mean[1] + .7);
- SVGUtil.setAtt(meanMarkerCenter2, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_MEAN_CENTER);
-
- layer.appendChild(meanMarkerCenter);
- layer.appendChild(meanMarkerCenter2);
-
- if(stars) {
- SVGPath star = new SVGPath();
- for(DBID id : clus.getIDs()) {
- double[] obj = proj.fastProjectDataToRenderSpace(rel.get(id));
- star.moveTo(mean);
- star.drawTo(obj);
- }
- Element stare = star.makeElement(svgp);
- SVGUtil.setCSSClass(stare, CSS_MEAN_STAR + "_" + cnum);
- layer.appendChild(stare);
- }
- }
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- if(!svgp.getCSSClassManager().contains(CSS_MEAN_CENTER)) {
- CSSClass center = new CSSClass(this, CSS_MEAN_CENTER);
- center.setStatement(SVGConstants.CSS_STROKE_PROPERTY, context.getStyleLibrary().getTextColor(StyleLibrary.DEFAULT));
- center.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.AXIS_TICK) / 2);
- svgp.addCSSClassOrLogError(center);
- }
- if(!svgp.getCSSClassManager().contains(CSS_MEAN)) {
- CSSClass center = new CSSClass(this, CSS_MEAN);
- center.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, "0.7");
- svgp.addCSSClassOrLogError(center);
- }
- if(stars) {
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
-
- Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
- for(int cnum = 0; ci.hasNext(); cnum++) {
- ci.next();
- if(!svgp.getCSSClassManager().contains(CSS_MEAN_STAR + "_" + cnum)) {
- CSSClass center = new CSSClass(this, CSS_MEAN_STAR + "_" + cnum);
- center.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(cnum));
- center.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- center.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, "0.7");
- svgp.addCSSClassOrLogError(center);
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find clusterings we can visualize:
+ Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
+ for(Clustering<?> c : clusterings) {
+ if(c.getAllClusters().size() > 0) {
+ // Does the cluster have a model with cluster means?
+ if(testMeanModel(c)) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA + 1;
+ baseResult.getHierarchy().add(c, task);
+ baseResult.getHierarchy().add(p, task);
+ }
}
}
}
}
/**
- * Factory for visualizers to generate an SVG-Element containing a marker for
- * the mean in a KMeans-Clustering
+ * Instance.
*
* @author Heidi Kolb
*
- * @apiviz.stereotype factory
- * @apiviz.uses ClusterMeanVisualization oneway - - «create»
+ * @apiviz.has MeanModel oneway - - visualizes
+ * @apiviz.has MedoidModel oneway - - visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractScatterplotVisualization {
/**
- * Option ID for visualization of cluster means.
- *
- * <pre>
- * -cluster.stars
- * </pre>
+ * CSS class name for center of the means
+ */
+ private static final String CSS_MEAN_CENTER = "mean-center";
+
+ /**
+ * CSS class name for center of the means
+ */
+ private static final String CSS_MEAN = "mean-marker";
+
+ /**
+ * CSS class name for center of the means
*/
- public static final OptionID STARS_ID = OptionID.getOrCreateOptionID("cluster.stars", "Visualize mean-based clusters using stars.");
+ private static final String CSS_MEAN_STAR = "mean-star";
/**
- * Draw stars
+ * Clustering to visualize.
*/
- private boolean stars;
+ Clustering<Model> clustering;
/**
* Constructor.
*
- * @param stars Draw stars
+ * @param task Visualization task
*/
- public Factory(boolean stars) {
- super();
- this.stars = stars;
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.clustering = task.getResult();
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ClusterMeanVisualization(task, stars);
- }
+ protected void redraw() {
+ addCSSClasses(svgp);
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // Find clusterings we can visualize:
- Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
- for(Clustering<?> c : clusterings) {
- if(c.getAllClusters().size() > 0) {
- // Does the cluster have a model with cluster means?
- if(testMeanModel(c)) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA + 1);
- baseResult.getHierarchy().add(c, task);
- baseResult.getHierarchy().add(p, task);
- }
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ MarkerLibrary ml = style.markers();
+ double marker_size = style.getSize(StyleLibrary.MARKERPLOT);
+
+ Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
+ for(int cnum = 0; ci.hasNext(); cnum++) {
+ Cluster<Model> clus = ci.next();
+ Model model = clus.getModel();
+ double[] mean;
+ if(model instanceof MeanModel) {
+ @SuppressWarnings("unchecked")
+ MeanModel<? extends NumberVector<?>> mmodel = (MeanModel<? extends NumberVector<?>>) model;
+ mean = proj.fastProjectDataToRenderSpace(mmodel.getMean());
+ }
+ else if(model instanceof MedoidModel) {
+ MedoidModel mmodel = (MedoidModel) model;
+ mean = proj.fastProjectDataToRenderSpace(rel.get(mmodel.getMedoid()));
+ }
+ else {
+ continue;
+ }
+
+ // add a greater Marker for the mean
+ Element meanMarker = ml.useMarker(svgp, layer, mean[0], mean[1], cnum, marker_size * 3);
+ SVGUtil.setAtt(meanMarker, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_MEAN);
+
+ // Add a fine cross to mark the exact location of the mean.
+ Element meanMarkerCenter = svgp.svgLine(mean[0] - .7, mean[1], mean[0] + .7, mean[1]);
+ SVGUtil.setAtt(meanMarkerCenter, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_MEAN_CENTER);
+ Element meanMarkerCenter2 = svgp.svgLine(mean[0], mean[1] - .7, mean[0], mean[1] + .7);
+ SVGUtil.setAtt(meanMarkerCenter2, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_MEAN_CENTER);
+
+ layer.appendChild(meanMarkerCenter);
+ layer.appendChild(meanMarkerCenter2);
+
+ if(settings.stars) {
+ SVGPath star = new SVGPath();
+ for(DBIDIter id = clus.getIDs().iter(); id.valid(); id.advance()) {
+ double[] obj = proj.fastProjectDataToRenderSpace(rel.get(id));
+ star.moveTo(mean);
+ star.drawTo(obj);
}
+ Element stare = star.makeElement(svgp);
+ SVGUtil.setCSSClass(stare, CSS_MEAN_STAR + "_" + cnum);
+ layer.appendChild(stare);
}
}
}
/**
- * Test if the given clustering has a mean model.
+ * Adds the required CSS-Classes
*
- * @param c Clustering to inspect
- * @return true when the clustering has a mean or medoid model.
+ * @param svgp SVG-Plot
*/
- private static boolean testMeanModel(Clustering<?> c) {
- Model firstmodel = c.getAllClusters().get(0).getModel();
- if(firstmodel instanceof MeanModel<?>) {
- return true;
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ if(!svgp.getCSSClassManager().contains(CSS_MEAN_CENTER)) {
+ CSSClass center = new CSSClass(this, CSS_MEAN_CENTER);
+ center.setStatement(SVGConstants.CSS_STROKE_PROPERTY, style.getTextColor(StyleLibrary.DEFAULT));
+ center.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.AXIS_TICK) * .5);
+ svgp.addCSSClassOrLogError(center);
}
- if(firstmodel instanceof MedoidModel) {
- return true;
+ if(!svgp.getCSSClassManager().contains(CSS_MEAN)) {
+ CSSClass center = new CSSClass(this, CSS_MEAN);
+ center.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, "0.7");
+ svgp.addCSSClassOrLogError(center);
+ }
+ if(settings.stars) {
+ ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+
+ Iterator<Cluster<Model>> ci = clustering.getAllClusters().iterator();
+ for(int cnum = 0; ci.hasNext(); cnum++) {
+ ci.next();
+ if(!svgp.getCSSClassManager().contains(CSS_MEAN_STAR + "_" + cnum)) {
+ CSSClass center = new CSSClass(this, CSS_MEAN_STAR + "_" + cnum);
+ center.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(cnum));
+ center.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
+ center.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, "0.7");
+ svgp.addCSSClassOrLogError(center);
+ }
+ }
}
- return false;
}
+ }
+ /**
+ * Test if the given clustering has a mean model.
+ *
+ * @param c Clustering to inspect
+ * @return true when the clustering has a mean or medoid model.
+ */
+ private static boolean testMeanModel(Clustering<?> c) {
+ Model firstmodel = c.getAllClusters().get(0).getModel();
+ if(firstmodel instanceof MeanModel<?>) {
+ return true;
+ }
+ if(firstmodel instanceof MedoidModel) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
/**
- * Parameterization class.
- *
- * @author Erich Schubert
+ * Option ID for visualization of cluster means.
*
- * @apiviz.exclude
+ * <pre>
+ * -cluster.stars
+ * </pre>
*/
- public static class Parameterizer extends AbstractParameterizer {
- protected boolean stars = false;
-
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- Flag starsF = new Flag(STARS_ID);
- if(config.grab(starsF)) {
- stars = starsF.getValue();
- }
- }
+ public static final OptionID STARS_ID = new OptionID("cluster.stars", "Visualize mean-based clusters using stars.");
+
+ /**
+ * Whether to draw cluster stars
+ */
+ protected boolean stars = false;
- @Override
- protected Factory makeInstance() {
- return new Factory(stars);
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ Flag starsF = new Flag(STARS_ID);
+ if(config.grab(starsF)) {
+ stars = starsF.isTrue();
}
}
+
+ @Override
+ protected ClusterMeanVisualization makeInstance() {
+ return new ClusterMeanVisualization(this);
+ }
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterOrderVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterOrderVisualization.java
index 0d43875c..bd173e80 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterOrderVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/ClusterOrderVisualization.java
@@ -45,103 +45,105 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
/**
- * Cluster order visualizer.
+ * Cluster order visualizer: connect objects via the spanning tree the cluster
+ * order represents.
*
* @author Erich Schubert
*
- * @apiviz.has ClusterOrderResult oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-// TODO: listen for CLUSTER ORDER changes.
-public class ClusterOrderVisualization extends AbstractScatterplotVisualization implements DataStoreListener {
+public class ClusterOrderVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Predecessor Graph";
/**
- * CSS class name
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- private static final String CSSNAME = "predecessor";
-
- /**
- * The result we visualize
- */
- protected ClusterOrderResult<?> result;
-
- public ClusterOrderVisualization(VisualizationTask task) {
- super(task);
- result = task.getResult();
- context.addDataStoreListener(this);
- incrementalRedraw();
+ public ClusterOrderVisualization() {
+ super();
}
@Override
- public void destroy() {
- super.destroy();
- context.removeDataStoreListener(this);
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- public void redraw() {
- CSSClass cls = new CSSClass(this, CSSNAME);
- context.getStyleLibrary().lines().formatCSSClass(cls, 0, context.getStyleLibrary().getLineWidth(StyleLibrary.CLUSTERORDER));
-
- svgp.addCSSClassOrLogError(cls);
-
- for(ClusterOrderEntry<?> ce : result) {
- DBID thisId = ce.getID();
- DBID prevId = ce.getPredecessorID();
- if(thisId == null || prevId == null) {
- continue;
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ClusterOrderResult<DoubleDistance>> cos = ResultUtil.filterResults(result, ClusterOrderResult.class);
+ for(ClusterOrderResult<DoubleDistance> co : cos) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, co, p.getRelation(), this);
+ task.initDefaultVisibility(false);
+ task.level = VisualizationTask.LEVEL_DATA - 1;
+ baseResult.getHierarchy().add(co, task);
+ baseResult.getHierarchy().add(p, task);
}
- double[] thisVec = proj.fastProjectDataToRenderSpace(rel.get(thisId));
- double[] prevVec = proj.fastProjectDataToRenderSpace(rel.get(prevId));
-
- // FIXME: DO NOT COMMIT
- thisVec[0] = thisVec[0] * 0.95 + prevVec[0] * 0.05;
- thisVec[1] = thisVec[1] * 0.95 + prevVec[1] * 0.05;
-
- Element arrow = svgp.svgLine(prevVec[0], prevVec[1], thisVec[0], thisVec[1]);
- SVGUtil.setCSSClass(arrow, cls.getName());
-
- layer.appendChild(arrow);
}
}
/**
- * Visualize an OPTICS cluster order by drawing connection lines.
+ * Instance
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses ClusterOrderVisualization oneway - - «create»
+ * @apiviz.has ClusterOrderResult oneway - - visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ // TODO: listen for CLUSTER ORDER changes.
+ public class Instance extends AbstractScatterplotVisualization implements DataStoreListener {
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * CSS class name
*/
- public Factory() {
- super();
+ private static final String CSSNAME = "predecessor";
+
+ /**
+ * The result we visualize
+ */
+ protected ClusterOrderResult<?> result;
+
+ public Instance(VisualizationTask task) {
+ super(task);
+ result = task.getResult();
+ context.addDataStoreListener(this);
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new ClusterOrderVisualization(task);
+ public void destroy() {
+ super.destroy();
+ context.removeDataStoreListener(this);
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<ClusterOrderResult<DoubleDistance>> cos = ResultUtil.filterResults(result, ClusterOrderResult.class);
- for(ClusterOrderResult<DoubleDistance> co : cos) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, co, p.getRelation(), this);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 1);
- baseResult.getHierarchy().add(co, task);
- baseResult.getHierarchy().add(p, task);
+ public void redraw() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ CSSClass cls = new CSSClass(this, CSSNAME);
+ style.lines().formatCSSClass(cls, 0, style.getLineWidth(StyleLibrary.CLUSTERORDER));
+
+ svgp.addCSSClassOrLogError(cls);
+
+ for(ClusterOrderEntry<?> ce : result) {
+ DBID thisId = ce.getID();
+ DBID prevId = ce.getPredecessorID();
+ if(thisId == null || prevId == null) {
+ continue;
}
+ double[] thisVec = proj.fastProjectDataToRenderSpace(rel.get(thisId));
+ double[] prevVec = proj.fastProjectDataToRenderSpace(rel.get(prevId));
+
+ // FIXME: DO NOT COMMIT
+ thisVec[0] = thisVec[0] * 0.95 + prevVec[0] * 0.05;
+ thisVec[1] = thisVec[1] * 0.95 + prevVec[1] * 0.05;
+
+ Element arrow = svgp.svgLine(prevVec[0], prevVec[1], thisVec[0], thisVec[1]);
+ SVGUtil.setCSSClass(arrow, cls.getName());
+
+ layer.appendChild(arrow);
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/EMClusterVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/EMClusterVisualization.java
index 6070361e..2806812a 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/EMClusterVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/EMClusterVisualization.java
@@ -32,6 +32,7 @@ import org.w3c.dom.Element;
import de.lmu.ifi.dbs.elki.data.Cluster;
import de.lmu.ifi.dbs.elki.data.Clustering;
+import de.lmu.ifi.dbs.elki.data.DoubleVector;
import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.data.model.EMModel;
import de.lmu.ifi.dbs.elki.data.model.MeanModel;
@@ -69,114 +70,199 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatter
*
* @author Robert Rödler
*
- * @apiviz.has EMModel oneway - - visualizes
- * @apiviz.uses GrahamScanConvexHull2D
- *
- * @param <NV> Type of the NumberVector being visualized.
+ * @apiviz.stereotype factory
+ * @apiviz.uses EMClusterVisualization oneway - - «create»
*/
-// TODO: nicer stacking of n-fold hulls
-// TODO: can we find a proper sphere for 3+ dimensions?
-public class EMClusterVisualization<NV extends NumberVector<NV, ?>> extends AbstractScatterplotVisualization {
+public class EMClusterVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "EM Cluster Visualization";
/**
- * Generic tags to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * Constants for quantiles of standard deviation
*/
- public static final String EMBORDER = "EMClusterBorder";
+ final static double[] sigma = new double[] { 0.41, 0.223, 0.047 };
/**
- * The result we work on
+ * Constructor
*/
- Clustering<EMModel<NV>> clustering;
+ public EMClusterVisualization() {
+ super();
+ }
- private static final double KAPPA = SVGHyperSphere.EUCLIDEAN_KAPPA;
+ @Override
+ public Instance<DoubleVector> makeVisualization(VisualizationTask task) {
+ return new Instance<DoubleVector>(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find clusterings we can visualize:
+ Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
+ for(Clustering<?> c : clusterings) {
+ if(c.getAllClusters().size() > 0) {
+ // Does the cluster have a model with cluster means?
+ Clustering<MeanModel<DoubleVector>> mcls = findMeanModel(c);
+ if(mcls != null) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA + 3;
+ baseResult.getHierarchy().add(c, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
+ }
+ }
/**
- * StyleParameter:
+ * Test if the given clustering has a mean model.
+ *
+ * @param <NV> Vector type
+ * @param c Clustering to inspect
+ * @return the clustering cast to return a mean model, null otherwise.
*/
- private int times = 3;
+ @SuppressWarnings("unchecked")
+ private static <NV extends NumberVector<?>> Clustering<MeanModel<NV>> findMeanModel(Clustering<?> c) {
+ final Model firstModel = c.getAllClusters().get(0).getModel();
+ if(c.getAllClusters().get(0).getModel() instanceof MeanModel<?> && firstModel instanceof EMModel<?>) {
+ return (Clustering<MeanModel<NV>>) c;
+ }
+ return null;
+ }
- private int opacStyle = 1;
+ /**
+ * Instance.
+ *
+ * @author Robert Rödler
+ *
+ * @apiviz.has EMModel oneway - - visualizes
+ * @apiviz.uses GrahamScanConvexHull2D
+ *
+ * @param <NV> Type of the NumberVector being visualized.
+ */
+ // TODO: nicer stacking of n-fold hulls
+ // TODO: can we find a proper sphere for 3+ dimensions?
+ public class Instance<NV extends NumberVector<?>> extends AbstractScatterplotVisualization {
+ /**
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String EMBORDER = "EMClusterBorder";
- private int softBorder = 1;
+ /**
+ * The result we work on
+ */
+ Clustering<EMModel<NV>> clustering;
- private int drawStyle = 0;
+ private static final double KAPPA = SVGHyperSphere.EUCLIDEAN_KAPPA;
- final static double[] sigma = new double[] { 0.41, 0.223, 0.047 };
+ /**
+ * StyleParameter:
+ */
+ private int times = 3;
- /**
- * Constructor
- *
- * @param task VisualizationTask
- */
- public EMClusterVisualization(VisualizationTask task) {
- super(task);
- this.clustering = task.getResult();
- incrementalRedraw();
- }
+ private int opacStyle = 1;
- @Override
- protected void redraw() {
- // set styles
- addCSSClasses(svgp);
-
- // PCARunner
- PCARunner<NV> pcarun = ClassGenericsUtil.parameterizeOrAbort(PCARunner.class, new EmptyParameterization());
-
- Iterator<Cluster<EMModel<NV>>> ci = clustering.getAllClusters().iterator();
- for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
- Cluster<EMModel<NV>> clus = ci.next();
- DBIDs ids = clus.getIDs();
-
- if(ids.size() > 0) {
- Matrix covmat = clus.getModel().getCovarianceMatrix();
- NV centroid = clus.getModel().getMean();
- Vector cent = new Vector(proj.fastProjectDataToRenderSpace(centroid));
-
- // Compute the eigenvectors
- SortedEigenPairs eps = pcarun.processCovarMatrix(covmat).getEigenPairs();
-
- Vector[] pc = new Vector[eps.size()];
- for(int i = 0; i < eps.size(); i++) {
- EigenPair ep = eps.getEigenPair(i);
- Vector sev = ep.getEigenvector().times(Math.sqrt(ep.getEigenvalue()));
- pc[i] = new Vector(proj.fastProjectRelativeDataToRenderSpace(sev.getArrayRef()));
- }
- if(drawStyle != 0 || eps.size() == 2) {
- drawSphere2D(cnum, cent, pc);
+ private int softBorder = 1;
+
+ private int drawStyle = 0;
+
+ /**
+ * Constructor
+ *
+ * @param task VisualizationTask
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.clustering = task.getResult();
+ incrementalRedraw();
+ }
+
+ @Override
+ protected void redraw() {
+ // set styles
+ addCSSClasses(svgp);
+
+ // PCARunner
+ PCARunner<NV> pcarun = ClassGenericsUtil.parameterizeOrAbort(PCARunner.class, new EmptyParameterization());
+
+ Iterator<Cluster<EMModel<NV>>> ci = clustering.getAllClusters().iterator();
+ for(int cnum = 0; cnum < clustering.getAllClusters().size(); cnum++) {
+ Cluster<EMModel<NV>> clus = ci.next();
+ DBIDs ids = clus.getIDs();
+
+ if(ids.size() > 0) {
+ Matrix covmat = clus.getModel().getCovarianceMatrix();
+ NV centroid = clus.getModel().getMean();
+ Vector cent = new Vector(proj.fastProjectDataToRenderSpace(centroid));
+
+ // Compute the eigenvectors
+ SortedEigenPairs eps = pcarun.processCovarMatrix(covmat).getEigenPairs();
+
+ Vector[] pc = new Vector[eps.size()];
+ for(int i = 0; i < eps.size(); i++) {
+ EigenPair ep = eps.getEigenPair(i);
+ Vector sev = ep.getEigenvector().times(Math.sqrt(ep.getEigenvalue()));
+ pc[i] = new Vector(proj.fastProjectRelativeDataToRenderSpace(sev.getArrayRef()));
+ }
+ if(drawStyle != 0 || eps.size() == 2) {
+ drawSphere2D(cnum, cent, pc);
+ }
+ else {
+ Polygon chres = makeHullComplex(pc);
+ drawHullLines(cnum, cent, chres);
+ }
}
- else {
- Polygon chres = makeHullComplex(pc);
- drawHullLines(cnum, cent, chres);
+ }
+ }
+
+ protected void drawSphere2D(int cnum, Vector cent, Vector[] pc) {
+ for(int dim1 = 0; dim1 < pc.length - 1; dim1++) {
+ for(int dim2 = dim1 + 1; dim2 < pc.length; dim2++) {
+ for(int i = 1; i <= times; i++) {
+ SVGPath path = new SVGPath();
+
+ Vector direction1 = pc[dim1].times(KAPPA * i);
+ Vector direction2 = pc[dim2].times(KAPPA * i);
+
+ Vector p1 = cent.plusTimes(pc[dim1], i);
+ Vector p2 = cent.plusTimes(pc[dim2], i);
+ Vector p3 = cent.minusTimes(pc[dim1], i);
+ Vector p4 = cent.minusTimes(pc[dim2], i);
+
+ path.moveTo(p1);
+ path.cubicTo(p1.plus(direction2), p2.plus(direction1), p2);
+ path.cubicTo(p2.minus(direction1), p3.plus(direction2), p3);
+ path.cubicTo(p3.minus(direction2), p4.minus(direction1), p4);
+ path.cubicTo(p4.plus(direction1), p1.minus(direction2), p1);
+ path.close();
+
+ Element ellipse = path.makeElement(svgp);
+ SVGUtil.addCSSClass(ellipse, EMBORDER + cnum);
+ if(opacStyle == 1) {
+ CSSClass cls = new CSSClass(null, "temp");
+ double s = (i >= 1 && i <= sigma.length) ? sigma[i - 1] : 0.0;
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, s);
+ SVGUtil.setAtt(ellipse, SVGConstants.SVG_STYLE_ATTRIBUTE, cls.inlineCSS());
+ }
+ layer.appendChild(ellipse);
+ }
}
}
}
- }
- protected void drawSphere2D(int cnum, Vector cent, Vector[] pc) {
- for(int dim1 = 0; dim1 < pc.length - 1; dim1++) {
- for(int dim2 = dim1 + 1; dim2 < pc.length; dim2++) {
+ protected void drawHullLines(int cnum, Vector cent, Polygon chres) {
+ if(chres.size() > 1) {
for(int i = 1; i <= times; i++) {
SVGPath path = new SVGPath();
-
- Vector direction1 = pc[dim1].times(KAPPA * i);
- Vector direction2 = pc[dim2].times(KAPPA * i);
-
- Vector p1 = cent.plusTimes(pc[dim1], i);
- Vector p2 = cent.plusTimes(pc[dim2], i);
- Vector p3 = cent.minusTimes(pc[dim1], i);
- Vector p4 = cent.minusTimes(pc[dim2], i);
-
- path.moveTo(p1);
- path.cubicTo(p1.plus(direction2), p2.plus(direction1), p2);
- path.cubicTo(p2.minus(direction1), p3.plus(direction2), p3);
- path.cubicTo(p3.minus(direction2), p4.minus(direction1), p4);
- path.cubicTo(p4.plus(direction1), p1.minus(direction2), p1);
+ for(int p = 0; p < chres.size(); p++) {
+ Vector cur = cent.plusTimes(chres.get(p), i);
+ path.drawTo(cur);
+ }
path.close();
-
Element ellipse = path.makeElement(svgp);
SVGUtil.addCSSClass(ellipse, EMBORDER + cnum);
if(opacStyle == 1) {
@@ -189,18 +275,99 @@ public class EMClusterVisualization<NV extends NumberVector<NV, ?>> extends Abst
}
}
}
- }
- protected void drawHullLines(int cnum, Vector cent, Polygon chres) {
- if(chres.size() > 1) {
+ protected Polygon makeHull(Vector[] pc) {
+ GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
+
+ Vector diag = new Vector(0, 0);
+ for(int j = 0; j < pc.length; j++) {
+ hull.add(pc[j]);
+ hull.add(pc[j].times(-1));
+ for(int k = j + 1; k < pc.length; k++) {
+ Vector q = pc[k];
+ Vector ppq = pc[j].plus(q).timesEquals(MathUtil.SQRTHALF);
+ Vector pmq = pc[j].minus(q).timesEquals(MathUtil.SQRTHALF);
+ hull.add(ppq);
+ hull.add(ppq.times(-1));
+ hull.add(pmq);
+ hull.add(pmq.times(-1));
+ }
+ diag.plusEquals(pc[j]);
+ }
+ diag.timesEquals(1.0 / Math.sqrt(pc.length));
+ hull.add(diag);
+ hull.add(diag.times(-1));
+
+ Polygon chres = hull.getHull();
+ return chres;
+ }
+
+ protected Polygon makeHullComplex(Vector[] pc) {
+ GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
+
+ Vector diag = new Vector(0, 0);
+ for(int j = 0; j < pc.length; j++) {
+ hull.add(pc[j]);
+ hull.add(pc[j].times(-1));
+ for(int k = j + 1; k < pc.length; k++) {
+ Vector q = pc[k];
+ Vector ppq = pc[j].plus(q).timesEquals(MathUtil.SQRTHALF);
+ Vector pmq = pc[j].minus(q).timesEquals(MathUtil.SQRTHALF);
+ hull.add(ppq);
+ hull.add(ppq.times(-1));
+ hull.add(pmq);
+ hull.add(pmq.times(-1));
+ for(int l = k + 1; l < pc.length; l++) {
+ Vector r = pc[k];
+ Vector ppqpr = ppq.plus(r).timesEquals(Math.sqrt(1 / 3.));
+ Vector pmqpr = pmq.plus(r).timesEquals(Math.sqrt(1 / 3.));
+ Vector ppqmr = ppq.minus(r).timesEquals(Math.sqrt(1 / 3.));
+ Vector pmqmr = pmq.minus(r).timesEquals(Math.sqrt(1 / 3.));
+ hull.add(ppqpr);
+ hull.add(ppqpr.times(-1));
+ hull.add(pmqpr);
+ hull.add(pmqpr.times(-1));
+ hull.add(ppqmr);
+ hull.add(ppqmr.times(-1));
+ hull.add(pmqmr);
+ hull.add(pmqmr.times(-1));
+ }
+ }
+ diag.plusEquals(pc[j]);
+ }
+ diag.timesEquals(1.0 / Math.sqrt(pc.length));
+ hull.add(diag);
+ hull.add(diag.times(-1));
+ Polygon chres = hull.getHull();
+ return chres;
+ }
+
+ protected void drawHullArc(int cnum, Vector cent, Polygon chres) {
for(int i = 1; i <= times; i++) {
SVGPath path = new SVGPath();
+
+ ArrayList<Vector> delta = new ArrayList<Vector>(chres.size());
+ for(int p = 0; p < chres.size(); p++) {
+ Vector prev = chres.get((p - 1 + chres.size()) % chres.size());
+ Vector curr = chres.get(p);
+ Vector next = chres.get((p + 1) % chres.size());
+ Vector d1 = next.minus(curr).normalize();
+ Vector d2 = curr.minus(prev).normalize();
+ delta.add(d1.plus(d2));
+ // delta.add(next.minus(prev));
+ }
+
for(int p = 0; p < chres.size(); p++) {
- Vector cur = cent.plusTimes(chres.get(p), i);
- path.drawTo(cur);
+ Vector cur = cent.plus(chres.get(p));
+ Vector nex = cent.plus(chres.get((p + 1) % chres.size()));
+ Vector dcur = delta.get(p);
+ Vector dnex = delta.get((p + 1) % chres.size());
+ drawArc(path, cent, cur, nex, dcur, dnex, i);
}
path.close();
+
Element ellipse = path.makeElement(svgp);
+
SVGUtil.addCSSClass(ellipse, EMBORDER + cnum);
if(opacStyle == 1) {
CSSClass cls = new CSSClass(null, "temp");
@@ -211,262 +378,98 @@ public class EMClusterVisualization<NV extends NumberVector<NV, ?>> extends Abst
layer.appendChild(ellipse);
}
}
- }
-
- protected Polygon makeHull(Vector[] pc) {
- GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
-
- Vector diag = new Vector(0, 0);
- for(int j = 0; j < pc.length; j++) {
- hull.add(pc[j]);
- hull.add(pc[j].times(-1));
- for(int k = j + 1; k < pc.length; k++) {
- Vector q = pc[k];
- Vector ppq = pc[j].plus(q).timesEquals(MathUtil.SQRTHALF);
- Vector pmq = pc[j].minus(q).timesEquals(MathUtil.SQRTHALF);
- hull.add(ppq);
- hull.add(ppq.times(-1));
- hull.add(pmq);
- hull.add(pmq.times(-1));
- }
- diag.plusEquals(pc[j]);
- }
- diag.timesEquals(1.0 / Math.sqrt(pc.length));
- hull.add(diag);
- hull.add(diag.times(-1));
-
- Polygon chres = hull.getHull();
- return chres;
- }
-
- protected Polygon makeHullComplex(Vector[] pc) {
- GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
-
- Vector diag = new Vector(0, 0);
- for(int j = 0; j < pc.length; j++) {
- hull.add(pc[j]);
- hull.add(pc[j].times(-1));
- for(int k = j + 1; k < pc.length; k++) {
- Vector q = pc[k];
- Vector ppq = pc[j].plus(q).timesEquals(MathUtil.SQRTHALF);
- Vector pmq = pc[j].minus(q).timesEquals(MathUtil.SQRTHALF);
- hull.add(ppq);
- hull.add(ppq.times(-1));
- hull.add(pmq);
- hull.add(pmq.times(-1));
- for(int l = k + 1; l < pc.length; l++) {
- Vector r = pc[k];
- Vector ppqpr = ppq.plus(r).timesEquals(Math.sqrt(1 / 3.));
- Vector pmqpr = pmq.plus(r).timesEquals(Math.sqrt(1 / 3.));
- Vector ppqmr = ppq.minus(r).timesEquals(Math.sqrt(1 / 3.));
- Vector pmqmr = pmq.minus(r).timesEquals(Math.sqrt(1 / 3.));
- hull.add(ppqpr);
- hull.add(ppqpr.times(-1));
- hull.add(pmqpr);
- hull.add(pmqpr.times(-1));
- hull.add(ppqmr);
- hull.add(ppqmr.times(-1));
- hull.add(pmqmr);
- hull.add(pmqmr.times(-1));
- }
- }
- diag.plusEquals(pc[j]);
- }
- diag.timesEquals(1.0 / Math.sqrt(pc.length));
- hull.add(diag);
- hull.add(diag.times(-1));
- Polygon chres = hull.getHull();
- return chres;
- }
- protected void drawHullArc(int cnum, Vector cent, Polygon chres) {
- for(int i = 1; i <= times; i++) {
- SVGPath path = new SVGPath();
-
- ArrayList<Vector> delta = new ArrayList<Vector>(chres.size());
- for(int p = 0; p < chres.size(); p++) {
- Vector prev = chres.get((p - 1 + chres.size()) % chres.size());
- Vector curr = chres.get(p);
- Vector next = chres.get((p + 1) % chres.size());
- Vector d1 = next.minus(curr).normalize();
- Vector d2 = curr.minus(prev).normalize();
- delta.add(d1.plus(d2));
- // delta.add(next.minus(prev));
- }
-
- for(int p = 0; p < chres.size(); p++) {
- Vector cur = cent.plus(chres.get(p));
- Vector nex = cent.plus(chres.get((p + 1) % chres.size()));
- Vector dcur = delta.get(p);
- Vector dnex = delta.get((p + 1) % chres.size());
- drawArc(path, cent, cur, nex, dcur, dnex, i);
+ /**
+ * Draw an arc to simulate the hyper ellipse.
+ *
+ * @param path Path to draw to
+ * @param cent Center
+ * @param pre Previous point
+ * @param nex Next point
+ * @param scale Scaling factor
+ */
+ private void drawArc(SVGPath path, Vector cent, Vector pre, Vector nex, Vector oPrev, Vector oNext, double scale) {
+ // Delta vectors
+ final Vector rPrev = pre.minus(cent);
+ final Vector rNext = nex.minus(cent);
+ final Vector rPrNe = pre.minus(nex);
+ // Scaled fix points
+ final Vector sPrev = cent.plusTimes(rPrev, scale);
+ final Vector sNext = cent.plusTimes(rNext, scale);
+ // Orthogonal vectors to the relative vectors
+ // final Vector oPrev = new Vector(rPrev.get(1), -rPrev.get(0));
+ // final Vector oNext = new Vector(-rNext.get(1), rNext.get(0));
+
+ // Compute the intersection of rPrev+tp*oPrev and rNext+tn*oNext
+ // rPrNe == rPrev - rNext
+ final double zp = rPrNe.get(0) * oNext.get(1) - rPrNe.get(1) * oNext.get(0);
+ final double zn = rPrNe.get(0) * oPrev.get(1) - rPrNe.get(1) * oPrev.get(0);
+ final double n = oPrev.get(1) * oNext.get(0) - oPrev.get(0) * oNext.get(1);
+ if(n == 0) {
+ LoggingUtil.warning("Parallel?!?");
+ path.drawTo(sNext.get(0), sNext.get(1));
+ return;
}
- path.close();
+ final double tp = Math.abs(zp / n);
+ final double tn = Math.abs(zn / n);
+ // LoggingUtil.warning("tp: "+tp+" tn: "+tn);
- Element ellipse = path.makeElement(svgp);
+ // Guide points
+ final Vector gPrev = sPrev.plusTimes(oPrev, KAPPA * scale * tp);
+ final Vector gNext = sNext.minusTimes(oNext, KAPPA * scale * tn);
- SVGUtil.addCSSClass(ellipse, EMBORDER + cnum);
- if(opacStyle == 1) {
- CSSClass cls = new CSSClass(null, "temp");
- double s = (i >= 1 && i <= sigma.length) ? sigma[i - 1] : 0.0;
- cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, s);
- SVGUtil.setAtt(ellipse, SVGConstants.SVG_STYLE_ATTRIBUTE, cls.inlineCSS());
+ if(!path.isStarted()) {
+ path.moveTo(sPrev);
}
- layer.appendChild(ellipse);
+ // path.drawTo(sPrev);
+ // path.drawTo(gPrev);
+ // path.drawTo(gNext);
+ // path.drawTo(sNext));
+ // path.moveTo(sPrev);
+ // if(tp < 0 || tn < 0) {
+ // path.drawTo(sNext);
+ // }
+ // else {
+ path.cubicTo(gPrev, gNext, sNext);
+ // }
}
- }
- /**
- * Draw an arc to simulate the hyper ellipse.
- *
- * @param path Path to draw to
- * @param cent Center
- * @param pre Previous point
- * @param nex Next point
- * @param scale Scaling factor
- */
- private void drawArc(SVGPath path, Vector cent, Vector pre, Vector nex, Vector oPrev, Vector oNext, double scale) {
- // Delta vectors
- final Vector rPrev = pre.minus(cent);
- final Vector rNext = nex.minus(cent);
- final Vector rPrNe = pre.minus(nex);
- // Scaled fix points
- final Vector sPrev = cent.plusTimes(rPrev, scale);
- final Vector sNext = cent.plusTimes(rNext, scale);
- // Orthogonal vectors to the relative vectors
- // final Vector oPrev = new Vector(rPrev.get(1), -rPrev.get(0));
- // final Vector oNext = new Vector(-rNext.get(1), rNext.get(0));
-
- // Compute the intersection of rPrev+tp*oPrev and rNext+tn*oNext
- // rPrNe == rPrev - rNext
- final double zp = rPrNe.get(0) * oNext.get(1) - rPrNe.get(1) * oNext.get(0);
- final double zn = rPrNe.get(0) * oPrev.get(1) - rPrNe.get(1) * oPrev.get(0);
- final double n = oPrev.get(1) * oNext.get(0) - oPrev.get(0) * oNext.get(1);
- if(n == 0) {
- LoggingUtil.warning("Parallel?!?");
- path.drawTo(sNext.get(0), sNext.get(1));
- return;
- }
- final double tp = Math.abs(zp / n);
- final double tn = Math.abs(zn / n);
- // LoggingUtil.warning("tp: "+tp+" tn: "+tn);
-
- // Guide points
- final Vector gPrev = sPrev.plusTimes(oPrev, KAPPA * scale * tp);
- final Vector gNext = sNext.minusTimes(oNext, KAPPA * scale * tn);
-
- if(!path.isStarted()) {
- path.moveTo(sPrev);
- }
- // path.drawTo(sPrev);
- // path.drawTo(gPrev);
- // path.drawTo(gNext);
- // path.drawTo(sNext));
- // path.moveTo(sPrev);
- // if(tp < 0 || tn < 0) {
- // path.drawTo(sNext);
- // }
- // else {
- path.cubicTo(gPrev, gNext, sNext);
- // }
- }
-
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- if(!svgp.getCSSClassManager().contains(EMBORDER)) {
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
- String color;
- int clusterID = 0;
-
- for(@SuppressWarnings("unused")
- Cluster<?> cluster : clustering.getAllClusters()) {
- CSSClass cls = new CSSClass(this, EMBORDER + clusterID);
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT) / 2);
-
- if(clustering.getAllClusters().size() == 1) {
- color = "black";
- }
- else {
- color = colors.getColor(clusterID);
- }
- if(softBorder == 0) {
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
- }
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
- cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.15);
-
- svgp.addCSSClassOrLogError(cls);
- if(opacStyle == 0) {
- break;
- }
- clusterID++;
- }
- }
- }
-
- /**
- * Visualizer for generating SVG-Elements containing ellipses for first,
- * second and third standard deviation
- *
- * @author Robert Rödler
- *
- * @apiviz.stereotype factory
- * @apiviz.uses EMClusterVisualization oneway - - «create»
- *
- * @param <NV> Type of the NumberVector being visualized.
- */
- public static class Factory<NV extends NumberVector<NV, ?>> extends AbstractVisFactory {
/**
- * Constructor
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
*/
- public Factory() {
- super();
- }
-
- @Override
- public EMClusterVisualization<NV> makeVisualization(VisualizationTask task) {
- return new EMClusterVisualization<NV>(task);
- }
+ private void addCSSClasses(SVGPlot svgp) {
+ if(!svgp.getCSSClassManager().contains(EMBORDER)) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+ String color;
+ int clusterID = 0;
+
+ for(@SuppressWarnings("unused")
+ Cluster<?> cluster : clustering.getAllClusters()) {
+ CSSClass cls = new CSSClass(this, EMBORDER + clusterID);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) * .5);
+
+ if(clustering.getAllClusters().size() == 1) {
+ color = "black";
+ }
+ else {
+ color = colors.getColor(clusterID);
+ }
+ if(softBorder == 0) {
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, color);
+ }
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.15);
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // Find clusterings we can visualize:
- Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
- for(Clustering<?> c : clusterings) {
- if(c.getAllClusters().size() > 0) {
- // Does the cluster have a model with cluster means?
- Clustering<MeanModel<NV>> mcls = findMeanModel(c);
- if(mcls != null) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA + 3);
- baseResult.getHierarchy().add(c, task);
- baseResult.getHierarchy().add(p, task);
- }
+ svgp.addCSSClassOrLogError(cls);
+ if(opacStyle == 0) {
+ break;
}
+ clusterID++;
}
}
}
-
- /**
- * Test if the given clustering has a mean model.
- *
- * @param <NV> Vector type
- * @param c Clustering to inspect
- * @return the clustering cast to return a mean model, null otherwise.
- */
- @SuppressWarnings("unchecked")
- private static <NV extends NumberVector<NV, ?>> Clustering<MeanModel<NV>> findMeanModel(Clustering<?> c) {
- final Model firstModel = c.getAllClusters().get(0).getModel();
- if(c.getAllClusters().get(0).getModel() instanceof MeanModel<?> && firstModel instanceof EMModel<?>) {
- return (Clustering<MeanModel<NV>>) c;
- }
- return null;
- }
}
-}
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/VoronoiVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/VoronoiVisualization.java
index d6ce810c..7ae28f42 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/VoronoiVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/cluster/VoronoiVisualization.java
@@ -33,16 +33,17 @@ import org.w3c.dom.Element;
import de.lmu.ifi.dbs.elki.data.Cluster;
import de.lmu.ifi.dbs.elki.data.Clustering;
import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.model.KMeansModel;
import de.lmu.ifi.dbs.elki.data.model.MeanModel;
import de.lmu.ifi.dbs.elki.data.model.MedoidModel;
import de.lmu.ifi.dbs.elki.data.model.Model;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.math.geometry.SweepHullDelaunay2D;
import de.lmu.ifi.dbs.elki.math.geometry.SweepHullDelaunay2D.Triangle;
import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
@@ -65,13 +66,10 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatter
* See also: {@link de.lmu.ifi.dbs.elki.algorithm.clustering.kmeans.KMeansLloyd
* KMeans clustering}
*
- * @author Robert Rödler
- * @author Erich Schubert
- *
- * @apiviz.has MeanModel oneway - - visualizes
- * @apiviz.has MedoidModel oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class VoronoiVisualization extends AbstractScatterplotVisualization {
+public class VoronoiVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
@@ -90,238 +88,240 @@ public class VoronoiVisualization extends AbstractScatterplotVisualization {
* @apiviz.exclude
*/
public static enum Mode {
- VORONOI, DELAUNAY, V_AND_D
+ /**
+ * Draw Voronoi cells.
+ */
+ VORONOI,
+ /**
+ * Draw Delaunay triangulation.
+ */
+ DELAUNAY,
+ /**
+ * Draw both Delaunay and Voronoi.
+ */
+ V_AND_D
}
/**
- * The result we work on
+ * Settings.
*/
- Clustering<Model> clustering;
+ private Parameterizer settings;
/**
- * The Voronoi diagram
- */
- Element voronoi;
-
- /**
- * Active drawing mode.
- */
- private Mode mode;
-
- /**
- * Constructor
+ * Constructor.
*
- * @param task VisualizationTask
- * @param mode Drawing mode
+ * @param settings Drawing mode
*/
- public VoronoiVisualization(VisualizationTask task, Mode mode) {
- super(task);
- this.clustering = task.getResult();
- this.mode = mode;
- incrementalRedraw();
+ public VoronoiVisualization(Parameterizer settings) {
+ super();
+ this.settings = settings;
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
- final List<Cluster<Model>> clusters = clustering.getAllClusters();
-
- if(clusters.size() < 2) {
- return;
- }
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
- // Collect cluster means
- if(clusters.size() == 2) {
- ArrayList<double[]> means = new ArrayList<double[]>(clusters.size());
- {
- for(Cluster<Model> clus : clusters) {
- Model model = clus.getModel();
- double[] mean;
- if(model instanceof MeanModel) {
- @SuppressWarnings("unchecked")
- MeanModel<? extends NumberVector<?, ?>> mmodel = (MeanModel<? extends NumberVector<?, ?>>) model;
- mean = proj.fastProjectDataToRenderSpace(mmodel.getMean());
- }
- else if(model instanceof MedoidModel) {
- MedoidModel mmodel = (MedoidModel) model;
- mean = proj.fastProjectDataToRenderSpace(rel.get(mmodel.getMedoid()));
- }
- else {
- continue;
- }
- means.add(mean);
- }
- }
- if(mode == Mode.VORONOI || mode == Mode.V_AND_D) {
- Element path = VoronoiDraw.drawFakeVoronoi(proj, means).makeElement(svgp);
- SVGUtil.addCSSClass(path, KMEANSBORDER);
- layer.appendChild(path);
- }
- if(mode == Mode.DELAUNAY || mode == Mode.V_AND_D) {
- Element path = new SVGPath(means.get(0)).drawTo(means.get(1)).makeElement(svgp);
- SVGUtil.addCSSClass(path, KMEANSBORDER);
- layer.appendChild(path);
- }
- }
- else {
- ArrayList<Vector> vmeans = new ArrayList<Vector>(clusters.size());
- ArrayList<double[]> means = new ArrayList<double[]>(clusters.size());
- {
- for(Cluster<Model> clus : clusters) {
- Model model = clus.getModel();
- Vector mean;
- if(model instanceof MeanModel) {
- @SuppressWarnings("unchecked")
- MeanModel<? extends NumberVector<?, ?>> mmodel = (MeanModel<? extends NumberVector<?, ?>>) model;
- mean = mmodel.getMean().getColumnVector();
- }
- else if(model instanceof MedoidModel) {
- MedoidModel mmodel = (MedoidModel) model;
- mean = rel.get(mmodel.getMedoid()).getColumnVector();
- }
- else {
- continue;
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ // Find clusterings we can visualize:
+ Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
+ for (Clustering<?> c : clusterings) {
+ if (c.getAllClusters().size() > 0) {
+ // Does the cluster have a model with cluster means?
+ if (testMeanModel(c)) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for (ScatterPlotProjector<?> p : ps) {
+ if (RelationUtil.dimensionality(p.getRelation()) == 2) {
+ final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA + 3;
+ baseResult.getHierarchy().add(p, task);
+ baseResult.getHierarchy().add(c, task);
+ }
}
- vmeans.add(mean);
- means.add(mean.getArrayRef());
}
}
- // Compute Delaunay Triangulation
- ArrayList<Triangle> delaunay = new SweepHullDelaunay2D(vmeans).getDelaunay();
- if(mode == Mode.VORONOI || mode == Mode.V_AND_D) {
- Element path = VoronoiDraw.drawVoronoi(proj, delaunay, means).makeElement(svgp);
- SVGUtil.addCSSClass(path, KMEANSBORDER);
- layer.appendChild(path);
- }
- if(mode == Mode.DELAUNAY || mode == Mode.V_AND_D) {
- Element path = VoronoiDraw.drawDelaunay(proj, delaunay, means).makeElement(svgp);
- SVGUtil.addCSSClass(path, KMEANSBORDER);
- layer.appendChild(path);
- }
}
}
/**
- * Adds the required CSS-Classes
+ * Test if the given clustering has a mean model.
*
- * @param svgp SVG-Plot
+ * @param c Clustering to inspect
+ * @return true when the clustering has a mean or medoid model.
*/
- private void addCSSClasses(SVGPlot svgp) {
- // Class for the distance markers
- if(!svgp.getCSSClassManager().contains(KMEANSBORDER)) {
- CSSClass cls = new CSSClass(this, KMEANSBORDER);
- cls = new CSSClass(this, KMEANSBORDER);
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT) * .5);
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- svgp.addCSSClassOrLogError(cls);
+ private static boolean testMeanModel(Clustering<?> c) {
+ Model firstmodel = c.getAllClusters().get(0).getModel();
+ if (firstmodel instanceof KMeansModel<?>) {
+ return true;
+ }
+ if (firstmodel instanceof MedoidModel) {
+ return true;
}
+ return false;
}
/**
- * Factory for visualizers to generate an SVG-Element containing the lines
- * between kMeans clusters
+ * Instance.
*
* @author Robert Rödler
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses VoronoiVisualization oneway - - «create»
+ * @apiviz.has MeanModel oneway - - visualizes
+ * @apiviz.has MedoidModel oneway - - visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractScatterplotVisualization {
/**
- * Mode for drawing: Voronoi, Delaunay, both
- *
- * <p>
- * Key: {@code -voronoi.mode}
- * </p>
+ * The result we work on.
*/
- public static final OptionID MODE_ID = OptionID.getOrCreateOptionID("voronoi.mode", "Mode for drawing the voronoi cells (and/or delaunay triangulation)");
+ Clustering<Model> clustering;
/**
- * Drawing mode
+ * The Voronoi diagram.
*/
- private Mode mode;
+ Element voronoi;
/**
- * Constructor
+ * Constructor.
*
- * @param mode Drawing mode
+ * @param task VisualizationTask
*/
- public Factory(Mode mode) {
- super();
- this.mode = mode;
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.clustering = task.getResult();
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new VoronoiVisualization(task, mode);
- }
+ protected void redraw() {
+ addCSSClasses(svgp);
+ final List<Cluster<Model>> clusters = clustering.getAllClusters();
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- // Find clusterings we can visualize:
- Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class);
- for(Clustering<?> c : clusterings) {
- if(c.getAllClusters().size() > 0) {
- // Does the cluster have a model with cluster means?
- if(testMeanModel(c)) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- if(DatabaseUtil.dimensionality(p.getRelation()) == 2) {
- final VisualizationTask task = new VisualizationTask(NAME, c, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA + 3);
- baseResult.getHierarchy().add(p, task);
- baseResult.getHierarchy().add(c, task);
- }
+ if (clusters.size() < 2) {
+ return;
+ }
+
+ // Collect cluster means
+ if (clusters.size() == 2) {
+ ArrayList<double[]> means = new ArrayList<double[]>(clusters.size());
+ {
+ for (Cluster<Model> clus : clusters) {
+ Model model = clus.getModel();
+ double[] mean;
+ if (model instanceof MeanModel) {
+ @SuppressWarnings("unchecked")
+ MeanModel<? extends NumberVector<?>> mmodel = (MeanModel<? extends NumberVector<?>>) model;
+ mean = proj.fastProjectDataToRenderSpace(mmodel.getMean());
+ } else if (model instanceof MedoidModel) {
+ MedoidModel mmodel = (MedoidModel) model;
+ mean = proj.fastProjectDataToRenderSpace(rel.get(mmodel.getMedoid()));
+ } else {
+ continue;
}
+ means.add(mean);
}
}
+ if (settings.mode == Mode.VORONOI || settings.mode == Mode.V_AND_D) {
+ Element path = VoronoiDraw.drawFakeVoronoi(proj, means).makeElement(svgp);
+ SVGUtil.addCSSClass(path, KMEANSBORDER);
+ layer.appendChild(path);
+ }
+ if (settings.mode == Mode.DELAUNAY || settings.mode == Mode.V_AND_D) {
+ Element path = new SVGPath(means.get(0)).drawTo(means.get(1)).makeElement(svgp);
+ SVGUtil.addCSSClass(path, KMEANSBORDER);
+ layer.appendChild(path);
+ }
+ } else {
+ ArrayList<Vector> vmeans = new ArrayList<Vector>(clusters.size());
+ ArrayList<double[]> means = new ArrayList<double[]>(clusters.size());
+ {
+ for (Cluster<Model> clus : clusters) {
+ Model model = clus.getModel();
+ Vector mean;
+ if (model instanceof MeanModel) {
+ @SuppressWarnings("unchecked")
+ MeanModel<? extends NumberVector<?>> mmodel = (MeanModel<? extends NumberVector<?>>) model;
+ mean = mmodel.getMean().getColumnVector();
+ } else if (model instanceof MedoidModel) {
+ MedoidModel mmodel = (MedoidModel) model;
+ mean = rel.get(mmodel.getMedoid()).getColumnVector();
+ } else {
+ continue;
+ }
+ vmeans.add(mean);
+ means.add(mean.getArrayRef());
+ }
+ }
+ // Compute Delaunay Triangulation
+ ArrayList<Triangle> delaunay = new SweepHullDelaunay2D(vmeans).getDelaunay();
+ if (settings.mode == Mode.VORONOI || settings.mode == Mode.V_AND_D) {
+ Element path = VoronoiDraw.drawVoronoi(proj, delaunay, means).makeElement(svgp);
+ SVGUtil.addCSSClass(path, KMEANSBORDER);
+ layer.appendChild(path);
+ }
+ if (settings.mode == Mode.DELAUNAY || settings.mode == Mode.V_AND_D) {
+ Element path = VoronoiDraw.drawDelaunay(proj, delaunay, means).makeElement(svgp);
+ SVGUtil.addCSSClass(path, KMEANSBORDER);
+ layer.appendChild(path);
+ }
}
}
/**
- * Test if the given clustering has a mean model.
+ * Adds the required CSS-Classes.
*
- * @param c Clustering to inspect
- * @return true when the clustering has a mean or medoid model.
+ * @param svgp SVG-Plot
*/
- private static boolean testMeanModel(Clustering<?> c) {
- Model firstmodel = c.getAllClusters().get(0).getModel();
- if(firstmodel instanceof MeanModel<?>) {
- return true;
+ private void addCSSClasses(SVGPlot svgp) {
+ // Class for the distance markers
+ if (!svgp.getCSSClassManager().contains(KMEANSBORDER)) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ CSSClass cls = new CSSClass(this, KMEANSBORDER);
+ cls = new CSSClass(this, KMEANSBORDER);
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) * .5);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ svgp.addCSSClassOrLogError(cls);
}
- if(firstmodel instanceof MedoidModel) {
- return true;
- }
- return false;
}
+ }
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
/**
- * Parameterization class.
+ * Mode for drawing: Voronoi, Delaunay, both.
*
- * @author Erich Schubert
- *
- * @apiviz.exclude
+ * <p>
+ * Key: {@code -voronoi.mode}
+ * </p>
*/
- public static class Parameterizer extends AbstractParameterizer {
- protected Mode mode;
+ public static final OptionID MODE_ID = new OptionID("voronoi.mode", "Mode for drawing the voronoi cells (and/or delaunay triangulation)");
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- EnumParameter<Mode> modeP = new EnumParameter<Mode>(MODE_ID, Mode.class, Mode.VORONOI);
- if(config.grab(modeP)) {
- mode = modeP.getValue();
- }
- }
+ /**
+ * Drawing mode.
+ */
+ protected Mode mode;
- @Override
- protected Factory makeInstance() {
- return new Factory(mode);
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ EnumParameter<Mode> modeP = new EnumParameter<Mode>(MODE_ID, Mode.class, Mode.VORONOI);
+ if (config.grab(modeP)) {
+ mode = modeP.getValue();
}
}
+
+ @Override
+ protected VoronoiVisualization makeInstance() {
+ return new VoronoiVisualization(this);
+ }
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/density/DensityEstimationOverlay.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/density/DensityEstimationOverlay.java
index c07bc571..96658910 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/density/DensityEstimationOverlay.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/density/DensityEstimationOverlay.java
@@ -52,184 +52,184 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatter
* <em>in the projection, not the actual data!</em>
*
* @author Erich Schubert
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-// TODO: make parameterizable, in particular color map, kernel bandwidth and
-// kernel function
-public class DensityEstimationOverlay extends AbstractScatterplotVisualization {
+public class DensityEstimationOverlay extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Density estimation overlay";
/**
- * Density map resolution
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- private int resolution = 500;
+ public DensityEstimationOverlay() {
+ super();
+ }
- /**
- * The actual image
- */
- private BufferedImage img = null;
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(result, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, p.getRelation(), p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA + 1;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
/**
- * Constructor.
+ * Instance for a particular data set.
*
- * @param task Task
+ * @author Erich Schubert
*/
- public DensityEstimationOverlay(VisualizationTask task) {
- super(task);
- incrementalRedraw();
- }
+ // TODO: make parameterizable, in particular color map, kernel bandwidth and
+ // kernel function
+ public class Instance extends AbstractScatterplotVisualization {
+ /**
+ * Density map resolution
+ */
+ private int resolution = 500;
- @Override
- protected void redraw() {
- if(img == null) {
- renderImage();
+ /**
+ * The actual image
+ */
+ private BufferedImage img = null;
+
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
}
- CanvasSize canvas = proj.estimateViewport();
- String imguri = ThumbnailRegistryEntry.INTERNAL_PREFIX + ThumbnailRegistryEntry.registerImage(img);
- Element itag = svgp.svgElement(SVGConstants.SVG_IMAGE_TAG);
- SVGUtil.setAtt(itag, SVGConstants.SVG_IMAGE_RENDERING_ATTRIBUTE, SVGConstants.SVG_OPTIMIZE_SPEED_VALUE);
- SVGUtil.setAtt(itag, SVGConstants.SVG_X_ATTRIBUTE, canvas.minx);
- SVGUtil.setAtt(itag, SVGConstants.SVG_Y_ATTRIBUTE, canvas.miny);
- SVGUtil.setAtt(itag, SVGConstants.SVG_WIDTH_ATTRIBUTE, canvas.maxx - canvas.minx);
- SVGUtil.setAtt(itag, SVGConstants.SVG_HEIGHT_ATTRIBUTE, canvas.maxy - canvas.miny);
- SVGUtil.setAtt(itag, SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_OPACITY_PROPERTY + ": .5");
- itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, imguri);
-
- layer.appendChild(itag);
- }
+ @Override
+ protected void redraw() {
+ if(img == null) {
+ renderImage();
+ }
- @Reference(authors = "D. W. Scott", title = "Multivariate density estimation", booktitle = "Multivariate Density Estimation: Theory, Practice, and Visualization", url = "http://dx.doi.org/10.1002/9780470316849.fmatter")
- private double[] initializeBandwidth(double[][] data) {
- MeanVariance mv0 = new MeanVariance();
- MeanVariance mv1 = new MeanVariance();
- // For Kernel bandwidth.
- for(double[] projected : data) {
- mv0.put(projected[0]);
- mv1.put(projected[1]);
+ CanvasSize canvas = proj.estimateViewport();
+ String imguri = ThumbnailRegistryEntry.INTERNAL_PREFIX + ThumbnailRegistryEntry.registerImage(img);
+ Element itag = svgp.svgElement(SVGConstants.SVG_IMAGE_TAG);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_IMAGE_RENDERING_ATTRIBUTE, SVGConstants.SVG_OPTIMIZE_SPEED_VALUE);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_X_ATTRIBUTE, canvas.minx);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_Y_ATTRIBUTE, canvas.miny);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_WIDTH_ATTRIBUTE, canvas.maxx - canvas.minx);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_HEIGHT_ATTRIBUTE, canvas.maxy - canvas.miny);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_STYLE_ATTRIBUTE, SVGConstants.CSS_OPACITY_PROPERTY + ": .5");
+ itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, imguri);
+
+ layer.appendChild(itag);
}
- // Set bandwidths according to Scott's rule:
- // Note: in projected space, d=2.
- double[] bandwidth = new double[2];
- bandwidth[0] = MathUtil.SQRT5 * mv0.getSampleStddev() * Math.pow(rel.size(), -1 / 6.);
- bandwidth[1] = MathUtil.SQRT5 * mv1.getSampleStddev() * Math.pow(rel.size(), -1 / 6.);
- return bandwidth;
- }
- private void renderImage() {
- // TODO: SAMPLE? Do region queries?
- // Project the data just once, keep a copy.
- double[][] data = new double[rel.size()][];
- {
- int i = 0;
- for(DBIDIter iditer = rel.iterDBIDs(); iditer.valid(); iditer.advance()) {
- data[i] = proj.fastProjectDataToRenderSpace(rel.get(iditer));
- i++;
+ @Reference(authors = "D. W. Scott", title = "Multivariate density estimation", booktitle = "Multivariate Density Estimation: Theory, Practice, and Visualization", url = "http://dx.doi.org/10.1002/9780470316849.fmatter")
+ private double[] initializeBandwidth(double[][] data) {
+ MeanVariance mv0 = new MeanVariance();
+ MeanVariance mv1 = new MeanVariance();
+ // For Kernel bandwidth.
+ for(double[] projected : data) {
+ mv0.put(projected[0]);
+ mv1.put(projected[1]);
}
+ // Set bandwidths according to Scott's rule:
+ // Note: in projected space, d=2.
+ double[] bandwidth = new double[2];
+ bandwidth[0] = MathUtil.SQRT5 * mv0.getSampleStddev() * Math.pow(rel.size(), -1 / 6.);
+ bandwidth[1] = MathUtil.SQRT5 * mv1.getSampleStddev() * Math.pow(rel.size(), -1 / 6.);
+ return bandwidth;
}
- double[] bandwidth = initializeBandwidth(data);
- // Compare by first component
- Comparator<double[]> comp0 = new Comparator<double[]>() {
- @Override
- public int compare(double[] o1, double[] o2) {
- return Double.compare(o1[0], o2[0]);
- }
- };
- // Compare by second component
- Comparator<double[]> comp1 = new Comparator<double[]>() {
- @Override
- public int compare(double[] o1, double[] o2) {
- return Double.compare(o1[1], o2[1]);
+
+ private void renderImage() {
+ // TODO: SAMPLE? Do region queries?
+ // Project the data just once, keep a copy.
+ double[][] data = new double[rel.size()][];
+ {
+ int i = 0;
+ for(DBIDIter iditer = rel.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ data[i] = proj.fastProjectDataToRenderSpace(rel.get(iditer));
+ i++;
+ }
}
- };
- // TODO: choose comparator order based on smaller bandwidth?
- Arrays.sort(data, comp0);
-
- CanvasSize canvas = proj.estimateViewport();
- double min0 = canvas.minx, max0 = canvas.maxx, ste0 = (max0 - min0) / resolution;
- double min1 = canvas.miny, max1 = canvas.maxy, ste1 = (max1 - min1) / resolution;
-
- double kernf = 9. / (16 * bandwidth[0] * bandwidth[1]);
- double maxdens = 0.0;
- double[][] dens = new double[resolution][resolution];
- {
- // TODO: incrementally update the loff/roff values?
- for(int x = 0; x < resolution; x++) {
- double xlow = min0 + ste0 * x, xhig = xlow + ste0;
- int loff = unflip(Arrays.binarySearch(data, new double[] { xlow - bandwidth[0] }, comp0));
- int roff = unflip(Arrays.binarySearch(data, new double[] { xhig + bandwidth[0] }, comp0));
- // Resort by second component
- Arrays.sort(data, loff, roff, comp1);
- for(int y = 0; y < resolution; y++) {
- double ylow = min1 + ste1 * y, yhig = ylow + ste1;
- int boff = unflip(Arrays.binarySearch(data, loff, roff, new double[] { 0, ylow - bandwidth[1] }, comp1));
- int toff = unflip(Arrays.binarySearch(data, loff, roff, new double[] { 0, yhig + bandwidth[1] }, comp1));
- for(int pos = boff; pos < toff; pos++) {
- double[] val = data[pos];
- double d0 = (val[0] < xlow) ? (xlow - val[0]) : (val[0] > xhig) ? (val[0] - xhig) : 0;
- double d1 = (val[1] < ylow) ? (ylow - val[1]) : (val[1] > yhig) ? (val[1] - yhig) : 0;
- d0 = d0 / bandwidth[0];
- d1 = d1 / bandwidth[1];
- dens[x][y] += kernf * (1 - d0 * d0) * (1 - d1 * d1);
+ double[] bandwidth = initializeBandwidth(data);
+ // Compare by first component
+ Comparator<double[]> comp0 = new Comparator<double[]>() {
+ @Override
+ public int compare(double[] o1, double[] o2) {
+ return Double.compare(o1[0], o2[0]);
+ }
+ };
+ // Compare by second component
+ Comparator<double[]> comp1 = new Comparator<double[]>() {
+ @Override
+ public int compare(double[] o1, double[] o2) {
+ return Double.compare(o1[1], o2[1]);
+ }
+ };
+ // TODO: choose comparator order based on smaller bandwidth?
+ Arrays.sort(data, comp0);
+
+ CanvasSize canvas = proj.estimateViewport();
+ double min0 = canvas.minx, max0 = canvas.maxx, ste0 = (max0 - min0) / resolution;
+ double min1 = canvas.miny, max1 = canvas.maxy, ste1 = (max1 - min1) / resolution;
+
+ double kernf = 9. / (16 * bandwidth[0] * bandwidth[1]);
+ double maxdens = 0.0;
+ double[][] dens = new double[resolution][resolution];
+ {
+ // TODO: incrementally update the loff/roff values?
+ for(int x = 0; x < resolution; x++) {
+ double xlow = min0 + ste0 * x, xhig = xlow + ste0;
+ int loff = unflip(Arrays.binarySearch(data, new double[] { xlow - bandwidth[0] }, comp0));
+ int roff = unflip(Arrays.binarySearch(data, new double[] { xhig + bandwidth[0] }, comp0));
+ // Resort by second component
+ Arrays.sort(data, loff, roff, comp1);
+ for(int y = 0; y < resolution; y++) {
+ double ylow = min1 + ste1 * y, yhig = ylow + ste1;
+ int boff = unflip(Arrays.binarySearch(data, loff, roff, new double[] { 0, ylow - bandwidth[1] }, comp1));
+ int toff = unflip(Arrays.binarySearch(data, loff, roff, new double[] { 0, yhig + bandwidth[1] }, comp1));
+ for(int pos = boff; pos < toff; pos++) {
+ double[] val = data[pos];
+ double d0 = (val[0] < xlow) ? (xlow - val[0]) : (val[0] > xhig) ? (val[0] - xhig) : 0;
+ double d1 = (val[1] < ylow) ? (ylow - val[1]) : (val[1] > yhig) ? (val[1] - yhig) : 0;
+ d0 = d0 / bandwidth[0];
+ d1 = d1 / bandwidth[1];
+ dens[x][y] += kernf * (1 - d0 * d0) * (1 - d1 * d1);
+ }
+ maxdens = Math.max(maxdens, dens[x][y]);
}
- maxdens = Math.max(maxdens, dens[x][y]);
+ // Restore original sorting, as the intervals overlap
+ Arrays.sort(data, loff, roff, comp0);
}
- // Restore original sorting, as the intervals overlap
- Arrays.sort(data, loff, roff, comp0);
}
- }
- img = new BufferedImage(resolution, resolution, BufferedImage.TYPE_INT_ARGB);
- {
- for(int x = 0; x < resolution; x++) {
- for(int y = 0; y < resolution; y++) {
- int rgb = KMLOutputHandler.getColorForValue(dens[x][y] / maxdens).getRGB();
- img.setRGB(x, y, rgb);
+ img = new BufferedImage(resolution, resolution, BufferedImage.TYPE_INT_ARGB);
+ {
+ for(int x = 0; x < resolution; x++) {
+ for(int y = 0; y < resolution; y++) {
+ int rgb = KMLOutputHandler.getColorForValue(dens[x][y] / maxdens).getRGB();
+ img.setRGB(x, y, rgb);
+ }
}
}
}
- }
- private int unflip(int binarySearch) {
- if(binarySearch < 0) {
- return (-binarySearch) - 1;
- }
- else {
- return binarySearch;
- }
- }
-
- /**
- * The visualization factory
- *
- * @author Erich Schubert
- *
- * @apiviz.stereotype factory
- * @apiviz.uses DensityEstimationOverlay oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
- /**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
- */
- public Factory() {
- super();
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new DensityEstimationOverlay(task);
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(result, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, p.getRelation(), p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA + 1);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(p, task);
+ private int unflip(int binarySearch) {
+ if(binarySearch < 0) {
+ return (-binarySearch) - 1;
+ }
+ else {
+ return binarySearch;
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeMBRVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeMBRVisualization.java
index 61635625..9d466a8f 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeMBRVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeMBRVisualization.java
@@ -60,14 +60,10 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatter
*
* @author Erich Schubert
*
- * @apiviz.has AbstractRStarTree oneway - - visualizes
- * @apiviz.uses SVGHyperCube
- *
- * @param <N> Tree node type
- * @param <E> Tree entry type
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-// TODO: listen for tree changes instead of data changes?
-public class TreeMBRVisualization<N extends AbstractRStarTreeNode<N, E>, E extends SpatialEntry> extends AbstractScatterplotVisualization implements DataStoreListener {
+public class TreeMBRVisualization extends AbstractVisFactory {
/**
* Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
*/
@@ -79,109 +75,152 @@ public class TreeMBRVisualization<N extends AbstractRStarTreeNode<N, E>, E exten
public static final String NAME = "Index MBRs";
/**
- * Fill parameter.
- */
- protected boolean fill = false;
-
- /**
- * The tree we visualize
+ * Settings
*/
- protected AbstractRStarTree<N, E> tree;
+ protected Parameterizer settings;
/**
* Constructor.
*
- * @param task Visualization task
- * @param fill Fill flag
+ * @param settings Settings
*/
- @SuppressWarnings("unchecked")
- public TreeMBRVisualization(VisualizationTask task, boolean fill) {
- super(task);
- this.tree = AbstractRStarTree.class.cast(task.getResult());
- this.fill = fill;
- incrementalRedraw();
- context.addDataStoreListener(this);
+ public TreeMBRVisualization(Parameterizer settings) {
+ super();
+ this.settings = settings;
}
@Override
- protected void redraw() {
- int projdim = proj.getVisibleDimensions2D().cardinality();
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
-
- if(tree != null) {
- E root = tree.getRootEntry();
- for(int i = 0; i < tree.getHeight(); i++) {
- CSSClass cls = new CSSClass(this, INDEX + i);
- // Relative depth of this level. 1.0 = toplevel
- final double relDepth = 1. - (((double) i) / tree.getHeight());
- if(fill) {
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, colors.getColor(i));
- cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.1 / (projdim - 1));
- }
- else {
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<RStarTreeNode, SpatialEntry>(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<AbstractRStarTree<RStarTreeNode, SpatialEntry>> trees = ResultUtil.filterResults(result, AbstractRStarTree.class);
+ for(AbstractRStarTree<RStarTreeNode, SpatialEntry> tree : trees) {
+ if(tree instanceof Result) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, (Result) tree, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_BACKGROUND + 1;
+ baseResult.getHierarchy().add((Result) tree, task);
+ baseResult.getHierarchy().add(p, task);
}
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- svgp.addCSSClassOrLogError(cls);
}
- visualizeRTreeEntry(svgp, layer, proj, tree, root, 0);
}
}
/**
- * Recursively draw the MBR rectangles.
+ * Instance for a particular tree
*
- * @param svgp SVG Plot
- * @param layer Layer
- * @param proj Projection
- * @param rtree Rtree to visualize
- * @param entry Current entry
- * @param depth Current depth
+ * @author Erich Schubert
+ *
+ * @apiviz.has AbstractRStarTree oneway - - visualizes
+ * @apiviz.uses SVGHyperCube
+ *
+ * @param <N> Tree node type
+ * @param <E> Tree entry type
*/
- private void visualizeRTreeEntry(SVGPlot svgp, Element layer, Projection2D proj, AbstractRStarTree<? extends N, E> rtree, E entry, int depth) {
- SpatialComparable mbr = entry;
+ // TODO: listen for tree changes instead of data changes?
+ public class Instance<N extends AbstractRStarTreeNode<N, E>, E extends SpatialEntry> extends AbstractScatterplotVisualization implements DataStoreListener {
+ /**
+ * The tree we visualize
+ */
+ protected AbstractRStarTree<N, E> tree;
- if(fill) {
- Element r = SVGHyperCube.drawFilled(svgp, INDEX + depth, proj, SpatialUtil.getMin(mbr), SpatialUtil.getMax(mbr));
- layer.appendChild(r);
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ @SuppressWarnings("unchecked")
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.tree = AbstractRStarTree.class.cast(task.getResult());
+ incrementalRedraw();
+ context.addDataStoreListener(this);
}
- else {
- Element r = SVGHyperCube.drawFrame(svgp, proj, SpatialUtil.getMin(mbr), SpatialUtil.getMax(mbr));
- SVGUtil.setCSSClass(r, INDEX + depth);
- layer.appendChild(r);
+
+ @Override
+ protected void redraw() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ int projdim = proj.getVisibleDimensions2D().cardinality();
+ ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+
+ if(tree != null) {
+ E root = tree.getRootEntry();
+ for(int i = 0; i < tree.getHeight(); i++) {
+ CSSClass cls = new CSSClass(this, INDEX + i);
+ // Relative depth of this level. 1.0 = toplevel
+ final double relDepth = 1. - (((double) i) / tree.getHeight());
+ if(settings.fill) {
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * style.getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.1 / (projdim - 1));
+ }
+ else {
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * style.getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ }
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ visualizeRTreeEntry(svgp, layer, proj, tree, root, 0);
+ }
}
- if(!entry.isLeafEntry()) {
- N node = rtree.getNode(entry);
- for(int i = 0; i < node.getNumEntries(); i++) {
- E child = node.getEntry(i);
- if(!child.isLeafEntry()) {
- visualizeRTreeEntry(svgp, layer, proj, rtree, child, depth + 1);
+ /**
+ * Recursively draw the MBR rectangles.
+ *
+ * @param svgp SVG Plot
+ * @param layer Layer
+ * @param proj Projection
+ * @param rtree Rtree to visualize
+ * @param entry Current entry
+ * @param depth Current depth
+ */
+ private void visualizeRTreeEntry(SVGPlot svgp, Element layer, Projection2D proj, AbstractRStarTree<? extends N, E> rtree, E entry, int depth) {
+ SpatialComparable mbr = entry;
+
+ if(settings.fill) {
+ Element r = SVGHyperCube.drawFilled(svgp, INDEX + depth, proj, SpatialUtil.getMin(mbr), SpatialUtil.getMax(mbr));
+ layer.appendChild(r);
+ }
+ else {
+ Element r = SVGHyperCube.drawFrame(svgp, proj, SpatialUtil.getMin(mbr), SpatialUtil.getMax(mbr));
+ SVGUtil.setCSSClass(r, INDEX + depth);
+ layer.appendChild(r);
+ }
+
+ if(!entry.isLeafEntry()) {
+ N node = rtree.getNode(entry);
+ for(int i = 0; i < node.getNumEntries(); i++) {
+ E child = node.getEntry(i);
+ if(!child.isLeafEntry()) {
+ visualizeRTreeEntry(svgp, layer, proj, rtree, child, depth + 1);
+ }
}
}
}
- }
- @Override
- public void destroy() {
- super.destroy();
- context.removeDataStoreListener(this);
+ @Override
+ public void destroy() {
+ super.destroy();
+ context.removeDataStoreListener(this);
+ }
}
/**
- * Factory
+ * Parameterization class.
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses TreeMBRVisualization oneway - - «create»
+ * @apiviz.exclude
*/
- public static class Factory extends AbstractVisFactory {
+ public static class Parameterizer extends AbstractParameterizer {
/**
* Flag for half-transparent filling of bubbles.
*
@@ -189,67 +228,22 @@ public class TreeMBRVisualization<N extends AbstractRStarTreeNode<N, E>, E exten
* Key: {@code -index.fill}
* </p>
*/
- public static final OptionID FILL_ID = OptionID.getOrCreateOptionID("index.fill", "Partially transparent filling of index pages.");
+ public static final OptionID FILL_ID = new OptionID("index.fill", "Partially transparent filling of index pages.");
- /**
- * Fill parameter.
- */
protected boolean fill = false;
- /**
- * Constructor.
- *
- * @param fill
- */
- public Factory(boolean fill) {
- super();
- this.fill = fill;
- }
-
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new TreeMBRVisualization<RStarTreeNode, SpatialEntry>(task, fill);
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<AbstractRStarTree<RStarTreeNode, SpatialEntry>> trees = ResultUtil.filterResults(result, AbstractRStarTree.class);
- for(AbstractRStarTree<RStarTreeNode, SpatialEntry> tree : trees) {
- if(tree instanceof Result) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, (Result) tree, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_BACKGROUND + 1);
- baseResult.getHierarchy().add((Result) tree, task);
- baseResult.getHierarchy().add(p, task);
- }
- }
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ Flag fillF = new Flag(FILL_ID);
+ if(config.grab(fillF)) {
+ fill = fillF.isTrue();
}
}
- /**
- * Parameterization class.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- public static class Parameterizer extends AbstractParameterizer {
- protected boolean fill = false;
-
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- Flag fillF = new Flag(FILL_ID);
- if(config.grab(fillF)) {
- fill = fillF.getValue();
- }
- }
-
- @Override
- protected Factory makeInstance() {
- return new Factory(fill);
- }
+ @Override
+ protected TreeMBRVisualization makeInstance() {
+ return new TreeMBRVisualization(this);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeSphereVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeSphereVisualization.java
index daebab02..e89be028 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeSphereVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/index/TreeSphereVisualization.java
@@ -65,14 +65,10 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatter
*
* @author Erich Schubert
*
- * @apiviz.has AbstractMTree oneway - - visualizes
- * @apiviz.uses SVGHyperSphere
- *
- * @param <N> Tree node type
- * @param <E> Tree entry type
+ * @apiviz.stereotype factory
+ * @apiviz.uses TreeSphereVisualization oneway - - «create»
*/
-// TODO: listen for tree changes!
-public class TreeSphereVisualization<D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<?, D, N, E>, E extends MTreeEntry<D>> extends AbstractScatterplotVisualization implements DataStoreListener {
+public class TreeSphereVisualization extends AbstractVisFactory {
/**
* Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
*/
@@ -92,37 +88,40 @@ public class TreeSphereVisualization<D extends NumberDistance<D, ?>, N extends A
MANHATTAN, EUCLIDEAN, LPCROSS
}
- protected double p;
-
/**
- * Drawing mode (distance) to use
+ * Settings
*/
- protected Modus dist = Modus.LPCROSS;
+ protected Parameterizer settings;
/**
- * The tree we visualize
+ * Constructor.
+ *
+ * @param settings Settings
*/
- protected AbstractMTree<?, D, N, E> tree;
+ public TreeSphereVisualization(Parameterizer settings) {
+ super();
+ this.settings = settings;
+ }
- /**
- * Fill parameter.
- */
- protected boolean fill = false;
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for (ScatterPlotProjector<?> p : ps) {
+ Collection<AbstractMTree<?, DoubleDistance, ?, ?>> trees = ResultUtil.filterResults(result, AbstractMTree.class);
+ for (AbstractMTree<?, DoubleDistance, ?, ?> tree : trees) {
+ if (canVisualize(tree) && tree instanceof Result) {
+ final VisualizationTask task = new VisualizationTask(NAME, (Result) tree, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_BACKGROUND + 1;
+ baseResult.getHierarchy().add((Result) tree, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
+ }
- /**
- * Constructor
- *
- * @param task Task
- * @param fill fill flag
- */
- @SuppressWarnings("unchecked")
- public TreeSphereVisualization(VisualizationTask task, boolean fill) {
- super(task);
- this.tree = AbstractMTree.class.cast(task.getResult());
- this.p = getLPNormP(this.tree);
- this.fill = fill;
- incrementalRedraw();
- context.addDataStoreListener(this);
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<DoubleDistance, MTreeNode<Object, DoubleDistance>, MTreeEntry<DoubleDistance>>(task);
}
/**
@@ -131,14 +130,14 @@ public class TreeSphereVisualization<D extends NumberDistance<D, ?>, N extends A
* @param tree Tree to visualize
* @return p value
*/
- public static Double getLPNormP(AbstractMTree<?, ?, ?, ?> tree) {
- // Note: we deliberately lose generics here, so the compilers complain less
- // on the next typecheck and cast!
+ public static double getLPNormP(AbstractMTree<?, ?, ?, ?> tree) {
+ // Note: we deliberately lose generics here, so the compilers complain
+ // less on the next typecheck and cast!
DistanceFunction<?, ?> distanceFunction = tree.getDistanceQuery().getDistanceFunction();
- if(LPNormDistanceFunction.class.isInstance(distanceFunction)) {
+ if (LPNormDistanceFunction.class.isInstance(distanceFunction)) {
return ((LPNormDistanceFunction) distanceFunction).getP();
}
- return null;
+ return 0;
}
/**
@@ -148,170 +147,160 @@ public class TreeSphereVisualization<D extends NumberDistance<D, ?>, N extends A
* @return whether the tree is visualizable
*/
public static boolean canVisualize(AbstractMTree<?, ?, ?, ?> tree) {
- Double p = getLPNormP(tree);
- return (p != null);
- }
-
- @Override
- protected void redraw() {
- int projdim = proj.getVisibleDimensions2D().cardinality();
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
-
- p = getLPNormP(tree);
- if(tree != null) {
- if(ManhattanDistanceFunction.class.isInstance(tree.getDistanceQuery())) {
- dist = Modus.MANHATTAN;
- }
- else if(EuclideanDistanceFunction.class.isInstance(tree.getDistanceQuery())) {
- dist = Modus.EUCLIDEAN;
- }
- else {
- dist = Modus.LPCROSS;
- }
- E root = tree.getRootEntry();
- final int mtheight = tree.getHeight();
- for(int i = 0; i < mtheight; i++) {
- CSSClass cls = new CSSClass(this, INDEX + i);
- // Relative depth of this level. 1.0 = toplevel
- final double relDepth = 1. - (((double) i) / mtheight);
- if(fill) {
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, colors.getColor(i));
- cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.1 / (projdim - 1));
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- }
- else {
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- }
- svgp.addCSSClassOrLogError(cls);
- }
- visualizeMTreeEntry(svgp, this.layer, proj, tree, root, 0);
- }
+ return getLPNormP(tree) > 0;
}
/**
- * Recursively draw the MBR rectangles.
- *
- * @param svgp SVG Plot
- * @param layer Layer
- * @param proj Projection
- * @param mtree Mtree to visualize
- * @param entry Current entry
- * @param depth Current depth
- */
- private void visualizeMTreeEntry(SVGPlot svgp, Element layer, Projection2D proj, AbstractMTree<?, D, N, E> mtree, E entry, int depth) {
- DBID roid = entry.getRoutingObjectID();
- if(roid != null) {
- NumberVector<?, ?> ro = rel.get(roid);
- D rad = entry.getCoveringRadius();
-
- final Element r;
- if(dist == Modus.MANHATTAN) {
- r = SVGHyperSphere.drawManhattan(svgp, proj, ro, rad);
- }
- else if(dist == Modus.EUCLIDEAN) {
- r = SVGHyperSphere.drawEuclidean(svgp, proj, ro, rad);
- }
- // TODO: add visualizer for infinity norm?
- else {
- // r = SVGHyperSphere.drawCross(svgp, proj, ro, rad);
- r = SVGHyperSphere.drawLp(svgp, proj, ro, rad, p);
- }
- SVGUtil.setCSSClass(r, INDEX + (depth - 1));
- layer.appendChild(r);
- }
-
- if(!entry.isLeafEntry()) {
- N node = mtree.getNode(entry);
- for(int i = 0; i < node.getNumEntries(); i++) {
- E child = node.getEntry(i);
- if(!child.isLeafEntry()) {
- visualizeMTreeEntry(svgp, layer, proj, mtree, child, depth + 1);
- }
- }
- }
- }
-
- @Override
- public void destroy() {
- super.destroy();
- context.removeDataStoreListener(this);
- }
-
- /**
- * Factory
+ * Instance for a particular tree.
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses TreeSphereVisualization oneway - - «create»
+ * @apiviz.has AbstractMTree oneway - - visualizes
+ * @apiviz.uses SVGHyperSphere
+ *
+ * @param <N> Tree node type
+ * @param <E> Tree entry type
*/
- public static class Factory extends AbstractVisFactory {
+ // TODO: listen for tree changes!
+ public class Instance<D extends NumberDistance<D, ?>, N extends AbstractMTreeNode<?, D, N, E>, E extends MTreeEntry<D>> extends AbstractScatterplotVisualization implements DataStoreListener {
+ protected double p;
+
/**
- * Fill parameter.
+ * Drawing mode (distance) to use
*/
- protected boolean fill = false;
+ protected Modus dist = Modus.LPCROSS;
+
+ /**
+ * The tree we visualize
+ */
+ protected AbstractMTree<?, D, N, E> tree;
/**
- * Constructor.
+ * Constructor
*
- * @param fill
+ * @param task Task
*/
- public Factory(boolean fill) {
- super();
- this.fill = fill;
+ @SuppressWarnings("unchecked")
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.tree = AbstractMTree.class.cast(task.getResult());
+ this.p = getLPNormP(this.tree);
+ incrementalRedraw();
+ context.addDataStoreListener(this);
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- Collection<AbstractMTree<?, DoubleDistance, ?, ?>> trees = ResultUtil.filterResults(result, AbstractMTree.class);
- for(AbstractMTree<?, DoubleDistance, ?, ?> tree : trees) {
- if(canVisualize(tree) && tree instanceof Result) {
- final VisualizationTask task = new VisualizationTask(NAME, (Result) tree, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_BACKGROUND + 1);
- baseResult.getHierarchy().add((Result) tree, task);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ int projdim = proj.getVisibleDimensions2D().cardinality();
+ ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+
+ p = getLPNormP(tree);
+ if (tree != null) {
+ if (ManhattanDistanceFunction.class.isInstance(tree.getDistanceQuery())) {
+ dist = Modus.MANHATTAN;
+ } else if (EuclideanDistanceFunction.class.isInstance(tree.getDistanceQuery())) {
+ dist = Modus.EUCLIDEAN;
+ } else {
+ dist = Modus.LPCROSS;
+ }
+ E root = tree.getRootEntry();
+ final int mtheight = tree.getHeight();
+ for (int i = 0; i < mtheight; i++) {
+ CSSClass cls = new CSSClass(this, INDEX + i);
+ // Relative depth of this level. 1.0 = toplevel
+ final double relDepth = 1. - (((double) i) / mtheight);
+ if (settings.fill) {
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * style.getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.1 / (projdim - 1));
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ } else {
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, colors.getColor(i));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, relDepth * style.getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
}
+ svgp.addCSSClassOrLogError(cls);
}
+ visualizeMTreeEntry(svgp, this.layer, proj, tree, root, 0);
}
}
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new TreeSphereVisualization<DoubleDistance, MTreeNode<Object, DoubleDistance>, MTreeEntry<DoubleDistance>>(task, fill);
- }
-
/**
- * Parameterization class.
+ * Recursively draw the MBR rectangles.
*
- * @author Erich Schubert
- *
- * @apiviz.exclude
+ * @param svgp SVG Plot
+ * @param layer Layer
+ * @param proj Projection
+ * @param mtree Mtree to visualize
+ * @param entry Current entry
+ * @param depth Current depth
*/
- public static class Parameterizer extends AbstractParameterizer {
- protected boolean fill = false;
+ private void visualizeMTreeEntry(SVGPlot svgp, Element layer, Projection2D proj, AbstractMTree<?, D, N, E> mtree, E entry, int depth) {
+ DBID roid = entry.getRoutingObjectID();
+ if (roid != null) {
+ NumberVector<?> ro = rel.get(roid);
+ D rad = entry.getCoveringRadius();
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- Flag fillF = new Flag(TreeMBRVisualization.Factory.FILL_ID);
- if(config.grab(fillF)) {
- fill = fillF.getValue();
+ final Element r;
+ if (dist == Modus.MANHATTAN) {
+ r = SVGHyperSphere.drawManhattan(svgp, proj, ro, rad);
+ } else if (dist == Modus.EUCLIDEAN) {
+ r = SVGHyperSphere.drawEuclidean(svgp, proj, ro, rad);
+ }
+ // TODO: add visualizer for infinity norm?
+ else {
+ // r = SVGHyperSphere.drawCross(svgp, proj, ro, rad);
+ r = SVGHyperSphere.drawLp(svgp, proj, ro, rad, p);
}
+ SVGUtil.setCSSClass(r, INDEX + (depth - 1));
+ layer.appendChild(r);
}
- @Override
- protected Factory makeInstance() {
- return new Factory(fill);
+ if (!entry.isLeafEntry()) {
+ N node = mtree.getNode(entry);
+ for (int i = 0; i < node.getNumEntries(); i++) {
+ E child = node.getEntry(i);
+ if (!child.isLeafEntry()) {
+ visualizeMTreeEntry(svgp, layer, proj, mtree, child, depth + 1);
+ }
+ }
}
}
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ context.removeDataStoreListener(this);
+ }
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ protected boolean fill = false;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ Flag fillF = new Flag(TreeMBRVisualization.Parameterizer.FILL_ID);
+ if (config.grab(fillF)) {
+ fill = fillF.isTrue();
+ }
+ }
+
+ @Override
+ protected TreeSphereVisualization makeInstance() {
+ return new TreeSphereVisualization(this);
+ }
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/BubbleVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/BubbleVisualization.java
index 769dc53d..ebe9612d 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/BubbleVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/BubbleVisualization.java
@@ -50,6 +50,7 @@ import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
import de.lmu.ifi.dbs.elki.visualization.style.ClassStylingPolicy;
import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleResult;
import de.lmu.ifi.dbs.elki.visualization.style.StylingPolicy;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
@@ -66,10 +67,11 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
* @author Remigius Wojdanowski
* @author Erich Schubert
*
- * @apiviz.has OutlierResult oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
@Reference(authors = "E. Achtert, H.-P. Kriegel, L. Reichert, E. Schubert, R. Wojdanowski, A. Zimek", title = "Visual Evaluation of Outlier Detection Models", booktitle = "Proceedings of the 15th International Conference on Database Systems for Advanced Applications (DASFAA), Tsukuba, Japan, 2010", url = "http://dx.doi.org/10.1007/978-3-642-12098-5_34")
-public class BubbleVisualization extends AbstractScatterplotVisualization implements DataStoreListener {
+public class BubbleVisualization extends AbstractVisFactory {
/**
* Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
*/
@@ -81,160 +83,207 @@ public class BubbleVisualization extends AbstractScatterplotVisualization implem
public static final String NAME = "Outlier Bubbles";
/**
- * Fill parameter.
+ * Current settings
*/
- protected boolean fill;
-
- /**
- * Scaling function to use for Bubbles
- */
- protected ScalingFunction scaling;
-
- /**
- * The outlier result to visualize
- */
- protected OutlierResult result;
+ protected Parameterizer settings;
/**
* Constructor.
*
- * @param task Visualization task
- * @param scaling Scaling function
- * @param fill Fill flag
+ * @param settings Settings
*/
- public BubbleVisualization(VisualizationTask task, ScalingFunction scaling, boolean fill) {
- super(task);
- this.result = task.getResult();
- this.scaling = scaling;
- this.fill = fill;
- context.addDataStoreListener(this);
- context.addResultListener(this);
- incrementalRedraw();
+ public BubbleVisualization(Parameterizer settings) {
+ super();
+ this.settings = settings;
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_STYLE;
}
@Override
- public void destroy() {
- super.destroy();
- context.removeResultListener(this);
- context.removeDataStoreListener(this);
+ public Visualization makeVisualization(VisualizationTask task) {
+ if(settings.scaling != null && settings.scaling instanceof OutlierScalingFunction) {
+ final OutlierResult outlierResult = task.getResult();
+ ((OutlierScalingFunction) settings.scaling).prepare(outlierResult);
+ }
+ return new Instance(task);
}
@Override
- public void redraw() {
- StylingPolicy stylepolicy = context.getStyleResult().getStylingPolicy();
- // bubble size
- final double bubble_size = context.getStyleLibrary().getSize(StyleLibrary.BUBBLEPLOT);
- if(stylepolicy instanceof ClassStylingPolicy) {
- ClassStylingPolicy colors = (ClassStylingPolicy) stylepolicy;
- setupCSS(svgp, colors);
- // draw data
- for(DBIDIter objId = sample.getSample().iter(); objId.valid(); objId.advance()) {
- final Double radius = getScaledForId(objId);
- if(radius > 0.01 && !Double.isInfinite(radius)) {
- final NumberVector<?, ?> vec = rel.get(objId);
- if(vec != null) {
- double[] v = proj.fastProjectDataToRenderSpace(vec);
- Element circle = svgp.svgCircle(v[0], v[1], radius * bubble_size);
- SVGUtil.addCSSClass(circle, BUBBLE + colors.getStyleForDBID(objId));
- layer.appendChild(circle);
- }
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<OutlierResult> ors = ResultUtil.filterResults(result, OutlierResult.class);
+ for(OutlierResult o : ors) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ boolean vis = true;
+ // Quick and dirty hack: hide if parent result is also an outlier result
+ // Since that probably is already visible and we're redundant.
+ for(Result r : o.getHierarchy().getParents(o)) {
+ if(r instanceof OutlierResult) {
+ vis = false;
+ break;
}
}
- }
- else {
- // draw data
- for(DBIDIter objId = sample.getSample().iter(); objId.valid(); objId.advance()) {
- final Double radius = getScaledForId(objId);
- if(radius > 0.01 && !Double.isInfinite(radius)) {
- final NumberVector<?, ?> vec = rel.get(objId);
- if(vec != null) {
- double[] v = proj.fastProjectDataToRenderSpace(vec);
- Element circle = svgp.svgCircle(v[0], v[1], radius * bubble_size);
- int color = stylepolicy.getColorForDBID(objId);
- final StringBuffer style = new StringBuffer();
- if(fill) {
- style.append(SVGConstants.CSS_FILL_PROPERTY + ":").append(SVGUtil.colorToString(color));
- style.append(SVGConstants.CSS_FILL_OPACITY_PROPERTY + ":0.5");
- }
- else {
- style.append(SVGConstants.CSS_STROKE_VALUE + ":").append(SVGUtil.colorToString(color));
- style.append(SVGConstants.CSS_FILL_PROPERTY + ":" + SVGConstants.CSS_NONE_VALUE);
- }
- SVGUtil.setAtt(circle, SVGConstants.SVG_STYLE_ATTRIBUTE, style.toString());
- layer.appendChild(circle);
- }
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, o, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA;
+ if(!vis) {
+ task.initDefaultVisibility(false);
}
+ baseResult.getHierarchy().add(o, task);
+ baseResult.getHierarchy().add(p, task);
}
}
}
- @Override
- public void resultChanged(Result current) {
- super.resultChanged(current);
- if(sample == current || context.getStyleResult() == current) {
- synchronizedRedraw();
- }
- }
-
/**
- * Registers the Bubble-CSS-Class at a SVGPlot.
+ * Factory for producing bubble visualizations
*
- * @param svgp the SVGPlot to register the Tooltip-CSS-Class.
- * @param policy Clustering to use
+ * @author Erich Schubert
+ *
+ * @apiviz.has OutlierResult oneway - - visualizes
*/
- private void setupCSS(SVGPlot svgp, ClassStylingPolicy policy) {
- ColorLibrary colors = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
+ public class Instance extends AbstractScatterplotVisualization implements DataStoreListener {
+ /**
+ * The outlier result to visualize
+ */
+ protected OutlierResult result;
- // creating IDs manually because cluster often return a null-ID.
- for(int clusterID = policy.getMinStyle(); clusterID < policy.getMaxStyle(); clusterID++) {
- CSSClass bubble = new CSSClass(svgp, BUBBLE + clusterID);
- bubble.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.result = task.getResult();
+ context.addDataStoreListener(this);
+ context.addResultListener(this);
+ incrementalRedraw();
+ }
- String color = colors.getColor(clusterID);
+ @Override
+ public void destroy() {
+ super.destroy();
+ context.removeResultListener(this);
+ context.removeDataStoreListener(this);
+ }
- if(fill) {
- bubble.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
- bubble.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.5);
+ @Override
+ public void redraw() {
+ final StyleResult style = context.getStyleResult();
+ StylingPolicy stylepolicy = style.getStylingPolicy();
+ // bubble size
+ final double bubble_size = style.getStyleLibrary().getSize(StyleLibrary.BUBBLEPLOT);
+ if(stylepolicy instanceof ClassStylingPolicy) {
+ ClassStylingPolicy colors = (ClassStylingPolicy) stylepolicy;
+ setupCSS(svgp, colors);
+ // draw data
+ for(DBIDIter objId = sample.getSample().iter(); objId.valid(); objId.advance()) {
+ final double radius = getScaledForId(objId);
+ if(radius > 0.01 && !Double.isInfinite(radius)) {
+ final NumberVector<?> vec = rel.get(objId);
+ if(vec != null) {
+ double[] v = proj.fastProjectDataToRenderSpace(vec);
+ Element circle = svgp.svgCircle(v[0], v[1], radius * bubble_size);
+ SVGUtil.addCSSClass(circle, BUBBLE + colors.getStyleForDBID(objId));
+ layer.appendChild(circle);
+ }
+ }
+ }
}
else {
- // for diamond-shaped strokes, see bugs.sun.com, bug ID 6294396
- bubble.setStatement(SVGConstants.CSS_STROKE_VALUE, color);
- bubble.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ // draw data
+ for(DBIDIter objId = sample.getSample().iter(); objId.valid(); objId.advance()) {
+ final double radius = getScaledForId(objId);
+ if(radius > 0.01 && !Double.isInfinite(radius)) {
+ final NumberVector<?> vec = rel.get(objId);
+ if(vec != null) {
+ double[] v = proj.fastProjectDataToRenderSpace(vec);
+ Element circle = svgp.svgCircle(v[0], v[1], radius * bubble_size);
+ int color = stylepolicy.getColorForDBID(objId);
+ final StringBuilder cssstyle = new StringBuilder();
+ if(settings.fill) {
+ cssstyle.append(SVGConstants.CSS_FILL_PROPERTY).append(':').append(SVGUtil.colorToString(color));
+ cssstyle.append(SVGConstants.CSS_FILL_OPACITY_PROPERTY).append(":0.5");
+ }
+ else {
+ cssstyle.append(SVGConstants.CSS_STROKE_VALUE).append(':').append(SVGUtil.colorToString(color));
+ cssstyle.append(SVGConstants.CSS_FILL_PROPERTY).append(':').append(SVGConstants.CSS_NONE_VALUE);
+ }
+ SVGUtil.setAtt(circle, SVGConstants.SVG_STYLE_ATTRIBUTE, cssstyle.toString());
+ layer.appendChild(circle);
+ }
+ }
+ }
}
-
- svgp.addCSSClassOrLogError(bubble);
}
- }
- /**
- * Convenience method to apply scalings in the right order.
- *
- * @param id object ID to get scaled score for
- * @return a Double representing a outlierness-score, after it has modified by
- * the given scales.
- */
- protected double getScaledForId(DBIDRef id) {
- double d = result.getScores().get(id).doubleValue();
- if(Double.isNaN(d) || Double.isInfinite(d)) {
- return 0.0;
+ @Override
+ public void resultChanged(Result current) {
+ super.resultChanged(current);
+ if(sample == current || context.getStyleResult() == current) {
+ synchronizedRedraw();
+ }
}
- if(scaling == null) {
- return result.getOutlierMeta().normalizeScore(d);
+
+ /**
+ * Registers the Bubble-CSS-Class at a SVGPlot.
+ *
+ * @param svgp the SVGPlot to register the Tooltip-CSS-Class.
+ * @param policy Clustering to use
+ */
+ private void setupCSS(SVGPlot svgp, ClassStylingPolicy policy) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+
+ // creating IDs manually because cluster often return a null-ID.
+ for(int clusterID = policy.getMinStyle(); clusterID < policy.getMaxStyle(); clusterID++) {
+ CSSClass bubble = new CSSClass(svgp, BUBBLE + clusterID);
+ bubble.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
+
+ String color = colors.getColor(clusterID);
+
+ if(settings.fill) {
+ bubble.setStatement(SVGConstants.CSS_FILL_PROPERTY, color);
+ bubble.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, 0.5);
+ }
+ else {
+ // for diamond-shaped strokes, see bugs.sun.com, bug ID 6294396
+ bubble.setStatement(SVGConstants.CSS_STROKE_VALUE, color);
+ bubble.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ }
+
+ svgp.addCSSClassOrLogError(bubble);
+ }
}
- else {
- return scaling.getScaled(d);
+
+ /**
+ * Convenience method to apply scalings in the right order.
+ *
+ * @param id object ID to get scaled score for
+ * @return a Double representing a outlierness-score, after it has modified
+ * by the given scales.
+ */
+ protected double getScaledForId(DBIDRef id) {
+ double d = result.getScores().get(id).doubleValue();
+ if(Double.isNaN(d) || Double.isInfinite(d)) {
+ return 0.0;
+ }
+ if(settings.scaling == null) {
+ return result.getOutlierMeta().normalizeScore(d);
+ }
+ else {
+ return settings.scaling.getScaled(d);
+ }
}
}
/**
- * Factory for producing bubble visualizations
+ * Parameterization class.
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses BubbleVisualization oneway - - «create»
+ * @apiviz.exclude
*/
- public static class Factory extends AbstractVisFactory {
+ public static class Parameterizer extends AbstractParameterizer {
/**
* Flag for half-transparent filling of bubbles.
*
@@ -242,7 +291,7 @@ public class BubbleVisualization extends AbstractScatterplotVisualization implem
* Key: {@code -bubble.fill}
* </p>
*/
- public static final OptionID FILL_ID = OptionID.getOrCreateOptionID("bubble.fill", "Half-transparent filling of bubbles.");
+ public static final OptionID FILL_ID = new OptionID("bubble.fill", "Half-transparent filling of bubbles.");
/**
* Parameter for scaling functions
@@ -251,7 +300,7 @@ public class BubbleVisualization extends AbstractScatterplotVisualization implem
* Key: {@code -bubble.scaling}
* </p>
*/
- public static final OptionID SCALING_ID = OptionID.getOrCreateOptionID("bubble.scaling", "Additional scaling function for bubbles.");
+ public static final OptionID SCALING_ID = new OptionID("bubble.scaling", "Additional scaling function for bubbles.");
/**
* Fill parameter.
@@ -263,90 +312,23 @@ public class BubbleVisualization extends AbstractScatterplotVisualization implem
*/
protected ScalingFunction scaling;
- /**
- * Constructor.
- *
- * @param fill
- * @param scaling
- */
- public Factory(boolean fill, ScalingFunction scaling) {
- super();
- this.fill = fill;
- this.scaling = scaling;
- thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_STYLE;
- }
-
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- if(this.scaling != null && this.scaling instanceof OutlierScalingFunction) {
- final OutlierResult outlierResult = task.getResult();
- ((OutlierScalingFunction) this.scaling).prepare(outlierResult);
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ Flag fillF = new Flag(FILL_ID);
+ if(config.grab(fillF)) {
+ fill = fillF.isTrue();
}
- return new BubbleVisualization(task, scaling, fill);
- }
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<OutlierResult> ors = ResultUtil.filterResults(result, OutlierResult.class);
- for(OutlierResult o : ors) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- boolean vis = true;
- // Quick and dirty hack: hide if parent result is also an outlier result
- // Since that probably is already visible and we're redundant.
- for(Result r : o.getHierarchy().getParents(o)) {
- if(r instanceof OutlierResult) {
- vis = false;
- break;
- }
- }
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, o, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA);
- if(!vis) {
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- }
- baseResult.getHierarchy().add(o, task);
- baseResult.getHierarchy().add(p, task);
- }
+ ObjectParameter<ScalingFunction> scalingP = new ObjectParameter<ScalingFunction>(SCALING_ID, OutlierScalingFunction.class, true);
+ if(config.grab(scalingP)) {
+ scaling = scalingP.instantiateClass(config);
}
}
- /**
- * Parameterization class.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- public static class Parameterizer extends AbstractParameterizer {
- /**
- * Fill parameter.
- */
- protected boolean fill = false;
-
- /**
- * Scaling function to use for Bubbles
- */
- protected ScalingFunction scaling = null;
-
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- Flag fillF = new Flag(FILL_ID);
- if(config.grab(fillF)) {
- fill = fillF.getValue();
- }
-
- ObjectParameter<ScalingFunction> scalingP = new ObjectParameter<ScalingFunction>(SCALING_ID, OutlierScalingFunction.class, true);
- if(config.grab(scalingP)) {
- scaling = scalingP.instantiateClass(config);
- }
- }
-
- @Override
- protected Factory makeInstance() {
- return new Factory(fill, scaling);
- }
+ @Override
+ protected BubbleVisualization makeInstance() {
+ return new BubbleVisualization(this);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/COPVectorVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/COPVectorVisualization.java
new file mode 100644
index 00000000..e953f9ae
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/outlier/COPVectorVisualization.java
@@ -0,0 +1,190 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.outlier;
+
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.List;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+
+import de.lmu.ifi.dbs.elki.algorithm.outlier.COP;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.relation.Relation;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.VMath;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Reference;
+import de.lmu.ifi.dbs.elki.utilities.documentation.Title;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
+
+/**
+ * Visualize error vectors as produced by COP.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
+ * @apiviz.has OutlierResult oneway - - visualizes
+ */
+@Title("COP: Correlation Outlier Probability")
+@Reference(authors = "Hans-Peter Kriegel, Peer Kröger, Erich Schubert, Arthur Zimek", title = "Outlier Detection in Arbitrarily Oriented Subspaces", booktitle = "Proc. IEEE International Conference on Data Mining (ICDM 2012)")
+public class COPVectorVisualization extends AbstractVisFactory {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ public static final String NAME = "Error Vectors";
+
+ /**
+ * Constructor.
+ */
+ public COPVectorVisualization() {
+ super();
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ List<OutlierResult> ors = ResultUtil.filterResults(result, OutlierResult.class);
+ for (OutlierResult o : ors) {
+ List<Relation<?>> rels = ResultUtil.filterResults(o, Relation.class);
+ for (Relation<?> rel : rels) {
+ if (!rel.getShortName().equals(COP.COP_ERRORVEC)) {
+ continue;
+ }
+ List<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ boolean vis = true;
+ for (ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, rel, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA;
+ if (!vis) {
+ task.initDefaultVisibility(false);
+ }
+ baseResult.getHierarchy().add(o, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
+ }
+
+ /**
+ * Visualize error vectors as produced by COP.
+ *
+ * @author Erich Schubert
+ */
+ public class Instance extends AbstractScatterplotVisualization implements DataStoreListener {
+ /**
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String VEC = "copvec";
+
+ /**
+ * The outlier result to visualize
+ */
+ protected Relation<Vector> result;
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.result = task.getResult();
+ context.addDataStoreListener(this);
+ incrementalRedraw();
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ context.removeDataStoreListener(this);
+ }
+
+ @Override
+ public void redraw() {
+ setupCSS(svgp);
+ for (DBIDIter objId = sample.getSample().iter(); objId.valid(); objId.advance()) {
+ Vector evec = result.get(objId);
+ if (evec == null) {
+ continue;
+ }
+ double[] ev = proj.fastProjectRelativeDataToRenderSpace(evec);
+ // TODO: avoid hard-coded plot threshold
+ if (VMath.euclideanLength(ev) < 0.01) {
+ continue;
+ }
+ final NumberVector<?> vec = rel.get(objId);
+ if (vec == null) {
+ continue;
+ }
+ double[] v = proj.fastProjectDataToRenderSpace(vec);
+ Element arrow = svgp.svgLine(v[0], v[1], v[0] + ev[0], v[1] + ev[1]);
+ SVGUtil.addCSSClass(arrow, VEC);
+ layer.appendChild(arrow);
+ }
+ }
+
+ @Override
+ public void resultChanged(Result current) {
+ if (sample == current) {
+ synchronizedRedraw();
+ }
+ }
+
+ /**
+ * Registers the COP error vector-CSS-Class at a SVGPlot.
+ *
+ * @param svgp the SVGPlot to register the Tooltip-CSS-Class.
+ */
+ private void setupCSS(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ CSSClass bubble = new CSSClass(svgp, VEC);
+ bubble.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT) / 2);
+
+ // ColorLibrary colors = style.getColorSet(StyleLibrary.PLOT);
+ String color = "red"; // TODO: use style library
+ bubble.setStatement(SVGConstants.CSS_STROKE_VALUE, color);
+ bubble.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ svgp.addCSSClassOrLogError(bubble);
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/DistanceFunctionVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/DistanceFunctionVisualization.java
new file mode 100644
index 00000000..257a3c54
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/DistanceFunctionVisualization.java
@@ -0,0 +1,370 @@
+package de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.selection;
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.Collection;
+
+import org.apache.batik.util.SVGConstants;
+import org.w3c.dom.Element;
+
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.VectorUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.database.ids.DistanceDBIDPair;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.ArcCosineDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.CosineDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distancefunction.LPNormDistanceFunction;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.DistanceDBIDResultIter;
+import de.lmu.ifi.dbs.elki.distance.distanceresultlist.KNNResult;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance;
+import de.lmu.ifi.dbs.elki.distance.distancevalue.NumberDistance;
+import de.lmu.ifi.dbs.elki.index.preprocessed.knn.AbstractMaterializeKNNPreprocessor;
+import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
+import de.lmu.ifi.dbs.elki.math.linearalgebra.VMath;
+import de.lmu.ifi.dbs.elki.result.DBIDSelection;
+import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
+import de.lmu.ifi.dbs.elki.result.Result;
+import de.lmu.ifi.dbs.elki.result.ResultUtil;
+import de.lmu.ifi.dbs.elki.result.SelectionResult;
+import de.lmu.ifi.dbs.elki.utilities.exceptions.ObjectNotFoundException;
+import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
+import de.lmu.ifi.dbs.elki.visualization.css.CSSClass;
+import de.lmu.ifi.dbs.elki.visualization.projections.CanvasSize;
+import de.lmu.ifi.dbs.elki.visualization.projections.Projection2D;
+import de.lmu.ifi.dbs.elki.visualization.projector.ScatterPlotProjector;
+import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGHyperSphere;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPath;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
+import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualization;
+
+/**
+ * Factory for visualizers to generate an SVG-Element containing dots as markers
+ * representing the kNN of the selected Database objects.
+ *
+ * To use this, add a kNN preprocessor index to your database!
+ *
+ * @author Erich Schubert
+ * @author Robert Rödler
+ *
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
+ */
+// FIXME: for >2 dimensions, cosine doesn't seem to be correct yet.
+public class DistanceFunctionVisualization extends AbstractVisFactory {
+ /**
+ * A short name characterizing this Visualizer.
+ */
+ public static final String NAME = "k Nearest Neighbor Visualization";
+
+ /**
+ * Constructor
+ */
+ public DistanceFunctionVisualization() {
+ super();
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance<DoubleDistance>(task);
+ }
+
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<AbstractMaterializeKNNPreprocessor<?, ?, ?>> kNNIndex = ResultUtil.filterResults(result, AbstractMaterializeKNNPreprocessor.class);
+ for(AbstractMaterializeKNNPreprocessor<?, ?, ?> kNN : kNNIndex) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, kNN, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA - 1;
+ baseResult.getHierarchy().add(kNN, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
+ }
+
+ /**
+ * Get the "p" value of an Lp norm.
+ *
+ * @param kNN kNN preprocessor
+ * @return p of LP norm, or NaN
+ */
+ public static double getLPNormP(AbstractMaterializeKNNPreprocessor<?, ?, ?> kNN) {
+ DistanceFunction<?, ?> distanceFunction = kNN.getDistanceQuery().getDistanceFunction();
+ if(LPNormDistanceFunction.class.isInstance(distanceFunction)) {
+ return ((LPNormDistanceFunction) distanceFunction).getP();
+ }
+ return Double.NaN;
+ }
+
+ /**
+ * Test whether the given preprocessor used an angular distance function
+ *
+ * @param kNN kNN preprocessor
+ * @return true when angular
+ */
+ public static boolean isAngularDistance(AbstractMaterializeKNNPreprocessor<?, ?, ?> kNN) {
+ DistanceFunction<?, ?> distanceFunction = kNN.getDistanceQuery().getDistanceFunction();
+ if(CosineDistanceFunction.class.isInstance(distanceFunction)) {
+ return true;
+ }
+ if(ArcCosineDistanceFunction.class.isInstance(distanceFunction)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Visualizes Cosine and ArcCosine distance functions
+ *
+ * @param svgp SVG Plot
+ * @param proj Visualization projection
+ * @param mid mean vector
+ * @param angle Opening angle in radians
+ * @return path element
+ */
+ public static Element drawCosine(SVGPlot svgp, Projection2D proj, NumberVector<?> mid, double angle) {
+ // Project origin
+ double[] pointOfOrigin = proj.fastProjectDataToRenderSpace(new double[proj.getInputDimensionality()]);
+
+ // direction of the selected Point
+ double[] selPoint = proj.fastProjectDataToRenderSpace(mid);
+
+ double[] range1, range2;
+ {
+ // Rotation plane:
+ double[] p1 = proj.fastProjectRenderToDataSpace(new double[] { selPoint[0] + 10, selPoint[1] });
+ double[] p2 = proj.fastProjectRenderToDataSpace(new double[] { selPoint[0], selPoint[1] + 10 });
+ double[] pm = mid.getColumnVector().getArrayRef();
+ // Compute relative vectors
+ VMath.minusEquals(p1, pm);
+ VMath.minusEquals(p2, pm);
+ // Scale p1 and p2 to unit length:
+ VMath.timesEquals(p1, 1. / VMath.euclideanLength(p1));
+ VMath.timesEquals(p2, 1. / VMath.euclideanLength(p2));
+ {
+ double test = VMath.scalarProduct(p1, p2);
+ if(Math.abs(test) > 1E-10) {
+ LoggingUtil.warning("Projection does not seem to be orthogonal?");
+ }
+ }
+ // Project onto p1, p2:
+ double l1 = VMath.scalarProduct(pm, p1), l2 = VMath.scalarProduct(pm, p2);
+ // Rotate projection by + and - angle
+ // Using sin(-x) = -sin(x) and cos(-x)=cos(x)
+ final double cangle = Math.cos(angle), sangle = Math.sin(angle);
+ double r11 = +cangle * l1 - sangle * l2, r12 = +sangle * l1 + cangle * l2;
+ double r21 = +cangle * l1 + sangle * l2, r22 = -sangle * l1 + cangle * l2;
+ // Build rotated vectors - remove projected component, add rotated
+ // component:
+ double[] r1 = VMath.copy(pm), r2 = VMath.copy(pm);
+ VMath.plusTimesEquals(r1, p1, -l1 + r11);
+ VMath.plusTimesEquals(r1, p2, -l2 + r12);
+ VMath.plusTimesEquals(r2, p1, -l1 + r21);
+ VMath.plusTimesEquals(r2, p2, -l2 + r22);
+ // Project to render space:
+ range1 = proj.fastProjectDataToRenderSpace(r1);
+ range2 = proj.fastProjectDataToRenderSpace(r2);
+ }
+
+ // Continue lines to viewport.
+ {
+ CanvasSize viewport = proj.estimateViewport();
+ VMath.minusEquals(range1, pointOfOrigin);
+ VMath.minusEquals(range2, pointOfOrigin);
+ VMath.timesEquals(range1, viewport.continueToMargin(pointOfOrigin, range1));
+ VMath.timesEquals(range2, viewport.continueToMargin(pointOfOrigin, range2));
+ VMath.plusEquals(range1, pointOfOrigin);
+ VMath.plusEquals(range2, pointOfOrigin);
+ // Go backwards into the other direction - the origin might not be in the
+ // viewport!
+ double[] start1 = VMath.minus(pointOfOrigin, range1);
+ double[] start2 = VMath.minus(pointOfOrigin, range2);
+ VMath.timesEquals(start1, viewport.continueToMargin(range1, start1));
+ VMath.timesEquals(start2, viewport.continueToMargin(range2, start2));
+ VMath.plusEquals(start1, range1);
+ VMath.plusEquals(start2, range2);
+
+ // TODO: add filled variant?
+ SVGPath path = new SVGPath();
+ path.moveTo(start1);
+ path.lineTo(range1);
+ path.moveTo(start2);
+ path.lineTo(range2);
+ return path.makeElement(svgp);
+ }
+ }
+
+ /**
+ * Instance, visualizing a particular set of kNNs
+ *
+ * @author Robert Rödler
+ * @author Erich Schubert
+ *
+ * @apiviz.has SelectionResult oneway - - visualizes
+ * @apiviz.has DBIDSelection oneway - - visualizes
+ *
+ * @param <D> Distance type
+ */
+ public class Instance<D extends NumberDistance<D, ?>> extends AbstractScatterplotVisualization implements DataStoreListener {
+ /**
+ * Generic tags to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String KNNMARKER = "kNNMarker";
+
+ public static final String KNNDIST = "kNNDist";
+
+ public static final String DISTANCEFUNCTION = "distancefunction";
+
+ /**
+ * The selection result we work on
+ */
+ private AbstractMaterializeKNNPreprocessor<? extends NumberVector<?>, D, ?> result;
+
+ /**
+ * Constructor
+ *
+ * @param task VisualizationTask
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.result = task.getResult();
+ context.addDataStoreListener(this);
+ context.addResultListener(this);
+ incrementalRedraw();
+ }
+
+ @Override
+ protected void redraw() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ addCSSClasses(svgp);
+ final double p = getLPNormP(result);
+ final boolean angular = isAngularDistance(result);
+
+ final double size = style.getSize(StyleLibrary.SELECTION);
+ DBIDSelection selContext = context.getSelection();
+ if(selContext != null) {
+ DBIDs selection = selContext.getSelectedIds();
+
+ for(DBIDIter i = selection.iter(); i.valid(); i.advance()) {
+ final KNNResult<D> knn = result.get(i);
+ for(DistanceDBIDResultIter<D> iter = knn.iter(); iter.valid(); iter.advance()) {
+ try {
+ double[] v = proj.fastProjectDataToRenderSpace(rel.get(iter));
+ Element dot = svgp.svgCircle(v[0], v[1], size);
+ SVGUtil.addCSSClass(dot, KNNMARKER);
+ layer.appendChild(dot);
+
+ Element lbl = svgp.svgText(v[0] + size, v[1] + size, iter.getDistance().toString());
+ SVGUtil.addCSSClass(lbl, KNNDIST);
+ layer.appendChild(lbl);
+ }
+ catch(ObjectNotFoundException e) {
+ // ignore
+ }
+ }
+ // Last element
+ DistanceDBIDPair<D> last = knn.get(knn.size() - 1);
+ // Draw hypersphere if possible
+ {
+ final Element dist;
+ if(p == 1.0) {
+ dist = SVGHyperSphere.drawManhattan(svgp, proj, rel.get(i), last.getDistance());
+ }
+ else if(p == 2.0) {
+ dist = SVGHyperSphere.drawEuclidean(svgp, proj, rel.get(i), last.getDistance());
+ }
+ else if(!Double.isNaN(p)) {
+ dist = SVGHyperSphere.drawLp(svgp, proj, rel.get(i), last.getDistance(), p);
+ }
+ else if(angular) {
+ final NumberVector<?> refvec = rel.get(i);
+ // Recompute the angle - it could be cosine or arccosine distance
+ double maxangle = Math.acos(VectorUtil.cosAngle(refvec, rel.get(last)));
+ dist = drawCosine(svgp, proj, refvec, maxangle);
+ }
+ else {
+ dist = null;
+ }
+ if(dist != null) {
+ SVGUtil.addCSSClass(dist, DISTANCEFUNCTION);
+ layer.appendChild(dist);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ // Class for the distance markers
+ if(!svgp.getCSSClassManager().contains(KNNMARKER)) {
+ CSSClass cls = new CSSClass(this, KNNMARKER);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_DARKGREEN_VALUE);
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
+ svgp.addCSSClassOrLogError(cls);
+ }
+ // Class for the distance function
+ if(!svgp.getCSSClassManager().contains(DISTANCEFUNCTION)) {
+ CSSClass cls = new CSSClass(this, DISTANCEFUNCTION);
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_RED_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ svgp.addCSSClassOrLogError(cls);
+ }
+ // Class for the distance label
+ if(!svgp.getCSSClassManager().contains(KNNDIST)) {
+ CSSClass cls = new CSSClass(this, KNNDIST);
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
+ cls.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, style.getTextSize(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_FONT_FAMILY_PROPERTY, style.getFontFamily(StyleLibrary.PLOT));
+ svgp.addCSSClassOrLogError(cls);
+ }
+ }
+
+ @Override
+ public void resultChanged(Result current) {
+ if(current instanceof SelectionResult) {
+ synchronizedRedraw();
+ return;
+ }
+ super.resultChanged(current);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/MoveObjectsToolVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/MoveObjectsToolVisualization.java
index 6b466a88..c2282bf1 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/MoveObjectsToolVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/MoveObjectsToolVisualization.java
@@ -56,179 +56,179 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatter
* @author Heidi Kolb
* @author Erich Schubert
*
- * @apiviz.has NumberVector oneway - - edits
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class MoveObjectsToolVisualization extends AbstractScatterplotVisualization implements DragListener {
+public class MoveObjectsToolVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Move Objects";
/**
- * CSS tag for our event rectangle
+ * Constructor
*/
- protected static final String CSS_ARROW = "moveArrow";
-
- /**
- * Element for the rectangle to add listeners
- */
- private Element etag;
-
- /**
- * Element to contain the drag arrow
- */
- private Element rtag;
-
- /**
- * Constructor.
- *
- * @param task Task
- */
- public MoveObjectsToolVisualization(VisualizationTask task) {
- super(task);
- incrementalRedraw();
+ public MoveObjectsToolVisualization() {
+ super();
}
@Override
- public void resultChanged(Result current) {
- if(sample == current) {
- synchronizedRedraw();
- }
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
-
- rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
- SVGUtil.addCSSClass(rtag, CSS_ARROW);
- layer.appendChild(rtag);
-
- DragableArea drag = new DragableArea(svgp, -0.6 * StyleLibrary.SCALE, -0.7 * StyleLibrary.SCALE, 1.3 * StyleLibrary.SCALE, 1.4 * StyleLibrary.SCALE, this);
- etag = drag.getElement();
- layer.appendChild(etag);
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<UpdatableDatabase> dbs = ResultUtil.filterResults(result, UpdatableDatabase.class);
+ if(dbs.isEmpty()) {
+ return;
+ }
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, p.getRelation(), p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ task.tool = true;
+ task.thumbnail = false;
+ task.noexport = true;
+ task.initDefaultVisibility(false);
+ // baseResult.getHierarchy().add(p.getRelation(), task);
+ baseResult.getHierarchy().add(p, task);
+ }
}
/**
- * Updates the objects with the given DBIDs It will be moved depending on the
- * given Vector
+ * Instance.
+ *
+ * @author Heidi Kolb
+ * @author Erich Schubert
*
- * @param dbids - DBIDs of the objects to move
- * @param movingVector - Vector for moving object
+ * @apiviz.has NumberVector oneway - - edits
*/
- // TODO: move to DatabaseUtil?
- private void updateDB(DBIDs dbids, Vector movingVector) {
- NumberVector<?, ?> nv = null;
- throw new AbortException("FIXME: INCOMPLETE TRANSITION");
- /*
- * database.accumulateDataStoreEvents();
- * Representation<DatabaseObjectMetadata> mrep =
- * database.getMetadataQuery(); for(DBID dbid : dbids) { NV obj =
- * database.get(dbid); // Copy metadata to keep DatabaseObjectMetadata meta
- * = mrep.get(dbid);
- *
- * Vector v = proj.projectDataToRenderSpace(obj); v.set(0, v.get(0) +
- * movingVector.get(0)); v.set(1, v.get(1) + movingVector.get(1)); NV nv =
- * proj.projectRenderToDataSpace(v, obj); nv.setID(obj.getID());
- *
- * try { database.delete(dbid); database.insert(new Pair<NV,
- * DatabaseObjectMetadata>(nv, meta)); } catch(UnableToComplyException e) {
- * de.lmu.ifi.dbs.elki.logging.LoggingUtil.exception(e); } }
- * database.flushDataStoreEvents();
+ public class Instance extends AbstractScatterplotVisualization implements DragListener {
+ /**
+ * CSS tag for our event rectangle
*/
- }
+ protected static final String CSS_ARROW = "moveArrow";
- /**
- * Delete the children of the element
- *
- * @param container SVG-Element
- */
- private void deleteChildren(Element container) {
- while(container.hasChildNodes()) {
- container.removeChild(container.getLastChild());
+ /**
+ * Element for the rectangle to add listeners
+ */
+ private Element etag;
+
+ /**
+ * Element to contain the drag arrow
+ */
+ private Element rtag;
+
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
}
- }
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVGPlot
- */
- private void addCSSClasses(SVGPlot svgp) {
- // Class for the rectangle to add eventListeners
- if(!svgp.getCSSClassManager().contains(CSS_ARROW)) {
- final CSSClass acls = new CSSClass(this, CSS_ARROW);
- final StyleLibrary style = context.getStyleLibrary();
- acls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
- acls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.SELECTION_ACTIVE));
- acls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- svgp.addCSSClassOrLogError(acls);
+ @Override
+ public void resultChanged(Result current) {
+ if(sample == current) {
+ synchronizedRedraw();
+ }
}
- }
- @Override
- public boolean startDrag(SVGPoint startPoint, Event evt) {
- return true;
- }
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
- @Override
- public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- deleteChildren(rtag);
- rtag.appendChild(svgp.svgLine(startPoint.getX(), startPoint.getY(), dragPoint.getX(), dragPoint.getY()));
- return true;
- }
+ rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ SVGUtil.addCSSClass(rtag, CSS_ARROW);
+ layer.appendChild(rtag);
- @Override
- public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- Vector movingVector = new Vector(2);
- movingVector.set(0, dragPoint.getX() - startPoint.getX());
- movingVector.set(1, dragPoint.getY() - startPoint.getY());
- if(context.getSelection() != null) {
- updateDB(context.getSelection().getSelectedIds(), movingVector);
+ DragableArea drag = new DragableArea(svgp, -0.6 * StyleLibrary.SCALE, -0.7 * StyleLibrary.SCALE, 1.3 * StyleLibrary.SCALE, 1.4 * StyleLibrary.SCALE, this);
+ etag = drag.getElement();
+ layer.appendChild(etag);
}
- deleteChildren(rtag);
- return true;
- }
- /**
- * Factory for tool visualizations for changing objects in the database
- *
- * @author Heidi Kolb
- * @author Erich Schubert
- *
- * @apiviz.stereotype factory
- * @apiviz.uses MoveObjectsToolVisualization oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
/**
- * Constructor
+ * Updates the objects with the given DBIDs It will be moved depending on
+ * the given Vector
+ *
+ * @param dbids - DBIDs of the objects to move
+ * @param movingVector - Vector for moving object
*/
- public Factory() {
- super();
+ // TODO: move to DatabaseUtil?
+ private void updateDB(DBIDs dbids, Vector movingVector) {
+ NumberVector<?> nv = null;
+ throw new AbortException("FIXME: INCOMPLETE TRANSITION");
+ /*
+ * database.accumulateDataStoreEvents();
+ * Representation<DatabaseObjectMetadata> mrep =
+ * database.getMetadataQuery(); for(DBID dbid : dbids) { NV obj =
+ * database.get(dbid); // Copy metadata to keep DatabaseObjectMetadata
+ * meta = mrep.get(dbid);
+ *
+ * Vector v = proj.projectDataToRenderSpace(obj); v.set(0, v.get(0) +
+ * movingVector.get(0)); v.set(1, v.get(1) + movingVector.get(1)); NV nv =
+ * proj.projectRenderToDataSpace(v, obj); nv.setID(obj.getID());
+ *
+ * try { database.delete(dbid); database.insert(new Pair<NV,
+ * DatabaseObjectMetadata>(nv, meta)); } catch(UnableToComplyException e)
+ * { de.lmu.ifi.dbs.elki.logging.LoggingUtil.exception(e); } }
+ * database.flushDataStoreEvents();
+ */
+ }
+
+ /**
+ * Delete the children of the element
+ *
+ * @param container SVG-Element
+ */
+ private void deleteChildren(Element container) {
+ while(container.hasChildNodes()) {
+ container.removeChild(container.getLastChild());
+ }
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVGPlot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ // Class for the rectangle to add eventListeners
+ if(!svgp.getCSSClassManager().contains(CSS_ARROW)) {
+ final CSSClass acls = new CSSClass(this, CSS_ARROW);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ acls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
+ acls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.SELECTION_ACTIVE));
+ acls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ svgp.addCSSClassOrLogError(acls);
+ }
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new MoveObjectsToolVisualization(task);
+ public boolean startDrag(SVGPoint startPoint, Event evt) {
+ return true;
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<UpdatableDatabase> dbs = ResultUtil.filterResults(result, UpdatableDatabase.class);
- if(dbs.isEmpty()) {
- return;
- }
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, p.getRelation(), p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_NOTHUMB, true);
- task.put(VisualizationTask.META_NOEXPORT, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- // baseResult.getHierarchy().add(p.getRelation(), task);
- baseResult.getHierarchy().add(p, task);
+ public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ deleteChildren(rtag);
+ rtag.appendChild(svgp.svgLine(startPoint.getX(), startPoint.getY(), dragPoint.getX(), dragPoint.getY()));
+ return true;
+ }
+
+ @Override
+ public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ Vector movingVector = new Vector(2);
+ movingVector.set(0, dragPoint.getX() - startPoint.getX());
+ movingVector.set(1, dragPoint.getY() - startPoint.getY());
+ if(context.getSelection() != null) {
+ updateDB(context.getSelection().getSelectedIds(), movingVector);
}
+ deleteChildren(rtag);
+ return true;
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionConvexHullVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionConvexHullVisualization.java
index 8a28ab3e..f3d41e08 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionConvexHullVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionConvexHullVisualization.java
@@ -58,114 +58,115 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
*
* @author Robert Rödler
*
- * @apiviz.has SelectionResult oneway - - visualizes
- * @apiviz.has DBIDSelection oneway - - visualizes
- * @apiviz.uses GrahamScanConvexHull2D
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class SelectionConvexHullVisualization extends AbstractScatterplotVisualization implements DataStoreListener {
+public class SelectionConvexHullVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Convex Hull of Selection";
/**
- * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * Constructor
*/
- public static final String SELECTEDHULL = "selectionConvexHull";
-
- /**
- * Constructor.
- *
- * @param task Task
- */
- public SelectionConvexHullVisualization(VisualizationTask task) {
- super(task);
- context.addResultListener(this);
- context.addDataStoreListener(this);
- incrementalRedraw();
+ public SelectionConvexHullVisualization() {
+ super();
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
- DBIDSelection selContext = context.getSelection();
- if(selContext != null) {
- DBIDs selection = selContext.getSelectedIds();
- GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
- for(DBIDIter iter = selection.iter(); iter.valid(); iter.advance()) {
- try {
- hull.add(new Vector(proj.fastProjectDataToRenderSpace(rel.get(iter))));
- }
- catch(ObjectNotFoundException e) {
- // ignore
- }
- }
- Polygon chres = hull.getHull();
- if(chres != null && chres.size() >= 3) {
- SVGPath path = new SVGPath(chres);
-
- Element selHull = path.makeElement(svgp);
- SVGUtil.addCSSClass(selHull, SELECTEDHULL);
- // TODO: use relative selection size for opacity?
- layer.appendChild(selHull);
- }
- }
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- // Class for the dot markers
- if(!svgp.getCSSClassManager().contains(SELECTEDHULL)) {
- CSSClass cls = new CSSClass(this, SELECTEDHULL);
- // cls = new CSSClass(this, CONVEXHULL);
- cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, context.getStyleLibrary().getColor(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, context.getStyleLibrary().getLineWidth(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, context.getStyleLibrary().getColor(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, ".25");
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- svgp.addCSSClassOrLogError(cls);
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA - 2;
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
+ }
}
}
/**
- * Factory for visualizers to generate an SVG-Element containing the convex
- * hull of the selected points
+ * Instance
*
* @author Robert Rödler
*
- * @apiviz.stereotype factory
- * @apiviz.uses SelectionConvexHullVisualization oneway - - «create»
+ * @apiviz.has SelectionResult oneway - - visualizes
+ * @apiviz.has DBIDSelection oneway - - visualizes
+ * @apiviz.uses GrahamScanConvexHull2D
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractScatterplotVisualization implements DataStoreListener {
/**
- * Constructor
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
*/
- public Factory() {
- super();
- thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
- }
+ public static final String SELECTEDHULL = "selectionConvexHull";
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionConvexHullVisualization(task);
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ context.addResultListener(this);
+ context.addDataStoreListener(this);
+ incrementalRedraw();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
- for(SelectionResult selres : selectionResults) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 2);
- baseResult.getHierarchy().add(selres, task);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ addCSSClasses(svgp);
+ DBIDSelection selContext = context.getSelection();
+ if(selContext != null) {
+ DBIDs selection = selContext.getSelectedIds();
+ GrahamScanConvexHull2D hull = new GrahamScanConvexHull2D();
+ for(DBIDIter iter = selection.iter(); iter.valid(); iter.advance()) {
+ try {
+ hull.add(new Vector(proj.fastProjectDataToRenderSpace(rel.get(iter))));
+ }
+ catch(ObjectNotFoundException e) {
+ // ignore
+ }
}
+ Polygon chres = hull.getHull();
+ if(chres != null && chres.size() >= 3) {
+ SVGPath path = new SVGPath(chres);
+
+ Element selHull = path.makeElement(svgp);
+ SVGUtil.addCSSClass(selHull, SELECTEDHULL);
+ // TODO: use relative selection size for opacity?
+ layer.appendChild(selHull);
+ }
+ }
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ // Class for the dot markers
+ if(!svgp.getCSSClassManager().contains(SELECTEDHULL)) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ CSSClass cls = new CSSClass(this, SELECTEDHULL);
+ // cls = new CSSClass(this, CONVEXHULL);
+ cls.setStatement(SVGConstants.CSS_STROKE_PROPERTY, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, ".25");
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ svgp.addCSSClassOrLogError(cls);
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionCubeVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionCubeVisualization.java
index 5151e81e..6d4c14ab 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionCubeVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionCubeVisualization.java
@@ -28,13 +28,13 @@ import java.util.Collection;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.result.DBIDSelection;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
import de.lmu.ifi.dbs.elki.result.RangeSelection;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.SelectionResult;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
@@ -59,141 +59,179 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
*
* @author Heidi Kolb
*
- * @apiviz.has SelectionResult oneway - - visualizes
- * @apiviz.has RangeSelection oneway - - visualizes
- * @apiviz.uses SVGHyperCube
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class SelectionCubeVisualization extends AbstractScatterplotVisualization {
+public class SelectionCubeVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Selection Range";
/**
- * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * Settings
*/
- public static final String MARKER = "selectionCubeMarker";
+ protected Parameterizer settings;
/**
- * CSS class for the filled cube
- */
- public static final String CSS_CUBE = "selectionCube";
-
- /**
- * CSS class for the cube frame
- */
- public static final String CSS_CUBEFRAME = "selectionCubeFrame";
-
- /**
- * Fill parameter.
+ * Constructor.
+ *
+ * @param settings Settings
*/
- protected boolean nofill = false;
-
- public SelectionCubeVisualization(VisualizationTask task, boolean nofill) {
- super(task);
- this.nofill = nofill;
- addCSSClasses(svgp);
- context.addResultListener(this);
- incrementalRedraw();
+ public SelectionCubeVisualization(Parameterizer settings) {
+ super();
+ this.settings = settings;
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
}
@Override
- public void destroy() {
- context.removeResultListener(this);
- super.destroy();
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- final StyleLibrary style = context.getStyleLibrary();
- // Class for the cube
- if(!svgp.getCSSClassManager().contains(CSS_CUBE)) {
- CSSClass cls = new CSSClass(this, CSS_CUBE);
- cls.setStatement(SVGConstants.CSS_STROKE_VALUE, style.getColor(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
- cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
- if(nofill) {
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
- }
- else {
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA - 2;
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
}
- svgp.addCSSClassOrLogError(cls);
- }
- // Class for the cube frame
- if(!svgp.getCSSClassManager().contains(CSS_CUBEFRAME)) {
- CSSClass cls = new CSSClass(this, CSS_CUBEFRAME);
- cls.setStatement(SVGConstants.CSS_STROKE_VALUE, style.getColor(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.SELECTION));
-
- svgp.addCSSClassOrLogError(cls);
}
}
/**
- * Generates a cube and a frame depending on the selection stored in the
- * context
+ * Instance.
+ *
+ * @author Heidi Kolb
*
- * @param svgp The plot
- * @param proj The projection
+ * @apiviz.has SelectionResult oneway - - visualizes
+ * @apiviz.has RangeSelection oneway - - visualizes
+ * @apiviz.uses SVGHyperCube
*/
- private void setSVGRect(SVGPlot svgp, Projection2D proj) {
- DBIDSelection selContext = context.getSelection();
- if(selContext instanceof RangeSelection) {
- DoubleDoublePair[] ranges = ((RangeSelection) selContext).getRanges();
- int dim = DatabaseUtil.dimensionality(rel);
+ public class Instance extends AbstractScatterplotVisualization {
+ /**
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String MARKER = "selectionCubeMarker";
+
+ /**
+ * CSS class for the filled cube
+ */
+ public static final String CSS_CUBE = "selectionCube";
+
+ /**
+ * CSS class for the cube frame
+ */
+ public static final String CSS_CUBEFRAME = "selectionCubeFrame";
+
+ public Instance(VisualizationTask task) {
+ super(task);
+ addCSSClasses(svgp);
+ context.addResultListener(this);
+ incrementalRedraw();
+ }
- double[] min = new double[dim];
- double[] max = new double[dim];
- for(int d = 0; d < dim; d++) {
- if(ranges != null && ranges[d] != null) {
- min[d] = ranges[d].first;
- max[d] = ranges[d].second;
+ @Override
+ public void destroy() {
+ context.removeResultListener(this);
+ super.destroy();
+ }
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ // Class for the cube
+ if(!svgp.getCSSClassManager().contains(CSS_CUBE)) {
+ CSSClass cls = new CSSClass(this, CSS_CUBE);
+ cls.setStatement(SVGConstants.CSS_STROKE_VALUE, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.PLOT));
+ cls.setStatement(SVGConstants.CSS_STROKE_LINECAP_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ cls.setStatement(SVGConstants.CSS_STROKE_LINEJOIN_PROPERTY, SVGConstants.CSS_ROUND_VALUE);
+ if(settings.nofill) {
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
}
else {
- min[d] = proj.getScale(d).getMin();
- max[d] = proj.getScale(d).getMax();
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_FILL_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
}
+ svgp.addCSSClassOrLogError(cls);
}
- if(nofill) {
- Element r = SVGHyperCube.drawFrame(svgp, proj, min, max);
- SVGUtil.setCSSClass(r, CSS_CUBEFRAME);
- layer.appendChild(r);
- }
- else {
- Element r = SVGHyperCube.drawFilled(svgp, CSS_CUBE, proj, min, max);
- layer.appendChild(r);
+ // Class for the cube frame
+ if(!svgp.getCSSClassManager().contains(CSS_CUBEFRAME)) {
+ CSSClass cls = new CSSClass(this, CSS_CUBEFRAME);
+ cls.setStatement(SVGConstants.CSS_STROKE_VALUE, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.SELECTION));
+
+ svgp.addCSSClassOrLogError(cls);
}
+ }
+
+ /**
+ * Generates a cube and a frame depending on the selection stored in the
+ * context
+ *
+ * @param svgp The plot
+ * @param proj The projection
+ */
+ private void setSVGRect(SVGPlot svgp, Projection2D proj) {
+ DBIDSelection selContext = context.getSelection();
+ if(selContext instanceof RangeSelection) {
+ DoubleDoublePair[] ranges = ((RangeSelection) selContext).getRanges();
+ int dim = RelationUtil.dimensionality(rel);
+
+ double[] min = new double[dim];
+ double[] max = new double[dim];
+ for(int d = 0; d < dim; d++) {
+ if(ranges != null && ranges[d] != null) {
+ min[d] = ranges[d].first;
+ max[d] = ranges[d].second;
+ }
+ else {
+ min[d] = proj.getScale(d).getMin();
+ max[d] = proj.getScale(d).getMax();
+ }
+ }
+ if(settings.nofill) {
+ Element r = SVGHyperCube.drawFrame(svgp, proj, min, max);
+ SVGUtil.setCSSClass(r, CSS_CUBEFRAME);
+ layer.appendChild(r);
+ }
+ else {
+ Element r = SVGHyperCube.drawFilled(svgp, CSS_CUBE, proj, min, max);
+ layer.appendChild(r);
+ }
+ }
}
- }
- @Override
- protected void redraw() {
- DBIDSelection selContext = context.getSelection();
- if(selContext != null && selContext instanceof RangeSelection) {
- setSVGRect(svgp, proj);
+ @Override
+ protected void redraw() {
+ DBIDSelection selContext = context.getSelection();
+ if(selContext instanceof RangeSelection) {
+ setSVGRect(svgp, proj);
+ }
}
}
/**
- * Factory for visualizers to generate an SVG-Element containing a cube as
- * marker representing the selected range for each dimension
+ * Parameterization class.
*
- * @author Heidi Kolb
+ * @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses SelectionCubeVisualization oneway - - «create»
+ * @apiviz.exclude
*/
- public static class Factory extends AbstractVisFactory {
+ public static class Parameterizer extends AbstractParameterizer {
/**
* Flag for half-transparent filling of selection cubes.
*
@@ -201,66 +239,25 @@ public class SelectionCubeVisualization extends AbstractScatterplotVisualization
* Key: {@code -selectionrange.nofill}
* </p>
*/
- public static final OptionID NOFILL_ID = OptionID.getOrCreateOptionID("selectionrange.nofill", "Use wireframe style for selection ranges.");
+ public static final OptionID NOFILL_ID = new OptionID("selectionrange.nofill", "Use wireframe style for selection ranges.");
/**
* Fill parameter.
*/
- protected boolean nofill = false;
-
- /**
- * Constructor.
- *
- * @param nofill
- */
- public Factory(boolean nofill) {
- super();
- this.nofill = nofill;
- thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
- }
+ protected boolean nofill;
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionCubeVisualization(task, nofill);
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
- for(SelectionResult selres : selectionResults) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 2);
- baseResult.getHierarchy().add(selres, task);
- baseResult.getHierarchy().add(p, task);
- }
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ Flag nofillF = new Flag(NOFILL_ID);
+ if(config.grab(nofillF)) {
+ nofill = nofillF.isTrue();
}
}
- /**
- * Parameterization class.
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- public static class Parameterizer extends AbstractParameterizer {
- protected boolean nofill;
-
- @Override
- protected void makeOptions(Parameterization config) {
- super.makeOptions(config);
- Flag nofillF = new Flag(NOFILL_ID);
- if(config.grab(nofillF)) {
- nofill = nofillF.getValue();
- }
- }
-
- @Override
- protected Factory makeInstance() {
- return new Factory(nofill);
- }
+ @Override
+ protected SelectionCubeVisualization makeInstance() {
+ return new SelectionCubeVisualization(this);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionDotVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionDotVisualization.java
index 1dc5ce13..b70755c4 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionDotVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionDotVisualization.java
@@ -54,110 +54,111 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.thumbs.ThumbnailVisualizati
*
* @author Heidi Kolb
*
- * @apiviz.has SelectionResult oneway - - visualizes
- * @apiviz.has DBIDSelection oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class SelectionDotVisualization extends AbstractScatterplotVisualization implements DataStoreListener {
+public class SelectionDotVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Selection";
/**
- * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * Constructor
*/
- public static final String MARKER = "selectionDotMarker";
-
- /**
- * Constructor.
- *
- * @param task Task
- */
- public SelectionDotVisualization(VisualizationTask task) {
- super(task);
- context.addResultListener(this);
- context.addDataStoreListener(this);
- incrementalRedraw();
+ public SelectionDotVisualization() {
+ super();
+ thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
}
@Override
- public void destroy() {
- context.removeResultListener(this);
- super.destroy();
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
- final double size = context.getStyleLibrary().getSize(StyleLibrary.SELECTION);
- DBIDSelection selContext = context.getSelection();
- if(selContext != null) {
- DBIDs selection = selContext.getSelectedIds();
- for(DBIDIter iter = selection.iter(); iter.valid(); iter.advance()) {
- try {
- double[] v = proj.fastProjectDataToRenderSpace(rel.get(iter));
- Element dot = svgp.svgCircle(v[0], v[1], size);
- SVGUtil.addCSSClass(dot, MARKER);
- layer.appendChild(dot);
- }
- catch(ObjectNotFoundException e) {
- // ignore
- }
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_DATA - 1;
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
}
}
}
/**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- private void addCSSClasses(SVGPlot svgp) {
- // Class for the dot markers
- if(!svgp.getCSSClassManager().contains(MARKER)) {
- CSSClass cls = new CSSClass(this, MARKER);
- final StyleLibrary style = context.getStyleLibrary();
- cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION));
- cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
- svgp.addCSSClassOrLogError(cls);
- }
- }
-
- /**
- * Factory for visualizers to generate an SVG-Element containing dots as
- * markers representing the selected Database's objects.
+ * Instance
*
* @author Heidi Kolb
*
- * @apiviz.stereotype factory
- * @apiviz.uses SelectionDotVisualization oneway - - «create»
+ * @apiviz.has SelectionResult oneway - - visualizes
+ * @apiviz.has DBIDSelection oneway - - visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractScatterplotVisualization implements DataStoreListener {
+ /**
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ public static final String MARKER = "selectionDotMarker";
+
/**
- * Constructor
+ * Constructor.
+ *
+ * @param task Task
*/
- public Factory() {
- super();
- thumbmask |= ThumbnailVisualization.ON_DATA | ThumbnailVisualization.ON_SELECTION;
+ public Instance(VisualizationTask task) {
+ super(task);
+ context.addResultListener(this);
+ context.addDataStoreListener(this);
+ incrementalRedraw();
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionDotVisualization(task);
+ public void destroy() {
+ context.removeResultListener(this);
+ super.destroy();
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
- for(SelectionResult selres : selectionResults) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_DATA - 1);
- baseResult.getHierarchy().add(selres, task);
- baseResult.getHierarchy().add(p, task);
+ protected void redraw() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ addCSSClasses(svgp);
+ final double size = style.getSize(StyleLibrary.SELECTION);
+ DBIDSelection selContext = context.getSelection();
+ if(selContext != null) {
+ DBIDs selection = selContext.getSelectedIds();
+ for(DBIDIter iter = selection.iter(); iter.valid(); iter.advance()) {
+ try {
+ double[] v = proj.fastProjectDataToRenderSpace(rel.get(iter));
+ Element dot = svgp.svgCircle(v[0], v[1], size);
+ SVGUtil.addCSSClass(dot, MARKER);
+ layer.appendChild(dot);
+ }
+ catch(ObjectNotFoundException e) {
+ // ignore
+ }
}
}
}
+
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ private void addCSSClasses(SVGPlot svgp) {
+ // Class for the dot markers
+ if(!svgp.getCSSClassManager().contains(MARKER)) {
+ CSSClass cls = new CSSClass(this, MARKER);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION));
+ cls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION));
+ svgp.addCSSClassOrLogError(cls);
+ }
+ }
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolCubeVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolCubeVisualization.java
index bc16fd83..f088d219 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolCubeVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolCubeVisualization.java
@@ -35,6 +35,7 @@ import de.lmu.ifi.dbs.elki.data.NumberVector;
import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs;
+import de.lmu.ifi.dbs.elki.database.relation.RelationUtil;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.result.DBIDSelection;
import de.lmu.ifi.dbs.elki.result.HierarchicalResult;
@@ -42,7 +43,6 @@ import de.lmu.ifi.dbs.elki.result.RangeSelection;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultUtil;
import de.lmu.ifi.dbs.elki.result.SelectionResult;
-import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil;
import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleDoublePair;
import de.lmu.ifi.dbs.elki.visualization.VisualizationTask;
import de.lmu.ifi.dbs.elki.visualization.batikutil.DragableArea;
@@ -57,18 +57,18 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatterplotVisualization;
/**
- * Tool-Visualization for the tool to select ranges
+ * Tool-Visualization for the tool to select ranges.
*
* @author Heidi Kolb
*
- * @apiviz.has SelectionResult oneway - - updates
- * @apiviz.has RangeSelection oneway - - updates
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class SelectionToolCubeVisualization extends AbstractScatterplotVisualization implements DragableArea.DragListener {
+public class SelectionToolCubeVisualization extends AbstractVisFactory {
/**
* The logger for this class.
*/
- protected static final Logging logger = Logging.getLogger(SelectionToolCubeVisualization.class);
+ private static final Logging LOG = Logging.getLogger(SelectionToolCubeVisualization.class);
/**
* A short name characterizing this Visualizer.
@@ -76,219 +76,218 @@ public class SelectionToolCubeVisualization extends AbstractScatterplotVisualiza
private static final String NAME = "Range Selection";
/**
- * Generic tag to indicate the type of element. Used in IDs, CSS-Classes etc.
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}.
*/
- private static final String CSS_RANGEMARKER = "selectionRangeMarker";
-
- /**
- * Dimension
- */
- private int dim;
-
- /**
- * Element for selection rectangle
- */
- private Element rtag;
-
- /**
- * Element for the rectangle to add listeners
- */
- private Element etag;
-
- /**
- * Constructor.
- *
- * @param task Task
- */
- public SelectionToolCubeVisualization(VisualizationTask task) {
- super(task);
- this.dim = DatabaseUtil.dimensionality(rel);
- incrementalRedraw();
+ public SelectionToolCubeVisualization() {
+ super();
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
-
- // rtag: tag for the selected rect
- rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
- SVGUtil.addCSSClass(rtag, CSS_RANGEMARKER);
- layer.appendChild(rtag);
-
- // etag: sensitive area
- DragableArea drag = new DragableArea(svgp, -0.6 * StyleLibrary.SCALE, -0.7 * StyleLibrary.SCALE, 1.3 * StyleLibrary.SCALE, 1.4 * StyleLibrary.SCALE, this);
- etag = drag.getElement();
- layer.appendChild(etag);
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
- /**
- * Delete the children of the element
- *
- * @param container SVG-Element
- */
- private void deleteChildren(Element container) {
- while(container.hasChildNodes()) {
- container.removeChild(container.getLastChild());
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for (SelectionResult selres : selectionResults) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for (ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ task.tool = true;
+ task.thumbnail = false;
+ task.noexport = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
+ }
}
}
/**
- * Set the selected ranges and the mask for the actual dimensions in the
- * context
+ * Instance.
*
- * @param x1 x-value of the first dimension
- * @param x2 x-value of the second dimension
- * @param y1 y-value of the first dimension
- * @param y2 y-value of the second dimension
+ * @author Heidi Kolb
+ *
+ * @apiviz.has SelectionResult oneway - - updates
+ * @apiviz.has RangeSelection oneway - - updates
*/
- private void updateSelectionRectKoordinates(double x1, double x2, double y1, double y2, DoubleDoublePair[] ranges) {
- BitSet actDim = proj.getVisibleDimensions2D();
- double[] v1 = new double[dim];
- double[] v2 = new double[dim];
- v1[0] = x1;
- v1[1] = y1;
- v2[0] = x2;
- v2[1] = y2;
-
- double[] nv1 = proj.fastProjectRenderToDataSpace(v1);
- double[] nv2 = proj.fastProjectRenderToDataSpace(v2);
-
- for(int d = actDim.nextSetBit(0); d >= 0; d = actDim.nextSetBit(d + 1)) {
- ranges[d] = new DoubleDoublePair(Math.min(nv1[d], nv2[d]), Math.max(nv1[d], nv2[d]));
- }
- }
+ public class Instance extends AbstractScatterplotVisualization implements DragableArea.DragListener {
+ /**
+ * Generic tag to indicate the type of element. Used in IDs, CSS-Classes
+ * etc.
+ */
+ private static final String CSS_RANGEMARKER = "selectionRangeMarker";
- @Override
- public boolean startDrag(SVGPoint startPoint, Event evt) {
- return true;
- }
+ /**
+ * Dimension.
+ */
+ private int dim;
- @Override
- public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- deleteChildren(rtag);
- double x = Math.min(startPoint.getX(), dragPoint.getX());
- double y = Math.min(startPoint.getY(), dragPoint.getY());
- double width = Math.abs(startPoint.getX() - dragPoint.getX());
- double height = Math.abs(startPoint.getY() - dragPoint.getY());
- rtag.appendChild(svgp.svgRect(x, y, width, height));
- return true;
- }
+ /**
+ * Element for selection rectangle.
+ */
+ private Element rtag;
- @Override
- public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- deleteChildren(rtag);
- if(startPoint.getX() != dragPoint.getX() || startPoint.getY() != dragPoint.getY()) {
- updateSelection(proj, startPoint, dragPoint);
- }
- return true;
- }
+ /**
+ * Element for the rectangle to add listeners.
+ */
+ private Element etag;
- /**
- * Update the selection in the context.
- *
- * @param proj The projection
- * @param p1 First Point of the selected rectangle
- * @param p2 Second Point of the selected rectangle
- */
- private void updateSelection(Projection proj, SVGPoint p1, SVGPoint p2) {
- if(p1 == null || p2 == null) {
- logger.warning("no rect selected: p1: " + p1 + " p2: " + p2);
- return;
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.dim = RelationUtil.dimensionality(rel);
+ incrementalRedraw();
}
- DBIDSelection selContext = context.getSelection();
- ModifiableDBIDs selection;
- if(selContext != null) {
- selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
- }
- else {
- selection = DBIDUtil.newHashSet();
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
+
+ // rtag: tag for the selected rect
+ rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ SVGUtil.addCSSClass(rtag, CSS_RANGEMARKER);
+ layer.appendChild(rtag);
+
+ // etag: sensitive area
+ DragableArea drag = new DragableArea(svgp, -0.6 * StyleLibrary.SCALE, -0.7 * StyleLibrary.SCALE, 1.3 * StyleLibrary.SCALE, 1.4 * StyleLibrary.SCALE, this);
+ etag = drag.getElement();
+ layer.appendChild(etag);
}
- DoubleDoublePair[] ranges;
-
- double x1 = Math.min(p1.getX(), p2.getX());
- double x2 = Math.max(p1.getX(), p2.getX());
- double y1 = Math.max(p1.getY(), p2.getY());
- double y2 = Math.min(p1.getY(), p2.getY());
- if(selContext instanceof RangeSelection) {
- ranges = ((RangeSelection) selContext).getRanges();
- }
- else {
- ranges = new DoubleDoublePair[dim];
- }
- updateSelectionRectKoordinates(x1, x2, y1, y2, ranges);
-
- selection.clear();
- candidates: for(DBIDIter iditer = rel.iterDBIDs(); iditer.valid(); iditer.advance()) {
- NumberVector<?, ?> dbTupel = rel.get(iditer);
- for(int i = 0; i < dim; i++) {
- if(ranges != null && ranges[i] != null) {
- if(dbTupel.doubleValue(i + 1) < ranges[i].first || dbTupel.doubleValue(i + 1) > ranges[i].second) {
- continue candidates;
- }
- }
+ /**
+ * Delete the children of the element.
+ *
+ * @param container SVG-Element
+ */
+ private void deleteChildren(Element container) {
+ while (container.hasChildNodes()) {
+ container.removeChild(container.getLastChild());
}
- selection.add(iditer);
}
- context.setSelection(new RangeSelection(selection, ranges));
- }
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- protected void addCSSClasses(SVGPlot svgp) {
- // Class for the range marking
- if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
- final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
- final StyleLibrary style = context.getStyleLibrary();
- rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
- rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION_ACTIVE));
- svgp.addCSSClassOrLogError(rcls);
- }
- }
-
- /**
- * Factory for tool visualizations for selecting ranges and the inclosed
- * objects
- *
- * @author Heidi Kolb
- *
- * @apiviz.stereotype factory
- * @apiviz.uses SelectionToolCubeVisualization oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Set the selected ranges and the mask for the actual dimensions in the
+ * context.
+ *
+ * @param x1 x-value of the first dimension
+ * @param x2 x-value of the second dimension
+ * @param y1 y-value of the first dimension
+ * @param y2 y-value of the second dimension
+ * @param ranges Ranges to update
*/
- public Factory() {
- super();
+ private void updateSelectionRectKoordinates(double x1, double x2, double y1, double y2, DoubleDoublePair[] ranges) {
+ BitSet actDim = proj.getVisibleDimensions2D();
+ double[] v1 = new double[dim];
+ double[] v2 = new double[dim];
+ v1[0] = x1;
+ v1[1] = y1;
+ v2[0] = x2;
+ v2[1] = y2;
+
+ double[] nv1 = proj.fastProjectRenderToDataSpace(v1);
+ double[] nv2 = proj.fastProjectRenderToDataSpace(v2);
+
+ for (int d = actDim.nextSetBit(0); d >= 0; d = actDim.nextSetBit(d + 1)) {
+ ranges[d] = new DoubleDoublePair(Math.min(nv1[d], nv2[d]), Math.max(nv1[d], nv2[d]));
+ }
+ }
+
+ @Override
+ public boolean startDrag(SVGPoint startPoint, Event evt) {
+ return true;
}
@Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionToolCubeVisualization(task);
+ public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ deleteChildren(rtag);
+ double x = Math.min(startPoint.getX(), dragPoint.getX());
+ double y = Math.min(startPoint.getY(), dragPoint.getY());
+ double width = Math.abs(startPoint.getX() - dragPoint.getX());
+ double height = Math.abs(startPoint.getY() - dragPoint.getY());
+ rtag.appendChild(svgp.svgRect(x, y, width, height));
+ return true;
}
@Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
- for(SelectionResult selres : selectionResults) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_NOTHUMB, true);
- task.put(VisualizationTask.META_NOEXPORT, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(selres, task);
- baseResult.getHierarchy().add(p, task);
+ public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ deleteChildren(rtag);
+ if (startPoint.getX() != dragPoint.getX() || startPoint.getY() != dragPoint.getY()) {
+ updateSelection(proj, startPoint, dragPoint);
+ }
+ return true;
+ }
+
+ /**
+ * Update the selection in the context.
+ *
+ * @param proj The projection
+ * @param p1 First Point of the selected rectangle
+ * @param p2 Second Point of the selected rectangle
+ */
+ private void updateSelection(Projection proj, SVGPoint p1, SVGPoint p2) {
+ if (p1 == null || p2 == null) {
+ LOG.warning("no rect selected: p1: " + p1 + " p2: " + p2);
+ return;
+ }
+
+ DBIDSelection selContext = context.getSelection();
+ ModifiableDBIDs selection;
+ if (selContext != null) {
+ selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
+ } else {
+ selection = DBIDUtil.newHashSet();
+ }
+ DoubleDoublePair[] ranges;
+
+ double x1 = Math.min(p1.getX(), p2.getX());
+ double x2 = Math.max(p1.getX(), p2.getX());
+ double y1 = Math.max(p1.getY(), p2.getY());
+ double y2 = Math.min(p1.getY(), p2.getY());
+
+ if (selContext instanceof RangeSelection) {
+ ranges = ((RangeSelection) selContext).getRanges();
+ } else {
+ ranges = new DoubleDoublePair[dim];
+ }
+ updateSelectionRectKoordinates(x1, x2, y1, y2, ranges);
+
+ selection.clear();
+ candidates: for (DBIDIter iditer = rel.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ NumberVector<?> dbTupel = rel.get(iditer);
+ for (int i = 0; i < dim; i++) {
+ if (ranges != null && ranges[i] != null) {
+ if (dbTupel.doubleValue(i) < ranges[i].first || dbTupel.doubleValue(i) > ranges[i].second) {
+ continue candidates;
+ }
+ }
}
+ selection.add(iditer);
+ }
+ context.setSelection(new RangeSelection(selection, ranges));
+ }
+
+ /**
+ * Adds the required CSS-Classes.
+ *
+ * @param svgp SVG-Plot
+ */
+ protected void addCSSClasses(SVGPlot svgp) {
+ // Class for the range marking
+ if (!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
+ final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
+ rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION_ACTIVE));
+ svgp.addCSSClassOrLogError(rcls);
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolDotVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolDotVisualization.java
index b52f37c9..6fa8200c 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolDotVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection/SelectionToolDotVisualization.java
@@ -56,21 +56,16 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.scatterplot.AbstractScatter
*
* @author Heidi Kolb
*
- * @apiviz.has SelectionResult oneway - - updates
- * @apiviz.has DBIDSelection oneway - - updates
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance - - «create»
*/
-public class SelectionToolDotVisualization extends AbstractScatterplotVisualization implements DragableArea.DragListener {
+public class SelectionToolDotVisualization extends AbstractVisFactory {
/**
* A short name characterizing this Visualizer.
*/
private static final String NAME = "Object Selection";
/**
- * CSS class of the selection rectangle while selecting.
- */
- private static final String CSS_RANGEMARKER = "selectionRangeMarker";
-
- /**
* Input modes
*
* @apiviz.exclude
@@ -80,194 +75,199 @@ public class SelectionToolDotVisualization extends AbstractScatterplotVisualizat
}
/**
- * Element for selection rectangle
- */
- Element rtag;
-
- /**
- * Element for the rectangle to add listeners
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- Element etag;
-
- /**
- * Constructor.
- *
- * @param task Task
- */
- public SelectionToolDotVisualization(VisualizationTask task) {
- super(task);
- incrementalRedraw();
+ public SelectionToolDotVisualization() {
+ super();
}
@Override
- protected void redraw() {
- addCSSClasses(svgp);
-
- //
- rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
- SVGUtil.addCSSClass(rtag, CSS_RANGEMARKER);
- layer.appendChild(rtag);
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
- // etag: sensitive area
- DragableArea drag = new DragableArea(svgp, -0.6 * StyleLibrary.SCALE, -0.7 * StyleLibrary.SCALE, 1.3 * StyleLibrary.SCALE, 1.4 * StyleLibrary.SCALE, this);
- etag = drag.getElement();
- layer.appendChild(etag);
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
+ for(SelectionResult selres : selectionResults) {
+ Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
+ for(ScatterPlotProjector<?> p : ps) {
+ final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
+ task.level = VisualizationTask.LEVEL_INTERACTIVE;
+ task.tool = true;
+ task.thumbnail = false;
+ task.noexport = true;
+ task.initDefaultVisibility(false);
+ baseResult.getHierarchy().add(selres, task);
+ baseResult.getHierarchy().add(p, task);
+ }
+ }
}
/**
- * Delete the children of the element
+ * Instance
*
- * @param container SVG-Element
+ * @author Heidi Kolb
+ *
+ * @apiviz.has SelectionResult oneway - - updates
+ * @apiviz.has DBIDSelection oneway - - updates
*/
- private void deleteChildren(Element container) {
- while(container.hasChildNodes()) {
- container.removeChild(container.getLastChild());
+ public class Instance extends AbstractScatterplotVisualization implements DragableArea.DragListener {
+ /**
+ * CSS class of the selection rectangle while selecting.
+ */
+ private static final String CSS_RANGEMARKER = "selectionRangeMarker";
+
+ /**
+ * Element for selection rectangle
+ */
+ Element rtag;
+
+ /**
+ * Element for the rectangle to add listeners
+ */
+ Element etag;
+
+ /**
+ * Constructor.
+ *
+ * @param task Task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ incrementalRedraw();
}
- }
- @Override
- public boolean startDrag(SVGPoint startPoint, Event evt) {
- return true;
- }
+ @Override
+ protected void redraw() {
+ addCSSClasses(svgp);
- @Override
- public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- deleteChildren(rtag);
- double x = Math.min(startPoint.getX(), dragPoint.getX());
- double y = Math.min(startPoint.getY(), dragPoint.getY());
- double width = Math.abs(startPoint.getX() - dragPoint.getX());
- double height = Math.abs(startPoint.getY() - dragPoint.getY());
- rtag.appendChild(svgp.svgRect(x, y, width, height));
- return true;
- }
+ //
+ rtag = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ SVGUtil.addCSSClass(rtag, CSS_RANGEMARKER);
+ layer.appendChild(rtag);
- @Override
- public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
- Mode mode = getInputMode(evt);
- deleteChildren(rtag);
- if(startPoint.getX() != dragPoint.getX() || startPoint.getY() != dragPoint.getY()) {
- updateSelection(mode, proj, startPoint, dragPoint);
+ // etag: sensitive area
+ DragableArea drag = new DragableArea(svgp, -0.6 * StyleLibrary.SCALE, -0.7 * StyleLibrary.SCALE, 1.3 * StyleLibrary.SCALE, 1.4 * StyleLibrary.SCALE, this);
+ etag = drag.getElement();
+ layer.appendChild(etag);
}
- return true;
- }
- /**
- * Get the current input mode, on each mouse event.
- *
- * @param evt Mouse event.
- * @return current input mode
- */
- private Mode getInputMode(Event evt) {
- if(evt instanceof DOMMouseEvent) {
- DOMMouseEvent domme = (DOMMouseEvent) evt;
- // TODO: visual indication of mode possible?
- if(domme.getShiftKey()) {
- return Mode.ADD;
- }
- else if(domme.getCtrlKey()) {
- return Mode.INVERT;
- }
- else {
- return Mode.REPLACE;
+ /**
+ * Delete the children of the element
+ *
+ * @param container SVG-Element
+ */
+ private void deleteChildren(Element container) {
+ while(container.hasChildNodes()) {
+ container.removeChild(container.getLastChild());
}
}
- // Default mode is replace.
- return Mode.REPLACE;
- }
- /**
- * Updates the selection in the context.<br>
- *
- * @param mode Input mode
- * @param proj
- * @param p1 first point of the selected rectangle
- * @param p2 second point of the selected rectangle
- */
- private void updateSelection(Mode mode, Projection2D proj, SVGPoint p1, SVGPoint p2) {
- DBIDSelection selContext = context.getSelection();
- // Note: we rely on SET semantics below!
- HashSetModifiableDBIDs selection;
- if(selContext == null || mode == Mode.REPLACE) {
- selection = DBIDUtil.newHashSet();
+ @Override
+ public boolean startDrag(SVGPoint startPoint, Event evt) {
+ return true;
}
- else {
- selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
+
+ @Override
+ public boolean duringDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ deleteChildren(rtag);
+ double x = Math.min(startPoint.getX(), dragPoint.getX());
+ double y = Math.min(startPoint.getY(), dragPoint.getY());
+ double width = Math.abs(startPoint.getX() - dragPoint.getX());
+ double height = Math.abs(startPoint.getY() - dragPoint.getY());
+ rtag.appendChild(svgp.svgRect(x, y, width, height));
+ return true;
}
- for(DBIDIter iditer = rel.iterDBIDs(); iditer.valid(); iditer.advance()) {
- double[] vec = proj.fastProjectDataToRenderSpace(rel.get(iditer));
- if(vec[0] >= Math.min(p1.getX(), p2.getX()) && vec[0] <= Math.max(p1.getX(), p2.getX()) && vec[1] >= Math.min(p1.getY(), p2.getY()) && vec[1] <= Math.max(p1.getY(), p2.getY())) {
- if(mode == Mode.INVERT) {
- if(!selection.contains(iditer)) {
- selection.add(iditer);
- }
- else {
- selection.remove(iditer);
- }
+
+ @Override
+ public boolean endDrag(SVGPoint startPoint, SVGPoint dragPoint, Event evt, boolean inside) {
+ Mode mode = getInputMode(evt);
+ deleteChildren(rtag);
+ if(startPoint.getX() != dragPoint.getX() || startPoint.getY() != dragPoint.getY()) {
+ updateSelection(mode, proj, startPoint, dragPoint);
+ }
+ return true;
+ }
+
+ /**
+ * Get the current input mode, on each mouse event.
+ *
+ * @param evt Mouse event.
+ * @return current input mode
+ */
+ private Mode getInputMode(Event evt) {
+ if(evt instanceof DOMMouseEvent) {
+ DOMMouseEvent domme = (DOMMouseEvent) evt;
+ // TODO: visual indication of mode possible?
+ if(domme.getShiftKey()) {
+ return Mode.ADD;
+ }
+ else if(domme.getCtrlKey()) {
+ return Mode.INVERT;
}
else {
- // In REPLACE and ADD, add objects.
- // The difference was done before by not re-using the selection.
- // Since we are using a set, we can just add in any case.
- selection.add(iditer);
+ return Mode.REPLACE;
}
}
+ // Default mode is replace.
+ return Mode.REPLACE;
}
- context.setSelection(new DBIDSelection(selection));
- }
- /**
- * Adds the required CSS-Classes
- *
- * @param svgp SVG-Plot
- */
- protected void addCSSClasses(SVGPlot svgp) {
- // Class for the range marking
- if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
- final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
- final StyleLibrary style = context.getStyleLibrary();
- rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
- rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION_ACTIVE));
- svgp.addCSSClassOrLogError(rcls);
- }
- }
-
- /**
- * Factory for tool visualizations for selecting objects
- *
- * @author Heidi Kolb
- *
- * @apiviz.stereotype factory
- * @apiviz.uses SelectionToolDotVisualization - - «create»
- */
- public static class Factory extends AbstractVisFactory {
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * Updates the selection in the context.<br>
+ *
+ * @param mode Input mode
+ * @param proj
+ * @param p1 first point of the selected rectangle
+ * @param p2 second point of the selected rectangle
*/
- public Factory() {
- super();
- }
-
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SelectionToolDotVisualization(task);
+ private void updateSelection(Mode mode, Projection2D proj, SVGPoint p1, SVGPoint p2) {
+ DBIDSelection selContext = context.getSelection();
+ // Note: we rely on SET semantics below!
+ HashSetModifiableDBIDs selection;
+ if(selContext == null || mode == Mode.REPLACE) {
+ selection = DBIDUtil.newHashSet();
+ }
+ else {
+ selection = DBIDUtil.newHashSet(selContext.getSelectedIds());
+ }
+ for(DBIDIter iditer = rel.iterDBIDs(); iditer.valid(); iditer.advance()) {
+ double[] vec = proj.fastProjectDataToRenderSpace(rel.get(iditer));
+ if(vec[0] >= Math.min(p1.getX(), p2.getX()) && vec[0] <= Math.max(p1.getX(), p2.getX()) && vec[1] >= Math.min(p1.getY(), p2.getY()) && vec[1] <= Math.max(p1.getY(), p2.getY())) {
+ if(mode == Mode.INVERT) {
+ if(!selection.contains(iditer)) {
+ selection.add(iditer);
+ }
+ else {
+ selection.remove(iditer);
+ }
+ }
+ else {
+ // In REPLACE and ADD, add objects.
+ // The difference was done before by not re-using the selection.
+ // Since we are using a set, we can just add in any case.
+ selection.add(iditer);
+ }
+ }
+ }
+ context.setSelection(new DBIDSelection(selection));
}
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<SelectionResult> selectionResults = ResultUtil.filterResults(result, SelectionResult.class);
- for(SelectionResult selres : selectionResults) {
- Collection<ScatterPlotProjector<?>> ps = ResultUtil.filterResults(baseResult, ScatterPlotProjector.class);
- for(ScatterPlotProjector<?> p : ps) {
- final VisualizationTask task = new VisualizationTask(NAME, selres, p.getRelation(), this);
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_INTERACTIVE);
- task.put(VisualizationTask.META_TOOL, true);
- task.put(VisualizationTask.META_NOTHUMB, true);
- task.put(VisualizationTask.META_NOEXPORT, true);
- task.put(VisualizationTask.META_VISIBLE_DEFAULT, false);
- baseResult.getHierarchy().add(selres, task);
- baseResult.getHierarchy().add(p, task);
- }
+ /**
+ * Adds the required CSS-Classes
+ *
+ * @param svgp SVG-Plot
+ */
+ protected void addCSSClasses(SVGPlot svgp) {
+ // Class for the range marking
+ if(!svgp.getCSSClassManager().contains(CSS_RANGEMARKER)) {
+ final CSSClass rcls = new CSSClass(this, CSS_RANGEMARKER);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ rcls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getColor(StyleLibrary.SELECTION_ACTIVE));
+ rcls.setStatement(SVGConstants.CSS_OPACITY_PROPERTY, style.getOpacity(StyleLibrary.SELECTION_ACTIVE));
+ svgp.addCSSClassOrLogError(rcls);
}
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailVisualization.java
index 9ea016a5..dd502146 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/thumbs/ThumbnailVisualization.java
@@ -91,7 +91,8 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
protected int tresolution;
/**
- * The event mask. See {@link #ON_DATA}, {@link #ON_SELECTION}, {@link #ON_STYLE}, {@link #NO_PROJECTION}
+ * The event mask. See {@link #ON_DATA}, {@link #ON_SELECTION},
+ * {@link #ON_STYLE}, {@link #NO_PROJECTION}
*/
private int mask;
@@ -110,14 +111,13 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
public ThumbnailVisualization(VisFactory visFactory, VisualizationTask task, int mask) {
super(task);
this.visFactory = visFactory;
- Integer tres = task.getGenerics(VisualizationTask.THUMBNAIL_RESOLUTION, Integer.class);
- this.tresolution = tres;
+ this.tresolution = task.thumbsize;
this.layer = task.getPlot().svgElement(SVGConstants.SVG_G_TAG);
this.thumbid = -1;
this.thumb = null;
this.mask = mask;
// Listen for database events only when needed.
- if((mask & ON_DATA) == ON_DATA) {
+ if ((mask & ON_DATA) == ON_DATA) {
context.addDataStoreListener(this);
}
// Listen for result changes, including the one we monitor
@@ -126,7 +126,7 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
@Override
public void destroy() {
- if(pendingThumbnail != null) {
+ if (pendingThumbnail != null) {
ThumbnailThread.UNQUEUE(pendingThumbnail);
}
// TODO: remove image from registry?
@@ -136,7 +136,7 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
@Override
public Element getLayer() {
- if(thumbid < 0) {
+ if (thumbid < 0) {
synchronizedRedraw();
}
return layer;
@@ -150,15 +150,14 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
@Override
protected void incrementalRedraw() {
final Element oldcontainer;
- if(layer.hasChildNodes()) {
+ if (layer.hasChildNodes()) {
oldcontainer = layer;
layer = (Element) layer.cloneNode(false);
- }
- else {
+ } else {
oldcontainer = null;
}
redraw();
- if(oldcontainer != null && oldcontainer.getParentNode() != null) {
+ if (oldcontainer != null && oldcontainer.getParentNode() != null) {
oldcontainer.getParentNode().replaceChild(layer, oldcontainer);
}
}
@@ -168,14 +167,13 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
*/
@Override
protected void redraw() {
- if(thumbid < 0) {
+ if (thumbid < 0) {
// LoggingUtil.warning("Generating new thumbnail " + this);
layer.appendChild(SVGUtil.svgWaitIcon(task.getPlot().getDocument(), 0, 0, task.getWidth(), task.getHeight()));
- if(pendingThumbnail == null) {
+ if (pendingThumbnail == null) {
pendingThumbnail = ThumbnailThread.QUEUE(this);
}
- }
- else {
+ } else {
// LoggingUtil.warning("Injecting Thumbnail " + this);
Element i = task.getPlot().svgElement(SVGConstants.SVG_IMAGE_TAG);
SVGUtil.setAtt(i, SVGConstants.SVG_X_ATTRIBUTE, 0);
@@ -196,7 +194,7 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
// Work on a clone
VisualizationTask clone = task.clone(plot, context);
- clone.put(VisualizationTask.THUMBNAIL, false);
+ clone.thumbnail = false;
Visualization vis = visFactory.makeVisualization(clone);
plot.getRoot().appendChild(vis.getLayer());
@@ -208,13 +206,11 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
// The visualization will not be used anymore.
vis.destroy();
synchronizedRedraw();
- }
- catch(Exception e) {
+ } catch (Exception e) {
final Logging logger = Logging.getLogger(task.getFactory().getClass());
- if(logger != null && logger.isDebugging()) {
+ if (logger != null && logger.isDebugging()) {
logger.exception("Thumbnail failed.", e);
- }
- else {
+ } else {
LoggingUtil.warning("Thumbnail for " + task.getFactory().getClass().getName() + " failed - enable debugging to see details.");
}
// TODO: hide the failed image?
@@ -231,11 +227,11 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
@Override
public void resultChanged(Result current) {
- if((mask & ON_SELECTION) == ON_SELECTION && current instanceof SelectionResult) {
+ if ((mask & ON_SELECTION) == ON_SELECTION && current instanceof SelectionResult) {
refreshThumbnail();
return;
}
- if((mask & ON_STYLE) == ON_STYLE && current instanceof StyleResult) {
+ if ((mask & ON_STYLE) == ON_STYLE && current instanceof StyleResult) {
refreshThumbnail();
return;
}
@@ -245,4 +241,4 @@ public class ThumbnailVisualization extends AbstractVisualization implements Thu
}
super.resultChanged(current);
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/ClusterEvaluationVisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/ClusterEvaluationVisualization.java
index 5b58e842..4b57a503 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/ClusterEvaluationVisFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/ClusterEvaluationVisualization.java
@@ -48,7 +48,7 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGScoreBar;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualizationInstance;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
/**
@@ -58,10 +58,10 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @author Sascha Goldhofer
*
* @apiviz.stereotype factory
- * @apiviz.uses StaticVisualization oneway - - «create»
+ * @apiviz.uses StaticVisualizationInstance oneway - - «create»
* @apiviz.has de.lmu.ifi.dbs.elki.evaluation.clustering.EvaluateClustering.ScoreResult oneway - - visualizes
*/
-public class ClusterEvaluationVisFactory extends AbstractVisFactory {
+public class ClusterEvaluationVisualization extends AbstractVisFactory {
/**
* Name for this visualizer.
*/
@@ -80,7 +80,7 @@ public class ClusterEvaluationVisFactory extends AbstractVisFactory {
/**
* Constructor.
*/
- public ClusterEvaluationVisFactory() {
+ public ClusterEvaluationVisualization() {
super();
}
@@ -91,7 +91,7 @@ public class ClusterEvaluationVisFactory extends AbstractVisFactory {
final VisualizationTask task = new VisualizationTask(NAME, sr, null, this);
task.width = .5;
task.height = 2.0;
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_STATIC);
+ task.level = VisualizationTask.LEVEL_STATIC;
baseResult.getHierarchy().add(sr, task);
}
}
@@ -181,11 +181,12 @@ public class ClusterEvaluationVisFactory extends AbstractVisFactory {
double cols = 10; // Math.max(10, (int) (i * task.getHeight() /
// task.getWidth()));
double rows = ypos;
- final double margin = task.getContext().getStyleLibrary().getSize(StyleLibrary.MARGIN);
+ final StyleLibrary style = task.getContext().getStyleResult().getStyleLibrary();
+ final double margin = style.getSize(StyleLibrary.MARGIN);
final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), cols, rows, margin / StyleLibrary.SCALE);
SVGUtil.setAtt(parent, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
- return new StaticVisualization(task, parent);
+ return new StaticVisualizationInstance(task, parent);
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisualization.java
index 117c3c2c..d97b5117 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/HistogramVisualization.java
@@ -47,7 +47,7 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGSimpleLinearAxis;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualizationInstance;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
/**
@@ -56,10 +56,10 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @author Erich Schubert
*
* @apiviz.stereotype factory
- * @apiviz.uses StaticVisualization oneway - - «create»
+ * @apiviz.uses StaticVisualizationInstance oneway - - «create»
* @apiviz.has HistogramResult oneway - - visualizes
*/
-public class HistogramVisFactory extends AbstractVisFactory {
+public class HistogramVisualization extends AbstractVisFactory {
/**
* Histogram visualizer name
*/
@@ -74,7 +74,7 @@ public class HistogramVisFactory extends AbstractVisFactory {
* Constructor, adhering to
* {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- public HistogramVisFactory() {
+ public HistogramVisualization() {
super();
}
@@ -82,31 +82,30 @@ public class HistogramVisFactory extends AbstractVisFactory {
public Visualization makeVisualization(VisualizationTask task) {
VisualizerContext context = task.getContext();
SVGPlot svgp = task.getPlot();
- HistogramResult<? extends NumberVector<?, ?>> curve = task.getResult();
-
- double scale = StyleLibrary.SCALE;
- final double sizex = scale;
- final double sizey = scale * task.getHeight() / task.getWidth();
- final double margin = context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
+ HistogramResult<? extends NumberVector<?>> curve = task.getResult();
+
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final double sizex = StyleLibrary.SCALE;
+ final double sizey = StyleLibrary.SCALE * task.getHeight() / task.getWidth();
+ final double margin = style.getSize(StyleLibrary.MARGIN);
Element layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), sizex, sizey, margin);
SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
-
+
// find maximum, determine step size
- Integer dim = null;
+ int dim = -1;
DoubleMinMax xminmax = new DoubleMinMax();
DoubleMinMax yminmax = new DoubleMinMax();
- for(NumberVector<?, ?> vec : curve) {
- xminmax.put(vec.doubleValue(1));
- if(dim == null) {
+ for (NumberVector<?> vec : curve) {
+ xminmax.put(vec.doubleValue(0));
+ if (dim < 0) {
dim = vec.getDimensionality();
- }
- else {
+ } else {
// TODO: test and throw always
assert (dim == vec.getDimensionality());
}
- for(int i = 1; i < dim; i++) {
- yminmax.put(vec.doubleValue(i + 1));
+ for (int i = 0; i < dim; i++) {
+ yminmax.put(vec.doubleValue(i));
}
}
// Minimum should always start at 0 for histograms
@@ -118,42 +117,41 @@ public class HistogramVisFactory extends AbstractVisFactory {
double range = xminmax.getMax() - xminmax.getMin();
double binwidth = range / (size - 1);
- LinearScale xscale = new LinearScale(xminmax.getMin() - binwidth / 2, xminmax.getMax() + binwidth / 2);
+ LinearScale xscale = new LinearScale(xminmax.getMin() - binwidth * .5, xminmax.getMax() + binwidth * .5);
LinearScale yscale = new LinearScale(yminmax.getMin(), yminmax.getMax());
SVGPath[] path = new SVGPath[dim];
- for(int i = 0; i < dim; i++) {
- path[i] = new SVGPath(sizex * xscale.getScaled(xminmax.getMin() - binwidth / 2), sizey);
+ for (int i = 0; i < dim; i++) {
+ path[i] = new SVGPath(sizex * xscale.getScaled(xminmax.getMin() - binwidth * .5), sizey);
}
// draw curves.
- for(NumberVector<?, ?> vec : curve) {
- for(int d = 0; d < dim; d++) {
- path[d].lineTo(sizex * (xscale.getScaled(vec.doubleValue(1) - binwidth / 2)), sizey * (1 - yscale.getScaled(vec.doubleValue(d + 2))));
- path[d].lineTo(sizex * (xscale.getScaled(vec.doubleValue(1) + binwidth / 2)), sizey * (1 - yscale.getScaled(vec.doubleValue(d + 2))));
+ for (NumberVector<?> vec : curve) {
+ for (int d = 0; d < dim; d++) {
+ path[d].lineTo(sizex * (xscale.getScaled(vec.doubleValue(0) - binwidth * .5)), sizey * (1 - yscale.getScaled(vec.doubleValue(d + 1))));
+ path[d].lineTo(sizex * (xscale.getScaled(vec.doubleValue(0) + binwidth * .5)), sizey * (1 - yscale.getScaled(vec.doubleValue(d + 1))));
}
}
// close all histograms
- for(int i = 0; i < dim; i++) {
- path[i].lineTo(sizex * xscale.getScaled(xminmax.getMax() + binwidth / 2), sizey);
+ for (int i = 0; i < dim; i++) {
+ path[i].lineTo(sizex * xscale.getScaled(xminmax.getMax() + binwidth * .5), sizey);
}
// add axes
try {
- SVGSimpleLinearAxis.drawAxis(svgp, layer, yscale, 0, sizey, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, context.getStyleLibrary());
- SVGSimpleLinearAxis.drawAxis(svgp, layer, xscale, 0, sizey, sizex, sizey, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, context.getStyleLibrary());
- }
- catch(CSSNamingConflict e) {
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, yscale, 0, sizey, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, style);
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, xscale, 0, sizey, sizex, sizey, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, style);
+ } catch (CSSNamingConflict e) {
LoggingUtil.exception(e);
}
// Setup line styles and insert lines.
- ColorLibrary cl = context.getStyleLibrary().getColorSet(StyleLibrary.PLOT);
- for(int d = 0; d < dim; d++) {
+ ColorLibrary cl = style.getColorSet(StyleLibrary.PLOT);
+ for (int d = 0; d < dim; d++) {
CSSClass csscls = new CSSClass(this, SERIESID + "_" + d);
csscls.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, SVGConstants.SVG_NONE_VALUE);
csscls.setStatement(SVGConstants.SVG_STROKE_ATTRIBUTE, cl.getColor(d));
- csscls.setStatement(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, context.getStyleLibrary().getLineWidth(StyleLibrary.PLOT));
+ csscls.setStatement(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, style.getLineWidth(StyleLibrary.PLOT));
svgp.addCSSClassOrLogError(csscls);
Element line = path[d].makeElement(svgp);
@@ -161,17 +159,17 @@ public class HistogramVisFactory extends AbstractVisFactory {
layer.appendChild(line);
}
- return new StaticVisualization(task, layer);
+ return new StaticVisualizationInstance(task, layer);
}
@Override
public void processNewResult(HierarchicalResult baseResult, Result newResult) {
- List<HistogramResult<? extends NumberVector<?, ?>>> histograms = ResultUtil.filterResults(newResult, HistogramResult.class);
- for(HistogramResult<? extends NumberVector<?, ?>> histogram : histograms) {
+ List<HistogramResult<? extends NumberVector<?>>> histograms = ResultUtil.filterResults(newResult, HistogramResult.class);
+ for (HistogramResult<? extends NumberVector<?>> histogram : histograms) {
final VisualizationTask task = new VisualizationTask(NAME, histogram, null, this);
task.width = 2.0;
task.height = 1.0;
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_STATIC);
+ task.level = VisualizationTask.LEVEL_STATIC;
baseResult.getHierarchy().add(histogram, task);
}
}
@@ -181,4 +179,4 @@ public class HistogramVisFactory extends AbstractVisFactory {
// TODO: depending on the histogram complexity?
return false;
}
-} \ No newline at end of file
+}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/KeyVisualization.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/KeyVisualization.java
index 62a4fb5c..f21e4df6 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/KeyVisualization.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/KeyVisualization.java
@@ -52,147 +52,182 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
/**
* Visualizer, displaying the key for a clustering.
- *
+ *
* @author Erich Schubert
*
- * @apiviz.has Clustering oneway - - visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class KeyVisualization extends AbstractVisualization {
+public class KeyVisualization extends AbstractVisFactory {
/**
* Name for this visualizer.
*/
private static final String NAME = "Cluster Key";
- /**
- * Clustering to display
- */
- private Clustering<Model> clustering;
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result newResult) {
+ // Find clusterings we can visualize:
+ Collection<Clustering<?>> clusterings = ResultUtil.filterResults(newResult, Clustering.class);
+ for(Clustering<?> c : clusterings) {
+ final int numc = c.getAllClusters().size();
+ if(numc > 0) {
+ // FIXME: compute from labels?
+ final double maxwidth = 10.;
+ final VisualizationTask task = new VisualizationTask(NAME, c, null, this);
+ final int cols = getPreferredColumns(1.0, 1.0, numc, maxwidth);
+ final int rows = (int) Math.ceil(numc / (double) cols);
+ final double div = Math.max(2. + rows, cols * maxwidth);
+ task.width = cols * maxwidth / div;
+ task.height = (2. + rows) / div;
+ task.level = VisualizationTask.LEVEL_STATIC;
+ task.nodetail = true;
+ baseResult.getHierarchy().add(c, task);
+ }
+ }
+ }
/**
- * Constructor.
+ * Compute the preferred number of columns.
*
- * @param task Visualization task
+ * @param width Target width
+ * @param height Target height
+ * @param numc Number of clusters
+ * @param maxwidth Max width of entries
+ * @return Preferred number of columns
*/
- public KeyVisualization(VisualizationTask task) {
- super(task);
- this.clustering = task.getResult();
- context.addResultListener(this);
+ public static int getPreferredColumns(double width, double height, int numc, double maxwidth) {
+ // Maximum width (compared to height) of labels - guess.
+ // FIXME: do we really need to do this three-step computation?
+ // Number of rows we'd use in a squared layout:
+ final double rows = Math.ceil(Math.pow(numc * maxwidth, height / (width + height)));
+ // Given this number of rows (plus two for header), use this many columns:
+ return (int) Math.ceil(numc / (rows + 2));
}
@Override
- public void destroy() {
- context.removeResultListener(this);
- super.destroy();
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- public void resultChanged(Result current) {
- super.resultChanged(current);
- if(current == context.getStyleResult()) {
- incrementalRedraw();
- }
+ public boolean allowThumbnails(VisualizationTask task) {
+ return false;
}
- @Override
- protected void redraw() {
- SVGPlot svgp = task.getPlot();
- final List<Cluster<Model>> allcs = clustering.getAllClusters();
-
- StyleLibrary style = context.getStyleLibrary();
- MarkerLibrary ml = style.markers();
- layer = svgp.svgElement(SVGConstants.SVG_G_TAG);
-
- // Add a label for the clustering.
- {
- Element label = svgp.svgText(0.1, 0.7, clustering.getLongName());
- label.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: 0.4; fill: "+style.getTextColor(StyleLibrary.DEFAULT));
- layer.appendChild(label);
+ /**
+ * Instance
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has Clustering oneway - - visualizes
+ */
+ public class Instance extends AbstractVisualization {
+ /**
+ * Clustering to display
+ */
+ private Clustering<Model> clustering;
+
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.clustering = task.getResult();
+ context.addResultListener(this);
}
- // TODO: multi-column layout!
- int i = 0;
- for(Cluster<Model> c : allcs) {
- ml.useMarker(svgp, layer, 0.3, i + 1.5, i, 0.3);
- Element label = svgp.svgText(0.7, i + 1.7, c.getNameAutomatic());
- label.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: 0.6; fill: "+style.getTextColor(StyleLibrary.DEFAULT));
- layer.appendChild(label);
- i++;
+ @Override
+ public void destroy() {
+ context.removeResultListener(this);
+ super.destroy();
}
- // Add a button to set style policy
- {
- StylingPolicy sp = context.getStyleResult().getStylingPolicy();
- if(sp instanceof ClusterStylingPolicy && ((ClusterStylingPolicy) sp).getClustering() == clustering) {
- // Don't show the button when active. May confuse people more than the disappearing button
-
- // SVGButton button = new SVGButton(.1, i + 1.1, 3.8, .7, .2);
- // button.setTitle("Active style", "darkgray");
- // layer.appendChild(button.render(svgp));
- }
- else {
- SVGButton button = new SVGButton(.1, i + 1.1, 3.8, .7, .2);
- button.setTitle("Set style", "black");
- Element elem = button.render(svgp);
- // Attach listener
- EventTarget etr = (EventTarget) elem;
- etr.addEventListener(SVGConstants.SVG_CLICK_EVENT_TYPE, new EventListener() {
- @Override
- public void handleEvent(Event evt) {
- setStylePolicy();
- }
- }, false);
- layer.appendChild(elem);
+ @Override
+ public void resultChanged(Result current) {
+ super.resultChanged(current);
+ if(current == context.getStyleResult()) {
+ incrementalRedraw();
}
}
- int rows = i + 2;
- int cols = Math.max(6, (int) (rows * task.getHeight() / task.getWidth()));
- final double margin = style.getSize(StyleLibrary.MARGIN);
- final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), cols, rows, margin / StyleLibrary.SCALE);
- SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
- }
+ @Override
+ protected void redraw() {
+ SVGPlot svgp = task.getPlot();
+ StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ MarkerLibrary ml = style.markers();
+
+ // Maximum width (compared to height) of labels - guess.
+ // FIXME: compute from labels?
+ final double maxwidth = 10.;
+
+ final List<Cluster<Model>> allcs = clustering.getAllClusters();
+ final int numc = allcs.size();
+ final int cols = getPreferredColumns(task.getWidth(), task.getHeight(), numc, maxwidth);
+ final int rows = 2 + (int) Math.ceil(numc / (double) cols);
+ // We use a coordinate system based on rows, so columns are at c*maxwidth
+
+ layer = svgp.svgElement(SVGConstants.SVG_G_TAG);
+ // Add a label for the clustering.
+ {
+ Element label = svgp.svgText(0.1, 0.7, clustering.getLongName());
+ label.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: 0.4; fill: " + style.getTextColor(StyleLibrary.DEFAULT));
+ layer.appendChild(label);
+ }
- /**
- * Trigger a style change.
- */
- protected void setStylePolicy() {
- context.getStyleResult().setStylingPolicy(new ClusterStylingPolicy(clustering, context.getStyleLibrary()));
- context.getHierarchy().resultChanged(context.getStyleResult());
- }
+ int i = 0;
+ for(Cluster<Model> c : allcs) {
+ final int col = i / rows;
+ final int row = i % rows;
+ ml.useMarker(svgp, layer, 0.3 + maxwidth * col, row + 1.5, i, 0.3);
+ Element label = svgp.svgText(0.7 + maxwidth * col, row + 1.7, c.getNameAutomatic());
+ label.setAttribute(SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: 0.6; fill: " + style.getTextColor(StyleLibrary.DEFAULT));
+ layer.appendChild(label);
+ i++;
+ }
- /**
- * Visualization factory
- *
- * @author Erich Schubert
- *
- * @apiviz.stereotype factory
- * @apiviz.uses KeyVisualization oneway - - «create»
- */
- public static class Factory extends AbstractVisFactory {
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result newResult) {
- // Find clusterings we can visualize:
- Collection<Clustering<?>> clusterings = ResultUtil.filterResults(newResult, Clustering.class);
- for (Clustering<?> c : clusterings) {
- if(c.getAllClusters().size() > 0) {
- final VisualizationTask task = new VisualizationTask(NAME, c, null, this);
- task.width = 1.0;
- task.height = 1.0;
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_STATIC);
- task.put(VisualizationTask.META_NODETAIL, true);
- baseResult.getHierarchy().add(c, task);
+ // Add a button to set style policy
+ {
+ StylingPolicy sp = context.getStyleResult().getStylingPolicy();
+ if(sp instanceof ClusterStylingPolicy && ((ClusterStylingPolicy) sp).getClustering() == clustering) {
+ // Don't show the button when active. May confuse people more than the
+ // disappearing button?
+
+ // SVGButton button = new SVGButton(.1, rows + 1.1, 3.8, .7, .2);
+ // button.setTitle("Active style", "darkgray");
+ // layer.appendChild(button.render(svgp));
+ }
+ else {
+ SVGButton button = new SVGButton(.1, rows + 1.1, 3.8, .7, .2);
+ button.setTitle("Set style", "black");
+ Element elem = button.render(svgp);
+ // Attach listener
+ EventTarget etr = (EventTarget) elem;
+ etr.addEventListener(SVGConstants.SVG_CLICK_EVENT_TYPE, new EventListener() {
+ @Override
+ public void handleEvent(Event evt) {
+ setStylePolicy();
+ }
+ }, false);
+ layer.appendChild(elem);
}
}
- }
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new KeyVisualization(task);
+ // int rows = i + 2;
+ // int cols = Math.max(6, (int) (rows * task.getHeight() /
+ // task.getWidth()));
+ final double margin = style.getSize(StyleLibrary.MARGIN);
+ final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), cols * maxwidth, rows, margin / StyleLibrary.SCALE);
+ SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
}
- @Override
- public boolean allowThumbnails(VisualizationTask task) {
- return false;
+ /**
+ * Trigger a style change.
+ */
+ protected void setStylePolicy() {
+ context.getStyleResult().setStylingPolicy(new ClusterStylingPolicy(clustering, context.getStyleResult().getStyleLibrary()));
+ context.getHierarchy().resultChanged(context.getStyleResult());
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/LabelVisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/LabelVisualization.java
index b6eb4549..d29a6467 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/LabelVisFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/LabelVisualization.java
@@ -35,7 +35,7 @@ import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualizationInstance;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
/**
@@ -45,9 +45,9 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @author Erich Schubert
*
* @apiviz.stereotype factory
- * @apiviz.uses StaticVisualization oneway - - «create»
+ * @apiviz.uses StaticVisualizationInstance oneway - - «create»
*/
-public class LabelVisFactory extends AbstractVisFactory {
+public class LabelVisualization extends AbstractVisFactory {
/**
* The label to render
*/
@@ -61,7 +61,7 @@ public class LabelVisFactory extends AbstractVisFactory {
/**
* Constructor. Solely for API purposes (Parameterizable!)
*/
- public LabelVisFactory() {
+ public LabelVisualization() {
super();
}
@@ -70,7 +70,7 @@ public class LabelVisFactory extends AbstractVisFactory {
*
* @param label Label to use
*/
- public LabelVisFactory(String label) {
+ public LabelVisualization(String label) {
this(label, false);
}
@@ -80,7 +80,7 @@ public class LabelVisFactory extends AbstractVisFactory {
* @param label Label to use
* @param rotated Rotated 90 deg to the left
*/
- public LabelVisFactory(String label, boolean rotated) {
+ public LabelVisualization(String label, boolean rotated) {
super();
this.label = label;
this.rotated = rotated;
@@ -96,7 +96,7 @@ public class LabelVisFactory extends AbstractVisFactory {
SVGPlot svgp = task.getPlot();
VisualizerContext context = task.getContext();
CSSClass cls = new CSSClass(svgp, "unmanaged");
- StyleLibrary style = context.getStyleLibrary();
+ StyleLibrary style = context.getStyleResult().getStyleLibrary();
double fontsize = style.getTextSize("overview.labels") / StyleLibrary.SCALE;
cls.setStatement(SVGConstants.CSS_FONT_SIZE_PROPERTY, SVGUtil.fmt(fontsize));
cls.setStatement(SVGConstants.CSS_FILL_PROPERTY, style.getTextColor("overview.labels"));
@@ -104,17 +104,17 @@ public class LabelVisFactory extends AbstractVisFactory {
Element layer;
if(!rotated) {
- layer = svgp.svgText(task.getWidth() / 2, task.getHeight() / 2 + .35 * fontsize, this.label);
+ layer = svgp.svgText(task.getWidth() * .5, task.getHeight() * .5 + .35 * fontsize, this.label);
SVGUtil.setAtt(layer, SVGConstants.SVG_STYLE_ATTRIBUTE, cls.inlineCSS());
SVGUtil.setAtt(layer, SVGConstants.SVG_TEXT_ANCHOR_ATTRIBUTE, SVGConstants.SVG_MIDDLE_VALUE);
}
else {
- layer = svgp.svgText(- task.getHeight() / 2, task.getWidth() / 2 + .35 * fontsize, this.label);
+ layer = svgp.svgText(- task.getHeight() * .5, task.getWidth() * .5 + .35 * fontsize, this.label);
SVGUtil.setAtt(layer, SVGConstants.SVG_STYLE_ATTRIBUTE, cls.inlineCSS());
SVGUtil.setAtt(layer, SVGConstants.SVG_TEXT_ANCHOR_ATTRIBUTE, SVGConstants.SVG_MIDDLE_VALUE);
SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, "rotate(-90)");
}
- return new StaticVisualization(task, layer);
+ return new StaticVisualizationInstance(task, layer);
}
@Override
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/PixmapVisualizer.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/PixmapVisualizer.java
index 9269f404..19cc6254 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/PixmapVisualizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/PixmapVisualizer.java
@@ -45,99 +45,99 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
*
* @author Erich Schubert
*
- * @apiviz.has PixmapResult oneway - 1 visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class PixmapVisualizer extends AbstractVisualization {
+public class PixmapVisualizer extends AbstractVisFactory {
/**
* Name for this visualizer.
*/
private static final String NAME = "Pixmap Visualizer";
/**
- * The actual pixmap result.
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- private PixmapResult result;
+ public PixmapVisualizer() {
+ super();
+ }
- /**
- * Constructor.
- *
- * @param task Visualization task
- */
- public PixmapVisualizer(VisualizationTask task) {
- super(task);
- this.result = task.getResult();
+ @Override
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<PixmapResult> prs = ResultUtil.filterResults(result, PixmapResult.class);
+ for(PixmapResult pr : prs) {
+ // Add plots, attach visualizer
+ final VisualizationTask task = new VisualizationTask(NAME, pr, null, this);
+ task.width = pr.getImage().getWidth() / (double) pr.getImage().getHeight();
+ task.height = 1.0;
+ task.level = VisualizationTask.LEVEL_STATIC;
+ baseResult.getHierarchy().add(pr, task);
+ }
+ }
+
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
}
@Override
- protected void redraw() {
- // TODO: Use width, height, imgratio, number of OPTICS plots!
- double scale = StyleLibrary.SCALE;
-
- final double sizex = scale;
- final double sizey = scale * task.getHeight() / task.getWidth();
- final double margin = 0.0; // context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
- layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
- final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), sizex, sizey, margin);
- SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
-
- RenderedImage img = result.getImage();
- // is ratio, target ratio
- double iratio = img.getHeight() / img.getWidth();
- double tratio = task.getHeight() / task.getWidth();
- // We want to place a (iratio, 1.0) object on a (tratio, 1.0) screen.
- // Both dimensions must fit:
- double zoom = (iratio >= tratio) ? Math.min(tratio / iratio, 1.0) : Math.max(iratio / tratio, 1.0);
-
- Element itag = svgp.svgElement(SVGConstants.SVG_IMAGE_TAG);
- SVGUtil.setAtt(itag, SVGConstants.SVG_IMAGE_RENDERING_ATTRIBUTE, SVGConstants.SVG_OPTIMIZE_SPEED_VALUE);
- SVGUtil.setAtt(itag, SVGConstants.SVG_X_ATTRIBUTE, 0);
- SVGUtil.setAtt(itag, SVGConstants.SVG_Y_ATTRIBUTE, 0);
- SVGUtil.setAtt(itag, SVGConstants.SVG_WIDTH_ATTRIBUTE, scale * zoom * iratio);
- SVGUtil.setAtt(itag, SVGConstants.SVG_HEIGHT_ATTRIBUTE, scale * zoom);
- itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, result.getAsFile().toURI().toString());
-
- layer.appendChild(itag);
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
}
/**
- * Factory class for pixmap visualizers.
+ * Instance.
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses PixmapVisualizer oneway - - «create»
+ * @apiviz.has PixmapResult oneway - 1 visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractVisualization {
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * The actual pixmap result.
*/
- public Factory() {
- super();
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<PixmapResult> prs = ResultUtil.filterResults(result, PixmapResult.class);
- for(PixmapResult pr : prs) {
- // Add plots, attach visualizer
- final VisualizationTask task = new VisualizationTask(NAME, pr, null, this);
- task.width = pr.getImage().getWidth() / pr.getImage().getHeight();
- task.height = 1.0;
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_STATIC);
- baseResult.getHierarchy().add(pr, task);
- }
- }
+ private PixmapResult result;
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new PixmapVisualizer(task);
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.result = task.getResult();
}
@Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
+ protected void redraw() {
+ // TODO: Use width, height, imgratio, number of OPTICS plots!
+ double scale = StyleLibrary.SCALE;
+
+ final double sizex = scale;
+ final double sizey = scale * task.getHeight() / task.getWidth();
+ final double margin = 0.0; // context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
+ layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
+ final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), sizex, sizey, margin);
+ SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
+
+ RenderedImage img = result.getImage();
+ // is ratio, target ratio
+ double iratio = img.getHeight() / img.getWidth();
+ double tratio = task.getHeight() / task.getWidth();
+ // We want to place a (iratio, 1.0) object on a (tratio, 1.0) screen.
+ // Both dimensions must fit:
+ double zoom = (iratio >= tratio) ? Math.min(tratio / iratio, 1.0) : Math.max(iratio / tratio, 1.0);
+
+ Element itag = svgp.svgElement(SVGConstants.SVG_IMAGE_TAG);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_IMAGE_RENDERING_ATTRIBUTE, SVGConstants.SVG_OPTIMIZE_SPEED_VALUE);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_X_ATTRIBUTE, 0);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_Y_ATTRIBUTE, 0);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_WIDTH_ATTRIBUTE, scale * zoom * iratio);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_HEIGHT_ATTRIBUTE, scale * zoom);
+ itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, result.getAsFile().toURI().toString());
+
+ layer.appendChild(itag);
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SettingsVisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SettingsVisualization.java
index 2d726257..736f28e7 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SettingsVisFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SettingsVisualization.java
@@ -41,7 +41,7 @@ import de.lmu.ifi.dbs.elki.visualization.style.StyleLibrary;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualizationInstance;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
/**
@@ -50,11 +50,11 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @author Erich Schubert
*
* @apiviz.stereotype factory
- * @apiviz.uses StaticVisualization oneway - - «create»
+ * @apiviz.uses StaticVisualizationInstance oneway - - «create»
* @apiviz.has SettingsResult oneway - - visualizes
*/
// TODO: make this a menu item instead of a "visualization"?
-public class SettingsVisFactory extends AbstractVisFactory {
+public class SettingsVisualization extends AbstractVisFactory {
/**
* Name for this visualizer.
*/
@@ -64,7 +64,7 @@ public class SettingsVisFactory extends AbstractVisFactory {
* Constructor, adhering to
* {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- public SettingsVisFactory() {
+ public SettingsVisualization() {
super();
}
@@ -74,7 +74,7 @@ public class SettingsVisFactory extends AbstractVisFactory {
VisualizerContext context = task.getContext();
SVGPlot svgp = task.getPlot();
- Collection<Pair<Object, Parameter<?, ?>>> settings = sr.getSettings();
+ Collection<Pair<Object, Parameter<?>>> settings = sr.getSettings();
Element layer = svgp.svgElement(SVGConstants.SVG_G_TAG);
@@ -82,7 +82,7 @@ public class SettingsVisFactory extends AbstractVisFactory {
int i = 0;
Object last = null;
- for(Pair<Object, Parameter<?, ?>> setting : settings) {
+ for(Pair<Object, Parameter<?>> setting : settings) {
if(setting.first != last && setting.first != null) {
String name;
try {
@@ -128,11 +128,11 @@ public class SettingsVisFactory extends AbstractVisFactory {
int cols = Math.max(30, (int) (i * task.getHeight() / task.getWidth()));
int rows = i;
- final double margin = context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
+ final double margin = context.getStyleResult().getStyleLibrary().getSize(StyleLibrary.MARGIN);
final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), cols, rows, margin / StyleLibrary.SCALE);
SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
- return new StaticVisualization(task, layer);
+ return new StaticVisualizationInstance(task, layer);
}
@Override
@@ -142,7 +142,7 @@ public class SettingsVisFactory extends AbstractVisFactory {
final VisualizationTask task = new VisualizationTask(NAME, sr, null, this);
task.width = 1.0;
task.height = 1.0;
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_STATIC);
+ task.level = VisualizationTask.LEVEL_STATIC;
baseResult.getHierarchy().add(sr, task);
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SimilarityMatrixVisualizer.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SimilarityMatrixVisualizer.java
index c53a722a..f1404050 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SimilarityMatrixVisualizer.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/SimilarityMatrixVisualizer.java
@@ -29,7 +29,7 @@ import java.util.Collection;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.Element;
-import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
import de.lmu.ifi.dbs.elki.database.relation.Relation;
import de.lmu.ifi.dbs.elki.evaluation.similaritymatrix.ComputeSimilarityMatrixImage;
import de.lmu.ifi.dbs.elki.evaluation.similaritymatrix.ComputeSimilarityMatrixImage.SimilarityMatrix;
@@ -49,123 +49,121 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
*
* @author Erich Schubert
*
- * @apiviz.has SimilarityMatrix oneway - 1 visualizes
+ * @apiviz.stereotype factory
+ * @apiviz.uses Instance oneway - - «create»
*/
-public class SimilarityMatrixVisualizer extends AbstractVisualization {
+public class SimilarityMatrixVisualizer extends AbstractVisFactory {
/**
* Name for this visualizer.
*/
private static final String NAME = "Similarity Matrix Visualizer";
/**
- * The actual pixmap result.
+ * Constructor, adhering to
+ * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
*/
- private SimilarityMatrix result;
-
- /**
- * Constructor.
- *
- * @param task Visualization task
- */
- public SimilarityMatrixVisualizer(VisualizationTask task) {
- super(task);
- this.result = task.getResult();
+ public SimilarityMatrixVisualizer() {
+ super();
}
@Override
- protected void redraw() {
- // TODO: Use width, height, imgratio, number of OPTICS plots!
- double scale = StyleLibrary.SCALE;
-
- final double sizex = scale;
- final double sizey = scale * task.getHeight() / task.getWidth();
- final double margin = context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
- layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
- final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), sizex, sizey, margin);
- SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
-
- RenderedImage img = result.getImage();
- // is ratio, target ratio
- double iratio = img.getHeight() / img.getWidth();
- double tratio = task.getHeight() / task.getWidth();
- // We want to place a (iratio, 1.0) object on a (tratio, 1.0) screen.
- // Both dimensions must fit:
- double zoom = (iratio >= tratio) ? Math.min(tratio / iratio, 1.0) : Math.max(iratio / tratio, 1.0);
-
- Element itag = svgp.svgElement(SVGConstants.SVG_IMAGE_TAG);
- SVGUtil.setAtt(itag, SVGConstants.SVG_IMAGE_RENDERING_ATTRIBUTE, SVGConstants.SVG_OPTIMIZE_SPEED_VALUE);
- SVGUtil.setAtt(itag, SVGConstants.SVG_X_ATTRIBUTE, margin * 0.75);
- SVGUtil.setAtt(itag, SVGConstants.SVG_Y_ATTRIBUTE, margin * 0.75);
- SVGUtil.setAtt(itag, SVGConstants.SVG_WIDTH_ATTRIBUTE, scale * zoom * iratio);
- SVGUtil.setAtt(itag, SVGConstants.SVG_HEIGHT_ATTRIBUTE, scale * zoom);
- itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, result.getAsFile().toURI().toString());
- layer.appendChild(itag);
-
- // Add object labels
- final int size = result.getIDs().size();
- final double hlsize = scale * zoom * iratio / size;
- final double vlsize = scale * zoom / size;
- int i = 0;
- final Relation<String> lrep = DatabaseUtil.guessObjectLabelRepresentation(result.getRelation().getDatabase());
- for(DBID id : result.getIDs()) {
- String label = lrep.get(id);
- if(label != null) {
- // Label on horizontal axis
- final double hlx = margin * 0.75 + hlsize * (i + .8);
- final double hly = margin * 0.7;
- Element lbl = svgp.svgText(hlx, hly, label);
- SVGUtil.setAtt(lbl, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, "rotate(-90," + hlx + "," + hly + ")");
- SVGUtil.setAtt(lbl, SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: " + hlsize * 0.8);
- layer.appendChild(lbl);
- // Label on vertical axis
- Element lbl2 = svgp.svgText(margin * 0.7, margin * 0.75 + vlsize * (i + .8), label);
- SVGUtil.setAtt(lbl2, SVGConstants.SVG_TEXT_ANCHOR_ATTRIBUTE, SVGConstants.SVG_END_VALUE);
- SVGUtil.setAtt(lbl2, SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: " + vlsize * 0.8);
- layer.appendChild(lbl2);
- }
- i++;
+ public void processNewResult(HierarchicalResult baseResult, Result result) {
+ Collection<ComputeSimilarityMatrixImage.SimilarityMatrix> prs = ResultUtil.filterResults(result, ComputeSimilarityMatrixImage.SimilarityMatrix.class);
+ for(ComputeSimilarityMatrixImage.SimilarityMatrix pr : prs) {
+ // Add plots, attach visualizer
+ final VisualizationTask task = new VisualizationTask(NAME, pr, null, this);
+ task.width = 1.0;
+ task.height = 1.0;
+ task.level = VisualizationTask.LEVEL_STATIC;
+ baseResult.getHierarchy().add(pr, task);
}
}
+ @Override
+ public Visualization makeVisualization(VisualizationTask task) {
+ return new Instance(task);
+ }
+
+ @Override
+ public boolean allowThumbnails(VisualizationTask task) {
+ // Don't use thumbnails
+ return false;
+ }
+
/**
- * Factory class for pixmap visualizers.
+ * Instance
*
* @author Erich Schubert
*
- * @apiviz.stereotype factory
- * @apiviz.uses PixmapVisualizer oneway - - «create»
+ * @apiviz.has SimilarityMatrix oneway - 1 visualizes
*/
- public static class Factory extends AbstractVisFactory {
+ public class Instance extends AbstractVisualization {
/**
- * Constructor, adhering to
- * {@link de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable}
+ * The actual pixmap result.
*/
- public Factory() {
- super();
- }
-
- @Override
- public void processNewResult(HierarchicalResult baseResult, Result result) {
- Collection<ComputeSimilarityMatrixImage.SimilarityMatrix> prs = ResultUtil.filterResults(result, ComputeSimilarityMatrixImage.SimilarityMatrix.class);
- for(ComputeSimilarityMatrixImage.SimilarityMatrix pr : prs) {
- // Add plots, attach visualizer
- final VisualizationTask task = new VisualizationTask(NAME, pr, null, this);
- task.width = 1.0;
- task.height = 1.0;
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_STATIC);
- baseResult.getHierarchy().add(pr, task);
- }
- }
+ private SimilarityMatrix result;
- @Override
- public Visualization makeVisualization(VisualizationTask task) {
- return new SimilarityMatrixVisualizer(task);
+ /**
+ * Constructor.
+ *
+ * @param task Visualization task
+ */
+ public Instance(VisualizationTask task) {
+ super(task);
+ this.result = task.getResult();
}
@Override
- public boolean allowThumbnails(VisualizationTask task) {
- // Don't use thumbnails
- return false;
+ protected void redraw() {
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final double sizex = StyleLibrary.SCALE;
+ final double sizey = StyleLibrary.SCALE * task.getHeight() / task.getWidth();
+ final double margin = style.getSize(StyleLibrary.MARGIN);
+ layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
+ final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), sizex, sizey, margin);
+ SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
+
+ RenderedImage img = result.getImage();
+ // is ratio, target ratio
+ double iratio = img.getHeight() / img.getWidth();
+ double tratio = task.getHeight() / task.getWidth();
+ // We want to place a (iratio, 1.0) object on a (tratio, 1.0) screen.
+ // Both dimensions must fit:
+ double zoom = (iratio >= tratio) ? Math.min(tratio / iratio, 1.0) : Math.max(iratio / tratio, 1.0);
+
+ Element itag = svgp.svgElement(SVGConstants.SVG_IMAGE_TAG);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_IMAGE_RENDERING_ATTRIBUTE, SVGConstants.SVG_OPTIMIZE_SPEED_VALUE);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_X_ATTRIBUTE, margin * 0.75);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_Y_ATTRIBUTE, margin * 0.75);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_WIDTH_ATTRIBUTE, StyleLibrary.SCALE * zoom * iratio);
+ SVGUtil.setAtt(itag, SVGConstants.SVG_HEIGHT_ATTRIBUTE, StyleLibrary.SCALE * zoom);
+ itag.setAttributeNS(SVGConstants.XLINK_NAMESPACE_URI, SVGConstants.XLINK_HREF_QNAME, result.getAsFile().toURI().toString());
+ layer.appendChild(itag);
+
+ // Add object labels
+ final int size = result.getIDs().size();
+ final double hlsize = StyleLibrary.SCALE * zoom * iratio / size;
+ final double vlsize = StyleLibrary.SCALE * zoom / size;
+ int i = 0;
+ final Relation<String> lrep = DatabaseUtil.guessObjectLabelRepresentation(result.getRelation().getDatabase());
+ for(DBIDIter id = result.getIDs().iter(); id.valid(); id.advance()) {
+ String label = lrep.get(id);
+ if(label != null) {
+ // Label on horizontal axis
+ final double hlx = margin * 0.75 + hlsize * (i + .8);
+ final double hly = margin * 0.7;
+ Element lbl = svgp.svgText(hlx, hly, label);
+ SVGUtil.setAtt(lbl, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, "rotate(-90," + hlx + "," + hly + ")");
+ SVGUtil.setAtt(lbl, SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: " + hlsize * 0.8);
+ layer.appendChild(lbl);
+ // Label on vertical axis
+ Element lbl2 = svgp.svgText(margin * 0.7, margin * 0.75 + vlsize * (i + .8), label);
+ SVGUtil.setAtt(lbl2, SVGConstants.SVG_TEXT_ANCHOR_ATTRIBUTE, SVGConstants.SVG_END_VALUE);
+ SVGUtil.setAtt(lbl2, SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size: " + vlsize * 0.8);
+ layer.appendChild(lbl2);
+ }
+ i++;
+ }
}
}
} \ No newline at end of file
diff --git a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/XYCurveVisFactory.java b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/XYCurveVisualization.java
index 507de376..76ce7c09 100644
--- a/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/XYCurveVisFactory.java
+++ b/src/de/lmu/ifi/dbs/elki/visualization/visualizers/visunproj/XYCurveVisualization.java
@@ -48,7 +48,7 @@ import de.lmu.ifi.dbs.elki.visualization.svg.SVGPlot;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGSimpleLinearAxis;
import de.lmu.ifi.dbs.elki.visualization.svg.SVGUtil;
import de.lmu.ifi.dbs.elki.visualization.visualizers.AbstractVisFactory;
-import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualization;
+import de.lmu.ifi.dbs.elki.visualization.visualizers.StaticVisualizationInstance;
import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
/**
@@ -57,10 +57,10 @@ import de.lmu.ifi.dbs.elki.visualization.visualizers.Visualization;
* @author Erich Schubert
*
* @apiviz.stereotype factory
- * @apiviz.uses StaticVisualization oneway - - «create»
+ * @apiviz.uses StaticVisualizationInstance oneway - - «create»
* @apiviz.has XYCurve oneway - - visualizes
*/
-public class XYCurveVisFactory extends AbstractVisFactory {
+public class XYCurveVisualization extends AbstractVisFactory {
/**
* Name for this visualizer.
*/
@@ -79,7 +79,7 @@ public class XYCurveVisFactory extends AbstractVisFactory {
/**
* Constructor, Parameterizable style - does nothing.
*/
- public XYCurveVisFactory() {
+ public XYCurveVisualization() {
super();
}
@@ -90,10 +90,10 @@ public class XYCurveVisFactory extends AbstractVisFactory {
XYCurve curve = task.getResult();
setupCSS(context, svgp);
- double scale = StyleLibrary.SCALE;
- final double sizex = scale;
- final double sizey = scale * task.getHeight() / task.getWidth();
- final double margin = context.getStyleLibrary().getSize(StyleLibrary.MARGIN);
+ final StyleLibrary style = context.getStyleResult().getStyleLibrary();
+ final double sizex = StyleLibrary.SCALE;
+ final double sizey = StyleLibrary.SCALE * task.getHeight() / task.getWidth();
+ final double margin = style.getSize(StyleLibrary.MARGIN);
Element layer = SVGUtil.svgElement(svgp.getDocument(), SVGConstants.SVG_G_TAG);
final String transform = SVGUtil.makeMarginTransform(task.getWidth(), task.getHeight(), sizex, sizey, margin);
SVGUtil.setAtt(layer, SVGConstants.SVG_TRANSFORM_ATTRIBUTE, transform);
@@ -113,15 +113,15 @@ public class XYCurveVisFactory extends AbstractVisFactory {
// add axes
try {
- SVGSimpleLinearAxis.drawAxis(svgp, layer, scaley, 0, sizey, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, context.getStyleLibrary());
- SVGSimpleLinearAxis.drawAxis(svgp, layer, scalex, 0, sizey, sizex, sizey, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, context.getStyleLibrary());
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, scaley, 0, sizey, 0, 0, SVGSimpleLinearAxis.LabelStyle.LEFTHAND, style);
+ SVGSimpleLinearAxis.drawAxis(svgp, layer, scalex, 0, sizey, sizex, sizey, SVGSimpleLinearAxis.LabelStyle.RIGHTHAND, style);
}
catch(CSSNamingConflict e) {
LoggingUtil.exception(e);
}
// Add axis labels
{
- Element labelx = svgp.svgText(sizex / 2, sizey + margin * .9, curve.getLabelx());
+ Element labelx = svgp.svgText(sizex * .5, sizey + margin * .9, curve.getLabelx());
SVGUtil.setCSSClass(labelx, CSS_AXIS_LABEL);
layer.appendChild(labelx);
Element labely = svgp.svgText(margin * -.8, sizey * .5, curve.getLabely());
@@ -161,7 +161,7 @@ public class XYCurveVisFactory extends AbstractVisFactory {
}
layer.appendChild(line);
- return new StaticVisualization(task, layer);
+ return new StaticVisualizationInstance(task, layer);
}
/**
@@ -170,7 +170,7 @@ public class XYCurveVisFactory extends AbstractVisFactory {
* @param svgp Plot
*/
private void setupCSS(VisualizerContext context, SVGPlot svgp) {
- StyleLibrary style = context.getStyleLibrary();
+ StyleLibrary style = context.getStyleResult().getStyleLibrary();
CSSClass csscls = new CSSClass(this, SERIESID);
// csscls.setStatement(SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE, "0.2%");
csscls.setStatement(SVGConstants.SVG_FILL_ATTRIBUTE, SVGConstants.SVG_NONE_VALUE);
@@ -193,7 +193,7 @@ public class XYCurveVisFactory extends AbstractVisFactory {
final VisualizationTask task = new VisualizationTask(NAME, curve, null, this);
task.width = 1.0;
task.height = 1.0;
- task.put(VisualizationTask.META_LEVEL, VisualizationTask.LEVEL_STATIC);
+ task.level = VisualizationTask.LEVEL_STATIC;
baseResult.getHierarchy().add(curve, task);
}
}