轨迹线调整

This commit is contained in:
fan 2023-06-28 13:41:18 +08:00
parent 16f535514d
commit 4e7d373031
14 changed files with 364 additions and 88 deletions

View File

@ -75,6 +75,9 @@
<train-window-property
v-else-if="drawStore.selectedGraphicType === TrainWindow.Type"
></train-window-property>
<path-line-property
v-else-if="drawStore.selectedGraphicType === PathLine.Type"
></path-line-property>
</q-card-section>
</template>
</q-card>
@ -100,6 +103,7 @@ import SignalProperty from './properties/SignalProperty.vue';
import TurnoutProperty from './properties/TurnoutProperty.vue';
import SectionProperty from './properties/SectionProperty.vue';
import RunLineProperty from './properties/RunLineProperty.vue';
import PathLineProperty from './properties/PathLineProperty.vue';
import { Link } from 'src/graphics/link/Link';
import { Rect } from 'src/graphics/rect/Rect';
import { Platform } from 'src/graphics/platform/Platform';
@ -113,6 +117,7 @@ import { Turnout } from 'src/graphics/turnout/Turnout';
import { RunLine } from 'src/graphics/runLine/RunLine';
import { Section } from 'src/graphics/section/Section';
import { TrainWindow } from 'src/graphics/trainWindow/TrainWindow';
import { PathLine } from 'src/graphics/pathLine/PathLine';
const drawStore = useDrawStore();
</script>

View File

@ -0,0 +1,70 @@
<template>
<q-form>
<q-input outlined readonly v-model="pathLineModel.id" label="id" hint="" />
<q-checkbox
v-model="pathLineModel.isUp"
label="是否上行"
@update:model-value="onUpdate"
/>
<template :key="item" v-for="item in pathLineModel.kilometerPoints">
<div style="display: flex; margin-top: 5px">
<q-input
outlined
v-model.number="item.point.x"
type="number"
@blur="onUpdate"
:label="`${item.stName}x:`"
/>
<q-input
outlined
v-model.number="item.point.y"
type="number"
@blur="onUpdate"
:label="`${item.stName}y:`"
/>
</div>
<div style="display: flex; margin-top: 5px">
<q-input outlined readonly v-model="item.stName" label="车站" hint="" />
<q-input
outlined
v-model.number="item.kilometer"
type="number"
@blur="onUpdate"
:label="`${item.stName}公里标:`"
/>
</div>
</template>
</q-form>
</template>
<script setup lang="ts">
import { PathLineData } from 'src/drawApp/graphics/PathLineInteraction';
import { PathLine } from 'src/graphics/pathLine/PathLine';
import { useDrawStore } from 'src/stores/draw-store';
import { reactive, onMounted, watch } from 'vue';
const drawStore = useDrawStore();
const pathLineModel = reactive(new PathLineData());
drawStore.$subscribe;
watch(
() => drawStore.selectedGraphic,
(val) => {
if (val && val.type == PathLine.Type) {
pathLineModel.copyFrom(val.saveData() as PathLineData);
}
}
);
onMounted(() => {
const pathLine = drawStore.selectedGraphic as PathLine;
if (pathLine) {
pathLineModel.copyFrom(pathLine.saveData());
}
});
function onUpdate() {
const pathLine = drawStore.selectedGraphic as PathLine;
if (pathLine) {
drawStore.getDrawApp().updateGraphicAndRecord(pathLine, pathLineModel);
}
}
</script>

View File

@ -142,7 +142,6 @@ onMounted(() => {
function onUpdate() {
const runLine = drawStore.selectedGraphic as RunLine;
console.log(runLineModel, '*******');
if (runLine) {
drawStore.getDrawApp().updateGraphicAndRecord(runLine, runLineModel);
}

View File

@ -16,6 +16,11 @@
lazy-rules
autogrow
/>
<q-checkbox
v-model="stationLineModel.hideName"
label="名称是否隐藏"
@update:model-value="onUpdate"
/>
<q-select
outlined
@blur="onUpdate"

View File

@ -39,6 +39,12 @@ export class PathLineData extends GraphicDataBase implements IPathLineData {
(p) => new graphicData.Point({ x: p.x, y: p.y })
);
}
get isUp(): boolean {
return this.data.isUp;
}
set isUp(v: boolean) {
this.data.isUp = v;
}
get kilometerPoints(): KilometerPoint[] {
return this.data.kilometerPoints;
}
@ -48,6 +54,7 @@ export class PathLineData extends GraphicDataBase implements IPathLineData {
new graphicData.KilometerPoint({
point: new graphicData.Point({ x: p.point.x, y: p.point.y }),
kilometer: p.kilometer,
stName: p.stName,
})
);
}

View File

@ -39,6 +39,12 @@ export class SignalData extends GraphicDataBase implements ISignalData {
set mirror(v: boolean) {
this.data.mirror = v;
}
get kilometer(): number {
return this.data.kilometer;
}
set kilometer(v: number) {
this.data.kilometer = v;
}
clone(): SignalData {
return new SignalData(this.data.cloneMessage());
}
@ -320,30 +326,30 @@ export class SignalOperateInteraction extends GraphicInteractionPlugin<Signal> {
signalCloseConfig.handler = () => {
signal.states.redOpen = true;
signal.states.greenOpen = false;
signal.chagneState();
signal.doRepaint();
};
signalOpenConfig.handler = () => {
signal.states.redOpen = false;
signal.states.greenOpen = true;
signal.chagneState();
signal.doRepaint();
};
signalFleetConfig.handler = () => {
signal.states.fleetMode = true;
signal.chagneState();
signal.doRepaint();
};
humanControlConfig.handler = () => {
signal.states.autoRouteDisable = true;
signal.chagneState();
signal.doRepaint();
};
logicConfig.handler = () => {
signal.states.extinguish = true;
signal.chagneState();
signal.doRepaint();
};
signalRedFlashConfig.handler = () => {
signal.states.redFlash = true;
signal.states.redOpen = false;
signal.states.greenOpen = false;
signal.chagneState();
signal.doRepaint();
};
SignalOperateMenu.open(e.global);

View File

@ -37,6 +37,12 @@ export class StationLineData
set hasTransfer(v: boolean) {
this.data.hasTransfer = v;
}
get hideName(): boolean {
return this.data.hideName;
}
set hideName(v: boolean) {
this.data.hideName = v;
}
clone(): StationLineData {
return new StationLineData(this.data.cloneMessage());
}

View File

@ -1,25 +1,25 @@
import { JlGraphic, GraphicData, JlGraphicTemplate } from 'src/jl-graphic';
import { Graphics, IPointData } from 'pixi.js';
import { Graphics, IPointData, Point } from 'pixi.js';
import { RunLine } from '../runLine/RunLine';
import { getDrawApp } from 'src/drawApp';
// calculateDistanceFromPointToLine
import {
calculateDistanceFromPointToLine,
calculateFootPointFromPointToLine,
distance,
} from 'src/jl-graphic';
import { calculateFootPointFromPointToLine, distance } from 'src/jl-graphic';
import { StationLine } from '../stationLine/StationLine';
export interface KilometerPoint {
get point(): IPointData;
set point(point: IPointData);
get kilometer(): number;
set kilometer(kilometer: number);
get stName(): string;
set stName(stName: string);
}
export interface IPathLineData extends GraphicData {
get code(): string;
set code(v: string);
get points(): IPointData[]; // 线坐标点
set points(points: IPointData[]);
get isUp(): boolean;
set isUp(v: boolean);
get kilometerPoints(): KilometerPoint[];
set kilometerPoints(kilometerPoints: KilometerPoint[]);
clone(): IPathLineData;
@ -93,21 +93,32 @@ export class PathLine extends JlGraphic {
generatePathLineKilometerPoints(stas: string[]) {
const kilometerPoints: KilometerPoint[] = [];
stas.forEach((stasId) => {
const sta = this.queryStore.queryById(stasId);
this.datas.points.some((p, index) => {
const sta = this.queryStore.queryById(stasId) as StationLine;
let fp: Point | null = null;
let verticalLength = 100;
this.datas.points.forEach((p, index) => {
if (index) {
const prep = this.datas.points[index - 1];
const fp = calculateFootPointFromPointToLine(prep, p, sta.position);
const fpn = calculateFootPointFromPointToLine(prep, p, sta.position);
const length = distance(prep.x, prep.y, p.x, p.y);
const fln = distance(fpn.x, fpn.y, sta.position.x, sta.position.y);
if (
distance(fp.x, fp.y, prep.x, prep.y) <= length &&
distance(fp.x, fp.y, p.x, p.y) <= length
distance(fpn.x, fpn.y, prep.x, prep.y) <= length &&
distance(fpn.x, fpn.y, p.x, p.y) <= length &&
fln <= verticalLength
) {
kilometerPoints.push({ point: fp, kilometer: 0 });
return true;
verticalLength = fln;
fp = fpn;
}
}
});
if (fp) {
kilometerPoints.push({
point: fp,
kilometer: 0,
stName: sta.datas.code,
});
}
});
this.datas.kilometerPoints = kilometerPoints;
}

View File

@ -1,7 +1,30 @@
import { GraphicDrawAssistant, JlDrawApp } from 'src/jl-graphic';
import { IPathLineData, PathLineTemplate } from './PathLine';
import { Point, Graphics, LINE_JOIN } from 'pixi.js';
import {
GraphicDrawAssistant,
JlDrawApp,
linePoint,
GraphicInteractionPlugin,
GraphicApp,
JlGraphic,
DraggablePoint,
GraphicTransformEvent,
calculateFootPointFromPointToLine,
calculateDistanceFromPointToLine,
distance,
} from 'src/jl-graphic';
import {
IPathLineData,
PathLineTemplate,
PathLine,
pathLineConsts,
} from './PathLine';
import {
Point,
Graphics,
LINE_JOIN,
IHitArea,
DisplayObject,
IPointData,
} from 'pixi.js';
export interface IPathLineDrawOptions {
newData: () => IPathLineData;
}
@ -16,7 +39,7 @@ export class PathLineDraw extends GraphicDrawAssistant<
constructor(app: JlDrawApp, template: PathLineTemplate) {
super(app, template, 'sym_o_horizontal_rule', '不展示');
this.container.addChild(this.graphic);
// PathLinePointsEditPlugin.init(app);
PathLinePointsEditPlugin.init(app);
}
clearCache(): void {
this.points = [];
@ -60,66 +83,134 @@ export class PathLineDraw extends GraphicDrawAssistant<
}
}
// export class PathLineGraphicHitArea implements IHitArea {
// pathLine: PathLine;
// constructor(pathLine: PathLine) {
// this.pathLine = pathLine;
// }
// contains(x: number, y: number): boolean {
// const p = new Point(x, y);
// for (let i = 1; i < this.pathLine.datas.points.length; i++) {
// const p1 = this.pathLine.datas.points[i - 1];
// const p2 = this.pathLine.datas.points[i];
// if (linePoint(p1, p2, p, pathLineConsts.pathLineWidth)) {
// return true;
// }
// }
// return false;
// }
// }
export class PathLineGraphicHitArea implements IHitArea {
pathLine: PathLine;
constructor(pathLine: PathLine) {
this.pathLine = pathLine;
}
contains(x: number, y: number): boolean {
const p = new Point(x, y);
for (let i = 1; i < this.pathLine.datas.points.length; i++) {
const p1 = this.pathLine.datas.points[i - 1];
const p2 = this.pathLine.datas.points[i];
if (linePoint(p1, p2, p, pathLineConsts.pathLineWidth)) {
return true;
}
}
return false;
}
}
// export class PathLinePointsEditPlugin extends GraphicInteractionPlugin<PathLine> {
// static Name = 'LinkPointsDrag';
// constructor(app: GraphicApp) {
// super(PathLinePointsEditPlugin.Name, app);
// }
// static init(app: GraphicApp): PathLinePointsEditPlugin {
// return new PathLinePointsEditPlugin(app);
// }
// filter(...grahpics: JlGraphic[]): PathLine[] | undefined {
// return grahpics.filter((g) => g.type == PathLine.Type) as PathLine[];
// }
// bind(g: PathLine): void {
// g.pathLine.eventMode = 'static';
// g.pathLine.cursor = 'pointer';
// g.pathLine.hitArea = new PathLineGraphicHitArea(g);
// g.on('selected', this.onSelected, this);
// g.on('unselected', this.onUnselected, this);
// }
// unbind(g: PathLine): void {
// g.off('selected', this.onSelected, this);
// g.off('unselected', this.onUnselected, this);
// }
export class PathLinePointsEditPlugin extends GraphicInteractionPlugin<PathLine> {
static Name = 'LinkPointsDrag';
constructor(app: GraphicApp) {
super(PathLinePointsEditPlugin.Name, app);
}
static init(app: GraphicApp): PathLinePointsEditPlugin {
return new PathLinePointsEditPlugin(app);
}
filter(...grahpics: JlGraphic[]): PathLine[] | undefined {
return grahpics.filter((g) => g.type == PathLine.Type) as PathLine[];
}
bind(g: PathLine): void {
g.pathLine.eventMode = 'static';
g.pathLine.cursor = 'pointer';
g.pathLine.draggable = false;
g.pathLine.scalable = false;
g.pathLine.rotatable = false;
g.pathLine.hitArea = new PathLineGraphicHitArea(g);
g.on('selected', this.onSelected, this);
g.on('unselected', this.onUnselected, this);
}
unbind(g: PathLine): void {
g.off('selected', this.onSelected, this);
g.off('unselected', this.onUnselected, this);
}
// onSelected(g: DisplayObject): void {
// const runLine = g as PathLine;
// let lep;
// lep = runLine.getAssistantAppend<PolylineEditPlugin>(
// PolylineEditPlugin.Name
// );
// if (!lep) {
// lep = new PolylineEditPlugin(runLine);
// runLine.addAssistantAppend(lep);
// }
// lep.showAll();
// }
// onUnselected(g: DisplayObject): void {
// const runLine = g as PathLine;
// const lep = runLine.getAssistantAppend<PolylineEditPlugin>(
// PolylineEditPlugin.Name
// );
// if (lep) {
// lep.hideAll();
// }
// }
// }
onSelected(g: DisplayObject): void {
const pathLine = g as PathLine;
pathLine.draggable = false;
pathLine.scalable = false;
pathLine.rotatable = false;
pathLine.datas.kilometerPoints.forEach((kp, index) => {
const dp = new DraggablePoint(kp.point);
dp.on(
'transforming',
(e: GraphicTransformEvent) => {
this.movePointOnLine(pathLine.datas.points, e);
},
this
);
dp.on(
'transformend',
(e: GraphicTransformEvent) => {
const kilometerPoints = [...pathLine.datas.kilometerPoints];
kilometerPoints[index] = {
point: new Point(e.target.position.x, e.target.position.y),
kilometer: kilometerPoints[index].kilometer,
name: kilometerPoints[index].name,
};
pathLine.datas.kilometerPoints = kilometerPoints;
},
this
);
pathLine.addChild(dp);
});
}
onUnselected(g: DisplayObject): void {
const pathLine = g as PathLine;
if (pathLine.children.length > 1) {
pathLine.removeChildren(1);
}
}
kpTransforming(e: GraphicTransformEvent) {
console.log(e, '----');
}
movePointOnLine(points: IPointData[], e: GraphicTransformEvent) {
let minDistance = Number.MAX_SAFE_INTEGER;
let fp = null;
const np = e.target.position;
for (let i = 0; i < points.length; i++) {
const distancep = distance(np.x, np.y, points[i].x, points[i].y);
if (i !== 0) {
const distanceF = calculateDistanceFromPointToLine(
points[i - 1],
points[i],
np
);
const fpn = calculateFootPointFromPointToLine(
points[i - 1],
points[i],
np
);
const distance1 = distance(
fpn.x,
fpn.y,
points[i - 1].x,
points[i - 1].y
);
const distance2 = distance(fpn.x, fpn.y, points[i].x, points[i].y);
const distance3 = distance(
points[i - 1].x,
points[i - 1].y,
points[i].x,
points[i].y
);
if (distancep < minDistance) {
minDistance = distancep;
fp = points[i];
}
if (distanceF < minDistance && distance1 + distance2 <= distance3 + 1) {
minDistance = distanceF;
fp = fpn;
}
} else {
minDistance = distancep;
fp = points[i];
}
}
if (fp) {
e.target.position.set(fp.x, fp.y);
}
}
}

View File

@ -215,6 +215,7 @@ export class RunLine extends JlGraphic {
const pathLineDown = pathLineDrawAssistant.quickCreate(pointsDown);
this.datas.upPathLineId = pathLineUp?.id || '';
this.datas.downPathLineId = pathLineDown?.id || '';
this.generatePathLineKilometerPoints();
}
getStartPoint(): IPointData {

View File

@ -15,6 +15,8 @@ export interface ISignalData extends GraphicData {
set code(v: string);
get mirror(): boolean;
set mirror(v: boolean);
get kilometer(): number;
set kilometer(v: number);
clone(): ISignalData;
copyFrom(data: ISignalData): void;
eq(other: ISignalData): boolean;

View File

@ -11,6 +11,9 @@ export interface IStationLineData extends GraphicData {
set code(v: string);
get hasTransfer(): boolean;
set hasTransfer(v: boolean);
get hideName(): boolean;
set hideName(v: boolean);
clone(): IStationLineData;
copyFrom(data: IStationLineData): void;
eq(other: IStationLineData): boolean;
@ -117,6 +120,7 @@ export class StationLine extends JlGraphic {
codeGraph.style.fill = stationConsts.codeColor;
codeGraph.setVectorFontSize(stationConsts.codeFontSize);
codeGraph.anchor.set(0.5);
codeGraph.visible = !this.datas.hideName;
if (this.datas.childTransforms?.length) {
const pos = this.datas.childTransforms[0].transform.position;
codeGraph.position.set(pos.x, pos.y);

View File

@ -1937,6 +1937,7 @@ export namespace graphicData {
common?: CommonInfo;
code?: string;
hasTransfer?: boolean;
hideName?: boolean;
}) {
super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
@ -1950,6 +1951,9 @@ export namespace graphicData {
if ("hasTransfer" in data && data.hasTransfer != undefined) {
this.hasTransfer = data.hasTransfer;
}
if ("hideName" in data && data.hideName != undefined) {
this.hideName = data.hideName;
}
}
}
get common() {
@ -1973,10 +1977,17 @@ export namespace graphicData {
set hasTransfer(value: boolean) {
pb_1.Message.setField(this, 3, value);
}
get hideName() {
return pb_1.Message.getFieldWithDefault(this, 4, false) as boolean;
}
set hideName(value: boolean) {
pb_1.Message.setField(this, 4, value);
}
static fromObject(data: {
common?: ReturnType<typeof CommonInfo.prototype.toObject>;
code?: string;
hasTransfer?: boolean;
hideName?: boolean;
}): StationLine {
const message = new StationLine({});
if (data.common != null) {
@ -1988,6 +1999,9 @@ export namespace graphicData {
if (data.hasTransfer != null) {
message.hasTransfer = data.hasTransfer;
}
if (data.hideName != null) {
message.hideName = data.hideName;
}
return message;
}
toObject() {
@ -1995,6 +2009,7 @@ export namespace graphicData {
common?: ReturnType<typeof CommonInfo.prototype.toObject>;
code?: string;
hasTransfer?: boolean;
hideName?: boolean;
} = {};
if (this.common != null) {
data.common = this.common.toObject();
@ -2005,6 +2020,9 @@ export namespace graphicData {
if (this.hasTransfer != null) {
data.hasTransfer = this.hasTransfer;
}
if (this.hideName != null) {
data.hideName = this.hideName;
}
return data;
}
serialize(): Uint8Array;
@ -2017,6 +2035,8 @@ export namespace graphicData {
writer.writeString(2, this.code);
if (this.hasTransfer != false)
writer.writeBool(3, this.hasTransfer);
if (this.hideName != false)
writer.writeBool(4, this.hideName);
if (!w)
return writer.getResultBuffer();
}
@ -2035,6 +2055,9 @@ export namespace graphicData {
case 3:
message.hasTransfer = reader.readBool();
break;
case 4:
message.hideName = reader.readBool();
break;
default: reader.skipField();
}
}
@ -2688,6 +2711,7 @@ export namespace graphicData {
common?: CommonInfo;
code?: string;
mirror?: boolean;
kilometer?: number;
}) {
super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
@ -2701,6 +2725,9 @@ export namespace graphicData {
if ("mirror" in data && data.mirror != undefined) {
this.mirror = data.mirror;
}
if ("kilometer" in data && data.kilometer != undefined) {
this.kilometer = data.kilometer;
}
}
}
get common() {
@ -2724,10 +2751,17 @@ export namespace graphicData {
set mirror(value: boolean) {
pb_1.Message.setField(this, 3, value);
}
get kilometer() {
return pb_1.Message.getFieldWithDefault(this, 4, 0) as number;
}
set kilometer(value: number) {
pb_1.Message.setField(this, 4, value);
}
static fromObject(data: {
common?: ReturnType<typeof CommonInfo.prototype.toObject>;
code?: string;
mirror?: boolean;
kilometer?: number;
}): Signal {
const message = new Signal({});
if (data.common != null) {
@ -2739,6 +2773,9 @@ export namespace graphicData {
if (data.mirror != null) {
message.mirror = data.mirror;
}
if (data.kilometer != null) {
message.kilometer = data.kilometer;
}
return message;
}
toObject() {
@ -2746,6 +2783,7 @@ export namespace graphicData {
common?: ReturnType<typeof CommonInfo.prototype.toObject>;
code?: string;
mirror?: boolean;
kilometer?: number;
} = {};
if (this.common != null) {
data.common = this.common.toObject();
@ -2756,6 +2794,9 @@ export namespace graphicData {
if (this.mirror != null) {
data.mirror = this.mirror;
}
if (this.kilometer != null) {
data.kilometer = this.kilometer;
}
return data;
}
serialize(): Uint8Array;
@ -2768,6 +2809,8 @@ export namespace graphicData {
writer.writeString(2, this.code);
if (this.mirror != false)
writer.writeBool(3, this.mirror);
if (this.kilometer != 0)
writer.writeFloat(4, this.kilometer);
if (!w)
return writer.getResultBuffer();
}
@ -2786,6 +2829,9 @@ export namespace graphicData {
case 3:
message.mirror = reader.readBool();
break;
case 4:
message.kilometer = reader.readFloat();
break;
default: reader.skipField();
}
}
@ -3255,6 +3301,7 @@ export namespace graphicData {
constructor(data?: any[] | {
point?: Point;
kilometer?: number;
stName?: string;
}) {
super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
@ -3265,6 +3312,9 @@ export namespace graphicData {
if ("kilometer" in data && data.kilometer != undefined) {
this.kilometer = data.kilometer;
}
if ("stName" in data && data.stName != undefined) {
this.stName = data.stName;
}
}
}
get point() {
@ -3282,9 +3332,16 @@ export namespace graphicData {
set kilometer(value: number) {
pb_1.Message.setField(this, 2, value);
}
get stName() {
return pb_1.Message.getFieldWithDefault(this, 3, "") as string;
}
set stName(value: string) {
pb_1.Message.setField(this, 3, value);
}
static fromObject(data: {
point?: ReturnType<typeof Point.prototype.toObject>;
kilometer?: number;
stName?: string;
}): KilometerPoint {
const message = new KilometerPoint({});
if (data.point != null) {
@ -3293,12 +3350,16 @@ export namespace graphicData {
if (data.kilometer != null) {
message.kilometer = data.kilometer;
}
if (data.stName != null) {
message.stName = data.stName;
}
return message;
}
toObject() {
const data: {
point?: ReturnType<typeof Point.prototype.toObject>;
kilometer?: number;
stName?: string;
} = {};
if (this.point != null) {
data.point = this.point.toObject();
@ -3306,6 +3367,9 @@ export namespace graphicData {
if (this.kilometer != null) {
data.kilometer = this.kilometer;
}
if (this.stName != null) {
data.stName = this.stName;
}
return data;
}
serialize(): Uint8Array;
@ -3316,6 +3380,8 @@ export namespace graphicData {
writer.writeMessage(1, this.point, () => this.point.serialize(writer));
if (this.kilometer != 0)
writer.writeFloat(2, this.kilometer);
if (this.stName.length)
writer.writeString(3, this.stName);
if (!w)
return writer.getResultBuffer();
}
@ -3331,6 +3397,9 @@ export namespace graphicData {
case 2:
message.kilometer = reader.readFloat();
break;
case 3:
message.stName = reader.readString();
break;
default: reader.skipField();
}
}

@ -1 +1 @@
Subproject commit fbe6c9e273d7cf6a54b8d61d5964c210921daf40
Subproject commit 6c684988299ebe2019da7687dd5b3104ef770a79