feat: 迁入 MiGu.Server、平台前端与车辆列表 reflection 回退
从 Simple-FR 拆出 Platform.Server 并重命名为 MiGu.Server;frontends 源码与构建脚本迁入本仓库。地图监控在 projection/cars 失败或为空时回退 reflection 车辆列表;Simple 仓库已移除旧 Platform.Server。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
import type { BatchOp } from '@/api/mapEdit'
|
||||
|
||||
/**
|
||||
* 对齐算法:纯前端计算每个选中对象的新坐标,再以 batch ops(patch)回写到反射 API。
|
||||
* 仅支持有 (x, y) 坐标的对象(site / image / text / model 等 UIHelper 子类);
|
||||
* track 是端点连线,没有自己的坐标,alignment 不影响 track。
|
||||
*/
|
||||
|
||||
export interface AlignTarget {
|
||||
kind: string
|
||||
id: number
|
||||
x: number
|
||||
y: number
|
||||
width?: number
|
||||
height?: number
|
||||
}
|
||||
|
||||
export type AlignMode =
|
||||
| 'left'
|
||||
| 'right'
|
||||
| 'top'
|
||||
| 'bottom'
|
||||
| 'centerH'
|
||||
| 'centerV'
|
||||
| 'center'
|
||||
| 'distributeH'
|
||||
| 'distributeV'
|
||||
| 'toFirst'
|
||||
| 'toLast'
|
||||
|
||||
/**
|
||||
* 根据对齐模式计算 patch ops。返回值为 mapEdit.batch 直接消费的 BatchOp 数组。
|
||||
* 输入要求至少 2 个对象(distribute 需要 ≥ 3 才有意义)。
|
||||
*/
|
||||
export function buildAlignOps(targets: AlignTarget[], mode: AlignMode): BatchOp[] {
|
||||
if (targets.length < 2) return []
|
||||
|
||||
const ops: BatchOp[] = []
|
||||
|
||||
const xs = targets.map((t) => t.x)
|
||||
const ys = targets.map((t) => t.y)
|
||||
const minX = Math.min(...xs)
|
||||
const maxX = Math.max(...xs)
|
||||
const minY = Math.min(...ys)
|
||||
const maxY = Math.max(...ys)
|
||||
const avgX = xs.reduce((a, b) => a + b, 0) / xs.length
|
||||
const avgY = ys.reduce((a, b) => a + b, 0) / ys.length
|
||||
|
||||
switch (mode) {
|
||||
case 'left':
|
||||
for (const t of targets) ops.push(patch(t, { x: minX }))
|
||||
break
|
||||
case 'right':
|
||||
for (const t of targets) ops.push(patch(t, { x: maxX }))
|
||||
break
|
||||
case 'top':
|
||||
// 工程坐标 y 向上为正:top 取 max
|
||||
for (const t of targets) ops.push(patch(t, { y: maxY }))
|
||||
break
|
||||
case 'bottom':
|
||||
for (const t of targets) ops.push(patch(t, { y: minY }))
|
||||
break
|
||||
case 'centerH':
|
||||
for (const t of targets) ops.push(patch(t, { x: avgX }))
|
||||
break
|
||||
case 'centerV':
|
||||
for (const t of targets) ops.push(patch(t, { y: avgY }))
|
||||
break
|
||||
case 'center':
|
||||
for (const t of targets) ops.push(patch(t, { x: avgX, y: avgY }))
|
||||
break
|
||||
case 'distributeH': {
|
||||
const sorted = [...targets].sort((a, b) => a.x - b.x)
|
||||
const step = (sorted[sorted.length - 1]!.x - sorted[0]!.x) / (sorted.length - 1)
|
||||
sorted.forEach((t, i) => {
|
||||
const x = sorted[0]!.x + step * i
|
||||
if (Math.abs(x - t.x) > 0.5) ops.push(patch(t, { x }))
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'distributeV': {
|
||||
const sorted = [...targets].sort((a, b) => a.y - b.y)
|
||||
const step = (sorted[sorted.length - 1]!.y - sorted[0]!.y) / (sorted.length - 1)
|
||||
sorted.forEach((t, i) => {
|
||||
const y = sorted[0]!.y + step * i
|
||||
if (Math.abs(y - t.y) > 0.5) ops.push(patch(t, { y }))
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'toFirst': {
|
||||
const ref = targets[0]!
|
||||
for (const t of targets.slice(1)) ops.push(patch(t, { x: ref.x, y: ref.y }))
|
||||
break
|
||||
}
|
||||
case 'toLast': {
|
||||
const ref = targets[targets.length - 1]!
|
||||
for (const t of targets.slice(0, -1)) ops.push(patch(t, { x: ref.x, y: ref.y }))
|
||||
break
|
||||
}
|
||||
}
|
||||
return ops
|
||||
}
|
||||
|
||||
function patch(t: AlignTarget, data: Record<string, unknown>): BatchOp {
|
||||
return { action: 'patch', kind: t.kind, id: t.id, data }
|
||||
}
|
||||
Reference in New Issue
Block a user