//
// DKDrawing+FiletypePattern.m
// DrawKit
//
// Created by Brad Larson on 5/16/2008.
//
#import "DKDrawing+FiletypePattern.h"
#import "SPDrawableRect.h"
#import "SPDrawablePoint.h"
#import "DKArcPath.h"
#define MARGIN_PERCENTAGE_FOR_PATTERNS 0.1
@implementation DKDrawing (FiletypePattern)
#pragma mark -
#pragma mark Loading
/*
SonoPlot XML file format
*/
+ (DKDrawing*) drawingWithPattern:(NSData*)drawingData;
{
// Initialize drawing and add required layers
DKDrawing *currentDrawing = [[DKDrawing alloc] initWithSize:NSMakeSize(1, 1)];
[currentDrawing setDrawingUnits:@"Microns" unitToPointsConversionFactor:kSPMicronToPointsConversion];
[currentDrawing setSnapsToGuides:NO];
[DKGridLayer setGridThemeColour:[[NSColor blackColor] colorWithAlphaComponent:0.5]];
DKGridLayer* grid = [[DKGridLayer alloc] init];
[grid setSpan:(kSPMicronToPointsConversion * 100) unitToPointsConversionFactor:kSPMicronToPointsConversion measurementSystem:kGCMetricDrawingGrid drawingUnits:@"Microns" divisions:10 majors:2 rulerSteps:10];
[currentDrawing addLayer:grid];
DKObjectDrawingLayer* currentLayer = [[DKObjectDrawingLayer alloc] init];
// Disable object snap
[currentLayer setAllowsSnapToObjects:NO];
[currentDrawing addLayer:currentLayer];
[currentDrawing setActiveLayer:currentLayer];
DKGuideLayer* guides = [[DKGuideLayer alloc] init];
[currentDrawing addLayer:guides];
[guides release];
[grid tweakDrawingMargins];
// Pull in XML document and begin processing
NSXMLDocument *xmlDocumentFromDisk;
NSError *error = nil;
xmlDocumentFromDisk = [[NSXMLDocument alloc] initWithData:drawingData options:(NSXMLNodePreserveWhitespace|NSXMLNodePreserveCDATA) error:&error];
if (xmlDocumentFromDisk == nil)
xmlDocumentFromDisk = [[NSXMLDocument alloc] initWithData:drawingData options:NSXMLDocumentTidyXML error:&error];
if (xmlDocumentFromDisk == nil)
return nil;
NSXMLNode *child = [[xmlDocumentFromDisk rootElement] childAtIndex:0];
if (![[[xmlDocumentFromDisk rootElement] name] isEqualToString:@"pattern"])
return nil;
// Set some defaults for the drawing metadata
[[currentDrawing drawingInfo] addEntriesFromDictionary:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], @"isDrawingConnectedLinesWithoutPickingUpDispenser",
[NSNumber numberWithBool:YES], @"isReturningToStartingPositionWhenDone",
[NSNumber numberWithDouble:1.0], @"scaleFactorForX",
[NSNumber numberWithDouble:1.0], @"scaleFactorForY",
nil]];
// Set up a default style for drawing elements
CGFloat lineWidth = [grid quartzDistanceForGridDistance:50.0];
DKStyle* filledFeatureStyle = [[DKStyle alloc] init];
DKStyle* emptyFeatureStyle = [[DKStyle alloc] init];
// Fill with a translucent grey so that users can see if they are doing multiple passes over the same region
DKFill* featureFill = [DKFill fillWithColour:[[NSColor blackColor] colorWithAlphaComponent:0.25]];
[filledFeatureStyle addRenderer:featureFill];
// Do rounded ends and joins to visually simulate the radius of a dispenser
DKStroke* featureStroke = [DKStroke strokeWithWidth:lineWidth colour:[NSColor blackColor]];
[featureStroke setLineCapStyle:NSRoundLineCapStyle];
[featureStroke setLineJoinStyle:NSRoundLineJoinStyle];
[filledFeatureStyle addRenderer:featureStroke];
[emptyFeatureStyle addRenderer:featureStroke];
// Store the style and stroke away as easily accessible metadata so that line width changes can be reflected globally
[currentDrawing setMetadataObject:filledFeatureStyle forKey:@"filledFeatureStyle"];
[currentDrawing setMetadataObject:emptyFeatureStyle forKey:@"emptyFeatureStyle"];
[currentDrawing setMetadataObject:featureStroke forKey:@"featureStroke"];
NSDictionary *patternItemLookupTable = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInteger:0], @"point",
[NSNumber numberWithInteger:1], @"line",
[NSNumber numberWithInteger:2], @"horizfill",
[NSNumber numberWithInteger:3], @"vertfill",
[NSNumber numberWithInteger:4], @"arc",
[NSNumber numberWithInteger:5], @"arcfill",
[NSNumber numberWithInteger:6], @"xmultiplier",
[NSNumber numberWithInteger:7], @"ymultiplier",
[NSNumber numberWithInteger:8], @"multiplier",
[NSNumber numberWithInteger:9], @"continuous",
[NSNumber numberWithInteger:10], @"noreturn",
[NSNumber numberWithInteger:11], @"featurewidth",
[NSNumber numberWithInteger:12], @"gridspacing",
nil];
// Set the default line style to use rounded caps at ends and joins
do
{
NSNumber * switchCode = [patternItemLookupTable objectForKey:child.name];
if (switchCode != nil)
{
NSDictionary *xmlPatternItemAttributes = [self patternElementAttributesFromXMLNode:child];
switch ([switchCode integerValue])
{
// TODO: Find a better way of doing the resize-to-fits
case 0: // Point
{
[currentDrawing resizeToFitPoint:NSMakePoint([[xmlPatternItemAttributes valueForKey:@"x"] doubleValue], [[xmlPatternItemAttributes valueForKey:@"y"] doubleValue])];
SPDrawablePoint *currentPoint = [[SPDrawablePoint alloc] initWithPatternItemAttributes:xmlPatternItemAttributes style:emptyFeatureStyle grid:grid];
[currentLayer addObject:currentPoint];
[currentPoint release];
}; break;
case 1: // Line
{
[currentDrawing resizeToFitPoint:NSMakePoint([[xmlPatternItemAttributes valueForKey:@"x"] doubleValue], [[xmlPatternItemAttributes valueForKey:@"y"] doubleValue])];
[currentDrawing resizeToFitPoint:NSMakePoint([[xmlPatternItemAttributes valueForKey:@"x2"] doubleValue], [[xmlPatternItemAttributes valueForKey:@"y2"] doubleValue])];
DKDrawablePath *currentPath = [[DKDrawablePath alloc] initWithPatternItemAttributes:xmlPatternItemAttributes style:emptyFeatureStyle grid:grid];
[currentLayer addObject:currentPath];
[currentPath release];
}; break;
case 2: // Horizontal fill
{
[currentDrawing resizeToFitPoint:NSMakePoint([[xmlPatternItemAttributes valueForKey:@"x"] doubleValue], [[xmlPatternItemAttributes valueForKey:@"y"] doubleValue])];
[currentDrawing resizeToFitPoint:NSMakePoint([[xmlPatternItemAttributes valueForKey:@"x2"] doubleValue], [[xmlPatternItemAttributes valueForKey:@"y2"] doubleValue])];
SPDrawableRect *currentRect = [[SPDrawableRect alloc] initWithPatternItemAttributes:xmlPatternItemAttributes style:filledFeatureStyle grid:grid];
[currentRect setMetadataObject:[NSNumber numberWithBool:YES] forKey:@"fillHorizontally"];
[currentLayer addObject:currentRect];
[currentRect release];
}; break;
case 3: // Vertical fill
{
[currentDrawing resizeToFitPoint:NSMakePoint([[xmlPatternItemAttributes valueForKey:@"x"] doubleValue], [[xmlPatternItemAttributes valueForKey:@"y"] doubleValue])];
[currentDrawing resizeToFitPoint:NSMakePoint([[xmlPatternItemAttributes valueForKey:@"x2"] doubleValue], [[xmlPatternItemAttributes valueForKey:@"y2"] doubleValue])];
SPDrawableRect *currentRect = [[SPDrawableRect alloc] initWithPatternItemAttributes:xmlPatternItemAttributes style:filledFeatureStyle grid:grid];
[currentRect setMetadataObject:[NSNumber numberWithBool:NO] forKey:@"fillHorizontally"];
[currentLayer addObject:currentRect];
[currentRect release];
}; break;
case 4: // Arc
{
[currentDrawing resizeToFitPoint:NSMakePoint([[xmlPatternItemAttributes valueForKey:@"x"] doubleValue], [[xmlPatternItemAttributes valueForKey:@"y"] doubleValue])];
// TODO: Resize drawing to deal with limits of arc
DKArcPath *currentPath = [[DKArcPath alloc] initWithPatternItemAttributes:xmlPatternItemAttributes style:emptyFeatureStyle grid:grid];
[currentLayer addObject:currentPath];
[currentPath release];
}; break;
case 5: // Arc fill
{
// NSPoint pointToConvert = NSMakePoint([[xmlPatternItemAttributes valueForKey:@"x"] doubleValue], [[xmlPatternItemAttributes valueForKey:@"y"] doubleValue]);
//
// double startAngle = [[xmlPatternItemAttributes valueForKey:@"startangle"] doubleValue];
// double endAngle = [[xmlPatternItemAttributes valueForKey:@"endangle"] doubleValue];
// if (startAngle == endAngle)
// {
// startAngle = 0;
// endAngle = 360;
// }
// NSBezierPath* currentBezierPath = [NSBezierPath bezierPath];
// [currentBezierPath appendBezierPathWithArcWithCenter:[grid pointForGridLocation:pointToConvert]
// radius:[grid quartzDistanceForGridDistance:[[xmlPatternItemAttributes valueForKey:@"radius"] doubleValue]]
// startAngle:startAngle
// endAngle:endAngle
// clockwise:[[xmlPatternItemAttributes valueForKey:@"clockwise"] boolValue]];
// [currentBezierPath appendBezierPathWithArcWithCenter:[grid pointForGridLocation:pointToConvert]
// radius:[grid quartzDistanceForGridDistance:[[xmlPatternItemAttributes valueForKey:@"insideradius"] doubleValue]]
// startAngle:endAngle
// endAngle:startAngle
// clockwise:![[xmlPatternItemAttributes valueForKey:@"clockwise"] boolValue]];
//
// DKDrawableShape* currentShape = [[DKDrawablePath alloc] initWithBezierPath:currentBezierPath];
// NSString *velocityString = [xmlPatternItemAttributes valueForKey:@"velocity"];
// if (velocityString != nil)
// [currentShape setFloatValue:[velocityString floatValue] forKey:@"velocity"];
//
// NSNumber *counterClockwise = [xmlPatternItemAttributes valueForKey:@"counterclockwise"];
// if (counterClockwise != nil)
// [currentShape setIntValue:1 forKey:@"counterClockwise"];
//
// [currentShape setString:[xmlPatternItemAttributes valueForKey:@"solution"] forKey:@"solution"];
//
// [currentLayer addObject:currentShape];
// [currentShape setStyle:featureStyle];
// [currentShape release];
}; break;
case 6:[currentDrawing setFloatValue:[[child stringValue] doubleValue] forKey:@"scaleFactorForX"]; break;
case 7:[currentDrawing setFloatValue:[[child stringValue] doubleValue] forKey:@"scaleFactorForY"]; break;
case 8:
{
[currentDrawing setFloatValue:[[child stringValue] doubleValue] forKey:@"scaleFactorForX"];
[currentDrawing setFloatValue:[[child stringValue] doubleValue] forKey:@"scaleFactorForY"];
}; break;
case 9:[currentDrawing setIntValue:1 forKey:@"isDrawingConnectedLinesWithoutPickingUpDispenser"]; break;
case 10:[currentDrawing setIntValue:0 forKey:@"isReturningToStartingPositionWhenDone"]; break;
case 11:
{
[featureStroke setWidth:[grid quartzDistanceForGridDistance:[[child stringValue] doubleValue]]];
}; break;
case 12:[grid setSpan:([grid quartzDistanceForGridDistance:[[child stringValue] doubleValue]] * 10.0) divisions:10 majors:2]; break;
}
}
} while ((child = [child nextSibling]));
[grid release];
// Remove capability of dragging objects off the drawing when they hit its edge
NSRect currentDragExclusionRect = [currentLayer dragExclusionRect];
currentDragExclusionRect.origin.x -= [currentDrawing leftMargin];
currentDragExclusionRect.origin.y -= [currentDrawing topMargin];
currentDragExclusionRect.size.width += ([currentDrawing leftMargin] + [currentDrawing rightMargin]);
currentDragExclusionRect.size.height += ([currentDrawing topMargin] + [currentDrawing bottomMargin]);
[currentLayer setDragExclusionRect:currentDragExclusionRect];
[currentLayer setAllowsObjectsToBeTargetedByDrags:NO];
[currentLayer release];
[filledFeatureStyle release];
[emptyFeatureStyle release];
return [currentDrawing autorelease];
}
// This takes all of the sub-elements from a pattern item (0, for example) and creates a dictionary of key-value pairs from them
+ (NSDictionary*) patternElementAttributesFromXMLNode:(NSXMLNode*)xmlNode;
{
NSMutableDictionary* xmlKeysAndValues = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"0", @"solution",
nil];
// Use dictionary to map to the keys
NSXMLNode *child = [xmlNode childAtIndex:0];
if (child == nil)
return nil;
if ([child name] == nil)
return nil;
do
{
if ([child stringValue] == nil)
[xmlKeysAndValues setValue:[NSNumber numberWithBool:YES] forKey:[child name]];
else
[xmlKeysAndValues setValue:[child stringValue] forKey:[child name]];
} while ((child = [child nextSibling]));
return [xmlKeysAndValues autorelease];
}
- (void) resizeToFitPoint:(NSPoint) p;
{
if ([self gridLayer] == nil)
return;
BOOL needsResize = NO;
NSPoint scaledPoint = [[self gridLayer] pointForGridLocation:p];
scaledPoint.x += [self leftMargin];
scaledPoint.y += [self topMargin];
NSSize drawingSize = [self drawingSize];
// Grow the drawing to fit the elements being loaded, with a little padding
if (scaledPoint.x > drawingSize.width)
{
drawingSize.width = scaledPoint.x;
needsResize = YES;
}
if (scaledPoint.y > drawingSize.height)
{
drawingSize.height = scaledPoint.y;
needsResize = YES;
}
if (needsResize)
{
float marginSize = MARGIN_PERCENTAGE_FOR_PATTERNS * [[self gridLayer] quartzDistanceForGridDistance:drawingSize.width];
[self setMarginsLeft:marginSize top:marginSize right:marginSize bottom:marginSize];
[self setDrawingSize:drawingSize];
}
}
- (void) resizeDrawingInMicrons:(NSSize) newSize;
{
NSSize newDocumentSize;
DKGridLayer* grid = [self gridLayer];
// Resize the margins to be a set percentage of the width of the pattern, we just use them for a nice border
float marginSize = MARGIN_PERCENTAGE_FOR_PATTERNS * [grid quartzDistanceForGridDistance:newSize.width];
[self setMarginsLeft:marginSize top:marginSize right:marginSize bottom:marginSize];
newDocumentSize.width = (2.0 * marginSize) + [grid quartzDistanceForGridDistance:newSize.width];
newDocumentSize.height = (2.0 * marginSize) + [grid quartzDistanceForGridDistance:newSize.height];
[self setDrawingSize:newDocumentSize];
[self setNeedsDisplay:YES];
[grid invalidateCache];
[grid synchronizeRulers];
[grid setNeedsDisplay:YES];
}
#pragma mark -
#pragma mark Saving
- (NSData*) dataForPattern;
{
NSXMLElement *root = (NSXMLElement *)[NSXMLNode elementWithName:@"pattern"];
NSXMLDocument *xmlDocumentToSave = [[NSXMLDocument alloc] initWithRootElement:root];
xmlDocumentToSave.version = @"1.0";
xmlDocumentToSave.characterEncoding = @"utf-8";
// First, write out the application-specific document metadata
float multiplierHolder = [self floatValueForKey:@"scaleFactorForX"];
if (multiplierHolder == 0.0)
multiplierHolder = 1.0;
[DKDrawing addPropertyToNode:root withName:@"xmultiplier" floatValue:multiplierHolder];
multiplierHolder = [self floatValueForKey:@"scaleFactorForY"];
if (multiplierHolder == 0.0)
multiplierHolder = 1.0;
[DKDrawing addPropertyToNode:root withName:@"ymultiplier" floatValue:multiplierHolder];
multiplierHolder = [[self gridLayer] gridDistanceForQuartzDistance:[[self metadataObjectForKey:@"featureStroke"] width]];
[DKDrawing addPropertyToNode:root withName:@"featurewidth" floatValue:multiplierHolder];
[DKDrawing addPropertyToNode:root withName:@"gridspacing" intValue:(int)round([[self gridLayer] gridDistanceForQuartzDistance:[[self gridLayer] span]] / 10.0)];
if ([self intValueForKey:@"isDrawingConnectedLinesWithoutPickingUpDispenser"])
[root addChild:[NSXMLNode elementWithName:@"continuous"]];
if (![self intValueForKey:@"isReturningToStartingPositionWhenDone"])
[root addChild:[NSXMLNode elementWithName:@"noreturn"]];
// Iterate over each layer to grab all drawable objects
for (DKLayer *currentLayer in [self layers])
{
if ([currentLayer respondsToSelector:@selector(objects)])
{
for (DKDrawableObject *currentObject in [(DKObjectOwnerLayer *)currentLayer objects])
{
[root addChild:[currentObject patternXMLNodeForObjectWithGrid:[self gridLayer]]];
}
}
}
NSData *xmlDataToReturn = [xmlDocumentToSave XMLDataWithOptions:NSXMLNodePrettyPrint];
[xmlDocumentToSave release];
return xmlDataToReturn;
}
+ (void) addPropertyToNode:(NSXMLElement *)xmlNode withName:(NSString *)propertyName intValue:(int)newValue;
{
NSXMLElement *xmlSubNode = [NSXMLNode elementWithName:propertyName];
xmlSubNode.stringValue = [NSString stringWithFormat:@"%d", newValue];
[xmlNode addChild:xmlSubNode];
}
+ (void) addPropertyToNode:(NSXMLElement *)xmlNode withName:(NSString *)propertyName floatValue:(float)newValue;
{
NSXMLElement *xmlSubNode = [NSXMLNode elementWithName:propertyName];
xmlSubNode.stringValue = [NSString stringWithFormat:@"%.2f", newValue];
[xmlNode addChild:xmlSubNode];
}
+ (void) addPropertyToNode:(NSXMLElement *)xmlNode withName:(NSString *)propertyName stringValue:(NSString *)newValue;
{
NSXMLElement *xmlSubNode = [NSXMLNode elementWithName:propertyName];
xmlSubNode.stringValue = newValue;
[xmlNode addChild:xmlSubNode];
}
- (void) optimizeDrawingOrder;
{
// for (DKLayer *currentLayer in [self layers])
// {
// if ([currentLayer respondsToSelector:@selector(objects)])
// {
// for (DKDrawableObject *currentObject in [(DKObjectOwnerLayer *)currentLayer objects])
// {
// [root addChild:[currentObject patternXMLNodeForObjectWithGrid:[self gridLayer]]];
// }
// }
// }
//
}
@end
@implementation DKDrawableObject (FiletypePattern)
- (id)initWithPatternItemAttributes:(NSDictionary *)xmlPatternItemAttributes style:(DKStyle *)featureStyle grid:(DKGridLayer *)grid;
{
self = [self init];
if (self != nil)
{
}
return self;
}
- (NSXMLNode *)patternXMLNodeForObjectWithGrid:(DKGridLayer *)grid;
{
return nil;
}
@end
@implementation DKDrawablePath (FiletypePattern)
// TODO: Handle multi-segmented paths and Bezier paths
- (id)initWithPatternItemAttributes:(NSDictionary *)xmlPatternItemAttributes style:(DKStyle *)featureStyle grid:(DKGridLayer *)grid;
{
self = [self init];
if (self != nil)
{
NSBezierPath* currentBezierPath = [NSBezierPath bezierPath];
NSPoint pointToConvert = NSMakePoint([[xmlPatternItemAttributes valueForKey:@"x"] doubleValue], [[xmlPatternItemAttributes valueForKey:@"y"] doubleValue]);
[currentBezierPath moveToPoint:[grid pointForGridLocation:pointToConvert]];
pointToConvert = NSMakePoint([[xmlPatternItemAttributes valueForKey:@"x2"] doubleValue], [[xmlPatternItemAttributes valueForKey:@"y2"] doubleValue]);
[currentBezierPath lineToPoint:[grid pointForGridLocation:pointToConvert]];
if ([currentBezierPath elementCount] < 2) // Check to make sure that the start point and end point are different, otherwise problems ensue
{
NSLog(@"Ignoring line with same start and end points");
}
[self setPath:currentBezierPath];
NSString *velocityString = [xmlPatternItemAttributes valueForKey:@"velocity"];
if (velocityString != nil)
[self setFloatValue:[velocityString floatValue] forKey:@"velocity"];
[self setString:[xmlPatternItemAttributes valueForKey:@"solution"] forKey:@"solution"];
[self setStyle:featureStyle];
}
return self;
}
- (NSXMLNode *)patternXMLNodeForObjectWithGrid:(DKGridLayer *)grid;
{
NSBezierPath *pathForObject = [self path];
NSPoint pointsForCurrentBezierPathElement[3];
NSBezierPathElement currentPathElement;
NSPoint startingPoint, endingPoint;
if ([pathForObject elementCount] < 2)
return nil; // TODO: Add error handling here
else
{
currentPathElement = [pathForObject elementAtIndex:0 associatedPoints:pointsForCurrentBezierPathElement];
startingPoint = [grid gridLocationForPoint:pointsForCurrentBezierPathElement[0]];
currentPathElement = [pathForObject elementAtIndex:1 associatedPoints:pointsForCurrentBezierPathElement];
endingPoint = [grid gridLocationForPoint:pointsForCurrentBezierPathElement[0]];
// TODO: Add checking on these to make sure that they are correct elements and not curve elements
}
NSXMLElement *xmlNodeForItem = [NSXMLNode elementWithName:@"line"];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"x" intValue:(int)round(startingPoint.x)];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"y" intValue:(int)round(startingPoint.y)];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"z" intValue:0]; // TODO: Store Z portion of coordinate in some manner
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"x2" intValue:(int)round(endingPoint.x)];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"y2" intValue:(int)round(endingPoint.y)];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"z2" intValue:0]; // TODO: Store Z portion of coordinate in some manner
if ([self hasMetadataForKey:@"velocity"])
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"velocity" intValue:(int)round([self floatValueForKey:@"velocity"])];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"solution" stringValue:[self stringForKey:@"solution"]];
return xmlNodeForItem;
}
@end
@implementation DKArcPath (FiletypePattern)
- (id)initWithPatternItemAttributes:(NSDictionary *)xmlPatternItemAttributes style:(DKStyle *)featureStyle grid:(DKGridLayer *)grid;
{
self = [self init];
if (self != nil)
{
NSPoint pointToConvert = NSMakePoint([[xmlPatternItemAttributes valueForKey:@"x"] doubleValue], [[xmlPatternItemAttributes valueForKey:@"y"] doubleValue]);
double startAngle = [[xmlPatternItemAttributes valueForKey:@"startangle"] doubleValue];
double endAngle = [[xmlPatternItemAttributes valueForKey:@"endangle"] doubleValue];
if (startAngle == endAngle)
{
startAngle = 0;
endAngle = 360;
}
[self moveToPoint:[grid pointForGridLocation:pointToConvert]];
[self setRadius:[grid quartzDistanceForGridDistance:[[xmlPatternItemAttributes valueForKey:@"radius"] doubleValue]]];
[self setStartAngle:startAngle];
[self setEndAngle:endAngle];
// TODO: Deal with counterclockwise arcs
// clockwise:[[xmlPatternItemAttributes valueForKey:@"clockwise"] boolValue]];
NSString *velocityString = [xmlPatternItemAttributes valueForKey:@"velocity"];
if (velocityString != nil)
[self setFloatValue:[velocityString floatValue] forKey:@"velocity"];
NSNumber *counterClockwise = [xmlPatternItemAttributes valueForKey:@"counterclockwise"];
if (counterClockwise != nil)
[self setIntValue:1 forKey:@"counterClockwise"];
[self setString:[xmlPatternItemAttributes valueForKey:@"solution"] forKey:@"solution"];
[self setStyle:featureStyle];
}
return self;
}
- (NSXMLNode *)patternXMLNodeForObjectWithGrid:(DKGridLayer *)grid;
{
NSBezierPath *pathForObject = [self path];
NSPoint pointsForCurrentBezierPathElement[3];
NSBezierPathElement currentPathElement;
NSPoint startingPoint;
if ([pathForObject elementCount] < 1)
return nil; // TODO: Add error handling here
else
{
currentPathElement = [pathForObject elementAtIndex:0 associatedPoints:pointsForCurrentBezierPathElement];
startingPoint = [grid gridLocationForPoint:pointsForCurrentBezierPathElement[0]];
// TODO: Add checking on these to make sure that they are correct elements and not curve elements
}
NSXMLElement *xmlNodeForItem = [NSXMLNode elementWithName:@"arc"];
NSPoint convertedCenterPoint = [grid gridLocationForPoint:mCentre];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"x" intValue:(int)round(convertedCenterPoint.x)];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"y" intValue:(int)round(convertedCenterPoint.y)];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"z" intValue:0]; // TODO: Store Z portion of coordinate in some manner
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"startangle" floatValue:[self startAngle]]; // TODO: Store Z portion of coordinate in some manner
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"endangle" floatValue:[self endAngle]]; // TODO: Store Z portion of coordinate in some manner
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"radius" floatValue:[grid gridDistanceForQuartzDistance:[self radius]]]; // TODO: Store Z portion of coordinate in some manner
if ([self hasMetadataForKey:@"counterClockwise"])
[xmlNodeForItem addChild:[NSXMLNode elementWithName:@"counterClockwise"]];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"solution" stringValue:[self stringForKey:@"solution"]];
if ([self hasMetadataForKey:@"velocity"])
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"velocity" intValue:(int)round([self floatValueForKey:@"velocity"])];
return xmlNodeForItem;
}
@end
@implementation SPDrawablePoint (FiletypePattern)
- (id)initWithPatternItemAttributes:(NSDictionary *)xmlPatternItemAttributes style:(DKStyle *)featureStyle grid:(DKGridLayer *)grid;
{
self = [self init];
if (self != nil)
{
NSPoint pointToConvert = NSMakePoint([[xmlPatternItemAttributes valueForKey:@"x"] doubleValue], [[xmlPatternItemAttributes valueForKey:@"y"] doubleValue]);
[self setCenter:[grid pointForGridLocation:pointToConvert]];
// [currentPoint setPathEditingMode:kGCPathCreateModeEditExisting];
[self setString:[xmlPatternItemAttributes valueForKey:@"solution"] forKey:@"solution"];
[self setIntValue:[[xmlPatternItemAttributes valueForKey:@"duration"] intValue] forKey:@"duration"];
[self setStyle:featureStyle];
}
return self;
}
- (NSXMLNode *)patternXMLNodeForObjectWithGrid:(DKGridLayer *)grid;
{
NSBezierPath *pathForObject = [self path];
NSPoint pointsForCurrentBezierPathElement[3];
NSBezierPathElement currentPathElement;
NSPoint startingPoint;
if ([pathForObject elementCount] < 1)
return nil; // TODO: Add error handling here
else
{
currentPathElement = [pathForObject elementAtIndex:0 associatedPoints:pointsForCurrentBezierPathElement];
startingPoint = [grid gridLocationForPoint:pointsForCurrentBezierPathElement[0]];
// TODO: Add checking on these to make sure that they are correct elements and not curve elements
}
NSXMLElement *xmlNodeForItem = [NSXMLNode elementWithName:@"point"];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"x" intValue:(int)round(startingPoint.x)];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"y" intValue:(int)round(startingPoint.y)];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"z" intValue:0]; // TODO: Store Z portion of coordinate in some manner
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"solution" stringValue:[self stringForKey:@"solution"]];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"duration" intValue:[self intValueForKey:@"duration"]];
return xmlNodeForItem;
}
@end
@implementation SPDrawableRect (FiletypePattern)
- (id)initWithPatternItemAttributes:(NSDictionary *)xmlPatternItemAttributes style:(DKStyle *)featureStyle grid:(DKGridLayer *)grid;
{
self = [self init];
if (self != nil)
{
NSPoint point1 = NSMakePoint([[xmlPatternItemAttributes valueForKey:@"x"] doubleValue], [[xmlPatternItemAttributes valueForKey:@"y"] doubleValue]);
NSPoint point2 = NSMakePoint([[xmlPatternItemAttributes valueForKey:@"x2"] doubleValue], [[xmlPatternItemAttributes valueForKey:@"y2"] doubleValue]);
NSLog(@"Loading line");
NSLog(@"%f, %f", point1.x, point1.y);
NSLog(@"%f, %f", point2.x, point2.y);
point1 = [grid pointForGridLocation:point1];
point2 = [grid pointForGridLocation:point2];
NSRect rectangleToDraw;
if (point1.x < point2.x)
rectangleToDraw.origin.x = point1.x;
else
rectangleToDraw.origin.x = point2.x;
if (point1.y < point2.y)
rectangleToDraw.origin.y = point1.y;
else
rectangleToDraw.origin.y = point2.y;
rectangleToDraw.size.width = fabs(point2.x - point1.x);
rectangleToDraw.size.height = fabs(point2.y - point1.y);
NSPoint cp;
cp.x = NSMidX( rectangleToDraw );
cp.y = NSMidY( rectangleToDraw );
[[self path] appendBezierPathWithRect:[[self class] unitRectAtOrigin]];
[self setSize:rectangleToDraw.size];
[self moveToPoint:cp];
[self setStyle:featureStyle];
NSString *velocityString = [xmlPatternItemAttributes valueForKey:@"velocity"];
if (velocityString != nil)
[self setFloatValue:[velocityString floatValue] forKey:@"velocity"];
[self setString:[xmlPatternItemAttributes valueForKey:@"solution"] forKey:@"solution"];
}
return self;
}
- (NSXMLNode *)patternXMLNodeForObjectWithGrid:(DKGridLayer *)grid;
{
NSBezierPath *pathForObject = [self transformedPath];
// Deal with solid, filled shape
NSPoint pointsForCurrentBezierPathElement[3];
NSBezierPathElement currentPathElement;
unsigned int currentElementIndex;
NSPoint startingPoint, endingPoint;
for (currentElementIndex = 0; currentElementIndex < [pathForObject elementCount]; currentElementIndex++)
{
currentPathElement = [pathForObject elementAtIndex:currentElementIndex associatedPoints:pointsForCurrentBezierPathElement];
switch (currentPathElement)
{
case NSMoveToBezierPathElement:
{
startingPoint = [grid gridLocationForPoint:pointsForCurrentBezierPathElement[0]];
}; break;
case NSLineToBezierPathElement:
{
endingPoint = [grid gridLocationForPoint:pointsForCurrentBezierPathElement[0]];
}; break;
}
}
NSXMLElement *xmlNodeForItem;
if ([[self metadataObjectForKey:@"fillHorizontally"] boolValue])
{
xmlNodeForItem = [NSXMLNode elementWithName:@"horizfill"];
}
else
{
xmlNodeForItem = [NSXMLNode elementWithName:@"vertfill"];
}
currentPathElement = [pathForObject elementAtIndex:0 associatedPoints:pointsForCurrentBezierPathElement];
startingPoint = [grid gridLocationForPoint:pointsForCurrentBezierPathElement[0]];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"x" intValue:(int)round(startingPoint.x)];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"y" intValue:(int)round(startingPoint.y)];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"z" intValue:0]; // TODO: Store Z portion of coordinate in some manner
currentPathElement = [pathForObject elementAtIndex:2 associatedPoints:pointsForCurrentBezierPathElement];
endingPoint = [grid gridLocationForPoint:pointsForCurrentBezierPathElement[0]];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"x2" intValue:(int)round(endingPoint.x)];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"y2" intValue:(int)round(endingPoint.y)];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"z2" intValue:0]; // TODO: Store Z portion of coordinate in some manner
if ([self hasMetadataForKey:@"velocity"])
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"velocity" intValue:(int)round([self floatValueForKey:@"velocity"])];
[DKDrawing addPropertyToNode:xmlNodeForItem withName:@"solution" stringValue:[self stringForKey:@"solution"]];
return xmlNodeForItem;
}
@end