/** Trims all steps after endFrame. */ protected void trimSteps() { // return if trimming not needed VideoClip clip = trackerPanel.getPlayer().getVideoClip(); int n = clip.getFrameCount() - 1; int end = getEndFrame() == Integer.MAX_VALUE ? n : getEndFrame(); while (end > getStartFrame() && !clip.includesFrame(end)) { end--; } if (end >= lastValidFrame) return; int trimCount = (tracePtsPerStep * (lastValidFrame - end)) / clip.getStepSize(); ParticleModel[] models = getModels(); for (ParticleModel next : models) { // create smaller trace arrays and copy existing points into them next.locked = false; int traceLength = next.traceX.length - trimCount; if (traceLength < 0) return; // trap for error during closing next.prevX = next.traceX; next.prevY = next.traceY; next.traceX = new double[traceLength]; next.traceY = new double[traceLength]; System.arraycopy(next.prevX, 0, next.traceX, 0, traceLength); System.arraycopy(next.prevY, 0, next.traceY, 0, traceLength); // reduce number of steps next.steps.setLength(end + 1); // refresh derivatives next.updateDerivatives(end - 2, lastValidFrame - end + 2); // restore state restoreState(end); next.support.firePropertyChange("steps", null, null); // $NON-NLS-1$ next.locked = true; } lastValidFrame = end; repaint(); // trackerPanel.repaint(); }
/** * Sets the start frame for this model. Also sets the initial time to the video clip time at the * start frame. * * @param n the desired start frame */ public void setStartFrame(int n) { VideoClip clip = trackerPanel.getPlayer().getVideoClip(); n = Math.max(n, clip.getFirstFrameNumber()); // not less than first frame int end = clip.getLastFrameNumber(); n = Math.min(n, end); // not greater than last frame n = Math.min(n, getEndFrame()); // not greater than endFrame if (n == startFrame) return; startFrame = n; refreshInitialTime(); lastValidFrame = -1; refreshSteps(); trackerPanel.repaint(); firePropertyChange("model_start", null, getStartFrame()); // $NON-NLS-1$ }
/** * Sets the end frame for this model. * * @param n the desired end frame */ public void setEndFrame(int n) { VideoClip clip = trackerPanel.getPlayer().getVideoClip(); int end = clip.getLastFrameNumber(); n = Math.max(n, 0); // not less than zero n = Math.max(n, getStartFrame()); // not less than startFrame if (n == getEndFrame()) return; endFrame = n < end ? n : Integer.MAX_VALUE; if (n < lastValidFrame) { trimSteps(); } else { refreshSteps(); } trackerPanel.repaint(); firePropertyChange("model_end", null, getEndFrame()); // $NON-NLS-1$ }
/** * Constructs a ClipControl object. This is an abstract class that cannot be instantiated * directly. * * @param videoClip the video clip */ protected ClipControl(VideoClip videoClip) { clip = videoClip; video = clip.getVideo(); support = new SwingPropertyChangeSupport(this); }
/** * Returns an instance of ClipControl. * * @param clip the video clip * @return an appropriate clip control */ public static ClipControl getControl(VideoClip clip) { Video video = clip.getVideo(); if (playAllSteps || video == null || video instanceof ImageVideo) return new StepperClipControl(clip); return new VideoClipControl(clip); }
/** * Gets the current frame number. * * @return the frame number */ public int getFrameNumber() { return clip.stepToFrame(stepNumber); }
/** Refreshes step positions. */ protected void refreshSteps() { locked = true; if (refreshStepsLater) return; // return if this is an empty dynamic system if (this instanceof DynamicSystem) { DynamicSystem system = (DynamicSystem) this; if (system.particles.length == 0) return; } if (trackerPanel != null) { refreshDerivsLater = trackerPanel.getPlayer().getClipControl().isPlaying(); int n = trackerPanel.getFrameNumber(); VideoClip clip = trackerPanel.getPlayer().getVideoClip(); // determine last frame to be marked (must satisfy both model and clip) int end = Math.min(getEndFrame(), n); while (end > getStartFrame() && !clip.includesFrame(end)) { end--; } if (end <= lastValidFrame) return; if (lastValidFrame == -1) { reset(); // initializes model, sets lastValidFrame to marked frame, if any if (lastValidFrame == -1 || end <= lastValidFrame) return; } int start = lastValidFrame; Tracker.logTime( this.getClass().getSimpleName() + this.hashCode() + " refreshing steps " + start + " to " + end); //$NON-NLS-1$ //$NON-NLS-2$ boolean singleStep = (end - start == 1); // step forward to end ImageCoordSystem coords = trackerPanel.getCoords(); // get underlying coords if appropriate boolean useDefault = isUseDefaultReferenceFrame(); while (useDefault && coords instanceof ReferenceFrame) { coords = ((ReferenceFrame) coords).getCoords(); } double startTime = t0 + dt * tracePtsPerStep * (start - getStartFrame()) / clip.getStepSize(); double stepSize = 1.0 * clip.getStepSize() / tracePtsPerStep; int stepCount = (tracePtsPerStep * (end - start)) / clip.getStepSize(); ParticleModel[] models = getModels(); // prepare larger trace arrays and copy existing points into them for (ParticleModel next : models) { next.locked = false; int traceLength = next.traceX.length + stepCount; next.prevX = next.traceX; next.prevY = next.traceY; next.traceX = new double[traceLength]; next.traceY = new double[traceLength]; System.arraycopy(next.prevX, 0, next.traceX, 0, next.prevX.length); System.arraycopy(next.prevY, 0, next.traceY, 0, next.prevY.length); } for (int i = 0; i < stepCount; i++) { int stepNumber = i + 1; int frameNumber = start + (int) (stepNumber * stepSize); time = startTime + stepNumber * dt; Point2D[] points = getNextTracePositions(); if (points == null) continue; AffineTransform transform = coords.getToImageTransform(frameNumber); for (int j = 0; j < models.length; j++) { transform.transform(points[j], points[j]); // determine if point is invalid due to out of bounds boolean valid = Math.abs(points[j].getX()) < xLimit && Math.abs(points[j].getY()) < yLimit; if (!valid && !invalidWarningShown) { invalidWarningShown = true; Runnable runner = new Runnable() { // avoids deadlock? public void run() { // if (invalidWarningShown) return; JOptionPane.showMessageDialog( trackerPanel, TrackerRes.getString("ParticleModel.Dialog.Offscreen.Message1") + XML.NEW_LINE //$NON-NLS-1$ + TrackerRes.getString( "ParticleModel.Dialog.Offscreen.Message2"), //$NON-NLS-1$ TrackerRes.getString("ParticleModel.Dialog.Offscreen.Title"), // $NON-NLS-1$ JOptionPane.WARNING_MESSAGE); } }; SwingUtilities.invokeLater(runner); } models[j].traceX[models[j].prevX.length + i] = valid ? points[j].getX() : Double.NaN; models[j].traceY[models[j].prevY.length + i] = valid ? points[j].getY() : Double.NaN; if (stepNumber % tracePtsPerStep == 0) { // refresh position step saveState(frameNumber); PositionStep step = (PositionStep) models[j].getStep(frameNumber); if (step == null) { step = createPositionStep(models[j], frameNumber, 0, 0); step.setFootprint(models[j].getFootprint()); models[j].steps.setStep(frameNumber, step); } step.getPosition().setPosition(valid ? points[j] : nan); // this method is fast } } } int count = 4 + (end - start); int startUpdate = start; // step back twice to pick up possible valid derivatives if (startUpdate > clip.getStepSize()) startUpdate -= clip.getStepSize(); if (startUpdate > clip.getStepSize()) startUpdate -= clip.getStepSize(); lastValidFrame = end; for (ParticleModel next : models) { next.steps.setLength(end + 1); coords = trackerPanel.getCoords(); // get active coords // special treatment if this is the origin of current reference frame if (coords instanceof ReferenceFrame && ((ReferenceFrame) coords).getOriginTrack() == next) { // set origins of reference frame boolean prev = next.refreshing; // save refreshing value next.refreshing = true; ((ReferenceFrame) coords).setOrigins(); // then set positions to zero wrt origins for (int i = 0; i < clip.getStepCount(); i++) { int frameNumber = clip.stepToFrame(i); PositionStep step = (PositionStep) next.getStep(frameNumber); if (step == null) continue; AffineTransform transform = coords.getToImageTransform(frameNumber); next.point.setLocation(0, 0); transform.transform(next.point, next.point); step.getPosition().setPosition(next.point); // this method is fast } next.refreshing = prev; // restore refreshing value if (!refreshDerivsLater) { next.updateDerivatives(startUpdate, count); } } else if (!refreshDerivsLater) { next.updateDerivatives(startUpdate, count); } if (next.vAtOrigin) next.vTailsToOriginItem.doClick(); if (next.aAtOrigin) next.aTailsToOriginItem.doClick(); if (!refreshDerivsLater) { if (singleStep) next.support.firePropertyChange("step", null, new Integer(n)); // $NON-NLS-1$ else next.support.firePropertyChange("steps", null, null); // $NON-NLS-1$ } // erase refreshed steps for (int i = start + 1; i <= end; i++) { Step step = next.getStep(i); if (step != null) step.erase(); } next.locked = true; } trackerPanel.repaint(); } }