Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/port80/graph/impl/GraphShape.java


1   //
2   // Copyright(c) 2002, Chris Leung
3   //
4   
5   package com.port80.graph.impl;
6   
7   import java.awt.Color;
8   import java.awt.Font;
9   import java.awt.Graphics2D;
10  import java.awt.Paint;
11  import java.awt.Rectangle;
12  import java.awt.Shape;
13  import java.awt.Stroke;
14  import java.awt.font.FontRenderContext;
15  import java.awt.font.TextAttribute;
16  import java.awt.font.TextLayout;
17  import java.awt.geom.AffineTransform;
18  import java.awt.geom.Line2D;
19  import java.awt.geom.PathIterator;
20  import java.awt.geom.Point2D;
21  import java.awt.geom.Rectangle2D;
22  import java.awt.image.BufferedImage;
23  import java.text.AttributedString;
24  import java.util.ArrayList;
25  import java.util.List;
26  
27  import com.port80.graph.IGraphShape;
28  import com.port80.graph.IGraphStroke;
29  import com.port80.text.AttrTextParser;
30  import com.port80.text.TextAttr;
31  import com.port80.util.msg;
32  import com.port80.util.struct.IntList;
33  
34  /** Basic graph shape class that implements IGraphShape interface.
35   *
36   *  . Wrapper around a java.awt.Shape objects and an enclosed unit
37   *    square.  When the unit square is scaled to accommodate the label
38   *    bounds, the shape is scaled accordingly.
39   *
40   */
41  public class GraphShape implements IGraphShape {
42  
43    // Static fields ///////////////////////////////////////////////////////
44    //
45  
46    private static final String NAME = "GraphShape";
47    private static final boolean DEBUG = false;
48  
49    private static final Graphics2D defaultGraphics;
50    static {
51      defaultGraphics = (Graphics2D) (new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB).getGraphics());
52    }
53  
54    // Instance fields /////////////////////////////////////////////////////
55    //
56  
57    private String name;
58    private Shape original;
59    private Rectangle2D clientRect;
60    private boolean keepRatio;
61    //
62    private String label;
63    private Point2D origin;
64    private Font font;
65    private IGraphStroke borderStroke;
66    private Color fontColor;
67    private Color borderColor;
68    private Color fillColor;
69    private int minWidth;
70    private int minHeight;
71    private int exclude;
72    //
73    private TextLayout[] layouts;
74    private int[] aligns;
75    private BufferedImage imageBuffer;
76    //
77    private Shape shapeObject;
78    private double xScale = 1.0;
79    private double yScale = 1.0;
80    private double baseX = 0.0;
81    private double baseY = 0.0;
82    private boolean dirty = false;
83    //
84    // Constants.
85    //
86    private double xPadding = 5.0;
87    private double yPadding = 2.0;
88    private double textSpacing = 0.0;
89    private double hrSpacing = 2.0;
90  
91    // Constructors ////////////////////////////////////////////////////////
92    //
93  
94    public GraphShape(String name, Shape shape, Rectangle2D rect, boolean keepratio) {
95      this.name = name;
96      this.original = shape;
97      this.clientRect = (Rectangle2D) rect.clone();
98      this.keepRatio = keepratio;
99      this.origin = new Point2D.Double(0.0, 0.0);
100   }
101 
102   public GraphShape(String name, IGraphShape gs) {
103     this.name = name;
104     this.original = gs.getTemplateShape();
105     this.clientRect = (Rectangle2D) gs.getClientRect().clone();
106     this.keepRatio = gs.isKeepRatio();
107     this.origin = gs.getOrigin();
108   }
109 
110   // IGraphShape interface ///////////////////////////////////////////////
111   //
112 
113   public String getName() {
114     return name;
115   }
116   public Shape getShape() {
117     return shapeObject;
118   }
119   public Shape getTemplateShape() {
120     return original;
121   }
122   public Rectangle2D getClientRect() {
123     return clientRect;
124   }
125   public boolean isKeepRatio() {
126     return keepRatio;
127   }
128   public Point2D getOrigin() {
129     return origin;
130   }
131   public double getBaseX() {
132     return baseX;
133   }
134   public double getBaseY() {
135     return baseY;
136   }
137   public TextLayout[] getLayouts() {
138     return layouts;
139   }
140   public BufferedImage getImageBuffer() {
141     return imageBuffer;
142   }
143 
144   public void setOrigin(Point2D p) {
145     origin = p;
146   }
147 
148   /** Update shape size and label and render content into an image buffer.
149    */
150   public void update(
151     Graphics2D g2d,
152     String label,
153     Point2D pos,
154     int minwidth,
155     int minheight,
156     Font font,
157     IGraphStroke stroke,
158     Color fontcolor,
159     Color bordercolor,
160     Color fillcolor,
161     int exclude) {
162     if (label == null)
163       msg.err(NAME + ".update(): label==null");
164     this.label = label;
165     this.minWidth = minwidth;
166     this.minHeight = minHeight;
167     this.font = font;
168     this.borderStroke = stroke;
169     this.fontColor = fontcolor;
170     this.borderColor = bordercolor;
171     this.fillColor = fillcolor;
172     this.exclude = exclude;
173     if (this.fontColor == null)
174       this.fontColor = Color.black;
175     if (this.borderColor == null)
176       this.borderColor = Color.black;
177 
178     // Determine bounds for label, scale shape accordingly and
179     // render shape into an BufferedImage.
180 
181     imageBuffer = null;
182 
183     int index = 0;
184     int nlines = 1;
185     /*
186     while((index=label.indexOf("\n",index)+1)>0) ++nlines;
187     layouts=new TextLayout[nlines];
188     aligns=new int[nlines];
189     */
190 
191     Rectangle2D bounds;
192     TextLayout layout;
193     String str;
194     List astack = new ArrayList();
195     String text = label;
196     double textwidth = 0;
197     double textheight = 0;
198     FontRenderContext frc = defaultGraphics.getFontRenderContext();
199     nlines = 0;
200     int align = 0;
201     List layoutList = new ArrayList();
202     IntList alignList = new IntList();
203     if (DEBUG)
204       msg.println(NAME + ".update(): text=|" + text + "|");
205     do {
206       index = text.indexOf("\n");
207       if (index < 0)
208         str = text;
209       else {
210         str = text.substring(0, index);
211         text = text.substring(index + 1);
212       }
213       if (DEBUG)
214         msg.println(NAME + ".update(): str=|" + str + "|");
215       AttrTextParser parser = AttrTextParser.parse(str, font, astack);
216       align = parser.getAlign();
217       if (exclude > 0 && (align & parser.ALIGN_ALT) != 0)
218         continue;
219       if ((align & parser.ALIGN_HR_BEFORE) != 0)
220         textheight += hrSpacing;
221       str = parser.getText();
222       AttributedString atext = null;
223       if (str.length() > 0) {
224         atext = new AttributedString(str);
225         List alist = parser.getAttrs();
226         atext.addAttribute(TextAttribute.FONT, font);
227         for (int i = 0; i < alist.size(); ++i) {
228           TextAttr a = (TextAttr) alist.get(i);
229           if (false)
230             msg.println(NAME + ".update(): a=" + a);
231           for (int n = 0; n < a.size(); ++n) {
232             atext.addAttribute(
233               (TextAttribute) a.getKey(n),
234               a.getValue(n),
235               a.getStart(),
236               a.getEnd());
237           }
238         }
239       } else {
240         atext = new AttributedString(" ");
241       }
242       //
243       layout = new TextLayout(atext.getIterator(), frc);
244       bounds = layout.getBounds();
245       if (bounds.getWidth() > textwidth)
246         textwidth = bounds.getWidth();
247       if (nlines > 0)
248         textheight += textSpacing;
249       textheight += layout.getAscent() + layout.getDescent();
250       if ((align & parser.ALIGN_HR_AFTER) != 0)
251         textheight += hrSpacing;
252       alignList.add(align);
253       layoutList.add(layout);
254       //aligns[nlines]=align;
255       //layouts[nlines]=layout;
256       ++nlines;
257     } while (index >= 0);
258     layouts = new TextLayout[layoutList.size()];
259     aligns = new int[alignList.size()];
260     for (int i = 0; i < layoutList.size(); ++i) {
261       layouts[i] = (TextLayout) layoutList.get(i);
262       aligns[i] = alignList.get(i);
263     }
264     double xscale = minWidth;
265     double yscale = minHeight;
266     if (textwidth > xscale)
267       xscale = textwidth;
268     if (textheight > yscale)
269       yscale = textheight;
270     xscale += 2 * xPadding;
271     yscale += 2 * yPadding;
272     reshape(xscale, yscale, pos, textwidth, textheight);
273   }
274 
275   /** Render shape content to given Graphics2D.
276    */
277   public void render(Graphics2D g2d) {
278     //
279     // Fill
280     //
281     if (fillColor != null) {
282       g2d.setPaint(fillColor);
283       g2d.fill(shapeObject);
284       if (DEBUG)
285         msg.println(
286           NAME
287             + ".render(): name="
288             + name
289             + ", stroke="
290             + borderStroke.getName()
291             + ", fillcolor="
292             + fillColor
293             + ", shapeObject="
294             + shapeObject.toString());
295     }
296     //
297     // Border
298     //
299     Stroke stroke = borderStroke.getStroke();
300     if (stroke != null) {
301       g2d.setStroke(stroke);
302       g2d.setPaint(borderColor);
303       g2d.draw(shapeObject);
304       if (DEBUG)
305         msg.println(
306           NAME
307             + ".render(): border: color="
308             + borderColor
309             + ", stroke="
310             + stroke
311             + ", shape="
312             + shapeObject);
313     }
314     //
315     // Draw client rectangle for debugging.
316     //
317     //g2d.setPaint(Color.red);
318     //g2d.setStroke(new BasicStroke(1.0f,BasicStroke.CAP_SQUARE,BasicStroke.JOIN_MITER,10.0f,
319     //         new float[] {2.0f,2.0f},0f));
320     //g2d.draw(getClientRect());
321     //g2d.setStroke(stroke);
322     //
323     g2d.setFont(font);
324     g2d.setPaint(fontColor);
325     Rectangle2D bounds;
326     double w = clientRect.getWidth();
327     double basex = clientRect.getX();
328     double x = 0;
329     double y = clientRect.getY();
330     Stroke hrstroke = StrokeFactory.create("solid").getStroke();
331     for (int i = 0; i < layouts.length; ++i) {
332       if ((aligns[i] & AttrTextParser.ALIGN_HR_BEFORE) != 0) {
333         Paint color = g2d.getPaint();
334         Stroke savedstroke = g2d.getStroke();
335         g2d.setPaint(Color.gray);
336         g2d.setStroke(hrstroke);
337         g2d.draw(new Line2D.Double(basex, y + (hrSpacing / 2), basex + w, y + hrSpacing / 2));
338         y += hrSpacing;
339         g2d.setPaint(color);
340         g2d.setStroke(savedstroke);
341       }
342       TextLayout layout = layouts[i];
343       bounds = layout.getBounds();
344       x = basex;
345       if ((aligns[i] & AttrTextParser.ALIGN_LEFT) != 0);
346       else if ((aligns[i] & AttrTextParser.ALIGN_RIGHT) != 0)
347         x += w - bounds.getWidth();
348       else
349         x += (w - bounds.getWidth()) / 2f;
350       y += layout.getAscent();
351       layout.draw(g2d, (float) x, (float) y);
352       y += layout.getDescent();
353       if ((aligns[i] & AttrTextParser.ALIGN_HR_AFTER) != 0) {
354         Paint color = g2d.getPaint();
355         Stroke savedstroke = g2d.getStroke();
356         g2d.setPaint(Color.gray);
357         g2d.setStroke(hrstroke);
358         g2d.draw(new Line2D.Double(basex, y + (hrSpacing / 2), basex + w, y + hrSpacing / 2));
359         y += hrSpacing;
360         g2d.setPaint(color);
361         g2d.setStroke(savedstroke);
362       }
363     }
364   }
365 
366   /** Scale shape and set new original.
367    *
368    *  . xscale and yscale is usually the same as textwidth and
369    *    textheight.  However, xscale may > textwidth to set a minimium
370    *    width regardless of textwidth or add more margins to the text.
371    *
372    *  @param xscale x-axis scaling factor.
373    *  @param yscale y-axis scaling factor.
374    *  @param neworigin the new origin coordinate.
375    *  @param fm FontMetrics used for the text label.
376    *  @param textwidth actual text width.
377    *  @param textheight actual text height.
378    */
379   public void reshape(double xscale, double yscale, Point2D neworigin, double textwidth, double textheight) {
380     if (keepRatio) {
381       double scale = Math.max(xscale, yscale);
382       xscale = scale;
383       yscale = scale;
384     }
385     Rectangle2D r = original.getBounds2D();
386     double linewidth= (double) borderStroke.getLineWidth();
387     xscale *= (r.getWidth()*xscale + linewidth) / (r.getWidth()*xscale);
388     yscale *= (r.getWidth()*yscale + linewidth) / (r.getWidth()*yscale);
389     AffineTransform tx = AffineTransform.getTranslateInstance(neworigin.getX(), neworigin.getY());
390     tx.scale(xscale, yscale);
391     //    if (origin.getX() != 0 || origin.getY() != 0)
392     //      tx.translate(-origin.getX(), -origin.getY());
393     shapeObject = tx.createTransformedShape(original);
394     // We have simplified a bit here assuming that origin is
395     // always at center of the client rectangle.
396     double w = textwidth;
397     double h = textheight;
398     clientRect.setRect(neworigin.getX() - w / 2.0, neworigin.getY() - h / 2.0, w, h);
399     xScale = xscale;
400     yScale = yscale;
401     origin = neworigin;
402   }
403 
404   public String toString() {
405     return name
406       + ": origin="
407       + origin.getX()
408       + ","
409       + origin.getY()
410       + ": scale="
411       + xScale
412       + ", "
413       + yScale
414       + "\n\t"
415       + ": clientRect="
416       + (int) clientRect.getX()
417       + ","
418       + (int) clientRect.getY()
419       + ","
420       + (int) clientRect.getWidth()
421       + ","
422       + (int) clientRect.getHeight()
423       + ": base="
424       + baseX
425       + ", "
426       + baseY;
427   }
428 
429   // Cloneable interface /////////////////////////////////////////////////
430 
431   public Object clone() {
432     return new GraphShape(name, this);
433   }
434 
435   // Shape interface /////////////////////////////////////////////////////
436 
437   public boolean contains(double x, double y) {
438     return shapeObject.contains(x, y);
439   }
440   public boolean contains(double x, double y, double w, double h) {
441     return shapeObject.contains(x, y, w, h);
442   }
443   public boolean contains(Point2D p) {
444     return shapeObject.contains(p);
445   }
446   public boolean contains(Rectangle2D r) {
447     return shapeObject.contains(r);
448   }
449   public Rectangle getBounds() {
450     Rectangle ret = shapeObject.getBounds();
451     float lw = borderStroke.getLineWidth();
452     ret.setRect(ret.getX() - lw / 2, ret.getY() - lw / 2, ret.getWidth() + lw, ret.getHeight() + lw);
453     return ret;
454   }
455   public Rectangle2D getBounds2D() {
456     Rectangle2D ret = shapeObject.getBounds2D();
457     float lw = borderStroke.getLineWidth();
458     ret.setRect(ret.getX() - lw / 2, ret.getY() - lw / 2, ret.getWidth() + lw, ret.getHeight() + lw);
459     return ret;
460   }
461   public PathIterator getPathIterator(AffineTransform t) {
462     return shapeObject.getPathIterator(t);
463   }
464   public PathIterator getPathIterator(AffineTransform t, double flatness) {
465     return shapeObject.getPathIterator(t, flatness);
466   }
467   public boolean intersects(double x, double y, double w, double h) {
468     return shapeObject.intersects(x, y, w, h);
469   }
470   public boolean intersects(Rectangle2D r) {
471     return shapeObject.intersects(r);
472   }
473 
474   ////////////////////////////////////////////////////////////////////////
475 
476 }