import { AnyShapeStyle , RectangleShapeStyle , StrokeStyle } from "./configs" ; import { LinePosition , Position } from "./types" ; import * as V from "../common/vector" ; import minBy from "lodash-es/minBy" ; interface Line { source : Position ; target : Position ; } param line returns export function lineTo2Positions ( line ) { const { x1 , x2 , y1 , y2 } = line ; return [ { x : x1 , y : y1 } , { x : x2 , y : y2 } , ] ; } param p1 param p2 returns export function positionsToLinePosition ( p1 , p2 ) { return { x1 : p1 . x , y1 : p1 . y , x2 : p2 . x , y2 : p2 . y } ; } param line param targetSide param center param radius returns export function getIntersectionOfLineAndCircle ( line , targetSide , center , radius ) { return ( V . getIntersectionOfLineTargetAndCircle ( V . Vector . fromObject ( targetSide ? line . source : line . target ) , V . Vector . fromObject ( targetSide ? line . target : line . source ) , V . Vector . fromObject ( center ) , radius ) ?. toObject ( ) ?? null ) ; } param line1 param line2 returns export function getIntersectionPointOfLines ( line1 , line2 ) { const l1 = V . fromPositions ( line1 . source , line1 . target ) ; const l2 = V . fromPositions ( line2 . source , line2 . target ) ; return V . getIntersectionPointOfLines ( l1 , l2 ) . toObject ( ) ; } param point param center param radius returns export function isPointContainedInCircle ( point , center , radius ) { const p = V . Vector . fromObject ( point ) ; const c = V . Vector . fromObject ( center ) ; const v = p . subtract ( c ) ; return v . lengthSq ( ) < radius * radius ; } param line returns export function calculateDistance ( line ) { const x = line . x2 - line . x1 ; const y = line . y2 - line . y1 ; return Math . sqrt ( Math . pow ( x , 2 ) + Math . pow ( y , 2 ) ) ; } param sourcePos param targetPos param rect param scale returns function calculateDistanceToAvoidOverlapsWithRect ( sourcePos , targetPos , rect , scale ) { const centerLine = V . fromPositions ( sourcePos , targetPos ) ; const left = targetPos . x - ( rect . width / 2 ) * scale ; const top = targetPos . y - ( rect . height / 2 ) * scale ; const right = targetPos . x + ( rect . width / 2 ) * scale ; const bottom = targetPos . y + ( rect . height / 2 ) * scale ; const vertexes = [ ] ; const borderRadius = rect . borderRadius * scale ; if ( borderRadius == 0 ) { vertexes . push ( V . Vector . fromArray ( [ left , top ] ) , V . Vector . fromArray ( [ left , bottom ] ) , V . Vector . fromArray ( [ right , top ] ) , V . Vector . fromArray ( [ right , bottom ] ) ) ; } else { const hypo = borderRadius * Math . sin ( Math . PI / 4 ) ; vertexes . push ( V . Vector . fromArray ( [ left + borderRadius , top ] ) , V . Vector . fromArray ( [ left , top + borderRadius ] ) , V . Vector . fromArray ( [ left + borderRadius - hypo , top + borderRadius - hypo ] ) , V . Vector . fromArray ( [ left + borderRadius , bottom ] ) , V . Vector . fromArray ( [ left , bottom - borderRadius ] ) , V . Vector . fromArray ( [ left + borderRadius - hypo , bottom - borderRadius + hypo ] ) , V . Vector . fromArray ( [ right - borderRadius , top ] ) , V . Vector . fromArray ( [ right , top + borderRadius ] ) , V . Vector . fromArray ( [ right - borderRadius + hypo , top + borderRadius - hypo ] ) , V . Vector . fromArray ( [ right - borderRadius , bottom ] ) , V . Vector . fromArray ( [ right , bottom - borderRadius ] ) , V . Vector . fromArray ( [ right - borderRadius + hypo , bottom - borderRadius + hypo ] ) ) ; } const hits = vertexes . map ( p => V . getNearestPoint ( p , centerLine ) ) ; const minP = minBy ( hits , p => V . toLineVector ( centerLine . source , p ) . lengthSq ( ) ) ?? centerLine . target ; return V . toLineVector ( minP , centerLine . target ) . length ( ) ; } param linePos param edgeStyle param margin param padding param scale returns export function calculateEdgeLabelArea ( linePos , edgeStyle , margin , padding , scale ) { const line = V . fromLinePosition ( linePos ) ; const normalized = line . v . clone ( ) . normalize ( ) ; const sv = padding === 0 ? line . source : line . source . clone ( ) . add ( normalized . clone ( ) . multiplyScalar ( padding * scale ) ) ; const tv = padding === 0 ? line . target : line . target . clone ( ) . subtract ( normalized . clone ( ) . multiplyScalar ( padding * scale ) ) ; const labelMargin = ( edgeStyle . width / 2 + margin ) * scale ; const vMargin = V . Vector . fromArray ( [ - normalized . y , normalized . x ] ) . multiplyScalar ( labelMargin ) ; let sourceAbove = sv . clone ( ) . subtract ( vMargin ) . toObject ( ) as Position ; let sourceBelow = sv . clone ( ) . add ( vMargin ) . toObject ( ) as Position ; let targetAbove = tv . clone ( ) . subtract ( vMargin ) . toObject ( ) as Position ; let targetBelow = tv . clone ( ) . add ( vMargin ) . toObject ( ) as Position ; const angle = line . v . angleDeg ( ) ; if ( angle < - 90 || angle >= 90 ) { [ sourceAbove , sourceBelow ] = [ sourceBelow , sourceAbove ] ; [ targetAbove , targetBelow ] = [ targetBelow , targetAbove ] ; } return { source : { above : sourceAbove , below : sourceBelow } , target : { above : targetAbove , below : targetBelow } , } ; } param sourceNodePos param targetNodePos param sourceNodeShape param targetNodeShape returns export function calculateDistancesFromCenterOfNodeToEndOfNode ( sourceNodePos , targetNodePos , sourceNodeShape , targetNodeShape ) { let distance1 ; if ( sourceNodeShape . type === "circle" ) { distance1 = sourceNodeShape . radius ; } else { distance1 = calculateDistanceToAvoidOverlapsWithRect ( targetNodePos , sourceNodePos , sourceNodeShape , 1 ) ; } let distance2 ; if ( targetNodeShape . type === "circle" ) { distance2 = targetNodeShape . radius ; } else { distance2 = calculateDistanceToAvoidOverlapsWithRect ( sourceNodePos , targetNodePos , targetNodeShape , 1 ) ; } return [ distance1 , distance2 ] ; } param linePos param sourceMargin param targetMargin returns export function applyMarginToLine ( linePos , sourceMargin , targetMargin ) { const line = V . fromLinePosition ( linePos ) ; return applyMarginToLineInner ( line , sourceMargin , targetMargin ) ; } function applyMarginToLineInner ( line , sourceMargin , targetMargin ) { const normalized = line . v . clone ( ) . normalize ( ) ; line . v . angle ( ) ; const sv = line . source . clone ( ) . add ( normalized . clone ( ) . multiplyScalar ( sourceMargin ) ) ; const tv = line . target . clone ( ) . subtract ( normalized . clone ( ) . multiplyScalar ( targetMargin ) ) ; let [ x1 , y1 ] = sv . toArray ( ) ; let [ x2 , y2 ] = tv . toArray ( ) ; const check = V . toLineVector ( sv , tv ) ; if ( line . v . angle ( ) * check . angle ( ) < 0 ) { const c1 = V . Vector . fromArray ( [ ( x1 + x2 ) / 2 , ( y1 + y2 ) / 2 ] ) ; const c2 = c1 . clone ( ) . add ( normalized . multiplyScalar ( 0.5 ) ) ; [ x1 , y1 ] = c1 . toArray ( ) ; [ x2 , y2 ] = c2 . toArray ( ) ; } return { x1 , y1 , x2 , y2 } ; } param pos param center param radian returns export function moveOnCircumference ( pos , center , radian ) { const { x , y } = pos ; const dx = x - center . x ; const dy = y - center . y ; return { x : dx * Math . cos ( radian ) - dy * Math . sin ( radian ) + center . x , y : dx * Math . sin ( radian ) + dy * Math . cos ( radian ) + center . y , } ; } param theta returns export function reverseAngleRadian ( theta ) { if ( theta > 0 ) { return - ( Math . PI * 2 - theta ) ; } else { return Math . PI * 2 + theta ; } } export function inverseLine ( line ) { return { x1 : line . x2 , y1 : line . y2 , x2 : line . x1 , y2 : line . y1 } ; } export function calculateBezierCurveControlPoint ( p1 , center , p2 , theta0 ) { const control = [ ] ; const centerToSource = V . fromVectors ( center , p1 ) ; const centerToTarget = V . fromVectors ( center , p2 ) ; let theta = V . calculateRelativeAngleRadian ( centerToSource , centerToTarget ) ; if ( theta0 * theta < 0 ) { theta = reverseAngleRadian ( theta ) ; } const middle = V . Vector . fromObject ( moveOnCircumference ( p1 , center , - theta / 2 ) ) ; const centerToMp = V . fromVectors ( center , middle ) ; const mpTangent = V . calculatePerpendicularLine ( centerToMp ) ; const theta1 = V . calculateRelativeAngleRadian ( centerToSource , centerToMp ) ; let tangent = V . calculatePerpendicularLine ( centerToSource ) ; if ( Math . abs ( theta1 ) < Math . PI / 2 ) { const cp = V . getIntersectionPointOfLines ( tangent , mpTangent ) ; control . push ( cp ) ; } else { const mp = V . Vector . fromObject ( moveOnCircumference ( middle , center , theta1 / 2 ) ) ; const tangent2 = V . calculatePerpendicularLine ( V . fromVectors ( center , V . Vector . fromObject ( mp ) ) ) ; const cp1 = V . getIntersectionPointOfLines ( tangent , tangent2 ) ; const cp2 = V . getIntersectionPointOfLines ( tangent2 , mpTangent ) ; control . push ( cp1 , mp , cp2 ) ; } control . push ( middle ) ; const theta2 = V . calculateRelativeAngleRadian ( centerToTarget , centerToMp ) ; tangent = V . calculatePerpendicularLine ( centerToTarget ) ; if ( Math . abs ( theta2 ) < Math . PI / 2 ) { const cp = V . getIntersectionPointOfLines ( tangent , mpTangent ) ; control . push ( cp ) ; } else { const mp = V . Vector . fromObject ( moveOnCircumference ( middle , center , theta2 / 2 ) ) ; const tangent2 = V . calculatePerpendicularLine ( V . fromVectors ( center , V . Vector . fromObject ( mp ) ) ) ; const cp1 = V . getIntersectionPointOfLines ( mpTangent , tangent2 ) ; const cp2 = V . getIntersectionPointOfLines ( tangent2 , tangent ) ; control . push ( cp1 , mp , cp2 ) ; } return control ; }