import java.awt.*; import java.awt.LayoutManager; /** * TiltLayout packs RLabels tightly together, tilting * them if necessary to fill available space. */ public class TiltLayout implements LayoutManager { public void addLayoutComponent(String name, Component comp) { } public void removeLayoutComponent(Component comp) { } public Dimension minimumLayoutSize(Container parent) { return new Dimension(0, 0); } public Dimension preferredLayoutSize(Container parent) { return new Dimension(500, 500); } public void layoutContainer(Container parent) { // compute the necessary tilt angle double angle = getTiltAngle(parent); // your code here } private double getTiltAngle(Container parent) { double parentWidth = parent.getWidth(); int n = parent.getComponentCount(); double totalHeight = 0; double totalWidth = 0; double lastWidth = 0; double firstHeight = 0; for (int i = 0; i < n; ++i) { RLabel label = (RLabel) parent.getComponent(i); double oldAngle = label.getAngle(); label.setAngle(0); Dimension size = label.getPreferredSize(); label.setAngle(oldAngle); if (i == 0) { firstHeight = size.height; } totalHeight += size.height; totalWidth += size.width; if (i == n-1) { lastWidth = size.width; } } // special case: parent is wide enough if (parentWidth >= totalWidth) { // lay out horizontally return 0; } // special case: parent is too narrow if (parentWidth <= totalHeight) { // lay out vertically return Math.PI/2; } // parentWidth = (totalHeight-firstHeight)/sin(angle) // + firstHeight*sin(angle) // + lastWidth*cos(angle) // // Solve for angle using Newton's method. Find root of // f(angle) = parentWidth // - (totalHeight-firstHeight)/sin(angle) // - firstHeight*sin(angle) // - lastWidth*cos(angle) // // f'(angle) = (totalHeight-firstHeight)*cos(angle)/sin^2(angle) // - firstHeight*cos(angle) // + lastWidth*sin(angle) // // start with a good guess: double angle = Math.asin(totalHeight / parentWidth); // limit the number of iterations for (int i=0; i<5; ++i) { System.err.println("guessing angle " + angle); double sinAngle = Math.sin(angle); double cosAngle = Math.cos(angle); // compute f(angle) double f = parentWidth - (totalHeight-firstHeight)/sinAngle - firstHeight*sinAngle - lastWidth*cosAngle; if (Math.abs(f) < 0.5) { // we're within a pixel of parentWidth; good enough return angle; } // Newton's method: improve angle by adding -f(angle)/f'(angle) double df = (totalHeight-firstHeight)*cosAngle/(sinAngle*sinAngle) - firstHeight*cosAngle + lastWidth*sinAngle; angle += -f / df; } return angle; } }