Files
MapLibreParticleLab/platform/ios/src/MGLUserLocationHeadingBeamLayer.m

105 lines
3.7 KiB
Objective-C

#import "MGLUserLocationHeadingBeamLayer.h"
#import "MGLFaux3DUserLocationAnnotationView.h"
#import "MGLGeometry.h"
@implementation MGLUserLocationHeadingBeamLayer
{
CAShapeLayer *_maskLayer;
}
- (instancetype)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView
{
CGFloat size = MGLUserLocationAnnotationHaloSize;
self = [super init];
self.bounds = CGRectMake(0, 0, size, size);
self.position = CGPointMake(CGRectGetMidX(userLocationView.bounds), CGRectGetMidY(userLocationView.bounds));
self.contents = (__bridge id)[self gradientImageWithTintColor:userLocationView.tintColor.CGColor];
self.contentsGravity = kCAGravityBottom;
self.contentsScale = UIScreen.mainScreen.scale;
self.opacity = 0.4;
self.shouldRasterize = YES;
self.rasterizationScale = UIScreen.mainScreen.scale;
self.drawsAsynchronously = YES;
_maskLayer = [CAShapeLayer layer];
_maskLayer.frame = self.bounds;
_maskLayer.path = [self clippingMaskForAccuracy:0];
self.mask = _maskLayer;
return self;
}
- (void)updateHeadingAccuracy:(CLLocationDirection)accuracy
{
// recalculate the clipping mask based on updated accuracy
_maskLayer.path = [self clippingMaskForAccuracy:accuracy];
}
- (void)updateTintColor:(CGColorRef)color
{
// redraw the raw tinted gradient
self.contents = (__bridge id)[self gradientImageWithTintColor:color];
}
- (CGImageRef)gradientImageWithTintColor:(CGColorRef)tintColor
{
UIImage *image;
CGFloat haloRadius = MGLUserLocationAnnotationHaloSize / 2.0;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(MGLUserLocationAnnotationHaloSize, haloRadius), NO, 0);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = UIGraphicsGetCurrentContext();
// gradient from the tint color to no-alpha tint color
CGFloat gradientLocations[] = {0.0, 1.0};
CGGradientRef gradient = CGGradientCreateWithColors(
colorSpace,
(__bridge CFArrayRef)@[(__bridge id)tintColor,
(id)CFBridgingRelease(CGColorCreateCopyWithAlpha(tintColor, 0))],
gradientLocations);
// draw the gradient from the center point to the edge (full halo radius)
CGPoint centerPoint = CGPointMake(haloRadius, haloRadius);
CGContextDrawRadialGradient(context, gradient,
centerPoint, 0.0,
centerPoint, haloRadius,
kNilOptions);
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
return image.CGImage;
}
- (CGPathRef)clippingMaskForAccuracy:(CGFloat)accuracy
{
// size the mask using accuracy, but keep within a good display range
CGFloat clippingDegrees = 90 - accuracy;
clippingDegrees = fmin(clippingDegrees, 70); // most accurate
clippingDegrees = fmax(clippingDegrees, 10); // least accurate
CGRect ovalRect = CGRectMake(0, 0, MGLUserLocationAnnotationHaloSize, MGLUserLocationAnnotationHaloSize);
UIBezierPath *ovalPath = UIBezierPath.bezierPath;
// clip the oval to ± incoming accuracy degrees (converted to radians), from the top
[ovalPath addArcWithCenter:CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect))
radius:CGRectGetWidth(ovalRect) / 2.0
startAngle:MGLRadiansFromDegrees(-180 + clippingDegrees)
endAngle:MGLRadiansFromDegrees(-clippingDegrees)
clockwise:YES];
[ovalPath addLineToPoint:CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect))];
[ovalPath closePath];
return ovalPath.CGPath;
}
@end