/**
* Copyright (C) 2009, 2010 SC 4ViewSoft SRL
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.achartengine.chart;
import org.achartengine.model.Point;
import org.achartengine.model.SeriesSelection;
import org.achartengine.renderer.DefaultRenderer;
import org.achartengine.renderer.SimpleSeriesRenderer;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.renderer.XYMultipleSeriesRenderer.Orientation;
import org.ohmage.Utilities;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.RectF;
import java.io.Serializable;
import java.util.List;
/**
* An abstract class to be implemented by the chart rendering classes.
*/
public abstract class AbstractChart implements Serializable {
/**
* The graphical representation of the chart.
*
* @param canvas the canvas to paint to
* @param x the top left x value of the view to draw to
* @param y the top left y value of the view to draw to
* @param width the width of the view to draw to
* @param height the height of the view to draw to
* @param paint the paint
*/
public abstract void draw(Canvas canvas, int x, int y, int width, int height, Paint paint);
/**
* Draws the chart background.
*
* @param renderer the chart renderer
* @param canvas the canvas to paint to
* @param x the top left x value of the view to draw to
* @param y the top left y value of the view to draw to
* @param width the width of the view to draw to
* @param height the height of the view to draw to
* @param paint the paint used for drawing
* @param newColor if a new color is to be used
* @param color the color to be used
*/
protected void drawBackground(DefaultRenderer renderer, Canvas canvas, int x, int y, int width,
int height, Paint paint, boolean newColor, int color) {
if (renderer.isApplyBackgroundColor() || newColor) {
if (newColor) {
paint.setColor(color);
} else {
paint.setColor(renderer.getBackgroundColor());
}
paint.setStyle(Style.FILL);
canvas.drawRect(x, y, x + width, y + height, paint);
}
}
/**
* Draws the chart legend.
*
* @param canvas the canvas to paint to
* @param renderer the series renderer
* @param titles the titles to go to the legend
* @param left the left X value of the area to draw to
* @param right the right X value of the area to draw to
* @param y the y value of the area to draw to
* @param width the width of the area to draw to
* @param height the height of the area to draw to
* @param legendSize the legend size
* @param paint the paint to be used for drawing
* @param calculate if only calculating the legend size
*
* @return the legend height
*/
protected int drawLegend(Canvas canvas, DefaultRenderer renderer, String[] titles, int left,
int right, int y, int width, int height, int legendSize, Paint paint, boolean calculate) {
float size = height / 2;
if (renderer.isShowLegend()) {
float currentX = left;
float currentY = y + height - legendSize + size;
paint.setTextAlign(Align.LEFT);
paint.setTextSize(renderer.getLegendTextSize());
int sLength = Math.min(titles.length, renderer.getSeriesRendererCount());
for (int i = 0; i < sLength; i++) {
final float lineSize = getLegendShapeWidth(i);
String text = titles[i];
float[] widths = new float[text.length()];
paint.getTextWidths(text, widths);
float sum = 0;
for (float value : widths) {
sum += value;
}
float extraSize = lineSize + 35 + sum;
float currentWidth = currentX + extraSize;
if (i > 0 && getExceed(currentWidth, renderer, right, width)) {
currentX = left;
currentY += renderer.getLegendTextSize();
size += renderer.getLegendTextSize();
currentWidth = currentX + extraSize;
}
if (getExceed(currentWidth, renderer, right, width)) {
float maxWidth = right - currentX - lineSize - 10;
if (isVertical(renderer)) {
maxWidth = width - currentX - lineSize - 10;
}
int nr = paint.breakText(text, true, maxWidth, widths);
text = text.substring(0, nr) + "...";
}
if (!calculate) {
if (titles.length == renderer.getSeriesRendererCount()) {
if(renderer.isLegendGrayscale())
paint.setColor(Utilities.colorGrayscale(renderer.getSeriesRendererAt(i).getColor()));
else
paint.setColor(renderer.getSeriesRendererAt(i).getColor());
} else {
paint.setColor(Color.GRAY);
}
drawLegendShape(canvas, renderer.getSeriesRendererAt(i), currentX, currentY, i, paint);
paint.setColor(Color.BLACK);
canvas.drawText(text, currentX + lineSize + 10, currentY + 5, paint);
}
currentX += extraSize;
}
}
return Math.round(size + renderer.getLegendTextSize());
}
/**
* Calculates if the current width exceeds the total width.
*
* @param currentWidth the current width
* @param renderer the renderer
* @param right the right side pixel value
* @param width the total width
* @return if the current width exceeds the total width
*/
protected boolean getExceed(float currentWidth, DefaultRenderer renderer, int right, int width) {
boolean exceed = currentWidth > right;
if (isVertical(renderer)) {
exceed = currentWidth > width;
}
return exceed;
}
/**
* Checks if the current chart is rendered as vertical.
*
* @param renderer the renderer
* @return if the chart is rendered as a vertical one
*/
protected boolean isVertical(DefaultRenderer renderer) {
return renderer instanceof XYMultipleSeriesRenderer
&& ((XYMultipleSeriesRenderer) renderer).getOrientation() == Orientation.VERTICAL;
}
/**
* The graphical representation of a path.
*
* @param canvas the canvas to paint to
* @param points the points that are contained in the path to paint
* @param paint the paint to be used for painting
* @param circular if the path ends with the start point
*/
protected void drawPath(Canvas canvas, float[] points, Paint paint, boolean circular) {
Path path = new Path();
path.moveTo(points[0], points[1]);
for (int i = 2; i < points.length; i += 2) {
path.lineTo(points[i], points[i + 1]);
}
if (circular) {
path.lineTo(points[0], points[1]);
}
canvas.drawPath(path, paint);
}
/**
* Returns the legend shape width.
*
* @param seriesIndex the series index
* @return the legend shape width
*/
public abstract int getLegendShapeWidth(int seriesIndex);
/**
* The graphical representation of the legend shape.
*
* @param canvas the canvas to paint to
* @param renderer the series renderer
* @param x the x value of the point the shape should be drawn at
* @param y the y value of the point the shape should be drawn at
* @param seriesIndex the series index
* @param paint the paint to be used for drawing
*/
public abstract void drawLegendShape(Canvas canvas, SimpleSeriesRenderer renderer, float x,
float y, int seriesIndex, Paint paint);
/**
* Calculates the best text to fit into the available space.
*
* @param text the entire text
* @param width the width to fit the text into
* @param paint the paint
* @return the text to fit into the space
*/
private String getFitText(String text, float width, Paint paint) {
String newText = text;
int length = text.length();
int diff = 0;
while (paint.measureText(newText) > width && diff < length) {
diff++;
newText = text.substring(0, length - diff) + "...";
}
if (diff == length) {
newText = "...";
}
return newText;
}
/**
* Calculates the current legend size.
*
* @param renderer the renderer
* @param defaultHeight the default height
* @param extraHeight the added extra height
* @return the legend size
*/
protected int getLegendSize(DefaultRenderer renderer, int defaultHeight, float extraHeight) {
int legendSize = renderer.getLegendHeight();
if (renderer.isShowLegend() && legendSize == 0) {
legendSize = defaultHeight;
}
if (!renderer.isShowLegend() && renderer.isShowLabels()) {
legendSize = (int) (renderer.getLabelsTextSize() * 4 / 3 + extraHeight);
}
return legendSize;
}
/**
* Draws a text label.
*
* @param canvas the canvas
* @param labelText the label text
* @param renderer the renderer
* @param prevLabelsBounds the previous rendered label bounds
* @param centerX the round chart center on X axis
* @param centerY the round chart center on Y axis
* @param shortRadius the short radius for the round chart
* @param longRadius the long radius for the round chart
* @param currentAngle the current angle
* @param angle the label extra angle
* @param left the left side
* @param right the right side
* @param paint the paint
*/
protected void drawLabel(Canvas canvas, String labelText, DefaultRenderer renderer,
List<RectF> prevLabelsBounds, int centerX, int centerY, float shortRadius, float longRadius,
float currentAngle, float angle, int left, int right, Paint paint) {
if (renderer.isShowLabels()) {
paint.setColor(renderer.getLabelsColor());
double rAngle = Math.toRadians(90 - (currentAngle + angle / 2));
double sinValue = Math.sin(rAngle);
double cosValue = Math.cos(rAngle);
int x1 = Math.round(centerX + (float) (shortRadius * sinValue));
int y1 = Math.round(centerY + (float) (shortRadius * cosValue));
int x2 = Math.round(centerX + (float) (longRadius * sinValue));
int y2 = Math.round(centerY + (float) (longRadius * cosValue));
float size = renderer.getLabelsTextSize();
float extra = Math.max(size / 2, 10);
paint.setTextAlign(Align.LEFT);
if (x1 > x2) {
extra = -extra;
paint.setTextAlign(Align.RIGHT);
}
float xLabel = x2 + extra;
float yLabel = y2;
float width = right - xLabel;
if (x1 > x2) {
width = xLabel - left;
}
labelText = getFitText(labelText, width, paint);
float widthLabel = paint.measureText(labelText);
boolean okBounds = false;
while (!okBounds) {
boolean intersects = false;
int length = prevLabelsBounds.size();
for (int j = 0; j < length && !intersects; j++) {
RectF prevLabelBounds = prevLabelsBounds.get(j);
if (prevLabelBounds.intersects(xLabel, yLabel, xLabel + widthLabel, yLabel + size)) {
intersects = true;
yLabel = Math.max(yLabel, prevLabelBounds.bottom);
}
}
okBounds = !intersects;
}
y2 = (int) (yLabel - size / 2);
canvas.drawLine(x1, y1, x2, y2, paint);
canvas.drawLine(x2, y2, x2 + extra, y2, paint);
canvas.drawText(labelText, xLabel, yLabel, paint);
prevLabelsBounds.add(new RectF(xLabel, yLabel, xLabel + widthLabel, yLabel + size));
}
}
/**
* Given screen coordinates, returns the series and point indexes of a chart
* element. If there is no chart element (line, point, bar, etc) at those
* coordinates, null is returned.
*
* @param screenPoint
* @return
*/
public SeriesSelection getSeriesAndPointForScreenCoordinate(Point screenPoint) {
return null;
}
}