Authored by Hal Mueller

incorporated Will MacKay's RMPath enhancement patch (08 Jan version). Closes Issue 29

@@ -63,6 +63,7 @@ @@ -63,6 +63,7 @@
63 63
64 float renderedScale; 64 float renderedScale;
65 RMMapContents *contents; 65 RMMapContents *contents;
  66 + CGRect boundsInMercators;
66 } 67 }
67 68
68 - (id) initWithContents: (RMMapContents*)aContents; 69 - (id) initWithContents: (RMMapContents*)aContents;
@@ -71,10 +72,11 @@ @@ -71,10 +72,11 @@
71 @property CGPathDrawingMode drawingMode; 72 @property CGPathDrawingMode drawingMode;
72 // This is the position on the map of the first point. 73 // This is the position on the map of the first point.
73 74
  75 +@property (nonatomic, assign) BOOL scaleLineWidth;
74 @property float lineWidth; 76 @property float lineWidth;
75 @property (nonatomic, assign) RMXYPoint origin; 77 @property (nonatomic, assign) RMXYPoint origin;
76 -@property (readwrite, assign) UIColor *lineColor;  
77 -@property (readwrite, assign) UIColor *fillColor; 78 +@property (nonatomic, assign) UIColor *lineColor;
  79 +@property (nonatomic, assign) UIColor *fillColor;
78 80
79 - (void) addLineToXY: (RMXYPoint) point; 81 - (void) addLineToXY: (RMXYPoint) point;
80 - (void) addLineToScreenPoint: (CGPoint) point; 82 - (void) addLineToScreenPoint: (CGPoint) point;
@@ -42,8 +42,6 @@ @@ -42,8 +42,6 @@
42 return nil; 42 return nil;
43 43
44 contents = aContents; 44 contents = aContents;
45 -  
46 - path = CGPathCreateMutable();  
47 45
48 lineWidth = 100.0f; 46 lineWidth = 100.0f;
49 drawingMode = kCGPathFillStroke; 47 drawingMode = kCGPathFillStroke;
@@ -52,6 +50,7 @@ @@ -52,6 +50,7 @@
52 self.masksToBounds = NO; 50 self.masksToBounds = NO;
53 51
54 scaleLineWidth = YES; 52 scaleLineWidth = YES;
  53 + boundsInMercators = CGRectZero;
55 // self.frame = CGRectMake(100, 100, 100, 100); 54 // self.frame = CGRectMake(100, 100, 100, 100);
56 // [self setNeedsDisplayOnBoundsChange:YES]; 55 // [self setNeedsDisplayOnBoundsChange:YES];
57 56
@@ -68,6 +67,8 @@ @@ -68,6 +67,8 @@
68 CGPathRelease(path); 67 CGPathRelease(path);
69 [self setLineColor:nil]; 68 [self setLineColor:nil];
70 [self setFillColor:nil]; 69 [self setFillColor:nil];
  70 + [points release];
  71 + points = nil;
