公共的线条备用

This commit is contained in:
joylink_zhaoerwei 2024-10-09 18:52:48 +08:00
parent 04caf21f2f
commit 662d7260ca
15 changed files with 1135 additions and 10 deletions

2
package-lock.json generated
View File

@ -11,7 +11,7 @@
"@quasar/extras": "^1.16.4",
"axios": "^1.2.1",
"google-protobuf": "^3.21.4",
"jl-graphic": "git+http://120.46.212.6:3000/joylink/graphic-pixi.git#v0.1.15",
"jl-graphic": "git+http://120.46.212.6:3000/joylink/graphic-pixi.git#v0.1.18",
"js-base64": "^3.7.5",
"pinia": "^2.0.11",
"quasar": "^2.16.0",

View File

@ -18,7 +18,7 @@
"@quasar/extras": "^1.16.4",
"axios": "^1.2.1",
"google-protobuf": "^3.21.4",
"jl-graphic": "git+https://gitea.joylink.club/joylink/graphic-pixi.git#v0.1.17",
"jl-graphic": "git+https://gitea.joylink.club/joylink/graphic-pixi.git#v0.1.18",
"js-base64": "^3.7.5",
"pinia": "^2.0.11",
"quasar": "^2.16.0",

View File

@ -6,7 +6,11 @@
<div class="text-h6">{{ drawStore.drawGraphicName + ' 模板' }}</div>
</q-card-section>
<q-separator inset></q-separator>
<q-card-section> </q-card-section>
<q-card-section>
<template v-if="drawStore.drawGraphicType === Line.Type">
<line-template></line-template>
</template>
</q-card-section>
</q-card>
</div>
<!-- 画布或图形对象属性 -->
@ -29,6 +33,9 @@
<rect-property
v-else-if="drawStore.selectedGraphicType === Rect.Type"
/>
<line-property
v-else-if="drawStore.selectedGraphicType === Line.Type"
/>
<cctv-button-property
v-else-if="drawStore.selectedGraphicType === CCTVButton.Type"
/>
@ -50,6 +57,9 @@ import cctvButtonProperty from './properties/CCTV/CCTVButtonProperty.vue';
import { CCTVButton } from 'src/graphics/CCTV/cctvButton/CCTVButton';
import RectProperty from './properties/RectProperty.vue';
import { Rect } from 'src/graphics/rect/Rect';
import LineTemplate from './templates/LineTemplate.vue';
import LineProperty from './properties/LineProperty.vue';
import { Line } from 'src/graphics/line/Line';
const drawStore = useDrawStore();
</script>

View File

@ -0,0 +1,58 @@
<template>
<q-form class="q-gutter-sm">
<q-input outlined readonly v-model="lineModel.id" label="id" />
<q-input
outlined
type="number"
v-model.number="lineModel.segmentsCount"
v-if="lineModel.isCurve"
@blur="onUpdate"
label="曲线分段数"
/>
<q-input
outlined
v-model="lineModel.lineColor"
@blur="onUpdate"
label="线色"
lazy-rules
:rules="[(val) => (val && val.length > 0) || '线色不能为空']"
>
<template v-slot:append>
<q-icon name="colorize" class="cursor-pointer">
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
<q-color
v-model="lineModel.lineColor"
@change="
(val) => {
lineModel.lineColor = val;
onUpdate();
}
"
/>
</q-popup-proxy>
</q-icon>
</template>
</q-input>
<q-input
outlined
v-model.number="lineModel.lineWidth"
type="number"
@blur="onUpdate"
label="宽度"
lazy-rules
:rules="[(val) => (val && val > 0) || '宽度必须大于0']"
/>
</q-form>
</template>
<script setup lang="ts">
import { useDrawStore } from 'src/stores/draw-store';
import { useFormData } from 'src/components/DrawAppFormUtils';
import { LineData } from 'src/drawApp/graphics/LineInteraction';
const drawStore = useDrawStore();
const { data: lineModel, onUpdate } = useFormData(
new LineData(),
drawStore.getDrawApp()
);
</script>

View File

@ -1,6 +1,6 @@
<template>
<q-form>
<q-input outlined readonly v-model="rectModel.id" label="id" hint="" />
<q-form class="q-gutter-sm">
<q-input outlined readonly v-model="rectModel.id" label="id" />
<q-input
outlined
v-model.number="rectModel.lineWidth"
@ -10,7 +10,6 @@
lazy-rules
:rules="[(val) => (val && val > 0) || '画布宽必须大于0']"
/>
<q-input
outlined
v-model="rectModel.lineColor"

View File

@ -0,0 +1,42 @@
<template>
<q-form>
<q-btn-toggle
v-model="template.isCurve"
:options="[
{ label: '曲线(Q)', value: true },
{ label: '直线(Z)', value: false },
]"
@update:model-value="onUpdate"
toggle-color="primary"
></q-btn-toggle>
<q-input
v-model="template.segmentsCount"
v-if="template.isCurve"
label="曲线分段数"
@blur="onUpdate"
></q-input>
</q-form>
</template>
<script setup lang="ts">
import { LineTemplate, ILineTemplateProperty } from 'src/graphics/line/Line';
import { useDrawStore } from 'src/stores/draw-store';
import { reactive } from 'vue';
const drawStore = useDrawStore();
const template = reactive<ILineTemplateProperty>({
isCurve: false,
segmentsCount: 10,
});
function onUpdate() {
const gt = drawStore.drawGraphicTemplate as LineTemplate;
if (gt) {
gt.isCurve = template.isCurve;
gt.segmentsCount = template.segmentsCount;
}
}
</script>

