package tim.prune.function;
import javax.swing.JOptionPane;
import tim.prune.App;
import tim.prune.I18nManager;
import tim.prune.data.DataPoint;
import tim.prune.data.Track;
import tim.prune.undo.UndoInterpolate;
/**
* Function to interpolate between the points in a range
*/
public class InterpolateFunction extends SingleNumericParameterFunction
{
/**
* Constructor
* @param inApp app object
*/
public InterpolateFunction(App inApp) {
super(inApp, 1, 1000);
}
/** @return name key */
public String getNameKey() {
return "function.interpolate";
}
/** @return description key for input parameter */
public String getDescriptionKey() {
return "dialog.interpolate.parameter.text";
}
/** @return current (or default) parameter value */
public int getCurrentParamValue() {
return 0;
}
/**
* Perform the operation
*/
public void begin()
{
// not needed, we just use the completeFunction method instead
}
/**
* Complete the function after the input parameter has been chosen
*/
public void completeFunction(int inParam)
{
// Firstly, work out whether the selected range only contains waypoints or not
final int startIndex = _app.getTrackInfo().getSelection().getStart();
final int endIndex = _app.getTrackInfo().getSelection().getEnd();
boolean betweenWaypoints = false;
// if there are only waypoints, then ask whether to interpolate them
if (!selectedRangeHasTrackpoints(_app.getTrackInfo().getTrack(), startIndex, endIndex))
{
int answer = JOptionPane.showConfirmDialog(_parentFrame,
I18nManager.getText("dialog.interpolate.betweenwaypoints"),
I18nManager.getText(getNameKey()), JOptionPane.YES_NO_OPTION);
if (answer == JOptionPane.NO_OPTION) {
// user said no, so nothing to do
return;
}
betweenWaypoints = true;
}
if (startIndex < 0 || endIndex < 0 || endIndex <= startIndex) {
return;
}
// construct new point array with the interpolated points
final int numToAdd = inParam;
final Track track = _app.getTrackInfo().getTrack();
final int maxToAdd = (endIndex-startIndex) * numToAdd;
final int extendedSize = track.getNumPoints() + maxToAdd;
DataPoint[] oldPoints = track.cloneContents();
DataPoint[] newPoints = new DataPoint[extendedSize];
// Copy points before
System.arraycopy(oldPoints, 0, newPoints, 0, startIndex);
// Loop, copying points and interpolating
int destIndex = startIndex;
DataPoint prevPoint = null;
for (int i=startIndex; i<= endIndex; i++)
{
DataPoint p = _app.getTrackInfo().getTrack().getPoint(i);
if (prevPoint != null && ((p.isWaypoint() && betweenWaypoints) || (!p.isWaypoint() && !p.getSegmentStart())))
{
// interpolate between the previous point and this one
DataPoint[] addition = prevPoint.interpolate(p, numToAdd);
System.arraycopy(addition, 0, newPoints, destIndex, numToAdd);
destIndex += numToAdd;
}
// copy point
newPoints[destIndex] = p;
destIndex++;
if (!p.isWaypoint() || betweenWaypoints)
{
prevPoint = p;
}
else if (!p.isWaypoint()) {
prevPoint = null;
}
// If it's a waypoint, then keep the old prevPoint
}
final int totalInserted = destIndex - endIndex - 1;
// Copy the points after the selected range
System.arraycopy(oldPoints, endIndex, newPoints, destIndex-1, track.getNumPoints()-endIndex);
// If necessary, make a new array of the correct size and do another arraycopy into it
final int newTotalPoints = track.getNumPoints() + totalInserted;
if (newTotalPoints != newPoints.length)
{
DataPoint[] croppedPoints = new DataPoint[newTotalPoints];
System.arraycopy(newPoints, 0, croppedPoints, 0, newTotalPoints);
newPoints = croppedPoints;
}
// Make undo object
UndoInterpolate undo = new UndoInterpolate(_app.getTrackInfo(), totalInserted);
// Replace track with new points array
if (track.replaceContents(newPoints))
{
_app.completeFunction(undo, I18nManager.getText("confirm.interpolate"));
// Alter selection
_app.getTrackInfo().getSelection().selectRange(startIndex, endIndex + totalInserted);
}
}
/**
* Check if the given Track has trackpoints in the specified range
* @param inTrack track object
* @param inStart start index
* @param inEnd end index
* @return true if there are any non-waypoints in the range
*/
private static boolean selectedRangeHasTrackpoints(Track inTrack, int inStart, int inEnd)
{
for (int i=inStart; i<= inEnd; i++)
{
DataPoint p = inTrack.getPoint(i);
if (p != null && !p.isWaypoint()) {
return true;
}
}
return false;
}
}