001: /* ====================================================================
002: * The JRefactory License, Version 1.0
003: *
004: * Copyright (c) 2001 JRefactory. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution,
019: * if any, must include the following acknowledgment:
020: * "This product includes software developed by the
021: * JRefactory (http://www.sourceforge.org/projects/jrefactory)."
022: * Alternately, this acknowledgment may appear in the software itself,
023: * if and wherever such third-party acknowledgments normally appear.
024: *
025: * 4. The names "JRefactory" must not be used to endorse or promote
026: * products derived from this software without prior written
027: * permission. For written permission, please contact seguin@acm.org.
028: *
029: * 5. Products derived from this software may not be called "JRefactory",
030: * nor may "JRefactory" appear in their name, without prior written
031: * permission of Chris Seguin.
032: *
033: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
034: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
035: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
036: * DISCLAIMED. IN NO EVENT SHALL THE CHRIS SEGUIN OR
037: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
038: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
039: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
040: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
041: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
042: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
043: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
044: * SUCH DAMAGE.
045: * ====================================================================
046: *
047: * This software consists of voluntary contributions made by many
048: * individuals on behalf of JRefactory. For more information on
049: * JRefactory, please see
050: * <http://www.sourceforge.org/projects/jrefactory>.
051: */
052: package org.acm.seguin.uml;
053:
054: import java.awt.Color;
055: import java.awt.Component;
056: import java.awt.Dimension;
057: import java.awt.Graphics;
058: import java.awt.Point;
059: import java.awt.Rectangle;
060: import java.awt.print.PageFormat;
061: import java.io.File;
062: import java.io.FileWriter;
063: import java.io.IOException;
064: import java.io.Reader;
065: import java.io.PrintWriter;
066: import java.util.Iterator;
067: import java.util.Map;
068: import java.util.HashMap;
069: import java.util.Random;
070: import javax.swing.JScrollBar;
071: import javax.swing.JScrollPane;
072: import javax.swing.JViewport;
073: import org.acm.seguin.awt.ExceptionPrinter;
074: import org.acm.seguin.ide.common.ClassListPanel;
075: import org.acm.seguin.io.Saveable;
076: import org.acm.seguin.summary.TypeSummary;
077: import org.acm.seguin.summary.PackageSummary;
078: import org.acm.seguin.uml.line.AssociationRelationship;
079: import org.acm.seguin.uml.line.LineMouseAdapter;
080: import org.acm.seguin.uml.line.LinedPanel;
081: import org.acm.seguin.uml.line.SegmentedLine;
082: import org.acm.seguin.uml.line.Vertex;
083: import org.acm.seguin.uml.print.UMLPagePrinter;
084: import org.acm.seguin.util.FileSettings;
085: import org.acm.seguin.util.MissingSettingsException;
086: import org.acm.seguin.uml.line.ImplementsRelationship;
087: import org.acm.seguin.uml.line.InheretenceRelationship;
088: import org.acm.seguin.uml.line.EndPointPanel;
089:
090: /**
091: * Draws a UML diagram for all the classes in a package
092: *
093: *@author Chris Seguin
094: *@author <a href="JRefactory@ladyshot.demon.co.uk">Mike Atkinson</a>
095: *@version $Id: UMLPackage.java,v 1.6 2003/09/01 00:25:32 mikeatkinson Exp $
096: *@created September 12, 2001
097: */
098: public class UMLPackage extends LinedPanel implements Saveable {
099: // Instance Variables
100: private PackageSummary summary;
101: private SegmentedLine currentLine = null;
102: private boolean hasChanged;
103: private String packageName;
104: private JScrollPane scrollPane;
105: private ClassListPanel classListPanel = null;
106: private boolean first = false;
107: private boolean loading = false;
108: private double scale = 1.0;
109:
110: /**
111: * Constructor for UMLPackage
112: *
113: *@param packageSummary the summary of the package
114: */
115: public UMLPackage(PackageSummary packageSummary) {
116: setSummary(packageSummary);
117: initialise(new PackageLoader(this , summary));
118: }
119:
120: /**
121: * Constructor for UMLPackage
122: *
123: *@param filename the name of the file
124: */
125: public UMLPackage(String filename) {
126: initialise(new PackageLoader(this , filename));
127: }
128:
129: /**
130: * Constructor for UMLPackage
131: *
132: *@param input the input stream
133: */
134: public UMLPackage(Reader input) {
135: initialise(new PackageLoader(this , input));
136: }
137:
138: private void initialise(PackageLoader loader) {
139: // Initialize the instance variables
140: defaultValues();
141:
142: // Don't use a layout manager
143: setLayout(null);
144:
145: // Reset the size
146: setSize(getPreferredSize());
147:
148: addMouseAdapter();
149:
150: // Load the summaries
151: new Thread(loader).start();
152: }
153:
154: /**
155: * Sets the Dirty attribute of the UMLPackage object
156: */
157: public void setDirty() {
158: hasChanged = true;
159: }
160:
161: /**
162: * Sets the ScrollPane attribute of the UMLPackage object
163: *
164: *@param value The new ScrollPane value
165: */
166: public void setScrollPane(JScrollPane value) {
167: scrollPane = value;
168: }
169:
170: /**
171: * Sets the class list panel
172: *
173: *@param value the new list
174: */
175: public void setClassListPanel(ClassListPanel value) {
176: classListPanel = value;
177: first = true;
178: }
179:
180: /**
181: * Sets the loading value
182: *
183: *@param value The new Loading value
184: */
185: public void setLoading(boolean value) {
186: loading = value;
187: }
188:
189: /**
190: * Gets the PackageName attribute of the UMLPackage object
191: *
192: *@return The PackageName value
193: */
194: public String getPackageName() {
195: return packageName;
196: }
197:
198: /**
199: * Get the components that are UMLTypes
200: *
201: *@return Description of the Returned Value
202: */
203: public UMLType[] getTypes() {
204: // Instance Variables
205: Component[] children = getComponents();
206: int last = children.length;
207: int count = 0;
208:
209: // Count the UMLTypes
210: for (int ndx = 0; ndx < last; ndx++) {
211: if (children[ndx] instanceof UMLType) {
212: count++;
213: }
214: }
215:
216: // Count the UMLTypes
217: UMLType[] results = new UMLType[count];
218: int item = 0;
219: for (int ndx = 0; ndx < last; ndx++) {
220: if (children[ndx] instanceof UMLType) {
221: results[item] = (UMLType) children[ndx];
222: item++;
223: }
224: }
225:
226: // Return the result
227: return results;
228: }
229:
230: /**
231: * Sets the scale and resizes the panel.
232: *
233: *@param value Description of Parameter
234: */
235: public void scale(double value) {
236: super .scale(value);
237: this .scale = value;
238: setSize(getPreferredSize());
239: }
240:
241: /**
242: * Returns the minimum size
243: *
244: *@return The size
245: */
246: public Dimension getMinimumSize() {
247: return getPreferredSize();
248: }
249:
250: /**
251: * Returns the preferred size
252: *
253: *@return The size
254: */
255: public Dimension getPreferredSize() {
256: // Initialize local variables
257: int wide = 10;
258: int high = 10;
259: Component[] children = getComponents();
260: int last = children.length;
261:
262: // Deselect the children
263: for (int ndx = 0; ndx < last; ndx++) {
264: Rectangle bounds = children[ndx].getBounds();
265: wide = Math.max(wide, 20 + bounds.x + bounds.width);
266: high = Math.max(high, 20 + bounds.y + bounds.height);
267: }
268:
269: return new Dimension(wide, high);
270: }
271:
272: /**
273: * Get the summary
274: *
275: *@return The package summary
276: */
277: public PackageSummary getSummary() {
278: return summary;
279: }
280:
281: /**
282: * Gets the File attribute of the UMLPackage object
283: *
284: *@return The File value
285: */
286: public File getFile() {
287: return PackageLoader.getFile(this );
288: }
289:
290: /**
291: * Gets the Dirty attribute of the UMLPackage object
292: *
293: *@return The Dirty value
294: */
295: public boolean isDirty() {
296: return hasChanged;
297: }
298:
299: /**
300: * Determines the title
301: *
302: *@return the title
303: */
304: public String getTitle() {
305: return ("UML Diagram for " + packageName);
306: }
307:
308: /**
309: * Remove the association
310: *
311: *@param field Description of Parameter
312: */
313: public void removeAssociation(UMLField field) {
314: System.out.println("removeAssociation(" + field + ")");
315: EndPointPanel end = null;
316: Iterator iter = getLineIterator();
317: while (iter.hasNext()) {
318: Object next = iter.next();
319: if (next instanceof AssociationRelationship) {
320: AssociationRelationship assoc = (AssociationRelationship) next;
321: if (assoc.getField().equals(field)) {
322: assoc.delete();
323: iter.remove();
324: end = assoc.getEndPanel();
325: System.out.println("endPanel=" + end);
326: break;
327: }
328: }
329: }
330: // check to see whether the end of the association is used elsewhere.
331: boolean found = false;
332: if (end != null) {
333: iter = getLineIterator();
334: while (iter.hasNext()) {
335: Object next = iter.next();
336: if (next instanceof SegmentedLine) {
337: SegmentedLine line = (SegmentedLine) next;
338: if (end == line.getStartPanel()) {
339: System.out.println(" found start line from "
340: + end);
341: found = true;
342: break;
343: } else if (end == line.getEndPanel()) {
344: System.out.println(" found start line from "
345: + end);
346: found = true;
347: break;
348: }
349: }
350: }
351: }
352: // If the end does not occur in any other association then, if it is a Type
353: // and does not belong in this package we can remove it.
354: if (!found) {
355: System.out.println("not found");
356: if (end instanceof UMLType) {
357: System.out.println(" end is of type UMLType");
358: UMLType type = (UMLType) end;
359: TypeSummary ts = (TypeSummary) type.getSourceSummary();
360: PackageSummary ps = ts.getPackageSummary();
361: System.out.println(" ts=" + ts + ", ps=" + ps
362: + ", summary=" + summary);
363: if (ps != summary) {
364: System.out.println(" end.getPackage() == this");
365: remove(end);
366: }
367: }
368: }
369: }
370:
371: /**
372: * Paint this object
373: *
374: *@param g the graphics object
375: */
376: public void paint(Graphics g) {
377: setBackground(Color.lightGray);
378: g.setColor(Color.lightGray);
379: Dimension size = getSize();
380: g.fillRect(0, 0, size.width, size.height);
381:
382: // Draw the grid
383: PageFormat pf = UMLPagePrinter.getPageFormat(false);
384: if (pf != null) {
385: int pageHeight = (int) UMLPagePrinter.getPageHeight();
386: int pageWidth = (int) UMLPagePrinter.getPageWidth();
387:
388: g.setColor(Color.gray);
389: for (int x = pageWidth; x < size.width; x += pageWidth) {
390: g.drawLine(x, 0, x, size.height);
391: }
392:
393: for (int y = pageHeight; y < size.width; y += pageHeight) {
394: g.drawLine(0, y, size.width, y);
395: }
396: }
397:
398: // Abort once we are loading
399: if (loading) {
400: return;
401: }
402:
403: // Draw the segmented lines
404: Iterator iter = getLineIterator();
405: while (iter.hasNext()) {
406: ((SegmentedLine) iter.next()).paint(g);
407: }
408:
409: // Draw the components
410: paintChildren(g);
411: }
412:
413: /**
414: * Print this object
415: *
416: *@param g the graphics object
417: *@param x the x coordinate
418: *@param y the y coordinate
419: */
420: public void print(Graphics g, int x, int y) {
421: Component[] children = getComponents();
422: int last = children.length;
423:
424: for (int ndx = 0; ndx < last; ndx++) {
425: if (children[ndx] instanceof UMLType) {
426: Point pt = children[ndx].getLocation();
427: ((UMLType) children[ndx]).print(g, x + pt.x, y + pt.y);
428: } else if (children[ndx] instanceof UMLLine) {
429: Point pt = children[ndx].getLocation();
430: ((UMLLine) children[ndx]).print(g, x + pt.x, y + pt.y);
431: }
432: }
433:
434: Iterator iter = getLineIterator();
435: while (iter.hasNext()) {
436: ((SegmentedLine) iter.next()).paint(g);
437: }
438: }
439:
440: /**
441: * Reloads the UML class diagrams
442: */
443: public void reload() {
444: // Save the image
445: try {
446: save();
447: } catch (IOException ioe) {
448: ExceptionPrinter.print(ioe, false);
449: }
450:
451: // Reload it
452: PackageLoader loader = new PackageLoader(this , summary);
453: new Thread(loader).start();
454:
455: // Reset the size
456: setSize(getPreferredSize());
457:
458: reset();
459:
460: // Nothing has changed
461: hasChanged = false;
462: }
463:
464: /**
465: * Description of the Method
466: */
467: public void clear() {
468: removeAll();
469: super .clear();
470: }
471:
472: /**
473: * Determine what you hit
474: *
475: *@param actual The hit location
476: */
477: public void hit(Point actual) {
478: currentLine = null;
479: Iterator iter = getLineIterator();
480: while ((currentLine == null) && iter.hasNext()) {
481: SegmentedLine next = (SegmentedLine) iter.next();
482: if (next.hit(actual)) {
483: currentLine = next;
484: }
485: }
486:
487: while (iter.hasNext()) {
488: SegmentedLine next = (SegmentedLine) iter.next();
489: next.select(false);
490: }
491:
492: repaint();
493: }
494:
495: /**
496: * Dragging a segmented line point
497: *
498: *@param actual The mouse's current location
499: */
500: public void drag(Point actual) {
501: if (currentLine != null) {
502: currentLine.drag(actual);
503: repaint();
504: }
505: }
506:
507: /**
508: * User dropped an item
509: */
510: public void drop() {
511: if (currentLine != null) {
512: currentLine.drop();
513:
514: hasChanged = true;
515:
516: currentLine = null;
517: }
518:
519: reset();
520: }
521:
522: /**
523: * Save the files
524: *
525: *@exception IOException Description of Exception
526: */
527: public void save() throws IOException {
528: // Make sure we have something that has changed
529: if (!hasChanged) {
530: return;
531: }
532:
533: // Local Variables
534: Component[] children = getComponents();
535: int last = children.length;
536:
537: File outputFile = PackageLoader.getFile(summary);
538: PrintWriter output = new PrintWriter(new FileWriter(outputFile));
539:
540: output.println("V[1.1:" + summary.getName() + "]");
541:
542: // Save the line segments
543: Iterator iter = getLineIterator();
544: while (iter.hasNext()) {
545: ((SegmentedLine) iter.next()).save(output);
546: }
547:
548: // Save the types
549: for (int ndx = 0; ndx < last; ndx++) {
550: if (children[ndx] instanceof UMLType) {
551: ((UMLType) children[ndx]).save(output);
552: }
553: }
554:
555: output.close();
556:
557: // Nothing has changed
558: hasChanged = false;
559: }
560:
561: /**
562: * Tells the scrollbar to jump to this location
563: *
564: *@param type Description of Parameter
565: */
566: public void jumpTo(TypeSummary type) {
567: UMLType umlType = findType(type);
568: if (umlType == null) {
569: return;
570: }
571:
572: Point pt = umlType.getLocation();
573:
574: JScrollBar horiz = scrollPane.getHorizontalScrollBar();
575: horiz.setValue(pt.x - 10);
576: JScrollBar vert = scrollPane.getVerticalScrollBar();
577: vert.setValue(pt.y - 10);
578: }
579:
580: /**
581: * Find the type based on a summary
582: *
583: *@param searching the variable we are searching for
584: *@return the UML type object
585: */
586: protected UMLType findType(TypeSummary searching) {
587: // Instance Variables
588: Component[] children = getComponents();
589: int last = children.length;
590: int count = 0;
591: TypeSummary current;
592:
593: if (searching == null) {
594: return null;
595: }
596:
597: // Count the UMLTypes
598: for (int ndx = 0; ndx < last; ndx++) {
599: if (children[ndx] instanceof UMLType) {
600: current = ((UMLType) children[ndx]).getSummary();
601: if (searching.equals(current)) {
602: return (UMLType) children[ndx];
603: }
604: }
605: }
606:
607: // Not found
608: return null;
609: }
610:
611: /**
612: * Find the type based on a id code
613: *
614: *@param id the code we are searching for
615: *@return the UML type object
616: */
617: protected UMLType find(String id) {
618: // Instance Variables
619: Component[] children = getComponents();
620: int last = children.length;
621: int count = 0;
622: String current;
623:
624: if (id == null) {
625: return null;
626: }
627:
628: // Find the id that matches
629: for (int ndx = 0; ndx < last; ndx++) {
630: if (children[ndx] instanceof UMLType) {
631: current = ((UMLType) children[ndx]).getID();
632: if (id.equals(current)) {
633: return (UMLType) children[ndx];
634: }
635: }
636: }
637:
638: // Not found
639: return null;
640: }
641:
642: /**
643: * Find the type based on a id code
644: *
645: *@param panel1 Description of Parameter
646: *@param panel2 Description of Parameter
647: *@return the UML type object
648: */
649: protected SegmentedLine find(String panel1, String panel2) {
650: UMLType first = find(panel1);
651: UMLType second = find(panel2);
652:
653: if ((first == null) || (second == null)) {
654: return null;
655: }
656:
657: Iterator iter = getLineIterator();
658: while (iter.hasNext()) {
659: SegmentedLine line = (SegmentedLine) iter.next();
660: if (line.match(first, second)) {
661: return line;
662: }
663: }
664:
665: return null;
666: }
667:
668: /**
669: * Sets the summary
670: *
671: *@param value The package summary
672: */
673: void setSummary(PackageSummary value) {
674: summary = value;
675: if (summary != null) {
676: packageName = summary.getName();
677: }
678: }
679:
680: /**
681: * Tells the class list panel to laod itself
682: */
683: void updateClassListPanel() {
684: setSize(getPreferredSize());
685: if (classListPanel == null) {
686: return;
687: }
688:
689: if (first) {
690: first = false;
691: return;
692: }
693:
694: classListPanel.load(summary);
695: }
696:
697: /**
698: * Set up the default values
699: */
700: private void defaultValues() {
701: packageName = "Unknown Package";
702: hasChanged = false;
703:
704: try {
705: FileSettings umlBundle = FileSettings
706: .getRefactorySettings("uml");
707: umlBundle.setContinuallyReload(true);
708: Vertex.setVertexSize(umlBundle
709: .getInteger("sticky.point.size"));
710: Vertex.setNear(umlBundle.getDouble("halo.size"));
711: } catch (MissingSettingsException mse) {
712: Vertex.setNear(3.0);
713: Vertex.setVertexSize(5);
714: }
715: }
716:
717: /**
718: * Adds a feature to the MouseAdapter attribute of the UMLPackage object
719: */
720: private void addMouseAdapter() {
721: LineMouseAdapter adapter = new LineMouseAdapter(this );
722: addMouseListener(adapter);
723: addMouseMotionListener(adapter);
724: }
725:
726: /**
727: * Resets the scroll panes
728: */
729: public void reset() {
730: if (scrollPane == null) {
731: setSize(getPreferredSize());
732: repaint();
733: } else {
734: Dimension panelSize = getPreferredSize();
735: JViewport view = scrollPane.getViewport();
736: Dimension viewSize = view.getSize();
737: setSize(Math.max(panelSize.width, viewSize.width), Math
738: .max(panelSize.height, viewSize.height));
739: view.setViewSize(getSize());
740: scrollPane.repaint();
741: }
742: }
743:
744: /**
745: * Initialises the positions of all types randomly.
746: *
747: *@since 2.7.04
748: */
749: public void resetPositions() {
750: UMLType[] types = getTypes();
751: int size = (int) (Math.sqrt(types.length) * 1000);
752: Random r = new Random();
753: for (int i = 0; i < types.length; i++) {
754: types[i].setLocation(r.nextInt(size), r.nextInt(size));
755: }
756: //scale(1.0);
757: }
758:
759: private Map forces = new HashMap();
760:
761: /**
762: * Uses a mixture of Simulated Annealling, springs along relationships and
763: * gravity (to position subclasses below their parents).
764: *
765: *@param temperature notional initial temperature of the diagram, a value of 2000 works well.
766: *@param iterations number of times to reduce temperature
767: *@since 2.7.04
768: */
769: public void rearragePositions(int temperature, int iterations,
770: double springStrength) {
771: //System.out.println("rearragePositions()");
772: double oldScale = scale;
773: scale(1.0);
774: Map groups = group();
775: Random r = new Random();
776: UMLType[] types = getTypes();
777:
778: for (int times = 1; times <= iterations; times++) {
779: for (int i = 0; i < types.length; i++) {
780: types[i].shift(
781: r.nextInt(temperature) / (times * times), r
782: .nextInt(temperature)
783: / (times * times * times));
784: }
785: forces = new HashMap();
786: Iterator iter = getLineIterator();
787: while (iter.hasNext()) {
788: SegmentedLine line = (SegmentedLine) iter.next();
789: EndPointPanel start = line.getStartPanel();
790: EndPointPanel end = line.getEndPanel();
791: if (times < (iterations - 10)) {
792: addForce(start, end, start.getBounds(), end
793: .getBounds(), -springStrength * 0.5 / times);
794: if (line instanceof InheretenceRelationship
795: || line instanceof ImplementsRelationship) {
796: addGravity(start, temperature / (times * times));
797: addGravity(end, -temperature / (times * times));
798: }
799: }
800: }
801: for (int i = 0; i < types.length; i++) {
802: for (int j = i + 1; j < types.length; j++) {
803: EndPointPanel start = types[i];
804: EndPointPanel end = types[j];
805: int startGroup = ((Integer) groups.get(start))
806: .intValue();
807: int endGroup = ((Integer) groups.get(end))
808: .intValue();
809: double pushStrength = (startGroup == endGroup) ? springStrength
810: : springStrength * 4.0;
811: double pullStrength = (startGroup == endGroup) ? springStrength * 0.3
812: : springStrength * 0.5 / times;
813:
814: Rectangle boundsStart = start.getBounds();
815: Rectangle boundsEnd = end.getBounds();
816: //if (boundsStart.intersects(boundsEnd)) {
817: // //System.out.println("intersects inner");
818: // addForce(start, end, boundsStart, boundsEnd, pushStrength*5.0/times);
819: //} else {
820: boundsStart.x -= 60;
821: boundsStart.y -= 60;
822: boundsStart.width += 120;
823: boundsStart.height += 120;
824: boundsEnd.x -= 60;
825: boundsEnd.y -= 60;
826: boundsEnd.width += 120;
827: boundsEnd.height += 120;
828: if (boundsStart.intersects(boundsEnd)) {
829: //System.out.println("intersects outer");
830: addInvSqForce(start, end, boundsStart,
831: boundsEnd, pushStrength * 40.0);
832: } else {
833: if (times < (iterations - 10)) {
834: addForce(start, end, start.getBounds(), end
835: .getBounds(), -pullStrength
836: / (times * types.length));
837: }
838: }
839: //}
840: }
841: }
842: Iterator i = forces.keySet().iterator();
843: while (i.hasNext()) {
844: EndPointPanel t = (EndPointPanel) i.next();
845: Pair pair = (Pair) forces.get(t);
846: t.shift((int) pair.x, (int) pair.y);
847: //System.out.println("t="+t+" shift by("+(int)pair.x+", "+(int)pair.y+")");
848: }
849: }
850: int minX = Integer.MAX_VALUE;
851: int minY = Integer.MAX_VALUE;
852: for (int i = 0; i < types.length; i++) {
853: Rectangle bounds = types[i].getBounds();
854: minX = Math.min(minX, bounds.x);
855: minY = Math.min(minY, bounds.y);
856: }
857: for (int i = 0; i < types.length; i++) {
858: types[i].shift(20 - minX, 20 - minY);
859: }
860: forces = null;
861: scale(oldScale);
862: }
863:
864: private static class Pair {
865: double x = 0.0;
866: double y = 0.0;
867: }
868:
869: private void addGravity(EndPointPanel start, double strength) {
870: //System.out.println(" addGravity()");
871: Pair pair = (Pair) forces.get(start);
872: if (pair == null) {
873: pair = new Pair();
874: forces.put(start, pair);
875: }
876: pair.y += strength;
877: }
878:
879: private void addForce(EndPointPanel start, EndPointPanel end,
880: Rectangle boundsStart, Rectangle boundsEnd, double strength) {
881: //System.out.println(" addForce()");
882: int startMiddleX = boundsStart.x + boundsStart.width / 2;
883: int startMiddleY = boundsStart.y + boundsStart.height / 2;
884: int endMiddleX = boundsEnd.x + boundsEnd.width / 2;
885: int endMiddleY = boundsEnd.y + boundsEnd.height / 2;
886: int diffX = startMiddleX - endMiddleX;
887: int diffY = startMiddleY - endMiddleY;
888: //double force = Math.sqrt(diffX*diffX + diffY*diffY);
889: Pair pair = (Pair) forces.get(start);
890: if (pair == null) {
891: pair = new Pair();
892: forces.put(start, pair);
893: }
894:
895: pair.x += strength * diffX;
896: pair.y += strength * diffY;
897: //System.out.println(" start="+start+", x="+(strength * diffX)+", y="+(strength * diffY) +" => force=("+pair.x+","+pair.y+")");
898: pair = (Pair) forces.get(end);
899: if (pair == null) {
900: pair = new Pair();
901: forces.put(end, pair);
902: }
903: pair.x -= strength * diffX;
904: pair.y -= strength * diffY;
905: //System.out.println(" end="+end+", x="+(strength * diffX)+", y="+(strength * diffY) +" => force=("+pair.x+","+pair.y+")");
906: }
907:
908: private void addInvSqForce(EndPointPanel start, EndPointPanel end,
909: Rectangle boundsStart, Rectangle boundsEnd, double strength) {
910: //System.out.println(" addForce()");
911: int startMiddleX = boundsStart.x + boundsStart.width / 2;
912: int startMiddleY = boundsStart.y + boundsStart.height / 2;
913: int endMiddleX = boundsEnd.x + boundsEnd.width / 2;
914: int endMiddleY = boundsEnd.y + boundsEnd.height / 2;
915: int diffX = startMiddleX - endMiddleX;
916: int diffY = startMiddleY - endMiddleY;
917: double dSquared = diffX * diffX + diffY * diffY;
918: double d = Math.sqrt(dSquared);
919: if (dSquared < 100) {
920: dSquared = 100;
921: }
922: strength *= Math.sqrt(boundsStart.width * boundsStart.height);
923: strength *= Math.sqrt(boundsEnd.width * boundsEnd.height);
924: strength /= dSquared;
925: Pair pair = (Pair) forces.get(start);
926: if (pair == null) {
927: pair = new Pair();
928: forces.put(start, pair);
929: }
930:
931: pair.x += strength * diffX / d;
932: pair.y += strength * diffY / d;
933: //System.out.println(" start="+start+", x="+(strength * diffX)+", y="+(strength * diffY) +" => force=("+pair.x+","+pair.y+")");
934: pair = (Pair) forces.get(end);
935: if (pair == null) {
936: pair = new Pair();
937: forces.put(end, pair);
938: }
939: pair.x -= strength * diffX / d;
940: pair.y -= strength * diffY / d;
941: //System.out.println(" end="+end+", x="+(strength * diffX)+", y="+(strength * diffY) +" => force=("+pair.x+","+pair.y+")");
942: }
943:
944: private Map group() {
945: UMLType[] types = getTypes();
946: Map groups = new HashMap();
947: for (int i = 0; i < types.length; i++) {
948: groups.put(types[i], new Integer(i));
949: }
950: Iterator iter = getLineIterator();
951: while (iter.hasNext()) {
952: SegmentedLine line = (SegmentedLine) iter.next();
953: EndPointPanel start = line.getStartPanel();
954: EndPointPanel end = line.getEndPanel();
955: int startGroup = ((Integer) groups.get(start)).intValue();
956: int endGroup = ((Integer) groups.get(end)).intValue();
957: if (startGroup != endGroup) {
958: // join groups
959: Iterator g = groups.entrySet().iterator();
960: while (g.hasNext()) {
961: Map.Entry entry = (Map.Entry) g.next();
962: int v = ((Integer) entry.getValue()).intValue();
963: if (v == endGroup) {
964: entry.setValue(new Integer(startGroup));
965: }
966: }
967: }
968: }
969: return groups;
970: }
971: }
|