1
|
/*******************************************************************************
|
2
|
* Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada.
|
3
|
* All rights reserved. This program and the accompanying materials are made
|
4
|
* available under the terms of the Eclipse Public License v1.0 which
|
5
|
* accompanies this distribution, and is available at
|
6
|
* http://www.eclipse.org/legal/epl-v10.html
|
7
|
*
|
8
|
* Contributors: The Chisel Group, University of Victoria
|
9
|
*******************************************************************************/
|
10
|
package org.eclipse.zest.layouts.algorithms;
|
11
|
|
12
|
import java.util.Arrays;
|
13
|
|
14
|
import org.eclipse.zest.layouts.LayoutStyles;
|
15
|
import org.eclipse.zest.layouts.dataStructures.InternalNode;
|
16
|
import org.eclipse.zest.layouts.dataStructures.InternalRelationship;
|
17
|
|
18
|
/**
|
19
|
* @version 2.0
|
20
|
* @author Ian Bull
|
21
|
* @author Casey Best and Rob Lintern
|
22
|
*/
|
23
|
public class GridLayoutAlgorithm extends AbstractLayoutAlgorithm {
|
24
|
|
25
|
private static final double PADDING_PERCENTAGE = 0.95;
|
26
|
|
27
|
protected int rowPadding = 0;
|
28
|
|
29
|
public void setLayoutArea(double x, double y, double width, double height) {
|
30
|
throw new RuntimeException("Operation not implemented");
|
31
|
}
|
32
|
|
33
|
int rows, cols, numChildren;
|
34
|
double colWidth, rowHeight, offsetX, offsetY;
|
35
|
int totalProgress;
|
36
|
double h, w;
|
37
|
|
38
|
/**
|
39
|
* Initializes the grid layout.
|
40
|
* @param styles
|
41
|
* @see LayoutStyles
|
42
|
*/
|
43
|
public GridLayoutAlgorithm(int styles) {
|
44
|
super(styles);
|
45
|
}
|
46
|
|
47
|
/**
|
48
|
* Inititalizes the grid layout with no style.
|
49
|
*/
|
50
|
public GridLayoutAlgorithm() {
|
51
|
this(LayoutStyles.NONE);
|
52
|
}
|
53
|
|
54
|
protected int getCurrentLayoutStep() {
|
55
|
// TODO: This isn't right
|
56
|
return 0;
|
57
|
}
|
58
|
|
59
|
protected int getTotalNumberOfLayoutSteps() {
|
60
|
return totalProgress;
|
61
|
}
|
62
|
|
63
|
/**
|
64
|
*
|
65
|
*/
|
66
|
protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) {
|
67
|
|
68
|
// TODO: Filter unwanted entities and relationships
|
69
|
//super.applyLayout (entitiesToLayout, relationshipsToConsider, boundsX, boundsY, boundsWidth, boundsHeight);
|
70
|
// now begin
|
71
|
numChildren = entitiesToLayout.length;
|
72
|
if (numChildren < 1)
|
73
|
return;
|
74
|
|
75
|
int[] colsAndRows = calculateNumberOfRowsAndCols(numChildren, x, y, width, height);
|
76
|
cols = colsAndRows[0];
|
77
|
rows = colsAndRows[1];
|
78
|
|
79
|
totalProgress = rows + 2;
|
80
|
fireProgressEvent(1, totalProgress);
|
81
|
|
82
|
// sort the entities
|
83
|
if (comparator != null) {
|
84
|
Arrays.sort(entitiesToLayout, comparator);
|
85
|
} else {
|
86
|
Arrays.sort(entitiesToLayout);
|
87
|
}
|
88
|
fireProgressEvent(2, totalProgress);
|
89
|
|
90
|
// Calculate row height and column width
|
91
|
colWidth = width / cols;
|
92
|
rowHeight = height / rows;
|
93
|
|
94
|
// Calculate amount to scale children
|
95
|
double[] nodeSize = calculateNodeSize(colWidth, rowHeight);
|
96
|
w = nodeSize[0];
|
97
|
h = nodeSize[1];
|
98
|
offsetX = (colWidth - w) / 2.0; // half of the space between columns
|
99
|
offsetY = (rowHeight - h) / 2.0; // half of the space between rows
|
100
|
}
|
101
|
|
102
|
/**
|
103
|
* Use this algorithm to layout the given entities, using the given relationships and bounds.
|
104
|
* The entities will be placed in the same order as they are passed in, unless a comparator
|
105
|
* is supplied.
|
106
|
*
|
107
|
* @param entitiesToLayout Apply the algorithm to these entities
|
108
|
* @param relationshipsToConsider Only consider these relationships when applying the algorithm.
|
109
|
* @param boundsX The left side of the bounds in which the layout can place the entities.
|
110
|
* @param boundsY The top side of the bounds in which the layout can place the entities.
|
111
|
* @param boundsWidth The width of the bounds in which the layout can place the entities.
|
112
|
* @param boundsHeight The height of the bounds in which the layout can place the entities.
|
113
|
* @throws RuntimeException Thrown if entitiesToLayout doesn't contain all of the endpoints for each relationship in relationshipsToConsider
|
114
|
*/
|
115
|
protected synchronized void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double boundsX, double boundsY, double boundsWidth, double boundsHeight) {
|
116
|
|
117
|
int index = 0;
|
118
|
for (int i = 0; i < rows; i++) {
|
119
|
for (int j = 0; j < cols; j++) {
|
120
|
if ((i * cols + j) < numChildren) {
|
121
|
// find new position for child
|
122
|
double xmove = boundsX + j * colWidth + offsetX;
|
123
|
double ymove = boundsY + i * rowHeight + offsetY;
|
124
|
InternalNode sn = entitiesToLayout[index++];
|
125
|
sn.setInternalLocation(xmove, ymove);
|
126
|
sn.setInternalSize(Math.max(w, MIN_ENTITY_SIZE), Math.max(h, MIN_ENTITY_SIZE));
|
127
|
}
|
128
|
}
|
129
|
fireProgressEvent(2 + i, totalProgress);
|
130
|
}
|
131
|
updateLayoutLocations(entitiesToLayout);
|
132
|
fireProgressEvent(totalProgress, totalProgress);
|
133
|
}
|
134
|
|
135
|
protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) {
|
136
|
|
137
|
}
|
138
|
|
139
|
/**
|
140
|
* Calculates and returns an array containing the number of columns, followed by the number of rows
|
141
|
*/
|
142
|
protected int[] calculateNumberOfRowsAndCols(int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) {
|
143
|
if (getEntityAspectRatio() == 1.0) {
|
144
|
return calculateNumberOfRowsAndCols_square(numChildren, boundX, boundY, boundWidth, boundHeight);
|
145
|
} else {
|
146
|
return calculateNumberOfRowsAndCols_rectangular(numChildren);
|
147
|
}
|
148
|
}
|
149
|
|
150
|
protected int[] calculateNumberOfRowsAndCols_square(int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) {
|
151
|
int rows = Math.max(1, (int) Math.sqrt(numChildren * boundHeight / boundWidth));
|
152
|
int cols = Math.max(1, (int) Math.sqrt(numChildren * boundWidth / boundHeight));
|
153
|
|
154
|
// if space is taller than wide, adjust rows first
|
155
|
if (boundWidth <= boundHeight) {
|
156
|
//decrease number of rows and columns until just enough or not enough
|
157
|
while (rows * cols > numChildren) {
|
158
|
if (rows > 1)
|
159
|
rows--;
|
160
|
if (rows * cols > numChildren)
|
161
|
if (cols > 1)
|
162
|
cols--;
|
163
|
}
|
164
|
//increase number of rows and columns until just enough
|
165
|
while (rows * cols < numChildren) {
|
166
|
rows++;
|
167
|
if (rows * cols < numChildren)
|
168
|
cols++;
|
169
|
}
|
170
|
} else {
|
171
|
//decrease number of rows and columns until just enough or not enough
|
172
|
while (rows * cols > numChildren) {
|
173
|
if (cols > 1)
|
174
|
cols--;
|
175
|
if (rows * cols > numChildren)
|
176
|
if (rows > 1)
|
177
|
rows--;
|
178
|
}
|
179
|
//increase number of rows and columns until just enough
|
180
|
while (rows * cols < numChildren) {
|
181
|
cols++;
|
182
|
if (rows * cols < numChildren)
|
183
|
rows++;
|
184
|
}
|
185
|
}
|
186
|
int[] result = { cols, rows };
|
187
|
return result;
|
188
|
}
|
189
|
|
190
|
protected int[] calculateNumberOfRowsAndCols_rectangular(int numChildren) {
|
191
|
int rows = Math.max(1, (int) Math.ceil(Math.sqrt(numChildren)));
|
192
|
int cols = Math.max(1, (int) Math.ceil(Math.sqrt(numChildren)));
|
193
|
int[] result = { cols, rows };
|
194
|
return result;
|
195
|
}
|
196
|
|
197
|
protected double[] calculateNodeSize(double colWidth, double rowHeight) {
|
198
|
double childW = Math.max(MIN_ENTITY_SIZE, PADDING_PERCENTAGE * colWidth);
|
199
|
double childH = Math.max(MIN_ENTITY_SIZE, PADDING_PERCENTAGE * (rowHeight - rowPadding));
|
200
|
double whRatio = colWidth / rowHeight;
|
201
|
if (whRatio < getEntityAspectRatio()) {
|
202
|
childH = childW / getEntityAspectRatio();
|
203
|
} else {
|
204
|
childW = childH * getEntityAspectRatio();
|
205
|
}
|
206
|
double[] result = { childW, childH };
|
207
|
return result;
|
208
|
}
|
209
|
|
210
|
/**
|
211
|
* Increases the padding between rows in the grid
|
212
|
* @param rowPadding Value will not be set if less than 0.
|
213
|
*/
|
214
|
public void setRowPadding(int rowPadding) {
|
215
|
if (rowPadding < 0) {
|
216
|
return;
|
217
|
}
|
218
|
this.rowPadding = rowPadding;
|
219
|
}
|
220
|
|
221
|
protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) {
|
222
|
if (asynchronous && continueous)
|
223
|
return false;
|
224
|
else if (asynchronous && !continueous)
|
225
|
return true;
|
226
|
else if (!asynchronous && continueous)
|
227
|
return false;
|
228
|
else if (!asynchronous && !continueous)
|
229
|
return true;
|
230
|
|
231
|
return false;
|
232
|
}
|
233
|
|
234
|
}
|