All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mgmtp.perfload.loadprofiles.ui.util.GraphPointsCalculator Maven / Gradle / Ivy

There is a newer version: 1.1.0
Show newest version
/*
 * Copyright (c) 2013 mgm technology partners GmbH
 *
 * 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 com.mgmtp.perfload.loadprofiles.ui.util;

import static com.google.common.collect.Sets.newLinkedHashSet;
import static com.google.common.collect.Sets.newTreeSet;
import static java.lang.Math.ulp;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.Set;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.mgmtp.perfload.loadprofiles.model.CurveAssignment;
import com.mgmtp.perfload.loadprofiles.ui.model.LoadProfileEntity;
import com.mgmtp.perfload.loadprofiles.ui.model.Stairs;

/**
 * Calculates points for the load curve graph.
 * 
 * @author rnaegele
 */
public class GraphPointsCalculator {

	/**
	 * Calculates graph points for the given list of curve assignments (one graph per curve name).
	 * 
	 * @param stairs
	 *            The list of curve assignments
	 * @return A map of lists of points. The curve names are the keys.
	 */
	public Map> calculatePoints(final Collection stairs) {
		List curveNames = computeCurveNames(stairs);

		Map> pointsMap = Maps.newLinkedHashMap();

		for (final String curveName : curveNames) {
			List curves = Lists.newArrayList();

			for (Stairs item : stairs) {
				if (item.operation.getName().equals(curveName)) {
					CurveItem curve = new CurveItem(calculatePoints(item));
					curves.add(curve);
				}
			}

			pointsMap.put(curveName, sumUpCurves(curves));
		}

		return pointsMap;
	}

	/**
	 * Sums up the specified curves returning a list of points representing one curve.
	 * 
	 * @param curves
	 *            A list of separate curves
	 * @return The summed list of points
	 */
	private Set sumUpCurves(final List curves) {
		Map curvePoints = Maps.newHashMap();
		Set xValues = newLinkedHashSet();

		// Determine all x-values that have points on the separate curves
		// and collect all points by their x-values
		int curveCount = curves.size();
		for (int i = 0; i < curveCount; ++i) {
			CurveItem curve = curves.get(i);
			for (Point p : curve.getPoints()) {
				Double x = p.getX();
				Point[] pointsForX = curvePoints.get(x);
				if (pointsForX == null) {
					pointsForX = new Point[curveCount];
					curvePoints.put(x, pointsForX);
				}
				// Store point in the array for the x-value. Array elements may remain emtpy (null),
				// if there is no point for an x-value for the curve with the current index.
				pointsForX[i] = p;
				xValues.add(x);
			}
		}

		Set points = newLinkedHashSet();

		// iterate over x-values
		for (Double x : xValues) {
			//			if (i == 0) {
			//				// y is zero for the first point
			//				points.add(new Point(x, 0.));
			//			} else {
			// Get points for a certain x-value
			Point[] pointsForX = curvePoints.get(x);
			double sumY = 0.;
			for (int j = 0, len = pointsForX.length; j < len; ++j) {
				Point p = pointsForX[j];
				if (p == null) {
					CurveItem curve = curves.get(j);
					if (curve.isInRange(x)) {
						// If the curve is in range but does not have a direct y-value for this x,
						// we need to calculate the value on the line between the previous and the next point of the curve.
						double y = curve.calculateYForX(x);

						// Add calculated y-value
						sumY += y;
					}
				} else {
					// If there is a y-value for this x, we can directly add it.
					sumY += p.getY();
				}
				//				}

			}
			// Create a new point with current x-value and the sum of y-values
			points.add(new Point(x, sumY));
		}

		return points;
	}

	/**
	 * Calculates graph points for the given curve assignment.
	 * 
	 * @param stairs
	 *            The load profile entity
	 * @return A list of points
	 */
	public Set calculatePoints(final Stairs stairs) {
		Set points = newLinkedHashSet();

		int numSteps = stairs.numSteps;
		int offset = stairs.a + stairs.b;

		points.add(Point.of(stairs.t0, 0));

		for (int i = 0; i < numSteps; ++i) {
			points.add(Point.of(stairs.t0 + stairs.a + i * offset, (i + 1) * stairs.h));
			points.add(Point.of(stairs.t0 + stairs.a + stairs.b + i * offset, (i + 1) * stairs.h));
		}

		points.add(Point.of(stairs.t0 + numSteps * offset + stairs.c, 0));

		// A single curve must have distinct x-values, which is not the case, if a or c are set to 0. Otherwise the logic
		// for summing up single curves would not work. Thus, we simply move the first x-value of two distinct
		// ones by an ulp to the left.
		Point previous = null;
		Set changedPoints = newLinkedHashSet();
		for (Point p : points) {
			if (previous != null) {
				if (previous.getX() == p.getX()) {
					changedPoints.add(Point.of(previous.getX() - ulp(previous.getX()), previous.getY()));
				} else {
					changedPoints.add(previous);
				}
			}
			previous = p;
		}
		changedPoints.add(previous);
		return changedPoints;
	}

	private List computeCurveNames(final Collection loadProfileEnities) {
		List curveNames = Lists.newArrayList();

		for (LoadProfileEntity lpe : loadProfileEnities) {
			if (lpe instanceof CurveAssignment) {
				CurveAssignment ca = (CurveAssignment) lpe;
				if (!curveNames.contains(ca.operation.getName())) {
					curveNames.add(ca.operation.getName());
				}
			}
		}
		Collections.sort(curveNames);

		return curveNames;
	}

	private static class CurveItem {
		private final NavigableSet points;
		private final double minX;
		private final double maxX;

		/**
		 * Creates a curve from the specified points
		 * 
		 * @param points
		 *            the points that make up the curve
		 */
		CurveItem(final Set points) {
			this.points = newTreeSet(points);
			this.minX = this.points.iterator().next().getX();
			this.maxX = this.points.descendingIterator().next().getX();
		}

		boolean isInRange(final double x) {
			return x >= minX && x <= maxX;
		}

		/**
		 * Calculates the y-value on this curve for the given x.
		 */
		double calculateYForX(final double x) {
			Point p1 = null;
			for (Point p : points) {
				if (p.getX() == x) {
					return p.getY();
				}
				if (p1 != null) {
					Point p2 = p;
					if (p1.getX() < x && p2.getX() > x) {
						return calculateYOnLine(p1, p2, x);
					}
				}
				p1 = p;
			}
			throw new IllegalArgumentException(x + " not in range of curve");
		}

		/**
		 * Calculates the y-value for x on a line through points p1 and p2.
		 */
		double calculateYOnLine(final Point p1, final Point p2, final double x) {
			// slope of a line:
			// m = (y2 - y1) / (x2 - x1)
			double m = (p2.getY() - p1.getY()) / (p2.getX() - p1.getX());

			// y-intercept of the line
			// c = y - mx
			double c = p1.getY() - m * p1.getX();

			// value for y on the line between p1 and p2
			double y = m * x + c;

			return y;
		}

		public NavigableSet getPoints() {
			return newTreeSet(points);
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy