package com.baselet.diagram.draw;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import com.baselet.control.StringStyle;
import com.baselet.control.basics.geom.DimensionDouble;
import com.baselet.control.basics.geom.Line;
import com.baselet.control.basics.geom.Lines;
import com.baselet.control.basics.geom.PointDouble;
import com.baselet.control.basics.geom.Rectangle;
import com.baselet.control.constants.FacetConstants;
import com.baselet.control.enums.AlignHorizontal;
import com.baselet.control.enums.FormatLabels;
import com.baselet.control.enums.LineType;
import com.baselet.diagram.draw.helper.ColorOwn;
import com.baselet.diagram.draw.helper.ColorOwn.Transparency;
import com.baselet.diagram.draw.helper.Style;
import com.baselet.diagram.draw.helper.StyleException;
public abstract class DrawHandler {
protected static final double HALF_PX = 0.5f;
protected Style style = new Style();
private final Style overlay = new Style();
private final ArrayList<DrawFunction> drawablesBackground = new ArrayList<DrawFunction>();
private final ArrayList<DrawFunction> drawablesForeground = new ArrayList<DrawFunction>();
public static enum Layer {
Foreground, Background
}
private Layer layer = Layer.Background;
private boolean enableDrawing = true;
/**
* all background elements are drawn before drawing the foreground elements
* can be useful e.g. if a printText call is made before a drawRectangle call although it should be placed behind the rectangle
*/
public void setLayer(Layer layer) {
this.layer = layer;
}
public void setEnableDrawing(boolean enableDrawing) {
this.enableDrawing = enableDrawing;
}
protected Style getOverlay() {
return overlay;
}
protected void addDrawable(DrawFunction drawable) {
if (enableDrawing) {
if (layer == Layer.Foreground) {
drawablesForeground.add(drawable);
}
else {
drawablesBackground.add(drawable);
}
}
// if drawing is disabled don't add the DrawFunction to any collection
}
public void drawAll(boolean isSelected) {
if (isSelected) {
overlay.setForegroundColor(ColorOwn.SELECTION_FG);
}
else {
overlay.setForegroundColor(null);
}
drawAll();
}
public void clearCache() {
drawablesBackground.clear();
drawablesForeground.clear();
}
public final double textHeightMaxWithSpace() {
return textHeightMax() + getDistanceBetweenTextLines();
}
public final double textHeightMax() {
return textDimension(new StringStyle(Collections.<FormatLabels> emptySet(), "Hy")).getHeight(); // "Hy" is a good dummy for a generic max height and depth
}
/**
* @param singleLineWithMarkup a single line (no \n) with interpreted markup
* @return the height of the given text
* @see StringStyle#replaceNotEscaped(String)
* @see StringStyle#analyzeFormatLabels(String)
* @see #textHeight(StringStyle)
*/
public final double textHeight(String singleLineWithMarkup) {
return textHeight(escapeAndAnalyzeSingleLine(singleLineWithMarkup));
}
/**
* @param singleLine a single line (no \n), no further processing of the String takes place.
* i.e. The text is printed unmodified with the given formating.
* @return the height of the given text
*/
public final double textHeight(StringStyle singleLine) {
return textDimension(singleLine).getHeight();
}
/**
*
* @param singleLineWithMarkup a single line (no \n) with interpreted markup
* @return the width of the given text
* @see StringStyle#replaceNotEscaped(String)
* @see StringStyle#analyzeFormatLabels(String)
* @see #textWidth(StringStyle)
*/
public final double textWidth(String singleLineWithMarkup) {
return textWidth(escapeAndAnalyzeSingleLine(singleLineWithMarkup));
}
/**
* @param singleLine single line (no \n), no further processing of the String takes place.
* i.e. The text is printed unmodified with the given formating.
* @return the width of the given text
*/
public final double textWidth(StringStyle singleLine) {
return textDimension(singleLine).getWidth();
}
public final void setForegroundColor(String color) {
if (color.equals(FacetConstants.FOREGROUND_COLOR_KEY)) {
setForegroundColor(ColorOwn.DEFAULT_FOREGROUND);
}
else {
setForegroundColor(ColorOwn.forString(color, Transparency.FOREGROUND)); // if fgColor is not a valid string null will be set
}
}
public final void setForegroundColor(ColorOwn color) {
if (color == null) {
style.setForegroundColor(ColorOwn.DEFAULT_FOREGROUND);
}
else {
style.setForegroundColor(color);
}
}
public final void setBackgroundColorAndKeepTransparency(String color) {
if (color.equals(FacetConstants.BACKGROUND_COLOR_KEY)) {
setBackgroundColor(ColorOwn.DEFAULT_BACKGROUND);
}
else {
// #295: if bg is the default, use background transparency, but if bg has been set reuse its transparency (otherwise transparency= would only work if the line comes after bg=)
ColorOwn oldBg = getBackgroundColor();
int newAlpha = oldBg == ColorOwn.DEFAULT_BACKGROUND ? Transparency.BACKGROUND.getAlpha() : oldBg.getAlpha();
setBackgroundColor(ColorOwn.forString(color, newAlpha));
}
}
public final void setBackgroundColor(ColorOwn color) {
if (color == null) {
style.setBackgroundColor(ColorOwn.DEFAULT_BACKGROUND);
}
else {
style.setBackgroundColor(color);
}
}
public ColorOwn getForegroundColor() {
return style.getForegroundColor();
}
public ColorOwn getBackgroundColor() {
return style.getBackgroundColor();
}
public double getLineWidth() {
return style.getLineWidth();
}
public void resetColorSettings() {
setForegroundColor(FacetConstants.FOREGROUND_COLOR_KEY);
setBackgroundColorAndKeepTransparency(FacetConstants.BACKGROUND_COLOR_KEY);
}
public final void setFontSize(double fontSize) {
assertDoubleRange(fontSize);
style.setFontSize(fontSize);
}
public double getFontSize() {
return style.getFontSize();
}
public final void setLineType(LineType type) {
style.setLineType(type);
}
public LineType getLineType() {
return style.getLineType();
}
public final void setLineWidth(double lineWidth) {
assertDoubleRange(lineWidth);
style.setLineWidth(lineWidth);
}
private void assertDoubleRange(double doubleValue) {
if (doubleValue < 0 || doubleValue > 5000) {
throw new StyleException("value must be >=0 and <=5000");
}
}
public void resetStyle() {
resetColorSettings();
style.setFontSize(getDefaultFontSize());
style.setLineType(LineType.SOLID);
style.setLineWidth(1);
}
public Style getStyleClone() {
return style.cloneFromMe();
}
public void setStyle(Style style) {
this.style = style.cloneFromMe();
}
public void drawAll() {
for (DrawFunction d : drawablesBackground) {
d.run();
}
for (DrawFunction d : drawablesForeground) {
d.run();
}
}
public double getDistanceBorderToText() {
return 5;
}
public double getDistanceBetweenTextLines() {
return 3;
}
/**
* @param singleLine single line (no \n), no further processing of the String takes place.
* i.e. The text is printed unmodified with the given formating.
* @return the dimension the text would need if printed with the current settings
*/
protected DimensionDouble textDimension(StringStyle singleLine) {
return textDimensionHelper(singleLine);
}
/* HELPER METHODS */
/**
* @param singleLine single line (no \n), no further processing of the String takes place.
* i.e. The text is printed unmodified with the given formating.
* @return the dimension the text would need if printed with the current settings
*/
protected abstract DimensionDouble textDimensionHelper(StringStyle singleLine);
protected abstract double getDefaultFontSize();
/* DRAW METHODS */
public void drawRectangle(Rectangle rect) {
drawRectangle(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
}
public void drawLine(Line line) {
drawLine(line.getStart().getX(), line.getStart().getY(), line.getEnd().getX(), line.getEnd().getY());
}
public void drawLine(double x1, double y1, double x2, double y2) {
drawLines(new PointDouble(x1, y1), new PointDouble(x2, y2));
}
public void drawLines(Collection<PointDouble> points) {
drawLines(points.toArray(new PointDouble[points.size()]));
}
public void drawLines(Line... lines) {
drawLines(Lines.toPoints(lines));
}
protected StringStyle escapeAndAnalyzeSingleLine(String singleLineWithMarkup) {
return StringStyle.analyzeFormatLabels(StringStyle.replaceNotEscaped(singleLineWithMarkup));
}
public void print(String multiLineWithMarkup, double x, double y, AlignHorizontal align) {
print(multiLineWithMarkup, new PointDouble(x, y), align);
}
public void print(StringStyle singleLine, double x, double y, AlignHorizontal align) {
printHelper(new StringStyle[] { singleLine }, new PointDouble(x, y), align);
}
/**
* @param multiLineWithMarkup can contain multiple lines (separated by \n). Each line is then analyzed and printed.
* @param point
* @param align the horizontal alignment
*/
public void print(String multiLineWithMarkup, PointDouble point, AlignHorizontal align) {
String[] lines = multiLineWithMarkup.split("\n");
StringStyle[] formatedLines = new StringStyle[lines.length];
for (int i = 0; i < lines.length; i++) {
formatedLines[i] = escapeAndAnalyzeSingleLine(lines[i]);
}
printHelper(formatedLines, point, align);
}
/**
* @param lines each element is a single line (no \n), no further processing of the String takes place.
* i.e. The each element is printed unmodified with the given formating.
* @param format the format which is used for each line
* @param point
* @param align the horizontal alignment
*/
public void print(String[] lines, Set<FormatLabels> format, PointDouble point, AlignHorizontal align) {
StringStyle[] formatedLines = new StringStyle[lines.length];
for (int i = 0; i < lines.length; i++) {
formatedLines[i] = new StringStyle(format, lines[i]);
}
printHelper(formatedLines, point, align);
}
/**
* @param x topLeft corner of the surrounding rectangle of the ellipse
* @param y topLeft corner of the surrounding rectangle of the ellipse
* @param width of the full ellipse, i.e. width of the surrounding rectangle of the ellipse
* @param height of the full ellipse, i.e. height of the surrounding rectangle of the ellipse
* @param start of the arc in degrees. 0 corresponds to the right side of a horizontal line. 90 corresponds to the top of a vertical line.
* @param extent can be up to 360 (extend in degrees from the start parameter)
*/
public abstract void drawArc(double x, double y, double width, double height, double start, double extent, boolean open);
public abstract void drawCircle(double x, double y, double radius);
public abstract void drawEllipse(double x, double y, double width, double height);
public abstract void drawLines(PointDouble... points);
public abstract void drawRectangle(double x, double y, double width, double height);
public abstract void drawRectangleRound(double x, double y, double width, double height, double radius);
/**
* @param lines each element is a single line (no \n), no further processing of the String takes place.
* i.e. The each element is printed unmodified with the given formating.
* @param point
* @param align the horizontal alignment
*/
public abstract void printHelper(StringStyle[] lines, PointDouble point, AlignHorizontal align);
}