/*Description - Returns a Vector3[] of the points along a requested path * to the requested target value. This will also include the current * specified location. * * Params: pathType -> The type of path to calculate * strength -> The energy to add / remove if negative * currentLocation -> The points current location * graphOrigin -> The origin(Translation) of the Mesh specified by graphSpace * graphSpace -> The type of graph to create the point from * */ public static Vector3[] GetConstraintPath(pathType pathType, double strength, Vector3 currentLocation, Vector3 graphOrigin, graphSpaceEnums graphSpace) { ArrayList pathInThermoPoints = new ArrayList(); //Used to easily store all of a point's atributes, then depending on graph type use specific attributes bool inPositive = (strength > 0); //Will be used to switch between logic for adding or removing energy, "very nifty this is" Vector3[] pathPoints; //Returned to caller int gridPointStart = 0; //Index in V/T_Vals that provided point starts from currentLocation = currentLocation - graphOrigin; //currentLocation must be adjusted out of unity space double currentT = (double)Mathf.Exp(currentLocation.x); //Real temperature of current point double currentV = (double)Mathf.Exp(-currentLocation.z); //Real volume of current point double currentU = ThermoMath.GetU_TV(currentT, currentV, false); //Real energy at current point double currentP = ThermoMath.GetP_TV(currentT, currentV, false); //Real Pressure at current point double currentH = ThermoMath.GetH_TV(currentT, currentV, false); //Real Enthalpy at current point double currentS = ThermoMath.GetS_TV(currentT, currentV, false); //Real Entropy at current point double targetU = currentU + strength; //Target Internal Energy based on the strength double finalV = ThermoMath.VOTX(currentT, targetU, pathType.CONSTANT_U); //Volume at targetU double finalT = ThermoMath.TOVX(currentV, targetU, pathType.CONSTANT_U); //Temperature at targetU //If no positive path exists return an empty array if(inPositive ? currentU > targetU : currentU < targetU) return new Vector3[]{}; //Construct path ThermoPoints depending on constraint switch(pathType){ /*CONSTANT TEMPERATURE*/ case pathType.CONSTANT_T: //Find the first specific volume on the grid that is greater than the specific volume of the current point for(gridPointStart = 0; gridPointStart < V_Vals.Length; gridPointStart++){ if(V_Vals[gridPointStart] > currentV) break; } //Adjust counter depending on if we are adding or removing energy gridPointStart = inPositive ? gridPointStart : (gridPointStart - 1); //Set the first point in the path to be the current point pathInThermoPoints.Add(new ThermoPoint(currentT, currentV, ThermoMath.GetP_TV(currentT, currentV, false), ThermoMath.GetU_TV(currentT, currentV, false), ThermoMath.GetH_TV(currentT, currentV, false), ThermoMath.GetS_TV(currentT, currentV, false), ThermoMath.area.UNKNOWN, 0)); //Find all points in the path that line up with the grid (spacing) and are below the target energy value for(gridPointStart = gridPointStart + 0; (inPositive ? (gridPointStart < V_Vals.Length) : (gridPointStart >= 0)) && (inPositive ? (ThermoMath.GetU_TV(currentT, V_Vals[gridPointStart], false) < targetU) : (ThermoMath.GetU_TV(currentT, V_Vals[gridPointStart], false) > targetU)); gridPointStart = (inPositive ? (gridPointStart+1) : (gridPointStart-1))){ pathInThermoPoints.Add(new ThermoPoint(currentT, V_Vals[gridPointStart], ThermoMath.GetP_TV(currentT, V_Vals[gridPointStart], false), ThermoMath.GetU_TV(currentT, V_Vals[gridPointStart], false), ThermoMath.GetH_TV(currentT, V_Vals[gridPointStart], false), ThermoMath.GetS_TV(currentT, V_Vals[gridPointStart], false), ThermoMath.area.UNKNOWN, pathInThermoPoints.Count)); } //Set the final point to the point with the target energy value if((inPositive ? (gridPointStart != 0) : (gridPointStart != -1)) && gridPointStart != V_Vals.Length) pathInThermoPoints.Add(new ThermoPoint(currentT, finalV, ThermoMath.GetP_TV(currentT, finalV, false), targetU, ThermoMath.GetH_TV(currentT, finalV, false), ThermoMath.GetS_TV(currentT, finalV, false), ThermoMath.area.UNKNOWN, pathInThermoPoints.Count)); break; /*CONSTANT VOLUME*/ case pathType.CONSTANT_V: //Find the first Temperature on the grid that is greater than the Temperature of the current point for(gridPointStart = 0; gridPointStart < T_Vals.Length; gridPointStart++){ if(T_Vals[gridPointStart] > currentT) break; } //Adjust counter depending on if we are adding or removing energy gridPointStart = inPositive ? gridPointStart : (gridPointStart - 1); //Set the first point in the path to be the current point pathInThermoPoints.Add(new ThermoPoint(currentT, currentV, ThermoMath.GetP_TV(currentT, currentV, false), ThermoMath.GetU_TV(currentT, currentV, false), ThermoMath.GetH_TV(currentT, currentV, false), ThermoMath.GetS_TV(currentT, currentV, false), ThermoMath.area.UNKNOWN, 0)); //Find all points in the path that line up with the grid (spacing) and are below the target energy value for(gridPointStart = gridPointStart + 0; (inPositive ? (gridPointStart < T_Vals.Length) : (gridPointStart >= 0)) && (inPositive ? (ThermoMath.GetU_TV(T_Vals[gridPointStart], currentV, false) < targetU) : (ThermoMath.GetU_TV(T_Vals[gridPointStart], currentV, false) > targetU)); gridPointStart = (inPositive ? (gridPointStart+1) : (gridPointStart-1))){ pathInThermoPoints.Add(new ThermoPoint(T_Vals[gridPointStart], currentV, ThermoMath.GetP_TV(T_Vals[gridPointStart], currentV, false), ThermoMath.GetU_TV(T_Vals[gridPointStart], currentV, false), ThermoMath.GetH_TV(T_Vals[gridPointStart], currentV, false), ThermoMath.GetS_TV(T_Vals[gridPointStart], currentV, false), ThermoMath.area.UNKNOWN, pathInThermoPoints.Count)); } //If we ended on a point not equal to the target energy value set the final point to the point with the //target energy value if((inPositive ? (gridPointStart != 0) : (gridPointStart != -1)) && gridPointStart != T_Vals.Length) pathInThermoPoints.Add(new ThermoPoint(finalT, currentV, ThermoMath.GetP_TV(finalT, currentV, false), targetU, ThermoMath.GetH_TV(finalT, currentV, false), ThermoMath.GetS_TV(finalT, currentV, false), ThermoMath.area.UNKNOWN, pathInThermoPoints.Count)); break; case pathType.CONSTANT_P: //Find the first Volume on the grid that is greater than the Volume of the current point for(gridPointStart = 0; gridPointStart < V_Vals.Length; gridPointStart++){ if(V_Vals[gridPointStart] > currentV) break; } //Adjust counter depending on if we are adding or removing energy gridPointStart = inPositive ? gridPointStart : (gridPointStart - 1); //Set the first point in the path to be the current point pathInThermoPoints.Add(new ThermoPoint(currentT, currentV, ThermoMath.GetP_TV(currentT, currentV, false), ThermoMath.GetU_TV(currentT, currentV, false), ThermoMath.GetH_TV(currentT, currentV, false), ThermoMath.GetS_TV(currentT, currentV, false), ThermoMath.area.UNKNOWN, 0)); //As we will be using currentT throughout the path calculation, set it based on the first volume //greater than that of the current position currentT = ThermoMath.TOVX(V_Vals[gridPointStart], currentP, pathType.CONSTANT_P); //Find all points in the path that line up with the grid (spacing) and are below the target energy value for(gridPointStart = gridPointStart + 0; (inPositive ? (gridPointStart < V_Vals.Length) : (gridPointStart >= 0)) && (inPositive ? (ThermoMath.GetU_TV(currentT, V_Vals[gridPointStart], false) < targetU && currentT < T_Vals[T_Vals.Length-1]) : (ThermoMath.GetU_TV(currentT, V_Vals[gridPointStart], false) > targetU && currentT > T_Vals[0])); gridPointStart = (inPositive ? (gridPointStart+1) : (gridPointStart-1))){ pathInThermoPoints.Add(new ThermoPoint( currentT, V_Vals[gridPointStart], currentP, ThermoMath.GetU_TV(currentT, V_Vals[gridPointStart], false), ThermoMath.GetH_TV(currentT, V_Vals[gridPointStart], false), ThermoMath.GetS_TV(currentT, V_Vals[gridPointStart], false), ThermoMath.area.UNKNOWN, pathInThermoPoints.Count) ); //Update currentT unless we are on the first/last Volume of the grid if(inPositive ? (gridPointStart+1 != V_Vals.Length) : (gridPointStart != 0)) currentT = ThermoMath.TOVX(V_Vals[gridPointStart+1], currentP, pathType.CONSTANT_P); } //Adjust final Temperature and Volume values in case we've extended beyond the surface bounds if(inPositive ? (finalV > V_Vals[V_Vals.Length-1]) : (finalV < V_Vals[0])) finalV = inPositive ? V_Vals[V_Vals.Length-1] : V_Vals[0]; if(inPositive ? (finalT > T_Vals[T_Vals.Length-1]) : (finalT < T_Vals[0])) finalT = inPositive ? T_Vals[T_Vals.Length-1] : T_Vals[0]; //If we ended on a point not equal to the target energy value set the final point to the point with the //target energy value if(inPositive ? (gridPointStart == V_Vals.Length) : (gridPointStart == -1)) pathInThermoPoints.Add(new ThermoPoint(finalT, finalV, currentP, targetU, ThermoMath.GetH_TV(finalT, finalV, false), ThermoMath.GetS_TV(finalT, finalV, false), ThermoMath.area.UNKNOWN, pathInThermoPoints.Count)); break; //CONSTANT INTERNAL ENERGY case pathType.CONSTANT_U: targetU = currentU; finalT = -1; finalV = -1; double t; double v; //double u; int length = 0; //Find where constant Internal Energy path will go out of bounds. for (gridPointStart = 0; gridPointStart < V_Vals.Length; gridPointStart++) { t = ThermoMath.TOVX(V_Vals[gridPointStart], targetU, pathType.CONSTANT_U); //t = ThermoMath.TOVU(V_Vals[gridPointStart], targetU); length++; if (t < .011) { break; } } //Find the first specific volume on the grid that is greater / less than the specific volume of the current point for(gridPointStart = 0; gridPointStart < V_Vals.Length; gridPointStart++){ if(V_Vals[gridPointStart] > currentV) break; } //Adjust depending on whether adding / removing energy gridPointStart = (inPositive ? gridPointStart : gridPointStart - 1); //Set the first point in the path to be the current point pathInThermoPoints.Add(new ThermoPoint(currentT, currentV, ThermoMath.GetP_TV(currentT, currentV, false), targetU, ThermoMath.GetH_TV(currentT, currentV, false), ThermoMath.GetS_TV(currentT, currentV, false), ThermoMath.area.UNKNOWN, 0)); //Find all points in the path that line up with the grid (spacing) and are within the surface / formula bounds for (int volumeIndex = gridPointStart; (inPositive ? (volumeIndex < length) : (volumeIndex >= 0)); volumeIndex = (inPositive ? (volumeIndex+1) : (volumeIndex-1))) { v = V_Vals[volumeIndex]; t = ThermoMath.TOVX(v, targetU, pathType.CONSTANT_U); //Retrieve the best Temperature for the constant Internal energy and grid aligned Volume point //t = ThermoMath.TOVU(v, targetU); //Add a new point to the path based on the retrieved temperature value pathInThermoPoints.Add(new ThermoPoint(t, v, ThermoMath.GetP_TV(t, v, false), targetU, ThermoMath.GetH_TV(t, v, false), ThermoMath.GetS_TV(t, v, false), ThermoMath.area.UNKNOWN, pathInThermoPoints.Count)); } break; /*case pathType.CONSTANT_H: break;*/ case pathType.CONSTANT_S: bool lowTemp, highTemp, lowVol, highVol; //Find the first Volume on the grid that is greater than the Volume of the current point for(gridPointStart = 0; gridPointStart < V_Vals.Length; gridPointStart++){ if(V_Vals[gridPointStart] > currentV) break; } //Adjust counter depending on if we are adding or removing energy gridPointStart = inPositive ? gridPointStart : (gridPointStart - 1); //Set the first point in the path to be the current point pathInThermoPoints.Add(new ThermoPoint(currentT, currentV, ThermoMath.GetP_TV(currentT, currentV, false), ThermoMath.GetU_TV(currentT, currentV, false), ThermoMath.GetH_TV(currentT, currentV, false), ThermoMath.GetS_TV(currentT, currentV, false), ThermoMath.area.UNKNOWN, 0)); //As we will be using currentT and currentV throughout the path calculation, set it based on the first volume //greater than that of the current position currentT = ThermoMath.TOVX(V_Vals[gridPointStart], currentS, pathType.CONSTANT_S); currentV = V_Vals[gridPointStart]; lowTemp = currentT > T_Vals[1]; highVol = currentV < V_Vals[V_Vals.Length-1]; lowVol = currentV > V_Vals[1]; highTemp = currentT < T_Vals[T_Vals.Length-1]; //Catches the case where we are at a position so close the surface edge, that we will not enter the path calculation //loop. if(inPositive ? gridPointStart+1 != V_Vals.Length && !lowTemp || !highVol : gridPointStart != 0 && !lowVol || !highTemp) pathInThermoPoints.Add(new ThermoPoint( currentT, V_Vals[inPositive ? gridPointStart+1 : gridPointStart-1], ThermoMath.GetP_TV(currentT, V_Vals[inPositive ? gridPointStart+1 : gridPointStart-1], false), ThermoMath.GetU_TV(currentT, V_Vals[inPositive ? gridPointStart+1 : gridPointStart-1], false), ThermoMath.GetH_TV(currentT, V_Vals[inPositive ? gridPointStart+1 : gridPointStart-1], false), currentS, //ThermoMath.GetS_TV(currentT, V_Vals[gridPointStart], false), ThermoMath.area.UNKNOWN, pathInThermoPoints.Count) ); //Find all points in the path that line up with the grid (spacing) and are below the target energy value for(gridPointStart = gridPointStart + 0; (inPositive ? (gridPointStart < V_Vals.Length) : (gridPointStart >= 0)) && (inPositive ? (ThermoMath.GetU_TV(currentT, V_Vals[gridPointStart], false) < targetU && lowTemp && highVol) : (ThermoMath.GetU_TV(currentT, V_Vals[gridPointStart], false) > targetU && lowVol && highTemp)); gridPointStart = (inPositive ? (gridPointStart+1) : (gridPointStart-1))){ pathInThermoPoints.Add(new ThermoPoint( currentT, V_Vals[gridPointStart], ThermoMath.GetP_TV(currentT, V_Vals[gridPointStart], false), ThermoMath.GetU_TV(currentT, V_Vals[gridPointStart], false), ThermoMath.GetH_TV(currentT, V_Vals[gridPointStart], false), currentS, //ThermoMath.GetS_TV(currentT, V_Vals[gridPointStart], false), ThermoMath.area.UNKNOWN, pathInThermoPoints.Count) ); //Update currentT unless we are on the first/last Volume of the grid if(inPositive ? (gridPointStart+1 != V_Vals.Length) : (gridPointStart != 0)){ currentT = ThermoMath.TOVX(V_Vals[inPositive ? gridPointStart+1 : gridPointStart-1], currentS, pathType.CONSTANT_S); currentV = V_Vals[inPositive ? gridPointStart+1 : gridPointStart-1]; //Edge check lowTemp = currentT >= T_Vals[1]; highVol = currentV < V_Vals[V_Vals.Length-1]; lowVol = currentV >= V_Vals[1]; highTemp = currentT < T_Vals[T_Vals.Length-1]; } //If close to the edge calculate the last point before exiting if(inPositive ? !lowTemp || !highVol : !lowVol || !highTemp) pathInThermoPoints.Add(new ThermoPoint( currentT, V_Vals[inPositive ? gridPointStart+1 : gridPointStart-1], ThermoMath.GetP_TV(currentT, V_Vals[inPositive ? gridPointStart+1 : gridPointStart-1], false), ThermoMath.GetU_TV(currentT, V_Vals[inPositive ? gridPointStart+1 : gridPointStart-1], false), ThermoMath.GetH_TV(currentT, V_Vals[inPositive ? gridPointStart+1 : gridPointStart-1], false), currentS, //ThermoMath.GetS_TV(currentT, V_Vals[gridPointStart], false), ThermoMath.area.UNKNOWN, pathInThermoPoints.Count) ); } //Adjust final Temperature and Volume values in case we've extended beyond the surface bounds //if(inPositive ? (finalV > V_Vals[V_Vals.Length-1]) : (finalV < V_Vals[0])) // finalV = inPositive ? V_Vals[V_Vals.Length-1] : V_Vals[0]; //if(inPositive ? (finalT > T_Vals[T_Vals.Length-1]) : (finalT < T_Vals[0])) // finalT = inPositive ? T_Vals[T_Vals.Length-1] : T_Vals[0]; //finalT = ThermoMath.TOVX(V_Vals[inPositive ? gridPointStart-1 : gridPointStart+1], currentS, pathType.CONSTANT_S); //finalV = ThermoMath.VOTX(finalT, currentS, pathType.CONSTANT_S); //If we ended on a point not equal to the target energy value set the final point to the point with the //target energy value //if(inPositive ? (gridPointStart != V_Vals.Length) : (gridPointStart != -1)) //Add catch here for finalT/V // pathInThermoPoints.Add(new ThermoPoint(finalT, finalV, ThermoMath.GetP_TV(finalT, finalV, false), // targetU, ThermoMath.GetH_TV(finalT, finalV, false), // ThermoMath.GetS_TV(finalT, finalV, false), ThermoMath.area.UNKNOWN, pathInThermoPoints.Count)); break; } pathPoints = new Vector3[pathInThermoPoints.Count]; //Depending on graphSpace set the accurate order of quantities defining each point //offset by the graphOrigin switch(graphSpace){ case graphSpaceEnums.TPV_Space: for(int i = 0; i < pathPoints.Length; i++){ pathPoints[i] = graphOrigin + new Vector3(Mathf.Log((float)((pathInThermoPoints[i] as ThermoPoint).T)), Mathf.Log((float)((pathInThermoPoints[i] as ThermoPoint).P)), -Mathf.Log((float)((pathInThermoPoints[i] as ThermoPoint).V))); } break; case graphSpaceEnums.TUV_Space: for(int i = 0; i < pathPoints.Length; i++){ pathPoints[i] = graphOrigin + new Vector3(Mathf.Log((float)((pathInThermoPoints[i] as ThermoPoint).T)), Mathf.Log((float)((pathInThermoPoints[i] as ThermoPoint).U)), -Mathf.Log((float)((pathInThermoPoints[i] as ThermoPoint).V))); } break; case graphSpaceEnums.THV_Space: for(int i = 0; i < pathPoints.Length; i++){ pathPoints[i] = graphOrigin + new Vector3(Mathf.Log((float)((pathInThermoPoints[i] as ThermoPoint).T)), Mathf.Log((float)((pathInThermoPoints[i] as ThermoPoint).H)), -Mathf.Log((float)((pathInThermoPoints[i] as ThermoPoint).V))); } break; case graphSpaceEnums.TSV_Space: for(int i = 0; i < pathPoints.Length; i++){ pathPoints[i] = graphOrigin + new Vector3(Mathf.Log((float)((pathInThermoPoints[i] as ThermoPoint).T)), Mathf.Log((float)((pathInThermoPoints[i] as ThermoPoint).S)), -Mathf.Log((float)((pathInThermoPoints[i] as ThermoPoint).V))); } break; } return pathPoints; }
/*RandomStart * Description - Returns a random point for positioning the ball / particle at the * start of the game. * * Params: graphOrigin -> The origin(Translation) of the Mesh specified by graphSpace * graphSpace -> The type of graph to create the point from * * Error: Will return Vector3(-1, -1, -1) if graphSpace doesn't exist * */ public static Vector3 RandomStart(Vector3 graphOrigin, graphSpaceEnums graphSpace) { int random_point = Random.Range(0, modelData.Count); switch(graphSpace){ case graphSpaceEnums.TPV_Space: return graphOrigin + new Vector3(Mathf.Log((float)((modelData[random_point] as ThermoPoint).T)), Mathf.Log((float)((modelData[random_point] as ThermoPoint).P)), -Mathf.Log((float)((modelData[random_point] as ThermoPoint).V))); case graphSpaceEnums.TUV_Space: return graphOrigin + new Vector3(Mathf.Log((float)((modelData[random_point] as ThermoPoint).T)), Mathf.Log((float)((modelData[random_point] as ThermoPoint).U)), -Mathf.Log((float)((modelData[random_point] as ThermoPoint).V))); case graphSpaceEnums.TPU_Space: return graphOrigin + new Vector3(Mathf.Log((float)((modelData[random_point] as ThermoPoint).T)), Mathf.Log((float)((modelData[random_point] as ThermoPoint).P)), -Mathf.Log((float)((modelData[random_point] as ThermoPoint).U))); case graphSpaceEnums.TPS_Space: return graphOrigin + new Vector3(Mathf.Log((float)((modelData[random_point] as ThermoPoint).T)), Mathf.Log((float)((modelData[random_point] as ThermoPoint).P)), -Mathf.Log((float)((modelData[random_point] as ThermoPoint).S))); case graphSpaceEnums.THV_Space: return graphOrigin + new Vector3(Mathf.Log((float)((modelData[random_point] as ThermoPoint).T)), Mathf.Log((float)((modelData[random_point] as ThermoPoint).H)), -Mathf.Log((float)((modelData[random_point] as ThermoPoint).V))); case graphSpaceEnums.TSV_Space: return graphOrigin + new Vector3(Mathf.Log((float)((modelData[random_point] as ThermoPoint).T)), Mathf.Log((float)((modelData[random_point] as ThermoPoint).S)), -Mathf.Log((float)((modelData[random_point] as ThermoPoint).V))); } return new Vector3(-1, -1, -1); }
/*GenerateGraph * Description - Returns a Mesh object with valid points for the * space specified.s * * Params: graphSpace -> The type of graph to create (axes) * */ public static Mesh GenerateGraph(graphSpaceEnums graphSpace) { water_surface = new Mesh(); switch(graphSpace){ case graphSpaceEnums.TPV_Space: water_surface.Clear(); water_surface.vertices = TPVverts(); break; case graphSpaceEnums.TUV_Space: water_surface.Clear(); water_surface.vertices = TUVverts(); break; case graphSpaceEnums.TPU_Space: water_surface.Clear(); water_surface.vertices = TPUverts(); break; case graphSpaceEnums.TPS_Space: water_surface.Clear(); water_surface.vertices = TPSverts(); break; case graphSpaceEnums.THV_Space: water_surface.Clear(); water_surface.vertices = THVverts(); break; case graphSpaceEnums.TSV_Space: water_surface.Clear(); water_surface.vertices = TSVverts(); break; } water_surface.triangles = mesh_triangles; water_surface.uv = mesh_uvs; water_surface.Optimize(); water_surface.RecalculateBounds(); water_surface.RecalculateNormals(); return water_surface; }