@antv/x6
Version:
JavaScript diagramming library that uses SVG and HTML for rendering
95 lines (80 loc) • 2.55 kB
text/typescript
import { FunctionExt } from '../../common'
import { Line, normalize, Point, toRad } from '../../geometry'
import type { RouterDefinition } from './index'
import { manhattan } from './manhattan/index'
import {
type ManhattanRouterOptions,
type ResolvedOptions,
resolve,
} from './manhattan/options'
export interface MetroRouterOptions extends ManhattanRouterOptions {}
const defaults: Partial<MetroRouterOptions> = {
maxDirectionChange: 45,
// an array of directions to find next points on the route
// different from start/end directions
directions() {
const step = resolve(this.step, this)
const cost = resolve(this.cost, this)
const diagonalCost = Math.ceil(Math.sqrt((step * step) << 1)) // eslint-disable-line no-bitwise
return [
{ cost, offsetX: step, offsetY: 0 },
{ cost: diagonalCost, offsetX: step, offsetY: step },
{ cost, offsetX: 0, offsetY: step },
{ cost: diagonalCost, offsetX: -step, offsetY: step },
{ cost, offsetX: -step, offsetY: 0 },
{ cost: diagonalCost, offsetX: -step, offsetY: -step },
{ cost, offsetX: 0, offsetY: -step },
{ cost: diagonalCost, offsetX: step, offsetY: -step },
]
},
fallbackRoute: metroFallbackRoute,
}
function metroFallbackRoute(
this: any,
from: Point,
to: Point,
options: ResolvedOptions,
) {
const theta = from.theta(to)
const route: Point[] = []
let a = { x: to.x, y: from.y }
let b = { x: from.x, y: to.y }
if (theta % 180 > 90) {
const t = a
a = b
b = t
}
const p1 = theta % 90 < 45 ? a : b
const l1 = new Line(from, p1)
const alpha = 90 * Math.ceil(theta / 90)
const p2 = Point.fromPolar(l1.squaredLength(), toRad(alpha + 135), p1)
const l2 = new Line(to, p2)
const intersectionPoint = l1.intersectsWithLine(l2)
const directionFrom = intersectionPoint || from
const quadrant = 360 / options.directions.length
const angleTheta = directionFrom.theta(to)
const normalizedAngle = normalize(angleTheta + quadrant / 2)
const directionAngle = quadrant * Math.floor(normalizedAngle / quadrant)
options.previousDirectionAngle = directionAngle
if (
intersectionPoint &&
!intersectionPoint.equals(from) &&
!intersectionPoint.equals(to)
) {
route.push(intersectionPoint.round())
}
return route
}
export const metro: RouterDefinition<Partial<MetroRouterOptions>> = function (
vertices,
options,
linkView,
) {
return FunctionExt.call(
manhattan,
this,
vertices,
{ ...defaults, ...options },
linkView,
)
}