Skip to content

Commit

Permalink
added (web)scriptability of record painting functions, intelligent pi…
Browse files Browse the repository at this point in the history
…ped input and exporting to QT movies
  • Loading branch information
Jared Flatow committed Apr 17, 2009
1 parent 8de9418 commit d05d962
Show file tree
Hide file tree
Showing 30 changed files with 1,005 additions and 275 deletions.
37 changes: 37 additions & 0 deletions Canvas.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// Canvas.h
// viz
//
// Created by Jared Flatow on 4/13/09.
// Copyright 2009 Jared Flatow. All rights reserved.
//

#import <Cocoa/Cocoa.h>
#import <QuartzCore/QuartzCore.h>

@class DataView;

@interface Canvas : NSObject {
DataView *dataView;
CGColorRef backgroundColor, fontColor;
CALayer *animationLayer;
CATextLayer *textLayer;
CGLayerRef graphicsLayer;
CGSize canvasSize;
}

@property DataView *dataView;
@property CGLayerRef graphicsLayer;
@property(readonly) CGFloat width, height;

- (id) initWithDataView:(DataView *) dataView;
- (void) blink;
- (void) clear;
- (CGContextRef) context;
- (void) paintCircleWithRadius:(CGFloat) radius atX:(CGFloat) x andY:(CGFloat) y;
- (void) renderInImage:(NSImage *) image;
- (NSImage *) renderInImage;
- (void) setNeedsDisplay;
- (void) setText:(NSString *) text;

@end
154 changes: 154 additions & 0 deletions Canvas.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
//
// Canvas.m
// viz
//
// Created by Jared Flatow on 4/13/09.
// Copyright 2009 Jared Flatow. All rights reserved.
//

#import "Canvas.h"
#import "DataView.h"

@implementation Canvas

@synthesize dataView, graphicsLayer;

#pragma mark Class Initialization Methods

+ (void) initialize {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
@"500", @"CanvasWidth",
@"500", @"CanvasHeight", nil];
[defaults registerDefaults:appDefaults];
}

#pragma mark WebScripting Protocol Methods

+ (BOOL) isKeyExcludedFromWebScript:(const char *) name {
return NO;
}

+ (BOOL) isSelectorExcludedFromWebScript:(SEL) aSelector {
return NO;
}

- (id) invokeUndefinedMethodFromWebScript:(NSString *) name
withArguments:(NSArray *) args {
DebugLog(@"No such method: %@", name);
return false;
}

#pragma mark Interface Methods

- (id) initWithDataView:(DataView *) aDataView {
if (self = [super init]) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
canvasSize = CGSizeMake([defaults floatForKey:@"CanvasWidth"], [defaults floatForKey:@"CanvasHeight"]);
backgroundColor = CGColorCreateGenericRGB(1, 1, 1, 0.8);
fontColor = CGColorCreateGenericRGB(0.1, 0.1, 0.1, 1.0);
animationLayer = [CALayer layer];
textLayer = [CATextLayer layer];
[textLayer setForegroundColor:fontColor];
[textLayer setFontSize:12];
[textLayer setAutoresizingMask:(kCALayerHeightSizable | kCALayerWidthSizable)];
[animationLayer setBackgroundColor:backgroundColor];
[animationLayer setDelegate:self];
[animationLayer addSublayer:textLayer];
[textLayer setFrame:[animationLayer frame]];
[aDataView addLayer:animationLayer];
[aDataView resizeTo:NSSizeFromCGSize(canvasSize)];
[self setDataView:aDataView];
[self setNeedsDisplay];
}
return self;
}

- (void) blink {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
[animation setRepeatCount:1];
[animation setFromValue:[NSNumber numberWithFloat:0.]];
[animation setToValue:[NSNumber numberWithFloat:1.]];
[animationLayer addAnimation:animation forKey:@"animateOpacity"];
}

- (void) clear {
[self setGraphicsLayer:CGLayerCreateWithContext(CGLayerGetContext(graphicsLayer), canvasSize, nil)];
[self setText:@""];
[self setNeedsDisplay];
}

- (CGContextRef) context {
return CGLayerGetContext(graphicsLayer);
}