View File

@ -23,6 +23,9 @@ import { common } from 'src/protos/common';
import { errorNotify, successNotify } from 'src/utils/CommonNotify';
import { saveDraft } from 'src/api/DraftApi';
import { sync_data_message } from 'src/protos/sync_data_message';
import { LineDraw } from 'src/graphics/line/LineDrawAssistant';
import { Line, LineTemplate } from 'src/graphics/line/Line';
import { LineData } from './graphics/LineInteraction';
const UndoOptions: MenuItemOptions = {
name: '撤销',
@ -50,6 +53,7 @@ export function initCommonDrawApp(app: IDrawApp) {
new ArrowDraw(app, new ArrowTemplate(new ArrowData()));
new TextContentDraw(app, new TextContentTemplate(new IscsTextData()));
new RectDraw(app, new RectTemplate(new RectData()));
new LineDraw(app, new LineTemplate(new LineData()));
// 画布右键菜单
app.registerMenu(DefaultCanvasMenu);
@ -77,6 +81,7 @@ interface ICommonStorage {
arrows: iscsGraphicData.Arrow[];
iscsTexts: iscsGraphicData.IscsText[];
rects: iscsGraphicData.Rect[];
lines: iscsGraphicData.Line[];
}
export function loadCommonDrawDatas(storage: ICommonStorage): GraphicData[] {
const datas: GraphicData[] = [];
@ -90,6 +95,9 @@ export function loadCommonDrawDatas(storage: ICommonStorage): GraphicData[] {
storage.rects.forEach((rect) => {
datas.push(new RectData(rect));
});
storage.lines.forEach((line) => {
datas.push(new LineData(line));
});
return datas;
}
@ -112,6 +120,9 @@ export function saveCommonDrawDatas(app: IDrawApp, storage: ICommonStorage) {
} else if (g instanceof Rect) {
const rectData = g.saveData();
storage.rects.push((rectData as RectData).data);
} else if (g instanceof Line) {
const lineData = g.saveData();
storage.lines.push((lineData as LineData).data);
}
});

View File

@ -0,0 +1,68 @@
import * as pb_1 from 'google-protobuf';
import { GraphicDataBase } from './GraphicDataBase';
import { ILineData, Line } from 'src/graphics/line/Line';
import { IPointData } from 'pixi.js';
import { iscsGraphicData } from 'src/protos/iscs_graphic_data';
import { common } from 'src/protos/common';
export class LineData extends GraphicDataBase implements ILineData {
constructor(data?: iscsGraphicData.Line) {
let line;
if (!data) {
line = new iscsGraphicData.Line({
common: GraphicDataBase.defaultCommonInfo(Line.Type),
});
} else {
line = data;
}
super(line);
}
public get data(): iscsGraphicData.Line {
return this.getData<iscsGraphicData.Line>();
}
get code(): string {
return this.data.code;
}
set code(v: string) {
this.data.code = v;
}
get points(): IPointData[] {
return this.data.points;
}
set points(points: IPointData[]) {
this.data.points = points.map((p) => new common.Point({ x: p.x, y: p.y }));
}
get isCurve(): boolean {
return this.data.isCurve;
}
set isCurve(v: boolean) {
this.data.isCurve = v;
}
get segmentsCount(): number {
return this.data.segmentsCount;
}
set segmentsCount(v: number) {
this.data.segmentsCount = v;
}
get lineWidth(): number {
return this.data.lineWidth;
}
set lineWidth(v: number) {
this.data.lineWidth = v;
}
get lineColor(): string {
return this.data.lineColor;
}
set lineColor(v: string) {
this.data.lineColor = v;
}
clone(): LineData {
return new LineData(this.data.cloneMessage());
}
copyFrom(data: LineData): void {
pb_1.Message.copyInto(data.data, this.data);
}
eq(other: LineData): boolean {
return pb_1.Message.equals(this.data, other.data);
}
}

108
src/graphics/line/Line.ts Normal file
View File

@ -0,0 +1,108 @@
import { IPointData } from 'pixi.js';
import {
GraphicData,
JlGraphic,
JlGraphicTemplate,
VectorText,
convertToBezierParams,
ILineGraphic,
} from 'jl-graphic';
import { LineGraphic } from '../lineGraphic/LineGraphic';
export interface ILineData extends GraphicData {
get code(): string; // 编号
set code(v: string);
get points(): IPointData[]; // 线坐标点
set points(points: IPointData[]);
get isCurve(): boolean; // 是否曲线
set isCurve(v: boolean);
get segmentsCount(): number; // 曲线分段数
set segmentsCount(v: number);
get lineWidth(): number; // 线宽
set lineWidth(v: number);
get lineColor(): string; // 线色
set lineColor(v: string);
clone(): ILineData;
copyFrom(data: ILineData): void;
eq(other: ILineData): boolean;
}
export class Line extends JlGraphic implements ILineGraphic {
static Type = 'Line';
lineGraphic: LineGraphic;
labelGraphic: VectorText;
childLines: Line[] = [];
constructor() {
super(Line.Type);
this.lineGraphic = new LineGraphic();
this.transformSave = true;
this.addChild(this.lineGraphic);
}
doRepaint() {
if (this.datas.points.length < 2) {
throw new Error('Link坐标数据异常');
}
this.lineGraphic.clear();
this.lineGraphic.isCurve = this.datas.isCurve;
if (this.lineGraphic.isCurve) {
this.lineGraphic.segmentsCount = this.datas.segmentsCount;
}
this.lineGraphic.points = this.datas.points;
this.lineGraphic.lineStyle(this.datas.lineWidth, this.datas.lineColor);
this.lineGraphic.paint();
}
getVerticesList(): IPointData[] {
if (this.datas.isCurve) {
return [
this.datas.points[0],
...convertToBezierParams(this.datas.points).map((param) => param.p2),
];
} else {
return this.datas.points;
}
}
getStartPoint() {
return this.datas.points[0];
}
getEndPoint(): IPointData {
return this.datas.points[this.datas.points.length - 1];
}
get datas(): ILineData {
return this.getDatas<ILineData>();
}
get linePoints(): IPointData[] {
return this.datas.points;
}
set linePoints(points: IPointData[]) {
const old = this.datas.clone();
old.points = points;
this.updateData(old);
}
}
export interface ILineTemplateProperty {
isCurve: boolean;
segmentsCount: number;
}
export class LineTemplate
extends JlGraphicTemplate<Line>
implements ILineTemplateProperty
{
isCurve = false;
segmentsCount = 10;
constructor(dataTemplate: ILineData) {
super(Line.Type, { dataTemplate });
}
new() {
const g = new Line();
g.loadData(this.datas);
return g;
}
}

View File

@ -0,0 +1,480 @@
import {
DisplayObject,
FederatedMouseEvent,
Graphics,
IHitArea,
IPointData,
Point,
} from 'pixi.js';
import {
DraggablePoint,
GraphicDrawAssistant,
GraphicInteractionPlugin,
GraphicTransformEvent,
IDrawApp,
IGraphicApp,
JlGraphic,
KeyListener,
VectorText,
calculateMirrorPoint,
convertToBezierParams,
linePoint,
pointPolygon,
BezierCurveEditPlugin,
IEditPointOptions,
ILineGraphic,
PolylineEditPlugin,
addWayPoint,
clearWayPoint,
ContextMenu,
MenuItemOptions,
AbsorbableLine,
AbsorbablePosition,
AbsorbablePoint,
distance,
} from 'jl-graphic';
import { ILineData, Line, LineTemplate } from './Line';
import { getWayLineIndex } from './LineUtils';
export const LineConsts = {
lineColor: '#f00',
lineWidth: 3,
};
export class LineDraw extends GraphicDrawAssistant<LineTemplate, ILineData> {
points: Point[] = [];
graphic = new Graphics();
keyQListener = new KeyListener({
value: 'KeyQ',
global: true,
onPress: () => {
if (this.points.length === 0) {
this.graphicTemplate.isCurve = true;
}
},
});
keyZListener = new KeyListener({
value: 'KeyZ',
global: true,
onPress: () => {
if (this.points.length === 0) {
this.graphicTemplate.isCurve = false;
}
},
});
constructor(app: IDrawApp, template: LineTemplate) {
super(app, template, 'sym_o_timeline', '线条Line');
this.container.addChild(this.graphic);
LinePointEditPlugin.init(app, this);
}
bind(): void {
super.bind();
this.app.addKeyboardListener(this.keyQListener, this.keyZListener);
}
unbind(): void {
super.unbind();
this.app.removeKeyboardListener(this.keyQListener, this.keyZListener);
}
onLeftDown(e: FederatedMouseEvent): void {
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: FederatedMouseEvent): void {
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(): void {
if (this.points.length < 2) {
this.finish();
return;
}
this.createAndStore(true);
}
onEsc(): void {
if (this.points.length < 2) {
this.finish();
return;
}
this.createAndStore(true);
}
redraw(p: Point): void {
if (this.points.length < 1) return;
const template = this.graphicTemplate;
this.graphic.clear();
this.graphic.lineStyle(LineConsts.lineWidth, LineConsts.lineColor);
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: ILineData): boolean {
const template = this.graphicTemplate;
if (
(!template.isCurve && this.points.length < 2) ||
(template.isCurve && this.points.length < 4)
) {
console.log('Line绘制因点不够取消绘制');
return false;
}
if (template.isCurve) {
this.points.pop();
}
data.isCurve = template.isCurve;
data.segmentsCount = template.segmentsCount;
data.points = this.points;
data.lineColor = LineConsts.lineColor;
data.lineWidth = LineConsts.lineWidth;
return true;
}
clearCache(): void {
this.points = [];
this.graphic.clear();
}
}
export class LineGraphicHitArea implements IHitArea {
line: Line;
constructor(line: Line) {
this.line = line;
}
contains(x: number, y: number): boolean {
if (this.line.datas.isCurve) {
const bps = convertToBezierParams(this.line.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],
this.line.datas.lineWidth
)
) {
return true;
}
}
} else {
for (let i = 1; i < this.line.datas.points.length; i++) {
const p1 = this.line.datas.points[i - 1];
const p2 = this.line.datas.points[i];
if (linePoint(p1, p2, { x, y }, this.line.datas.lineWidth)) {
return true;
}
}
}
return false;
}
}
function buildAbsorbablePositions(
line: Line,
dp: DraggablePoint
): AbsorbablePosition[] {
const aps: AbsorbablePosition[] = [];
const changePoints = line.localToCanvasPoints(...line.datas.points);
for (let i = 0; i < changePoints.length; i++) {
if (changePoints[i].equals(dp)) {
const { width, height } = line.getGraphicApp().canvas;
if (i == 0 || i == changePoints.length - 1) {
const ps =
i == 0 ? changePoints[1] : changePoints[changePoints.length - 2];
const x = new AbsorbableLine({ x: 0, y: ps.y }, { x: width, y: ps.y });
const y = new AbsorbableLine({ x: ps.x, y: 0 }, { x: ps.x, y: height });
aps.push(x, y);
} else {
const generateAxisPoint = [changePoints[i - 1], changePoints[i + 1]];
generateAxisPoint.forEach((point) => {
const x = new AbsorbableLine(
{ x: 0, y: point.y },
{ x: width, y: point.y }
);
const y = new AbsorbableLine(
{ x: point.x, y: 0 },
{ x: point.x, y: height }
);
aps.push(x, y);
});
}
break;
}
}
if (line instanceof Line) {
const lines = line.queryStore.queryByType<Line>(Line.Type);
lines.forEach((item) => {
if (item.id !== line.id) {
item.localToCanvasPoints(...item.datas.points).forEach((p) => {
aps.push(new AbsorbablePoint(p));
});
}
});
}
return aps;
}
export function onEditPointCreate(g: ILineGraphic, dp: DraggablePoint): void {
const line = g as Line;
dp.on('transformstart', (e: GraphicTransformEvent) => {
if (e.isShift()) {
line.getGraphicApp().setOptions({
absorbablePositions: buildAbsorbablePositions(line, dp),
});
}
});
}
class LinePolylineEditPlugin extends PolylineEditPlugin {
static Name = 'LinePolylineEditPlugin';
labels: VectorText[] = [];
constructor(g: ILineGraphic, options?: IEditPointOptions) {
super(g, options);
this.name = LinePolylineEditPlugin.Name;
}
}
class LineBazierCurveEditPlugin extends BezierCurveEditPlugin {
static Name = 'LineBazierCurveEditPlugin';
labels: VectorText[] = [];
constructor(g: ILineGraphic, options?: IEditPointOptions) {
super(g, options);
this.name = LineBazierCurveEditPlugin.Name;
}
}
export const addWaypointConfig: MenuItemOptions = {
name: '添加路径点',
};
export const clearWaypointsConfig: MenuItemOptions = {
name: '清除所有路径点',
};
const LineEditMenu: ContextMenu = ContextMenu.init({
name: '区段编辑菜单',
groups: [
{
items: [addWaypointConfig, clearWaypointsConfig],
},
],
});
export class LinePointEditPlugin extends GraphicInteractionPlugin<Line> {
static Name = 'LinePointDrag';
drawAssistant: LineDraw;
constructor(app: IGraphicApp, da: LineDraw) {
super(LinePointEditPlugin.Name, app);
this.drawAssistant = da;
app.registerMenu(LineEditMenu);
}
static init(app: IGraphicApp, da: LineDraw) {
return new LinePointEditPlugin(app, da);
}
filter(...grahpics: JlGraphic[]): Line[] | undefined {
return grahpics.filter((g) => g.type == Line.Type) as Line[];
}
bind(g: Line): void {
g.lineGraphic.eventMode = 'static';
g.lineGraphic.cursor = 'pointer';
g.lineGraphic.hitArea = new LineGraphicHitArea(g);
g.transformSave = true;
g.on('selected', this.onSelected, this);
g.on('unselected', this.onUnselected, this);
g.on('transformstart', this.onDragMove, this);
g.on('_rightclick', this.onContextMenu, this);
}
unbind(g: Line): void {
g.off('selected', this.onSelected, this);
g.off('unselected', this.onUnselected, this);
g.off('transformstart', this.onDragMove, this);
g.off('_rightclick', this.onContextMenu, this);
}
onContextMenu(e: FederatedMouseEvent) {
const target = e.target as DisplayObject;
const line = target.getGraphic() as Line;
this.app.updateSelected(line);
const p = line.screenToLocalPoint(e.global);
addWaypointConfig.handler = () => {
const linePoints = line.linePoints;
const { start, end } = getWayLineIndex(linePoints, p);
addWayPoint(line, false, start, end, p);
};
clearWaypointsConfig.handler = () => {
clearWayPoint(line, false);
};
LineEditMenu.open(e.global);
}
onSelected(g: DisplayObject): void {
const line = g as Line;
if (line.datas.isCurve) {
let lep = line.getAssistantAppend<LineBazierCurveEditPlugin>(
LineBazierCurveEditPlugin.Name
);
if (!lep) {
lep = new LineBazierCurveEditPlugin(line, {
onEditPointCreate,
});
line.addAssistantAppend(lep);
}
lep.showAll();
} else {
let lep = line.getAssistantAppend<LinePolylineEditPlugin>(
LinePolylineEditPlugin.Name
);
if (!lep) {
lep = new LinePolylineEditPlugin(line, { onEditPointCreate });
line.addAssistantAppend(lep);
}
lep.showAll();
}
}
onUnselected(g: DisplayObject): void {
const line = g as Line;
if (line.datas.isCurve) {
const lep = line.getAssistantAppend<LineBazierCurveEditPlugin>(
LineBazierCurveEditPlugin.Name
);
if (lep) {
lep.hideAll();
}
} else {
const lep = line.getAssistantAppend<LinePolylineEditPlugin>(
LinePolylineEditPlugin.Name
);
if (lep) {
lep.hideAll();
}
}
}
onDragMove(e: GraphicTransformEvent) {
const line = e.target as Line;
this.app.setOptions({
absorbablePositions: buildDragMoveAbsorbablePositions(line),
});
}
}
type dragType = Line;
function buildDragMoveAbsorbablePositions(
target: dragType
): AbsorbablePosition[] {
const aps: AbsorbablePosition[] = [];
const lines = target.queryStore.queryByType<Line>(Line.Type);
lines.forEach((line) => {
if (line.id !== target.id) {
line.localToCanvasPoints(...line.datas.points).forEach((p) => {
aps.push(new DragMoveAbsorbablePoint(p)); //区段端点
});
}
});
return aps;
}
class DragMoveAbsorbablePoint extends AbsorbablePoint {
moveTarget:
| {
position: IPointData;
portPos: IPointData[];
}
| undefined;
constructor(point: IPointData, absorbRange = 15) {
super(point, absorbRange);
}
tryAbsorb(...dragTargets: dragType[]): void {
const dragTarget = dragTargets[0];
if (this.moveTarget == undefined) {
this.moveTarget = {
position: dragTarget
.getGraphicApp()
.toCanvasCoordinates(dragTarget.getGlobalPosition()),
portPos: [
dragTarget.localToCanvasPoint(dragTarget.getStartPoint()),
dragTarget.localToCanvasPoint(dragTarget.getEndPoint()),
],
};
}
dragTarget
.localToCanvasPoints(...dragTarget.datas.points)
.forEach((p, i) => {
if (
distance(this._point.x, this._point.y, p.x, p.y) < this.absorbRange &&
this.moveTarget
) {
dragTarget.updatePositionByCanvasPosition(
new Point(
this.moveTarget.position.x +
this._point.x -
this.moveTarget.portPos[i].x,
this.moveTarget.position.y +
this._point.y -
this.moveTarget.portPos[i].y
)
);
}
});
}
}

