Files
divkit/client/ios/DivKit/Extensions/GradientExtentions.swift
T
burstein fc3dd8d0ed Apply vgsl angle gradient
commit_hash:985ba800434a47d4b37eb50c6d8e9fbc293e964b
2025-06-18 12:34:39 +03:00

192 lines
5.6 KiB
Swift

import CoreGraphics
import Foundation
import VGSL
extension Gradient.Linear {
init?(points: [Gradient.Point], angle: Int) {
guard points.count >= 2 else { return nil }
let sortedPoints = points.stableSort(isLessOrEqual: { $0.location <= $1.location })
guard let start = sortedPoints.first, let end = sortedPoints.last
else { return nil }
self.init(
startColor: start.color,
intermediatePoints: sortedPoints,
endColor: end.color,
direction: .angle(Double(angle))
)
}
init?(colors: [Color], angle: Int) {
guard colors.count >= 2 else {
return nil
}
var intermediateColors = colors
let start = intermediateColors.removeFirst()
let end = intermediateColors.removeLast()
self.init(
startColor: start,
intermediateColors: intermediateColors,
endColor: end,
direction: .angle(Double(angle))
)
}
}
extension Gradient.Radial {
init?(
points: [Gradient.Point],
end: Gradient.Radial.Radius,
centerX: Gradient.Radial.CenterPoint,
centerY: Gradient.Radial.CenterPoint
) {
guard points.count >= 2 else { return nil }
let sortedPoints = points.stableSort(isLessOrEqual: { $0.location <= $1.location })
guard let first = sortedPoints.first, let last = sortedPoints.last
else { return nil }
self.init(
centerX: centerX,
centerY: centerY,
end: end,
centerColor: first.color,
intermediatePoints: sortedPoints,
outerColor: last.color
)
}
init?(
colors: [Color],
end: Gradient.Radial.Radius,
centerX: Gradient.Radial.CenterPoint,
centerY: Gradient.Radial.CenterPoint
) {
guard colors.count >= 2 else {
return nil
}
var intermediateColors = colors
let centerColor = intermediateColors.removeFirst()
let outerColor = intermediateColors.removeLast()
let step = 1 / CGFloat(intermediateColors.count + 1)
let points = intermediateColors.enumerated().map {
(color: $0.element, location: CGFloat($0.offset + 1) * step)
}
self.init(
centerX: centerX,
centerY: centerY,
end: end,
centerColor: centerColor,
intermediatePoints: points,
outerColor: outerColor
)
}
}
extension DivLinearGradient {
func makeBlockLinearGradient(
_ context: DivBlockModelingContext
) -> Gradient.Linear? {
let expressionResolver = context.expressionResolver
if let colorMap {
let points: [Gradient.Point] = colorMap.compactMap { positionedColor -> Gradient.Point? in
if let color = positionedColor.resolveColor(expressionResolver),
let position = positionedColor.resolvePosition(expressionResolver) {
return (color: color, location: CGFloat(position))
}
return nil
}
return Gradient.Linear(
points: points,
angle: resolveAngle(expressionResolver)
)
} else if let colors = resolveColors(expressionResolver) {
return Gradient.Linear(
colors: colors,
angle: resolveAngle(expressionResolver)
)
} else {
context.addError(
message: "No colors specified in the linear gradient in the `colors` or `color_map` fields"
)
return nil
}
}
}
extension DivRadialGradient {
func makeBlockRadialGradient(
_ context: DivBlockModelingContext
) -> Gradient.Radial? {
let expressionResolver = context.expressionResolver
if let colorMap {
let points: [Gradient.Point] = colorMap.compactMap { positionedColor -> Gradient.Point? in
if let color = positionedColor.resolveColor(expressionResolver),
let position = positionedColor.resolvePosition(expressionResolver) {
return (color: color, location: CGFloat(position))
}
return nil
}
return Gradient.Radial(
points: points,
end: resolveRadius(expressionResolver),
centerX: resolveCenterX(expressionResolver),
centerY: resolveCenterY(expressionResolver)
)
} else if let colors = resolveColors(expressionResolver) {
return Gradient.Radial(
colors: colors,
end: resolveRadius(expressionResolver),
centerX: resolveCenterX(expressionResolver),
centerY: resolveCenterY(expressionResolver)
)
} else {
context.addError(
message: "No colors specified in the radial gradient in the `colors` or `color_map` fields"
)
return nil
}
}
func resolveRadius(_ resolver: ExpressionResolver) -> Gradient.Radial.Radius {
switch radius {
case let .divFixedSize(size):
.absolute(size.resolveValue(resolver) ?? 0)
case let .divRadialGradientRelativeRadius(radius):
switch radius.resolveValue(resolver) ?? .farthestCorner {
case .farthestCorner:
.relativeToBorders(.farthestCorner)
case .farthestSide:
.relativeToBorders(.farthestSide)
case .nearestCorner:
.relativeToBorders(.nearestCorner)
case .nearestSide:
.relativeToBorders(.nearestSide)
}
}
}
func resolveCenterX(_ resolver: ExpressionResolver) -> Gradient.Radial.CenterPoint {
switch centerX {
case let .divRadialGradientRelativeCenter(x):
.relative(x.resolveValue(resolver) ?? 0)
case let .divRadialGradientFixedCenter(x):
.absolute(x.resolveValue(resolver) ?? 0)
}
}
func resolveCenterY(_ resolver: ExpressionResolver) -> Gradient.Radial.CenterPoint {
switch centerY {
case let .divRadialGradientRelativeCenter(y):
.relative(y.resolveValue(resolver) ?? 0)
case let .divRadialGradientFixedCenter(y):
.absolute(y.resolveValue(resolver) ?? 0)
}
}
}