package holeg.model; import holeg.interfaces.TimelineDependent; import holeg.model.Flexibility.FlexState; import holeg.ui.controller.IndexTranslator; import holeg.ui.model.IdCounter; import holeg.utility.math.vector.Vec2f; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.logging.Logger; /** * The class "HolonElement" represents any possible element that can be added to * a HolonObject. * * @author Gruppe14 */ public class HolonElement implements TimelineDependent { private static final Logger log = Logger.getLogger(HolonElement.class.getName()); /** * Owner of the Element */ public transient HolonObject parentObject; /** * Whether the gadget is active or not (currently uses/produces the energy in energyPerElement) */ public boolean active; public Priority priority = Priority.Low; public List flexList = new ArrayList<>(); /** * Points of new TestGraph * Represent the Graph * the X component from a Point is period from 0..1 * the Y component from a Point is the percentage from 0..1 */ private LinkedList graphPoints = new LinkedList<>(); /** * Name of the gadget, e.g. TV */ private String name; /** * Amount of same elements */ private float energy; private Period period = new Period(); /* * Energy at each point of the graph with 100 predefined points. At the * beginning, it starts with all values at energyPerElement. * If switched to flexible, this represents the maximum of usable energy */ private transient float[] curveSample; private transient float actualEnergy = 0; /** * Create a new HolonElement with a user-defined name, amount of the same * element, energyPerElement and corresponding model. * * @param eleName String * @param energy float */ /** * same as standard constructor, but with already given id (so the counter is not increased twice) */ public HolonElement(HolonObject parentObject, String eleName, float energy) { this.parentObject = parentObject; setName(eleName); setEnergy(energy); this.active = true; initGraphPoints(); sampleGraph(); } /** * Create a copy of the HolonElement given each one a new ID. * * @param other element to copy */ public HolonElement(HolonElement other) { this.parentObject = other.parentObject; this.priority = other.getPriority(); this.period = other.period; this.flexList = new ArrayList<>(other.flexList); setName(other.getName()); setEnergy(other.getEnergy()); this.active = other.active; setGraphPoints(new LinkedList<>()); for (Vec2f p : other.getGraphPoints()) { this.graphPoints.add(new Vec2f(p)); } this.actualEnergy = other.actualEnergy; sampleGraph(); } @Override public Period getPeriod() { return period; } @Override public void setPeriod(Period period) { this.period = period; } /** * Get the user-defined Name. * * @return the name String */ public String getName() { return name; } /** * Set the name to any new name. * * @param name the name to set */ public void setName(String name) { this.name = name; } /** * Get the energyPerElement value of the selected Element. * * @return the energyPerElement */ public float getEnergy() { return energy; } /** * Set the energyPerElement value of the selected Element. * * @param energyPerElement the energyPerElement to set */ public void setEnergy(float energyPerElement) { log.finest(this.energy + " -> " + energyPerElement); this.energy = energyPerElement; } /** * Check the HolonElemnet is a Producer * * @return true when the energy used be each element is higher then 0 */ public boolean isProducer() { return (energy > 0); } /** * Check the HolonElemnet is a Consumer * * @return true when the energy used be each element is lower then 0 */ public boolean isConsumer() { return (energy < 0); } public Priority getPriority() { return priority; } public void setPriority(Priority priority) { this.priority = priority; } public String toString() { return "[HolonElement: " + ", eleName=" + name + ", parentName=" + parentObject.getName() + ", active=" + active + ", energyPerElement used=" + energy + "]"; } /** * Initialize the {@link HolonElement#graphPoints} List with the normal 2 Points at 100%. */ private void initGraphPoints() { graphPoints.clear(); graphPoints.add(new Vec2f(0f, 1.0f)); graphPoints.add(new Vec2f(1f, 1.0f)); } /** * Getter for the graphPoint List. * * @return {@link HolonElement#graphPoints} */ public LinkedList getGraphPoints() { return graphPoints; } /** * Setter for the graphPoint List. */ public void setGraphPoints(LinkedList graphPoints) { this.graphPoints = graphPoints; } //interfaces.GraphEditable @Override public GraphType getGraphType() { return GraphType.doubleGraph; } @Override public LinkedList getStateGraph() { return getGraphPoints(); } @Override public void sampleGraph() { curveSample = sampleGraph(100); } @Override public void reset() { initGraphPoints(); sampleGraph(); } /** * Generate out of the Graph Points a array of floats that represent the Curve at each sample position. The Values are in the Range [0,1]. * e.g. 0.0 represent: "0%" , 0.34 represent: 34% 1.0 represent: "100%" * * @param sampleLength amount of samplePositions. The positions are equidistant on the Range[0,1]. * @return the float array of samplepoints. */ private float[] sampleGraph(int sampleLength) { ListIterator iter = this.graphPoints.listIterator(); Vec2f before = iter.next(); Vec2f after = iter.next(); float[] sampleCurve = new float[sampleLength]; for (int i = 0; i < sampleLength; i++) { double graphX = (double) i / (double) (sampleLength - 1); //from 0.0 to 1.0 if (graphX > after.x) { before = after; after = iter.next(); } //t to determine how many percentage the graphX is to the next Point needed to calc Bezier //inverseLerp(valueBetween, min, max) (valueBetween - min) / (max - min) // e.g. old.x = 0.4, actual.x = 0.8 and graphX = 0.6 then t is 0.5 double t = (after.x - before.x > 0) ? (graphX - before.x) / (after.x - before.x) : 0.0; sampleCurve[i] = (float) getYBetweenTwoPoints(t, before, after); } return sampleCurve; } /** * Helper method for {@link HolonElement#sampleGraph(int)}. *

* Its get the start and Endposition and calculate the Points in between for the Bezi�r Curve. * Then its get the Y Value a.k.a. the percentage from the curve at the X value t. * * @param t is in Range [0,1] and represent how much the X value is traverse along the Curve between the two Points. * @param start is the start Point of the Curve. * @param end is the end Point of the Curve. * @return the percentage from the Curve at the X Value based on t. */ private double getYBetweenTwoPoints(double t, Vec2f start, Vec2f end) { float mitte = (start.x + end.x) * 0.5f; Vec2f bezier = getBezierPoint(t, start, new Vec2f(mitte, start.y), new Vec2f(mitte, end.y), end); return bezier.y; } /** * Helper method for {@link HolonElement#getYBetweenTwoPoints(double, Vec2f, Vec2f)}. *

* A Method for a normal Cubic Bezier Curve. A Cubic Bezier curve has four control points. * * @param t is in Range [0,1] how much it traverse along the curve. * @param p0 StartPoint * @param p1 ControlPoint * @param p2 ControlPoint * @param p3 EndPoint * @return the BezierPosition at t. */ private Vec2f getBezierPoint(double t, Vec2f p0, Vec2f p1, Vec2f p2, Vec2f p3) { /* * Calculate Bezi�r: * B(t) = (1-t)^3 * P0 + 3*(1-t)^2 * t * P1 + 3*(1-t)*t^2 * P2 + t^3 * P3 , 0 < t < 1 * * Source: //http://www.theappguruz.com/blog/bezier-curve-in-games */ Vec2f bezier = new Vec2f(); double OneSubT = 1 - t; double OneSubT2 = Math.pow(OneSubT, 2); double OneSubT3 = Math.pow(OneSubT, 3); double t2 = Math.pow(t, 2); double t3 = Math.pow(t, 3); bezier.x = (float) (OneSubT3 * p0.x + 3 * OneSubT2 * t * p1.x + 3 * OneSubT * t2 * p2.x + t3 * p3.x); bezier.y = (float) (OneSubT3 * p0.y + 3 * OneSubT2 * t * p1.y + 3 * OneSubT * t2 * p2.y + t3 * p3.y); return bezier; } /* * STATE */ //TODO(Tom2021-12-1): public -> package public void calculateState(int timestep) { flexList.forEach(flex -> flex.calculateState(timestep)); float energyWhenActive = energy * this.curveSample[IndexTranslator.getEffectiveIndex(this, timestep)]; actualEnergy = isOn() ? energyWhenActive : 0; } /** * Get the energyPerElement currently(at given time step) available */ public float calculateExpectedEnergyAtTimeStep(int timestep) { float energyWhenActive = energy * this.curveSample[IndexTranslator.getEffectiveIndex(this, timestep)]; return active ? energyWhenActive : 0; } public float getActualEnergy() { return actualEnergy; } public boolean isOn() { //return isFlexActive()?!active:active; //Bool logic XOR return isFlexActive() ^ active; } public boolean isFlexActive() { return flexList.stream().anyMatch(flex -> flex.getState() == FlexState.IN_USE || flex.getState() == FlexState.ON_COOLDOWN); } public enum Priority { Low, Medium, High, Essential } public void updateReference(){ flexList.forEach(flex -> flex.setElement(this)); } }