71 72
72 [super dealloc]; 73 [super dealloc];
73 } 74 }
@@ -80,26 +81,53 @@ @@ -80,26 +81,53 @@
80 - (void) recalculateGeometry 81 - (void) recalculateGeometry
81 { 82 {
82 float scale = [[contents mercatorToScreenProjection] scale]; 83 float scale = [[contents mercatorToScreenProjection] scale];
83 - // The bounds are actually in mercators...  
84 - CGRect boundsInMercators = CGPathGetBoundingBox(path);  
85 - boundsInMercators.origin.x -= lineWidth;  
86 - boundsInMercators.origin.y -= lineWidth;  
87 - boundsInMercators.size.width += 2*lineWidth;  
88 - boundsInMercators.size.height += 2*lineWidth;  
89 -  
90 - CGRect pixelBounds = RMScaleCGRectAboutPoint(boundsInMercators, 1.0f / scale, CGPointMake(0,0)); 84 + float scaledLineWidth;
  85 + CGPoint myPosition;
  86 + CGRect pixelBounds, screenBounds;
  87 + float offset;
  88 + const float outset = 100.0f; // provides a buffer off screen edges for when path is scaled or moved
91 89
92 -// NSLog(@"old bounds: %f %f %f %f", self.bounds.origin.x, self.bounds.origin.y, self.bounds.size.width, self.bounds.size.height);  
93 - self.bounds = pixelBounds;  
94 -// NSLog(@"new bounds: %f %f %f %f", self.bounds.origin.x, self.bounds.origin.y, self.bounds.size.width, self.bounds.size.height);  
95 -  
96 -// NSLog(@"old position: %f %f", self.position.x, self.position.y);  
97 - self.position = [[contents mercatorToScreenProjection] projectXYPoint: origin];  
98 -// NSLog(@"new position: %f %f", self.position.x, self.position.y); 90 + scaledLineWidth = lineWidth;
  91 + if(!scaleLineWidth) {
  92 + renderedScale = [contents scale];
  93 + scaledLineWidth *= renderedScale;
  94 + }
  95 + pixelBounds = CGRectInset(boundsInMercators, -scaledLineWidth, -scaledLineWidth);
  96 +
  97 + pixelBounds = RMScaleCGRectAboutPoint(pixelBounds, 1.0f / scale, CGPointZero);
99 98
100 -// NSLog(@"Old anchor point %f %f", self.anchorPoint.x, self.anchorPoint.y); 99 + // Clip bound rect to screen bounds.
  100 + // If bounds are not clipped, they won't display when you zoom in too much.
  101 + myPosition = [[contents mercatorToScreenProjection] projectXYPoint: origin];
  102 + screenBounds = [contents screenBounds];
  103 +
  104 + // Clip top
  105 + offset = myPosition.y + pixelBounds.origin.y - screenBounds.origin.y + outset;
  106 + if(offset < 0.0f) {
  107 + pixelBounds.origin.y -= offset;
  108 + pixelBounds.size.height += offset;
  109 + }
  110 + // Clip left
  111 + offset = myPosition.x + pixelBounds.origin.x - screenBounds.origin.x + outset;
  112 + if(offset < 0.0f) {
  113 + pixelBounds.origin.x -= offset;
  114 + pixelBounds.size.width += offset;
  115 + }
  116 + // Clip bottom
  117 + offset = myPosition.y + pixelBounds.origin.y + pixelBounds.size.height - screenBounds.origin.y - screenBounds.size.height - outset;
  118 + if(offset > 0.0f) {
  119 + pixelBounds.size.height -= offset;
  120 + }
  121 + // Clip right
  122 + offset = myPosition.x + pixelBounds.origin.x + pixelBounds.size.width - screenBounds.origin.x - screenBounds.size.width - outset;
  123 + if(offset > 0.0f) {
  124 + pixelBounds.size.width -= offset;
  125 + }
  126 +
  127 + self.position = myPosition;
  128 + self.bounds = pixelBounds;
101 self.anchorPoint = CGPointMake(-pixelBounds.origin.x / pixelBounds.size.width,-pixelBounds.origin.y / pixelBounds.size.height); 129 self.anchorPoint = CGPointMake(-pixelBounds.origin.x / pixelBounds.size.width,-pixelBounds.origin.y / pixelBounds.size.height);
102 -// NSLog(@"new anchor point %f %f", self.anchorPoint.x, self.anchorPoint.y); 130 + [self setNeedsDisplay];
103 } 131 }
104 132
105 - (void) addLineToXY: (RMXYPoint) point 133 - (void) addLineToXY: (RMXYPoint) point
@@ -110,12 +138,12 @@ @@ -110,12 +138,12 @@
110 138
111 if (points == nil) 139 if (points == nil)
112 { 140 {
113 - points = [[NSMutableArray alloc] init];  
114 - [points addObject:value]; 141 + points = [[NSMutableArray alloc] initWithObjects:value, nil];
115 origin = point; 142 origin = point;
116 143
117 self.position = [[contents mercatorToScreenProjection] projectXYPoint: origin]; 144 self.position = [[contents mercatorToScreenProjection] projectXYPoint: origin];
118 // NSLog(@"screen position set to %f %f", self.position.x, self.position.y); 145 // NSLog(@"screen position set to %f %f", self.position.x, self.position.y);
  146 + path = CGPathCreateMutable();
119 CGPathMoveToPoint(path, NULL, 0.0f, 0.0f); 147 CGPathMoveToPoint(path, NULL, 0.0f, 0.0f);
120 } 148 }
121 else 149 else
@@ -127,9 +155,11 @@ @@ -127,9 +155,11 @@
127 155
128 CGPathAddLineToPoint(path, NULL, point.x, -point.y); 156 CGPathAddLineToPoint(path, NULL, point.x, -point.y);
129 157
  158 + // The bounds are actually in mercators...
  159 + boundsInMercators = CGPathGetBoundingBox(path);
  160 +