View File

@ -0,0 +1,42 @@
import { calculateDistanceFromPointToLine, calculateFootPointFromPointToLine } from "jl-graphic";
import { IPointData } from 'pixi.js';
//获取所选线段的索引
export function getWayLineIndex(
points: IPointData[],
p: IPointData
): { start: number; end: number } {
let start = 0;
let end = 0;
let minDistance = 0;
for (let i = 1; i < points.length; i++) {
const sp = points[i - 1];
const ep = points[i];
let distance = calculateDistanceFromPointToLine(sp, ep, p);
distance = Math.round(distance * 100) / 100;
if (i == 1) {
minDistance = distance;
}
if (distance == minDistance) {
const minX = Math.min(sp.x, ep.x);
const maxX = Math.max(sp.x, ep.x);
const minY = Math.min(sp.y, ep.y);
const maxY = Math.max(sp.y, ep.y);
const point = calculateFootPointFromPointToLine(sp, ep, p);
if (
point.x >= minX &&
point.x <= maxX &&
point.y >= minY &&
point.y <= maxY
) {
start = i - 1;
}
}
if (distance < minDistance) {
minDistance = distance;
start = i - 1;
}
}
end = start + 1;
return { start, end };
}

View File

@ -0,0 +1,51 @@
import { Graphics, IPointData } from 'pixi.js';
import { assertBezierPoints, convertToBezierParams } from 'jl-graphic';
export class LineGraphic extends Graphics {
static Type = 'LineGraphic';
private _points: IPointData[] = [];
public get points(): IPointData[] {
return this._points;
}
public set points(value: IPointData[]) {
if (!this.isCurve) {
if (value.length < 2) {
throw Error('Polyline must have at least 2 points');
}
} else {
assertBezierPoints(value);
}
this._points = value;
}
private _segmentsCount = 10;
public get segmentsCount(): number {
return this._segmentsCount;
}
public set segmentsCount(value: number) {
if (value < 1) {
throw Error('segmentsCount must be at least 1');
}
this._segmentsCount = value;
}
isCurve = false;
constructor() {
super();
}
paint() {
if (this.isCurve) {
const bps = convertToBezierParams(this.points);
bps.forEach((bp) => {
this.drawBezierCurve(bp.p1, bp.p2, bp.cp1, bp.cp2, this.segmentsCount);
});
} else {
this.moveTo(this.points[0].x, this.points[0].y);
for (let i = 1; i < this.points.length; i++) {
this.lineTo(this.points[i].x, this.points[i].y);
}
}
}
}

