# my brand spanking new layout manager

Micah Wedemeyer

Ranch Hand

Posts: 68

posted 14 years ago

- 0

I've been working on a custom layout manager for a long time now, and I'm almost done. I call it OrbitLayout and it displays components in concentric circles. I'm itching to show it off, so if you'd like to give it a try, here it is:

To add components, use the following method:

Container con;

Componenent com;

// initialize them, etc

con.setLayout(new OrbitLayout(0,25); // for some spacing

int orbit = (choose an orbit here from 0 to n, where 0 is the center)

con.add(com, new Integer(orbit));

Try adding a few components (buttons work best) to a few orbits (although the center orbit 0 can only hold 1 component).

Please tell me what you think!!!

Micah

<code>

import java.util.*;

import java.awt.*;

/**

* This layout manager is used to display components in concentric circles surrounding

* a central component (or no central component). For the purposes of this

* source file, each circle is an orbit and the components are satellites.

*

* @author Micah Wedemeyer

* @version 1.0

*/

public class OrbitLayout implements LayoutManager2 {

/**

* A constant used to deliniate the central component that all other components orbit.

*/

final public static int CENTER = 0;

/**

* The minimum spacing between components in the same orbit.

*/

private int satGap;

/**

* The minimum spacing between each orbit.

*/

private int orbitGap;

/**

* The current outer-most orbit.

*/

private int outerOrbit;

/**

* A list of orbits. Each element in the list is a list itself of satellites

* in that orbit. The center object is at orbits[0],

* the first orbit at orbits[1], and the nth at orbits[n].

*/

private ArrayList orbits;

/**

* A central component that all the satellites orbit

*/

private Component center;

/**

* Creates an OrbitLayout with the specified spacing.

* @param satGap The minimum distance between satellites in the same orbit.

* @param orbitGap The minimum distance between concentric orbits.

*/

public OrbitLayout(int satGap, int orbitGap) {

this.satGap = satGap;

this.orbitGap = orbitGap;

outerOrbit = 0;

center = null;

orbits = new ArrayList();

orbits.add(new OrbitList(0));

}

/**

* Does not use, must be overridden for interface.

*/

public void addLayoutComponent(String s, Component c) {

}

/**

* Adds a component to the layout.

* @param c The component to be added.

* @param constraints (an Integer object) The orbit to add the component to.

*/

public void addLayoutComponent(Component c, Object constraints) {

if (constraints instanceof Integer) {

Integer orbit = (Integer)constraints;

addLayoutComponent(c, orbit.intValue());

}

}

/**

* Adds a component to the specified orbit.

* @param comp The component to be added.

* @param orbit The orbit to add the component to.

* @see OrbitLayout#CENTER

*/

public void addLayoutComponent (Component comp, int orbit) {

if (orbit < 0)<br /> throw new IllegalArgumentException("Orbit must be non-negative.");<br /> /* Special Case: The orbit is actually the center. */<br /> if (orbit == CENTER) {<br /> center = comp;<br /> // sets orbits[0] to an OrbitList containing only the center<br /> OrbitList o = new OrbitList(0);<br /> o.add(center);<br /> orbits.set(0, o);<br /> invalidateAllOrbits();<br /> return;<br /> }<br /> /**<br /> * If the new orbit is higher than the outerOrbit, allocate a new ArrayList for<br /> * the new orbit and all intervening orbits. This keeps the orbits List from<br /> * having null slots for empty orbits. This will make iteration easier.<br /> */<br /> if (orbit > outerOrbit) {

for (int i = outerOrbit + 1; i <= orbit; i++)<br /> orbits.add(new OrbitList(i));<br /> outerOrbit = orbit;<br /> }<br /> // Adds the component to its correct orbit.<br /> ((OrbitList)orbits.get(orbit)).add(comp);<br /> // Forces resizing of all external orbits.<br /> invalidateOrbits(orbit);<br /> }<br /> /**<br /> * Removes a component from the layout.<br /> * @param comp The component to be removed from this layout.<br /> */<br /> public void removeLayoutComponent (Component comp) {<br /> if (comp == center) {<br /> center = null;<br /> ((OrbitList)orbits.get(0)).clear();<br /> invalidateAllOrbits();<br /> return;<br /> }<br /> for (Iterator i = orbits.iterator(); i.hasNext(); ) {<br /> OrbitList orb = (OrbitList)i.next();<br /> for (Iterator j = orb.iterator(); j.hasNext(); ) {<br /> Component c = (Component)j.next();<br /> if (c == comp) {<br /> int currentOrbit = orbits.indexOf(orb);<br /> invalidateOrbits(currentOrbit);<br /> j.remove();<br /> return;<br /> }<br /> }<br /> }<br /> }<br /> /**<br /> * =================================<br /> * NEED TO FIX<br /> * ================================<br /> */<br /> public Dimension preferredLayoutSize(Container target) {<br /> // Not sure what to do here, yet.<br /> return target.getPreferredSize();<br /> }<br /> /**<br /> * ===============================<br /> * NEED TO FIX<br /> * ===========================<br /> */<br /> public Dimension minimumLayoutSize(Container target) {<br /> // Not sure what to do here, yet<br /> return target.getMinimumSize();<br /> }<br /> /**<br /> * The maximum size for this layout manager.<br /> * @return the max size for this layout manager.<br /> */<br /> public Dimension maximumLayoutSize(Container target) {<br /> return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);<br /> }<br /> /**<br /> * Returns the alignment along the x axis. This specifies how<br /> * the component would like to be aligned relative to other<br /> * components. The value should be a number between 0 and 1<br /> * where 0 represents alignment along the origin, 1 is aligned<br /> * the furthest away from the origin, 0.5 is centered, etc.<br /> */<br /> public float getLayoutAlignmentX(Container parent) {<br /> return 0.5f;<br /> }<br /> /**<br /> * Returns the alignment along the y axis. This specifies how<br /> * the component would like to be aligned relative to other<br /> * components. The value should be a number between 0 and 1<br /> * where 0 represents alignment along the origin, 1 is aligned<br /> * the furthest away from the origin, 0.5 is centered, etc.<br /> */<br /> public float getLayoutAlignmentY(Container parent) {<br /> return 0.5f;<br /> }<br /> /**<br /> * Invalidates the layout, flushing cached data.<br /> */<br /> public void invalidateLayout(Container parent) {<br /> /* Nothing to do here...I think. */<br /> }<br /> /**<br /> * Lays out the components in the specified container.<br /> * @param parent the container which needs to be laid out.<br /> */<br /> public void layoutContainer (Container parent) {<br /> Insets insets = parent.getInsets();<br /> // In order to make calculations easy, we will assume that (0,0)<br /> // is at the center of the container. We can do conversions at the end.<br /> Point origin = new Point();<br /> origin.x = (parent.getWidth() - insets.left - insets.right) / 2;<br /> origin.y = (parent.getHeight() - insets.top - insets.bottom) / 2;<br /> for (Iterator i = orbits.iterator(); i.hasNext(); ) {<br /> OrbitList currentOrb = (OrbitList)i.next();<br /> if (currentOrb.size() != 0) {<br /> // Calculate the angle in radians between the centerpoints of each<br /> // component. Since there are 2*pi radians in a circle, to evenly space<br /> // the components, we just space them by 2*pi/# of components.<br /> // Additional spacing (satGap) must be added later<br /> double spaceAngle = (Math.PI * 2) / currentOrb.size();<br /> // A multiplier for the spaceAngle. The first component will be placed<br /> // at Theta=0, the second at Theta=(spaceAngle), the third at<br /> // Theta=(spaceAngle * 2), etc.<br /> int angleMult = 0;<br /> for (Iterator j = currentOrb.iterator(); j.hasNext(); ) {<br /> Component c = (Component)j.next();<br /> polarLayout(c, currentOrb.getRadius(), spaceAngle * angleMult, origin);<br /> angleMult++;<br /> }<br /> }<br /> }<br /> }<br /> /**<br /> * Lays out a single component in the container using polar to cartesian<br /> * coordinate conversion.<br /> * @param c The component to be laid out.<br /> * @param radius The radius of the centerpoint of the component.<br /> * @param theta The angle from the origin (center of container) of the centerpoint<br /> * of the component.<br /> * @param origin The centerpoint of the container.<br /> */<br /> private void polarLayout(Component c, int radius, double theta, Point origin) {<br /> Dimension d = c.getPreferredSize();<br /> // Determine the centerpoint for the component in Cartesian coordinates<br /> // using the standard formula for polar -> Cartesian conversion.

Point centerpoint = new Point();

centerpoint.x = (int)(radius * Math.cos(theta)) + origin.x;

centerpoint.y = (int)(radius * Math.sin(theta)) + origin.y;

// Use the preferred size and the centerpoint to calculate the top left

// corner of where the component should be laid out.

Rectangle bounds = new Rectangle();

bounds.x = centerpoint.x - (d.width / 2);

bounds.y = centerpoint.y - (d.height / 2);

bounds.width = d.width;

bounds.height = d.height;

c.setBounds(bounds);

}

/**

* Invalidates all orbits at and above a specified orbit.

* This will force a recalculation at the next sizing or layout.

* This method is called when an orbit is changed (satellite added or removed).

* Each orbit above a changed orbit must also be recalculated, so this invalidates

* all orbits above.

*/

private void invalidateOrbits(int orb) {

for (ListIterator i = orbits.listIterator(orb); i.hasNext(); ) {

OrbitList o = (OrbitList)i.next();

o.invalidate();

}

}

/**

* Invalidates the radius of every orbit.

* @see invalidateRadii(int)

*/

private void invalidateAllOrbits() {

invalidateOrbits(0);

}

/**

* Draws circles to represent the orbits.

* @param parent The container that is being laid out.

*/

public void drawOrbits(Container parent) {

Insets insets = parent.getInsets();

Graphics g = parent.getGraphics();

// In order to make calculations easy, we will assume that (0,0)

// is at the center of the container. We can do conversions at the end.

Point origin = new Point();

origin.x = (parent.getWidth() - insets.left - insets.right) / 2;

origin.y = (parent.getHeight() - insets.top - insets.bottom) / 2;

for (Iterator i = orbits.iterator(); i.hasNext(); ) {

OrbitList currentOrb = (OrbitList)i.next();

int r = currentOrb.getRadius();

Point topLeft = new Point();

topLeft.x = origin.x - r;

topLeft.y = origin.y - r;

g.drawOval(topLeft.x, topLeft.y, r * 2, r * 2);

}

// Draw a small orbit for the centerpoint

g.fillOval(origin.x - 2, origin.y - 2, 4, 4);

}

/**

*

A class to represent each orbit. Each OrbitList is a list of satellites,

* plus the radius at which these satellites should be placed. In order to cut

* down execution time, it will not recalculate radius and maxOverlap each time an element is added.

*

*

Since radius is a function of the radii of closer

* orbits, this sets up a recursive relation. In order to escape the nasty time requirements

* of recursive relations, I made it possible to only recalculate the radii of those orbits

* which are invalid.

*

*

It is important to remember that when adding or removing satellites from an

* orbit, the orbit MUST BE invalidated. This will prevent resizing problems.

*

*

NOTE: This is not for a list of orbits, it is to represent a single orbit.

*/

class OrbitList extends ArrayList {

/**

* The orbit that this list represents.

*/

private int orbit;

/**

* The radius of current orbit in pixels.

*/

private int radius;

/**

* This is 1/2 the length of the main diagonal of the largest component in this orbit.

* This is important because this is the maximum amount that extends past an

* orbit. Therefore, if orbits are spaced at least that much, there will be

* no overlap.

*/

private int maxOverlap;

/**

* Tells whether the current radius and maxOverlap is correct.

*/

private boolean valid;

/**

* Creates an empty OrbitList without setting a correct radius. This will

* be done later, during calls to <code>getRadius()</code>.

* @param orbit The orbit to be represented.

*/

public OrbitList(int orbit) {

super();

this.orbit = orbit;

maxOverlap = 0;

radius = 0;

valid = false;

}

/**

* Gets the current valid radius. If the current radius is invalid, it computes

* and returns the valid one.

* NOTE: This is not a simple O(1) get method. There may be time consuming

* calculations involved. I tried to make it as efficient as possible.

* @return The current valid radius.

*/

public int getRadius() {

calculateRadius();

return radius;

}

/**

* Returns the maximum overlap.

* @return The maximum overlap of this orbit.

*/

public int getMaxOverlap() {

if (!isValid())

calculateOverlap();

return maxOverlap;

}

/**

* Calculates and sets the correct radius.

*/

private void calculateRadius() {

// If valid, don't do anything.

if (isValid())

return;

else if (orbit == 0) {

radius = 0;

}

else {

OrbitList previous = (OrbitList)orbits.get(orbit - 1);

radius = previous.getRadius() + previous.getMaxOverlap() + this.getMaxOverlap() + orbitGap;

}

valid = true;

}

/**

* Calculates and sets the maximum overlap of this orbit. This is 1/2 the length

* of the main diagonal of the largest component. This is important because

* this is the maximum amount that extends past an orbit. Therefore, if orbits

* are spaced at least that much, there will be no overlap.

*/

private void calculateOverlap () {

maxOverlap = 0;

for (Iterator i = this.iterator(); i.hasNext(); ) {

Component c = (Component)i.next();

Dimension d = c.getPreferredSize();

int w = d.width;

int h = d.height;

int over = (int)(Math.sqrt((w * w) + (h * h)) / 2);

maxOverlap = Math.max(maxOverlap, over);

}

}

/**

* Invalidates the current orbit, forcing radius and maxOverlap to be recalculated

* during the next call to <code>getRadius()</code> or <code>getMaxOverlap()</code>.

*/

public void invalidate() {

valid = false;

}

/**

* Determines if the current radius and maxOverlap are valid.

* @return true if the radius and maxOverlap are valid, false otherwise.

*/

public boolean isValid() {

return valid;

}

}

}

</code>

To add components, use the following method:

Container con;

Componenent com;

// initialize them, etc

con.setLayout(new OrbitLayout(0,25); // for some spacing

int orbit = (choose an orbit here from 0 to n, where 0 is the center)

con.add(com, new Integer(orbit));

Try adding a few components (buttons work best) to a few orbits (although the center orbit 0 can only hold 1 component).

Please tell me what you think!!!

Micah

<code>

import java.util.*;

import java.awt.*;

/**

* This layout manager is used to display components in concentric circles surrounding

* a central component (or no central component). For the purposes of this

* source file, each circle is an orbit and the components are satellites.

*

* @author Micah Wedemeyer

* @version 1.0

*/

public class OrbitLayout implements LayoutManager2 {

/**

* A constant used to deliniate the central component that all other components orbit.

*/

final public static int CENTER = 0;

/**

* The minimum spacing between components in the same orbit.

*/

private int satGap;

/**

* The minimum spacing between each orbit.

*/

private int orbitGap;

/**

* The current outer-most orbit.

*/

private int outerOrbit;

/**

* A list of orbits. Each element in the list is a list itself of satellites

* in that orbit. The center object is at orbits[0],

* the first orbit at orbits[1], and the nth at orbits[n].

*/

private ArrayList orbits;

/**

* A central component that all the satellites orbit

*/

private Component center;

/**

* Creates an OrbitLayout with the specified spacing.

* @param satGap The minimum distance between satellites in the same orbit.

* @param orbitGap The minimum distance between concentric orbits.

*/

public OrbitLayout(int satGap, int orbitGap) {

this.satGap = satGap;

this.orbitGap = orbitGap;

outerOrbit = 0;

center = null;

orbits = new ArrayList();

orbits.add(new OrbitList(0));

}

/**

* Does not use, must be overridden for interface.

*/

public void addLayoutComponent(String s, Component c) {

}

/**

* Adds a component to the layout.

* @param c The component to be added.

* @param constraints (an Integer object) The orbit to add the component to.

*/

public void addLayoutComponent(Component c, Object constraints) {

if (constraints instanceof Integer) {

Integer orbit = (Integer)constraints;

addLayoutComponent(c, orbit.intValue());

}

}

/**

* Adds a component to the specified orbit.

* @param comp The component to be added.

* @param orbit The orbit to add the component to.

* @see OrbitLayout#CENTER

*/

public void addLayoutComponent (Component comp, int orbit) {

if (orbit < 0)<br /> throw new IllegalArgumentException("Orbit must be non-negative.");<br /> /* Special Case: The orbit is actually the center. */<br /> if (orbit == CENTER) {<br /> center = comp;<br /> // sets orbits[0] to an OrbitList containing only the center<br /> OrbitList o = new OrbitList(0);<br /> o.add(center);<br /> orbits.set(0, o);<br /> invalidateAllOrbits();<br /> return;<br /> }<br /> /**<br /> * If the new orbit is higher than the outerOrbit, allocate a new ArrayList for<br /> * the new orbit and all intervening orbits. This keeps the orbits List from<br /> * having null slots for empty orbits. This will make iteration easier.<br /> */<br /> if (orbit > outerOrbit) {

for (int i = outerOrbit + 1; i <= orbit; i++)<br /> orbits.add(new OrbitList(i));<br /> outerOrbit = orbit;<br /> }<br /> // Adds the component to its correct orbit.<br /> ((OrbitList)orbits.get(orbit)).add(comp);<br /> // Forces resizing of all external orbits.<br /> invalidateOrbits(orbit);<br /> }<br /> /**<br /> * Removes a component from the layout.<br /> * @param comp The component to be removed from this layout.<br /> */<br /> public void removeLayoutComponent (Component comp) {<br /> if (comp == center) {<br /> center = null;<br /> ((OrbitList)orbits.get(0)).clear();<br /> invalidateAllOrbits();<br /> return;<br /> }<br /> for (Iterator i = orbits.iterator(); i.hasNext(); ) {<br /> OrbitList orb = (OrbitList)i.next();<br /> for (Iterator j = orb.iterator(); j.hasNext(); ) {<br /> Component c = (Component)j.next();<br /> if (c == comp) {<br /> int currentOrbit = orbits.indexOf(orb);<br /> invalidateOrbits(currentOrbit);<br /> j.remove();<br /> return;<br /> }<br /> }<br /> }<br /> }<br /> /**<br /> * =================================<br /> * NEED TO FIX<br /> * ================================<br /> */<br /> public Dimension preferredLayoutSize(Container target) {<br /> // Not sure what to do here, yet.<br /> return target.getPreferredSize();<br /> }<br /> /**<br /> * ===============================<br /> * NEED TO FIX<br /> * ===========================<br /> */<br /> public Dimension minimumLayoutSize(Container target) {<br /> // Not sure what to do here, yet<br /> return target.getMinimumSize();<br /> }<br /> /**<br /> * The maximum size for this layout manager.<br /> * @return the max size for this layout manager.<br /> */<br /> public Dimension maximumLayoutSize(Container target) {<br /> return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);<br /> }<br /> /**<br /> * Returns the alignment along the x axis. This specifies how<br /> * the component would like to be aligned relative to other<br /> * components. The value should be a number between 0 and 1<br /> * where 0 represents alignment along the origin, 1 is aligned<br /> * the furthest away from the origin, 0.5 is centered, etc.<br /> */<br /> public float getLayoutAlignmentX(Container parent) {<br /> return 0.5f;<br /> }<br /> /**<br /> * Returns the alignment along the y axis. This specifies how<br /> * the component would like to be aligned relative to other<br /> * components. The value should be a number between 0 and 1<br /> * where 0 represents alignment along the origin, 1 is aligned<br /> * the furthest away from the origin, 0.5 is centered, etc.<br /> */<br /> public float getLayoutAlignmentY(Container parent) {<br /> return 0.5f;<br /> }<br /> /**<br /> * Invalidates the layout, flushing cached data.<br /> */<br /> public void invalidateLayout(Container parent) {<br /> /* Nothing to do here...I think. */<br /> }<br /> /**<br /> * Lays out the components in the specified container.<br /> * @param parent the container which needs to be laid out.<br /> */<br /> public void layoutContainer (Container parent) {<br /> Insets insets = parent.getInsets();<br /> // In order to make calculations easy, we will assume that (0,0)<br /> // is at the center of the container. We can do conversions at the end.<br /> Point origin = new Point();<br /> origin.x = (parent.getWidth() - insets.left - insets.right) / 2;<br /> origin.y = (parent.getHeight() - insets.top - insets.bottom) / 2;<br /> for (Iterator i = orbits.iterator(); i.hasNext(); ) {<br /> OrbitList currentOrb = (OrbitList)i.next();<br /> if (currentOrb.size() != 0) {<br /> // Calculate the angle in radians between the centerpoints of each<br /> // component. Since there are 2*pi radians in a circle, to evenly space<br /> // the components, we just space them by 2*pi/# of components.<br /> // Additional spacing (satGap) must be added later<br /> double spaceAngle = (Math.PI * 2) / currentOrb.size();<br /> // A multiplier for the spaceAngle. The first component will be placed<br /> // at Theta=0, the second at Theta=(spaceAngle), the third at<br /> // Theta=(spaceAngle * 2), etc.<br /> int angleMult = 0;<br /> for (Iterator j = currentOrb.iterator(); j.hasNext(); ) {<br /> Component c = (Component)j.next();<br /> polarLayout(c, currentOrb.getRadius(), spaceAngle * angleMult, origin);<br /> angleMult++;<br /> }<br /> }<br /> }<br /> }<br /> /**<br /> * Lays out a single component in the container using polar to cartesian<br /> * coordinate conversion.<br /> * @param c The component to be laid out.<br /> * @param radius The radius of the centerpoint of the component.<br /> * @param theta The angle from the origin (center of container) of the centerpoint<br /> * of the component.<br /> * @param origin The centerpoint of the container.<br /> */<br /> private void polarLayout(Component c, int radius, double theta, Point origin) {<br /> Dimension d = c.getPreferredSize();<br /> // Determine the centerpoint for the component in Cartesian coordinates<br /> // using the standard formula for polar -> Cartesian conversion.

Point centerpoint = new Point();

centerpoint.x = (int)(radius * Math.cos(theta)) + origin.x;

centerpoint.y = (int)(radius * Math.sin(theta)) + origin.y;

// Use the preferred size and the centerpoint to calculate the top left

// corner of where the component should be laid out.

Rectangle bounds = new Rectangle();

bounds.x = centerpoint.x - (d.width / 2);

bounds.y = centerpoint.y - (d.height / 2);

bounds.width = d.width;

bounds.height = d.height;

c.setBounds(bounds);

}

/**

* Invalidates all orbits at and above a specified orbit.

* This will force a recalculation at the next sizing or layout.

* This method is called when an orbit is changed (satellite added or removed).

* Each orbit above a changed orbit must also be recalculated, so this invalidates

* all orbits above.

*/

private void invalidateOrbits(int orb) {

for (ListIterator i = orbits.listIterator(orb); i.hasNext(); ) {

OrbitList o = (OrbitList)i.next();

o.invalidate();

}

}

/**

* Invalidates the radius of every orbit.

* @see invalidateRadii(int)

*/

private void invalidateAllOrbits() {

invalidateOrbits(0);

}

/**

* Draws circles to represent the orbits.

* @param parent The container that is being laid out.

*/

public void drawOrbits(Container parent) {

Insets insets = parent.getInsets();

Graphics g = parent.getGraphics();

// In order to make calculations easy, we will assume that (0,0)

// is at the center of the container. We can do conversions at the end.

Point origin = new Point();

origin.x = (parent.getWidth() - insets.left - insets.right) / 2;

origin.y = (parent.getHeight() - insets.top - insets.bottom) / 2;

for (Iterator i = orbits.iterator(); i.hasNext(); ) {

OrbitList currentOrb = (OrbitList)i.next();

int r = currentOrb.getRadius();

Point topLeft = new Point();

topLeft.x = origin.x - r;

topLeft.y = origin.y - r;

g.drawOval(topLeft.x, topLeft.y, r * 2, r * 2);

}

// Draw a small orbit for the centerpoint

g.fillOval(origin.x - 2, origin.y - 2, 4, 4);

}

/**

*

A class to represent each orbit. Each OrbitList is a list of satellites,

* plus the radius at which these satellites should be placed. In order to cut

* down execution time, it will not recalculate radius and maxOverlap each time an element is added.

*

*

Since radius is a function of the radii of closer

* orbits, this sets up a recursive relation. In order to escape the nasty time requirements

* of recursive relations, I made it possible to only recalculate the radii of those orbits

* which are invalid.

*

*

It is important to remember that when adding or removing satellites from an

* orbit, the orbit MUST BE invalidated. This will prevent resizing problems.

*

*

NOTE: This is not for a list of orbits, it is to represent a single orbit.

*/

class OrbitList extends ArrayList {

/**

* The orbit that this list represents.

*/

private int orbit;

/**

* The radius of current orbit in pixels.

*/

private int radius;

/**

* This is 1/2 the length of the main diagonal of the largest component in this orbit.

* This is important because this is the maximum amount that extends past an

* orbit. Therefore, if orbits are spaced at least that much, there will be

* no overlap.

*/

private int maxOverlap;

/**

* Tells whether the current radius and maxOverlap is correct.

*/

private boolean valid;

/**

* Creates an empty OrbitList without setting a correct radius. This will

* be done later, during calls to <code>getRadius()</code>.

* @param orbit The orbit to be represented.

*/

public OrbitList(int orbit) {

super();

this.orbit = orbit;

maxOverlap = 0;

radius = 0;

valid = false;

}

/**

* Gets the current valid radius. If the current radius is invalid, it computes

* and returns the valid one.

* NOTE: This is not a simple O(1) get method. There may be time consuming

* calculations involved. I tried to make it as efficient as possible.

* @return The current valid radius.

*/

public int getRadius() {

calculateRadius();

return radius;

}

/**

* Returns the maximum overlap.

* @return The maximum overlap of this orbit.

*/

public int getMaxOverlap() {

if (!isValid())

calculateOverlap();

return maxOverlap;

}

/**

* Calculates and sets the correct radius.

*/

private void calculateRadius() {

// If valid, don't do anything.

if (isValid())

return;

else if (orbit == 0) {

radius = 0;

}

else {

OrbitList previous = (OrbitList)orbits.get(orbit - 1);

radius = previous.getRadius() + previous.getMaxOverlap() + this.getMaxOverlap() + orbitGap;

}

valid = true;

}

/**

* Calculates and sets the maximum overlap of this orbit. This is 1/2 the length

* of the main diagonal of the largest component. This is important because

* this is the maximum amount that extends past an orbit. Therefore, if orbits

* are spaced at least that much, there will be no overlap.

*/

private void calculateOverlap () {

maxOverlap = 0;

for (Iterator i = this.iterator(); i.hasNext(); ) {

Component c = (Component)i.next();

Dimension d = c.getPreferredSize();

int w = d.width;

int h = d.height;

int over = (int)(Math.sqrt((w * w) + (h * h)) / 2);

maxOverlap = Math.max(maxOverlap, over);

}

}

/**

* Invalidates the current orbit, forcing radius and maxOverlap to be recalculated

* during the next call to <code>getRadius()</code> or <code>getMaxOverlap()</code>.

*/

public void invalidate() {

valid = false;

}

/**

* Determines if the current radius and maxOverlap are valid.

* @return true if the radius and maxOverlap are valid, false otherwise.

*/

public boolean isValid() {

return valid;

}

}

}

</code>

It is sorta covered in the JavaRanch Style Guide. |