- (void) paintCircleWithRadius:(CGFloat) radius atX:(CGFloat) x andY:(CGFloat) y {
CGContextRef context = [self context];
CGContextBeginPath(context);
CGContextAddArc(context, x, y, radius, 0, 2 * M_PI, false);
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFillStroke);
}

- (void) renderInImage:(NSImage *) image {
[dataView lockFocus];
NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:[dataView bounds]];
[dataView unlockFocus];
CGContextRef imageContext = [[NSGraphicsContext graphicsContextWithBitmapImageRep:imageRep] graphicsPort];
[animationLayer renderInContext:imageContext];
[image addRepresentation:imageRep];
}

- (NSImage *) renderInImage {
NSImage *image = [[NSImage alloc] initWithSize:[dataView bounds].size];
[self renderInImage:image];
return image;
}

- (void) setGraphicsLayer:(CGLayerRef) layer {
CGLayerRelease(graphicsLayer);
graphicsLayer = layer;
}

- (void) setNeedsDisplay {
[animationLayer setNeedsDisplay];
}

- (void) setFillColorR:(CGFloat) r G:(CGFloat) g B:(CGFloat) b A:(CGFloat) a {
CGContextSetRGBFillColor([self context], r, g, b, a);
}

- (void) setStrokeColorR:(CGFloat) r G:(CGFloat) g B:(CGFloat) b A:(CGFloat) a {
CGContextSetRGBStrokeColor([self context], r, g, b, a);
}

- (void) setText:(NSString *) text {
[textLayer setString:text];
}

- (CGFloat) width {
return canvasSize.width;
}

- (CGFloat) height {
return canvasSize.height;
}

#pragma mark CALayer Delegate Methods

- (void) drawLayer:(CALayer *) theLayer inContext:(CGContextRef) context {
if (!graphicsLayer)
[self setGraphicsLayer:CGLayerCreateWithContext(context, canvasSize, nil)];
CGContextDrawLayerInRect(context, NSRectToCGRect([dataView bounds]), graphicsLayer);
}

#pragma mark Overridden NSObject Methods

- (void) dealloc {
CGLayerRelease(graphicsLayer);
CGColorRelease(backgroundColor);
CGColorRelease(fontColor);
[super dealloc];
}

@end
2 changes: 1 addition & 1 deletion Checkpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ CGLayerRef CGLayerCopy(CGLayerRef layerRef);
CGLayerRef graphicsLayer;
}

- (Checkpoint *) initFromRecordStream:(RecordStream *) recordStream;
- (id) initFromRecordStream:(RecordStream *) recordStream;
- (void) loadIntoRecordStream:(RecordStream *) recordStream;

@end
8 changes: 5 additions & 3 deletions Checkpoint.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//

#import "Checkpoint.h"
#import "Canvas.h"
#import "RecordStream.h"
#import "RecordPainter.h"

