164 lines
5.8 KiB
Swift
164 lines
5.8 KiB
Swift
//
|
|
// Copyright (c) 2023 Touch Instinct
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the Software), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
//
|
|
|
|
import CoreGraphics
|
|
|
|
public struct CurrentLocationDrawingOperation: DrawingOperation {
|
|
public enum Defaults {
|
|
public static var iconSize: CGSize {
|
|
CGSize(width: 27, height: 27)
|
|
}
|
|
|
|
public static var borderWidth: CGFloat {
|
|
2
|
|
}
|
|
}
|
|
|
|
public var iconSize: CGSize
|
|
public var borderWidth: CGFloat
|
|
public var mainColor: CGColor
|
|
public var borderColor: CGColor
|
|
public var backgroundColor: CGColor
|
|
public var showHeadingArrow: Bool
|
|
|
|
private var innerCircleSize: CGSize {
|
|
CGSize(width: innerBorderSize.width - borderWidth,
|
|
height: innerBorderSize.height - borderWidth)
|
|
}
|
|
|
|
private var innerBorderSize: CGSize {
|
|
CGSize(width: outerBorderSize.width - borderWidth,
|
|
height: outerBorderSize.height - borderWidth)
|
|
}
|
|
|
|
private var outerBorderSize: CGSize {
|
|
CGSize(width: iconSize.width - 8,
|
|
height: iconSize.height - 8)
|
|
}
|
|
|
|
private var innerCircleOrigin: CGPoint {
|
|
CGPoint(x: (iconSize.width - innerCircleSize.width) / 2,
|
|
y: (iconSize.height - innerCircleSize.height) / 2)
|
|
}
|
|
|
|
private var innerBorderOrigin: CGPoint {
|
|
CGPoint(x: (iconSize.width - innerBorderSize.width) / 2,
|
|
y: (iconSize.height - innerBorderSize.height) / 2)
|
|
}
|
|
|
|
private var outerBorderOrigin: CGPoint {
|
|
CGPoint(x: (iconSize.width - outerBorderSize.width) / 2,
|
|
y: (iconSize.height - outerBorderSize.height) / 2)
|
|
}
|
|
|
|
private var iconRect: CGRect {
|
|
CGRect(origin: .zero,
|
|
size: iconSize)
|
|
}
|
|
|
|
private var innerCircleRect: CGRect {
|
|
CGRect(origin: innerCircleOrigin,
|
|
size: innerCircleSize)
|
|
}
|
|
|
|
private var innerBorderRect: CGRect {
|
|
CGRect(origin: innerBorderOrigin,
|
|
size: innerBorderSize)
|
|
}
|
|
|
|
private var outerBorderRect: CGRect {
|
|
CGRect(origin: outerBorderOrigin,
|
|
size: outerBorderSize)
|
|
}
|
|
|
|
public init(iconSize: CGSize = Defaults.iconSize,
|
|
borderWidth: CGFloat = Defaults.borderWidth,
|
|
mainColor: CGColor,
|
|
borderColor: CGColor,
|
|
showHeadingArrow: Bool = true) {
|
|
|
|
self.iconSize = iconSize
|
|
self.borderWidth = borderWidth
|
|
self.mainColor = mainColor
|
|
self.borderColor = borderColor
|
|
self.backgroundColor = mainColor.copy(alpha: 0.3) ?? mainColor
|
|
self.showHeadingArrow = showHeadingArrow
|
|
}
|
|
|
|
public func affectedArea(in context: CGContext? = nil) -> CGRect {
|
|
iconRect
|
|
}
|
|
|
|
public func apply(in context: CGContext) {
|
|
let backgroundDrawing = SolidFillDrawingOperation(color: backgroundColor,
|
|
ellipseRect: iconRect)
|
|
|
|
let innerCircleDrawing = SolidFillDrawingOperation(color: mainColor,
|
|
ellipseRect: innerCircleRect)
|
|
|
|
let innerBorderDrawing = BorderDrawingOperation(frameableContentRect: innerBorderRect,
|
|
border: borderWidth,
|
|
color: borderColor,
|
|
radius: min(innerBorderSize.width, innerBorderSize.height) / 2,
|
|
exteriorBorder: false)
|
|
|
|
let outerBorderDrawing = BorderDrawingOperation(frameableContentRect: outerBorderRect,
|
|
border: borderWidth,
|
|
color: backgroundColor,
|
|
radius: min(outerBorderSize.width, outerBorderSize.height) / 2,
|
|
exteriorBorder: false)
|
|
|
|
// flip horizontally
|
|
|
|
context.translateBy(x: 0, y: iconSize.height)
|
|
context.scaleBy(x: 1, y: -1)
|
|
|
|
backgroundDrawing.apply(in: context)
|
|
|
|
context.setFillColor(mainColor)
|
|
context.setLineWidth(.zero)
|
|
|
|
guard showHeadingArrow else {
|
|
innerCircleDrawing.apply(in: context)
|
|
|
|
innerBorderDrawing.apply(in: context)
|
|
|
|
return
|
|
}
|
|
|
|
context.addLines(between: [
|
|
CGPoint(x: innerBorderRect.minX, y: innerBorderRect.midY),
|
|
CGPoint(x: iconRect.midX, y: iconRect.maxY),
|
|
CGPoint(x: innerBorderRect.maxX, y: innerBorderRect.midY)
|
|
])
|
|
|
|
context.drawPath(using: .fillStroke)
|
|
|
|
innerCircleDrawing.apply(in: context)
|
|
|
|
innerBorderDrawing.apply(in: context)
|
|
|
|
context.setBlendMode(.destinationAtop)
|
|
outerBorderDrawing.apply(in: context)
|
|
}
|
|
}
|