divyavamsee / core-plot

Automatically exported from code.google.com/p/core-plot
BSD 3-Clause "New" or "Revised" License
0 stars 0 forks source link

Animation support for x/y ranges in a plotspace #398

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
Since CPTPlotSpace is not a CALayer you cannot use normal animation code to 
smoothly switch between different ranges or slide to a new local range. The 
attached code uses a standard NSAnimation derivate to implement this 
functionality. As this code is very compact and easy to use (without the need 
for delegates, timers etc.) it might be worth to be added as animation proxy to 
the plotspace.

Enhancement: by overriding -currentValue the class could also offer new 
animation curves, say, to slide a plot and snap it in when it nearly reaches 
the final position.

@interface CorePlotXYRangeAnimation : NSAnimation {
   CPTXYPlotSpace* plotSpace; // The plotspace of which we animate the xRange or the yRange.
   CPTXYAxis* axis; // Can be nil.

   bool useXRange;
   double startPosition;
   double startLength;
   double targetPosition;
   double targetLength;
}

- (id)initWithDuration: (NSTimeInterval)duration
       animationCurve: (NSAnimationCurve)animationCurve
            plotSpace: (CPTXYPlotSpace*)theSpace
                 axis: (CPTXYAxis*)theAxis
            forXRange: (bool)forXRange;

@property (nonatomic, assign) double targetPosition;
@property (nonatomic, assign) double targetLength;

@end

@implementation CorePlotXYRangeAnimation

@synthesize targetPosition;
@synthesize targetLength;

- (id)initWithDuration: (NSTimeInterval)duration
       animationCurve: (NSAnimationCurve)animationCurve
            plotSpace: (CPTXYPlotSpace*)theSpace
                 axis: (CPTXYAxis*)theAxis
            forXRange: (bool)forXRange
{
   self = [super initWithDuration: duration animationCurve: animationCurve];
   if (self != nil) {
       plotSpace = theSpace;
       axis = theAxis;
       useXRange = forXRange; // False for yRange.

       if (useXRange) {
           startPosition = plotSpace.xRange.locationDouble;
           startLength = plotSpace.xRange.lengthDouble;
       } else {
           startPosition = plotSpace.yRange.locationDouble;
           startLength = plotSpace.yRange.lengthDouble;
       }

   }
   return self;
}

- (void)setCurrentProgress:(NSAnimationProgress)progress
{
   if (startPosition == targetPosition && startLength == targetLength) {
       [self stopAnimation];
       return;
   }

   [super setCurrentProgress: progress];
   float value = [self currentValue];

   double newPosition = startPosition * (1 - value) + targetPosition * value;
   double newLength = startLength * (1 - value) + targetLength * value;
   CPTPlotRange* plotRange = [CPTPlotRange plotRangeWithLocation: CPTDecimalFromDouble(newPosition)
                                                          length: CPTDecimalFromDouble(newLength)];

   if (useXRange) {
       // First check the global range if the new local range would exceed it. Without adjusting the
       // global range the new local range has no effect (in certain circumstances).
       double newGlobalPosition = newPosition;
       if (plotSpace.globalXRange.locationDouble < newGlobalPosition) {
           newGlobalPosition = plotSpace.globalXRange.locationDouble;
       }
       double newGlobalLength = newLength;
       if (plotSpace.globalXRange.lengthDouble > newGlobalLength) {
           newGlobalLength = plotSpace.globalXRange.lengthDouble;
       }
       CPTPlotRange* globalRange = [CPTPlotRange plotRangeWithLocation: CPTDecimalFromDouble(newGlobalPosition)
                                                                length: CPTDecimalFromDouble(newGlobalLength)];
       plotSpace.globalXRange = globalRange;
       plotSpace.xRange = plotRange;
   } else {
       double newGlobalPosition = newPosition;
       if (plotSpace.globalYRange.locationDouble < newGlobalPosition) {
           newGlobalPosition = plotSpace.globalYRange.locationDouble;
       }
       double newGlobalLength = newLength;
       if (plotSpace.globalYRange.lengthDouble > newGlobalLength) {
           newGlobalLength = plotSpace.globalYRange.lengthDouble;
       }
       CPTPlotRange* globalRange = [CPTPlotRange plotRangeWithLocation: CPTDecimalFromDouble(newGlobalPosition)
                                                                length: CPTDecimalFromDouble(newGlobalLength)];
       plotSpace.globalYRange = globalRange;
       plotSpace.yRange = plotRange;
   }

   if (axis != nil) {
       axis.visibleRange = plotRange;
   }
}

@end

Use it like this:

   CPTXYAxisSet* axisSet = (id)mainGraph.axisSet;

   float animationDuration = 0.3;
   if (([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) != 0) {
        animationDuration = 3;
    }

   CorePlotXYRangeAnimation* animation = [[CorePlotXYRangeAnimation alloc] initWithDuration: animationDuration
                                                                             animationCurve: NSAnimationEaseInOut
                                                                                  plotSpace: plotSpace
                                                                                       axis: axisSet.yAxis
                                                                                  forXRange: NO];
   animation.animationBlockingMode = NSAnimationBlocking;
   animation.targetPosition = [roundedMinValue doubleValue];
   animation.targetLength = [[roundedMaxValue decimalNumberBySubtracting: roundedMinValue] doubleValue];
   [animation startAnimation];
   [animation release];

Original issue reported on code.google.com by mike.lischke on 28 Jan 2012 at 9:20

GoogleCodeExporter commented 9 years ago

Original comment by eskr...@mac.com on 28 Jan 2012 at 2:17

GoogleCodeExporter commented 9 years ago
This sounds like a good approach, but since NSAnimation is OS X-only, we'll 
need to implement equivalent functionality that will work on iOS as well.

Original comment by eskr...@mac.com on 19 Mar 2012 at 1:36

GoogleCodeExporter commented 9 years ago
Well, if it were a CALayer there wouldn't be a need for such extra animation 
code.

Original comment by mike.lischke on 19 Mar 2012 at 7:45

GoogleCodeExporter commented 9 years ago
Related to issue #17.

Original comment by eskr...@mac.com on 21 Nov 2012 at 1:42

GoogleCodeExporter commented 9 years ago
This issue was closed by revision 4f9f899888cf.

Original comment by eskr...@mac.com on 17 Dec 2012 at 1:05

GoogleCodeExporter commented 9 years ago
Eric, this works excellently (at least the plot range animation I tried). Well 
done.

Original comment by mike.lischke on 17 Dec 2012 at 3:08

GoogleCodeExporter commented 9 years ago
I am unable to implement it for iOS. Can anyone please write some code to help 
me?

Original comment by utkarsh3...@gmail.com on 17 Apr 2015 at 4:52

GoogleCodeExporter commented 9 years ago
This issue was closed over two years ago. If you're having trouble, please ask 
a question on StackOverflow (tag core-plot) or the discussion board 
(https://groups.google.com/forum/#!forum/coreplot-discuss).

Original comment by eskr...@mac.com on 18 Apr 2015 at 1:49