Re: Creating a radar sweep effect
Re: Creating a radar sweep effect
- Subject: Re: Creating a radar sweep effect
- From: douglas welton <email@hidden>
- Date: Mon, 28 Sep 2009 08:44:41 -0400
Hi Guys,
Seems to me that CIImageAccumulator would be a possible solution for
this problem. Here's a pointer to the programming guide section for
dynamical systems:
<http://developer.apple.com/mac/library/documentation/GraphicsImaging/Conceptual/CoreImaging/ci_tasks/ci_tasks.html#//apple_ref/doc/uid/TP30001185-CH203-BAJIBEIF
>
regards,
douglas
On Sep 27, 2009, at 7:42 AM, Graham Cox wrote:
Hi again - you got me going now! ;-)
Occurs to me that there's no need to store more than one buffered
image (actually really obvious once it dawned on me). Checking the
OpenGL approach, this is what it does also - just one "history"
buffered image, which is then drawn into the new image at each
update, with a lower opacity such that over time it naturally fades
away. This is much more efficient as there are only two bitblits per
frame, and a maximum of two images in memory at once and then only
briefly, no matter how much "persistence" you dial in or what the
frame rate is, etc. It also looks a lot more realistic, so it's a
winner all round. Here's my revised view which still uses NSImage,
but even with this is working nice and smoothly. I also added a
"target" to show how actual blips on the screen could be handled.
@interface GCRadarView : NSView
{
NSTimer* mTimer;
NSTimeInterval mLastUpdateTime;
CGFloat mAngle;
NSImage* mHistoryImage;
}
- (void) drawHistoryBuffer;
- (void) updateHistoryBuffer;
- (void) drawContentIntoImage:(NSImage*) theImage;
- (void) animateWithTimer:(NSTimer*) timer;
- (IBAction) startSweep:(id) sender;
- (IBAction) stopSweep:(id) sender;
@end
#define SWEEP_RATE 0.5 // radians per second
//------------------------------------------------------------------------------------
#import "GCRadarView.h"
@implementation GCRadarView
- (void) drawHistoryBuffer
{
// draw the current buffered image to the view
[mHistoryImage drawInRect:[self bounds] fromRect:NSZeroRect
operation:NSCompositeSourceOver fraction:1.0];
}
- (void) updateHistoryBuffer
{
NSImage* newImage = [[NSImage alloc] initWithSize:[self
bounds].size];
// copy the current history into it at reduced opacity - dial in
different opacity values to change the "persistence"
// values closer to 1.0 give more persistence, closer to 0 give
less. 1.0 gives infinite persistence, so the image never fades.
[newImage lockFocus];
if( mHistoryImage )
[mHistoryImage drawInRect:[self bounds] fromRect:NSZeroRect
operation:NSCompositeSourceOver fraction:0.92];
// draw new content
[self drawContentIntoImage:newImage];
[newImage unlockFocus];
// this is now the history buffer:
[mHistoryImage release];
mHistoryImage = newImage;
}
- (void) drawContentIntoImage:(NSImage*) theImage
{
// draw view content to the image. Image is already focused.
NSTimeInterval currentTime = [NSDate
timeIntervalSinceReferenceDate];
NSTimeInterval elapsedTime = currentTime - mLastUpdateTime;
mLastUpdateTime = currentTime;
// sweep rate is 0.5 rad/sec - so how many radians since we last
updated?
CGFloat angleIncrement = SWEEP_RATE * elapsedTime;
mAngle += angleIncrement;
mAngle = fmod( mAngle, 2 * pi );
// set the transform to this angle rotated around the centre point
NSPoint centre;
centre.x = NSMidX([self bounds]);
centre.y = NSMidY([self bounds]);
[NSGraphicsContext saveGraphicsState];
NSAffineTransform* transform = [NSAffineTransform transform];
[transform translateXBy:centre.x yBy:centre.y];
[transform rotateByRadians:mAngle];
[transform translateXBy:-centre.x yBy:-centre.y];
[transform concat];
// set a shadow to simulate the backscatter glow
NSShadow* shadow = [[NSShadow alloc] init];
[shadow setShadowColor:[NSColor colorWithCalibratedRed:0.4 green:
1.0 blue:0.4 alpha:1.0]];
[shadow setShadowOffset:NSZeroSize];
[shadow setShadowBlurRadius:8.0];
[shadow set];
// draw the sweep line from centre to edge. The transform is
rotated so draw at a fixed angle.
[[NSColor greenColor] set];
[NSBezierPath setDefaultLineWidth:1.5];
[NSBezierPath strokeLineFromPoint:centre
toPoint:NSMakePoint( NSMaxX([self bounds]), NSMidY([self bounds]))];
// draw the "target" on the screen when the beam sweeps across it
to simulate a blip - here just a square
// restore first so that effect of rotation is removed - target
stays at a fixed place.
[NSGraphicsContext restoreGraphicsState];
if( fabs(mAngle) < 0.1 )
{
NSBezierPath* target = [NSBezierPath
bezierPathWithRect:NSMakeRect(( centre.x + NSMaxX([self bounds])) /
2.0, NSMidY([self bounds]), 10, 10 )];
[[NSColor greenColor] set];
[target fill];
}
[shadow release];
}
- (void) animateWithTimer:(NSTimer*) timer
{
// timer callback ends up here. It updates the history buffer and
marks the display needed.
#pragma unused(timer)
[self updateHistoryBuffer];
[self setNeedsDisplay:YES];
}
- (IBAction) startSweep:(id) sender
{
#pragma unused(sender)
if( mTimer == nil )
{
mTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/60.0
target:self selector:@selector(animateWithTimer:) userInfo:nil
repeats:YES];
mLastUpdateTime = [NSDate timeIntervalSinceReferenceDate];
}
}
- (IBAction) stopSweep:(id) sender
{
#pragma unused(sender)
if( mTimer )
{
[mTimer invalidate];
mTimer = nil;
}
}
#pragma mark -
#pragma mark - as a NSView
- (void) drawRect:(NSRect) dirtyRect
{
[[NSColor blackColor] set];
NSRectFill( dirtyRect );
[self drawHistoryBuffer];
}
- (void) dealloc
{
[self stopSweep:nil];
[mHistoryImage release];
[super dealloc];
}
@end
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden