// Charles A. Loomis, Jr., and University of California, Santa Cruz,
// Copyright (c) 2000
package org.freehep.swing.layout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.LayoutManager2;
import java.awt.Rectangle;
import java.awt.Window;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
/**
* The <code>ConstrainedGridLayout</code> layout manager is based on
* the <code>GraphPaperLayout</code> layout manager written by Michael
* Martak.
*
* This layout manager divides the container into a set of equally
* sized cells arranged in a grid which has <code>nw</code> cells
* in the horizontal direction and <code>nh</code> cells in the
* vertical direction. Components can occupy an rectangular subset of
* these cells; the component's position and size are based on given
* rectangular constraints.
*
* This extends the functionality of the <code>GraphPaperLayout</code>
* manager by also constraining the overall aspect ratio of the
* container. It will also force a containing window to resize if
* the desired size of the container isn't a size which is allowed by
* the contraints. A byproduct of this is that the preferred sizes of
* components controlled by this layout manager will be reset to the
* largest allowed size which is smaller than the current size.
*
* Components can overlap making this layout manager a good choice for
* a <code>JLayeredPane</code>. To allow the standard moveToFront and
* moveToBack methods to work as expected, the constraints of a
* removed component are retained so that if it is later added again
* without contraints the layout manager will still arrange the
* components reasonably. (The standard moveToFront implementation,
* removes a component and re-adds it without taking into account the
* possible constraints.)
*
* @author Charles A. Loomis, Jr.
* @version $Id: ConstrainedGridLayout.java 8584 2006-08-10 23:06:37Z duns $
* */
public class ConstrainedGridLayout
implements LayoutManager2 {
/**
* The virtual grid size. */
private Dimension gridSize;
/**
* The aspect ratio of the container. */
private Dimension aspectRatio;
/**
* The smallest dimension which maintains the aspect ratio and has
* a width and height which are multiples of the grid size. */
private Dimension containerFormat;
final public static String GRID_SIZE_ERROR =
"Grid size must have width and height > 0.";
final public static String ASPECT_RATIO_ERROR =
"Aspect ratio must have width and height > 0.";
final public static String CONSTRAINT_ERROR =
"Constraint must be a Rectangle with positive width and height.";
final public static String CONSTRAINED_GRID_LAYOUT_CONSTRAINT =
"ConstrainedGridLayout.RectangularConstraint";
final public static int MINIMUM_LAYOUT = 0;
final public static int PREFERRED_LAYOUT = 1;
/**
* Creates a <code>ConstrainedGridLayout</code> with the given
* grid size and aspect ratio.
*
* @param gridSize size of grid in logical units (width x height)
* @param aspectRatio aspect ratio of container (width x height) */
public ConstrainedGridLayout(Dimension gridSize,
Dimension aspectRatio) {
// Check that all of the dimensions given are reasonable.
if (gridSize.width<=0 || gridSize.height<=0 ||
gridSize.width>100 || gridSize.height>100) {
throw new IllegalArgumentException(GRID_SIZE_ERROR);
}
if (aspectRatio.width<=0 || aspectRatio.height<=0 ||
aspectRatio.width>100 || aspectRatio.height>100) {
throw new IllegalArgumentException(ASPECT_RATIO_ERROR);
}
// Copy the information into the layout manager.
this.gridSize = new Dimension(gridSize);
this.aspectRatio = new Dimension(aspectRatio);
// Calculate the minimum container format.
containerFormat = new Dimension();
containerFormat = getSmallestDimension(containerFormat,
aspectRatio,
gridSize);
}
/**
* Return a new <code>Dimension</code> which is a copy of the
* current grid size.
*
* @return current grid size */
public Dimension getGridSize() {
return new Dimension(gridSize);
}
/**
* Set the current grid size to the given <code>Dimension</code>.
* The grid size must have horizontal and vertical components
* which are positive.
*
* @param gridSize new grid size */
public void setGridSize(Dimension gridSize) {
if (gridSize.width<=0 || gridSize.height<=0 ||
gridSize.width>100 || gridSize.height>100) {
throw new IllegalArgumentException(GRID_SIZE_ERROR);
}
this.gridSize.setSize(gridSize);
// Get the smallest fraction which keeps the aspect ratio and
// grid size.
containerFormat = getSmallestDimension(containerFormat,
aspectRatio,
gridSize);
}
/**
* Return a new <code>Dimension</code> which is a copy of the
* current aspect ratio.
*
* @return current aspect ratio */
public Dimension getAspectRatio() {
return new Dimension(aspectRatio);
}
/**
* Set the current aspect ratio to the given
* <code>Dimension</code>. The aspect ratio must have horizontal
* and vertical components which are positive.
*
* @param aspectRatio new aspect ratio for the container */
public void setAspectRatio(Dimension aspectRatio) {
if (aspectRatio.width<=0 || aspectRatio.height<=0) {
throw new IllegalArgumentException(ASPECT_RATIO_ERROR);
}
this.aspectRatio.setSize(aspectRatio);
// Get the smallest fraction which keeps the aspect ratio and
// grid size.
containerFormat = getSmallestDimension(containerFormat,
aspectRatio,
gridSize);
}
/**
* Get the constraints being used for the given component.
*
* @param comp the component to lookup
*
* @return <code>Rectangle</code> describing the position and size
* of the given component */
public Rectangle getConstraints(Component comp) {
Rectangle r = null;
if (comp instanceof JComponent) {
JComponent jc = (JComponent) comp;
Object constraint =
jc.getClientProperty(CONSTRAINED_GRID_LAYOUT_CONSTRAINT);
if (constraint instanceof Rectangle) {
r = (Rectangle) constraint;
}
}
return (r!=null) ? new Rectangle(r) : null;
}
/**
* Set (or reset) the constraints for the given component.
*
* @param comp the component to constrain
* @param constraints <code>Rectangle</code> describing the
* position and size of the component */
public void setConstraints(Component comp, Rectangle constraints) {
if (comp instanceof JComponent) {
Rectangle copy = new Rectangle(constraints);
JComponent jc = (JComponent) comp;
jc.putClientProperty(CONSTRAINED_GRID_LAYOUT_CONSTRAINT,
copy);
}
}
/**
* Adds the specified component with the specified name to the
* layout. <code>ConstrainedGridLayout</code> will arrange this
* component normally if a constraint has been set previously or
* is set before the container is next validated. If not, then
* this component will be ignored by this layout manager.
*
* The component name is always ignored by this layout manager.
*
* @param name name of component (ignored)
* @param comp component to add to the layout */
public void addLayoutComponent(String name, Component comp) {
}
/**
* Removes the specified component from the layout. This method
* actually does nothing since the component list is obtained from
* the container when it is laid-out.
*
* The constraint is still stored in case the component is later
* added again to the container without the constraint being
* specified. (Happens when the standard implementation of
* moveToFront in a <code>JLayeredPane</code> is called.)
*
* @param comp the component to be removed */
public void removeLayoutComponent(Component comp) {
}
/**
* Calculates the preferred size dimensions for the specified
* panel given the components in the specified parent container.
*
* @param parent the component to be laid out
*
* @see #minimumLayoutSize */
public Dimension preferredLayoutSize(Container parent) {
return getLayoutSize(parent, PREFERRED_LAYOUT);
}
/**
* Calculates the minimum size dimensions for the specified
* panel given the components in the specified parent container.
*
* @param parent the component to be laid out
*
* @see #preferredLayoutSize */
public Dimension minimumLayoutSize(Container parent) {
return getLayoutSize(parent, MINIMUM_LAYOUT);
}
/**
* Calculate the largest minimum or preferred cell size.
*
* For the minimum or preferred cell sizes: The components'
* minimum or preferred sizes are obtained and then divided by the
* number of rows and columns that the component spans. The
* largest cell size is returned.
*
* @param parent the container in which to do the layout.
* @param selectionFlag either MINIMUM_LAYOUT or PREFERRED_LAYOUT.
*
* @return the appropriate cell size */
protected Dimension getLayoutSize(Container parent,
int selectionFlag) {
// Keep track of the cell size.
int cellHeight = 0;
int cellWidth = 0;
// Loop over all of the parent's components.
for (int i=0; i<parent.getComponentCount(); i++) {
// Get the component and the constraint.
Component c = parent.getComponent(i);
Rectangle r = getConstraints(c);
if (c!=null && r!=null) {
Dimension componentSize = null;
switch (selectionFlag) {
case MINIMUM_LAYOUT:
componentSize = c.getMinimumSize();
break;
case PREFERRED_LAYOUT:
componentSize = c.getPreferredSize();
break;
}
// Select the largest width and height needed.
cellWidth =
Math.max(cellWidth,
componentSize.width/r.width);
cellHeight =
Math.max(cellHeight,
componentSize.height/r.height);
}
}
// Calculate the total MINIMUM width and height needed.
int totalWidth = cellWidth*gridSize.width;
int totalHeight = cellHeight*gridSize.height;
int widthMultiple = totalWidth/containerFormat.width;
int heightMultiple = totalHeight/containerFormat.height;
int finalMultiple = Math.max(widthMultiple, heightMultiple);
Dimension containerSize =
new Dimension(containerFormat.width*finalMultiple,
containerFormat.height*finalMultiple);
if (parent instanceof JComponent)
((JComponent) parent).setPreferredSize(containerSize);
return containerSize;
}
/**
* Adjust the given size of the parent (minus the insets) to an
* appropriate size. This being the largest size which fits
* inside of the original size and is a multiple of the aspect
* ratio and of the grid size. Note that the input
* <code>Dimension</code> is modified and returned rather than
* creating a new object.
*
* @param trialSize parent's size minus insets to adjust;
* trialSize is overwritten with the adjusted size of the
* component
*
* @return boolean flag indicating whether the size was actually
* changed */
public boolean adjustSize(Dimension trialSize) {
int originalWidth = trialSize.width;
int originalHeight = trialSize.height;
int widthMultiple =
Math.max(1,trialSize.width/containerFormat.width);
int heightMultiple =
Math.max(1,trialSize.height/containerFormat.height);
int finalMultiple =
Math.min(widthMultiple, heightMultiple);
trialSize.width = containerFormat.width*finalMultiple;
trialSize.height = containerFormat.height*finalMultiple;
return (originalWidth!=trialSize.width ||
originalHeight!=trialSize.height);
}
/**
* Change the size of the component by the given increment in the
* size. Positive increments enlarge the component; negative
* increments reduce the component.
*
* @param parent the component controlled by this layout manager
* @param sizeIncrement number of increments by which to increase
* the size
*
* @return flag indicating whether the size actually changed */
public boolean adjustSize(Container parent, int sizeIncrement) {
boolean sizeChanged = false;
if (parent instanceof JComponent) {
JComponent jc = (JComponent) parent;
Insets insets = jc.getInsets();
Dimension size = jc.getSize();
size.width -= (insets.left+insets.right);
size.height -= (insets.top+insets.bottom);
int originalMultiple = size.width/containerFormat.width;
int newMultiple =
Math.max(1,originalMultiple+sizeIncrement);
if (originalMultiple!=newMultiple) {
size.width = containerFormat.width*newMultiple;
size.height = containerFormat.height*newMultiple;
// Add in the insets again.
size.width += (insets.left+insets.right);
size.height += (insets.top+insets.bottom);
// Set the preferred size.
sizeChanged = true;
jc.setPreferredSize(size);
}
}
// If another validation is needed, then tell the window
// containing this container.
if (sizeChanged)
SwingUtilities.invokeLater(new RunAnotherLayout(parent));
return sizeChanged;
}
/**
* Lays out the container in the specified container.
*
* @param parent the component which needs to be laid out */
public void layoutContainer(Container parent) {
// Flag used to indicate that the constraints caused this
// layout manager to set the size of the component to a
// smaller one than requested. Force the containing window to
// layout its contents again.
boolean needAnotherLayout = false;
// Lock the component tree while the layout is in progress.
synchronized (parent.getTreeLock()) {
// Get the number of components and the parent's insets.
int n = parent.getComponentCount();
Insets insets = parent.getInsets();
// Total size of the parent without the insets.
Dimension size = parent.getSize();
size.width -= (insets.left+insets.right);
size.height -= (insets.top+insets.bottom);
// Adjust the size for the constraints (component size
// and aspect ratio). If an adjustment is made then
// the layout will need to be done again. (But only
// do this if it is a JComponent, otherwise this will
// just cause an infinite loop.)
if (parent instanceof JComponent) {
JComponent jc = (JComponent) parent;
needAnotherLayout = adjustSize(size);
// Set the parent's preferred size.
if (needAnotherLayout) {
// Calculate the full size of parent.
Dimension fullSize = new Dimension(size);
fullSize.width += (insets.left+insets.right);
fullSize.height += (insets.top+insets.bottom);
jc.setPreferredSize(fullSize);
}
}
if (n>0) {
// Calculate the cell dimensions.
Dimension cellSize =
new Dimension(size.width/gridSize.width,
size.height/gridSize.height);
// Now resize the components.
for (int i=0; i<n; i++) {
Component c = parent.getComponent(i);
Rectangle r = getConstraints(c);
// If the constraints exist, then resize. Also
// set the preferred size of the components to the
// given size; this way extra space will be
// removed at the next revalidation of the
// container.
if (r!=null) {
int x = insets.left + (r.x * cellSize.width);
int y = insets.top + (r.y * cellSize.height);
int w = r.width*cellSize.width;
int h = r.height*cellSize.height;
// Set the size of the component.
c.setBounds(x, y, w, h);
}
}
}
}
// If another validation is needed, then tell the window
// containing this container.
if (needAnotherLayout)
SwingUtilities.invokeLater(new RunAnotherLayout(parent));
}
/**
* Return a rectangle containing the constrained bounds for the
* given input bounds. This is used to constrain the size of a
* component to this layout manager's grid.
*
* @param parent the Container which contains this component
* @param constrainedBounds the rectangle which will be
* overwritten with the result (if null, new Rectangle is created)
* @param inputBounds the input bounds to constrain to this layout
* manager's grid
*
* @return the constrained rectangle */
public Rectangle getConstrainedBounds(Container parent,
Rectangle constrainedBounds,
Rectangle inputBounds) {
// Get the parent's insets.
Insets insets = parent.getInsets();
// Total size of the parent without the insets.
Dimension size = parent.getSize();
size.width -= (insets.left+insets.right);
size.height -= (insets.top+insets.bottom);
// Get the number of pixels per division.
int xPixelsPerDivision = size.width/gridSize.width;
int yPixelsPerDivision = size.height/gridSize.height;
// Get the origin of the input rectangle.
int x0 = inputBounds.x - insets.left;
int y0 = inputBounds.y - insets.top;
// First quantize the width and height.
int iw = (int)
Math.round(((float) inputBounds.width)*gridSize.width/
((float) parent.getWidth()));
int ih = (int)
Math.round(((float) inputBounds.height)*gridSize.height/
((float) parent.getHeight()));
// And force these to be no bigger than the full grid size. Don't
// allow the window to disappear in either direction.
iw = Math.max(1,Math.min(iw,gridSize.width));
ih = Math.max(1,Math.min(ih,gridSize.height));
// Now calculate the quantized origin.
int ix0 = (int)
Math.round(((float) x0)*gridSize.width/
((float) parent.getWidth()));
int iy0 = (int)
Math.round(((float) y0)*gridSize.width/
((float) parent.getHeight()));
// Constrain these to allow the whole window to be within the grid.
ix0 = Math.max(0,Math.min(ix0,gridSize.width-iw));
iy0 = Math.max(0,Math.min(iy0,gridSize.height-ih));
// Calculate the origin in pixel coordinates.
x0 = xPixelsPerDivision*ix0 + insets.left;
y0 = yPixelsPerDivision*iy0 + insets.top;
// Make the return rectangle.
Rectangle result = constrainedBounds;
if (result==null) result = new Rectangle();
// Fill in the values.
result.width = xPixelsPerDivision*iw;
result.height = yPixelsPerDivision*ih;
result.x = x0;
result.y = y0;
return result;
}
/**
* Return a rectangle containing the appropriate constraint
* rectangle for the given input bounds. This constrains the
* input bounds to this layout manager's grid and returns the
* constraint rectangle in grid coordinates.
*
* @param parent the Container which contains this component
* @param constraints the rectangle which will be
* overwritten with the result (if null, new Rectangle is created)
* @param inputBounds the input bounds to constrain to this layout
* manager's grid
*
* @return the constraint rectangle */
public Rectangle getConstraints(Container parent,
Rectangle constraints,
Rectangle inputBounds) {
// Get the parent's insets.
Insets insets = parent.getInsets();
// Total size of the parent without the insets.
Dimension size = parent.getSize();
size.width -= (insets.left+insets.right);
size.height -= (insets.top+insets.bottom);
// Get the origin of the input rectangle.
int x0 = inputBounds.x - insets.left;
int y0 = inputBounds.y - insets.top;
// First quantize the width and height.
int iw = (int)
Math.round(((float) inputBounds.width)*gridSize.width/
((float) parent.getWidth()));
int ih = (int)
Math.round(((float) inputBounds.height)*gridSize.height/
((float) parent.getHeight()));
// And force these to be no bigger than the full grid size. Don't
// allow the window to disappear in either direction.
iw = Math.max(1,Math.min(iw,gridSize.width));
ih = Math.max(1,Math.min(ih,gridSize.height));
// Now calculate the quantized origin.
int ix0 = (int)
Math.round(((float) x0)*gridSize.width/
((float) parent.getWidth()));
int iy0 = (int)
Math.round(((float) y0)*gridSize.width/
((float) parent.getHeight()));
// Constrain these to allow the whole window to be within the grid.
ix0 = Math.max(0,Math.min(ix0,gridSize.width-iw));
iy0 = Math.max(0,Math.min(iy0,gridSize.height-ih));
// Make the return rectangle.
Rectangle result = constraints;
if (result==null) result = new Rectangle();
// Fill in the values.
result.width = iw;
result.height = ih;
result.x = ix0;
result.y = iy0;
return result;
}
/**
* This private class simply forces the containing window to
* layout its components once again. This is used to force the
* window to remove extra space which was created by satisying the
* contraints of this layout manager. */
private class RunAnotherLayout
implements Runnable {
Container parent;
public RunAnotherLayout(Container parent) {
this.parent = parent;
}
public void run() {
Window window =
(Window) SwingUtilities.getWindowAncestor(parent);
if (window!=null) window.pack();
}
}
/**
* Adds the specified component to the layout, using the specified
* constraint object.
*
* @param comp the component to be added
* @param constraints where/how the component is added to the
* layout; this must be a <code>Rectangle</code> */
public void addLayoutComponent(Component comp, Object constraints) {
if (constraints instanceof Rectangle) {
Rectangle r = (Rectangle) constraints;
if (r.width<=0 || r.height<=0) {
throw new IllegalArgumentException(CONSTRAINT_ERROR);
}
// Go ahead and put in the given constraints.
setConstraints(comp, r);
} else if (constraints != null) {
throw new IllegalArgumentException(CONSTRAINT_ERROR);
}
}
/**
* Returns the maximum size of this component. This just returns
* the largest size possible.
*
* @see java.awt.Component#getMinimumSize()
* @see java.awt.Component#getPreferredSize()
* @see LayoutManager */
public Dimension maximumLayoutSize(Container target) {
return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
}
/**
* Returns the alignment along the x axis. This specifies how the
* component would like to be aligned relative to other
* components. The value should be a number between 0 and 1 where
* 0 represents alignment along the origin, 1 is aligned the
* furthest away from the origin, 0.5 is centered, etc.
*
* This just returns the centering alignment (0.5). */
public float getLayoutAlignmentX(Container target) {
return 0.5f;
}
/**
* Returns the alignment along the y axis. This specifies how the
* component would like to be aligned relative to other
* components. The value should be a number between 0 and 1 where
* 0 represents alignment along the origin, 1 is aligned the
* furthest away from the origin, 0.5 is centered, etc.
*
* This just returns the centering alignment. */
public float getLayoutAlignmentY(Container target) {
return 0.5f;
}
/**
* Invalidates the layout, indicating that if the layout manager
* has cached information it should be discarded. */
public void invalidateLayout(Container target) {
}
/**
* The first prime numbers up to 100. */
final private static int[] primes = { 1, 2, 3, 5, 7, 11,
13, 17, 19, 23, 29,
31, 37, 41, 43, 47,
53, 59, 61, 67, 71,
73, 79, 83, 89, 97};
/**
* The prime factorizations of the numbers up to 100. */
final private static int[][] factorization = {
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,2,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,3,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},
{1,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0},
{1,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,2,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0},
{1,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,3,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0},
{1,2,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,2,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0},
{1,2,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0},
{1,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0},
{1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,2,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0},
{1,4,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0},
{1,2,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},
{1,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
{1,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,2,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0},
{1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,5,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,1,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
/**
* Get the smallest dimension which maintains the aspect ratio and
* has a width and height which is evenly divisible by the grid
* size. */
private static Dimension getSmallestDimension(Dimension containerFormat,
Dimension aspectRatio,
Dimension gridSize) {
int[] widthFactors = new int[primes.length];
int[] widthCommonDenominator = new int[primes.length];
int[] heightFactors = new int[primes.length];
int[] heightCommonDenominator = new int[primes.length];
// Make the prime factorization for the product.
primeFactorsOfProduct(widthFactors,
aspectRatio.width,
gridSize.width,
gridSize.height);
primeFactorsOfProduct(heightFactors,
aspectRatio.height,
gridSize.width,
gridSize.height);
// Get the least common denominator for the width and height.
leastCommonDenominator(widthCommonDenominator,
aspectRatio.width, gridSize.width);
leastCommonDenominator(heightCommonDenominator,
aspectRatio.height, gridSize.height);
// Remove the common factors.
removeFactors(widthFactors, widthCommonDenominator);
removeFactors(heightFactors, heightCommonDenominator);
removeCommonFactors(widthFactors,heightFactors);
// Put back in the common denominators.
addFactors(widthFactors, widthCommonDenominator);
addFactors(heightFactors, heightCommonDenominator);
// Return the modified dimension.
containerFormat.setSize(product(widthFactors),
product(heightFactors));
return containerFormat;
}
private static void primeFactorsOfProduct(int[] result,
int a, int b, int c) {
int[] factorsA = factorization[a];
int[] factorsB = factorization[b];
int[] factorsC = factorization[c];
for (int i=0; i<result.length; i++) {
result[i] = factorsA[i]+factorsB[i]+factorsC[i];
}
}
private static void leastCommonDenominator(int[] result, int a, int b) {
int[] factorsA = factorization[a];
int[] factorsB = factorization[b];
for (int i=0; i<result.length; i++) {
result[i] = Math.max(factorsA[i], factorsB[i]);
}
}
private static void removeFactors(int[] a, int[] b) {
for (int i=0; i<Math.min(a.length,b.length); i++) {
a[i] -= b[i];
}
}
private static void addFactors(int[] a, int[] b) {
for (int i=0; i<Math.min(a.length,b.length); i++) {
a[i] += b[i];
}
}
private static void removeCommonFactors(int[] a, int[] b) {
for (int i=0; i<Math.min(a.length,b.length); i++) {
int common = Math.min(a[i],b[i]);
a[i] -= common;
b[i] -= common;
}
}
private static int product(int[] arrays1) {
int result = 1;
for (int i=0; i<arrays1.length; i++) {
int number = primes[i];
for (int j=0; j<arrays1[i]; j++) {
result *= number;
}
}
return result;
}
}