Re: Creating a radar sweep effect
On 27 Sep 2009, at 07:50, Graham Cox wrote: One possibility is to use OpenGL. It has a history buffer mode (may not be called that - I forget exactly) that stores the previous image at a diminished brightness and that can be stacked for a series of frames, giving a fade or trail effect. I think you're referring to the accumulation buffer, which can be used for this kind of thing. That's probably the best way to implement this, to be honest. Failing that, you could use Core Image (assuming that's an option for you). Kind regards, Alastair. -- http://alastairs-place.net ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) 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: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Creating a radar sweep effect
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
Re: Creating a radar sweep effect
Am 27.09.2009 um 08:50 schrieb Graham Cox: I have used this approach to simulate an oscilloscope display and it works well in terms of realism, but performance can be an issue. You'll probably need to store at least 3 or 4 previous frames to get the effect you want - there's no really good way to do it in one pass and get realism. One way to improve performance would be to use CALayers or CGLayers. Have a pool of five previous layers, and reuse the oldest of those for the new image. By using layers, you can let the graphics card do the merging of the six frames, and the expensive drawing only happens once. Play with the layer alpha to have them fade out. Haven't tried this, but it's something I'd attempt if I was pressed for performance. Cheers, -- Uli Kusterer The Witnesses of TeachText are everywhere... http://groups.yahoo.com/group/mac-gui-dev/ ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) 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: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Creating a radar sweep effect
Hi John, The difficulty with this kind of display is simulating the persistence of the old cathode-ray tubes in a realistic way. Just drawing a shadow is unlikely to work, though it might help get you some way by creating the glow caused by scattering. One possibility is to use OpenGL. It has a history buffer mode (may not be called that - I forget exactly) that stores the previous image at a diminished brightness and that can be stacked for a series of frames, giving a fade or trail effect. Alternatively you can model persistence yourself by buffering up several frames (for example, using a NSBitmapImageRep) and then drawing the stack for each frame followed by the latest content. I have used this approach to simulate an oscilloscope display and it works well in terms of realism, but performance can be an issue. You'll probably need to store at least 3 or 4 previous frames to get the effect you want - there's no really good way to do it in one pass and get realism. Fact is those old tubes literally stored the image in the phosphors which naturally faded in their own time after the beam passed - to simulate that realistically requires that you model the image storage. --Graham On 27/09/2009, at 11:19 AM, John Cebasek wrote: Hi All: I'm wondering what the best way to create a radar sweep effect is? (Like in old war movies?) ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) 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: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com
Re: Creating a radar sweep effect
John, the code below is similar to my oscilloscope view in principle - here though I just use an NSImage to buffer the history, as it's simplest, but for performance you might try something more sophisticated. Also, when drawing the history, this just uses a linear opacity ramp - it might work better with a curve of some sort but I'll leave that to you! Hope it helps, --Graham @interface GCRadarView : NSView { NSMutableArray* mHistoryBuffer; NSTimer*mTimer; NSTimeInterval mLastUpdateTime; CGFloat mAngle; } - (void)drawHistoryBuffer; - (void)updateHistoryBuffer; - (void)drawContentIntoImage:(NSImage*) theImage; - (void)animateWithTimer:(NSTimer*) timer; - (IBAction)startSweep:(id) sender; - (IBAction)stopSweep:(id) sender; @end #define MAX_HISTORY_BUFFER_SIZE 6 #define SWEEP_RATE 0.3 // radians per second // #import GCRadarView.h @implementation GCRadarView - (void)drawHistoryBuffer { // draw the stack of images in the history buffer, each one with a different transparency if([mHistoryBuffer count] 0 ) { CGFloat opacityIncrement = 1.0 / [mHistoryBuffer count]; NSUInteger i; NSImage*image; for( i = 0; i [mHistoryBuffer count]; ++i ) { image = [mHistoryBuffer objectAtIndex:i]; [image drawInRect:[self bounds] fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:(CGFloat)i * opacityIncrement]; } } } - (void)updateHistoryBuffer { // this generates the images. The image list is limited to a maximum - if exceeded images are discarded // from the front of the list. New images are added to the end of the list. while([mHistoryBuffer count] MAX_HISTORY_BUFFER_SIZE ) [mHistoryBuffer removeObjectAtIndex:0]; NSImage* newImage = [[NSImage alloc] initWithSize:[self bounds].size]; [newImage lockFocus]; [self drawContentIntoImage:newImage]; [newImage unlockFocus]; [mHistoryBuffer addObject:newImage]; [newImage release]; } - (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 SWEEP_RATE rad/sec - so how many radians since we last updated? CGFloat angleIncrement = SWEEP_RATE * elapsedTime; mAngle += angleIncrement; mAngle = fmod( mAngle, 2 * pi );// not really needed // 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]; // draw the sweep line from centre to edge. The transform is rotated so we simply draw at a fixed angle. // add a shadow to simulate the glow of backscatter and help hide the discrete nature of the sweep lines 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]; // beam line [[NSColor greenColor] set]; [NSBezierPath setDefaultLineWidth:1.5]; [NSBezierPath strokeLineFromPoint:centre toPoint:NSMakePoint( NSMaxX ([self bounds]), 0 )]; [shadow release]; [NSGraphicsContext restoreGraphicsState]; } - (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/30.0 target:self selector:@selector(animateWithTimer:) userInfo:nil repeats:YES]; mLastUpdateTime = [NSDate
Re: Creating a radar sweep effect
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
Re: Creating a radar sweep effect
You can do this with a single accumulating bitmap. 1. Create a bitmap the size of your view filled with the background color 2. Fill the bitmap with the background color using an alpha = 1 / number of steps 3. Draw the radar onto the bitmap 4. Draw the bitmap in the view 5. Go to step 2 (I have done this exact thing on Win32, but not Cocoa so buyer beware). ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) 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: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com