View File

@ -258,6 +258,7 @@ import { saveDrawDatas } from 'src/drawApp/iscsApp';
import { saveDrawToServer } from 'src/drawApp/commonApp';
import { sync_data_message } from 'src/protos/sync_data_message';
import { useAuthStore } from 'src/stores/auth-store';
import { Line } from 'src/graphics/line/Line';
const $q = useQuasar();
const route = useRoute();
@ -365,6 +366,7 @@ onMounted(() => {
Arrow.Type,
TextContent.Type,
Rect.Type,
Line.Type,
CCTVButton.Type,
];
drawAssistantsTypes.forEach((type) => {
@ -554,7 +556,7 @@ function handleRecordData(op) {
function forceReloadDate() {
const drawApp = drawStore.getDrawApp();
const graphics = drawApp.queryStore.getAllGraphics();
graphics.forEach((graphic) => (graphic.visible = false));
drawApp.deleteGraphics(...graphics);
drawApp.updateSelected();
drawApp.forceReload();
}

View File

@ -721,6 +721,214 @@ export namespace iscsGraphicData {
return Rect.deserialize(bytes);
}
}
export class Line extends pb_1.Message {
#one_of_decls: number[][] = [];
constructor(data?: any[] | {
common?: dependency_1.common.CommonInfo;
code?: string;
points?: dependency_1.common.Point[];
isCurve?: boolean;
segmentsCount?: number;
lineWidth?: number;
lineColor?: string;
}) {
super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [3], this.#one_of_decls);
if (!Array.isArray(data) && typeof data == "object") {
if ("common" in data && data.common != undefined) {
this.common = data.common;
}
if ("code" in data && data.code != undefined) {
this.code = data.code;
}
if ("points" in data && data.points != undefined) {
this.points = data.points;
}
if ("isCurve" in data && data.isCurve != undefined) {
this.isCurve = data.isCurve;
}
if ("segmentsCount" in data && data.segmentsCount != undefined) {
this.segmentsCount = data.segmentsCount;
}
if ("lineWidth" in data && data.lineWidth != undefined) {
this.lineWidth = data.lineWidth;
}
if ("lineColor" in data && data.lineColor != undefined) {
this.lineColor = data.lineColor;
}
}
}
get common() {
return pb_1.Message.getWrapperField(this, dependency_1.common.CommonInfo, 1) as dependency_1.common.CommonInfo;
}
set common(value: dependency_1.common.CommonInfo) {
pb_1.Message.setWrapperField(this, 1, value);
}
get has_common() {
return pb_1.Message.getField(this, 1) != null;
}
get code() {
return pb_1.Message.getFieldWithDefault(this, 2, "") as string;
}
set code(value: string) {
pb_1.Message.setField(this, 2, value);
}
get points() {
return pb_1.Message.getRepeatedWrapperField(this, dependency_1.common.Point, 3) as dependency_1.common.Point[];
}
set points(value: dependency_1.common.Point[]) {
pb_1.Message.setRepeatedWrapperField(this, 3, value);
}
get isCurve() {
return pb_1.Message.getFieldWithDefault(this, 4, false) as boolean;
}
set isCurve(value: boolean) {
pb_1.Message.setField(this, 4, value);
}
get segmentsCount() {
return pb_1.Message.getFieldWithDefault(this, 5, 0) as number;
}
set segmentsCount(value: number) {
pb_1.Message.setField(this, 5, value);
}
get lineWidth() {
return pb_1.Message.getFieldWithDefault(this, 6, 0) as number;
}
set lineWidth(value: number) {
pb_1.Message.setField(this, 6, value);
}
get lineColor() {
return pb_1.Message.getFieldWithDefault(this, 7, "") as string;
}
set lineColor(value: string) {
pb_1.Message.setField(this, 7, value);
}
static fromObject(data: {
common?: ReturnType<typeof dependency_1.common.CommonInfo.prototype.toObject>;
code?: string;
points?: ReturnType<typeof dependency_1.common.Point.prototype.toObject>[];
isCurve?: boolean;
segmentsCount?: number;
lineWidth?: number;
lineColor?: string;
}): Line {
const message = new Line({});
if (data.common != null) {
message.common = dependency_1.common.CommonInfo.fromObject(data.common);
}
if (data.code != null) {
message.code = data.code;
}
if (data.points != null) {
message.points = data.points.map(item => dependency_1.common.Point.fromObject(item));
}
if (data.isCurve != null) {
message.isCurve = data.isCurve;
}
if (data.segmentsCount != null) {
message.segmentsCount = data.segmentsCount;
}
if (data.lineWidth != null) {
message.lineWidth = data.lineWidth;
}
if (data.lineColor != null) {
message.lineColor = data.lineColor;
}
return message;
}
toObject() {
const data: {
common?: ReturnType<typeof dependency_1.common.CommonInfo.prototype.toObject>;
code?: string;
points?: ReturnType<typeof dependency_1.common.Point.prototype.toObject>[];
isCurve?: boolean;
segmentsCount?: number;
lineWidth?: number;
lineColor?: string;
} = {};
if (this.common != null) {
data.common = this.common.toObject();
}
if (this.code != null) {
data.code = this.code;
}
if (this.points != null) {
data.points = this.points.map((item: dependency_1.common.Point) => item.toObject());
}
if (this.isCurve != null) {
data.isCurve = this.isCurve;
}
if (this.segmentsCount != null) {
data.segmentsCount = this.segmentsCount;
}
if (this.lineWidth != null) {
data.lineWidth = this.lineWidth;
}
if (this.lineColor != null) {
data.lineColor = this.lineColor;
}
return data;
}
serialize(): Uint8Array;
serialize(w: pb_1.BinaryWriter): void;
serialize(w?: pb_1.BinaryWriter): Uint8Array | void {
const writer = w || new pb_1.BinaryWriter();
if (this.has_common)
writer.writeMessage(1, this.common, () => this.common.serialize(writer));
if (this.code.length)
writer.writeString(2, this.code);
if (this.points.length)
writer.writeRepeatedMessage(3, this.points, (item: dependency_1.common.Point) => item.serialize(writer));
if (this.isCurve != false)
writer.writeBool(4, this.isCurve);
if (this.segmentsCount != 0)
writer.writeInt32(5, this.segmentsCount);
if (this.lineWidth != 0)
writer.writeInt32(6, this.lineWidth);
if (this.lineColor.length)
writer.writeString(7, this.lineColor);
if (!w)
return writer.getResultBuffer();
}
static deserialize(bytes: Uint8Array | pb_1.BinaryReader): Line {
const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new Line();
while (reader.nextField()) {
if (reader.isEndGroup())
break;
switch (reader.getFieldNumber()) {
case 1:
reader.readMessage(message.common, () => message.common = dependency_1.common.CommonInfo.deserialize(reader));
break;
case 2:
message.code = reader.readString();
break;
case 3:
reader.readMessage(message.points, () => pb_1.Message.addToRepeatedWrapperField(message, 3, dependency_1.common.Point.deserialize(reader), dependency_1.common.Point));
break;
case 4:
message.isCurve = reader.readBool();
break;
case 5:
message.segmentsCount = reader.readInt32();
break;
case 6:
message.lineWidth = reader.readInt32();
break;
case 7:
message.lineColor = reader.readString();
break;
default: reader.skipField();
}
}
return message;
}
serializeBinary(): Uint8Array {
return this.serialize();
}
static deserializeBinary(bytes: Uint8Array): Line {
return Line.deserialize(bytes);
}
}
export class CCTVButton extends pb_1.Message {
#one_of_decls: number[][] = [];
constructor(data?: any[] | {
@ -1318,9 +1526,10 @@ export namespace iscsGraphicData {
iscsTexts?: IscsText[];
rects?: Rect[];
cctvButtons?: CCTVButton[];
lines?: Line[];
}) {
super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [3, 4, 5, 6], this.#one_of_decls);
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [3, 4, 5, 6, 7], this.#one_of_decls);
if (!Array.isArray(data) && typeof data == "object") {
if ("stationName" in data && data.stationName != undefined) {
this.stationName = data.stationName;
@ -1340,6 +1549,9 @@ export namespace iscsGraphicData {
if ("cctvButtons" in data && data.cctvButtons != undefined) {
this.cctvButtons = data.cctvButtons;
}
if ("lines" in data && data.lines != undefined) {
this.lines = data.lines;
}
}
}
get stationName() {
@ -1381,6 +1593,12 @@ export namespace iscsGraphicData {
set cctvButtons(value: CCTVButton[]) {
pb_1.Message.setRepeatedWrapperField(this, 6, value);
}
get lines() {
return pb_1.Message.getRepeatedWrapperField(this, Line, 7) as Line[];
}
set lines(value: Line[]) {
pb_1.Message.setRepeatedWrapperField(this, 7, value);
}
static fromObject(data: {
stationName?: string;
canvas?: ReturnType<typeof dependency_1.common.Canvas.prototype.toObject>;
@ -1388,6 +1606,7 @@ export namespace iscsGraphicData {
iscsTexts?: ReturnType<typeof IscsText.prototype.toObject>[];
rects?: ReturnType<typeof Rect.prototype.toObject>[];
cctvButtons?: ReturnType<typeof CCTVButton.prototype.toObject>[];
lines?: ReturnType<typeof Line.prototype.toObject>[];
}): CCTVOfStationControlStorage {
const message = new CCTVOfStationControlStorage({});
if (data.stationName != null) {
@ -1408,6 +1627,9 @@ export namespace iscsGraphicData {
if (data.cctvButtons != null) {
message.cctvButtons = data.cctvButtons.map(item => CCTVButton.fromObject(item));
}
if (data.lines != null) {
message.lines = data.lines.map(item => Line.fromObject(item));
}
return message;
}
toObject() {
@ -1418,6 +1640,7 @@ export namespace iscsGraphicData {
iscsTexts?: ReturnType<typeof IscsText.prototype.toObject>[];
rects?: ReturnType<typeof Rect.prototype.toObject>[];
cctvButtons?: ReturnType<typeof CCTVButton.prototype.toObject>[];
lines?: ReturnType<typeof Line.prototype.toObject>[];
} = {};
if (this.stationName != null) {
data.stationName = this.stationName;
@ -1437,6 +1660,9 @@ export namespace iscsGraphicData {
if (this.cctvButtons != null) {
data.cctvButtons = this.cctvButtons.map((item: CCTVButton) => item.toObject());
}
if (this.lines != null) {
data.lines = this.lines.map((item: Line) => item.toObject());
}
return data;
}
serialize(): Uint8Array;
@ -1455,6 +1681,8 @@ export namespace iscsGraphicData {
writer.writeRepeatedMessage(5, this.rects, (item: Rect) => item.serialize(writer));
if (this.cctvButtons.length)
writer.writeRepeatedMessage(6, this.cctvButtons, (item: CCTVButton) => item.serialize(writer));
if (this.lines.length)
writer.writeRepeatedMessage(7, this.lines, (item: Line) => item.serialize(writer));
if (!w)
return writer.getResultBuffer();
}
@ -1482,6 +1710,9 @@ export namespace iscsGraphicData {
case 6:
reader.readMessage(message.cctvButtons, () => pb_1.Message.addToRepeatedWrapperField(message, 6, CCTVButton.deserialize(reader), CCTVButton));
break;
case 7:
reader.readMessage(message.lines, () => pb_1.Message.addToRepeatedWrapperField(message, 7, Line.deserialize(reader), Line));
break;
default: reader.skipField();
}
}
@ -1502,9 +1733,10 @@ export namespace iscsGraphicData {
arrows?: Arrow[];
iscsTexts?: IscsText[];
rects?: Rect[];
lines?: Line[];
}) {
super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [3, 4, 5], this.#one_of_decls);
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [3, 4, 5, 6], this.#one_of_decls);
if (!Array.isArray(data) && typeof data == "object") {
if ("stationName" in data && data.stationName != undefined) {
this.stationName = data.stationName;
@ -1521,6 +1753,9 @@ export namespace iscsGraphicData {
if ("rects" in data && data.rects != undefined) {
this.rects = data.rects;
}
if ("lines" in data && data.lines != undefined) {
this.lines = data.lines;
}
}
}
get stationName() {
@ -1556,12 +1791,19 @@ export namespace iscsGraphicData {
set rects(value: Rect[]) {
pb_1.Message.setRepeatedWrapperField(this, 5, value);
}
get lines() {
return pb_1.Message.getRepeatedWrapperField(this, Line, 6) as Line[];
}
set lines(value: Line[]) {
pb_1.Message.setRepeatedWrapperField(this, 6, value);
}
static fromObject(data: {
stationName?: string;
canvas?: ReturnType<typeof dependency_1.common.Canvas.prototype.toObject>;
arrows?: ReturnType<typeof Arrow.prototype.toObject>[];
iscsTexts?: ReturnType<typeof IscsText.prototype.toObject>[];
rects?: ReturnType<typeof Rect.prototype.toObject>[];
lines?: ReturnType<typeof Line.prototype.toObject>[];
}): FASPlatformAlarmStorage {
const message = new FASPlatformAlarmStorage({});
if (data.stationName != null) {
@ -1579,6 +1821,9 @@ export namespace iscsGraphicData {
if (data.rects != null) {
message.rects = data.rects.map(item => Rect.fromObject(item));
}
if (data.lines != null) {
message.lines = data.lines.map(item => Line.fromObject(item));
}
return message;
}
toObject() {
@ -1588,6 +1833,7 @@ export namespace iscsGraphicData {
arrows?: ReturnType<typeof Arrow.prototype.toObject>[];
iscsTexts?: ReturnType<typeof IscsText.prototype.toObject>[];
rects?: ReturnType<typeof Rect.prototype.toObject>[];
lines?: ReturnType<typeof Line.prototype.toObject>[];
} = {};
if (this.stationName != null) {
data.stationName = this.stationName;
@ -1604,6 +1850,9 @@ export namespace iscsGraphicData {
if (this.rects != null) {
data.rects = this.rects.map((item: Rect) => item.toObject());
}
if (this.lines != null) {
data.lines = this.lines.map((item: Line) => item.toObject());
}
return data;
}
serialize(): Uint8Array;
@ -1620,6 +1869,8 @@ export namespace iscsGraphicData {
writer.writeRepeatedMessage(4, this.iscsTexts, (item: IscsText) => item.serialize(writer));
if (this.rects.length)
writer.writeRepeatedMessage(5, this.rects, (item: Rect) => item.serialize(writer));
if (this.lines.length)
writer.writeRepeatedMessage(6, this.lines, (item: Line) => item.serialize(writer));
if (!w)
return writer.getResultBuffer();
}
@ -1644,6 +1895,9 @@ export namespace iscsGraphicData {
case 5:
reader.readMessage(message.rects, () => pb_1.Message.addToRepeatedWrapperField(message, 5, Rect.deserialize(reader), Rect));
break;
case 6:
reader.readMessage(message.lines, () => pb_1.Message.addToRepeatedWrapperField(message, 6, Line.deserialize(reader), Line));
break;
default: reader.skipField();
}
}

View File

@ -198,7 +198,7 @@ export namespace sync_data_message {
pb_1.Message.setField(this, 2, value);
}
get data() {
return pb_1.Message.getFieldWithDefault(this, 3, new Uint8Array()) as Uint8Array;
return pb_1.Message.getFieldWithDefault(this, 3, new Uint8Array(0)) as Uint8Array;
}
set data(value: Uint8Array) {
pb_1.Message.setField(this, 3, value);