add attack arrow

This commit is contained in:
ethan 2023-08-16 16:17:33 +08:00
parent 252dbf4bcc
commit 599f047e0a
6 changed files with 271 additions and 53 deletions

View File

@ -1,16 +1,16 @@
import CesiumPlot from "../src";
import CesiumPlot from '../src';
// import CesiumPlot from "../dist/CesiumPlot";
import * as Cesium from "./cesium/index";
import * as Cesium from './cesium/index';
// let raster = new Cesium.ArcGisMapServerImageryProvider({
// url: "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer",
// });
let keda = new Cesium.UrlTemplateImageryProvider({
url: "https://10.68.8.41:9043/kmap-server/gridMap/tile/{z}/{y}/{x}",
url: 'https://10.68.8.41:9043/kmap-server/gridMap/tile/{z}/{y}/{x}',
// url: 'http://10.68.8.58:8080/3d/dom2/{z}/{x}/{y}.png'
});
const viewer = new Cesium.Viewer("cesiumContainer", {
const viewer = new Cesium.Viewer('cesiumContainer', {
animation: false,
shouldAnimate: true,
geocoder: false,
@ -34,6 +34,12 @@ viewer.scene.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(107.857, 35.594498, 8000000),
});
document.getElementById("drawStraightArrow").onclick = () => {
const fineArrow = document.getElementById('drawFineArrow') as HTMLElement;
fineArrow.onclick = () => {
new CesiumPlot.FineArrow(Cesium, viewer, {});
};
const attackArrow = document.getElementById('drawAttackArrow') as HTMLElement;
attackArrow.onclick = () => {
new CesiumPlot.AttackArrow(Cesium, viewer, {});
};

View File

@ -15,19 +15,41 @@
padding: 0;
overflow: hidden;
}
#drawStraightArrow {
/* #btn {
position: absolute;
top: 10px;
left: 10px;
} */
.button-container {
position: absolute;
top: 10px;
left: 10px;
display: flex; /* 使用 Flex 布局 */
justify-content: center; /* 水平居中对齐 */
}
.button {
margin: 0 10px; /* 按钮之间的间距 */
padding: 6px 10px; /* 按钮内边距 */
background-color: #3498db; /* 按钮背景颜色 */
color: #ffffff; /* 按钮文字颜色 */
border: none; /* 去除边框 */
border-radius: 5px; /* 圆角边框 */
cursor: pointer; /* 鼠标指针样式 */
}
</style>
</head>
<body>
<div id="root"></div>
<div id="cesiumContainer"></div>
<button id="drawStraightArrow" class="btn btn-success">直箭头</button>
<div class="button-container">
<button id="drawFineArrow" class="button">直箭头</button>
<button id="drawAttackArrow" class="button">进攻方向箭头</button>
</div>
<script>
window.CESIUM_BASE_URL = "./examples/cesium";
window.CESIUM_BASE_URL = './examples/cesium';
</script>
<script type="module" src="./examples/index.ts"></script>
</body>

139
src/arrow/attack-arrow.ts Normal file
View File

@ -0,0 +1,139 @@
import Draw from '../draw';
import * as Utils from '../utils';
import { Cartesian3 } from '@examples/cesium';
export default class AttackArrow extends Draw {
points: Cartesian3[] = [];
headHeightFactor: number;
headWidthFactor: number;
neckHeightFactor: number;
neckWidthFactor: number;
headTailFactor: number;
constructor(cesium: any, viewer: any, style: any) {
super(cesium, viewer);
this.cesium = cesium;
this.headHeightFactor = 0.18;
this.headWidthFactor = 0.3;
this.neckHeightFactor = 0.85;
this.neckWidthFactor = 0.15;
this.headTailFactor = 0.8;
this.setState('drawing');
this.onDoubleClick();
}
/**
* Add points only on click events
*/
addPoint(cartesian: Cartesian3) {
this.points.push(cartesian);
if (this.points.length < 2) {
this.onMouseMove();
} else if (this.points.length === 2) {
this.setGeometryPoints(this.points);
this.addToMap();
this.lineEntity && this.viewer.entities.remove(this.lineEntity);
} else {
console.error('click...');
}
}
/**
* Draw a shape based on mouse movement points during the initial drawing.
*/
updateMovingPoint(cartesian: Cartesian3) {
const tempPoints = [...this.points, cartesian];
//
this.setGeometryPoints(tempPoints);
if (tempPoints.length === 2) {
this.drawLine();
} else {
const geometryPoints = this.createPolygon(tempPoints);
this.setGeometryPoints(geometryPoints);
this.addToMap();
}
}
/**
* Generate geometric shapes based on key points.
*/
createPolygon(positions: Cartesian3[]): Cartesian3[] {
const lnglatPoints = positions.map((pnt) => {
return this.cartesianToLnglat(pnt);
});
let [tailLeft, tailRight] = [lnglatPoints[0], lnglatPoints[1]];
if (Utils.isClockWise(lnglatPoints[0], lnglatPoints[1], lnglatPoints[2])) {
tailLeft = lnglatPoints[1];
tailRight = lnglatPoints[0];
}
const midTail = Utils.Mid(tailLeft, tailRight);
const bonePnts = [midTail].concat(lnglatPoints.slice(2));
const headPnts = this.getArrowHeadPoints(bonePnts, tailLeft, tailRight);
const [neckLeft, neckRight] = [headPnts[0], headPnts[4]];
const tailWidthFactor = Utils.MathDistance(tailLeft, tailRight) / Utils.getBaseLength(bonePnts);
const bodyPnts = this.getArrowBodyPoints(bonePnts, neckLeft, neckRight, tailWidthFactor);
const count = bodyPnts.length;
let leftPnts = [tailLeft].concat(bodyPnts.slice(0, count / 2));
leftPnts.push(neckLeft);
let rightPnts = [tailRight].concat(bodyPnts.slice(count / 2, count));
rightPnts.push(neckRight);
leftPnts = Utils.getQBSplinePoints(leftPnts);
rightPnts = Utils.getQBSplinePoints(rightPnts);
const points = leftPnts.concat(headPnts, rightPnts.reverse());
const temp = [].concat(...points);
const cartesianPoints = this.cesium.Cartesian3.fromDegreesArray(temp);
return cartesianPoints;
}
getPoints() {
return this.points;
}
getArrowHeadPoints(points, tailLeft, tailRight) {
try {
let len = Utils.getBaseLength(points);
let headHeight = len * this.headHeightFactor;
const headPnt = points[points.length - 1];
len = Utils.MathDistance(headPnt, points[points.length - 2]);
const tailWidth = Utils.MathDistance(tailLeft, tailRight);
if (headHeight > tailWidth * this.headTailFactor) {
headHeight = tailWidth * this.headTailFactor;
}
const headWidth = headHeight * this.headWidthFactor;
const neckWidth = headHeight * this.neckWidthFactor;
headHeight = headHeight > len ? len : headHeight;
const neckHeight = headHeight * this.neckHeightFactor;
const headEndPnt = Utils.getThirdPoint(points[points.length - 2], headPnt, 0, headHeight, true);
const neckEndPnt = Utils.getThirdPoint(points[points.length - 2], headPnt, 0, neckHeight, true);
const headLeft = Utils.getThirdPoint(headPnt, headEndPnt, Math.PI / 2, headWidth, false);
const headRight = Utils.getThirdPoint(headPnt, headEndPnt, Math.PI / 2, headWidth, true);
const neckLeft = Utils.getThirdPoint(headPnt, neckEndPnt, Math.PI / 2, neckWidth, false);
const neckRight = Utils.getThirdPoint(headPnt, neckEndPnt, Math.PI / 2, neckWidth, true);
return [neckLeft, headLeft, headPnt, headRight, neckRight];
} catch (e) {
console.log(e);
}
}
getArrowBodyPoints(points, neckLeft, neckRight, tailWidthFactor) {
const allLen = Utils.wholeDistance(points);
const len = Utils.getBaseLength(points);
const tailWidth = len * tailWidthFactor;
const neckWidth = Utils.MathDistance(neckLeft, neckRight);
const widthDif = (tailWidth - neckWidth) / 2;
// eslint-disable-next-line
let [tempLen, leftBodyPnts, rightBodyPnts] = [0, [], []];
for (let i = 1; i < points.length - 1; i++) {
const angle = Utils.getAngleOfThreePoints(points[i - 1], points[i], points[i + 1]) / 2;
tempLen += Utils.MathDistance(points[i - 1], points[i]);
const w = (tailWidth / 2 - (tempLen / allLen) * widthDif) / Math.sin(angle);
const left = Utils.getThirdPoint(points[i - 1], points[i], Math.PI - angle, w, true);
const right = Utils.getThirdPoint(points[i - 1], points[i], angle, w, false);
leftBodyPnts.push(left);
rightBodyPnts.push(right);
}
return leftBodyPnts.concat(rightBodyPnts);
}
}

View File

@ -4,10 +4,22 @@ import { Cartesian3 } from '@examples/cesium';
export default class FineArrow extends Draw {
points: Cartesian3[] = [];
arrowLengthScale: number = 5;
maxArrowLength: number = 2;
tailWidthFactor: number;
neckWidthFactor: number;
headWidthFactor: number;
headAngle: number;
neckAngle: number;
constructor(cesium: any, viewer: any, style: any) {
super(cesium, viewer);
this.cesium = cesium;
this.tailWidthFactor = 0.1;
this.neckWidthFactor = 0.2;
this.headWidthFactor = 0.25;
this.headAngle = Math.PI / 8.5;
this.neckAngle = Math.PI / 13;
this.setState('drawing');
}
@ -15,24 +27,32 @@ export default class FineArrow extends Draw {
* Add points only on click events
*/
addPoint(cartesian: Cartesian3) {
this.points.push();
if (this.points.length === 0) {
this.points[0] = cartesian;
if (this.points.length < 2) {
this.points.push(cartesian);
this.onMouseMove();
}
if (this.points.length === 2) {
const geometryPoints = this.createPolygon(this.points);
this.setGeometryPoints(geometryPoints);
this.addToMap();
this.removeMoveListener();
this.setState('static');
this.finishDrawing();
}
}
/**
* Update the last point as the mouse moves.
* Draw a shape based on mouse movement points during the initial drawing.
*/
updateMovingPoint(cartesian: Cartesian3, index: number) {
updateMovingPoint(cartesian: Cartesian3) {
const tempPoints = [...this.points, cartesian];
const geometryPoints = this.createPolygon(tempPoints);
this.setGeometryPoints(geometryPoints);
this.addToMap();
}
/**
* In edit mode, drag key points to update corresponding key point data.
*/
updateDraggingPoint(cartesian: Cartesian3, index: number) {
this.points[index] = cartesian;
const geometryPoints = this.createPolygon(this.points);
this.setGeometryPoints(geometryPoints);

View File

@ -5,28 +5,18 @@ import { State } from './interface';
export default class Draw {
cesium: typeof CesiumTypeOnly;
viewer: CesiumTypeOnly.Viewer;
arrowLengthScale: number = 5;
maxArrowLength: number = 2;
tailWidthFactor: number;
neckWidthFactor: number;
headWidthFactor: number;
headAngle: number;
neckAngle: number;
eventHandler: CesiumTypeOnly.ScreenSpaceEventHandler;
entity: CesiumTypeOnly.Entity;
polygonEntity: CesiumTypeOnly.Entity;
geometryPoints: CesiumTypeOnly.Cartesian3[] = [];
state: State = 'drawing';
controlPoints: CesiumTypeOnly.EntityCollection;
controlPointsEventHandler: CesiumTypeOnly.ScreenSpaceEventHandler;
lineEntity: CesiumTypeOnly.Entity;
constructor(cesium: typeof CesiumTypeOnly, viewer: CesiumTypeOnly.Viewer) {
this.cesium = cesium;
this.viewer = viewer;
this.tailWidthFactor = 0.1;
this.neckWidthFactor = 0.2;
this.headWidthFactor = 0.25;
this.headAngle = Math.PI / 8.5;
this.neckAngle = Math.PI / 13;
this.cartesianToLnglat = this.cartesianToLnglat.bind(this);
this.pixelToCartesian = this.pixelToCartesian.bind(this);
this.onClick();
@ -52,12 +42,18 @@ export default class Draw {
*/
onClick() {
this.eventHandler = new this.cesium.ScreenSpaceEventHandler(this.viewer.canvas);
this.eventHandler.setInputAction((evt) => {
this.eventHandler.setInputAction((evt: any) => {
const pickedObject = this.viewer.scene.pick(evt.position);
const hitEntities = this.cesium.defined(pickedObject) && pickedObject.id instanceof CesiumTypeOnly.Entity;
if (this.state === 'drawing') {
// In the drawing state, the points clicked are key nodes of the shape, and they are saved in this.points.
const cartesian = this.pixelToCartesian(evt.position);
const points = this.getPoints();
// If clicked outside the sphere or if the distance between the current click position and
// the previous click position is less than 10 meters, it is considered an invalid point.
if (!cartesian || (points.length > 0 && !this.checkDistance(cartesian, points[points.length - 1]))) {
return;
}
this.addPoint(cartesian);
} else if (this.state === 'edit') {
//In edit mode, exiting the edit state and deleting control points when clicking in the blank area.
@ -79,22 +75,38 @@ export default class Draw {
}
onMouseMove() {
this.eventHandler.setInputAction((evt) => {
this.drawingWhileMoving(evt.endPosition, 1);
this.eventHandler.setInputAction((evt: any) => {
const points = this.getPoints();
const cartesian = this.pixelToCartesian(evt.endPosition);
if (!cartesian) {
return;
}
if (this.checkDistance(cartesian, points[points.length - 1])) {
// Synchronize data to subclasses.If the distance is less than 10 meters, do not proceed
this.updateMovingPoint(cartesian, points.length);
}
}, this.cesium.ScreenSpaceEventType.MOUSE_MOVE);
}
drawingWhileMoving(position: CesiumTypeOnly.Cartesian2, index: number) {
const cartesian = this.pixelToCartesian(position);
const lnglat = this.cartesianToLnglat(cartesian);
const points = this.getPoints();
const lastPoint = this.cartesianToLnglat(points[points.length - 1]);
const distance = Utils.MathDistance(lnglat, lastPoint);
if (distance < 0.0001) {
return false;
onDoubleClick() {
// this.eventHandler = new this.cesium.ScreenSpaceEventHandler(this.viewer.canvas);
this.eventHandler.setInputAction((evt: any) => {
console.error('db click...');
this.finishDrawing();
}, this.cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
}
// Synchronize data to subclasses.
this.updateMovingPoint(cartesian, index);
/**
* Check if the distance between two points is greater than 10 meters.
*/
checkDistance(cartesian1: CesiumTypeOnly.Cartesian3, cartesian2: CesiumTypeOnly.Cartesian3) {
const distance = this.cesium.Cartesian3.distance(cartesian1, cartesian2);
return distance > 10;
}
finishDrawing() {
this.removeMoveListener();
this.setState('static');
}
removeClickListener() {
@ -117,8 +129,8 @@ export default class Draw {
const callback = () => {
return new this.cesium.PolygonHierarchy(this.geometryPoints);
};
if (!this.entity) {
this.entity = this.viewer.entities.add({
if (!this.polygonEntity) {
this.polygonEntity = this.viewer.entities.add({
polygon: new this.cesium.PolygonGraphics({
hierarchy: new this.cesium.CallbackProperty(callback, false),
show: true,
@ -129,6 +141,19 @@ export default class Draw {
}
}
drawLine() {
if (!this.lineEntity) {
this.lineEntity = this.viewer.entities.add({
polyline: {
positions: new this.cesium.CallbackProperty(() => this.geometryPoints, false),
width: 5,
// material: this.cesium.Color.RED,
clampToGround: true,
},
});
}
}
cartesianToLnglat(cartesian: CesiumTypeOnly.Cartesian3): number[] {
const lnglat = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian);
const lat = this.cesium.Math.toDegrees(lnglat.latitude);
@ -161,7 +186,7 @@ export default class Draw {
this.controlPointsEventHandler = new this.cesium.ScreenSpaceEventHandler(this.viewer.canvas);
// Listen for left mouse button press events
this.controlPointsEventHandler.setInputAction((clickEvent) => {
this.controlPointsEventHandler.setInputAction((clickEvent: any) => {
const pickedObject = this.viewer.scene.pick(clickEvent.position);
if (this.cesium.defined(pickedObject)) {
@ -179,12 +204,12 @@ export default class Draw {
}, this.cesium.ScreenSpaceEventType.LEFT_DOWN);
// Listen for mouse movement events
this.controlPointsEventHandler.setInputAction((moveEvent) => {
this.controlPointsEventHandler.setInputAction((moveEvent: any) => {
if (isDragging && draggedIcon) {
const cartesian = this.viewer.camera.pickEllipsoid(moveEvent.endPosition, this.viewer.scene.globe.ellipsoid);
if (cartesian) {
draggedIcon.position.setValue(cartesian);
this.drawingWhileMoving(moveEvent.endPosition, draggedIcon.index);
this.updateDraggingPoint(cartesian, draggedIcon.index);
}
}
}, this.cesium.ScreenSpaceEventType.MOUSE_MOVE);
@ -215,7 +240,11 @@ export default class Draw {
return this.cesium.Cartesian3();
}
updateMovingPoint(cartesian: CesiumTypeOnly.Cartesian3, index: number) {
updateMovingPoint(cartesian: CesiumTypeOnly.Cartesian3, index?: number) {
//Abstract method that must be implemented by subclasses.
}
updateDraggingPoint(cartesian: CesiumTypeOnly.Cartesian3, index: number) {
//Abstract method that must be implemented by subclasses.
}
}

View File

@ -1,7 +1,9 @@
import FineArrow from './arrow/fine-arrow'
import FineArrow from './arrow/fine-arrow';
import AttackArrow from './arrow/attack-arrow';
const CesiumPlot = {
FineArrow
}
const CesiumPlot = {
FineArrow,
AttackArrow,
};
export default CesiumPlot;