Skip to content

Commit

Permalink
- much better behaviour in memory consumption
Browse files Browse the repository at this point in the history
- fix for PaletteData using negative color shifts
- more flexibility in defining the sizes of the used grid and image
size
  • Loading branch information
Stephan Schwiebert committed Feb 5, 2012
1 parent de22871 commit eabe119
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.zest.cloudio.layout.DefaultLayouter;
import org.eclipse.zest.cloudio.layout.ILayouter;
import org.eclipse.zest.cloudio.util.CloudMatrix;
import org.eclipse.zest.cloudio.util.RectTree;
import org.eclipse.zest.cloudio.util.SmallRect;

Expand All @@ -67,18 +68,18 @@ public class TagCloud extends Canvas {
* Minimum 'resolution' of the {@link RectTree} used for
* collision handling.
*/
private static final int RESOLUTION = 5;
private final int accuracy;

/**
* Maximum size of the {@link RectTree} used for collision
* handling.
*/
private static final int TREE_SIZE = 5120/* * 2 */;
private final int maxSize;

/**
* Draw area.
*/
private final Rectangle cloudArea = new Rectangle(0, 0, TREE_SIZE, TREE_SIZE);
private final Rectangle cloudArea;

/**
* Maximum Font Size.
Expand Down Expand Up @@ -151,7 +152,7 @@ public class TagCloud extends Canvas {
*/
private Set<Word> selection = new HashSet<Word>();

private short[][] cloudMatrix;
private CloudMatrix cloudMatrix;

/**
* Executor service to process the creation of {@link RectTree} objects
Expand Down Expand Up @@ -208,16 +209,40 @@ public class TagCloud extends Canvas {
private Set<SelectionListener> selectionListeners = new HashSet<SelectionListener>();

/**
* Creates a new Tag cloud on the given parent. To add scroll bars
* Creates a new Tag cloud on the given parent. When using this constructor, please
* read the following carefully:
* <br>
* Parameter <code>accuracy</code> defines the size of the raster used
* when placing strings, and must be a value greater than <code>0</code>.
* An accuracy of <code>1</code> will theoretically give best results, as the
* drawable area is analyzed most detailed, but this will also be very slow.
* <br>
* Parameter <code>maxSize</code> defines the maximum size of the drawable
* area and <strong>must</strong> be a power-of of <code>accuracy</code>,
* such that <code>accuracy^n=maxSize</code> holds.
* <br>
* To add scroll bars
* to the cloud, use {@link SWT#HORIZONTAL} and {@link SWT#VERTICAL}.
* @param accuracy
* @param maxSize
* @param parent
* @param style
*/
public TagCloud(Composite parent, int style) {
public TagCloud(Composite parent, int style, int accuracy, int maxSize) {
super(parent, style);
Assert.isLegal(accuracy > 0, "Parameter accuracy must be greater than 0, but was " + accuracy);
Assert.isLegal(maxSize > 0, "Parameter maxSize must be greater than 0, but was " + maxSize);
int tmp = maxSize;
while(tmp > accuracy) {
tmp /= 2;
}
Assert.isLegal(tmp == accuracy, "Paramter maxSize must be a ");
this.accuracy = accuracy;
this.maxSize = maxSize;
cloudArea = new Rectangle(0, 0, maxSize, maxSize);
highlightColor = new Color(getDisplay(), Display.getDefault().getSystemColor(SWT.COLOR_RED).getRGB());
gc = new GC(this);
layouter = new DefaultLayouter(5, 5);
layouter = new DefaultLayouter(accuracy, accuracy);
setBackground(new Color(getDisplay(), Display.getDefault().getSystemColor(SWT.COLOR_BLACK).getRGB()));
initListeners();
textLayerImage = new Image(getDisplay(), 100,100);
Expand All @@ -230,6 +255,20 @@ public void widgetDisposed(DisposeEvent e) {
}
});
}


/**
* Creates a new Tag cloud on the given parent. To add scroll bars
* to the cloud, use {@link SWT#HORIZONTAL} and {@link SWT#VERTICAL}.
* This is a shortcut to {@link TagCloud#TagCloud(Composite, int, int, int),
* which sets the accuracy to <code>5</code> and the maximum size of the
* drawable area to <code>5120</code>.
* @param parent
* @param style
*/
public TagCloud(Composite parent, int style) {
this(parent, style, 5, 5120);
}

