summaryrefslogtreecommitdiff
path: root/src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection
diff options
context:
space:
mode:
Diffstat (limited to 'src/de/lmu/ifi/dbs/elki/visualization/visualizers/scatterplot/selection')
-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
7 files changed, 1154 insertions, 786 deletions
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);
}
}
}