import { cssLinearGradientLength , linearGradientStartAndEndPoints } from '@diez/framework-core' ; import { LinearGradientData , Point2DData } from '@diez/prefabs' ; import { colorToCss } from './css-color' ; returns const hypot = ( point ) => Math . sqrt ( point . x * point . x + point . y * point . y ) ; returns const normalizePoint = ( point ) => { const length = hypot ( point ) ; return { x : point . x / length , y : point . y / length , } ; } ; returns const subtractPoints = ( pointA , pointB ) => { return { x : pointA . x - pointB . x , y : pointA . y - pointB . y , } ; } ; returns const dotProduct = ( pointA , pointB ) => pointA . x * pointB . x + pointA . y * pointB . y ; returns const crossProduct = ( pointA , pointB ) => pointA . x * pointB . y - pointA . y * pointB . x ; param linePoint param lineVector param point const nearestPointOnLine = ( linePoint , lineVector , point ) => { const linePointToPoint = subtractPoints ( point , linePoint ) ; const t = dotProduct ( linePointToPoint , lineVector ) ; return { x : linePoint . x + lineVector . x * t , y : linePoint . y + lineVector . y * t , } ; } ; /**
 * Calculates the angle between the the provided points.
 *
 * ```
 * endA
 *   \
 *    \
 *     \__ result
 *      \_|________ endB
 *    start
 * ```
 */ const angleBetween = ( start , endA , endB ) => { const lineA = subtractPoints ( start , endA ) ; const lineB = subtractPoints ( start , endB ) ; const dot = dotProduct ( lineA , lineB ) ; const cross = crossProduct ( lineA , lineB ) ; return Math . atan2 ( cross , dot ) ; } ; /**
 * Determines if the provided `point` is in the direction of the vector created from the `lineStart` to `lineEnd`.
 *
 * The following would produce true:
 * ```
 *     x------------x-------------x
 * lineStart      point         lineEnd
 * ```
 * ```
 *        point
 *          x
 *     x--------------------------x
 * lineStart                   lineEnd
 * ```
 *
 * The following would produce false:
 * ```
 *    x         x--------------------------x
 *  point    lineStart                  lineEnd
 * ```
 * ```
 *        x--------------------------x
 *     lineStart                   lineEnd
 *    x
 *  point
 * ```
 */ const isPointInDirection = ( lineStart , lineEnd , point ) => { const angle = angleBetween ( lineStart , lineEnd , point ) ; return Math . abs ( angle ) < Math . PI / 2 ; } ; /**
 * Converts from an angle where 0deg is right and a positive counter-clockwise, to an angle where 0 degrees is up and
 * positive is clockwise.
 */ const convertToCSSLinearGradientAngle = ( angle ) => - angle + Math . PI / 2 ; returns const normalizedDirectionFromPoints = ( start , end ) => { const direction = subtractPoints ( end , start ) ; return normalizePoint ( direction ) ; } ; param angle param point returns const stopPositionForPoint = ( angle , point ) => { const length = cssLinearGradientLength ( angle ) ; const center = { x : 0.5 , y : 0.5 } ; const points = linearGradientStartAndEndPoints ( angle , length , center ) ; const start = convertPoint ( points . start ) ; const end = convertPoint ( points . end ) ; const direction = normalizedDirectionFromPoints ( start , end ) ; const projectedPoint = nearestPointOnLine ( center , direction , point ) ; const offsetVector = subtractPoints ( start , projectedPoint ) ; let offsetDistance = hypot ( offsetVector ) ; const isPointInFront = isPointInDirection ( start , end , projectedPoint ) ; if ( ! isPointInFront ) { offsetDistance *= - 1 ; } return offsetDistance / length ; } ; /**
 * Converts the provided point between the following coordinate spaces:
 * - (0, 0) is top left and (1, 1) is bottom right,
 * - (0, 0) is bottom left and (1, 1) is top right.
 */ export const convertPoint = ( point ) => { return { x : point . x , y : 1 - point . y , } ; } ; /**
 * Returns a string with a valid CSS <linear-gradient> value from a LinearGradient prefab instance.
 *
 * See https://drafts.csswg.org/css-images-3/#funcdef-linear-gradient
 */ export const linearGradientToCss = ( gradient ) => { if ( gradient . stops . length === 0 ) { return 'linear-gradient(none)' ; } const start = convertPoint ( gradient . start ) ; const end = convertPoint ( gradient . end ) ; const difference = subtractPoints ( end , start ) ; const angle = Math . atan2 ( difference . y , difference . x ) ; const cssGradientAngle = convertToCSSLinearGradientAngle ( angle ) ; const cssGradientLength = cssLinearGradientLength ( angle ) ; const stopPositionOffset = stopPositionForPoint ( cssGradientAngle , start ) ; const length = hypot ( difference ) ; const stops = gradient . stops . map ( ( stop ) => { const adjustedPosition = stopPositionOffset + ( stop . position * length / cssGradientLength ) ; const percentage = Math . round ( adjustedPosition * 100 ) ; return `${ colorToCss ( stop . color ) } ${ percentage }%` ; } ) ; const degrees = Math . round ( cssGradientAngle * 180 / Math . PI ) ; return `linear-gradient(${ degrees }deg, ${ stops . join ( ', ' ) })` ; } ;