Commit 723c098e authored by Nico Mack's avatar Nico Mack

Added leaders to ArcRangeGraph.

Introduced new VariableFormat utility class
parent 62273dfb
......@@ -110,7 +110,7 @@ public class NumericalVariable extends Variable<Double> {
return builder.toString();
}
public String formatValue() {
public String getFormattedValue() {
return format.format(value / scale);
}
......@@ -211,6 +211,22 @@ public class NumericalVariable extends Variable<Double> {
return this.maxValue;
}
/**
* Returns the normalized value of this variable if min and max value of variable have been
* defined, the plain value otherwise.
*
* @return the normalized value, i.e 0 <= x <= 1 of the variable.
*/
public double getNormalizedValue() {
double range = 0;
if ((minValue != -Double.MAX_VALUE) && (maxValue != Double.MAX_VALUE)) {
range = maxValue - minValue;
}
return (range > 0) ? (value - minValue) / range : value;
}
/**
*
* @param scale
......
/**
* Copyright Luxembourg Institute of Science and Technology, 2017. All rights reserved. If you wish
* to use this code for any purpose, please contact the author(s).
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package lu.list.itis.dkd.tui.utility;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
*/
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
public class Leader {
private double radialOffset;
private double leaderLineLength;
private double landingLineLength;
private Shape shape;
private Font labelFont;
private Color fillColour;
private Color strokeColour;
private Color labelColour;
private int strokeWidth;
private Stroke lineStroke;
private Line2D.Double leaderLine;
private Line2D.Double landingLine;
private String labelText;
private LineMetrics labelMetrics;
private Point labelPosition;
// ***************************************************************************
// * Constants *
// ***************************************************************************
private static final Point ZERO_ORIGIN = new Point(0, 0, 0, ScreenCoordinates.class);
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s) *
// ***************************************************************************
// ---------------------------------------------------------------------------
public Leader(double radialOffset, double leaderLineLength, double landingLineLength) {
this.radialOffset = radialOffset;
this.leaderLineLength = leaderLineLength;
this.landingLineLength = landingLineLength;
this.strokeWidth = 1;
this.lineStroke = new BasicStroke(strokeWidth);
this.strokeColour = Color.WHITE;
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Class Body *
// ***************************************************************************
// ---------------------------------------------------------------------------
public void setFont(Font font) {
this.labelFont = font;
}
// ---------------------------------------------------------------------------
public void setLabelShape(Shape shape) {
this.shape = shape;
}
// ---------------------------------------------------------------------------
public void setFillColour(Color colour) {
this.fillColour = colour;
}
// ---------------------------------------------------------------------------
public void setStrokeColour(Color colour) {
this.strokeColour = colour;
}
// ---------------------------------------------------------------------------
public void setStrokeWidth(int width) {
this.strokeWidth = width;
this.lineStroke = new BasicStroke(this.strokeWidth);
}
// ---------------------------------------------------------------------------
public void setLabelColour(Color colour) {
this.labelColour = colour;
}
// ---------------------------------------------------------------------------
public void updateLeader(float leaderAngle, String label) {
float inner = (float) radialOffset;
float outer = (float) (radialOffset + leaderLineLength);
// Build Leader Line using polar coordinates
Point leaderStart = new Point(inner, inner, leaderAngle, ScreenCoordinates.class);
Point leaderEnd = new Point(outer, outer, leaderAngle, ScreenCoordinates.class);
Point start = PolarCoordinateHelper.polarToCarthesian(ZERO_ORIGIN, leaderStart);
Point end = PolarCoordinateHelper.polarToCarthesian(ZERO_ORIGIN, leaderEnd);
leaderLine = new Line2D.Double(new Point2D.Double(start.x, start.y), new Point2D.Double(end.x, end.y));
// Build Landing Line
int quadrant = PolarCoordinateHelper.getQuadrant(leaderAngle);
boolean flipped = ((quadrant == 1) || (quadrant == 2));
if (landingLineLength > 0) {
start = end.clone();
end.x += (flipped) ? -landingLineLength : landingLineLength;
landingLine = new Line2D.Double(new Point2D.Double(start.x, start.y), new Point2D.Double(end.x, end.y));
}
if (shape != null) {
end.x += (flipped) ? -shape.getBounds2D().getWidth() : 0;
}
labelPosition = end.clone();
labelText = label;
if (this.labelFont != null) {
labelMetrics = labelFont.getLineMetrics(this.labelText, new FontRenderContext(null, true, true));
}
}
// ---------------------------------------------------------------------------
public void paint(Graphics2D canvas) {
AffineTransform transform = canvas.getTransform();
Graphics2D localCanvas = (Graphics2D) canvas.create();
localCanvas.setTransform(transform);
localCanvas.setPaint(this.strokeColour);
localCanvas.setStroke(this.lineStroke);
localCanvas.draw(this.leaderLine);
localCanvas.draw(this.landingLine);
if (this.shape != null) {
transform.translate(labelPosition.x, labelPosition.y);
localCanvas.setTransform(transform);
localCanvas.draw(this.shape);
if (this.fillColour != null) {
localCanvas.setPaint(this.fillColour);
localCanvas.fill(this.shape);
}
}
if ((this.labelColour != null) && (this.labelText != null)) {
localCanvas.setPaint(labelColour);
localCanvas.setFont(labelFont);
localCanvas.drawString(labelText, 5, (labelMetrics.getAscent() / 2));
}
localCanvas.dispose();
}
}
/**
* Copyright Luxembourg Institute of Science and Technology, 2017. All rights reserved. If you wish
* to use this code for any purpose, please contact the author(s).
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package lu.list.itis.dkd.tui.utility;
import lu.list.itis.dkd.tui.cps.variable.Variable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
*/
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
public class VariableFormat {
private List<Character> placeholders;
private String template;
// ***************************************************************************
// * Constants *
// ***************************************************************************
private static final char DISPLAY_NAME = 'd';
private static final char NAME = 'n';
private static final char VALUE = 'v';
private static final char FORMATTED_VALUE = 'f';
private static final char UNIT = 'u';
private static final char MIN_VALUE = 'm';
private static final char MAX_VALUE = 'x';
private static final char SCALE = 's';
private static final char TYPE = 't';
private static final Map<Character, String> VARIABLE_PROPERTIES = new HashMap<>();
static {
VARIABLE_PROPERTIES.put(DISPLAY_NAME, "DisplayName"); //$NON-NLS-1$
VARIABLE_PROPERTIES.put(NAME, "Name"); //$NON-NLS-1$
VARIABLE_PROPERTIES.put(VALUE, "Value"); //$NON-NLS-1$
VARIABLE_PROPERTIES.put(FORMATTED_VALUE, "FormattedValue"); //$NON-NLS-1$
VARIABLE_PROPERTIES.put(UNIT, "Unit"); //$NON-NLS-1$
VARIABLE_PROPERTIES.put(MIN_VALUE, "MinValue"); //$NON-NLS-1$
VARIABLE_PROPERTIES.put(MAX_VALUE, "MaxValue"); //$NON-NLS-1$
VARIABLE_PROPERTIES.put(SCALE, "Scale"); //$NON-NLS-1$
VARIABLE_PROPERTIES.put(TYPE, "Type"); //$NON-NLS-1$
}
private static final String PLACEHOLDER = "{}";
private static final Pattern VARIABLE_FORMAT_PATTERN = Pattern.compile("([dnfvumxst])", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
// private static final Pattern VARIABLE_FORMAT_PATTERN =
// Pattern.compile("([dnfumxst]|v(\\d+(\\.\\d+)?)?)", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
// private static final Pattern NUMERIC_FORMAT_PATTERN = Pattern.compile("\\d+(\\.\\d+)?)",
// Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
private static final Logger LOGGER = LoggerFactory.getLogger(VariableFormat.class.getSimpleName());
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s)
// ***************************************************************************
// ---------------------------------------------------------------------------
public VariableFormat(String formatString) {
this.template = this.parseFormatString(formatString);
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Primitive(s)
// ***************************************************************************
// ---------------------------------------------------------------------------
private String parseFormatString(String format) {
placeholders = new ArrayList<>();
StringBuilder templateBuilder = new StringBuilder();
Matcher formatMatcher = VARIABLE_FORMAT_PATTERN.matcher(format);
int position = 0;
while (formatMatcher.find()) {
templateBuilder.append(format.substring(position, formatMatcher.start()));
placeholders.add(formatMatcher.group(1).charAt(0));
templateBuilder.append(PLACEHOLDER);
position = formatMatcher.end();
}
templateBuilder.append(format.substring(position));
return templateBuilder.toString();
}
// ---------------------------------------------------------------------------
/**
* Returns the value of the specified property. The method relies on reflection to call the
* getter method for the specified property.
*
* @param p_Property
* specifies the name of the property to get value of.
* @return The return value of the getter method for the specified property, if available.
* Method returns <code>null</code> if either specified property is <code>null</code> or
* no getter exists.
*/
// ---------------------------------------------------------------------------
private Object getProperty(Variable<?> variable, String property) {
Class<?>[] parameterTypes = null;
Object[] parameters = null;
String methodName = ""; //$NON-NLS-1$
Method getter;
Object value = null;
try {
methodName = "get" + property; //$NON-NLS-1$
getter = variable.getClass().getMethod(methodName, parameterTypes);
value = getter.invoke(variable, parameters);
} catch (NoSuchMethodException exception) {
LOGGER.warn("Variable {} of type {} does not have a {} method!", variable.getName(), variable.getType(), methodName); //$NON-NLS-1$
return null;
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
LOGGER.error("Error while calling method {} on Variable {} of type {}!", methodName, variable.getName(), variable.getType(), e); //$NON-NLS-1$
}
return value;
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Class Body
// ***************************************************************************
// ---------------------------------------------------------------------------
public String format(Variable<?> variable) {
int index = 0;
Object[] values = new Object[placeholders.size()];
for (Character placeholder : placeholders) {
values[index++] = getProperty(variable, VARIABLE_PROPERTIES.get(placeholder));
}
return StringUtils.build(this.template, values);
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * End of Class
// ***************************************************************************
// ---------------------------------------------------------------------------
}
......@@ -43,11 +43,15 @@ public abstract class BaseArcRangeGraphBuilder<B extends BaseArcRangeGraphBuilde
public ColorPair strokeColour;
public ColorPair labelColour;
public ColorPair textColour;
public ColorPair leaderColour;
public ColorPair faceColour;
public ColorPair bezelColour;
public int strokeWidth;
public int landingLineLength;
public int leaderLineLength;
public Shape labelShape;
public Font textFont;
public String labelFormat;
// ***************************************************************************
// * Constants *
......@@ -101,18 +105,22 @@ public abstract class BaseArcRangeGraphBuilder<B extends BaseArcRangeGraphBuilde
/** Optional fields */
strokeWidth = BootstrappingUtils.getContentAsInteger(rootElement, Externalization.STROKE_WIDTH_NODE, BootstrappingUtils.OPTIONAL, 1, context);
leaderLineLength = BootstrappingUtils.getContentAsInteger(rootElement, Externalization.LEADER_LINE_LENGTH_NODE, BootstrappingUtils.OPTIONAL, 0, context);
landingLineLength = BootstrappingUtils.getContentAsInteger(rootElement, Externalization.LANDING_LINE_LENGTH_NODE, BootstrappingUtils.OPTIONAL, 0, context);
rampingTime = BootstrappingUtils.getContentAsInteger(rootElement, Externalization.RAMPING_TIME_NODE, BootstrappingUtils.OPTIONAL, 500, context);
fillColour = buildColorPair(rootElement.getChild(Externalization.FILL_COLOUR_ELEMENT), BootstrappingUtils.MANDATORY, context);
textColour = buildColorPair(rootElement.getChild(Externalization.TEXT_COLOUR_ELEMENT), BootstrappingUtils.OPTIONAL, null);
strokeColour = buildColorPair(rootElement.getChild(Externalization.STROKE_COLOUR_ELEMENT), BootstrappingUtils.MANDATORY, context);
labelColour = buildColorPair(rootElement.getChild(Externalization.LABEL_COLOUR_ELEMENT), BootstrappingUtils.MANDATORY, context);
leaderColour = buildColorPair(rootElement.getChild(Externalization.LEADER_COLOUR_NODE), BootstrappingUtils.OPTIONAL, null);
faceColour = buildColorPair(rootElement.getChild(Externalization.FACE_COLOUR_ELEMENT), BootstrappingUtils.OPTIONAL, context);
bezelColour = buildColorPair(rootElement.getChild(Externalization.BEZEL_COLOUR_ELEMENT), BootstrappingUtils.OPTIONAL, context);
labelShape = ShapeBootstrapper.getShape(rootElement.getChild(Externalization.LABEL_SHAPE_NODE), context, callback);
title = BootstrappingUtils.getContentAsString(rootElement, Externalization.TITLE_NODE, BootstrappingUtils.OPTIONAL, null);
labelFormat = BootstrappingUtils.getContentAsString(rootElement, Externalization.LABEL_FORMAT_NODE, BootstrappingUtils.OPTIONAL, null);
String fontName = BootstrappingUtils.getContentAsString(rootElement, Externalization.FONT_NODE, BootstrappingUtils.OPTIONAL, "Monospaced"); //$NON-NLS-1$
Integer fontSize = BootstrappingUtils.getContentAsInteger(rootElement, Externalization.FONT_SIZE_NODE, BootstrappingUtils.OPTIONAL, 14);
......
/**
* Copyright Luxembourg Institute of Science and Technology, 2017. All rights reserved. If you wish
* to use this code for any purpose, please contact the author(s).
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package lu.list.itis.dkd.tui.utility;
import static org.junit.Assert.assertEquals;
import lu.list.itis.dkd.tui.cps.variable.NumericalVariable;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
*/
public class VariableFormatTest {
private NumericalVariable numeric;
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
numeric = new NumericalVariable("TestVariable", "TPS", Math.PI);
numeric.setDisplayName("Variable Under Test");
numeric.setMaxValue(2 * Math.PI);
numeric.setMinValue(0);
numeric.setScale(1);
numeric.setNumberOfDecimals(4);
}
/**
* @throws java.lang.Exception
*/
@After
public void tearDown() throws Exception {}
@Test
public void test() {
VariableFormat formatter = new VariableFormat("d = f [u]");
String formatted = formatter.format(numeric);
assertEquals("Variable Under Test = 3.1416 [TPS]", formatted); //$NON-NLS-1$
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment