/*******************************************************************************
* Copyright (c) 2012-2015 INRIA.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Damien Dosimont <damien.dosimont@imag.fr>
* Youenn Corre <youenn.corret@inria.fr>
******************************************************************************/
package fr.inria.soctrace.tools.ocelotl.visualizations.spatiotemporal.views;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.PolylineConnection;
import org.eclipse.draw2d.RectangleFigure;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.swt.SWT;
import fr.inria.soctrace.tools.ocelotl.core.constants.OcelotlConstants;
import fr.inria.soctrace.tools.ocelotl.core.dataaggregmanager.spacetime.EventProducerHierarchy.Aggregation;
import fr.inria.soctrace.tools.ocelotl.core.dataaggregmanager.spacetime.EventProducerHierarchy.EventProducerNode;
import fr.inria.soctrace.tools.ocelotl.core.ivisuop.Part;
import fr.inria.soctrace.tools.ocelotl.ui.views.OcelotlView;
import fr.inria.soctrace.tools.ocelotl.ui.views.timelineview.MatrixView;
import fr.inria.soctrace.tools.ocelotl.ui.views.timelineview.SpatioTemporalAggregateView;
import fr.inria.soctrace.tools.ocelotl.visualizations.spatiotemporal.partition.VisualAggregation;
public abstract class SpatioTemporalView extends MatrixView {
public SpatioTemporalView(OcelotlView ocelotlView) {
super(ocelotlView);
}
abstract public class DrawSpatioTemporal {
protected int spaceClean = 8;
protected int spaceDirty = 10;
protected int spaceDirty2 = 1;
protected int iterationDirty = 3;
protected double rootHeight;
protected double height;
protected double width;
protected double logicWidth;
protected double logicHeight;
protected int minLogicWeight = OcelotlConstants.MinimalHeightDrawingThreshold;
protected List<Integer> xendlist;
protected List<Integer> yendlist;
public DrawSpatioTemporal() {
xendlist = new ArrayList<Integer>();
yendlist = new ArrayList<Integer>();
}
/**
* Compute the list of X values according to the number of time slices
* (parts)
*/
protected void initX(EventProducerNode epn) {
xendlist.clear();
for (int i = 0; i <= epn.getParts().size(); i++)
xendlist.add((int) (i * logicWidth + aBorder - space));
}
/**
* Compute the list of Y values according to the weight of the root node
*/
protected void initY(EventProducerNode epn) {
yendlist.clear();
for (int i = 0; i <= epn.getWeight(); i++)
yendlist.add((int) (rootHeight - height + i * logicHeight
- space - aBorder));
}
public void draw() {
rootHeight = root.getSize().height;
height = rootHeight - (2 * aBorder);
width = root.getSize().width - (2 * aBorder);
logicWidth = width / hierarchy.getRoot().getParts().size();
logicHeight = height / hierarchy.getRoot().getWeight();
initX(hierarchy.getRoot());
initY(hierarchy.getRoot());
print(hierarchy.getRoot(), 0, hierarchy.getRoot()
.getParts().size());
}
/**
* Print the matrix of data for a given eventproducerNode and for a
* given time range
*
* @param id
* id of the eventProducerNode
* @param start
* starting time slice
* @param end
* ending time slice
*/
protected void print(EventProducerNode epn, int start, int end) {
// Compute the parts for the current epn
List<Part> parts = computeParts(epn, start, end);
for (Part p : parts) {
// If p is an aggregation
if (((VisualAggregation) p.getData()).isAggregated())
drawAggregate(p.getStartPart(), epn.getIndex(),
p.getEndPart(), epn.getWeight(),
((VisualAggregation) p.getData()).getValue(), epn, false, false);
else {
// Check for each child that we have enough vertical space
// to display them
boolean aggy = false;
if (ocelotlView.getOcelotlParameters().getOcelotlSettings()
.isUseVisualAggregate()) {
for (EventProducerNode ep : epn.getChildrenNodes()) {
// if the space needed to print an element is
// smaller than 1 pixel
if ((ep.getWeight() * logicHeight - space) < minLogicWeight) {
// Aggregate
aggy = true;
break;
}
}
}
// If enough space
if (aggy == false)
// recursively call print() on the children node
printChildren(epn, p.getStartPart(), p.getEndPart());
else {
List<Part> aggParts = computeCommonCuts(epn,
p.getStartPart(), p.getEndPart());
for (Part pagg : aggParts) {
// Does the aggregated data contain some temporal
// cut
boolean hasNoCut = ((VisualAggregation) pagg
.getData()).isNoCutInside();
drawAggregate(pagg.getStartPart(),
epn.getIndex(), pagg.getEndPart(),
epn.getWeight(), ((VisualAggregation) p.getData()).getValue(), epn, true, hasNoCut);
}
}
}
}
}
/**
* Set the drawn rectangle's characteristics and its label
*
* @param epn
* the event producer
* @param startTimeSlice
* use to get the mainstate in mode view
* @param endTimeSlice
* use to get the mainstate in mode view
* @param isVisualAggregate
* use to set the color in partition view
* @param number
* use to set the color in partition view
* @return the created rectanglefigure
*/
abstract protected RectangleFigure setRectangle(EventProducerNode epn,
int startTimeSlice, int endTimeSlice,
boolean isVisualAggregate, int number);
/**
* Draw the aggregate
*
* @param logicX
* starting time slice of the aggregate
* @param logicY
* starting height (event producer) of the aggregate
* @param logicX2
* end time slice of the aggregate
* @param sizeY
* ending height (event producer) of the aggregate
* @param number
* value of the aggregate (use to set the color in partition
* view)
* @param epn
* the event producer node of the aggregate
* @param isVisualAggregate
* is the aggregate a visual aggregate (i.e. an aggregation
* when the resolution is too small to print all the cuts).
* @param clean
* is the visual aggregate clean (i.e. does is contains
* temporal cut)
*/
protected void drawAggregate(int logicX, int logicY, int logicX2,
int sizeY, int number, EventProducerNode epn,
boolean isVisualAggregate, boolean clean) {
// Set the rectangle characteristics
final RectangleFigure rectangle = setRectangle(epn, logicX,
logicX2, isVisualAggregate, number);
// Void state
if(rectangle == null)
return;
String label = ((Label) rectangle.getToolTip()).getText();
// Draw the rectangle
int xa = (int) ((logicX * logicWidth + aBorder));
int ya = (int) (rootHeight - height + logicY * logicHeight - aBorder);
int xb = xendlist.get(logicX2);
int yb = yendlist.get(logicY + sizeY);
root.add(rectangle, new Rectangle(new Point(xa, ya), new Point(xb,
yb)));
// Save it in the index if necessary
saveAggregate(xa, xb, ya, yb, epn, logicX, logicX2, label,
isVisualAggregate);
if (isVisualAggregate) {
if (!clean) {
drawTextureDirty(xa, xb, ya, yb, label);
} else {
drawTextureClean(xa, xb, ya, yb, label);
}
}
}
/**
* Compute the temporal parts for a given EventProducerNode
*
* @param epn
* the EventProducerNode
* @param start
* the starting time slice
* @param end
* the ending time slice
* @return a list of temporal TemporalPartition
*/
protected List<Part> computeParts(EventProducerNode epn, int start,
int end) {
List<Part> parts = new ArrayList<Part>();
int oldPart = epn.getParts().get(start);
// Init part
parts.add(new Part(start, start + 1, new VisualAggregation(false,
epn.getParts().get(start) != -1, epn.getParts().get(start),
true)));
for (int i = start + 1; i < end; i++) {
// If we are still in the same part, increase its size
if (epn.getParts().get(i) == oldPart) {
parts.get(parts.size() - 1).incrSize();
} else {
// Create a new part
oldPart = epn.getParts().get(i);
parts.add(new Part(i, i + 1, null));
parts.get(parts.size() - 1).setData(
new VisualAggregation(false,
epn.getParts().get(i) != -1, epn.getParts()
.get(i), true));
}
}
return parts;
}
/**
* Compute the common cut between the event producer node and its
* children
*
* @param epn
* the event producer node
* @param start
* the starting time slice
* @param end
* the ending time slice
* @return a list of parts
*/
protected List<Part> computeCommonCuts(EventProducerNode epn,
int start, int end) {
HashMap<EventProducerNode, List<Part>> hm = new HashMap<EventProducerNode, List<Part>>();
// Contains the parts which are common to all the children
List<Part> commonParts = new ArrayList<Part>();
// All parts (results)
List<Part> parts = new ArrayList<Part>();
// For each child
for (EventProducerNode child : epn.getChildrenNodes()) {
if (child.isAggregated() == Aggregation.FULL)
// Get parts
hm.put(child, computeParts(child, start, end));
else
// Get common cuts
hm.put(child, computeCommonCuts(child, start, end));
}
List<Part> testPart = hm.get(epn.getChildrenNodes().get(0));
for (Part p : testPart) {
boolean commonCut = false;
boolean cleanCut = true;
for (EventProducerNode child : epn.getChildrenNodes()) {
commonCut = false;
for (Part p2 : hm.get(child)) {
// If p and p2 has the same starting and ending dates
if (p.compare(p2)) {
commonCut = true;
if (((VisualAggregation) p2.getData())
.isVisualAggregate()
&& !((VisualAggregation) p2.getData())
.isNoCutInside())
cleanCut = false;
// Get to the next child
break;
}
}
// There was at least one child with no common cut
if (commonCut == false)
break;
}
// If the part is common to all the children
if (commonCut) {
// Add it to common parts
commonParts.add(new Part(p.getStartPart(), p.getEndPart(),
new VisualAggregation(true, false, -1, cleanCut)));
}
}
// If no common cut were found
if (commonParts.isEmpty()) {
// Just add one big part
parts.add(new Part(start, end, new VisualAggregation(true,
false, -1, false)));
return parts;
}
// If the common parts do not start at the starting slice
if (commonParts.get(0).getStartPart() != start)
parts.add(new Part(start, commonParts.get(0).getStartPart(),
new VisualAggregation(true, false, -1, false)));
// For each common part
for (Part ptemp : commonParts) {
// If parts is not empty and the last part does not end with
// the beginning of the current common part
if (parts.size() > 0
&& parts.get(parts.size() - 1).getEndPart() != ptemp
.getStartPart())
// Add a new part in the gap between the last part and the
// current common part
parts.add(new Part(
parts.get(parts.size() - 1).getEndPart(), ptemp
.getStartPart(), new VisualAggregation(
true, false, -1, false)));
// Add the common part
parts.add(ptemp);
}
// if the last part does not go until the end time slice
if (parts.get(parts.size() - 1).getEndPart() != end)
// Add a part to fill the gap
parts.add(new Part(parts.get(parts.size() - 1).getEndPart(),
end, new VisualAggregation(true, false, -1, false)));
return parts;
}
/**
* Recursively print all the children of the event producer with the
* given id
*
* @param id
* id of the event producer
* @param start
* starting slice
* @param end
* ending slice
*/
protected void printChildren(EventProducerNode epn, int start, int end) {
for (EventProducerNode ep : epn.getChildrenNodes())
print(ep, start, end);
}
/**
* Draw the texture for visual aggregate (add left to right lines) that
* do not contains temporal cut (i.e. clean)
*
* @param xa
* @param xb
* @param ya
* @param yb
* @param label
*/
protected void drawTextureClean(int xa, int xb, int ya, int yb,
String label) {
for (int x = xa + spaceClean - yb + ya; x < (xb); x = x
+ spaceClean + 1) {
final PolylineConnection line = new PolylineConnection();
int xinit = x;
int yinit = ya;
int xfinal = Math.min(xb, (xinit + (yb - ya)));
int yfinal = Math.min(yb, ya + xfinal - xinit);
if (xa > xinit) {
yinit = Math.min(yb, ya - xinit + xa);
xinit = xa;
}
line.setBackgroundColor(ColorConstants.white);
line.setForegroundColor(ColorConstants.white);
line.setEndpoints(new Point(xinit, yinit), new Point(xfinal,
yfinal));
line.setLineWidth(1);
line.setAntialias(SWT.ON);
line.setToolTip(new Label(label));
root.add(line);
}
}
/**
* Draw the texture for visual aggregate (add right to left lines) that
* contains temporal cut (i.e. dirty)
*
* @param xa
* @param xb
* @param ya
* @param yb
* @param label
*/
protected void drawTextureDirty(int xa, int xb, int ya, int yb,
String label) {
int i = 0;
for (int x = xa + spaceDirty2; x < (xb + yb - ya); x = x
+ spaceDirty2 + 1) {
i++;
if (i > iterationDirty - 1) {
i = 0;
x += spaceDirty;
}
if (x >= (xb + yb - ya)){
break;
}
final PolylineConnection line = new PolylineConnection();
int xinit = x;
int yinit = ya;
int xfinal = Math.max(xa, (xinit - (yb - ya)));
int yfinal = Math.min(yb, ya + xinit - xfinal);
if (xb < xinit) {
yinit = Math.min(yb, ya + xinit - xb);
xinit = xb;
}
line.setBackgroundColor(ColorConstants.white);
line.setForegroundColor(ColorConstants.white);
line.setEndpoints(new Point(xinit, yinit), new Point(xfinal,
yfinal));
line.setLineWidth(1);
line.setAntialias(SWT.ON);
line.setToolTip(new Label(label));
root.add(line);
}
}
/**
* Save the aggregate with its characteristics in the index
* @param xa
* @param xb
* @param ya
* @param yb
* @param epn
* @param start
* @param end
* @param label
* @param isVisualAggregate
*/
protected void saveAggregate(int xa, int xb, int ya, int yb,
EventProducerNode epn, int start, int end, String label,
boolean isVisualAggregate) {
aggregates.add(new SpatioTemporalAggregateView(new Rectangle(
new Point(xa, ya), new Point(xb, yb)), epn, start, end, xb
- xa, label, isVisualAggregate));
}
}
}