130 [self recalculateGeometry]; 161 [self recalculateGeometry];
131 } 162 }
132 - [self setNeedsDisplay];  
133 } 163 }
134 164
135 - (void) addLineToScreenPoint: (CGPoint) point 165 - (void) addLineToScreenPoint: (CGPoint) point
@@ -148,18 +178,24 @@ @@ -148,18 +178,24 @@
148 178
149 - (void)drawInContext:(CGContextRef)theContext 179 - (void)drawInContext:(CGContextRef)theContext
150 { 180 {
151 - renderedScale = [contents scale]; 181 + float scale, scaledLineWidth;
152 182
153 // CGContextFillRect(theContext, self.bounds);//CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)); 183 // CGContextFillRect(theContext, self.bounds);//CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height));
154 184
155 - float scale = 1.0f / [contents scale];  
156 - 185 + renderedScale = [contents scale];
  186 + scale = 1.0f / renderedScale;
  187 +
  188 + scaledLineWidth = lineWidth;
  189 + if(!scaleLineWidth) {
  190 + scaledLineWidth *= renderedScale;
  191 + }
  192 +
157 CGContextScaleCTM(theContext, scale, scale); 193 CGContextScaleCTM(theContext, scale, scale);
158 194
159 CGContextBeginPath(theContext); 195 CGContextBeginPath(theContext);
160 CGContextAddPath(theContext, path); 196 CGContextAddPath(theContext, path);
161 197
162 - CGContextSetLineWidth(theContext, lineWidth); 198 + CGContextSetLineWidth(theContext, scaledLineWidth);
163 CGContextSetStrokeColorWithColor(theContext, [lineColor CGColor]); 199 CGContextSetStrokeColorWithColor(theContext, [lineColor CGColor]);
164 CGContextSetFillColorWithColor(theContext, [fillColor CGColor]); 200 CGContextSetFillColorWithColor(theContext, [fillColor CGColor]);
165 CGContextDrawPath(theContext, drawingMode); 201 CGContextDrawPath(theContext, drawingMode);
@@ -180,7 +216,6 @@ @@ -180,7 +216,6 @@
180 { 216 {
181 lineWidth = newLineWidth; 217 lineWidth = newLineWidth;
182 [self recalculateGeometry]; 218 [self recalculateGeometry];
183 - [self setNeedsDisplay];  
184 } 219 }
185 220
186 - (CGPathDrawingMode) drawingMode 221 - (CGPathDrawingMode) drawingMode
@@ -220,16 +255,28 @@ @@ -220,16 +255,28 @@
220 } 255 }
221 } 256 }
222 257
  258 +- (BOOL)scaleLineWidth
  259 +{
  260 + return scaleLineWidth;
  261 +}
  262 +
  263 +- (void)setScaleLineWidth:(BOOL)newState
  264 +{
  265 + scaleLineWidth = newState;
  266 + [self recalculateGeometry];
  267 +}
  268 +
  269 +- (void)moveBy: (CGSize) delta {
  270 + [super moveBy:delta];
  271 +
  272 + [self recalculateGeometry];
  273 +}
  274 +
223 - (void)zoomByFactor: (float) zoomFactor near:(CGPoint) pivot 275 - (void)zoomByFactor: (float) zoomFactor near:(CGPoint) pivot
224 { 276 {
225 [super zoomByFactor:zoomFactor near:pivot]; 277 [super zoomByFactor:zoomFactor near:pivot];
226 -  
227 - float newScale = [contents scale];  
228 - if (newScale / renderedScale >= 2.0f  
229 - || newScale / renderedScale <= 0.4f)  
230 - {  
231 - [self setNeedsDisplay];  
232 - } 278 +
  279 + [self recalculateGeometry];
233 } 280 }
234 281
235 @end 282 @end