rt-graphic-component/components/packages/Section/common/SectionDrawAssistant.js
2024-01-10 15:46:15 +08:00

439 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { ContextMenu, GraphicDrawAssistant, KeyListener, calculateMirrorPoint, convertToBezierParams, pointPolygon, linePoint, GraphicInteractionPlugin, getWaypointRangeIndex, addWayPoint, clearWayPoint, PolylineEditPlugin, VectorText, AppConsts, BezierCurveEditPlugin, AbsorbableLine, AbsorbablePoint } from 'jl-graphic';
import { SectionType, defaultDisplayConfig } from './Section.js';
import { Graphics, Point } from 'pixi.js';
import { Turnout } from '../../Turnout/Turnout.js';
import { Section } from '../bjrtss/Section.js';
class SectionDraw extends GraphicDrawAssistant {
points = [];
graphic = new Graphics();
keyQListener = new KeyListener({
value: 'KeyQ',
global: true,
onPress: () => {
if (this.points.length === 0) ;
},
});
keyZListener = new KeyListener({
value: 'KeyZ',
global: true,
onPress: () => {
if (this.points.length === 0) ;
},
});
constructor(app, template) {
super(app, template, 'sym_o_timeline', '区段Section');
this.container.addChild(this.graphic);
SectionPointEditPlugin.init(app, this);
}
bind() {
super.bind();
this.app.addKeyboardListener(this.keyQListener, this.keyZListener);
}
unbind() {
super.unbind();
this.app.removeKeyboardListener(this.keyQListener, this.keyZListener);
}
onLeftDown(e) {
const { x, y } = this.toCanvasCoordinates(e.global);
const p = new Point(x, y);
if (this.graphicTemplate.isCurve) {
if (this.points.length == 0) {
this.points.push(p);
}
else {
this.points.push(p, p.clone());
}
}
else {
this.points.push(p);
}
}
onLeftUp(e) {
const template = this.graphicTemplate;
if (template.isCurve) {
const mp = this.toCanvasCoordinates(e.global);
if (this.points.length === 1) {
this.points.push(new Point(mp.x, mp.y));
}
else if (this.points.length > 1) {
const cp2 = this.points[this.points.length - 2];
const p = this.points[this.points.length - 1];
cp2.copyFrom(calculateMirrorPoint(p, mp));
this.points.push(mp);
}
}
}
onRightClick() {
if (this.points.length < 2) {
this.finish();
return;
}
this.createAndStore(true);
}
onEsc() {
if (this.points.length < 2) {
this.finish();
return;
}
this.createAndStore(true);
}
redraw(p) {
if (this.points.length < 1)
return;
const template = this.graphicTemplate;
this.graphic.clear();
this.graphic.lineStyle(3, '#555');
const ps = [...this.points];
if (template.isCurve) {
if (ps.length === 1) {
this.graphic.moveTo(ps[0].x, ps[0].y);
this.graphic.lineTo(p.x, p.y);
}
else {
if ((ps.length + 1) % 3 === 0) {
ps.push(p.clone(), p.clone());
}
else {
const cp = ps[ps.length - 2];
const p1 = ps[ps.length - 1];
const mp = calculateMirrorPoint(p1, p);
cp.copyFrom(mp);
}
const bps = convertToBezierParams(ps);
bps.forEach((bp) => {
this.graphic.drawBezierCurve(bp.p1, bp.p2, bp.cp1, bp.cp2, template.segmentsCount);
});
}
}
else {
ps.push(p);
ps.forEach((p, i) => {
if (i !== 0) {
this.graphic.lineTo(p.x, p.y);
}
else {
this.graphic.moveTo(p.x, p.y);
}
});
}
}
prepareData(data) {
const template = this.graphicTemplate;
if ((!template.isCurve && this.points.length < 2) ||
(template.isCurve && this.points.length < 4)) {
console.log('Section绘制因点不够取消绘制');
return false;
}
if (template.isCurve) {
this.points.pop();
}
data.isCurve = template.isCurve;
data.segmentsCount = template.segmentsCount;
data.points = this.points;
data.code = 'G000';
return true;
}
clearCache() {
this.points = [];
this.graphic.clear();
}
}
class SectionGraphicHitArea {
section;
constructor(section) {
this.section = section;
}
contains(x, y) {
if (this.section.datas.sectionType === SectionType.TurnoutPhysical) {
return false;
}
if (this.section.datas.isCurve) {
const bps = convertToBezierParams(this.section.datas.points);
for (let i = 0; i < bps.length; i++) {
const bp = bps[i];
if (pointPolygon({ x, y }, [bp.p1, bp.cp1, bp.cp2, bp.p2], defaultDisplayConfig.lineWidth)) {
return true;
}
}
}
else {
for (let i = 1; i < this.section.datas.points.length; i++) {
const p1 = this.section.datas.points[i - 1];
const p2 = this.section.datas.points[i];
if (linePoint(p1, p2, { x, y }, defaultDisplayConfig.lineWidth)) {
return true;
}
}
}
return false;
}
}
function buildAbsorbablePositions(section) {
const aps = [];
const sections = section.queryStore.queryByType(Section.Type);
sections.forEach((other) => {
const [ps, pe] = [
other.localToCanvasPoint(other.getStartPoint()),
other.localToCanvasPoint(other.getEndPoint()),
];
const { width, height } = section.getGraphicApp().canvas;
const xs = new AbsorbableLine({ x: 0, y: ps.y }, { x: width, y: ps.y });
const ys = new AbsorbableLine({ x: ps.x, y: 0 }, { x: ps.x, y: height });
const xe = new AbsorbableLine({ x: 0, y: pe.y }, { x: width, y: pe.y });
const ye = new AbsorbableLine({ x: pe.x, y: 0 }, { x: pe.x, y: height });
aps.push(xs, ys, xe, ye);
});
const turnouts = section.queryStore.queryByType(Turnout.Type);
turnouts.forEach((turnout) => {
turnout.getPortPoints().forEach((points) => {
turnout.localToCanvasPoints(...points).forEach((p) => {
aps.push(new AbsorbablePoint(p));
});
});
});
return aps;
}
function onEditPointCreate(g, dp) {
const section = g;
dp.on('transformstart', (e) => {
if (e.isShift()) {
section.getGraphicApp().setOptions({
absorbablePositions: buildAbsorbablePositions(section),
});
}
});
}
class SectionPolylineEditPlugin extends PolylineEditPlugin {
static Name = 'SectionPolylineEditPlugin';
labels = [];
constructor(g, options) {
super(g, options);
this.name = SectionPolylineEditPlugin.Name;
this.initLabels();
}
initLabels() {
this.labels = ['A', 'B'].map((str) => {
const vc = new VectorText(str, { fill: AppConsts.assistantElementColor });
vc.setVectorFontSize(14);
vc.anchor.set(0.5);
return vc;
});
this.addChild(...this.labels);
this.updateEditedPointsPosition();
}
updateEditedPointsPosition() {
super.updateEditedPointsPosition();
this.labels[0]?.position.set(this.editedPoints[0].x, this.editedPoints[0].y + 10);
this.labels[1]?.position.set(this.editedPoints[this.editedPoints.length - 1].x, this.editedPoints[this.editedPoints.length - 1].y + 10);
}
}
class SectionBazierCurveEditPlugin extends BezierCurveEditPlugin {
static Name = 'SectionBazierCurveEditPlugin';
labels = [];
constructor(g, options) {
super(g, options);
this.name = SectionBazierCurveEditPlugin.Name;
this.initLabels();
}
initLabels() {
this.labels = [new VectorText('A'), new VectorText('B')];
this.labels.forEach((label) => {
label.setVectorFontSize(14);
this.addChild(label);
});
this.updateEditedPointsPosition();
}
updateEditedPointsPosition() {
super.updateEditedPointsPosition();
this.labels[0]?.position.set(this.editedPoints[0].x, this.editedPoints[0].y + 10);
this.labels[1]?.position.set(this.editedPoints[this.editedPoints.length - 1].x, this.editedPoints[this.editedPoints.length - 1].y + 10);
}
}
const addWaypointConfig = {
name: '添加路径点',
};
const clearWaypointsConfig = {
name: '清除所有路径点',
};
const splitSectionConfig = {
name: '拆分',
// disabled: true,
};
const SectionEditMenu = ContextMenu.init({
name: '区段编辑菜单',
groups: [
{
items: [addWaypointConfig, clearWaypointsConfig],
},
{
items: [splitSectionConfig],
},
],
});
class SectionPointEditPlugin extends GraphicInteractionPlugin {
static Name = 'SectionPointDrag';
drawAssistant;
constructor(app, da) {
super(SectionPointEditPlugin.Name, app);
this.drawAssistant = da;
app.registerMenu(SectionEditMenu);
}
static init(app, da) {
return new SectionPointEditPlugin(app, da);
}
filter(...grahpics) {
return grahpics.filter((g) => g.type == Section.Type);
}
bind(g) {
g.lineGraphic.eventMode = 'static';
g.lineGraphic.cursor = 'pointer';
g.lineGraphic.hitArea = new SectionGraphicHitArea(g);
g.transformSave = true;
g.labelGraphic.eventMode = 'static';
g.labelGraphic.cursor = 'pointer';
g.labelGraphic.selectable = true;
g.labelGraphic.draggable = true;
g.on('selected', this.onSelected, this);
g.on('unselected', this.onUnselected, this);
// g.on('_rightclick', this.onContextMenu, this);
}
unbind(g) {
g.off('selected', this.onSelected, this);
g.off('unselected', this.onUnselected, this);
// g.off('_rightclick', this.onContextMenu, this);
}
onContextMenu(e) {
const target = e.target;
const section = target.getGraphic();
this.app.updateSelected(section);
if (section.datas.sectionType === SectionType.TurnoutPhysical) {
return;
}
const p = section.screenToLocalPoint(e.global);
addWaypointConfig.handler = () => {
const linePoints = section.linePoints;
const { start, end } = getWaypointRangeIndex(linePoints, false, p, defaultDisplayConfig.lineWidth);
addWayPoint(section, false, start, end, p);
};
clearWaypointsConfig.handler = () => {
clearWayPoint(section, false);
};
// TODO
/**
* 区段拆分的菜单项和对话框之前写在这里但引用了Quasar组件不是很合适暂时先注释掉
* Section中已有获取拆分的数据的方法考虑将生成对应目标区段的逻辑放业务层或这里提供生成方法供业务层调用
*/
// splitSectionConfig.disabled = false;
// splitSectionConfig.handler = () => {
// Dialog.create({
// title: '拆分区段',
// message: '请选择生成数量和方向',
// component: SectionSplitDialog,
// cancel: true,
// persistent: true,
// }).onOk((data: { num: number; dir: 'ltr' | 'rtl' }) => {
// const { num, dir } = data;
// const sectionData = section.datas;
// const points = section.getSplitPoints(num);
// const children: LogicSection[] = [];
// let codeAppend = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.slice(0, num);
// if (
// (dir === 'ltr' &&
// sectionData.points[0].x >
// sectionData.points[sectionData.points.length - 1].x) ||
// (dir === 'rtl' &&
// sectionData.points[0].x <
// sectionData.points[sectionData.points.length - 1].x)
// ) {
// codeAppend = codeAppend.split('').reverse().join('');
// }
// points.forEach((ps, i) => {
// const data = new LogicSectionData();
// const logicSectionDraw =
// this.drawAssistant.app.getDrawAssistant<LogicSectionDraw>(
// LogicSection.Type,
// );
// data.id = logicSectionDraw.nextId();
// data.code = `${sectionData.code}-${codeAppend.charAt(i % 26)}`;
// data.points = ps.map(
// (p) => new graphicData.Point({ x: p.x, y: p.y }),
// );
// data.id = this.drawAssistant.nextId();
// data.childTransforms = [
// new ChildTransform(
// 'label',
// new GraphicTransform(
// {
// x:
// data.points[0].x +
// (data.points[1].x - data.points[0].x) / 2,
// y:
// data.points[0].y +
// (data.points[1].y - data.points[0].y) / 2 +
// 20,
// },
// { x: 0, y: 0 },
// 0,
// { x: 0, y: 0 },
// ),
// ),
// ];
// const g = logicSectionDraw.graphicTemplate.new();
// g.loadData(data);
// logicSectionDraw.storeGraphic(g);
// children.push(g);
// });
// // sectionData.children = children.map((g) => g.datas.id);
// section.repaint();
// section.buildRelation();
// section.draggable = false;
// children.forEach((c) => c.buildRelation());
// this.app.updateSelected(...children);
// });
// };
SectionEditMenu.open(e.global);
}
onSelected(g) {
const section = g;
if (section.datas.sectionType === SectionType.TurnoutPhysical) {
return;
}
if (section.datas.isCurve) {
let lep = section.getAssistantAppend(SectionBazierCurveEditPlugin.Name);
if (!lep) {
lep = new SectionBazierCurveEditPlugin(section, {
onEditPointCreate,
});
section.addAssistantAppend(lep);
}
lep.showAll();
}
else {
let lep = section.getAssistantAppend(SectionPolylineEditPlugin.Name);
if (!lep) {
lep = new SectionPolylineEditPlugin(section, { onEditPointCreate });
section.addAssistantAppend(lep);
}
lep.showAll();
}
}
onUnselected(g) {
const section = g;
if (section.datas.isCurve) {
const lep = section.getAssistantAppend(SectionBazierCurveEditPlugin.Name);
if (lep) {
lep.hideAll();
}
}
else {
const lep = section.getAssistantAppend(SectionPolylineEditPlugin.Name);
if (lep) {
lep.hideAll();
}
}
}
}
export { SectionDraw, SectionGraphicHitArea, SectionPointEditPlugin, addWaypointConfig, clearWaypointsConfig, splitSectionConfig };