Expand All @@ -18,19 +19,20 @@ CGLayerRef CGLayerCopy(CGLayerRef layerRef) {

@implementation Checkpoint

- (Checkpoint *) initFromRecordStream:(RecordStream *) recordStream {
- (id) initFromRecordStream:(RecordStream *) recordStream {
if (self = [super init]) {
index = [recordStream index];
offset = [[recordStream fileHandle] offsetInFile];
graphicsLayer = CGLayerCopy([[recordStream recordPainter] graphicsLayer]);
graphicsLayer = CGLayerCopy([[[recordStream recordPainter] canvas] graphicsLayer]);
}
return self;
}

- (void) loadIntoRecordStream:(RecordStream *) recordStream {
[recordStream setIndex:index];
[[recordStream fileHandle] seekToFileOffset:offset];
[[recordStream recordPainter] setGraphicsLayer:CGLayerCopy(graphicsLayer)];
[[[recordStream recordPainter] canvas] setGraphicsLayer:CGLayerCopy(graphicsLayer)];
[[[recordStream recordPainter] canvas] setNeedsDisplay];
}

- (void) dealloc {
Expand Down
3 changes: 2 additions & 1 deletion DataSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//

#import <Cocoa/Cocoa.h>
#import <QTKit/QTKit.h>

@class DataView, RecordStream;

Expand All @@ -17,6 +18,6 @@

@property(assign) RecordStream *recordStream;

- (DataSource *) initWithPath:(NSString *) path;
- (id) initWithPath:(NSString *) path;

@end
90 changes: 66 additions & 24 deletions DataSource.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,40 @@
//

#import "DataSource.h"
#import "Canvas.h"
#import "DataView.h"
#import "RecordStream.h"
#import "RecordPainter.h"
#import "RecordStream.h"

@implementation DataSource

static NSString *QuickTimeMovieType = @"com.apple.quicktime-movie";

@synthesize recordStream;

- (DataSource *) initWithPath:(NSString *) path {
#pragma mark Interface Methods

- (id) initWithPath:(NSString *) path {
if (self = [super init])
if (!(recordStream = [RecordStream recordStreamWithPath:path]))
if (!(recordStream = [[RecordStream alloc] initWithPath:path]))
return nil;
return self;
}

- (NSString *) windowNibName {
return @"DataSource";
}
#pragma mark Delegated Methods

- (void) windowControllerWillLoadNib:(NSWindowController *) windowController {
DebugLog(@"%@ nib loading", [windowController windowNibName]);
- (void) insertText:(id) text {
if ([text isEqualToString:@" "])
[recordStream togglePlay];
else if ([text isEqualToString:@"-"])
[recordStream slowDown];
else if ([text isEqualToString:@"+"])
[recordStream speedUp];
}

- (void) windowControllerDidLoadNib:(NSWindowController *) windowController {
DebugLog(@"%@ nib loaded", [windowController windowNibName]);
[recordStream setRecordPainter:[[RecordPainter alloc] initWithDataView:dataView]];
- (IBAction) deleteBackward:(id) sender {
[recordStream prev];
}

- (IBAction) gotoBeginning:(id) sender {
Expand All @@ -56,33 +63,68 @@ - (IBAction) moveBackward:(id) sender {
[recordStream prev];
}

- (IBAction) moveUp:(id) sender {
[recordStream prev];
}

- (IBAction) moveDown:(id) sender {
[recordStream next];
}

- (IBAction) moveForward:(id) sender {
[recordStream next];
}

- (IBAction) moveLeft:(id) sender {
[recordStream reset];
[recordStream first];
}

- (IBAction) moveRight:(id) sender {
[recordStream last];
}

- (IBAction) deleteBackward:(id) sender {
[recordStream prev];
- (IBAction) play:(id) sender {
[recordStream play];
}

- (IBAction) stepBack:(id) sender {
[recordStream prevFrame];
}

- (IBAction) stepForward:(id) sender {
[recordStream nextFrame];
}

- (void) finalize {
DebugLog(@"finalizing datasource", nil);
[super finalize];
#pragma mark Overrides of NSDocument Methods

- (void) close {
[recordStream close];
[super close];
}

- (NSString *) windowNibName {
return @"DataSource";
}

- (void) windowControllerWillLoadNib:(NSWindowController *) windowController {
DebugLog(@"%@ nib loading", [windowController windowNibName]);
}

- (void) windowControllerDidLoadNib:(NSWindowController *) windowController {
[[recordStream recordPainter] setCanvas:[[Canvas alloc] initWithDataView:dataView]];
}

- (NSArray *) writableTypesForSaveOperation:(NSSaveOperationType) saveOperation {
return [NSArray arrayWithObject:QuickTimeMovieType];
}

- (BOOL) writeToURL:(NSURL *) absoluteURL ofType:(NSString *) typeName error:(NSError **) outError {
if ([[NSWorkspace sharedWorkspace] type:QuickTimeMovieType conformsToType:typeName]) {
QTMovie *movie = [[QTMovie alloc] initToWritableFile:[absoluteURL path] error:outError];
if (!movie)
return NO;
NSImage *image;
NSDictionary *attrs = [NSDictionary dictionaryWithObject:@"jpeg" forKey:QTAddImageCodecType];
do {
image = [[[recordStream recordPainter] canvas] renderInImage];
[movie addImage:image forDuration:QTMakeTime(1, [recordStream framesPerSecond]) withAttributes:attrs];
} while ([recordStream nextFrame]);
if ([movie updateMovieFile])
return YES;
}
return NO;
}

@end
2 changes: 1 addition & 1 deletion DataSourceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@

- (void) openDataSource;
- (BOOL) openDataSourcesForPaths:(NSArray *) paths;

- (void) closeAll;
@end
Loading

0 comments on commit d05d962

Please sign in to comment.