001: /**
002: * Chart2D, a java library for drawing two dimensional charts.
003: * Copyright (C) 2001 Jason J. Simas
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: * The author of this library may be contacted at:
019: * E-mail: jjsimas@users.sourceforge.net
020: * Street Address: J J Simas, 887 Tico Road, Ojai, CA 93023-3555 USA
021: */package net.sourceforge.chart2d;
022:
023: import java.awt.*;
024: import java.util.Date;
025:
026: /**
027: * This class manages a set of labels that can be used to encircle a PieArea,
028: * labeling its pie sectors. The way it works is that you must set the size of
029: * the max size for the class and also the size that it is supposed to
030: * encircle.
031: * This provides three different labeling types.
032: * The text of the label can be the data number of that sector, the text of
033: * the label can be a percent of the total on the data number of that sector,
034: * and the text of the label can be both.
035: * The font properties can be set using the methods of this' parent class.
036: * This class can return a point centered near each label so that lines can
037: * be drawn from the labels to their corresponding pie sectors.
038: */
039: final class PieInfoArea extends FontArea {
040:
041: /**
042: * Static variable for setLabelsType (int) method.
043: * RAW indicates that the actual number represented by the sector should be
044: * present.
045: */
046: final static int RAW = 1;
047:
048: /**
049: * Static variable for setLabelsType (int) method.
050: * PERCENT indicates that the percent the number represented by the sector
051: * makes of the whole should should be present.
052: */
053: final static int PERCENT = 2;
054:
055: private int labelsType;
056: private int rawLabelsPrecision;
057: private boolean betweenLabelsGapExistence;
058: private int betweenLabelsGapThicknessModel;
059: private TextArea[] labels;
060: private Point[] pointsNearLabels;
061: private boolean labelsPointsGapExistence;
062: private int labelsPointsGapThicknessModel;
063: private PieArea pieArea;
064: private Dimension interiorSize;
065: private Rectangle interiorBounds;
066: private boolean customSizing;
067: private Dimension customSize;
068: private float[] dataset;
069: private Color[] datasetColors;
070: private boolean pieLabelsExistence;
071:
072: private boolean needsUpdate;
073:
074: /**
075: * The general constructor for this class.
076: */
077: PieInfoArea() {
078:
079: pieArea = new PieArea();
080: setAutoSizes(false, false);
081: setBackgroundExistence(false);
082: setBorderExistence(false);
083: setGapExistence(false);
084: setAutoJustifys(false, false);
085: setFontPointModel(10);
086: setLabelsType(RAW + PERCENT);
087: setRawLabelsPrecision(0);
088: setBetweenLabelsGapExistence(true);
089: setBetweenLabelsGapThicknessModel(3);
090: setLabelsPointsGapExistence(true);
091: setLabelsPointsGapThicknessModel(2);
092: setCustomSize(false, new Dimension());
093: setPieLabelsExistence(true);
094: resetPieInfoAreaModel(true);
095: needsUpdate = true;
096: }
097:
098: /**
099: * Sets whether the labels exist or not. If the labels do not exist, their
100: * space will be taken up by the pie.
101: * @param existence A boolean indicating whether the labels are painted.
102: */
103: final void setPieLabelsExistence(boolean existence) {
104:
105: needsUpdate = true;
106: pieLabelsExistence = existence;
107: }
108:
109: /**
110: * Returns whether the labels exist or not. If the labels do not exist, their
111: * space will be taken up by the pie.
112: * @return A boolean indicating whether the labels are painted.
113: */
114: final boolean getPieLabelsExistence() {
115:
116: return pieLabelsExistence;
117: }
118:
119: /**
120: * For input of the raw numbers to represent by the pie. Array element i
121: * is sector i, clockwise from degree 135.
122: * @param values The raw numbers to represent by the pie.
123: */
124: final void setDataset(float[] values) {
125:
126: needsUpdate = true;
127: dataset = values;
128: }
129:
130: /**
131: * For input of the color of each sector that represents a datum of the data
132: * set. Array element i is sector i, clockise from degree 135.
133: * @param colors The colors of the sectors.
134: */
135: final void setDatasetColors(Color[] colors) {
136:
137: needsUpdate = true;
138: datasetColors = colors;
139: }
140:
141: /**
142: * Returns the raw numbers to represent by the pie.
143: * @return The raw numbers to represent by the pie.
144: */
145: final float[] getDataset() {
146:
147: return dataset;
148: }
149:
150: /**
151: * Returns this property.
152: * @return The colors of the lines.
153: */
154: final Color[] getDatasetColors() {
155:
156: return datasetColors;
157: }
158:
159: /**
160: * Indicates the final size of the area.
161: * Everything is calculated using the non-custom properties. When laying
162: * out the labels, the labels are mereley laid out wider than normal to the
163: * point that their ends touch the edges of the custom size...
164: * Hence, all extra space is added to the interior size.
165: * @param customize If true, then the custom size will be used.
166: * @param size The custom size if used.
167: */
168: final void setCustomSize(boolean customize, Dimension size) {
169:
170: needsUpdate = true;
171: customSizing = customize;
172: customSize = size;
173: }
174:
175: /**
176: * Gets in the PieArea to encircle with labels.
177: * @return The PieArea.
178: */
179: final PieArea getPieArea() {
180: return pieArea;
181: }
182:
183: /**
184: * Specifies whether the gaps between the labels and the points near each
185: * label exist.
186: * @param existence If true, the gaps exist.
187: */
188: final void setLabelsPointsGapExistence(boolean existence) {
189:
190: needsUpdate = true;
191: labelsPointsGapExistence = existence;
192: }
193:
194: /**
195: * Indicates how far from the labels, the points on each label should be.
196: * The points are returned by the getPointsNearLabels(...) method.
197: * @param thickness The model thickness of the gap between the label and the
198: * point.
199: */
200: final void setLabelsPointsGapThicknessModel(int thickness) {
201:
202: needsUpdate = true;
203: labelsPointsGapThicknessModel = thickness;
204: }
205:
206: /**
207: * Indicates how many significant digits are wanted.
208: * For example, if you want two decimal places, then pass -2.
209: * If you want no decimal places, then pass 0.
210: * For more information see ChartArea.getPrecisionRound(...).
211: * @param The desired precision of the raw labels.
212: */
213: final void setRawLabelsPrecision(int precision) {
214:
215: needsUpdate = true;
216: rawLabelsPrecision = precision;
217: }
218:
219: /**
220: * Indicates whether a gap should exist between the labels.
221: * This method overides setBetweenLabelsGapThicknessModel(...).
222: * @param existence If true, the gaps will exist.
223: */
224: final void setBetweenLabelsGapExistence(boolean existence) {
225:
226: needsUpdate = true;
227: betweenLabelsGapExistence = existence;
228: }
229:
230: /**
231: * Indicates the model thickness of the gap between each label.
232: * This thickness is actually the minimum model thickness. If there is
233: * extra space that the labels don't need it is divided evenly between the
234: * labels, but the gap will be at least what is specified here.
235: * @param thickness The model minimum thickness of the gap.
236: */
237: final void setBetweenLabelsGapThicknessModel(int thickness) {
238:
239: needsUpdate = true;
240: betweenLabelsGapThicknessModel = thickness;
241: }
242:
243: /**
244: * Indirectly sets the text of the labels. Possible values are RAW, PERCENT,
245: * and RAW + PERCENT. RAW causes the text of the label to be the actual data
246: * numbers. PERCENT causes the text to be the percent of the whole of the
247: * data. RAW + PERCENT causes the text of the label to be both, in the format
248: * of "p% - d" were p is the percent and d is the data number.
249: * @param type What text occurrs in each label.
250: */
251: final void setLabelsType(int type) {
252:
253: needsUpdate = true;
254: labelsType = type;
255: }
256:
257: /**
258: * Returns how far from the labels, the points on each label should be.
259: * The points are returned by the getPointsNearLabels(...) method.
260: * @return The model thickness of the gap between the label and the point.
261: */
262: final int getLabelsPointsGapThicknessModel() {
263:
264: return labelsPointsGapThicknessModel;
265: }
266:
267: /**
268: * Returns how many significant digits are wanted.
269: * For example, if you want two decimal places, then pass -2.
270: * If you want no decimal places, then pass 0.
271: * For more information see ChartArea.getPrecisionRound(...).
272: * @return The desired precision of the raw labels.
273: */
274: final int getRawLabelsPrecision() {
275:
276: return rawLabelsPrecision;
277: }
278:
279: /**
280: * Indirectly returns the text of the labels. Possible values are RAW,
281: * PERCENT,
282: * and RAW + PERCENT. RAW causes the text of the label to be the actual data
283: * numbers. PERCENT causes the text to be the percent of the whole of the
284: * data. RAW + PERCENT causes the text of the label to be both, in the format
285: * of "p% - d" were p is the percent and d is the data number.
286: * @return What text occurrs in each label.
287: */
288: final int getLabelsType() {
289:
290: return labelsType;
291: }
292:
293: /**
294: * Returns a bounding rectangle of the area the labels encircle.
295: * @return The bounding rectangle of the interior.
296: */
297: final Rectangle getInteriorBounds(Graphics2D g2D) {
298:
299: updatePieInfoArea(g2D);
300: return interiorBounds;
301: }
302:
303: /**
304: * Returns whether the gaps between the labels and the points near each
305: * label exist.
306: * @return boolean If true, the gaps exist.
307: */
308: final boolean getLabelsPointsGapExistence() {
309: return labelsPointsGapExistence;
310: }
311:
312: /**
313: * Returns an array of points, one for each label, that are near the labels.
314: * These points can be used for drawing lines from pie sectors to labels.
315: * @return Point[] The array of points.
316: */
317: final Point[] getPointsNearLabels(Graphics2D g2D) {
318:
319: updatePieInfoArea(g2D);
320: return pointsNearLabels;
321: }
322:
323: /**
324: * Indicates whether some property of this class has changed.
325: * @return True if some property has changed.
326: */
327: final boolean getPieInfoAreaNeedsUpdate() {
328: return (needsUpdate || getFontAreaNeedsUpdate() || pieArea
329: .getPieAreaNeedsUpdate());
330: }
331:
332: /**
333: * Resets the model for this class. The model is used for shrinking and
334: * growing of its components based on the maximum size of this class. If this
335: * method is called, then the next time the maximum size is set, this classes
336: * model maximum size will be made equal to the new maximum size. Effectively
337: * what this does is ensure that whenever this objects maximum size is equal
338: * to the one given, then all of the components will take on their default
339: * model sizes. Note: This is only useful when auto model max sizing is
340: * disabled.
341: * @param reset True causes the max model size to be set upon the next max
342: * sizing.
343: */
344: final void resetPieInfoAreaModel(boolean reset) {
345:
346: needsUpdate = true;
347: resetFontAreaModel(reset);
348: pieArea.resetPieAreaModel(reset);
349: }
350:
351: /**
352: * Updates all this classes variables. First updates it's parent class, then
353: * then updates its own variables.
354: */
355: final void updatePieInfoArea(Graphics2D g2D) {
356:
357: if (getPieInfoAreaNeedsUpdate()) {
358:
359: updateFontArea();
360: update(g2D);
361: pieArea.updatePieArea();
362: for (int i = 0; i < labels.length; ++i) {
363: labels[i].updateTextArea(g2D);
364: }
365: }
366: needsUpdate = false;
367: }
368:
369: /**
370: * Paints this class. Updates all variables, then paints its parent, since
371: * this class itself doesn't have anything to paint.
372: * @param g2D The graphics context for calculations and painting.
373: */
374: final void paintComponent(Graphics2D g2D) {
375:
376: updatePieInfoArea(g2D);
377: super .paintComponent(g2D);
378:
379: pieArea.paintComponent(g2D);
380: for (int i = 0; i < pieArea.getDataset().length; ++i) {
381: labels[i].paintComponent(g2D);
382: }
383: }
384:
385: private void update(Graphics2D g2D) {
386:
387: float widthRatio = getRatio(WIDTH);
388: float heightRatio = getRatio(HEIGHT);
389: pieArea.setCustomRatio(WIDTH, true, widthRatio);
390: pieArea.setCustomRatio(HEIGHT, true, heightRatio);
391:
392: pieArea.setDataset(dataset);
393: pieArea.setColors(datasetColors);
394: pieArea.setSize(MAX, new Dimension(100, 100)); //fake for accurate calculation
395: pieArea.setCustomSpaceSize(true, new Dimension(100, 100)); //fake for accurate calculation
396: pieArea.updatePieArea();
397: int[] numLabelsInSectors = pieArea.getNumSectorsInQuarters();
398: int[] numLabelsInQuarters = new int[4];
399: numLabelsInQuarters[TOP] = numLabelsInSectors[TOP];
400: numLabelsInQuarters[RIGHT] = numLabelsInSectors[RIGHT];
401: numLabelsInQuarters[BOTTOM] = numLabelsInSectors[BOTTOM];
402: numLabelsInQuarters[LEFT] = numLabelsInSectors[LEFT];
403: pieArea.setCustomSpaceSize(false, null);
404: pieArea.updatePieArea();
405:
406: int maxLabelsHorizontally = numLabelsInQuarters[TOP] > numLabelsInQuarters[BOTTOM] ? numLabelsInQuarters[TOP]
407: : numLabelsInQuarters[BOTTOM];
408: int maxLabelsVertically = numLabelsInQuarters[RIGHT] > numLabelsInQuarters[LEFT] ? numLabelsInQuarters[RIGHT]
409: : numLabelsInQuarters[LEFT];
410:
411: int betweenLabelsGapThickness = 0;
412: if (betweenLabelsGapExistence && pieLabelsExistence) {
413: betweenLabelsGapThickness = applyRatio(
414: betweenLabelsGapThicknessModel, getRatio(LESSER));
415: }
416:
417: Dimension labelsSizeMax;
418: if (pieLabelsExistence) {
419: int labelsMaxWidth1 = getSpaceSize(MAX).width / 2
420: - betweenLabelsGapThickness;
421: int labelsMaxWidth2 = Integer.MAX_VALUE;
422: if (maxLabelsHorizontally > 0) {
423: labelsMaxWidth2 = (getSpaceSize(MAX).width - (maxLabelsHorizontally - 1)
424: * betweenLabelsGapThickness)
425: / maxLabelsHorizontally;
426: }
427: int labelsMaxWidth = labelsMaxWidth1 < labelsMaxWidth2 ? labelsMaxWidth1
428: : labelsMaxWidth2;
429: labelsMaxWidth = labelsMaxWidth > 0 ? labelsMaxWidth : 0;
430:
431: int labelsMaxHeight1 = getSpaceSize(MAX).height / 2
432: - betweenLabelsGapThickness;
433: int labelsMaxHeight2 = (getSpaceSize(MAX).height - (maxLabelsVertically + 1)
434: * betweenLabelsGapThickness)
435: / (maxLabelsVertically + 2);
436: int labelsMaxHeight = labelsMaxHeight1 < labelsMaxHeight2 ? labelsMaxHeight1
437: : labelsMaxHeight2;
438: labelsMaxHeight = labelsMaxHeight > 0 ? labelsMaxHeight : 0;
439: labelsSizeMax = new Dimension(labelsMaxWidth,
440: labelsMaxHeight);
441: } else
442: labelsSizeMax = new Dimension();
443:
444: labels = new TextArea[dataset.length];
445: float datasetTotal = ChartArea.getDatasetTotal(dataset);
446: int labelsWidthMin = 0;
447: int labelsHeightMin = 0;
448: for (int i = 0; i < dataset.length; ++i) {
449:
450: TextArea label = new TextArea();
451: label.setCustomRatio(WIDTH, true, getRatio(WIDTH));
452: label.setCustomRatio(HEIGHT, true, getRatio(HEIGHT));
453: label.setAutoJustifys(false, false);
454: label.setAutoSizes(true, false);
455: label.setSize(MAX, labelsSizeMax);
456: label.setBackgroundExistence(false);
457: label.setBorderExistence(false);
458: label.setGapExistence(false);
459: label.setFontColor(getFontColor());
460: label.setFontName(getFontName());
461: label.setFontPointModel(getFontPointModel());
462: label.setFontStyle(getFontStyle());
463:
464: String text = "";
465: if (labelsType == RAW) {
466: text = ChartArea
467: .getFloatToString(ChartArea.getPrecisionRound(
468: dataset[i], rawLabelsPrecision),
469: rawLabelsPrecision);
470: } else if (labelsType == PERCENT) {
471: text = ChartArea.getFloatToString(ChartArea
472: .getPrecisionRound(100 * dataset[i]
473: / datasetTotal, 0), 0)
474: + "%";
475: } else {
476: String text1 = ChartArea
477: .getFloatToString(ChartArea.getPrecisionRound(
478: dataset[i], rawLabelsPrecision),
479: rawLabelsPrecision);
480: String text2 = ChartArea.getFloatToString(ChartArea
481: .getPrecisionRound(100 * dataset[i]
482: / datasetTotal, 0), 0)
483: + "%";
484: text = text1 + " (" + text2 + ")";
485: }
486: label.setText(text);
487:
488: label.updateTextArea(g2D);
489: labelsWidthMin = label.getSize(MIN).width > labelsWidthMin ? label
490: .getSize(MIN).width
491: : labelsWidthMin;
492: labelsHeightMin = label.getSize(MIN).height > labelsHeightMin ? label
493: .getSize(MIN).height
494: : labelsHeightMin;
495: labels[i] = label;
496: }
497:
498: Dimension labelsSizeMin = new Dimension(labelsWidthMin,
499: labelsHeightMin);
500:
501: int widthMin1 = (maxLabelsHorizontally - 1)
502: * betweenLabelsGapThickness + maxLabelsHorizontally
503: * labelsWidthMin;
504: int widthMin2 = 2 * labelsWidthMin;
505: int widthMin = widthMin1 > widthMin2 ? widthMin1 : widthMin2;
506:
507: int heightMin1 = (maxLabelsVertically + 1)
508: * betweenLabelsGapThickness + (maxLabelsVertically + 2)
509: * labelsHeightMin;
510: int heightMin2 = 2 * labelsHeightMin;
511: int heightMin = heightMin1 > heightMin2 ? heightMin1
512: : heightMin2;
513:
514: int pieSize = 0;
515: if (!getAutoSize(MIN) && !customSizing) {
516:
517: int availableWidth = getSpaceSize(MAX).width - widthMin;
518: int availableHeight = getSpaceSize(MAX).height - heightMin;
519: int available = availableWidth < availableHeight ? availableWidth
520: : availableHeight;
521: pieArea.setSize(MAX, new Dimension(available, available));
522: pieArea.updatePieArea();
523: pieSize = pieArea.getSize(MIN).width;
524:
525: widthMin += pieSize;
526: heightMin += pieSize;
527:
528: setSpaceSize(MIN, new Dimension(widthMin, heightMin));
529: } else if (!getAutoSize(MIN) && customSizing) {
530:
531: setSpaceSize(MIN, customSize);
532: }
533:
534: Dimension sizeMin = getSpaceSize(MIN);
535:
536: float midPointX = getSpaceSizeLocation(MIN).x + sizeMin.width
537: / 2f;
538: float midPointY = getSpaceSizeLocation(MIN).y + sizeMin.height
539: / 2f;
540:
541: pointsNearLabels = new Point[dataset.length];
542:
543: int labelsPointsGapThickness = labelsPointsGapExistence ? applyRatio(
544: labelsPointsGapThicknessModel, getRatio(LESSER))
545: : 0;
546:
547: int topInteriorY = 0, leftInteriorX = 0;
548: Point originTop = new Point(0, getSpaceSizeLocation(MIN).y);
549: int betweenGapTop = (sizeMin.width - numLabelsInQuarters[TOP]
550: * labelsWidthMin)
551: / (numLabelsInQuarters[TOP] + 1);
552: if (numLabelsInQuarters[TOP] % 2 == 0) {
553: originTop.setLocation(midPointX
554: - (numLabelsInQuarters[TOP] / 2) * labelsWidthMin
555: - ((numLabelsInQuarters[TOP] / 2) - 1 / 2f)
556: * betweenGapTop, originTop.y);
557: } else {
558: originTop
559: .setLocation(
560: (int) (midPointX
561: - (numLabelsInQuarters[TOP] / 2f)
562: * labelsWidthMin - (int) (numLabelsInQuarters[TOP] / 2)
563: * betweenGapTop), originTop.y);
564: }
565:
566: topInteriorY = originTop.y + labelsHeightMin;
567: for (int i = 0; i < numLabelsInQuarters[TOP]; ++i) {
568: labels[i]
569: .setSpaceSizeLocation(
570: MIN,
571: new Point(
572: (int) (originTop.x
573: + i
574: * (labelsWidthMin + betweenGapTop) + (labelsWidthMin - labels[i]
575: .getSize(MIN).width) / 2f),
576: originTop.y));
577: pointsNearLabels[i] = new Point((int) (labels[i]
578: .getSpaceSizeLocation(MIN).x + (labels[i]
579: .getSize(MIN).width / 2f)), originTop.y
580: + labelsHeightMin + labelsPointsGapThickness);
581: }
582:
583: Point originRight = new Point(getSpaceSizeLocation(MIN).x
584: + sizeMin.width - labelsWidthMin, 0);
585: int betweenGapRight = (sizeMin.height - (numLabelsInQuarters[RIGHT] + 2)
586: * labelsHeightMin)
587: / (numLabelsInQuarters[RIGHT] + 1);
588: if (numLabelsInQuarters[RIGHT] % 2 == 0) {
589: originRight
590: .setLocation(
591: originRight.x,
592: (int) (midPointY
593: - (numLabelsInQuarters[RIGHT] / 2)
594: * labelsHeightMin - ((numLabelsInQuarters[RIGHT] / 2) - 1 / 2f)
595: * betweenGapRight));
596: } else {
597: originRight
598: .setLocation(
599: originRight.x,
600: (int) (midPointY
601: - (numLabelsInQuarters[RIGHT] / 2f)
602: * labelsHeightMin - (int) (numLabelsInQuarters[RIGHT] / 2)
603: * betweenGapRight));
604: }
605:
606: int datasetOffsetRight = numLabelsInQuarters[TOP];
607: for (int i = 0; i < numLabelsInQuarters[RIGHT]; ++i) {
608: labels[i + datasetOffsetRight]
609: .setSpaceSizeLocation(
610: MIN,
611: new Point(
612: originRight.x
613: + (labelsWidthMin - labels[i
614: + datasetOffsetRight]
615: .getSize(MIN).width),
616: (int) (originRight.y
617: + i
618: * (labelsHeightMin + betweenGapRight) + (labelsHeightMin - labels[i
619: + datasetOffsetRight]
620: .getSize(MIN).height) / 2f)));
621: pointsNearLabels[i + datasetOffsetRight] = new Point(
622: originRight.x - labelsPointsGapThickness,
623: (int) (labels[i + datasetOffsetRight]
624: .getSpaceSizeLocation(MIN).y + (labels[i
625: + datasetOffsetRight].getSize(MIN).height / 2f)));
626: }
627:
628: Point originBottom = new Point(0, getSpaceSizeLocation(MIN).y
629: + getSpaceSize(MIN).height - labelsHeightMin);
630: int betweenGapBottom = (sizeMin.width - numLabelsInQuarters[BOTTOM]
631: * labelsWidthMin)
632: / (numLabelsInQuarters[BOTTOM] + 1);
633: if (numLabelsInQuarters[BOTTOM] % 2 == 0) {
634: originBottom.setLocation(midPointX
635: - (numLabelsInQuarters[BOTTOM] / 2)
636: * labelsWidthMin
637: - ((numLabelsInQuarters[BOTTOM] / 2) - 1 / 2f)
638: * betweenGapBottom, originBottom.y);
639: } else {
640: originBottom
641: .setLocation(
642: (int) (midPointX
643: - (numLabelsInQuarters[BOTTOM] / 2f)
644: * labelsWidthMin - (int) (numLabelsInQuarters[BOTTOM] / 2)
645: * betweenGapBottom), originBottom.y);
646: }
647:
648: int datasetOffsetBottom = numLabelsInQuarters[TOP]
649: + numLabelsInQuarters[RIGHT];
650: int j = numLabelsInQuarters[BOTTOM] - 1;
651: for (int i = 0; i < numLabelsInQuarters[BOTTOM]; ++i) {
652: labels[i + datasetOffsetBottom]
653: .setSpaceSizeLocation(
654: MIN,
655: new Point(
656: (int) (originBottom.x
657: + j
658: * (labelsWidthMin + betweenGapBottom) + (labelsWidthMin - labels[i
659: + datasetOffsetBottom]
660: .getSize(MIN).width) / 2f),
661: originBottom.y
662: + (labelsHeightMin - labels[i
663: + datasetOffsetBottom]
664: .getSize(MIN).height)));
665: --j;
666: pointsNearLabels[i + datasetOffsetBottom] = new Point(
667: (int) (labels[i + datasetOffsetBottom]
668: .getSpaceSizeLocation(MIN).x + (labels[i
669: + datasetOffsetBottom].getSize(MIN).width / 2f)),
670: originBottom.y - labelsPointsGapThickness);
671: }
672:
673: Point originLeft = new Point(getSpaceSizeLocation(MIN).x, 0);
674: int betweenGapLeft = (sizeMin.height - (numLabelsInQuarters[LEFT] + 2)
675: * labelsHeightMin)
676: / (numLabelsInQuarters[LEFT] + 1);
677: if (numLabelsInQuarters[LEFT] % 2 == 0) {
678: originLeft
679: .setLocation(
680: originLeft.x,
681: (int) (midPointY
682: - (numLabelsInQuarters[LEFT] / 2)
683: * labelsHeightMin - ((numLabelsInQuarters[LEFT] / 2) - 1 / 2f)
684: * betweenGapLeft));
685: } else {
686: originLeft
687: .setLocation(
688: originLeft.x,
689: (int) (midPointY
690: - (numLabelsInQuarters[LEFT] / 2f)
691: * labelsHeightMin - (int) (numLabelsInQuarters[LEFT] / 2)
692: * betweenGapLeft));
693: }
694: leftInteriorX = originLeft.x + labelsWidthMin;
695:
696: int datasetOffsetLeft = numLabelsInQuarters[TOP]
697: + numLabelsInQuarters[RIGHT]
698: + numLabelsInQuarters[BOTTOM];
699: j = numLabelsInQuarters[LEFT] - 1;
700: for (int i = 0; i < numLabelsInQuarters[LEFT]; ++i) {
701: labels[i + datasetOffsetLeft]
702: .setSpaceSizeLocation(
703: MIN,
704: new Point(
705: originLeft.x
706: + labelsWidthMin
707: - labels[i
708: + datasetOffsetLeft]
709: .getSize(MIN).width,
710: (int) (originLeft.y
711: + j
712: * (labelsHeightMin + betweenGapLeft) + (labelsHeightMin - labels[i
713: + datasetOffsetLeft]
714: .getSize(MIN).height) / 2f)));
715: --j;
716: pointsNearLabels[i + datasetOffsetLeft] = new Point(
717: originLeft.x + labelsWidthMin
718: + labelsPointsGapThickness,
719: (int) (labels[i + datasetOffsetLeft]
720: .getSpaceSizeLocation(MIN).y + (labels[i
721: + datasetOffsetLeft].getSize(MIN).height / 2f)));
722: }
723:
724: pieArea.setCustomSpaceSize(true, new Dimension(originRight.x
725: - originLeft.x - labelsWidthMin - 2
726: * pieArea.getOffsetThickness(), originBottom.y
727: - originTop.y - labelsHeightMin - 2
728: * pieArea.getOffsetThickness()));
729: pieArea.setSpaceSizeLocation(MIN, new Point(originLeft.x
730: + labelsWidthMin + pieArea.getOffsetThickness(),
731: originTop.y + labelsHeightMin
732: + pieArea.getOffsetThickness()));
733: }
734: }
|