/**
* Disposes all system resources created in this class. Resources which were
Expand Down Expand Up @@ -492,13 +531,13 @@ private ImageData createImageData(final Word word, Font font,
// draw time...
g.drawString(word.string, 0, 0, false);
int max = Math.max(x,y);
int tmp = TREE_SIZE;
int tmp = maxSize;
while(max < tmp) {
tmp = tmp/2;
}
tmp = tmp*2;
SmallRect root = new SmallRect(0, 0, tmp, tmp);
word.tree = new RectTree(root, RESOLUTION);
word.tree = new RectTree(root, accuracy);
final ImageData id = img.getImageData();
g.dispose();
img.dispose();
Expand All @@ -517,24 +556,25 @@ private void calcWordExtents(final Word word, final ImageData id) {
id.getPixels(0, y, id.width, pixels, 0);
for (int i = 0; i < pixels.length; i++) {
int pixel = pixels[i];
int r = (pixel & palette.redMask) >> palette.redShift;
int g = (pixel & palette.greenMask) >> palette.greenShift;
int b = (pixel & palette.blueMask) >> palette.blueShift;
// Extracting color values as in PaletteData.getRGB(int pixel):
int r = (pixel >> (palette.redShift < 0 ? -palette.redShift : palette.redShift)) & palette.redMask;
int g = (pixel >> (palette.greenShift < 0 ? -palette.greenShift : palette.greenShift)) & palette.greenMask;
int b = (pixel >> (palette.blueShift < 0 ? -palette.blueShift : palette.blueShift)) & palette.blueMask;
if(r < 220 && g < 220 && b < 220) {
matrix[y][i] = true;
}
}
}
for(int i = 0; i < id.width; i+= RESOLUTION) {
for(int j = 0; j < id.height; j += RESOLUTION) {
for(int i = 0; i < id.width; i+= accuracy) {
for(int j = 0; j < id.height; j += accuracy) {
final int x = Math.max(0, i-1);
final int y = Math.max(0, j-1);
final int xMax = Math.min(i+RESOLUTION+2, id.width);
final int yMax = Math.min(j+RESOLUTION+2, id.height);
final int xMax = Math.min(i+accuracy+2, id.width);
final int yMax = Math.min(j+accuracy+2, id.height);
found: for(int a = x; a < xMax; a++) {
for(int b = y; b < yMax; b++) {
if(matrix[b][a]) {
SmallRect r = new SmallRect(i, j, RESOLUTION, RESOLUTION);
SmallRect r = new SmallRect(i, j, accuracy, accuracy);
word.tree.insert(r, word.id);
break found;
}
Expand Down Expand Up @@ -572,13 +612,10 @@ protected int layoutWords(Collection<Word> wordsToUse, IProgressMonitor monitor)
int success = 0;
if(wordsToUse != null) {
double step = 100D / wordsToUse.size();
long lTime = 0;
final GC g = gc;
for (Word word : wordsToUse) {
long s = System.currentTimeMillis();
Point point = layouter.getInitialOffset(word, cloudArea);
boolean result = layouter.layout(point, word, cloudArea, cloudMatrix);
lTime += System.currentTimeMillis() - s;
if(!result) {
System.err.println("Failed to place " + word.string);
continue;
Expand Down Expand Up @@ -637,8 +674,8 @@ public void run() {
if(textLayerImage != null) {
textLayerImage.dispose();
}
r.width += 5;
r.height += 5;
r.width += accuracy;
r.height += accuracy;
textLayerImage = new Image(getDisplay(), r.width, r.height);
gc = new GC(textLayerImage);
if(r.width > tmpImage.getBounds().width - r.x || r.height >= tmpImage.getBounds().width - r.y) {
Expand Down Expand Up @@ -707,11 +744,10 @@ public int setWords(List<Word> values, IProgressMonitor monitor) {
* Reset the initial matrix
*/
private void resetLayout() {
if(cloudMatrix == null) cloudMatrix = new short[TREE_SIZE][TREE_SIZE];
for(int i = 0; i < cloudMatrix.length; i++) {
for(int j = 0; j < cloudMatrix[i].length; j++) {
cloudMatrix[i][j] = -1;
}
if(cloudMatrix == null) {
cloudMatrix = new CloudMatrix(maxSize, accuracy);
} else {
cloudMatrix.reset();
}
}

Expand Down Expand Up @@ -861,12 +897,12 @@ private Word getWordAt(Point point) {
Point translatedMousePos = translateMousePos(point.x, point.y);
translatedMousePos.x += regionOffset.x;
translatedMousePos.y += regionOffset.y;
int x = translatedMousePos.x/RESOLUTION;
int y = translatedMousePos.y/RESOLUTION;
if(x >= cloudMatrix.length || y >= cloudMatrix[x].length) {
int x = translatedMousePos.x/accuracy;
int y = translatedMousePos.y/accuracy;
if(x >= maxSize || y >= maxSize) {
return null;
}
short wordId = cloudMatrix[x][y];
short wordId = cloudMatrix.get(x,y);
if(wordId > 0) {
Word clicked = wordsToUse.get(wordId-1);
return clicked;
Expand Down Expand Up @@ -1162,7 +1198,7 @@ public void setOpacity(int opacity) {
}

/**
* Sets the minimum font size. Should be a reasonable value > 0 (twice of {@link TagCloud#RESOLUTION}
* Sets the minimum font size. Should be a reasonable value > 0 (twice of {@link TagCloud#accuracy}
* is recommended). By default, this value is 12.
* @param size
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.zest.cloudio.Word;
import org.eclipse.zest.cloudio.util.CloudMatrix;
import org.eclipse.zest.cloudio.util.RectTree;

/**
Expand Down Expand Up @@ -85,7 +86,7 @@ public Point getInitialOffset(Word word, Rectangle cloudArea) {
* @param cloudArea
* @return
*/
public boolean layout(Point offset, final Word word, final Rectangle cloudArea, short[][] mainTree) {
public boolean layout(Point offset, final Word word, final Rectangle cloudArea, CloudMatrix mainTree) {
Assert.isLegal(word != null, "Word cannot be null!");
Point next = new Point(-word.width/2, -word.height/2);
next.x += random.nextInt(25);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.zest.cloudio.Word;
import org.eclipse.zest.cloudio.util.CloudMatrix;

/**
*
Expand All @@ -25,10 +26,10 @@ public interface ILayouter {
* @param offset
* @param word
* @param cloudArea
* @param mainTree
* @param cloudMatrix
* @return
*/
public boolean layout(Point initial, final Word word, final Rectangle cloudArea, short[][] mainTree);
public boolean layout(Point initial, final Word word, final Rectangle cloudArea, CloudMatrix cloudMatrix);

/**
* Calculates the initial offset of the given word, within the bounds
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*******************************************************************************
* Copyright (c) 2011 Stephan Schwiebert. 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
*******************************************************************************/

package org.eclipse.zest.cloudio.util;

import org.eclipse.zest.cloudio.util.RectTree.RectNode;

/**
* This class contains all global information about the drawable area
* and the layouted words in form of a {@link RectTree}.
*
* @author sschwieb
*
*/
public class CloudMatrix {

private RectTree tree;

private final int max;

private final int minResolution;

public CloudMatrix(int maxSize, int minResolution) {
this.max = maxSize;
this.minResolution = minResolution;
reset();
}

public short get(int x, int y) {
return tree.getRoot().getWordId(x*minResolution,y*minResolution);
}

public boolean isEmpty(int x, int y) {
short id = tree.getRoot().getWordId(x*minResolution, y*minResolution);
return id == -1 || id == 0;
}

public void reset() {
SmallRect root = new SmallRect(0, 0, max, max);
tree = new RectTree(root, minResolution);
}

public void set(RectNode node, short id, short xOffset, short yOffset, int minResolution) {
int cleanX = (xOffset + node.rect.x) / minResolution;
int cleanY = (yOffset + node.rect.y) / minResolution;
SmallRect rect = new SmallRect(cleanX* minResolution, cleanY* minResolution, minResolution, minResolution);
tree.insert(rect, id);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class RectTree {

private short xOffset, yOffset;

private final RectNode root;
private RectNode root;

private LinkedList<RectNode> leaves;

Expand Down Expand Up @@ -137,18 +137,22 @@ public boolean isAvailable(final SmallRect oRect) {
}


public short getWordId(Point position) {
public short getWordId(int x, int y) {
if(filled > 0) return filled;
if(children == null) {
return filled;
}
for(int i = 0; i < childAreas.length; i++) {
if(childAreas[i].intersects(position.x-2, position.y-2, 4, 4) && children[i] != null) {
return children[i].getWordId(position);
if(childAreas[i].intersects(x-2, y-2, 4, 4) && children[i] != null) {
return children[i].getWordId(x, y);
}
}
return 0;
}

public short getWordId(Point position) {
return getWordId(position.x, position.y);
}

}

Expand All @@ -173,12 +177,12 @@ public void move(int x, int y) {
this.yOffset = (short) y;
}

public boolean fits(final short[][] mainTree) {
public boolean fits(final CloudMatrix mainTree) {
LinkedList<RectNode> leaves = getLeaves();
Iterator<RectNode> nodes = leaves.iterator();
while(nodes.hasNext()) {
RectNode node = nodes.next();
if(mainTree[(node.rect.x+xOffset)/minResolution][(node.rect.y+yOffset)/minResolution] != EMPTY) {
if(!mainTree.isEmpty((node.rect.x+xOffset)/minResolution,(node.rect.y+yOffset)/minResolution)) {
nodes.remove();
leaves.addFirst(node);
return false;
Expand Down Expand Up @@ -208,10 +212,10 @@ private void addLeaves(List<RectNode> leaves, RectNode current) {
}
}

public void place(final short[][] mainTree, short id) {
public void place(final CloudMatrix mainTree, short id) {
Collection<RectNode> leaves = getLeaves();
for (RectNode node : leaves) {
mainTree[(node.rect.x+xOffset)/minResolution][(node.rect.y+yOffset)/minResolution] = id;
mainTree.set(node, id, xOffset, yOffset, minResolution);
}
}

Expand All @@ -220,6 +224,14 @@ public void releaseRects() {
root.children = null;
}

public RectNode getRoot() {
return root;
}

public void reset() {
root = new RectNode(root.rect);
}



}

0 comments on commit eabe119

Please sign in to comment.