// // 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