/*
* This file is part of ELKI:
* Environment for Developing KDD-Applications Supported by Index-Structures
*
* Copyright (C) 2017
* 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/>.
*/
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.visualization.visualizers.VisFactory;
/**
* Container class, with ugly casts to reduce generics crazyness.
*
* @author Erich Schubert
* @since 0.4.0
*
* @apiviz.landmark
* @apiviz.has VisualizerContext
* @apiviz.has VisFactory
*/
public class VisualizationTask implements VisualizationItem, Comparable<VisualizationTask> {
/**
* Rendering flags enum.
*
* @author Erich Schubert
*/
public static enum RenderFlag {
/** Do not use thumbnails for easy visualizations. */
NO_THUMBNAIL(1),
/** Do not allow full-screening this task */
NO_DETAIL(2),
/** Do not include when exporting */
NO_EXPORT(4),
/** Do not embed */
NO_EMBED(8);
/**
* Bit.
*/
public final int bit;
/**
* Constructor.
*
* @param bit Bit
*/
private RenderFlag(int bit) {
this.bit = bit;
}
}
/**
* Update flags enum.
*
* TODO: can these be replaced by listeners?
*
* @author Erich Schubert
*/
public static enum UpdateFlag {
/** When data changes */
ON_DATA(1),
/** When selection changes */
ON_SELECTION(2),
/** When style changes */
ON_STYLEPOLICY(4),
/** When sample changes */
ON_SAMPLE(8);
/**
* Bit.
*/
public final int bit;
/**
* Constructor.
*
* @param bit Bit
*/
private UpdateFlag(int bit) {
this.bit = bit;
}
}
/**
* Meta data key: Level for visualizer ordering
*
* 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.
*/
private int level = 0;
/**
* Flag to control visibility.
*/
private boolean visible = true;
/**
* Render capabilities
*/
private int flags;
/**
* The update event mask. See {@link UpdateFlag#ON_DATA},
* {@link UpdateFlag#ON_SELECTION}, {@link UpdateFlag#ON_STYLEPOLICY},
* {@link UpdateFlag#ON_SAMPLE}.
*/
private int updatemask;
/**
* Flag to mark the visualizer as tool.
*/
private boolean tool = false;
/**
* Background layer
*/
public static final int LEVEL_BACKGROUND = 0;
/**
* Data layer
*/
public static final int LEVEL_DATA = 100;
/**
* Static plot layer
*/
public static final int LEVEL_STATIC = 200;
/**
* Passive foreground layer
*/
public static final int LEVEL_FOREGROUND = 300;
/**
* Active foreground layer (interactive elements)
*/
public static final int LEVEL_INTERACTIVE = 1000;
/**
* Name
*/
private String name;
/**
* The factory
*/
private VisFactory factory;
/**
* The result we are attached to
*/
private Object result;
/**
* The main representation
*/
private Relation<?> relation;
/**
* Width request
*/
private double reqwidth = 1.0;
/**
* Height request
*/
private double reqheight = 1.0;
/**
* Visualization task.
*
* @param factory Factory
* @param name Name
* @param result Result
* @param relation Relation to use
*/
public VisualizationTask(VisFactory factory, String name, Object result, Relation<?> relation) {
super();
this.name = name;
this.result = result;
this.relation = relation;
this.factory = factory;
}
/**
* Set (OR) the update flags.
*
* @param bits Bits to set
* @return {@code this}, for method chaining.
*/
public VisualizationTask with(UpdateFlag f) {
updatemask |= f.bit;
return this;
}
/**
* Update if any oft these bits is set.
*
* @param bits Bits to check.
* @return {@code true} if any bit is set.
*/
public boolean has(UpdateFlag f) {
return (updatemask & f.bit) != 0;
}
/**
* Set a task flag.
*
* @param bits Flag to set
* @return {@code this}, for method chaining.
*/
public VisualizationTask with(RenderFlag f) {
flags |= f.bit;
return this;
}
/**
* Update if any oft these flags is set.
*
* @param bits Bits to check.
* @return {@code true} if any bit is set.
*/
public boolean has(RenderFlag f) {
return (flags & f.bit) != 0;
}
/**
* Set the level (priority) of a visualization.
*
* @param level Level
* @return {@code this}, for method chaining.
*/
public VisualizationTask level(int level) {
this.level = level;
return this;
}
/**
* Get the level (priority) of the visualization.
*
* @return Level
*/
public int level() {
return level;
}
/**
* Flag as tool visualizer.
*
* TODO: don't use a separate boolean for this, but e.g. interface?
*
* @param t Tool flag
* @return {@code this}, for method chaining.
*/
public VisualizationTask tool(boolean t) {
this.tool = t;
return this;
}
/**
* Get the "tool" flag of the visualizer.
*
* @return tool flag.
*/
public boolean isTool() {
return tool;
}
/**
* Init the default visibility of a task.
*
* @param vis Visibility.
* @return {@code this}, for method chaining.
*/
public VisualizationTask visibility(boolean vis) {
visible = vis;
return this;
}
/**
* Get the visibility flag.
*
* @return Visibility
*/
public boolean isVisible() {
return visible;
}
/**
* Set the size request.
*
* @param w Width
* @param h Height
* @return {@code this}, for method chaining.
*/
public VisualizationTask requestSize(double w, double h) {
this.reqwidth = w;
this.reqheight = h;
return this;
}
/**
* Get the requested width.
*
* @return Width
*/
public double getRequestedWidth() {
return reqwidth;
}
/**
* Get the requested height.
*
* @return Height
*/
public double getRequestedHeight() {
return reqheight;
}
/**
* Get the visualizer factory.
*
* @return Visualizer factory
*/
public VisFactory getFactory() {
return factory;
}
@SuppressWarnings("unchecked")
public <R> R getResult() {
return (R) result;
}
@SuppressWarnings("unchecked")
public <R extends Relation<?>> R getRelation() {
return (R) relation;
}
@Override
public String getMenuName() {
return name;
}
@Override
public int compareTo(VisualizationTask other) {
// sort by levels first
if(this.level != other.level) {
return this.level - other.level;
}
// sort by name otherwise.
String name1 = this.getMenuName();
String name2 = other.getMenuName();
if(name1 != null && name2 != null && name1 != name2) {
return name1.compareTo(name2);
}
return 0;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("VisTask: ").append(factory.getClass().getName()).append(' ');
if(result != null && result instanceof Result) {
buf.append("Result: ").append(((Result) result).getLongName()).append(' ');
}
buf.append(super.toString());
return buf.toString();
}
@Override
public int hashCode() {
// We can't have our hashcode change with the map contents!
return System.identityHashCode(this);
}
@Override
public boolean equals(Object o) {
// Also don't inherit equals based on list contents!
return (this == o);
}
}