Authored by Justin R. Miller

remove RMPath for good in favor of RMShape

... ... @@ -32,7 +32,6 @@
#import "RMFoundation.h"
#import "RMProjection.h"
#import "RMMarker.h"
#import "RMPath.h"
#import "RMCircle.h"
#import "RMShape.h"
#import "RMAnnotation.h"
... ...
//
// RMPath.h
//
// Copyright (c) 2008-2012, Route-Me Contributors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#import <UIKit/UIKit.h>
#import "RMFoundation.h"
#import "RMMapLayer.h"
@class RMMapView;
@interface RMPath : RMMapLayer
{
BOOL isFirstPoint;
// The color of the line, or the outline if a polygon
UIColor *lineColor;
// The color of polygon's fill.
UIColor *fillColor;
CGMutablePathRef path;
CGRect pathBoundingBox;
BOOL ignorePathUpdates;
CGRect previousBounds;
// Width of the line, in pixels
float lineWidth;
/*! Drawing mode of the path; Choices are
kCGPathFill,
kCGPathEOFill,
kCGPathStroke,
kCGPathFillStroke,
kCGPathEOFillStroke */
CGPathDrawingMode drawingMode;
// Line cap and join styles
CGLineCap lineCap;
CGLineJoin lineJoin;
// Line dash style
CGFloat *_lineDashLengths;
CGFloat *_scaledLineDashLengths;
size_t _lineDashCount;
CGFloat lineDashPhase;
// Line shadow
CGFloat shadowBlur;
CGSize shadowOffset;
BOOL enableShadow;
BOOL scaleLineWidth;
BOOL scaleLineDash; // if YES line dashes will be scaled to keep a constant size if the layer is zoomed
float renderedScale;
RMMapView *mapView;
}
// DEPRECATED. Use RMShape instead.
- (id)initWithView:(RMMapView *)aMapView __attribute__ ((deprecated));
@property (nonatomic, assign) CGPathDrawingMode drawingMode;
@property (nonatomic, assign) CGLineCap lineCap;
@property (nonatomic, assign) CGLineJoin lineJoin;
@property (nonatomic, assign) NSArray *lineDashLengths;
@property (nonatomic, assign) CGFloat lineDashPhase;
@property (nonatomic, assign) BOOL scaleLineDash;
@property (nonatomic, assign) float lineWidth;
@property (nonatomic, assign) BOOL scaleLineWidth;
@property (nonatomic, assign) CGFloat shadowBlur;
@property (nonatomic, assign) CGSize shadowOffset;
@property (nonatomic, assign) BOOL enableShadow;
@property (nonatomic, retain) UIColor *lineColor;
@property (nonatomic, retain) UIColor *fillColor;
@property (nonatomic, readonly) CGRect pathBoundingBox;
- (void)moveToProjectedPoint:(RMProjectedPoint)projectedPoint;
- (void)moveToScreenPoint:(CGPoint)point;
- (void)moveToCoordinate:(CLLocationCoordinate2D)coordinate;
- (void)addLineToProjectedPoint:(RMProjectedPoint)projectedPoint;
- (void)addLineToScreenPoint:(CGPoint)point;
- (void)addLineToCoordinate:(CLLocationCoordinate2D)coordinate;
// Change the path without recalculating the geometry (performance!)
- (void)performBatchOperations:(void (^)(RMPath *aPath))block;
// This closes the path, connecting the last point to the first.
// After this action, no further points can be added to the path.
// There is no requirement that a path be closed.
- (void)closePath;
@end
//
// RMPath.m
//
// Copyright (c) 2008-2012, Route-Me Contributors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#import "RMPath.h"
#import "RMPixel.h"
#import "RMProjection.h"
#import "RMMapView.h"
#import "RMAnnotation.h"
@implementation RMPath
@synthesize scaleLineWidth;
@synthesize lineDashPhase;
@synthesize scaleLineDash;
@synthesize shadowBlur;
@synthesize shadowOffset;
@synthesize enableShadow;
@synthesize pathBoundingBox;
#define kDefaultLineWidth 2.0
- (id)initWithView:(RMMapView *)aMapView
{
if (!(self = [super init]))
return nil;
mapView = aMapView;
path = CGPathCreateMutable();
pathBoundingBox = CGRectZero;
ignorePathUpdates = NO;
previousBounds = CGRectZero;
lineWidth = kDefaultLineWidth;
drawingMode = kCGPathFillStroke;
lineCap = kCGLineCapButt;
lineJoin = kCGLineJoinMiter;
lineColor = [UIColor blackColor];
fillColor = [UIColor redColor];
_lineDashCount = 0;
_lineDashLengths = NULL;
_scaledLineDashLengths = NULL;
lineDashPhase = 0.0;
self.shadowBlur = 0.0;
self.shadowOffset = CGSizeMake(0, 0);
self.enableShadow = NO;
self.masksToBounds = YES;
scaleLineWidth = NO;
scaleLineDash = NO;
isFirstPoint = YES;
if ([self respondsToSelector:@selector(setContentsScale:)])
[(id)self setValue:[[UIScreen mainScreen] valueForKey:@"scale"] forKey:@"contentsScale"];
return self;
}
- (void)dealloc
{
mapView = nil;
CGPathRelease(path); path = NULL;
[self setLineDashLengths:nil];
[lineColor release]; lineColor = nil;
[fillColor release]; fillColor = nil;
[super dealloc];
}
//- (id <CAAction>)actionForKey:(NSString *)key
//{
// return nil;
//}
#pragma mark -
- (void)recalculateGeometry
{
if (ignorePathUpdates)
return;
CGPoint myPosition = self.annotation.position;
float scale = [mapView metersPerPixel];
float scaledLineWidth;
CGRect pixelBounds, screenBounds;
float offset;
const float outset = 100.0f; // provides a buffer off screen edges for when path is scaled or moved
// The bounds are actually in mercators...
/// \bug if "bounds are actually in mercators", shouldn't be using a CGRect
scaledLineWidth = lineWidth;
if (!scaleLineWidth)
{
renderedScale = [mapView metersPerPixel];
scaledLineWidth *= renderedScale;
}
CGRect boundsInMercators = CGPathGetBoundingBox(path);
boundsInMercators = CGRectInset(boundsInMercators, -scaledLineWidth, -scaledLineWidth);
pixelBounds = CGRectInset(boundsInMercators, -scaledLineWidth, -scaledLineWidth);
pixelBounds = RMScaleCGRectAboutPoint(pixelBounds, 1.0f / scale, CGPointZero);
// Clip bound rect to screen bounds.
// If bounds are not clipped, they won't display when you zoom in too much.
screenBounds = [mapView frame];
// Clip top
offset = myPosition.y + pixelBounds.origin.y - screenBounds.origin.y + outset;
if (offset < 0.0f)
{
pixelBounds.origin.y -= offset;
pixelBounds.size.height += offset;
}
// Clip left
offset = myPosition.x + pixelBounds.origin.x - screenBounds.origin.x + outset;
if (offset < 0.0f)
{
pixelBounds.origin.x -= offset;
pixelBounds.size.width += offset;
}
// Clip bottom
offset = myPosition.y + pixelBounds.origin.y + pixelBounds.size.height - screenBounds.origin.y - screenBounds.size.height - outset;
if (offset > 0.0f)
{
pixelBounds.size.height -= offset;
}
// Clip right
offset = myPosition.x + pixelBounds.origin.x + pixelBounds.size.width - screenBounds.origin.x - screenBounds.size.width - outset;
if (offset > 0.0f)
{
pixelBounds.size.width -= offset;
}
[super setPosition:myPosition];
self.bounds = pixelBounds;
previousBounds = pixelBounds;
// RMLog(@"x:%f y:%f screen bounds: %f %f %f %f", myPosition.x, myPosition.y, screenBounds.origin.x, screenBounds.origin.y, screenBounds.size.width, screenBounds.size.height);
// RMLog(@"new bounds: %f %f %f %f", self.bounds.origin.x, self.bounds.origin.y, self.bounds.size.width, self.bounds.size.height);
pathBoundingBox = CGRectMake(myPosition.x + self.bounds.origin.x, myPosition.y + self.bounds.origin.y, self.bounds.size.width, self.bounds.size.height);
self.anchorPoint = CGPointMake(-pixelBounds.origin.x / pixelBounds.size.width, -pixelBounds.origin.y / pixelBounds.size.height);
[self setNeedsDisplay];
}
- (void)drawInContext:(CGContextRef)theContext
{
renderedScale = [mapView metersPerPixel];
CGFloat *dashLengths = _lineDashLengths;
float scale = 1.0f / [mapView metersPerPixel];
float scaledLineWidth = lineWidth;
if (!scaleLineWidth)
scaledLineWidth *= renderedScale;
// NSLog(@"line width = %f, content scale = %f", scaledLineWidth, renderedScale);
if (!scaleLineDash && _lineDashLengths)
{
dashLengths = _scaledLineDashLengths;
for (size_t dashIndex=0; dashIndex<_lineDashCount; dashIndex++)
dashLengths[dashIndex] = _lineDashLengths[dashIndex] * renderedScale;
}
CGContextScaleCTM(theContext, scale, scale);
CGContextBeginPath(theContext);
CGContextAddPath(theContext, path);
CGContextSetLineWidth(theContext, scaledLineWidth);
CGContextSetLineCap(theContext, lineCap);
CGContextSetLineJoin(theContext, lineJoin);
CGContextSetStrokeColorWithColor(theContext, [lineColor CGColor]);
CGContextSetFillColorWithColor(theContext, [fillColor CGColor]);
if (_lineDashLengths)
CGContextSetLineDash(theContext, lineDashPhase, dashLengths, _lineDashCount);
if (self.enableShadow)
CGContextSetShadow(theContext, self.shadowOffset, self.shadowBlur);
// according to Apple's documentation, DrawPath closes the path if it's a filled style, so a call to ClosePath isn't necessary
CGContextDrawPath(theContext, drawingMode);
}
#pragma mark -
- (void)addPointToProjectedPoint:(RMProjectedPoint)point withDrawing:(BOOL)isDrawing
{
// RMLog(@"addLineToXY %f %f", point.x, point.y);
if (isFirstPoint)
{
isFirstPoint = FALSE;
projectedLocation = point;
self.position = [mapView projectedPointToPixel:projectedLocation];
// RMLog(@"screen position set to %f %f", self.position.x, self.position.y);
CGPathMoveToPoint(path, NULL, 0.0f, 0.0f);
}
else
{
point.x = point.x - projectedLocation.x;
point.y = point.y - projectedLocation.y;
if (isDrawing)
CGPathAddLineToPoint(path, NULL, point.x, -point.y);
else
CGPathMoveToPoint(path, NULL, point.x, -point.y);
[self recalculateGeometry];
}
[self setNeedsDisplay];
}
- (void)moveToProjectedPoint:(RMProjectedPoint)projectedPoint
{
[self addPointToProjectedPoint:projectedPoint withDrawing:NO];
}
- (void)moveToScreenPoint:(CGPoint)point
{
RMProjectedPoint mercator = [mapView pixelToProjectedPoint:point];
[self moveToProjectedPoint:mercator];
}
- (void)moveToCoordinate:(CLLocationCoordinate2D)coordinate
{
RMProjectedPoint mercator = [[mapView projection] coordinateToProjectedPoint:coordinate];
[self moveToProjectedPoint:mercator];
}
- (void)addLineToProjectedPoint:(RMProjectedPoint)projectedPoint
{
[self addPointToProjectedPoint:projectedPoint withDrawing:YES];
}
- (void)addLineToScreenPoint:(CGPoint)point
{
RMProjectedPoint mercator = [mapView pixelToProjectedPoint:point];
[self addLineToProjectedPoint:mercator];
}
- (void)addLineToCoordinate:(CLLocationCoordinate2D)coordinate
{
RMProjectedPoint mercator = [[mapView projection] coordinateToProjectedPoint:coordinate];
[self addLineToProjectedPoint:mercator];
}
- (void)performBatchOperations:(void (^)(RMPath *aPath))block
{
ignorePathUpdates = YES;
block(self);
ignorePathUpdates = NO;
[self recalculateGeometry];
}
#pragma mark - Accessors
- (void)closePath
{
CGPathCloseSubpath(path);
}
- (float)lineWidth
{
return lineWidth;
}
- (void)setLineWidth:(float)newLineWidth
{
lineWidth = newLineWidth;
[self recalculateGeometry];
}
- (CGPathDrawingMode)drawingMode
{
return drawingMode;
}
- (void)setDrawingMode:(CGPathDrawingMode)newDrawingMode
{
drawingMode = newDrawingMode;
[self setNeedsDisplay];
}
- (CGLineCap)lineCap
{
return lineCap;
}
- (void)setLineCap:(CGLineCap)newLineCap
{
lineCap = newLineCap;
[self setNeedsDisplay];
}
- (CGLineJoin)lineJoin
{
return lineJoin;
}
- (void)setLineJoin:(CGLineJoin)newLineJoin
{
lineJoin = newLineJoin;
[self setNeedsDisplay];
}
- (UIColor *)lineColor
{
return lineColor;
}
- (void)setLineColor:(UIColor *)aLineColor
{
if (lineColor != aLineColor)
{
[lineColor release];
lineColor = [aLineColor retain];
[self setNeedsDisplay];
}
}
- (UIColor *)fillColor
{
return fillColor;
}
- (void)setFillColor:(UIColor *)aFillColor
{
if (fillColor != aFillColor)
{
[fillColor release];
fillColor = [aFillColor retain];
[self setNeedsDisplay];
}
}
- (NSArray *)lineDashLengths
{
NSMutableArray *lengths = [NSMutableArray arrayWithCapacity:_lineDashCount];
for (size_t dashIndex=0; dashIndex<_lineDashCount; dashIndex++)
[lengths addObject:(id)[NSNumber numberWithFloat:_lineDashLengths[dashIndex]]];
return lengths;
}
- (void)setLineDashLengths:(NSArray *)lengths
{
if (_lineDashLengths)
{
free(_lineDashLengths);
_lineDashLengths = NULL;
}
if (_scaledLineDashLengths)
{
free(_scaledLineDashLengths);
_scaledLineDashLengths = NULL;
}
_lineDashCount = [lengths count];
if (!_lineDashCount)
return;
_lineDashLengths = calloc(_lineDashCount, sizeof(CGFloat));
if (!scaleLineDash)
_scaledLineDashLengths = calloc(_lineDashCount, sizeof(CGFloat));
NSEnumerator *lengthEnumerator = [lengths objectEnumerator];
id lenObj;
size_t dashIndex = 0;
while ((lenObj = [lengthEnumerator nextObject]))
{
if ([lenObj isKindOfClass:[NSNumber class]])
{
_lineDashLengths[dashIndex] = [lenObj floatValue];
}
else
{
_lineDashLengths[dashIndex] = 0.0;
dashIndex++;
}
}
}
- (void)setPosition:(CGPoint)newPosition animated:(BOOL)animated
{
if (CGPointEqualToPoint(newPosition, super.position) && CGRectEqualToRect(self.bounds, previousBounds)) return;
[self recalculateGeometry];
}
@end
... ... @@ -78,8 +78,6 @@
B8C974620E8A19E8007D16AD /* RMTileSource.h in Headers */ = {isa = PBXBuildFile; fileRef = B83E64EC0E80E73F001663B6 /* RMTileSource.h */; };
B8C9746B0E8A1A50007D16AD /* RMMapView.h in Headers */ = {isa = PBXBuildFile; fileRef = B8C974690E8A1A50007D16AD /* RMMapView.h */; };
B8C9746C0E8A1A50007D16AD /* RMMapView.m in Sources */ = {isa = PBXBuildFile; fileRef = B8C9746A0E8A1A50007D16AD /* RMMapView.m */; };
B8CEB1C50ED5A3480014C431 /* RMPath.h in Headers */ = {isa = PBXBuildFile; fileRef = B8CEB1C30ED5A3480014C431 /* RMPath.h */; };
B8CEB1C60ED5A3480014C431 /* RMPath.m in Sources */ = {isa = PBXBuildFile; fileRef = B8CEB1C40ED5A3480014C431 /* RMPath.m */; };
B8F3FC610EA2B382004D8F85 /* RMMapLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = B8F3FC600EA2B382004D8F85 /* RMMapLayer.m */; };
B8F3FC640EA2E792004D8F85 /* RMMarker.h in Headers */ = {isa = PBXBuildFile; fileRef = B8F3FC620EA2E792004D8F85 /* RMMarker.h */; };
B8F3FC650EA2E792004D8F85 /* RMMarker.m in Sources */ = {isa = PBXBuildFile; fileRef = B8F3FC630EA2E792004D8F85 /* RMMarker.m */; };
... ... @@ -224,8 +222,6 @@
B86F26AC0E87442C007A3773 /* RMMapLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMMapLayer.h; sourceTree = "<group>"; };
B8C974690E8A1A50007D16AD /* RMMapView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMMapView.h; sourceTree = "<group>"; };
B8C9746A0E8A1A50007D16AD /* RMMapView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMMapView.m; sourceTree = "<group>"; };
B8CEB1C30ED5A3480014C431 /* RMPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMPath.h; sourceTree = "<group>"; };
B8CEB1C40ED5A3480014C431 /* RMPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMPath.m; sourceTree = "<group>"; };
B8F3FC600EA2B382004D8F85 /* RMMapLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMMapLayer.m; sourceTree = "<group>"; };
B8F3FC620EA2E792004D8F85 /* RMMarker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMMarker.h; sourceTree = "<group>"; };
B8F3FC630EA2E792004D8F85 /* RMMarker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMMarker.m; sourceTree = "<group>"; };
... ... @@ -468,8 +464,6 @@
B8F3FC600EA2B382004D8F85 /* RMMapLayer.m */,
B8F3FC620EA2E792004D8F85 /* RMMarker.h */,
B8F3FC630EA2E792004D8F85 /* RMMarker.m */,
B8CEB1C30ED5A3480014C431 /* RMPath.h */,
B8CEB1C40ED5A3480014C431 /* RMPath.m */,
16F98C941590CFF000FF90CE /* RMShape.h */,
16F98C951590CFF000FF90CE /* RMShape.m */,
25757F4D1291C8640083D504 /* RMCircle.h */,
... ... @@ -611,7 +605,6 @@
23A0AAEB0EB90AA6003A4521 /* RMFoundation.h in Headers */,
B8800FF20EC3A239003E9CDD /* RMMapViewDelegate.h in Headers */,
B8800FF30EC3A23D003E9CDD /* RMConfiguration.h in Headers */,
B8CEB1C50ED5A3480014C431 /* RMPath.h in Headers */,
96492C400FA8AD3400EBA6D2 /* RMGlobalConstants.h in Headers */,
B1EB26C610B5D8E6009F8658 /* RMNotifications.h in Headers */,
D1437B37122869E400888DAE /* RMDBMapSource.h in Headers */,
... ... @@ -764,7 +757,6 @@
B8474BA50EB40094006A0BC1 /* RMDatabaseCache.m in Sources */,
126693040EB76C0B00E002D5 /* RMConfiguration.m in Sources */,
23A0AAE90EB90A99003A4521 /* RMFoundation.c in Sources */,
B8CEB1C60ED5A3480014C431 /* RMPath.m in Sources */,
D1437B36122869E400888DAE /* RMDBMapSource.m in Sources */,
25757F501291C8640083D504 /* RMCircle.m in Sources */,
16EC85D3133CA6C300219947 /* RMAbstractMercatorTileSource.m in Sources */,
... ...