temp
This commit is contained in:
parent
f90d025e5a
commit
081764b4b1
File diff suppressed because one or more lines are too long
@ -394,8 +394,20 @@
|
||||
padding: 0;
|
||||
border-color: get-color(item_bg-hover);
|
||||
|
||||
& > .ant-list-item-action {
|
||||
visibility: hidden;
|
||||
|
||||
& > li {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: get-color(item_bg-hover);
|
||||
|
||||
& > .ant-list-item-action {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
&.selected {
|
||||
|
@ -30,6 +30,8 @@ export interface MapRouteInfo {
|
||||
type: MapRouteType; // 线路类型
|
||||
direction?: -1 | 1; // 方向
|
||||
pass?: MapRoutePassType; // 可通行类型
|
||||
c1?: Point; // 控制点A
|
||||
c2?: Point; // 控制点B
|
||||
}
|
||||
//#endregion
|
||||
|
||||
@ -41,4 +43,5 @@ export interface MapAreaInfo {
|
||||
}
|
||||
//#endregion
|
||||
|
||||
export type MapRect = Record<'x' | 'y' | 'width' | 'height', number>;
|
||||
export type Point = Record<'x' | 'y', number>;
|
||||
export type Rect = Record<'x' | 'y' | 'width' | 'height', number>;
|
||||
|
@ -24,6 +24,7 @@ $icons: (
|
||||
redo,
|
||||
register,
|
||||
robot,
|
||||
route,
|
||||
save,
|
||||
search,
|
||||
trash_fill,
|
||||
|
BIN
src/assets/icons/dark/route.png
Normal file
BIN
src/assets/icons/dark/route.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 812 B |
Binary file not shown.
Before Width: | Height: | Size: 767 B After Width: | Height: | Size: 859 B |
@ -13,6 +13,12 @@
|
||||
"stroke": "#595959",
|
||||
"strokeActive": "#FCC947"
|
||||
},
|
||||
"route": {
|
||||
"stroke-0": "#8C8C8C",
|
||||
"stroke-1": "#49AA19",
|
||||
"stroke-2": "#D89614",
|
||||
"stroke-10": "#E63A3A"
|
||||
},
|
||||
"area": {
|
||||
"stroke-1": "#9ACDFF99",
|
||||
"fill-1": "#9ACDFF33",
|
||||
@ -21,6 +27,5 @@
|
||||
"stroke-12": "#0DBB8A99",
|
||||
"fill-12": "#0DBB8A33",
|
||||
"strokeActive": "#FCC947"
|
||||
},
|
||||
"line": "#8C8C8C"
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { MAP_POINT_TYPES, MapAreaType, type MapPen, type MapPointInfo, MapPointType, type MapRect } from '@api/map';
|
||||
import { MAP_POINT_TYPES, MapAreaType, type MapPen, type MapPointInfo, MapPointType, type Rect } from '@api/map';
|
||||
import type { RobotInfo } from '@api/robot';
|
||||
import type { PointBindModalRef } from '@common/modal/point-bind-modal.vue';
|
||||
import type { RobotBindModalRef } from '@common/modal/robot-bind-modal.vue';
|
||||
@ -27,7 +27,7 @@ const point = computed<MapPointInfo | null>(() => {
|
||||
return v;
|
||||
});
|
||||
|
||||
const rect = computed<MapRect | null>(() => {
|
||||
const rect = computed<Rect | null>(() => {
|
||||
if (isNil(pen.value)) return null;
|
||||
return editor.value.getPenRect(pen.value);
|
||||
});
|
||||
|
@ -46,7 +46,7 @@ watch(
|
||||
() => selected.clear(),
|
||||
);
|
||||
|
||||
const isAllSelected = computed<boolean>(() => robots.value.every(({ id }) => selected.has(id)));
|
||||
const isAllSelected = computed<boolean>(() => selected.size > 0 && robots.value.every(({ id }) => selected.has(id)));
|
||||
const selectAll = (checked: boolean) => {
|
||||
if (checked) {
|
||||
robots.value.forEach(({ id }) => selected.add(id));
|
||||
@ -90,6 +90,19 @@ const toDeleteGroup = (id: RobotGroup['id']) =>
|
||||
//#endregion
|
||||
|
||||
//#region 机器人操作
|
||||
const toRemoveRobot = (id: RobotInfo['id']) =>
|
||||
Modal.confirm({
|
||||
class: 'confirm',
|
||||
title: t('您确定要从场景中移除该机器人吗?'),
|
||||
centered: true,
|
||||
cancelText: t('返回'),
|
||||
okText: t('移除'),
|
||||
onOk: () => {
|
||||
editor.value.removeRobots([id]);
|
||||
selected.delete(id);
|
||||
},
|
||||
});
|
||||
|
||||
const toRemoveRobots = () =>
|
||||
Modal.confirm({
|
||||
class: 'confirm',
|
||||
@ -198,11 +211,20 @@ const toRemoveRobots = () =>
|
||||
<a-list rowKey="id" :data-source="getGroupRobots(robots)">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item
|
||||
class="ph-16"
|
||||
:class="{ selected: item.id === current }"
|
||||
:class="{ 'ph-16': !editable, 'pl-12': editable, 'pr-8': editable, selected: item.id === current }"
|
||||
style="height: 36px"
|
||||
@click="emit('change', item.id)"
|
||||
>
|
||||
<template v-if="editable" #actions>
|
||||
<a-button
|
||||
class="icon-btn panel-btn"
|
||||
:title="$t('移除机器人')"
|
||||
size="small"
|
||||
@click.stop="toRemoveRobot(item.id)"
|
||||
>
|
||||
<i class="icon trash_fill" />
|
||||
</a-button>
|
||||
</template>
|
||||
<a-space align="center" :size="8">
|
||||
<a-checkbox
|
||||
v-if="editable"
|
||||
|
@ -1,20 +1,33 @@
|
||||
import { EDITOR_CONFIG, type MapAreaInfo, MapAreaType, type MapPen, type MapPointInfo, MapPointType } from '@api/map';
|
||||
import {
|
||||
EDITOR_CONFIG,
|
||||
type MapAreaInfo,
|
||||
MapAreaType,
|
||||
type MapPen,
|
||||
type MapPointInfo,
|
||||
MapPointType,
|
||||
MapRoutePassType,
|
||||
MapRouteType,
|
||||
type Point,
|
||||
} from '@api/map';
|
||||
import type { RobotGroup, RobotInfo } from '@api/robot';
|
||||
import type { SceneData } from '@api/scene';
|
||||
import sTheme from '@core/theme.service';
|
||||
import { CanvasLayer, EditType, LockState, Meta2d, type Pen, s8 } from '@meta2d/core';
|
||||
import { CanvasLayer, EditType, LockState, Meta2d, type Meta2dStore, type Pen, s8 } from '@meta2d/core';
|
||||
import { useObservable } from '@vueuse/rxjs';
|
||||
import { clone, cloneDeep, get, isNil, isString, mapKeys, pick, remove, some } from 'lodash-es';
|
||||
import { BehaviorSubject, debounceTime, filter, map, Subject, switchMap } from 'rxjs';
|
||||
import { reactive, watch } from 'vue';
|
||||
|
||||
export type Point = Record<'x' | 'y', number>;
|
||||
|
||||
export class EditorService extends Meta2d {
|
||||
public async load(map?: string, editable = false): Promise<void> {
|
||||
const data = map ? JSON.parse(map) : undefined;
|
||||
this.open(data);
|
||||
this.setState(editable);
|
||||
|
||||
setTimeout(() => {
|
||||
this.addRoute(['21b74c90', '00f32a0']);
|
||||
this.addRoute(['21b74c90', '7f201a25'], 'bezier3');
|
||||
}, 1000);
|
||||
}
|
||||
public save(): string {
|
||||
const data = this.data();
|
||||
@ -244,6 +257,26 @@ export class EditorService extends Meta2d {
|
||||
const { direction = 1 } = pen.route ?? {};
|
||||
return `${p1.text} ${direction > 0 ? '→' : '←'} ${p2.text}`;
|
||||
}
|
||||
|
||||
public addRoute(p: [string, string], type = MapRouteType.直线): void {
|
||||
const [p1, p2] = p.map((v) => this.getPenById(v));
|
||||
if (!p1?.anchors?.length || !p2?.anchors?.length) return;
|
||||
const line = this.connectLine(p1, p2, undefined, undefined, false);
|
||||
const pen: MapPen = { tags: ['route'], route: { type }, lineWidth: 2 };
|
||||
this.bottom(line);
|
||||
this.setValue({ id: line.id, ...pen }, { render: false, history: false, doEvent: false });
|
||||
this.updateLineType(line, type);
|
||||
// this.pushHistory({ type: EditType.Add, pens: [cloneDeep(line)] });
|
||||
this.active([line]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
public changeRouteType(id: string, type: MapRouteType): void {
|
||||
const pen = this.getPenById(id);
|
||||
if (isNil(pen)) return;
|
||||
this.updateLineType(pen, type);
|
||||
this.setValue({ id, route: { type } }, { render: true, history: true, doEvent: true });
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region 区域
|
||||
@ -407,6 +440,7 @@ export class EditorService extends Meta2d {
|
||||
#register() {
|
||||
this.registerCanvasDraw({ point: drawPoint, line: drawLine, area: drawArea });
|
||||
this.registerAnchors({ point: anchorPoint });
|
||||
this.addDrawLineFn('bezier3', lineBezier3);
|
||||
}
|
||||
}
|
||||
|
||||
@ -477,16 +511,49 @@ function drawPoint(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
||||
ctx.restore();
|
||||
}
|
||||
function anchorPoint(pen: MapPen): void {
|
||||
pen.anchors = [{ x: 0.5, y: 0.5 }];
|
||||
pen.anchors = [{ penId: pen.id, id: 'c', x: 0.5, y: 0.5 }];
|
||||
}
|
||||
|
||||
function drawLine(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
||||
const theme = sTheme.editor;
|
||||
const [p1, p2] = pen.calculative?.worldAnchors ?? [];
|
||||
const { direction } = pen.route ?? {};
|
||||
const { x: x1 = 0, y: y1 = 0 } = p1 ?? {};
|
||||
const { x: x2 = 0, y: y2 = 0 } = p2 ?? {};
|
||||
const { type, direction = 1, pass = 0, c1, c2 } = pen.route ?? {};
|
||||
const { x: dx1 = x2 - x1, y: dy1 = 0 } = c1 ?? {};
|
||||
const { x: dx2 = 0, y: dy2 = y2 - y1 } = c2 ?? {};
|
||||
|
||||
ctx.save();
|
||||
ctx.lineWidth = 2;
|
||||
ctx.moveTo(x1, y1);
|
||||
ctx.strokeStyle = get(theme, `route.stroke-${pass}`) ?? '';
|
||||
switch (type) {
|
||||
case MapRouteType.直线:
|
||||
ctx.lineTo(x2, y2);
|
||||
break;
|
||||
case MapRouteType.三阶贝塞尔曲线:
|
||||
ctx.bezierCurveTo(x1 + dx1, y1 + dy1, x2 - dx2, y2 - dy2, x2, y2);
|
||||
p1.next = { x: x1 + dx1, y: y1 + dy1 };
|
||||
p2.prev = { x: x2 - dx2, y: y2 - dy2 };
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (pass === MapRoutePassType.禁行) {
|
||||
ctx.setLineDash([8, 4]);
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
function lineBezier3(_: Meta2dStore, pen: MapPen): void {
|
||||
if (pen.calculative?.worldAnchors?.length !== 2) return;
|
||||
const { c1, c2 } = pen.route ?? {};
|
||||
const [p1, p2] = pen.calculative?.worldAnchors ?? [];
|
||||
const { x: x1 = 0, y: y1 = 0 } = p1 ?? {};
|
||||
const { x: x2 = 0, y: y2 = 0 } = p2 ?? {};
|
||||
const { x: dx1 = x2 - x1, y: dy1 = 0 } = c1 ?? {};
|
||||
const { x: dx2 = 0, y: dy2 = y2 - y1 } = c2 ?? {};
|
||||
pen.calculative.worldAnchors[0].next = { x: x1 + dx1, y: y1 + dy1 };
|
||||
pen.calculative.worldAnchors[1].prev = { x: x2 - dx2, y: y2 - dy2 };
|
||||
}
|
||||
|
||||
function drawArea(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
||||
const theme = sTheme.editor;
|
||||
|
Loading…
x
Reference in New Issue
Block a user