@lugg/maps 0.2.0-alpha.3 → 0.2.0-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -311,7 +311,6 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
311
311
  .position(position)
312
312
  .title(markerView.title)
313
313
  .snippet(markerView.description)
314
- .collisionBehavior(CollisionBehavior.REQUIRED)
315
314
 
316
315
  Log.d(TAG, "adding marker: ${markerView.name} customview: ${markerView.hasCustomView}")
317
316
  if (markerView.hasCustomView) {
@@ -2,18 +2,24 @@ package com.luggmaps.core
2
2
 
3
3
  import android.animation.ValueAnimator
4
4
  import android.graphics.Color
5
+ import android.location.Location
5
6
  import android.view.animation.LinearInterpolator
6
7
  import com.google.android.gms.maps.model.LatLng
7
8
  import com.google.android.gms.maps.model.Polyline
8
9
  import com.google.android.gms.maps.model.StrokeStyle
9
10
  import com.google.android.gms.maps.model.StyleSpan
10
11
  import kotlin.math.floor
11
- import kotlin.math.max
12
12
  import kotlin.math.min
13
13
 
14
14
  class PolylineAnimator {
15
15
  var polyline: Polyline? = null
16
16
  var coordinates: List<LatLng> = emptyList()
17
+ set(value) {
18
+ field = value
19
+ if (animated && animator != null) {
20
+ computeCumulativeDistances()
21
+ }
22
+ }
17
23
  var strokeColors: List<Int> = listOf(Color.BLACK)
18
24
  var strokeWidth: Float = 1f
19
25
 
@@ -31,6 +37,8 @@ class PolylineAnimator {
31
37
 
32
38
  private var animator: ValueAnimator? = null
33
39
  private var animationProgress: Float = 0f
40
+ private var cumulativeDistances: FloatArray = floatArrayOf()
41
+ private var totalLength: Float = 0f
34
42
 
35
43
  fun update() {
36
44
  if (animated) return
@@ -50,8 +58,10 @@ class PolylineAnimator {
50
58
  private fun startAnimation() {
51
59
  if (animator != null) return
52
60
 
61
+ computeCumulativeDistances()
62
+
53
63
  animator = ValueAnimator.ofFloat(0f, 2.15f).apply {
54
- duration = 3650 // ~1.75s per phase * 2 + pause
64
+ duration = 2150
55
65
  repeatCount = ValueAnimator.INFINITE
56
66
  interpolator = LinearInterpolator()
57
67
  addUpdateListener { animation ->
@@ -62,6 +72,58 @@ class PolylineAnimator {
62
72
  }
63
73
  }
64
74
 
75
+ private fun computeCumulativeDistances() {
76
+ if (coordinates.size < 2) {
77
+ cumulativeDistances = floatArrayOf(0f)
78
+ totalLength = 0f
79
+ return
80
+ }
81
+
82
+ val distances = FloatArray(coordinates.size)
83
+ distances[0] = 0f
84
+ var total = 0f
85
+
86
+ for (i in 1 until coordinates.size) {
87
+ val prev = coordinates[i - 1]
88
+ val curr = coordinates[i]
89
+ val results = FloatArray(1)
90
+ Location.distanceBetween(prev.latitude, prev.longitude, curr.latitude, curr.longitude, results)
91
+ total += results[0]
92
+ distances[i] = total
93
+ }
94
+
95
+ cumulativeDistances = distances
96
+ totalLength = total
97
+ }
98
+
99
+ private fun indexForDistance(distance: Float): Int {
100
+ for (i in 1 until cumulativeDistances.size) {
101
+ if (cumulativeDistances[i] >= distance) {
102
+ return i - 1
103
+ }
104
+ }
105
+ return (cumulativeDistances.size - 2).coerceAtLeast(0)
106
+ }
107
+
108
+ private fun coordinateAtDistance(distance: Float): LatLng {
109
+ if (distance <= 0f) return coordinates.first()
110
+ if (distance >= totalLength) return coordinates.last()
111
+
112
+ val idx = indexForDistance(distance)
113
+ val segStart = cumulativeDistances[idx]
114
+ val segEnd = cumulativeDistances[idx + 1]
115
+ val segLength = segEnd - segStart
116
+
117
+ val t = if (segLength > 0) (distance - segStart) / segLength else 0f
118
+ val c1 = coordinates[idx]
119
+ val c2 = coordinates[idx + 1]
120
+
121
+ return LatLng(
122
+ c1.latitude + (c2.latitude - c1.latitude) * t,
123
+ c1.longitude + (c2.longitude - c1.longitude) * t
124
+ )
125
+ }
126
+
65
127
  private fun stopAnimation() {
66
128
  animator?.cancel()
67
129
  animator = null
@@ -69,71 +131,55 @@ class PolylineAnimator {
69
131
 
70
132
  private fun updateAnimatedPolyline() {
71
133
  val poly = polyline ?: return
72
- if (coordinates.size < 2) {
134
+ if (coordinates.size < 2 || totalLength <= 0f) {
73
135
  poly.points = coordinates
74
136
  return
75
137
  }
76
138
 
77
- val segmentCount = coordinates.size - 1
78
139
  val progress = min(animationProgress, 2f)
79
140
 
80
- val headPos: Float
81
- val tailPos: Float
141
+ val headDist: Float
142
+ val tailDist: Float
82
143
 
83
144
  if (progress <= 1f) {
84
- tailPos = 0f
85
- headPos = progress * segmentCount
145
+ tailDist = 0f
146
+ headDist = progress * totalLength
86
147
  } else {
87
148
  val shrinkProgress = progress - 1f
88
- tailPos = shrinkProgress * segmentCount
89
- headPos = segmentCount.toFloat()
149
+ tailDist = shrinkProgress * totalLength
150
+ headDist = totalLength
90
151
  }
91
152
 
92
- if (headPos <= tailPos || coordinates.isEmpty()) {
153
+ if (headDist <= tailDist) {
93
154
  poly.setSpans(emptyList())
94
155
  poly.points = listOf(coordinates.firstOrNull() ?: LatLng(0.0, 0.0))
95
156
  return
96
157
  }
97
158
 
98
- val startIndex = floor(tailPos).toInt()
99
- val endIndex = kotlin.math.ceil(headPos.toDouble()).toInt()
100
- val visibleLength = headPos - tailPos
159
+ val visibleLength = headDist - tailDist
160
+ val startIndex = indexForDistance(tailDist)
161
+ val endIndex = indexForDistance(headDist)
101
162
 
102
163
  val points = mutableListOf<LatLng>()
103
164
  val spans = mutableListOf<StyleSpan>()
104
165
 
105
- for (i in startIndex..minOf(endIndex, coordinates.size - 1)) {
106
- var coord = coordinates[i]
107
-
108
- // Interpolate tail
109
- if (i == startIndex && tailPos > startIndex.toFloat() && i + 1 < coordinates.size) {
110
- val t = tailPos - startIndex
111
- val next = coordinates[i + 1]
112
- coord = LatLng(
113
- coord.latitude + (next.latitude - coord.latitude) * t,
114
- coord.longitude + (next.longitude - coord.longitude) * t
115
- )
116
- }
166
+ points.add(coordinateAtDistance(tailDist))
117
167
 
118
- // Interpolate head
119
- if (i == endIndex && headPos < endIndex.toFloat() && i > 0) {
120
- val t = headPos - (endIndex - 1)
121
- val prev = coordinates[i - 1]
122
- coord = LatLng(
123
- prev.latitude + (coordinates[i].latitude - prev.latitude) * t,
124
- prev.longitude + (coordinates[i].longitude - prev.longitude) * t
125
- )
126
- }
168
+ for (i in (startIndex + 1)..endIndex) {
169
+ points.add(coordinates[i])
170
+ }
127
171
 
128
- points.add(coord)
172
+ val endCoord = coordinateAtDistance(headDist)
173
+ val lastAdded = points.lastOrNull()
174
+ if (lastAdded == null || endCoord.latitude != lastAdded.latitude || endCoord.longitude != lastAdded.longitude) {
175
+ points.add(endCoord)
176
+ }
129
177
 
130
- if (i < endIndex && i < segmentCount) {
131
- val segStartPos = max(i.toFloat(), tailPos)
132
- val segEndPos = min((i + 1).toFloat(), headPos)
133
- val gradientMid = ((segStartPos + segEndPos) / 2f - tailPos) / visibleLength
134
- val color = colorAtGradientPosition(gradientMid)
135
- spans.add(StyleSpan(StrokeStyle.colorBuilder(color).build()))
136
- }
178
+ for (i in 0 until (points.size - 1)) {
179
+ val segMidDist = tailDist + visibleLength * (i + 0.5f) / (points.size - 1)
180
+ val gradientPos = (segMidDist - tailDist) / visibleLength
181
+ val color = colorAtGradientPosition(gradientPos)
182
+ spans.add(StyleSpan(StrokeStyle.colorBuilder(color).build()))
137
183
  }
138
184
 
139
185
  poly.points = points
@@ -316,6 +316,9 @@ using namespace luggmaps::events;
316
316
  // Build new polyline from coordinates
317
317
  NSArray<CLLocation *> *coordinates = polylineView.coordinates;
318
318
  if (coordinates.count == 0) {
319
+ if (renderer) {
320
+ renderer.animated = NO;
321
+ }
319
322
  if (oldPolyline) {
320
323
  [_mapView removeOverlay:oldPolyline];
321
324
  polylineView.polyline = nil;
@@ -337,6 +340,8 @@ using namespace luggmaps::events;
337
340
 
338
341
  // If we have an existing renderer, update it in place
339
342
  if (renderer && oldPolyline) {
343
+ [_mapView removeOverlay:oldPolyline];
344
+ [_mapView addOverlay:newPolyline];
340
345
  [renderer updatePolyline:newPolyline];
341
346
  renderer.lineWidth = polylineView.strokeWidth;
342
347
  renderer.strokeColor = polylineView.strokeColors.firstObject;
@@ -442,7 +447,6 @@ using namespace luggmaps::events;
442
447
  reuseIdentifier:nil];
443
448
  annotationView.canShowCallout = YES;
444
449
  annotationView.displayPriority = MKFeatureDisplayPriorityRequired;
445
- annotationView.collisionMode = MKAnnotationViewCollisionModeNone;
446
450
 
447
451
  UIView *iconView = markerView.iconView;
448
452
  [iconView removeFromSuperview];
@@ -242,14 +242,14 @@ static NSString *const kDemoMapId = @"DEMO_MAP_ID";
242
242
  marker.position = markerView.coordinate;
243
243
  marker.title = markerView.title;
244
244
  marker.snippet = markerView.markerDescription;
245
- marker.groundAnchor = markerView.anchor;
246
-
247
245
  if (markerView.hasCustomView) {
248
246
  UIView *iconView = markerView.iconView;
249
247
  [iconView removeFromSuperview];
250
248
  marker.iconView = iconView;
249
+ marker.groundAnchor = markerView.anchor;
251
250
  } else {
252
251
  marker.iconView = nil;
252
+ marker.groundAnchor = CGPointMake(0.5, 1);
253
253
  }
254
254
  }
255
255
 
@@ -277,13 +277,12 @@ static NSString *const kDemoMapId = @"DEMO_MAP_ID";
277
277
  marker.position = markerView.coordinate;
278
278
  marker.title = markerView.title;
279
279
  marker.snippet = markerView.markerDescription;
280
- marker.collisionBehavior = GMSCollisionBehaviorRequired;
281
280
 
282
281
  if (markerView.hasCustomView) {
283
282
  marker.iconView = iconView;
283
+ marker.groundAnchor = markerView.anchor;
284
284
  }
285
285
 
286
- marker.groundAnchor = markerView.anchor;
287
286
  marker.map = _mapView;
288
287
 
289
288
  markerView.marker = marker;
@@ -4,12 +4,21 @@
4
4
  @implementation GMSPolylineAnimator {
5
5
  CADisplayLink *_displayLink;
6
6
  CGFloat _animationProgress;
7
+ NSArray<NSNumber *> *_cumulativeDistances;
8
+ CGFloat _totalLength;
7
9
  }
8
10
 
9
11
  - (void)dealloc {
10
12
  [self stopAnimation];
11
13
  }
12
14
 
15
+ - (void)setCoordinates:(NSArray<CLLocation *> *)coordinates {
16
+ [super setCoordinates:coordinates];
17
+ if (_animated && _displayLink) {
18
+ [self computeCumulativeDistances];
19
+ }
20
+ }
21
+
13
22
  - (void)setAnimated:(BOOL)animated {
14
23
  if (_animated == animated) {
15
24
  return;
@@ -28,18 +37,35 @@
28
37
  if (_displayLink) {
29
38
  return;
30
39
  }
40
+ [self computeCumulativeDistances];
31
41
  _animationProgress = 0;
32
42
  _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(animationTick:)];
33
43
  [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
34
44
  }
35
45
 
46
+ - (void)computeCumulativeDistances {
47
+ NSMutableArray<NSNumber *> *distances = [NSMutableArray array];
48
+ CGFloat total = 0;
49
+ [distances addObject:@(0)];
50
+
51
+ for (NSUInteger i = 1; i < self.coordinates.count; i++) {
52
+ CLLocation *prev = self.coordinates[i - 1];
53
+ CLLocation *curr = self.coordinates[i];
54
+ total += [prev distanceFromLocation:curr];
55
+ [distances addObject:@(total)];
56
+ }
57
+
58
+ _cumulativeDistances = [distances copy];
59
+ _totalLength = total;
60
+ }
61
+
36
62
  - (void)stopAnimation {
37
63
  [_displayLink invalidate];
38
64
  _displayLink = nil;
39
65
  }
40
66
 
41
67
  - (void)animationTick:(CADisplayLink *)displayLink {
42
- CGFloat speed = displayLink.duration / 1.75;
68
+ CGFloat speed = displayLink.duration / 1.0;
43
69
  _animationProgress += speed;
44
70
 
45
71
  if (_animationProgress >= 2.15) {
@@ -71,63 +97,86 @@
71
97
  }
72
98
  }
73
99
 
100
+ - (NSUInteger)indexForDistance:(CGFloat)distance {
101
+ for (NSUInteger i = 1; i < _cumulativeDistances.count; i++) {
102
+ if (_cumulativeDistances[i].doubleValue >= distance) {
103
+ return i - 1;
104
+ }
105
+ }
106
+ return _cumulativeDistances.count - 2;
107
+ }
108
+
109
+ - (CLLocationCoordinate2D)coordinateAtDistance:(CGFloat)distance {
110
+ if (distance <= 0) {
111
+ return self.coordinates.firstObject.coordinate;
112
+ }
113
+ if (distance >= _totalLength) {
114
+ return self.coordinates.lastObject.coordinate;
115
+ }
116
+
117
+ NSUInteger idx = [self indexForDistance:distance];
118
+ CGFloat segStart = _cumulativeDistances[idx].doubleValue;
119
+ CGFloat segEnd = _cumulativeDistances[idx + 1].doubleValue;
120
+ CGFloat segLength = segEnd - segStart;
121
+
122
+ CGFloat t = (segLength > 0) ? (distance - segStart) / segLength : 0;
123
+ CLLocationCoordinate2D c1 = self.coordinates[idx].coordinate;
124
+ CLLocationCoordinate2D c2 = self.coordinates[idx + 1].coordinate;
125
+
126
+ return CLLocationCoordinate2DMake(c1.latitude + (c2.latitude - c1.latitude) * t,
127
+ c1.longitude + (c2.longitude - c1.longitude) * t);
128
+ }
129
+
74
130
  - (void)updateAnimatedPolyline {
75
- if (!_polyline || self.coordinates.count < 2) {
131
+ if (!_polyline || self.coordinates.count < 2 || _totalLength <= 0) {
76
132
  return;
77
133
  }
78
134
 
79
- NSUInteger segmentCount = self.coordinates.count - 1;
80
135
  CGFloat progress = MIN(_animationProgress, 2.0);
81
- CGFloat headPos, tailPos;
136
+ CGFloat headDist, tailDist;
82
137
 
83
138
  if (progress <= 1.0) {
84
- tailPos = 0;
85
- headPos = progress * segmentCount;
139
+ tailDist = 0;
140
+ headDist = progress * _totalLength;
86
141
  } else {
87
142
  CGFloat shrinkProgress = progress - 1.0;
88
- tailPos = shrinkProgress * segmentCount;
89
- headPos = segmentCount;
143
+ tailDist = shrinkProgress * _totalLength;
144
+ headDist = _totalLength;
90
145
  }
91
146
 
92
- if (headPos <= tailPos) {
147
+ if (headDist <= tailDist) {
93
148
  _polyline.path = [GMSMutablePath path];
94
149
  return;
95
150
  }
96
151
 
97
- NSUInteger startIndex = (NSUInteger)floor(tailPos);
98
- NSUInteger endIndex = (NSUInteger)ceil(headPos);
99
- CGFloat visibleLength = headPos - tailPos;
152
+ CGFloat visibleLength = headDist - tailDist;
153
+ NSUInteger startIndex = [self indexForDistance:tailDist];
154
+ NSUInteger endIndex = [self indexForDistance:headDist];
100
155
 
101
156
  GMSMutablePath *path = [GMSMutablePath path];
102
157
  NSMutableArray<GMSStyleSpan *> *spans = [NSMutableArray array];
103
158
 
104
- for (NSUInteger i = startIndex; i <= endIndex && i < self.coordinates.count; i++) {
105
- CLLocationCoordinate2D coord = self.coordinates[i].coordinate;
106
-
107
- if (i == startIndex && tailPos > (CGFloat)startIndex) {
108
- CGFloat t = tailPos - (CGFloat)startIndex;
109
- CLLocationCoordinate2D nextCoord = self.coordinates[i + 1].coordinate;
110
- coord.latitude = coord.latitude + (nextCoord.latitude - coord.latitude) * t;
111
- coord.longitude = coord.longitude + (nextCoord.longitude - coord.longitude) * t;
112
- }
159
+ CLLocationCoordinate2D startCoord = [self coordinateAtDistance:tailDist];
160
+ [path addCoordinate:startCoord];
113
161
 
114
- if (i == endIndex && headPos < (CGFloat)endIndex && i > 0) {
115
- CGFloat t = headPos - (CGFloat)(endIndex - 1);
116
- CLLocationCoordinate2D prevCoord = self.coordinates[i - 1].coordinate;
117
- coord.latitude = prevCoord.latitude + (coord.latitude - prevCoord.latitude) * t;
118
- coord.longitude = prevCoord.longitude + (coord.longitude - prevCoord.longitude) * t;
119
- }
162
+ for (NSUInteger i = startIndex + 1; i <= endIndex; i++) {
163
+ [path addCoordinate:self.coordinates[i].coordinate];
164
+ }
120
165
 
121
- [path addCoordinate:coord];
166
+ CLLocationCoordinate2D endCoord = [self coordinateAtDistance:headDist];
167
+ CLLocationCoordinate2D lastAdded =
168
+ (endIndex < self.coordinates.count) ? self.coordinates[endIndex].coordinate : endCoord;
169
+ if (endCoord.latitude != lastAdded.latitude || endCoord.longitude != lastAdded.longitude) {
170
+ [path addCoordinate:endCoord];
171
+ }
122
172
 
123
- if (i < endIndex && i < segmentCount) {
124
- CGFloat segStartPos = MAX((CGFloat)i, tailPos);
125
- CGFloat segEndPos = MIN((CGFloat)(i + 1), headPos);
126
- CGFloat gradientMid = ((segStartPos + segEndPos) / 2.0 - tailPos) / visibleLength;
127
- UIColor *color = [self colorAtGradientPosition:gradientMid];
128
- GMSStrokeStyle *style = [GMSStrokeStyle solidColor:color];
129
- [spans addObject:[GMSStyleSpan spanWithStyle:style]];
130
- }
173
+ NSUInteger pathCount = path.count;
174
+ for (NSUInteger i = 0; i < pathCount - 1; i++) {
175
+ CGFloat segMidDist = tailDist + visibleLength * ((CGFloat)i + 0.5) / (CGFloat)(pathCount - 1);
176
+ CGFloat gradientPos = (segMidDist - tailDist) / visibleLength;
177
+ UIColor *color = [self colorAtGradientPosition:gradientPos];
178
+ GMSStrokeStyle *style = [GMSStrokeStyle solidColor:color];
179
+ [spans addObject:[GMSStyleSpan spanWithStyle:style]];
131
180
  }
132
181
 
133
182
  _polyline.path = path;
@@ -4,7 +4,9 @@
4
4
  @implementation MKPolylineAnimator {
5
5
  MKPolyline *_polyline;
6
6
  CADisplayLink *_displayLink;
7
- CGFloat _animationProgress; // 0→1 grow, 1→2 shrink
7
+ CGFloat _animationProgress;
8
+ NSArray<NSNumber *> *_cumulativeDistances;
9
+ CGFloat _totalLength;
8
10
  }
9
11
 
10
12
  - (id)initWithPolyline:(MKPolyline *)polyline {
@@ -38,23 +40,47 @@
38
40
  if (_displayLink) {
39
41
  return;
40
42
  }
43
+ [self computeCumulativeDistances];
41
44
  _animationProgress = 0;
42
45
  _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(animationTick:)];
43
46
  [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
44
47
  }
45
48
 
49
+ - (void)computeCumulativeDistances {
50
+ NSMutableArray<NSNumber *> *distances = [NSMutableArray array];
51
+ CGFloat total = 0;
52
+ [distances addObject:@(0)];
53
+
54
+ for (NSUInteger i = 1; i < _polyline.pointCount; i++) {
55
+ MKMapPoint p1 = _polyline.points[i - 1];
56
+ MKMapPoint p2 = _polyline.points[i];
57
+ total += MKMetersBetweenMapPoints(p1, p2);
58
+ [distances addObject:@(total)];
59
+ }
60
+
61
+ _cumulativeDistances = [distances copy];
62
+ _totalLength = total;
63
+ }
64
+
46
65
  - (void)stopAnimation {
47
66
  [_displayLink invalidate];
48
67
  _displayLink = nil;
49
68
  }
50
69
 
70
+ - (NSUInteger)indexForDistance:(CGFloat)distance {
71
+ for (NSUInteger i = 1; i < _cumulativeDistances.count; i++) {
72
+ if (_cumulativeDistances[i].doubleValue >= distance) {
73
+ return i - 1;
74
+ }
75
+ }
76
+ return _cumulativeDistances.count - 2;
77
+ }
78
+
51
79
  - (void)animationTick:(CADisplayLink *)displayLink {
52
- // ~1.75s per phase (grow or shrink), matching JS duration
53
- CGFloat speed = displayLink.duration / 1.75;
80
+ CGFloat speed = displayLink.duration / 1.0;
54
81
  _animationProgress += speed;
55
82
 
56
- // 0→1 grow, 1→2 shrink, then reset with small pause
57
- if (_animationProgress >= 2.15) { // 2.0 + 0.15 pause (~300ms at this speed)
83
+ if (_animationProgress >= 2.15) {
58
84
  _animationProgress = 0;
59
85
  }
60
86
 
@@ -67,6 +93,9 @@
67
93
  _polyline = polyline;
68
94
  [self invalidatePath];
69
95
  [self createPath];
96
+ if (_animated) {
97
+ [self computeCumulativeDistances];
98
+ }
70
99
  [self setNeedsDisplay];
71
100
  }
72
101
 
@@ -135,62 +164,64 @@
135
164
  }
136
165
 
137
166
  // Snake animation: grow from start, then shrink from start
138
- if (_animated && _polyline.pointCount > 1) {
167
+ if (_animated && _polyline.pointCount > 1 && _totalLength > 0) {
139
168
  CGFloat progress = MIN(_animationProgress, 2.0);
140
- CGFloat headPos, tailPos;
169
+ CGFloat headDist, tailDist;
141
170
 
142
171
  if (progress <= 1.0) {
143
- // Phase 1: grow from start to end
144
- tailPos = 0;
145
- headPos = progress * segmentCount;
172
+ tailDist = 0;
173
+ headDist = progress * _totalLength;
146
174
  } else {
147
- // Phase 2: shrink from start
148
175
  CGFloat shrinkProgress = progress - 1.0;
149
- tailPos = shrinkProgress * segmentCount;
150
- headPos = segmentCount;
176
+ tailDist = shrinkProgress * _totalLength;
177
+ headDist = _totalLength;
151
178
  }
152
179
 
153
- if (headPos <= tailPos) {
180
+ if (headDist <= tailDist) {
154
181
  return;
155
182
  }
156
183
 
157
- NSUInteger startIndex = (NSUInteger)floor(tailPos);
158
- NSUInteger endIndex = (NSUInteger)ceil(headPos);
159
- CGFloat visibleLength = headPos - tailPos;
184
+ CGFloat visibleLength = headDist - tailDist;
185
+ NSUInteger startIndex = [self indexForDistance:tailDist];
186
+ NSUInteger endIndex = [self indexForDistance:headDist];
187
+
188
+ for (NSUInteger i = startIndex; i <= endIndex && i < segmentCount; i++) {
189
+ CGFloat segStartDist = _cumulativeDistances[i].doubleValue;
190
+ CGFloat segEndDist = _cumulativeDistances[i + 1].doubleValue;
191
+
192
+ if (segEndDist <= tailDist || segStartDist >= headDist) {
193
+ continue;
194
+ }
160
195
 
161
- for (NSUInteger i = startIndex; i < endIndex && i < segmentCount; i++) {
162
196
  CGPoint segStart = [self pointForMapPoint:_polyline.points[i]];
163
197
  CGPoint segEnd = [self pointForMapPoint:_polyline.points[i + 1]];
164
198
 
165
199
  CGPoint drawStart = segStart;
166
200
  CGPoint drawEnd = segEnd;
167
- CGFloat segStartPos = (CGFloat)i;
168
- CGFloat segEndPos = (CGFloat)(i + 1);
201
+ CGFloat drawStartDist = segStartDist;
202
+ CGFloat drawEndDist = segEndDist;
203
+ CGFloat segLength = segEndDist - segStartDist;
169
204
 
170
- // Interpolate tail (partial segment at start)
171
- if (segStartPos < tailPos) {
172
- CGFloat t = tailPos - segStartPos;
205
+ if (segStartDist < tailDist && segLength > 0) {
206
+ CGFloat t = (tailDist - segStartDist) / segLength;
173
207
  drawStart.x = segStart.x + (segEnd.x - segStart.x) * t;
174
208
  drawStart.y = segStart.y + (segEnd.y - segStart.y) * t;
175
- segStartPos = tailPos;
209
+ drawStartDist = tailDist;
176
210
  }
177
211
 
178
- // Interpolate head (partial segment at end)
179
- if (segEndPos > headPos) {
180
- CGFloat t = headPos - (CGFloat)i;
212
+ if (segEndDist > headDist && segLength > 0) {
213
+ CGFloat t = (headDist - segStartDist) / segLength;
181
214
  drawEnd.x = segStart.x + (segEnd.x - segStart.x) * t;
182
215
  drawEnd.y = segStart.y + (segEnd.y - segStart.y) * t;
183
- segEndPos = headPos;
216
+ drawEndDist = headDist;
184
217
  }
185
218
 
186
- // Calculate gradient position (0-1) within visible portion
187
- CGFloat gradientStart = (segStartPos - tailPos) / visibleLength;
188
- CGFloat gradientEnd = (segEndPos - tailPos) / visibleLength;
219
+ CGFloat gradientStart = (drawStartDist - tailDist) / visibleLength;
220
+ CGFloat gradientEnd = (drawEndDist - tailDist) / visibleLength;
189
221
 
190
222
  UIColor *startColor = [self colorAtGradientPosition:gradientStart];
191
223
  UIColor *endColor = [self colorAtGradientPosition:gradientEnd];
192
224
 
193
- // Draw gradient line segment
194
225
  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
195
226
  NSArray *colors = @[(__bridge id)startColor.CGColor, (__bridge id)endColor.CGColor];
196
227
  CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, NULL);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lugg/maps",
3
- "version": "0.2.0-alpha.3",
3
+ "version": "0.2.0-alpha.5",
4
4
  "description": "Universal maps for React Native.",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",