diff --git a/apps/zxtool-site/.dumi/global.css b/apps/zxtool-site/.dumi/global.css index f61ae2b..8c6e2ee 100644 --- a/apps/zxtool-site/.dumi/global.css +++ b/apps/zxtool-site/.dumi/global.css @@ -11,8 +11,12 @@ button { .column { flex-direction: column; } -.info-img { - box-shadow: 1px 1px 4px #00000010; +.resume-img-wrapper { + > img { + width: 49%; + height: auto; + box-shadow: 2px 2px 4px #00000020; + } } .hr-with-content { diff --git a/apps/zxtool-site/docs/blog/img/intersectSphere.jpg b/apps/zxtool-site/docs/blog/img/intersectSphere.jpg new file mode 100644 index 0000000..6d64c14 Binary files /dev/null and b/apps/zxtool-site/docs/blog/img/intersectSphere.jpg differ diff --git a/apps/zxtool-site/docs/blog/img/perspectiveMat.png b/apps/zxtool-site/docs/blog/img/perspectiveMat.png new file mode 100644 index 0000000..53556a6 Binary files /dev/null and b/apps/zxtool-site/docs/blog/img/perspectiveMat.png differ diff --git a/apps/zxtool-site/docs/blog/intersect.md b/apps/zxtool-site/docs/blog/intersect.md new file mode 100644 index 0000000..abbf6e7 --- /dev/null +++ b/apps/zxtool-site/docs/blog/intersect.md @@ -0,0 +1,153 @@ +--- +title: 相交相关的计算 +toc: content +group: WebGL +--- + +# 相交相关的计算 + +## 判断点是否在三角形内部 + +### 通过面积判断 + +- 如果点和三角形的三个顶点构成的三个三角形的面积与大三角形的面积相等, 则点在三角形内部 +- 这种方法可以不用事先判断点是否与三角形共面 + +```ts +export const pointInTriangleV3_1 = (triangle: Vector3[], P: Vector3) => { + const [A, B, C] = triangle + + const ab = B.clone().sub(A) + const bc = C.clone().sub(B) + const area2 = ab.clone().cross(bc).length() // 向量叉乘的模为构成三角形面积的两倍 + let area2_1 = 0 + + for (let i = 0; i < 3; i++) { + const P1 = triangle[i] + const P2 = triangle[(i + 1) % 3] + area2_1 += P1.clone().sub(P).cross(P2.clone().sub(P)).length() + } + + return Math.abs(area2_1 - area2) <= 1e-5 +} +``` + +### 通过叉积判断 (二维) + +- 判断叉积值的正负 + +```ts +export const cross = (v1: Num2, v2: Num2) => { + return v1[0] * v2[1] - v1[1] * v2[0] +} +export const pointInTriangleV2 = (triangle: Num2[], P: Num2) => { + for (let i = 0; i < 3; i++) { + const P1 = triangle[i] + const P2 = triangle[(i + 1) % 3] + const v1: Num2 = [P1[0] - P[0], P1[1] - P[1]] + const v2: Num2 = [P2[0] - P[0], P2[1] - P[1]] + // 顶点逆时针的情况下用 < 0, 顺时针用 > 0 + if (cross(v1, v2) < 0) return false + } + return true +} +``` + +### 通过叉积判断 (三维) + +- 判断叉积是否与三角形法向量同方向 (通过叉积与法向量的点积判断) +- 这种方法需要事先判断点是否与三角形在同一平面内 + +```ts +export const pointInTriangleV3_2 = (triangle: Vector3[], P: Vector3) => { + const [A, B, C] = triangle + + const ab = B.clone().sub(A) + const bc = C.clone().sub(B) + const n = ab.clone().cross(bc) + + for (let i = 0; i < 3; i++) { + const P1 = triangle[i] + const P2 = triangle[(i + 1) % 3] + // 判断叉积是否大于等于 0 + // 三维向量叉积为法向量,需要判断 _n 和 n 是否同向, 同向则大于等于 0 + const _n = P1.clone().sub(P).cross(P2.clone().sub(P)) + const dot = _n.clone().dot(n) + if (dot < 0) return false + } + + return true +} +``` + +## 射线与平面的交点 + +- P = P1 + t\*v 射线表示为起点 + t倍的方向向量 +- n·(P - P2) = 0 平面内的向量与平面法向量垂直(点乘为 0) + +```ts +/** + * + * @param P1 射线起点 + * @param v 射线方向 + * @param P2 平面内一点 + * @param n 平面法向量 + */ +export const rayIntersectPlane = (P1: Vector3, v: Vector3, P2: Vector3, n: Vector3) => { + // 1: P = P1 + t*v + // 2: n·(P - P2) = 0 + // => n·(P1 + t*v - P2) = 0 + // => t = (n·P2 - n·P1)/(n·v) + // 若 t >= 0, 则射线与平面相交,且交点为 P(P1 + t*v), 若t < 0, 则不相交 + // 若 t = Infinity, 则射线与平面平行 + + const t = (n.clone().dot(P2) - n.clone().dot(P1)) / n.clone().dot(v) + if (t < 0 || !Number.isFinite(t)) return null + const P = P1.clone().add(v.clone().multiplyScalar(t)) + return P +} +``` + +## 射线与球的交点 + +![射线与球的交点](./img/intersectSphere.jpg) + +```ts +/** + * 射线和球的交点 + * @param P 射线起点 + * @param v 射线方向 + * @param O 圆心 + * @param r 半径 + */ +export const rayIntersectSphere = (P: Vector3, v: Vector3, O: Vector3, r: number) => { + // cos(∠OPE) = len(PE)/len(PO) 求出 PE 长度 + // sin(∠OPE) = len(OE)/len(PO) 求出 OE 长度 + // 勾股定理 求出 P1E 长度 + // t = len(PE)-len(P1E) | len(PE)+len(P1E) + + const po = O.clone().sub(P) + const po_l = po.length() + const po_n = po.clone().normalize() + + const sin = v.clone().cross(po_n).length() + const oe_l = po_l * sin + if (oe_l > r) return null + + const cos = po_n.clone().dot(v) + if (cos <= 0) return null // 只可能在射线反方向相交 + const pe_l = po_l * cos + + const p1e_l = Math.sqrt(r ** 2 - oe_l ** 2) + + if (p1e_l <= 1e-5) { + return [P.clone().add(v.clone().multiplyScalar(pe_l))] + } + + return [P.clone().add(v.clone().multiplyScalar(pe_l - p1e_l)), P.clone().add(v.clone().multiplyScalar(pe_l + p1e_l))] +} +``` + +## 应用 + +用于射线拾取: 先检测射线是否与几何体的包围球相交, 如果相交则遍历几何体的三角面判断是否与几何体相交或者求出相交点 diff --git a/apps/zxtool-site/docs/blog/pvMat.md b/apps/zxtool-site/docs/blog/pvMat.md new file mode 100644 index 0000000..6ce166b --- /dev/null +++ b/apps/zxtool-site/docs/blog/pvMat.md @@ -0,0 +1,189 @@ +--- +title: 视图矩阵和投影矩阵的推导 +toc: content +group: WebGL +--- + +# 视图矩阵和投影矩阵的推导 + +## 视图矩阵 (viewMatrix) + +- 视图矩阵就是 lookAt 的逆矩阵 +- lookAt: 先旋转, 后位移 + +```ts +const lookAt = (eye: Vector3, target: Vector3, up: Vector3) => { + const zAxis = new Vector3().subVectors(eye, target).normalize() + const xAxis = new Vector3().crossVectors(up, zAxis).normalize() + //基向量 yAxis,修正上方向 + const yAxis = new Vector3().crossVectors(zAxis, xAxis).normalize() + // const mat4=new Matrix4().set( + // xAxis.x,yAxis.x,zAxis.x,eye.x, + // xAxis.y,yAxis.y,zAxis.y,eye.y, + // xAxis.z,yAxis.z,zAxis.z,eye.z, + // 0,0,0,1 + // ) + // prettier-ignore + const moveMat = new Matrix4().set( + 1, 0, 0, eye.x, + 0, 1, 0, eye.y, + 0, 0, 1, eye.z, + 0, 0, 0, 1 + ) + // prettier-ignore + const rotateMat = new Matrix4().set( + xAxis.x,yAxis.x,zAxis.x,0, + xAxis.y,yAxis.y,zAxis.y,0, + xAxis.z,yAxis.z,zAxis.z,0, + 0,0,0,1 + ) + + return new Matrix4().multiplyMatrices(moveMat, rotateMat) +} +``` + +## 正交投影矩阵 (projectionMatrix - OrthographicMatrix) + +- 先将绘制区域移动到坐标原点, 然后将绘制区域缩放到投影空间 +- 经过视图矩阵变换, 相机位于坐标原点, 看向 z 轴负方向, n 和 f 实际上应该是负值 + +``` +// 位移矩阵 * 缩放矩阵 +[ + 2/(r-l),0,0,0 + 0,2/(t-b),0,0, + 0,0,2/(f-n),0, // n,f 取负得到 2/(n-f) + 0,0,0,1 +] * +[ + 1,0,0,(l+r)*-0.5 + 0,1,0,(b+t)*-0.5, + 0,0,1,(n+f)*-0.5, // n,f 取负得到 (n+f)*0.5 + 0,0,0,1 +] +``` + +```ts +const getOrthographicMat = (l: number, r: number, t: number, b: number, n: number, f: number) => { + return multiplyMats( + new Matrix4().makeScale(2 / (r - l), 2 / (t - b), 2 / (n - f)), + new Matrix4().makeTranslation((l + r) * -0.5, (b + t) * -0.5, (n + f) * 0.5), + ) +} +``` + +## 透视投影矩阵 (projectionMatrix - PerspectiveMatrix) + +将视锥体远裁截面压缩得和近裁截面一样大(用到齐次坐标, 先将这个矩阵称为 PressMatrix), 再乘以正交投影矩阵 + +0. 先将根据条件求出 l, r, t, b, n, f + +1. 缩小远裁截面 +
![perspectiveMat](./img/perspectiveMat.png)
+ 可以得到 P1.x/P2.x=P1.y/P2.y=P1.z/P2.z; P2.z = n, 所以如果想要将P1变到P2, 需要除以 P2.z/n, 可以得到齐次坐标为 `P2.z/n` + + 根据上面的条件可以算出 PressMatrix 的第四行 + + ``` + [ + _,_,_,_, + _,_,_,_, + _,_,_,_, + a,b,c,d, + ] * [x, y, z, w] + => ax+by+cz+dw = z/n + => c = 1/n; a=b=d=0 + ``` + +2. 应用齐次坐标后就能得到转换后的x,y值(不存在其他的旋转平移缩放),所以可以确定矩阵前两行 + + ``` + [ + 1,0,0,0, + 0,1,0,0, + _,_,_,_, + 0,0,1/n,0 + ] + ``` + +3. 应用齐次坐标后会导致 z 的值也会缩小,而 z 方向的缩放应该在`正交投影矩阵`里面进行, 还需要对 z 做一些变换 + + ``` + [ + _,_,_,_, + _,_,_,_, + a,b,c,d, + _,_,_,_, + ] * [x, y, z, w] + 得到变换后的z坐标 => ax+by+cz+dw + 应用齐次坐标 => (ax+by+cz+1d)*(n/z) + 可以得到两个等式 => + + 1. (ax+by+c*n+d)*(n/n) = n (z 等于 n 时, 计算出的 z 值为 n, 近裁截面不变) + 2. (ax+by+c*f+d)*(n/f) = f (z 等于 f 时, 计算出的 z 值为 f, 远裁截面不变) + => + + 1. ax+by+d = n-cn + 2. ax+by+d = f^2/n - cf + + => n-cn = f^2/n - cf + => cf-cn = (f^2-n^2)/n + => c = (f+n)/n + + 将上式代入 ax+by+d = n-cn + => ax+by+d = -f + => d = -f + + 得到矩阵 + => + [ + 1,0,0,0, + 0,1,0,0, + 0,0,(n+f)/n,-f, + 0,0,1/n,0 + ] + ``` + +4. 经过视图矩阵变换, 相机位于坐标原点, 看向 z 轴负方向, n 和 f 实际上应该是负值 + + 齐次坐标的特性可以给这个矩阵乘以一个数, 通常会乘以 n, 这样的话经过变换以后 gl_Position 的 w 分量就刚好等于 -z, 而此时相机在坐标原点, 顶点都在 z 轴负方向, w 就可以代表视点到该顶点的距离, 方便某些计算 + + ``` + [ + 1,0,0,0, + 0,1,0,0, + 0,0,(n+f)/n,f, + 0,0,1/-n,0 + ] + => + [ + n,0,0,0, + 0,n,0,0, + 0,0,n+f,n*f, + 0,0,-1,0 + ] + ``` + +5. 上面得到一个变换视锥体的矩阵 PressMatrix,左乘以`正交投影矩阵`得到透视投影矩阵 + => getOrthographicMat(l, r, t, b, n, f)\*PressMatrix + +```ts +const getPerspectiveMat = (fov: number, aspect: number, near: number, far: number) => { + const [n, f] = [near, far] + const halfFov = MathUtils.degToRad(fov) * 0.5 + const t = near * Math.tan(halfFov) + const height = t * 2 + const width = aspect * height + + // prettier-ignore + const _mat = new Matrix4().set( + n,0,0,0, + 0,n,0,0, + 0,0,n+f,n*f, + 0,0,-1,0 + ) + + const orthographicMat = getOrthographicMat(width * -0.5, width * 0.5, t, -t, n, f) + return new Matrix4().multiplyMatrices(orthographicMat, _mat) +} +``` diff --git a/apps/zxtool-site/docs/blog/resume.md b/apps/zxtool-site/docs/blog/resume.md index c9a4a33..681a924 100644 --- a/apps/zxtool-site/docs/blog/resume.md +++ b/apps/zxtool-site/docs/blog/resume.md @@ -1,86 +1,37 @@ --- -title: 项目信息 +title: 部分项目截图 toc: content group: title: 未分类 --- -# 项目信息 - -## 某公安内网智控侦查大屏 - -### 功能简介 - -- 疫情/犯罪信息填报 -- 内网其他平台接入 -- 基于 Cesium 的三维可视化页面 -- 公安内部设备(固定监控、执勤车辆、单兵执法仪、对讲机等)地图落点以及轨迹、监控视频流接入 -- 重大活动安保预案,无人机模拟航飞,公安人员信息接入等模块开发 -- 后台管理系统的开发等。 - -### 项目截图 - - - - - - - - -## 某镇基层网格治理信息系统 - -### 功能简介 - -- 基于 Cesium 的三维可视化页面 -- 首屏驾驶舱按照村/网格维度的信息展示 -- 驾驶舱信息和相关图表与地图展示联动 -- 基于村/网格维度的人员/房屋/自然资源/企业信息/雨水情站点等信息接入并落图展示以及相应模块面板开发,地图点位和相应模块联动等 -- 移动端用于网格员对其管理范围的人员/房屋等信息维护,事件上报 -- 数据管理中心用于村/网格/人员/房屋/自然资源等信息维护以及角色,权限分配等 - -### 项目截图 - - - - - - - -## 某统计局内部汇报项目 - -### 功能简介 - -- 基于内部数据,通过可视化表单配置出汇报记录,实现了部分 chart 和表格的配置展示 -- 支持简单的多媒体文件,网页链接,pdf 文档的配置展示 -- 实现`指标`、`指标细分项`,`计算对比项`的联动配置 -- 实现较为友好的新用户操作指引 -- 区分展示页面和配置页面,为不同用户提供不同功能等 - -### 项目截图 - - - - - - -## 其他参与开发项目 - -| 项目名称 | 参与程度 | -| ------------------------------ | ---------------------------------------------- | -| 某能源企业内部系统 | 实现后台管理系统 | -| 某地区产业地图项目 | 一些功能模块和地图图层的实现 | -| 多验合一协同审批 | 基于原有系统改造,新增多验合一模块 | -| 某地区未来社区项目 | 项目搭建,实现基于 BIM 的分层分户模块 | -| 某地区数字孪生建设试点 | 接手他人项目,一些功能模块和地图图层的实现 | -| 某地区数字孪生管理平台 | 基于原有系统改造,一些功能模块和地图图层的实现 | -| 一些其他项目,就不再一一列举了 | / | - - - - - - - - - - +# 部分项目截图 + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/packages/zxtool-cesium-utils/.eslintrc.json b/packages/zxtool-cesium-utils/.eslintrc.json index 2d19edd..c17a607 100644 --- a/packages/zxtool-cesium-utils/.eslintrc.json +++ b/packages/zxtool-cesium-utils/.eslintrc.json @@ -1,4 +1,7 @@ { "root": true, - "extends": ["custom/ts"] + "extends": ["custom/ts"], + "rules": { + "@typescript-eslint/no-unsafe-declaration-merging": "off" + } } diff --git a/packages/zxtool-cesium-utils/package.json b/packages/zxtool-cesium-utils/package.json index b4b70c9..c41a660 100644 --- a/packages/zxtool-cesium-utils/package.json +++ b/packages/zxtool-cesium-utils/package.json @@ -53,7 +53,8 @@ "@types/geojson": "^7946.0.13", "@zxtool/utils": "workspace:*", "dayjs": "^1.11.5", - "lodash": "^4.17.21" + "lodash": "^4.17.21", + "nanoid": "^4.0.2" }, "peerDependencies": { "cesium": ">=1.90.0" diff --git a/packages/zxtool-cesium-utils/src/v2/CesiumHelper.ts b/packages/zxtool-cesium-utils/src/helper/CesiumHelper.ts similarity index 87% rename from packages/zxtool-cesium-utils/src/v2/CesiumHelper.ts rename to packages/zxtool-cesium-utils/src/helper/CesiumHelper.ts index 271f07e..cc0c1c8 100644 --- a/packages/zxtool-cesium-utils/src/v2/CesiumHelper.ts +++ b/packages/zxtool-cesium-utils/src/helper/CesiumHelper.ts @@ -1,12 +1,18 @@ import { EmitterHelper, IObj } from "@zxtool/utils" +import * as Cesium from "cesium" import { CesiumHelperPlugin, CesiumHelperPluginProps } from "./plugins" import { ViewerPlugin } from "./plugins/ViewerPlugin" +export interface CesiumHelper { + getWidget(type: "viewer"): Cesium.Viewer | undefined + + getWidgetAsync(type: "viewer"): Promise +} + export class CesiumHelper { private isInit = false private readonly emitter = new EmitterHelper({ maxCount: { history: 1 } }) private readonly widgetCollection: Map = new Map() - readonly pluginCollection: Map = new Map() getWidget(key: PropertyKey): unknown { @@ -33,7 +39,6 @@ export class CesiumHelper { init(container: string | Element) { if (this.isInit) return - // todo this.addPlugin(new ViewerPlugin(), { container }) this.isInit = true diff --git a/packages/zxtool-cesium-utils/src/helper/CoordHelper.ts b/packages/zxtool-cesium-utils/src/helper/CoordHelper.ts new file mode 100644 index 0000000..7689f90 --- /dev/null +++ b/packages/zxtool-cesium-utils/src/helper/CoordHelper.ts @@ -0,0 +1,140 @@ +import { Num3 } from "@zxtool/utils" +import * as Cesium from "cesium" +import { Cartesian2, Cartesian3, Viewer } from "cesium" +import { LonLat, LonLatHeight } from "../type" +import { genZCUInfo } from "../util/util" + +const genInfo = genZCUInfo("CoordHelper") +const noCoordInfo = genInfo("请先设置坐标") + +export type Local2FixedFunc = + | "northEastDownToFixedFrame" + | "northUpEastToFixedFrame" + | "northWestUpToFixedFrame" + | "eastNorthUpToFixedFrame" + +export class CoordHelper { + private c3: Cartesian3 | null = null + private viewer: Viewer + + constructor(viewer: Viewer, c3?: Cartesian3) { + this.viewer = viewer + if (c3) this.c3 = c3 + } + + static c32lonLatHeight(c3: Cartesian3, viewer?: Viewer) { + const lonLatHeightRadian = CoordHelper.c32lonLatHeightRadian(c3, viewer) + const lon = Cesium.Math.toDegrees(lonLatHeightRadian[0]) + const lat = Cesium.Math.toDegrees(lonLatHeightRadian[1]) + return [lon, lat, lonLatHeightRadian[2]] as Num3 + } + static lonLatHeight2c3(lonLatHeight: LonLat | LonLatHeight, viewer?: Viewer) { + const ellipsoid = viewer?.scene.globe.ellipsoid ?? Cesium.Ellipsoid.WGS84 + return Cartesian3.fromDegrees(...(lonLatHeight as Num3), ellipsoid) + } + + static c32lonLatHeightRadian(c3: Cartesian3, viewer?: Viewer) { + const ellipsoid = viewer?.scene.globe.ellipsoid ?? Cesium.Ellipsoid.WGS84 + const cartographic = Cesium.Cartographic.fromCartesian(c3, ellipsoid) + // const cartographic = ellipsoid.cartesianToCartographic(c3) + return [cartographic.longitude, cartographic.latitude, cartographic.height] as Num3 + } + static lonLatHeightRadian2c3(lonLatHeight: LonLat | LonLatHeight, viewer?: Viewer) { + const ellipsoid = viewer?.scene.globe.ellipsoid ?? Cesium.Ellipsoid.WGS84 + return Cartesian3.fromRadians(...(lonLatHeight as Num3), ellipsoid) + } + + static c32screenCoord(c3: Cartesian3, viewer: Viewer) { + return Cesium.SceneTransforms.wgs84ToWindowCoordinates(viewer.scene, c3) + } + /** 从地球上选点 */ + static screenCoord2c3(coord: Cartesian2, viewer: Viewer) { + // viewer.camera.pickEllipsoid(coord, viewer.scene.globe.ellipsoid) + return viewer.scene.globe.pick(viewer.camera.getPickRay(coord)!, viewer.scene) ?? null + } + /** 从 scene 上选点, 包括倾斜和地形等 */ + static screenCoord2sceneC3(coord: Cartesian2, viewer: Viewer) { + return viewer.scene.pickPosition(coord) + } + + static local2fixed( + local: Cartesian3, + origin: Cartesian3, + func: Local2FixedFunc = "eastNorthUpToFixedFrame", + viewer?: Viewer, + ) { + const ellipsoid = viewer?.scene.globe.ellipsoid ?? Cesium.Ellipsoid.WGS84 + const transform = Cesium.Transforms[func](origin, ellipsoid) + return Cesium.Matrix4.multiplyByPoint(transform, local, new Cartesian3()) + } + static fixed2local( + fixed: Cartesian3, + origin: Cartesian3, + func: Local2FixedFunc = "eastNorthUpToFixedFrame", + viewer?: Viewer, + ) { + const ellipsoid = viewer?.scene.globe.ellipsoid ?? Cesium.Ellipsoid.WGS84 + const transform = Cesium.Matrix4.inverseTransformation(Cesium.Transforms[func](origin, ellipsoid), new Cesium.Matrix4()) + return Cesium.Matrix4.multiplyByPoint(transform, fixed, new Cartesian3()) + } + + static translate(c3: Cartesian3, xyz: Cartesian3, viewer?: Viewer) { + return CoordHelper.local2fixed(xyz, c3, undefined, viewer) + } + static setHeight(c3: Cartesian3, height: number, viewer?: Viewer) { + const coord = CoordHelper.c32lonLatHeightRadian(c3, viewer) + coord[2] = height + return CoordHelper.lonLatHeightRadian2c3(coord) + } + + setFromC3(c3: Cartesian3) { + this.c3 = c3 + return this + } + setFromLonLatHeight(lonLatHeight: LonLat | LonLatHeight) { + this.c3 = CoordHelper.lonLatHeight2c3(lonLatHeight, this.viewer) + return this + } + setFromLonLatHeightRadius(lonLatHeight: LonLat | LonLatHeight) { + this.c3 = CoordHelper.lonLatHeightRadian2c3(lonLatHeight, this.viewer) + return this + } + /** 从地球上选点 */ + setFromScreenCoord(coord: Cartesian2) { + this.c3 = CoordHelper.screenCoord2c3(coord, this.viewer) + return this + } + /** 从 scene 上选点, 包括倾斜和地形等 */ + setFromSceneScreenCoord(coord: Cartesian2) { + this.c3 = CoordHelper.screenCoord2sceneC3(coord, this.viewer) + return this + } + + getC3() { + if (!this.c3) throw new Error(noCoordInfo) + return this.c3 + } + getLonLatHeight() { + if (!this.c3) throw new Error(noCoordInfo) + return CoordHelper.c32lonLatHeight(this.c3, this.viewer) + } + getLonLatHeightRadian() { + if (!this.c3) throw new Error(noCoordInfo) + return CoordHelper.c32lonLatHeightRadian(this.c3, this.viewer) + } + getScreenCoord() { + if (!this.c3) throw new Error(noCoordInfo) + return CoordHelper.c32screenCoord(this.c3, this.viewer) + } + + translate(xyz: Cartesian3) { + if (!this.c3) throw new Error(noCoordInfo) + this.c3 = CoordHelper.translate(this.c3, xyz, this.viewer) + return this + } + setHeight(height: number) { + if (!this.c3) throw new Error(noCoordInfo) + this.c3 = CoordHelper.setHeight(this.c3, height, this.viewer) + return this + } +} diff --git a/packages/zxtool-cesium-utils/src/helper/DrawHelper.ts b/packages/zxtool-cesium-utils/src/helper/DrawHelper.ts new file mode 100644 index 0000000..b3a7b9b --- /dev/null +++ b/packages/zxtool-cesium-utils/src/helper/DrawHelper.ts @@ -0,0 +1,198 @@ +import type { Cartesian2, Cartesian3, CustomDataSource, Entity, Primitive, Viewer } from "cesium" +import * as Cesium from "cesium" +import { nanoid } from "nanoid" +import { ViewerUtil } from "../util" +import { genZCUInfo } from "../util/util" +import { CoordHelper } from "./CoordHelper" +import { MouseEventHelper } from "./MouseEventHelper" +import { PrimitiveManager } from "./PrimitiveManager" + +const genInfo = genZCUInfo("DrawHelper") + +export interface EventCbs { + onClick?: (position: Cartesian3, screenCoord: Cartesian2) => void + onMove?: (position: Cartesian3, screenCoord: Cartesian2) => void + onRecall?: (position: Cartesian3, screenCoord: Cartesian2) => void + onFinish?: (primitive: Primitive | null, positions: Cartesian3[] | null) => void + onCancel?: () => void +} + +export interface DrawProps extends Omit { + onFinish?: (props: { primitive: Primitive | null; entity: Entity | null; positions: Cartesian3[] | null }) => void +} + +export class DrawHelper { + private static isDrawing = false + + private key: string + private viewer: Viewer + private eventHelper: MouseEventHelper + private dataSource: CustomDataSource + private primitiveManager: PrimitiveManager + + private positions: Cartesian3[] = [] + private mode: "polygon" | "polyline" | "" = "" + + constructor(viewer: Viewer) { + this.viewer = viewer + this.key = nanoid() + this.eventHelper = new MouseEventHelper(viewer) + this.dataSource = ViewerUtil.getCustomDataSource({ viewer, name: this.key, autoCreate: true })! + this.primitiveManager = new PrimitiveManager(viewer) + } + + drawPolyline(props: DrawProps = {}) { + if (DrawHelper.isDrawing) return console.error(genInfo("已经有一个 DrawHelper 的实例正在绘制")) + DrawHelper.isDrawing = true + this.mode = "polyline" + + const { onFinish, ...rest } = props + this.addEvent({ + ...rest, + onFinish(primitive, positions) { + onFinish?.({ primitive, positions, entity: null }) + }, + }) + } + + drawPolygon(props: DrawProps = {}) { + if (DrawHelper.isDrawing) return console.error(genInfo("已经有一个 DrawHelper 的实例正在绘制")) + DrawHelper.isDrawing = true + this.mode = "polygon" + + const entity = this.dataSource.entities.add({ + polygon: { + hierarchy: new Cesium.CallbackProperty(() => { + const positions = this.positions.map(item => item.clone()) + return new Cesium.PolygonHierarchy(positions) + }, false), + material: Cesium.Color.CYAN.withAlpha(0.4), + }, + }) + + const { onFinish, ...rest } = props + this.addEvent({ + ...rest, + onFinish: (primitive, positions) => { + // @ts-ignore + entity.polygon!.hierarchy = new Cesium.PolygonHierarchy(positions?.map(item => item.clone())) + onFinish?.({ primitive, entity, positions }) + }, + }) + } + + private addEvent(props: EventCbs) { + const { onClick, onFinish, onMove, onRecall, onCancel } = props + this.eventHelper.addEvent({ + key: this.key, + type: "LEFT_CLICK", + cb: ({ position }) => { + const c3 = CoordHelper.screenCoord2sceneC3(position, this.viewer) + if (c3) { + onClick?.(c3, position) + if (!this.positions.length) this.positions.push(c3) + this.positions.push(c3) + this.updatePolyline() + } + }, + }) + this.eventHelper.addEvent({ + key: this.key, + type: "MOUSE_MOVE", + cb: ({ endPosition }) => { + const c3 = CoordHelper.screenCoord2sceneC3(endPosition, this.viewer) + if (c3) { + onMove?.(c3, endPosition) + this.positions[this.positions.length - 1] = c3 + this.updatePolyline() + } + }, + }) + this.eventHelper.addEvent({ + key: this.key, + type: "RIGHT_CLICK", + cb: ({ position }) => { + const c3 = CoordHelper.screenCoord2sceneC3(position, this.viewer) + this.positions.pop() + if (this.positions.length === 1) this.positions = [] + else if (c3) { + onRecall?.(c3, position) + this.positions[this.positions.length - 1] = c3 + this.updatePolyline() + } + }, + }) + window.addEventListener("keydown", e => { + if (e.key === " ") { + e.preventDefault() + const { primitive, positions } = this.finish() ?? {} + onFinish?.(primitive ?? null, positions ?? null) + } else if (e.key === "Escape") { + e.preventDefault() + this.cancel() + onCancel?.() + } + }) + } + + private updatePolyline() { + const key = `${this.key}_polyline` + this.primitiveManager.removeByCondition({ key }) + if (this.positions.length < 2) return null + + const positions = [...this.positions] + if (this.mode === "polygon") positions.push(this.positions[0]) + + const primitive = new Cesium.Primitive({ + geometryInstances: new Cesium.GeometryInstance({ + geometry: new Cesium.PolylineGeometry({ positions, width: 2 }), + }), + appearance: new Cesium.PolylineMaterialAppearance({ + material: Cesium.Material.fromType("Color", { color: Cesium.Color.CYAN }), + renderState: { depthTest: { enabled: false } }, + }), + asynchronous: false, + }) + this.primitiveManager.add({ primitive, key }) + return { primitive, positions } + } + + /** + * 结束绘制并保留绘制结果 + */ + finish() { + if (!DrawHelper.isDrawing) { + throw new Error("") + } + this.positions.pop() + const primitive = this.updatePolyline() + this.eventHelper.clear() + this.positions = [] + DrawHelper.isDrawing = false + this.mode = "" + return primitive + } + + /** + * 停止并清除绘制 + */ + cancel() { + if (!DrawHelper.isDrawing) { + throw new Error("") + } + this.primitiveManager.removeAll() + this.dataSource.entities.removeAll() + this.eventHelper.clear() + this.positions = [] + DrawHelper.isDrawing = false + this.mode = "" + } + + /** + * 清除绘制结果, 只能结束绘制后调用 + */ + clear() { + this.primitiveManager.removeAll() + this.dataSource.entities.removeAll() + } +} diff --git a/packages/zxtool-cesium-utils/src/helper/ImageryLayerManager.ts b/packages/zxtool-cesium-utils/src/helper/ImageryLayerManager.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/zxtool-cesium-utils/src/widget/MassivePointsHelper.ts b/packages/zxtool-cesium-utils/src/helper/MassivePointsHelper.ts similarity index 98% rename from packages/zxtool-cesium-utils/src/widget/MassivePointsHelper.ts rename to packages/zxtool-cesium-utils/src/helper/MassivePointsHelper.ts index 5a0c5ca..8b26dfd 100644 --- a/packages/zxtool-cesium-utils/src/widget/MassivePointsHelper.ts +++ b/packages/zxtool-cesium-utils/src/helper/MassivePointsHelper.ts @@ -1,9 +1,9 @@ import * as Cesium from "cesium" import { LonLat, LonLatKey, LonLatType, RECT } from "../type" +import { ViewerUtil } from "../util" import { EntityUtil, IEntities } from "../util/EntityUtil" -import { ViewerUtilSync } from "../util/ViewerUtilSync" -const { getScreenRect } = ViewerUtilSync +const { getScreenRect } = ViewerUtil export interface MassivePointsHelperOptions { xNums?: number diff --git a/packages/zxtool-cesium-utils/src/widget/ScreenEventHelper.ts b/packages/zxtool-cesium-utils/src/helper/MouseEventHelper.ts similarity index 61% rename from packages/zxtool-cesium-utils/src/widget/ScreenEventHelper.ts rename to packages/zxtool-cesium-utils/src/helper/MouseEventHelper.ts index 66cee59..00d3cb9 100644 --- a/packages/zxtool-cesium-utils/src/widget/ScreenEventHelper.ts +++ b/packages/zxtool-cesium-utils/src/helper/MouseEventHelper.ts @@ -1,6 +1,4 @@ -import { IObj } from "@zxtool/utils/dist/type" import * as Cesium from "cesium" -import { ViewerHelper } from "./ViewerHelper" // https://juejin.cn/post/7053298681037979678 export enum ScreenSpaceEventType { @@ -22,34 +20,33 @@ export enum ScreenSpaceEventType { } // https://www.coder.work/article/1309200 -export type ScreenEventType = keyof typeof ScreenSpaceEventType +export type MouseEventType = keyof typeof ScreenSpaceEventType export interface AddEventProps { key: PropertyKey - type: ScreenEventType - cb: (movement: IObj) => void - viewer?: Cesium.Viewer + type: MouseEventType + cb: (movement: any) => void } export interface RemoveEventProps { key: PropertyKey - type?: ScreenEventType + type?: MouseEventType } -export class _ScreenEventHelper { +export class GlobalMouseEventHelper { private handlerCollection: Map = new Map() - addEvent = async ({ key, type, cb, viewer: _viewer }: AddEventProps) => { - const viewer = await ViewerHelper.getViewerPromise(undefined, _viewer) + addEvent({ key, type, viewer, cb }: AddEventProps & { viewer: Cesium.Viewer }) { let handler = this.handlerCollection.get(key) if (!handler) { handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas) this.handlerCollection.set(key, handler) } handler.setInputAction(cb, ScreenSpaceEventType[type]) + return this } - removeEvent = async ({ key, type }: RemoveEventProps) => { + removeEvent({ key, type }: RemoveEventProps) { const handler = this.handlerCollection.get(key) if (!handler) return if (type) handler.removeInputAction(ScreenSpaceEventType[type]) @@ -58,6 +55,22 @@ export class _ScreenEventHelper { this.handlerCollection.delete(key) } } + + clear() { + this.handlerCollection.forEach(handler => handler.destroy()) + this.handlerCollection.clear() + } } -export const ScreenEventHelper = new _ScreenEventHelper() +export class MouseEventHelper extends GlobalMouseEventHelper { + private viewer: Cesium.Viewer + + constructor(viewer: Cesium.Viewer) { + super() + this.viewer = viewer + } + + addEvent(props: AddEventProps) { + return super.addEvent({ ...props, viewer: this.viewer }) + } +} diff --git a/packages/zxtool-cesium-utils/src/widget/PrimitiveManager.ts b/packages/zxtool-cesium-utils/src/helper/PrimitiveManager.ts similarity index 56% rename from packages/zxtool-cesium-utils/src/widget/PrimitiveManager.ts rename to packages/zxtool-cesium-utils/src/helper/PrimitiveManager.ts index ec346b5..3a88560 100644 --- a/packages/zxtool-cesium-utils/src/widget/PrimitiveManager.ts +++ b/packages/zxtool-cesium-utils/src/helper/PrimitiveManager.ts @@ -1,7 +1,6 @@ import { CommonUtil } from "@zxtool/utils" import * as Cesium from "cesium" -import { ViewerUtilSync } from "../util/ViewerUtilSync" -import { ViewerHelper } from "./ViewerHelper" +import { ViewerUtil } from "../util" export type IPrimitive = Cesium.GroundPrimitive | Cesium.ClassificationPrimitive | Cesium.Primitive @@ -17,43 +16,40 @@ export type AddPrimitiveProps = { } & Record export class PrimitiveManager { - private viewer?: Cesium.Viewer + private viewer: Cesium.Viewer readonly primitiveCollection = new Map() - constructor(viewer?: Cesium.Viewer) { - if (viewer) this.viewer = viewer + constructor(viewer: Cesium.Viewer) { + this.viewer = viewer } - async add(props: AddPrimitiveProps) { - const viewer = await ViewerHelper.getViewerPromise(undefined, this.viewer) + add(props: AddPrimitiveProps) { const { key = Symbol(), flyTo = true, primitive, ...rest } = props - if (!viewer.scene.primitives.contains(primitive)) viewer.scene.primitives.add(primitive) + if (!this.viewer.scene.primitives.contains(primitive)) this.viewer.scene.primitives.add(primitive) const primitiveObj = { key, primitive, ...rest } this.primitiveCollection.set(key, primitiveObj) - if (flyTo) ViewerUtilSync.flyToPrimitive(primitive, viewer) + if (flyTo) ViewerUtil.flyToPrimitive(this.viewer, primitive) return primitiveObj } - showByCondition = async (condition: Partial, flyTo?: boolean) => { - const viewer = await ViewerHelper.getViewerPromise(undefined, this.viewer) + showByCondition = (condition: Partial, flyTo?: boolean) => { const map = this.getByCondition(condition) let last: IPrimitive | undefined map.forEach(item => { item.primitive.show = true last = item.tileset }) - if (flyTo && last) ViewerUtilSync.flyToPrimitive(last, viewer) + if (flyTo && last) ViewerUtil.flyToPrimitive(this.viewer, last) } - showAll = async (flyTo?: boolean) => { - const viewer = await ViewerHelper.getViewerPromise(undefined, this.viewer) + showAll = (flyTo?: boolean) => { let last: IPrimitive | undefined this.primitiveCollection.forEach(item => { item.primitive.show = true last = item.primitive }) - if (flyTo && last) ViewerUtilSync.flyToPrimitive(last, viewer) + if (flyTo && last) ViewerUtil.flyToPrimitive(this.viewer, last) } hideByCondition = (condition: Partial) => { @@ -65,18 +61,16 @@ export class PrimitiveManager { this.primitiveCollection.forEach(v => (v.primitive.show = false)) } - removeByCondition = async (condition: Partial) => { - const viewer = await ViewerHelper.getViewerPromise(undefined, this.viewer) + removeByCondition = (condition: Partial) => { const map = this.getByCondition(condition) map.forEach(item => { - viewer.scene.primitives.remove(item.primitive) + this.viewer.scene.primitives.remove(item.primitive) this.primitiveCollection.delete(item.key) }) } - removeAll = async () => { - const viewer = await ViewerHelper.getViewerPromise(undefined, this.viewer) - this.primitiveCollection.forEach(item => viewer.scene.primitives.remove(item.primitive)) + removeAll = () => { + this.primitiveCollection.forEach(item => this.viewer.scene.primitives.remove(item.primitive)) this.primitiveCollection.clear() } diff --git a/packages/zxtool-cesium-utils/src/helper/SyncViewerHelper.ts b/packages/zxtool-cesium-utils/src/helper/SyncViewerHelper.ts new file mode 100644 index 0000000..d9fc3a6 --- /dev/null +++ b/packages/zxtool-cesium-utils/src/helper/SyncViewerHelper.ts @@ -0,0 +1,85 @@ +import * as Cesium from "cesium" +import { GlobalMouseEventHelper } from "./MouseEventHelper" + +export interface ViewerObj { + viewer: Cesium.Viewer + doSync?: boolean + beSync?: boolean + control?: boolean +} + +export class SyncViewerHelper { + readonly viewers: Map + + private flag = false + private curViewer: Cesium.Viewer | null = null + private globalMouseEventHelper = new GlobalMouseEventHelper() + + constructor(viewers: Map) { + this.viewers = viewers + } + + private syncViewer = () => { + this.viewers.forEach((viewerObj, key) => { + const { viewer, beSync = true } = viewerObj + if (!beSync) return + if (this.curViewer && viewer !== this.curViewer) { + viewer.camera.flyTo({ + destination: this.curViewer.camera.position, + orientation: { + heading: this.curViewer.camera.heading, + pitch: this.curViewer.camera.pitch, + roll: this.curViewer.camera.roll, + }, + duration: 0.0, + }) + } + }) + } + + startSync() { + this.flag = true + + this.viewers.forEach((viewerObj, key) => { + const { viewer, doSync = true, control = true } = viewerObj + + if (!control) { + const control = viewer.scene.screenSpaceCameraController + control.enableRotate = false + control.enableTranslate = false + control.enableZoom = false + control.enableTilt = false + control.enableLook = false + } + + if (doSync) { + this.globalMouseEventHelper.addEvent({ + key, + viewer, + type: "MOUSE_MOVE", + cb: () => (this.curViewer = viewer), + }) + + viewer.camera.changed.addEventListener(this.syncViewer) + viewer.camera.percentageChanged = 1e-10 + } + }) + } + + stopSync() { + this.viewers.forEach((viewerObj, key) => { + const { viewer } = viewerObj + this.globalMouseEventHelper.removeEvent({ key, type: "MOUSE_MOVE" }) + + viewer.camera.changed.removeEventListener(this.syncViewer) + viewer.camera.percentageChanged = 0.5 + }) + this.flag = false + } + + refreshSync() { + if (!this.flag) return + this.stopSync() + setTimeout(() => this.startSync()) + } +} diff --git a/packages/zxtool-cesium-utils/src/widget/TilesetManager.ts b/packages/zxtool-cesium-utils/src/helper/TilesetManager.ts similarity index 69% rename from packages/zxtool-cesium-utils/src/widget/TilesetManager.ts rename to packages/zxtool-cesium-utils/src/helper/TilesetManager.ts index f1001fc..09bc316 100644 --- a/packages/zxtool-cesium-utils/src/widget/TilesetManager.ts +++ b/packages/zxtool-cesium-utils/src/helper/TilesetManager.ts @@ -2,7 +2,6 @@ import { CommonUtil } from "@zxtool/utils" import * as Cesium from "cesium" import { ZCUConfig, ZCUConfigType } from "../util/ZCUConfig" import { genZCUInfo } from "../util/util" -import { ViewerHelper } from "./ViewerHelper" const genInfo = genZCUInfo("TilesetManager") @@ -18,18 +17,17 @@ export interface AddTilesetProps extends Partial { } export class TilesetManager { - private viewer?: Cesium.Viewer + private viewer: Cesium.Viewer readonly tilesetCollection = new Map() - constructor(viewer?: Cesium.Viewer) { - if (viewer) this.viewer = viewer + constructor(viewer: Cesium.Viewer) { + this.viewer = viewer } /** * @param props 如果 url 是数值则代表 Cesium.IonAssetId; url 和 tileset 传入一个即可, tileset 优先级更高 */ add = async (props: AddTilesetProps) => { - const viewer = await ViewerHelper.getViewerPromise(undefined, this.viewer) const { url, key = Symbol(), @@ -41,7 +39,7 @@ export class TilesetManager { let tilesetObj = this.tilesetCollection.get(key) if (tilesetObj?.tileset && tilesetObj.tileset === _tileset) { - if (flyTo) viewer.flyTo(tilesetObj.tileset) + if (flyTo) this.viewer.flyTo(tilesetObj.tileset) console.error(genInfo(`当前 key(${key.toString()}) 已经存在,新增 tileset 失败`)) return tilesetObj } @@ -52,32 +50,30 @@ export class TilesetManager { ? await Cesium.Cesium3DTileset.fromIonAssetId(url, tilesetOptions) : await Cesium.Cesium3DTileset.fromUrl(url, tilesetOptions)) - if (!viewer.scene.primitives.contains(tileset)) viewer.scene.primitives.add(tileset) + if (!this.viewer.scene.primitives.contains(tileset)) this.viewer.scene.primitives.add(tileset) tilesetObj = { key, tileset, ...rest } this.tilesetCollection.set(key, tilesetObj) - if (flyTo) viewer.flyTo(tileset) + if (flyTo) this.viewer.flyTo(tileset) return tilesetObj } - showByCondition = async (condition: Partial, flyTo?: boolean) => { - const viewer = await ViewerHelper.getViewerPromise(undefined, this.viewer) + showByCondition = (condition: Partial, flyTo?: boolean) => { const map = this.getByCondition(condition) let last: Cesium.Cesium3DTileset | undefined map.forEach(item => { item.tileset.show = true last = item.tileset }) - if (flyTo && last) viewer.flyTo(last) + if (flyTo && last) this.viewer.flyTo(last) } - showAll = async (flyTo?: boolean) => { - const viewer = await ViewerHelper.getViewerPromise(undefined, this.viewer) + showAll = (flyTo?: boolean) => { let last: Cesium.Cesium3DTileset | undefined this.tilesetCollection.forEach(item => { item.tileset.show = true last = item.tileset }) - if (flyTo && last) viewer.flyTo(last) + if (flyTo && last) this.viewer.flyTo(last) } hideByCondition = (condition: Partial) => { @@ -89,18 +85,16 @@ export class TilesetManager { this.tilesetCollection.forEach(v => (v.tileset.show = false)) } - removeByCondition = async (condition: Partial) => { - const viewer = await ViewerHelper.getViewerPromise(undefined, this.viewer) + removeByCondition = (condition: Partial) => { const map = this.getByCondition(condition) map.forEach(item => { - viewer.scene.primitives.remove(item.tileset) + this.viewer.scene.primitives.remove(item.tileset) this.tilesetCollection.delete(item.key) }) } removeAll = async () => { - const viewer = await ViewerHelper.getViewerPromise(undefined, this.viewer) - this.tilesetCollection.forEach(item => viewer.scene.primitives.remove(item.tileset)) + this.tilesetCollection.forEach(item => this.viewer.scene.primitives.remove(item.tileset)) this.tilesetCollection.clear() } diff --git a/packages/zxtool-cesium-utils/src/widget/index.ts b/packages/zxtool-cesium-utils/src/helper/index.ts similarity index 58% rename from packages/zxtool-cesium-utils/src/widget/index.ts rename to packages/zxtool-cesium-utils/src/helper/index.ts index a73e601..bc1b8bf 100644 --- a/packages/zxtool-cesium-utils/src/widget/index.ts +++ b/packages/zxtool-cesium-utils/src/helper/index.ts @@ -1,7 +1,9 @@ +export * from "./CesiumHelper" export * from "./CoordHelper" +export * from "./DrawHelper" export * from "./MassivePointsHelper" +export * from "./MouseEventHelper" export * from "./PrimitiveManager" -export * from "./ScreenEventHelper" export * from "./SyncViewerHelper" export * from "./TilesetManager" -export * from "./ViewerHelper" +export * from "./plugins" diff --git a/packages/zxtool-cesium-utils/src/helper/plugins/MouseEventPlugin.ts b/packages/zxtool-cesium-utils/src/helper/plugins/MouseEventPlugin.ts new file mode 100644 index 0000000..feaaf3c --- /dev/null +++ b/packages/zxtool-cesium-utils/src/helper/plugins/MouseEventPlugin.ts @@ -0,0 +1,127 @@ +import * as Cesium from "cesium" +import { CesiumHelperPlugin, CesiumHelperPluginProps } from "." +import { genZCUInfo } from "../../util/util" +import { MouseEventHelper } from "../MouseEventHelper" + +type RawCb = (picked: any, e: T) => void + +const genInfo = genZCUInfo("MouseEventPlugin") + +export class MouseEventPlugin implements CesiumHelperPlugin { + private static key = Symbol.for("__mouse_event__") + + private addProps: CesiumHelperPluginProps | null = null + private curObj: Cesium.Entity | Cesium.Primitive | null = null + private cbs = { + click: (e: { position: Cesium.Cartesian2 }) => { + const { position } = e + + const { entity, primitive, raw } = this.getPicked(position) + this.onClick?.(raw, e) + + if (entity) { + const fn = entity.__customField?.onClick + if (typeof fn === "function") { + this.clearClick?.() + fn(entity!, raw) + } + } else if (primitive) { + const fn = primitive.__customField?.onClick + if (typeof fn === "function") { + this.clearClick?.() + fn(primitive!, raw) + } + } + }, + move: (e: { startPosition: Cesium.Cartesian2; endPosition: Cesium.Cartesian2 }) => { + const { endPosition } = e + const { entity, primitive, raw } = this.getPicked(endPosition) + this.onMove?.(raw, e) + + const object = entity ?? primitive ?? null + const hover = object?.__customField?.hover ?? false + document.body.style.cursor = hover ? "pointer" : "default" + + if (this.curObj === object) return + const leave = this.curObj?.__customField?.onLeave + // @ts-ignore + if (typeof leave === "function") leave(this.curObj, raw) + const enter = object?.__customField?.onEnter + // @ts-ignore + if (typeof enter === "function") enter(object, raw) + + this.curObj = object + }, + } + + onClick?: RawCb<{ position: Cesium.Cartesian2 }> + onMove?: RawCb<{ startPosition: Cesium.Cartesian2; endPosition: Cesium.Cartesian2 }> + clearClick?: () => void + + private _mouseEventHelper?: MouseEventHelper + get mouseEventHelper() { + return this._mouseEventHelper + } + + private getPicked = ( + position: Cesium.Cartesian2, + ): { entity: Cesium.Entity | null; primitive: Cesium.Primitive | null; raw: any } => { + if (!this.addProps) return { entity: null, primitive: null, raw: null } + const { cesiumHelper } = this.addProps + + const viewer = cesiumHelper.getWidget("viewer") + if (!viewer) return { entity: null, primitive: null, raw: null } + + const picked = viewer.scene.pick(position) + if (!Cesium.defined(picked)) return { entity: null, primitive: null, raw: null } + + const res = { entity: null, primitive: null, raw: picked } + + if (picked.id instanceof Cesium.Entity) res.entity = picked.id + if (picked.primitive instanceof Cesium.Primitive) res.primitive = picked.primitive + return res + } + + add(props: CesiumHelperPluginProps): MouseEventPlugin { + this.addProps = props + const { cesiumHelper } = props + const { pluginCollection } = cesiumHelper + + const plugin = pluginCollection.get(MouseEventPlugin.key) as MouseEventPlugin + if (plugin) { + console.error(genInfo("已经存在一个 MouseEventPlugin, 不能重复添加")) + return plugin + } + + const viewer = cesiumHelper.getWidget("viewer") + if (!viewer) { + console.error(genInfo("不存在可用的 viewer")) + return this + } + + const mouseEventHelper = new MouseEventHelper(viewer) + mouseEventHelper.addEvent({ key: MouseEventPlugin.key, type: "LEFT_CLICK", cb: this.cbs.click }) + mouseEventHelper.addEvent({ key: MouseEventPlugin.key, type: "MOUSE_MOVE", cb: this.cbs.move }) + + this._mouseEventHelper = mouseEventHelper + + pluginCollection.set(MouseEventPlugin.key, this) + return this + } + + remove() { + if (!this.addProps) throw new Error(genInfo("未添加的插件不能被移除")) + const { cesiumHelper } = this.addProps + const { pluginCollection } = cesiumHelper + + this.mouseEventHelper?.clear() + this._mouseEventHelper = undefined + this.curObj = null + this.onClick = undefined + this.onMove = undefined + this.clearClick = undefined + + pluginCollection.delete(MouseEventPlugin.key) + this.addProps = null + } +} diff --git a/packages/zxtool-cesium-utils/src/v2/plugins/ViewerPlugin.ts b/packages/zxtool-cesium-utils/src/helper/plugins/ViewerPlugin.ts similarity index 79% rename from packages/zxtool-cesium-utils/src/v2/plugins/ViewerPlugin.ts rename to packages/zxtool-cesium-utils/src/helper/plugins/ViewerPlugin.ts index 53b7c46..2057450 100644 --- a/packages/zxtool-cesium-utils/src/v2/plugins/ViewerPlugin.ts +++ b/packages/zxtool-cesium-utils/src/helper/plugins/ViewerPlugin.ts @@ -1,6 +1,6 @@ import * as Cesium from "cesium" import { CesiumHelperPlugin, CesiumHelperPluginProps } from "." -import { CesiumUtil, ViewerUtilSync } from "../../util" +import { CesiumUtil, ViewerUtil } from "../../util" import { genZCUInfo } from "../../util/util" export type ViewerPluginAO = Cesium.Viewer.ConstructorOptions & { @@ -13,7 +13,7 @@ export type ViewerPluginAO = Cesium.Viewer.ConstructorOptions & { const genInfo = genZCUInfo("ViewerPlugin") export class ViewerPlugin implements CesiumHelperPlugin { - private key = Symbol.for("viewer") + private static key = Symbol.for("__viewer__") private addProps: CesiumHelperPluginProps | null = null private _viewer?: Cesium.Viewer @@ -26,7 +26,7 @@ export class ViewerPlugin implements CesiumHelperPlugin { const { cesiumHelper, widgetCollection, emitter } = props const { pluginCollection } = cesiumHelper - const plugin = pluginCollection.get(this.key) as ViewerPlugin + const plugin = pluginCollection.get(ViewerPlugin.key) as ViewerPlugin if (plugin) { console.error(genInfo("已经存在一个 ViewerPlugin, 不能重复添加")) return plugin @@ -35,10 +35,14 @@ export class ViewerPlugin implements CesiumHelperPlugin { const { container, hideWidget = true, fxaa = true, enableIframe, ...rest } = options if (!container) throw new Error("请传入 container") - const viewer = new Cesium.Viewer(container, { ...(hideWidget ? ViewerUtilSync.getHideWidgetOption() : null), ...rest }) + const viewer = new Cesium.Viewer(container, { + // msaaSamples: 4, + ...(hideWidget ? ViewerUtil.getHideWidgetOption() : null), + ...rest, + }) // @ts-ignore if (hideWidget) viewer.cesiumWidget.creditContainer.style.display = "none" - fxaa && ViewerUtilSync.fxaa(viewer) + fxaa && ViewerUtil.fxaa(viewer) enableIframe && CesiumUtil.enableIframe() viewer.scene.globe.depthTestAgainstTerrain = true @@ -46,7 +50,7 @@ export class ViewerPlugin implements CesiumHelperPlugin { widgetCollection.set("viewer", viewer) emitter.emit("viewer", viewer) - pluginCollection.set(this.key, this) + pluginCollection.set(ViewerPlugin.key, this) return this } @@ -61,7 +65,7 @@ export class ViewerPlugin implements CesiumHelperPlugin { this.viewer?.destroy() this._viewer = undefined - pluginCollection.delete(this.key) + pluginCollection.delete(ViewerPlugin.key) this.addProps = null } } diff --git a/packages/zxtool-cesium-utils/src/helper/plugins/WidgetPlugin.ts b/packages/zxtool-cesium-utils/src/helper/plugins/WidgetPlugin.ts new file mode 100644 index 0000000..c780869 --- /dev/null +++ b/packages/zxtool-cesium-utils/src/helper/plugins/WidgetPlugin.ts @@ -0,0 +1,65 @@ +import { Viewer } from "cesium" +import { CesiumHelperPlugin, CesiumHelperPluginProps } from "." +import { genZCUInfo } from "../../util/util" + +const genInfo = genZCUInfo("WidgetPlugin") + +interface WidgetPluginAO { + getWidget: (viewer: Viewer) => T + name: PropertyKey + key: PropertyKey + onRemove?: (widget: T) => void +} + +export class WidgetPlugin implements CesiumHelperPlugin> { + private addProps: CesiumHelperPluginProps | null = null + private options?: WidgetPluginAO + + private _widget?: T + get widget() { + return this._widget + } + + add(props: CesiumHelperPluginProps, options?: WidgetPluginAO): WidgetPlugin { + const { getWidget, name, key } = options ?? {} + if (!getWidget || !name || !key) throw new Error(genInfo("添加插件失败, 缺少必要参数")) + + this.options = options + this.addProps = props + const { cesiumHelper, widgetCollection, emitter } = props + const { pluginCollection } = cesiumHelper + + const plugin = pluginCollection.get(key) as WidgetPlugin + if (plugin) { + console.error(genInfo(`已经存在一个 key 为 ${String(key)} 的 WidgetPlugin, 不能重复添加`)) + return plugin + } + + const viewer = cesiumHelper.getWidget("viewer") + if (!viewer) throw new Error(genInfo("不存在可用的 viewer")) + + const widget = getWidget(viewer) + this._widget = widget + widgetCollection.set(name, widget) + emitter.emit(name, widget) + + pluginCollection.set(key, this) + return this + } + + remove() { + if (!this.addProps || !this.options || !this.widget) throw new Error(genInfo("未添加的插件不能被移除")) + const { cesiumHelper, widgetCollection, emitter } = this.addProps + const { name, key, onRemove } = this.options + const { pluginCollection } = cesiumHelper + + widgetCollection.delete(name) + emitter.clearHistory(name) + + onRemove?.(this.widget) + this._widget = undefined + + pluginCollection.delete(key) + this.addProps = null + } +} diff --git a/packages/zxtool-cesium-utils/src/v2/plugins/index.ts b/packages/zxtool-cesium-utils/src/helper/plugins/index.ts similarity index 87% rename from packages/zxtool-cesium-utils/src/v2/plugins/index.ts rename to packages/zxtool-cesium-utils/src/helper/plugins/index.ts index 50d55ac..83cb6b5 100644 --- a/packages/zxtool-cesium-utils/src/v2/plugins/index.ts +++ b/packages/zxtool-cesium-utils/src/helper/plugins/index.ts @@ -11,4 +11,6 @@ export interface CesiumHelperPlugin remove(options?: RO): void } +export * from "./MouseEventPlugin" export * from "./ViewerPlugin" +export * from "./WidgetPlugin" diff --git a/packages/zxtool-cesium-utils/src/index.ts b/packages/zxtool-cesium-utils/src/index.ts index 9d29d92..f7b0ba8 100644 --- a/packages/zxtool-cesium-utils/src/index.ts +++ b/packages/zxtool-cesium-utils/src/index.ts @@ -1,5 +1,4 @@ +export * from "./helper" export * from "./materialProperty" export type * from "./type" export * from "./util" -export * from "./v2" -export * from "./widget" diff --git a/packages/zxtool-cesium-utils/src/materialProperty/BlinkMaterialProperty.ts b/packages/zxtool-cesium-utils/src/materialProperty/BlinkMaterialProperty.ts new file mode 100644 index 0000000..71f645c --- /dev/null +++ b/packages/zxtool-cesium-utils/src/materialProperty/BlinkMaterialProperty.ts @@ -0,0 +1,101 @@ +import * as Cesium from "cesium" +import { merge } from "lodash" +import { MaterialUtil } from "../util/MaterialUtil" + +const textureShader = /* glsl */ ` +uniform sampler2D img; +uniform vec4 color; +uniform float speed; +uniform bool yoyo; + +czm_material czm_getMaterial(czm_materialInput materialInput){ + czm_material material = czm_getDefaultMaterial(materialInput); + vec2 st = materialInput.st; + float t = czm_frameNumber * speed * 0.01; + t = yoyo ? sin(t * 3.14) * 0.5 + 0.5: fract(t); + + vec4 iColor = texture(img, st); + iColor *= color; + + material.diffuse = iColor.rgb; + material.alpha = iColor.a * t; + return material; +}` + +const colorShader = /* glsl */ ` +uniform vec4 color; +uniform float speed; +uniform bool yoyo; + +czm_material czm_getMaterial(czm_materialInput materialInput){ + czm_material material = czm_getDefaultMaterial(materialInput); + float t = czm_frameNumber * speed * 0.01; + t = yoyo ? sin(t * 3.14) * 0.5 + 0.5: fract(t); + + material.diffuse = color.rgb; + material.alpha = t; + return material; +}` + +const defaultOptions: Required = { + img: "", + color: Cesium.Color.WHITE, + speed: 1.0, + yoyo: true, +} + +export interface BlinkMaterialPropertyOptions { + img?: string + color?: Cesium.Color + speed?: number + yoyo?: boolean +} + +export class BlinkMaterialProperty { + private _definitionChanged: Cesium.Event + private options: Required + + constructor(options: BlinkMaterialPropertyOptions) { + this.options = merge({ ...defaultOptions }, options) + this._definitionChanged = new Cesium.Event() + } + + get isConstant() { + return false + } + + get definitionChanged() { + return this._definitionChanged + } + + getType() { + return this.options.img ? "BlinkTextureMaterial" : "BlinkColorMaterial" + } + + getValue(time: any, result: any) { + if (!Cesium.defined(result)) result = {} + return merge(result, this.options) + } + + equals(other: any) { + return this === other && this.options === other.options + } +} + +MaterialUtil.addMaterial("BlinkTextureMaterial", { + fabric: { + type: "BlinkTextureMaterial", + uniforms: { ...defaultOptions }, + source: textureShader, + }, + translucent: true, +}) + +MaterialUtil.addMaterial("BlinkColorMaterial", { + fabric: { + type: "BlinkColorMaterial", + uniforms: { ...defaultOptions }, + source: colorShader, + }, + translucent: true, +}) diff --git a/packages/zxtool-cesium-utils/src/materialProperty/DiffuseColorMaterialProperty.ts b/packages/zxtool-cesium-utils/src/materialProperty/DiffuseColorMaterialProperty.ts deleted file mode 100644 index b9155c4..0000000 --- a/packages/zxtool-cesium-utils/src/materialProperty/DiffuseColorMaterialProperty.ts +++ /dev/null @@ -1,78 +0,0 @@ -import * as Cesium from "cesium" -import { merge } from "lodash" -import { MaterialUtil } from "../util/MaterialUtil" - -const glsl = /* glsl */ ` -#define PI 3.14159265358979323846 - -uniform vec4 color; -uniform float speed; -uniform bool opacity; - -czm_material czm_getMaterial(czm_materialInput materialInput){ - czm_material material = czm_getDefaultMaterial(materialInput); - vec2 st = materialInput.st; - st -= vec2(0.5); - float t = fract(czm_frameNumber * speed * 0.01); - - float len = length(st); - if(len > (t * 0.5)) discard; - - material.diffuse = color.rgb; - material.alpha = color.a; - if(opacity) material.alpha *= (1.0 - t); - return material; -}` - -export interface DiffuseColorMaterialPropertyOptions { - color?: Cesium.Color - speed?: number - opacity?: boolean -} - -export class DiffuseColorMaterialProperty { - private static readonly type = "DiffuseColorMaterial" - private static readonly defaultOptions: Required = { - color: Cesium.Color.CYAN, - speed: 1.0, - opacity: true, - } - - private _definitionChanged: Cesium.Event - private options: Required - - constructor(options: DiffuseColorMaterialPropertyOptions) { - this.options = merge({ ...DiffuseColorMaterialProperty.defaultOptions }, options) - this._definitionChanged = new Cesium.Event() - - MaterialUtil.addMaterial(DiffuseColorMaterialProperty.type, { - fabric: { - type: DiffuseColorMaterialProperty.type, - uniforms: { ...DiffuseColorMaterialProperty.defaultOptions }, - source: glsl, - }, - translucent: true, - }) - } - - get isConstant() { - return false - } - - get definitionChanged() { - return this._definitionChanged - } - - getType() { - return DiffuseColorMaterialProperty.type - } - - getValue(time: any, result: any) { - if (!Cesium.defined(result)) result = {} - return merge(result, this.options) - } - - equals(other: any) { - return this === other && this.options === other.options - } -} diff --git a/packages/zxtool-cesium-utils/src/materialProperty/DiffuseMaterialProperty.ts b/packages/zxtool-cesium-utils/src/materialProperty/DiffuseMaterialProperty.ts new file mode 100644 index 0000000..9b55d26 --- /dev/null +++ b/packages/zxtool-cesium-utils/src/materialProperty/DiffuseMaterialProperty.ts @@ -0,0 +1,117 @@ +import * as Cesium from "cesium" +import { merge } from "lodash" +import { MaterialUtil } from "../util/MaterialUtil" + +const colorShader = /* glsl */ ` +uniform vec4 color; +uniform float speed; +uniform bool opacity; + +czm_material czm_getMaterial(czm_materialInput materialInput){ + czm_material material = czm_getDefaultMaterial(materialInput); + vec2 st = materialInput.st; + st -= vec2(0.5); + float t = fract(czm_frameNumber * speed * 0.01); + + float len = length(st); + if(len > (t * 0.5)) discard; + + material.diffuse = color.rgb; + material.alpha = color.a; + if(opacity) material.alpha *= (1.0 - t); + return material; +}` + +const textureShader = /* glsl */ ` +uniform sampler2D img; +uniform vec4 color; +uniform float speed; +uniform float scale; +uniform bool opacity; + +czm_material czm_getMaterial(czm_materialInput materialInput){ + czm_material material = czm_getDefaultMaterial(materialInput); + vec2 st = materialInput.st; + + float t = fract(czm_frameNumber * speed * 0.005); + float _t = 1.0 / (t * scale + 1e-3); + + st -= vec2(0.5); + st *= _t; + st += vec2(0.5); + + if(st.x<0.0 || st.x>1.0 || st.y<0.0 || st.y>1.0) discard; + + vec4 _color = texture(img, st); + _color *= color; + + material.diffuse = _color.rgb; + material.alpha = _color.a; + if(opacity) material.alpha *= (1.0 - t); + return material; +}` + +const defaultOptions: Required = { + img: "", + color: Cesium.Color.WHITE, + speed: 1.0, + opacity: true, + scale: 1.0, +} + +export interface DiffuseMaterialPropertyOptions { + img?: string + color?: Cesium.Color + speed?: number + opacity?: boolean + scale?: number +} + +export class DiffuseMaterialProperty { + private _definitionChanged: Cesium.Event + private options: Required + + constructor(options: DiffuseMaterialPropertyOptions) { + this.options = merge({ ...defaultOptions }, options) + this._definitionChanged = new Cesium.Event() + } + + get isConstant() { + return false + } + + get definitionChanged() { + return this._definitionChanged + } + + getType() { + return this.options.img ? "DiffuseTextureMaterial" : "DiffuseColorMaterial" + } + + getValue(time: any, result: any) { + if (!Cesium.defined(result)) result = {} + return merge(result, this.options) + } + + equals(other: any) { + return this === other && this.options === other.options + } +} + +MaterialUtil.addMaterial("DiffuseColorMaterial", { + fabric: { + type: "DiffuseColorMaterial", + uniforms: { ...defaultOptions }, + source: colorShader, + }, + translucent: true, +}) + +MaterialUtil.addMaterial("DiffuseTextureMaterial", { + fabric: { + type: "DiffuseTextureMaterial", + uniforms: { ...defaultOptions }, + source: textureShader, + }, + translucent: true, +}) diff --git a/packages/zxtool-cesium-utils/src/materialProperty/DiffuseTextureMaterialProperty.ts b/packages/zxtool-cesium-utils/src/materialProperty/DiffuseTextureMaterialProperty.ts deleted file mode 100644 index f730f56..0000000 --- a/packages/zxtool-cesium-utils/src/materialProperty/DiffuseTextureMaterialProperty.ts +++ /dev/null @@ -1,87 +0,0 @@ -import * as Cesium from "cesium" -import { merge } from "lodash" -import { MaterialUtil } from "../util/MaterialUtil" - -const glsl = /* glsl */ ` -uniform float speed; -uniform sampler2D img; -uniform float scale; -uniform bool round; -uniform bool opacity; - -czm_material czm_getMaterial(czm_materialInput materialInput){ - czm_material material = czm_getDefaultMaterial(materialInput); - vec2 st = materialInput.st; - if(round && distance(st, vec2(0.5)) > 0.5) discard; - - float t = fract(czm_frameNumber * speed * 0.005); - float _t = 1.0 / (t * scale + 1e-3); - - st -= vec2(0.5); - st *= _t; - st += vec2(0.5); - - vec4 color = texture(img, st); - - material.diffuse = color.rgb; - material.alpha = color.a; - if(opacity) material.alpha *= (1.0 - t); - return material; -}` - -export interface DiffuseTextureMaterialPropertyOptions { - img: string - speed?: number - scale?: number - round?: boolean - opacity?: boolean -} - -export class DiffuseTextureMaterialProperty { - private static readonly type = "DiffuseTextureMaterial" - private static readonly defaultOptions: Required = { - img: "", - speed: 1.0, - scale: 1.0, - round: true, - opacity: true, - } - - private _definitionChanged: Cesium.Event - private options: Required - - constructor(options: DiffuseTextureMaterialPropertyOptions) { - this.options = merge({ ...DiffuseTextureMaterialProperty.defaultOptions }, options) - this._definitionChanged = new Cesium.Event() - - MaterialUtil.addMaterial(DiffuseTextureMaterialProperty.type, { - fabric: { - type: DiffuseTextureMaterialProperty.type, - uniforms: { ...DiffuseTextureMaterialProperty.defaultOptions }, - source: glsl, - }, - translucent: true, - }) - } - - get isConstant() { - return false - } - - get definitionChanged() { - return this._definitionChanged - } - - getType() { - return DiffuseTextureMaterialProperty.type - } - - getValue(time: any, result: any) { - if (!Cesium.defined(result)) result = {} - return merge(result, this.options) - } - - equals(other: any) { - return this === other && this.options === other.options - } -} diff --git a/packages/zxtool-cesium-utils/src/materialProperty/FlowColorMaterialProperty.ts b/packages/zxtool-cesium-utils/src/materialProperty/FlowColorMaterialProperty.ts index ddf19a3..00f1276 100644 --- a/packages/zxtool-cesium-utils/src/materialProperty/FlowColorMaterialProperty.ts +++ b/packages/zxtool-cesium-utils/src/materialProperty/FlowColorMaterialProperty.ts @@ -2,7 +2,7 @@ import * as Cesium from "cesium" import { merge } from "lodash" import { MaterialUtil } from "../util/MaterialUtil" -const glsl = /* glsl */ ` +const shader = /* glsl */ ` uniform vec4 color; uniform vec4 bgColor; uniform float speed; @@ -32,6 +32,15 @@ czm_material czm_getMaterial(czm_materialInput materialInput){ return material; }` +const type = "FlowColorMaterial" +const defaultOptions: Required = { + color: Cesium.Color.WHITE, + bgColor: Cesium.Color.WHITE.withAlpha(0.0), + speed: 1.0, + percent: 0.1, + gradient: true, +} + export interface FlowColorMaterialPropertyOptions { color?: Cesium.Color bgColor?: Cesium.Color @@ -41,30 +50,12 @@ export interface FlowColorMaterialPropertyOptions { } export class FlowColorMaterialProperty { - private static readonly type = "FlowColorMaterial" - private static readonly defaultOptions: Required = { - color: Cesium.Color.CYAN, - bgColor: Cesium.Color.WHITE.withAlpha(0.0), - speed: 1.0, - percent: 0.1, - gradient: true, - } - private _definitionChanged: Cesium.Event private options: Required constructor(options: FlowColorMaterialPropertyOptions) { - this.options = merge({ ...FlowColorMaterialProperty.defaultOptions }, options) + this.options = merge({ ...defaultOptions }, options) this._definitionChanged = new Cesium.Event() - - MaterialUtil.addMaterial(FlowColorMaterialProperty.type, { - fabric: { - type: FlowColorMaterialProperty.type, - uniforms: { ...FlowColorMaterialProperty.defaultOptions }, - source: glsl, - }, - translucent: true, - }) } get isConstant() { @@ -76,7 +67,7 @@ export class FlowColorMaterialProperty { } getType() { - return FlowColorMaterialProperty.type + return type } getValue(time: any, result: any) { @@ -88,3 +79,12 @@ export class FlowColorMaterialProperty { return this === other && this.options === other.options } } + +MaterialUtil.addMaterial(type, { + fabric: { + type: type, + uniforms: { ...defaultOptions }, + source: shader, + }, + translucent: true, +}) diff --git a/packages/zxtool-cesium-utils/src/materialProperty/FlowTextureMaterialProperty.ts b/packages/zxtool-cesium-utils/src/materialProperty/FlowTextureMaterialProperty.ts index 13f27b7..2d60c4e 100644 --- a/packages/zxtool-cesium-utils/src/materialProperty/FlowTextureMaterialProperty.ts +++ b/packages/zxtool-cesium-utils/src/materialProperty/FlowTextureMaterialProperty.ts @@ -2,57 +2,55 @@ import * as Cesium from "cesium" import { merge } from "lodash" import { MaterialUtil } from "../util/MaterialUtil" -const glsl = /* glsl */ ` -uniform float speed; +const shader = /* glsl */ ` uniform sampler2D img; -uniform bool yAxis; +uniform vec4 color; +uniform float speed; +uniform float rotate; + +mat2 rotate2d(float angle) { + return mat2( + cos(angle), - sin(angle), + sin(angle), cos(angle) + ); +} czm_material czm_getMaterial(czm_materialInput materialInput){ czm_material material = czm_getDefaultMaterial(materialInput); vec2 st = materialInput.st; + st *= rotate2d(rotate); float t = fract(czm_frameNumber * speed / 500.0); - vec4 color = vec4(0.0); - if(yAxis) { - color = texture(img, vec2(fract(st.t - t), st.s)); - } else { - color = texture(img, vec2(fract(st.s - t), st.t)); - } + vec4 _color = texture(img, vec2(fract(st.s - t), st.t)); + _color *= color; - material.diffuse = color.rgb; - material.alpha = color.a; + material.diffuse = _color.rgb; + material.alpha = _color.a; return material; }` +const type = "FlowTextureMaterial" +const defaultOptions: Required = { + img: "", + color: Cesium.Color.WHITE, + speed: 1.0, + rotate: 0.0, +} + export interface FlowTextureMaterialPropertyOptions { img: string + color?: Cesium.Color speed?: number - yAxis?: boolean + rotate?: number } export class FlowTextureMaterialProperty { - private static readonly type = "FlowTextureMaterial" - private static readonly defaultOptions: Required = { - img: "", - speed: 1.0, - yAxis: false, - } - private _definitionChanged: Cesium.Event private options: Required constructor(options: FlowTextureMaterialPropertyOptions) { - this.options = merge({ ...FlowTextureMaterialProperty.defaultOptions }, options) + this.options = merge({ ...defaultOptions }, options) this._definitionChanged = new Cesium.Event() - - MaterialUtil.addMaterial(FlowTextureMaterialProperty.type, { - fabric: { - type: FlowTextureMaterialProperty.type, - uniforms: { ...FlowTextureMaterialProperty.defaultOptions }, - source: glsl, - }, - translucent: true, - }) } get isConstant() { @@ -64,7 +62,7 @@ export class FlowTextureMaterialProperty { } getType() { - return FlowTextureMaterialProperty.type + return type } getValue(time: any, result: any) { @@ -76,3 +74,12 @@ export class FlowTextureMaterialProperty { return this === other && this.options === other.options } } + +MaterialUtil.addMaterial(type, { + fabric: { + type: type, + uniforms: { ...defaultOptions }, + source: shader, + }, + translucent: true, +}) diff --git a/packages/zxtool-cesium-utils/src/materialProperty/RotateColorMaterialProperty.ts b/packages/zxtool-cesium-utils/src/materialProperty/RotateColorMaterialProperty.ts deleted file mode 100644 index c111c2d..0000000 --- a/packages/zxtool-cesium-utils/src/materialProperty/RotateColorMaterialProperty.ts +++ /dev/null @@ -1,86 +0,0 @@ -import * as Cesium from "cesium" -import { merge } from "lodash" -import { MaterialUtil } from "../util/MaterialUtil" - -const glsl = /* glsl */ ` -#define PI 3.14159265358979323846 - -uniform vec4 color; -uniform float speed; - -mat2 rotate2d(float angle) { - return mat2( - cos(angle), - sin(angle), - sin(angle), cos(angle) - ); -} - -czm_material czm_getMaterial(czm_materialInput materialInput){ - czm_material material = czm_getDefaultMaterial(materialInput); - vec2 st = materialInput.st; - st -= vec2(0.5); - float t = czm_frameNumber * speed * 0.1; - st *= rotate2d(t); - - float r = length(st); - if (r > 0.5) discard; - - // [-PI, PI] - float angle = atan(st.y, st.x); - angle = (angle + PI) / (PI * 2.0); - - material.diffuse = color.rgb; - material.alpha = angle; - return material; -}` - -export interface RotateColorMaterialPropertyOptions { - color?: Cesium.Color - speed?: number -} - -export class RotateColorMaterialProperty { - private static readonly type = "RotateColorMaterial" - private static readonly defaultOptions: Required = { - color: Cesium.Color.CYAN, - speed: 1.0, - } - - private _definitionChanged: Cesium.Event - private options: Required - - constructor(options: RotateColorMaterialPropertyOptions) { - this.options = merge({ ...RotateColorMaterialProperty.defaultOptions }, options) - this._definitionChanged = new Cesium.Event() - - MaterialUtil.addMaterial(RotateColorMaterialProperty.type, { - fabric: { - type: RotateColorMaterialProperty.type, - uniforms: { ...RotateColorMaterialProperty.defaultOptions }, - source: glsl, - }, - translucent: true, - }) - } - - get isConstant() { - return false - } - - get definitionChanged() { - return this._definitionChanged - } - - getType() { - return RotateColorMaterialProperty.type - } - - getValue(time: any, result: any) { - if (!Cesium.defined(result)) result = {} - return merge(result, this.options) - } - - equals(other: any) { - return this === other && this.options === other.options - } -} diff --git a/packages/zxtool-cesium-utils/src/materialProperty/RotateTextureMaterialProperty.ts b/packages/zxtool-cesium-utils/src/materialProperty/RotateMaterialProperty.ts similarity index 51% rename from packages/zxtool-cesium-utils/src/materialProperty/RotateTextureMaterialProperty.ts rename to packages/zxtool-cesium-utils/src/materialProperty/RotateMaterialProperty.ts index 918bd98..c4db742 100644 --- a/packages/zxtool-cesium-utils/src/materialProperty/RotateTextureMaterialProperty.ts +++ b/packages/zxtool-cesium-utils/src/materialProperty/RotateMaterialProperty.ts @@ -2,11 +2,13 @@ import * as Cesium from "cesium" import { merge } from "lodash" import { MaterialUtil } from "../util/MaterialUtil" -const glsl = /* glsl */ ` +const shader = /* glsl */ ` #define PI 3.14159265358979323846 uniform sampler2D img; +uniform vec4 color; uniform float speed; +uniform bool clockwise; mat2 rotate2d(float angle) { return mat2( @@ -19,45 +21,43 @@ czm_material czm_getMaterial(czm_materialInput materialInput){ czm_material material = czm_getDefaultMaterial(materialInput); vec2 st = materialInput.st; + float _clockwise = clockwise ? 1.0 : -1.0; + float t = czm_frameNumber * speed * 0.01 * _clockwise; st -= vec2(0.5); - float t = czm_frameNumber * speed * 0.01; st *= rotate2d(t); st += vec2(0.5); - vec4 color = texture(img, st); + vec4 _color = texture(img, st); + _color *= color; - material.diffuse = color.rgb; - material.alpha = color.a; + material.diffuse = _color.rgb; + material.alpha = _color.a; return material; }` -export interface RotateTextureMaterialPropertyOptions { +const type = "RotateMaterial" + +const defaultOptions: Required = { + img: "", + color: Cesium.Color.WHITE, + speed: 1.0, + clockwise: true, +} + +export interface RotateMaterialPropertyOptions { img: string + color?: Cesium.Color speed?: number + clockwise?: boolean } -export class RotateTextureMaterialProperty { - private static readonly type = "RotateTextureMaterial" - private static readonly defaultOptions: Required = { - img: "", - speed: 1.0, - } - +export class RotateMaterialProperty { private _definitionChanged: Cesium.Event - private options: Required + private options: Required - constructor(options: RotateTextureMaterialPropertyOptions) { - this.options = merge({ ...RotateTextureMaterialProperty.defaultOptions }, options) + constructor(options: RotateMaterialPropertyOptions) { + this.options = merge({ ...defaultOptions }, options) this._definitionChanged = new Cesium.Event() - - MaterialUtil.addMaterial(RotateTextureMaterialProperty.type, { - fabric: { - type: RotateTextureMaterialProperty.type, - uniforms: { ...RotateTextureMaterialProperty.defaultOptions }, - source: glsl, - }, - translucent: true, - }) } get isConstant() { @@ -69,7 +69,7 @@ export class RotateTextureMaterialProperty { } getType() { - return RotateTextureMaterialProperty.type + return type } getValue(time: any, result: any) { @@ -81,3 +81,12 @@ export class RotateTextureMaterialProperty { return this === other && this.options === other.options } } + +MaterialUtil.addMaterial(type, { + fabric: { + type: type, + uniforms: { ...defaultOptions }, + source: shader, + }, + translucent: true, +}) diff --git a/packages/zxtool-cesium-utils/src/materialProperty/TrailLine2MaterialProperty.ts b/packages/zxtool-cesium-utils/src/materialProperty/TrailLine2MaterialProperty.ts deleted file mode 100644 index 30452bd..0000000 --- a/packages/zxtool-cesium-utils/src/materialProperty/TrailLine2MaterialProperty.ts +++ /dev/null @@ -1,104 +0,0 @@ -import * as Cesium from "cesium" -import { merge } from "lodash" -import { MaterialUtil } from "../util/MaterialUtil" - -const glsl = /* glsl */ ` -uniform vec4 color; -uniform vec4 bgColor; -uniform float speed; -uniform float percent; -uniform bool gradient; -uniform float count; - -const int N = 10; - -czm_material czm_getMaterial(czm_materialInput materialInput){ - czm_material material = czm_getDefaultMaterial(materialInput); - vec2 st = materialInput.st; - - float t = fract(czm_frameNumber * speed / 500.0); - - float alphas[N]; - float alpha; - - int i_count = int(min(float(N), count)); - for(int i = 0; i < N; i ++ ) { - if(i > i_count) break; - - float ti = fract(t + float(i) / float(i_count)); - ti *= (1.0 + percent); - - if(gradient) { - alphas[i] = smoothstep(ti - percent, ti , st.s) * step(-ti , - st.s); - } else { - alphas[i] = step(ti - percent, st.s) * step(-ti, - st.s); - } - - alpha += alphas[i] * color.a; - } - - vec4 mixedColor = mix(bgColor, vec4(color.rgb, 1.0), alpha); - - material.diffuse = mixedColor.rgb; - material.alpha = mixedColor.a; - return material; -}` - -export interface TrailLine2MaterialPropertyOptions { - color?: Cesium.Color - bgColor?: Cesium.Color - speed?: number - percent?: number - gradient?: boolean - count?: number -} - -export class TrailLine2MaterialProperty { - private static readonly type = "TrailLine2Material" - private static readonly defaultOptions: Required = { - color: Cesium.Color.CYAN, - bgColor: Cesium.Color.WHITE.withAlpha(0.0), - speed: 1.0, - percent: 0.1, - gradient: true, - count: 1.0, - } - - private _definitionChanged: Cesium.Event - private options: Required - - constructor(options: TrailLine2MaterialPropertyOptions) { - this.options = merge({ ...TrailLine2MaterialProperty.defaultOptions }, options) - this._definitionChanged = new Cesium.Event() - - MaterialUtil.addMaterial(TrailLine2MaterialProperty.type, { - fabric: { - type: TrailLine2MaterialProperty.type, - uniforms: { ...TrailLine2MaterialProperty.defaultOptions }, - source: glsl, - }, - translucent: true, - }) - } - - get isConstant() { - return false - } - - get definitionChanged() { - return this._definitionChanged - } - - getType() { - return TrailLine2MaterialProperty.type - } - - getValue(time: any, result: any) { - if (!Cesium.defined(result)) result = {} - return merge(result, this.options) - } - - equals(other: any) { - return this === other && this.options === other.options - } -} diff --git a/packages/zxtool-cesium-utils/src/materialProperty/TrailLineMaterialProperty.ts b/packages/zxtool-cesium-utils/src/materialProperty/TrailLineMaterialProperty.ts deleted file mode 100644 index 9739f62..0000000 --- a/packages/zxtool-cesium-utils/src/materialProperty/TrailLineMaterialProperty.ts +++ /dev/null @@ -1,64 +0,0 @@ -import * as Cesium from "cesium" -import { MaterialUtil } from "../util/MaterialUtil" - -const glsl = ` -czm_material czm_getMaterial(czm_materialInput materialInput) -{ - czm_material material = czm_getDefaultMaterial(materialInput); - vec2 st = materialInput.st; - - // [0, 1] - float t = fract(czm_frameNumber * speed / 1000.0); - - vec4 sampledColor = texture2D(image, vec2(fract(st.s - t), st.t)); - material.alpha = sampledColor.a; - material.diffuse = sampledColor.rgb; - return material; -}` - -export class TrailLineMaterialProperty { - _definitionChanged: Cesium.Event - - _image: string - _speed: number - - constructor(image: string, speed = 5) { - this._definitionChanged = new Cesium.Event() - - this._speed = speed - this._image = image - } - - get isConstant() { - return false - } - - get definitionChanged() { - return this._definitionChanged - } - - getType() { - return "TrailLineMaterial" - } - - getValue(time: any, result: any) { - if (!Cesium.defined(result)) result = {} - - result.speed = this._speed - result.image = this._image - return result - } - - equals(other: any) { - return this === other - } -} - -MaterialUtil.addMaterial("TrailLineMaterial", { - fabric: { - type: "TrailLineMaterial", - uniforms: { image: "", speed: 5 }, - source: glsl, - }, - translucent: true, -}) diff --git a/packages/zxtool-cesium-utils/src/materialProperty/index.ts b/packages/zxtool-cesium-utils/src/materialProperty/index.ts index 2982876..eb9625e 100644 --- a/packages/zxtool-cesium-utils/src/materialProperty/index.ts +++ b/packages/zxtool-cesium-utils/src/materialProperty/index.ts @@ -1,8 +1,5 @@ -export * from "./DiffuseColorMaterialProperty" -export * from "./DiffuseTextureMaterialProperty" +export * from "./BlinkMaterialProperty" +export * from "./DiffuseMaterialProperty" export * from "./FlowColorMaterialProperty" export * from "./FlowTextureMaterialProperty" -export * from "./RotateColorMaterialProperty" -export * from "./RotateTextureMaterialProperty" -export * from "./TrailLine2MaterialProperty" -export * from "./TrailLineMaterialProperty" +export * from "./RotateMaterialProperty" diff --git a/packages/zxtool-cesium-utils/src/type/type.d.ts b/packages/zxtool-cesium-utils/src/type/type.d.ts new file mode 100644 index 0000000..ce0a258 --- /dev/null +++ b/packages/zxtool-cesium-utils/src/type/type.d.ts @@ -0,0 +1,23 @@ +import { IObj } from "@zxtool/utils" + +declare module "cesium" { + export interface Entity { + __customField?: { + hover?: boolean + onClick?: (data: Entity, raw: any) => void + onEnter?: (data: Entity, raw: any) => void + onLeave?: (data: Entity, raw: any) => void + } & IObj + } + export interface Primitive { + __customField?: { + hover?: boolean + onClick?: (data: Primitive, raw: any) => void + onEnter?: (data: Primitive, raw: any) => void + onLeave?: (data: Primitive, raw: any) => void + } & IObj + } + export interface Cesium3DTileset { + __customField?: IObj + } +} diff --git a/packages/zxtool-cesium-utils/src/util/CesiumUtil.ts b/packages/zxtool-cesium-utils/src/util/CesiumUtil.ts index 95ff7e2..283ec54 100644 --- a/packages/zxtool-cesium-utils/src/util/CesiumUtil.ts +++ b/packages/zxtool-cesium-utils/src/util/CesiumUtil.ts @@ -1,9 +1,11 @@ +const enableIframe = () => { + const iframe = document.querySelector(".cesium-infoBox-iframe") + if (iframe) { + iframe.setAttribute("sandbox", "allow-same-origin allow-scripts allow-popups allow-forms") + iframe.setAttribute("src", "") + } +} + export const CesiumUtil = { - enableIframe() { - const iframe = document.querySelector(".cesium-infoBox-iframe") - if (iframe) { - iframe.setAttribute("sandbox", "allow-same-origin allow-scripts allow-popups allow-forms") - iframe.setAttribute("src", "") - } - }, + enableIframe, } diff --git a/packages/zxtool-cesium-utils/src/util/EntityUtil.ts b/packages/zxtool-cesium-utils/src/util/EntityUtil.ts index 30194ae..328aec3 100644 --- a/packages/zxtool-cesium-utils/src/util/EntityUtil.ts +++ b/packages/zxtool-cesium-utils/src/util/EntityUtil.ts @@ -1,6 +1,4 @@ import * as Cesium from "cesium" -import { ViewerHelper } from "../widget/ViewerHelper" -import { EntityUtilSync } from "./EntityUtilSync" export type IEntities = Cesium.Entity[] | Cesium.DataSource | Cesium.EntityCollection @@ -8,18 +6,6 @@ const getProperties = (entity: Cesium.Entity) => { return entity.properties?.getValue(new Cesium.JulianDate()) ?? {} } -const pickEntity = (windowPosition: Cesium.Cartesian2, viewer?: Cesium.Viewer) => { - return ViewerHelper.getViewerPromise(undefined, viewer).then(viewer => { - return EntityUtilSync.pickEntity(windowPosition, viewer) - }) -} - -const drillPickEntities = (windowPosition: Cesium.Cartesian2, viewer?: Cesium.Viewer) => { - return ViewerHelper.getViewerPromise(undefined, viewer).then(viewer => { - return EntityUtilSync.drillPickEntities(windowPosition, viewer) - }) -} - const getEntities = (target: IEntities) => { if (Array.isArray(target)) return target if (target instanceof Cesium.EntityCollection) return target.values @@ -28,7 +14,5 @@ const getEntities = (target: IEntities) => { export const EntityUtil = { getProperties, - pickEntity, - drillPickEntities, getEntities, } diff --git a/packages/zxtool-cesium-utils/src/util/EntityUtilSync.ts b/packages/zxtool-cesium-utils/src/util/EntityUtilSync.ts deleted file mode 100644 index 5bc50c2..0000000 --- a/packages/zxtool-cesium-utils/src/util/EntityUtilSync.ts +++ /dev/null @@ -1,33 +0,0 @@ -import * as Cesium from "cesium" - -const pickEntity = (windowPosition: Cesium.Cartesian2, viewer: Cesium.Viewer) => { - const picked = viewer.scene.pick(windowPosition) - if (!Cesium.defined(picked)) return null - - const id = Cesium.defaultValue(picked.id, picked.primitive.id) - if (id instanceof Cesium.Entity) return id - return null -} - -const drillPickEntities = (windowPosition: Cesium.Cartesian2, viewer: Cesium.Viewer) => { - let picked, entity - const pickedPrimitives = viewer.scene.drillPick(windowPosition) - const length = pickedPrimitives.length - const result = [] - const hash = {} - - for (let i = 0; i < length; i++) { - picked = pickedPrimitives[i] - entity = Cesium.defaultValue(picked.id, picked.primitive.id) - if (entity instanceof Cesium.Entity && !Cesium.defined(hash[entity.id])) { - result.push(entity) - hash[entity.id] = true - } - } - return result -} - -export const EntityUtilSync = { - pickEntity, - drillPickEntities, -} diff --git a/packages/zxtool-cesium-utils/src/util/TilesetUtil.ts b/packages/zxtool-cesium-utils/src/util/TilesetUtil.ts index 02c8afa..118c259 100644 --- a/packages/zxtool-cesium-utils/src/util/TilesetUtil.ts +++ b/packages/zxtool-cesium-utils/src/util/TilesetUtil.ts @@ -1,51 +1,88 @@ import * as Cesium from "cesium" -import { cloneDeep, isNil } from "lodash" -import { CoordHelper } from "../widget/CoordHelper" +import { Cesium3DTileset } from "cesium" +import { CoordHelper } from "../helper" +import { genZCUInfo } from "./util" -const padArr = (arr: any[], value: any, length?: number) => { - return Array.from(Array(length ?? arr.length)).map((item, index) => (isNil(arr[index]) ? value : arr[index])) -} +const genInfo = genZCUInfo("TilesetUtil") -type TransformModelFunc = (props: { - tileset: Cesium.Cesium3DTileset - alpha?: number - position?: [number, number, number?] // 经 纬 高 - translate?: [number, number, number] - rotate?: [number, number, number] - scale?: [number, number, number] -}) => Cesium.Cesium3DTileset - -const headingPitchRollToFixedFrame = (origin: Cesium.Cartesian3, headingPitchRoll: [number, number, number]) => { - return Cesium.Transforms.headingPitchRollToFixedFrame( - origin, - new Cesium.HeadingPitchRoll(...headingPitchRoll.map(item => Cesium.Math.toRadians(item))), - ) +export interface TransformTilesetOptions { + position?: Cesium.Cartesian3 + translate?: Cesium.Cartesian3 + scale?: Cesium.Cartesian3 + rotation?: Cesium.HeadingPitchRoll + viewer?: Cesium.Viewer } -const transformBIMModel: TransformModelFunc = props => { - const { tileset, alpha = 1, position: p = [], translate: t = [], rotate: r = [], scale: s = [] } = props - const [position, translate, rotate] = [padArr(p, 0, 3), padArr(t, 0, 3), padArr(r, 0, 3)] - const scale = padArr(s, 1, 3).map(item => item || 1) - - if (!(tileset.boundingSphere as any).__center) { - ;(tileset.boundingSphere as any).__center = cloneDeep(tileset.boundingSphere.center) +/** + * 调整位置, 平移, 缩放 + * 一般用作 3DTile 模型 + */ +const transformTileset = (tileset: Cesium.Cesium3DTileset, options: TransformTilesetOptions) => { + const { + position, + translate, + scale = new Cesium.Cartesian3(1, 1, 1), + rotation = new Cesium.HeadingPitchRoll(0, 0, 0), + viewer, + } = options + const backup = { + center: tileset.boundingSphere.center.clone(), + transform: tileset.root.transform.clone(), + modelMatrix: tileset.modelMatrix.clone(), } + let center = position ?? tileset.boundingSphere.center + if (translate) center = CoordHelper.translate(center, translate, viewer) + const translateMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center) + const scaleMatrix = Cesium.Matrix4.fromScale(scale) + const rotationMatrix = Cesium.Matrix4.fromRotation(Cesium.Matrix3.fromHeadingPitchRoll(rotation)) - let center = (tileset.boundingSphere as any).__center - if (position[0] && position[1]) center = Cesium.Cartesian3.fromDegrees(...(p as [number, number, number?])) - center = CoordHelper.translate(center, translate) + const temp = Cesium.Matrix4.multiply(rotationMatrix, scaleMatrix, new Cesium.Matrix4()) + const transform = Cesium.Matrix4.multiply(translateMatrix, temp, new Cesium.Matrix4()) - let modelMatrix = headingPitchRollToFixedFrame(center, rotate as [number, number, number]) - modelMatrix = Cesium.Matrix4.multiply(modelMatrix, Cesium.Matrix4.fromScale(new Cesium.Cartesian3(...scale)), modelMatrix) - tileset.root.transform = modelMatrix + tileset.root.transform = transform + tileset.modelMatrix = Cesium.Matrix4.IDENTITY + tileset.__customField = backup +} - alpha && (tileset.style = new Cesium.Cesium3DTileStyle({ color: `color('rgba(255,255,255,${alpha})')` })) +export interface TranslateTilesetOptions { + position?: Cesium.Cartesian3 + translate?: Cesium.Cartesian3 + viewer?: Cesium.Viewer +} +/** + * 调整位置, 会保持原来的姿态 + * 一般用作 3DTile 倾斜摄影 + */ +const translateTileset = (tileset: Cesium3DTileset, options: TranslateTilesetOptions) => { + const { position, translate, viewer } = options + const backup = { + center: tileset.boundingSphere.center.clone(), + transform: tileset.root.transform.clone(), + modelMatrix: tileset.modelMatrix.clone(), + } + let center = position ?? tileset.boundingSphere.center + if (translate) center = CoordHelper.translate(center, translate, viewer) - return tileset + const subtract = Cesium.Cartesian3.subtract(center, tileset.boundingSphere.center, new Cesium.Cartesian3()) + tileset.modelMatrix = Cesium.Matrix4.fromTranslation(subtract) + tileset.__customField = backup } -const TilesetUtil = { - transformBIMModel, +/** + * 恢复 3DTile 初始状态 + * 只对使用 transformTileset/translateTileset 调整状态的模型有效 + */ +const restoreTileset = (tileset: Cesium3DTileset) => { + const { transform, modelMatrix } = tileset.__customField ?? ({} as any) + if (!transform || !modelMatrix) { + return console.error(genInfo("restoreTileset 只对使用 transformTileset/translateTileset 调整状态的模型有效")) + } + tileset.modelMatrix = modelMatrix + tileset.root.transform = transform } -export default TilesetUtil +export const TilesetUtil = { + transformTileset, + translateTileset, + restoreTileset, +} diff --git a/packages/zxtool-cesium-utils/src/util/Tooltip.ts b/packages/zxtool-cesium-utils/src/util/Tooltip.ts new file mode 100644 index 0000000..29a6647 --- /dev/null +++ b/packages/zxtool-cesium-utils/src/util/Tooltip.ts @@ -0,0 +1,73 @@ +import type { CustomDataSource, Entity, Viewer } from "cesium" +import * as Cesium from "cesium" +import { CoordHelper, MouseEventHelper } from "../helper" +import { ViewerUtil } from "./ViewerUtil" +import { genZCUInfo } from "./util" + +const genInfo = genZCUInfo("Tooltip") + +export class Tooltip { + private static key = "__tooltip__" + + private viewer: Viewer + private dataSource: CustomDataSource + private eventHelper: MouseEventHelper + private hasTooltip = false + + private position = Cesium.Cartesian3.fromDegrees(0, 0) + private entity: Entity | null = null + + constructor(viewer: Viewer) { + this.viewer = viewer + this.dataSource = ViewerUtil.getCustomDataSource({ viewer, name: Tooltip.key, autoCreate: true })! + this.eventHelper = new MouseEventHelper(viewer) + } + + create(text: string) { + if (this.hasTooltip) { + return console.error(genInfo("已经创建了一个 Tooltip")) + } + this.hasTooltip = true + + this.entity = new Cesium.Entity({ + // @ts-ignore + position: new Cesium.CallbackProperty(() => this.position, false), + label: { + text, + font: "30px sans-serif", + scale: 0.6, + showBackground: true, + backgroundColor: Cesium.Color.BLACK.withAlpha(0.6), + backgroundPadding: new Cesium.Cartesian2(20, 15), + pixelOffset: new Cesium.Cartesian2(20, 0), + horizontalOrigin: Cesium.HorizontalOrigin.LEFT, + disableDepthTestDistance: Number.MAX_SAFE_INTEGER, + }, + }) + + this.dataSource.entities.add(this.entity) + + this.eventHelper.addEvent({ + key: Tooltip.key, + type: "MOUSE_MOVE", + cb: ({ endPosition }) => { + const c3 = CoordHelper.screenCoord2sceneC3(endPosition, this.viewer) + if (c3) this.position = c3 + }, + }) + } + + update(text: string) { + if (this.entity) { + this.entity.label!.text = new Cesium.ConstantProperty(text) + } + } + + remove() { + this.dataSource.entities.removeAll() + this.eventHelper.clear() + this.entity = null + this.position = Cesium.Cartesian3.fromDegrees(0, 0) + this.hasTooltip = false + } +} diff --git a/packages/zxtool-cesium-utils/src/util/ViewerUtil.ts b/packages/zxtool-cesium-utils/src/util/ViewerUtil.ts index afd8711..1142493 100644 --- a/packages/zxtool-cesium-utils/src/util/ViewerUtil.ts +++ b/packages/zxtool-cesium-utils/src/util/ViewerUtil.ts @@ -1,57 +1,160 @@ import * as Cesium from "cesium" +import { Viewer } from "cesium" import type { FeatureCollection } from "geojson" -import { IPrimitive } from "../widget/PrimitiveManager" -import { ViewerHelper } from "../widget/ViewerHelper" -import { ViewerUtilSync } from "./ViewerUtilSync" +import { IPrimitive } from "../helper/PrimitiveManager" -const hideWidget = (viewer?: Cesium.Viewer) => { - setTimeout(() => { - ViewerHelper.getViewerPromise(undefined, viewer).then(ViewerUtilSync.hideWidget) - }) +const getHideWidgetOption = () => { + return { + infoBox: false, // 信息窗口 + geocoder: false, // 查询按钮 + homeButton: false, // home按钮 + sceneModePicker: false, // 地图模式 + baseLayerPicker: false, // 图层选择 + navigationHelpButton: false, // 帮助按钮 + animation: false, // 动画控制器 + timeline: false, // 时间轴 + fullscreenButton: false, // 全屏按钮 + } +} + +const hideWidget = (viewer: Viewer) => { + viewer.infoBox.isDestroyed() || viewer.infoBox.destroy() + viewer.geocoder.isDestroyed() || viewer.geocoder.destroy() + viewer.homeButton.isDestroyed() || viewer.homeButton.destroy() + viewer.sceneModePicker.isDestroyed() || viewer.sceneModePicker.destroy() + viewer.baseLayerPicker.isDestroyed() || viewer.baseLayerPicker.destroy() + viewer.navigationHelpButton.isDestroyed() || viewer.navigationHelpButton.destroy() + viewer.animation.isDestroyed() || viewer.animation.destroy() + viewer.timeline.isDestroyed() || viewer.timeline.destroy() + viewer.fullscreenButton.isDestroyed() || viewer.fullscreenButton.destroy() + // @ts-ignore 隐藏 logo + viewer.cesiumWidget.creditContainer.style.display = "none" +} + +const fxaa = (viewer: Viewer) => { + // const supportsImageRenderingPixelated = viewer.cesiumWidget._supportsImageRenderingPixelated + // @ts-ignore 是否支持图像渲染像素化处理 + if (Cesium.FeatureDetection.supportsImageRenderingPixelated()) { + viewer.resolutionScale = window.devicePixelRatio + } + viewer.scene.postProcessStages.fxaa.enabled = true } -const fxaa = (viewer?: Cesium.Viewer) => { - ViewerHelper.getViewerPromise(undefined, viewer).then(ViewerUtilSync.fxaa) +const setSkyBox = (viewer: Viewer, urls: string[]) => { + viewer.scene.skyBox = new Cesium.SkyBox({ + sources: { + positiveX: urls[0], + negativeX: urls[1], + positiveY: urls[2], + negativeY: urls[3], + positiveZ: urls[4], + negativeZ: urls[5], + }, + }) } -const setSkyBox = ({ viewer, urls }: { viewer?: Cesium.Viewer; urls: string[] }) => { - ViewerHelper.getViewerPromise(undefined, viewer).then(viewer => ViewerUtilSync.setSkyBox(viewer, urls)) +const getScreenRect = (viewer: Viewer, type: "degree" | "radian" = "degree") => { + const rect = viewer.camera.computeViewRectangle() + if (rect) { + if (type === "radian") return { minx: rect.west, maxx: rect.east, miny: rect.south, maxy: rect.north } + return { + minx: Cesium.Math.toDegrees(rect.west), + maxx: Cesium.Math.toDegrees(rect.east), + miny: Cesium.Math.toDegrees(rect.south), + maxy: Cesium.Math.toDegrees(rect.north), + } + } + + const canvas = viewer.scene.canvas + const ellipsoid = viewer.scene.globe.ellipsoid + + const topLeft = viewer.camera.pickEllipsoid(new Cesium.Cartesian2(0, 0), ellipsoid) + const bottomRight = viewer.camera.pickEllipsoid(new Cesium.Cartesian2(canvas.clientWidth, canvas.clientHeight), ellipsoid) + + const topLeftCartographic = ellipsoid.cartesianToCartographic(topLeft!) + const bottomRightCartographic = ellipsoid.cartesianToCartographic(bottomRight!) + + if (type === "radian") { + return { + minx: topLeftCartographic.longitude, + maxx: bottomRightCartographic.longitude, + miny: bottomRightCartographic.latitude, + maxy: topLeftCartographic.latitude, + } + } + return { + minx: Cesium.Math.toDegrees(topLeftCartographic.longitude), + maxx: Cesium.Math.toDegrees(bottomRightCartographic.longitude), + miny: Cesium.Math.toDegrees(bottomRightCartographic.latitude), + maxy: Cesium.Math.toDegrees(topLeftCartographic.latitude), + } } -const getScreenRect = (viewer?: Cesium.Viewer, type: "degree" | "radian" = "degree") => { - return ViewerHelper.getViewerPromise(undefined, viewer).then(viewer => ViewerUtilSync.getScreenRect(viewer, type)) +const flyToPrimitive = (viewer: Viewer, primitive: IPrimitive) => { + if (!primitive) return + if (primitive instanceof Cesium.Primitive) { + // @ts-ignore + const boundingSpheres = primitive._boundingSpheres + if (!boundingSpheres?.length) return + const boundingSphere = Cesium.BoundingSphere.fromBoundingSpheres(boundingSpheres as any) + viewer.camera.flyToBoundingSphere(boundingSphere) + return + } + // @ts-ignore + flyToPrimitive(primitive._primitive, viewer) } -const flyToPrimitive = (primitive: IPrimitive, viewer?: Cesium.Viewer) => { - ViewerHelper.getViewerPromise(undefined, viewer).then(viewer => ViewerUtilSync.flyToPrimitive(primitive, viewer)) +const getTileLevels = (viewer: Viewer) => { + const levels = new Set() + // @ts-ignore + const tilesToRender = viewer.scene.globe._surface._tilesToRender + if (Array.isArray(tilesToRender)) { + tilesToRender.forEach(item => { + levels.add(item.level) + }) + } + return levels } -interface GetDataSourceProps { - viewer?: Cesium.Viewer +export interface GetDataSourceProps { + viewer: Viewer name: string autoCreate?: boolean } -const getCustomDataSource = (props: GetDataSourceProps) => { - const { viewer, ...rest } = props - return ViewerHelper.getViewerPromise(undefined, viewer).then(viewer => - ViewerUtilSync.getCustomDataSource({ viewer, ...rest }), - ) +const getCustomDataSource = (props: GetDataSourceProps): Cesium.CustomDataSource | null => { + const { viewer, name, autoCreate = false } = props + let dataSource = viewer.dataSources.getByName(name)[0] + if (!dataSource && autoCreate) { + dataSource = new Cesium.CustomDataSource(name) + dataSource.show = true + viewer.dataSources.add(dataSource) + } + return dataSource ?? null } -const getGeojsonDataSource = (props: GetDataSourceProps & { geojson?: string | FeatureCollection }) => { - const { viewer, ...rest } = props - return ViewerHelper.getViewerPromise(undefined, viewer).then(viewer => - ViewerUtilSync.getGeojsonDataSource({ viewer, ...rest }), - ) +const getGeojsonDataSource = ( + props: GetDataSourceProps & { geojson?: string | FeatureCollection }, +): Cesium.GeoJsonDataSource | null => { + const { viewer, name, geojson, autoCreate = false } = props + let dataSource = viewer.dataSources.getByName(name)[0] as Cesium.GeoJsonDataSource + if (!dataSource && autoCreate) { + dataSource = new Cesium.GeoJsonDataSource(name) + geojson && dataSource.load(geojson) + dataSource.show = true + viewer.dataSources.add(dataSource) + } + return dataSource ?? null } export const ViewerUtil = { + getHideWidgetOption, hideWidget, fxaa, setSkyBox, getScreenRect, flyToPrimitive, + getTileLevels, getCustomDataSource, getGeojsonDataSource, } diff --git a/packages/zxtool-cesium-utils/src/util/ViewerUtilSync.ts b/packages/zxtool-cesium-utils/src/util/ViewerUtilSync.ts deleted file mode 100644 index c5d1362..0000000 --- a/packages/zxtool-cesium-utils/src/util/ViewerUtilSync.ts +++ /dev/null @@ -1,146 +0,0 @@ -import * as Cesium from "cesium" -import type { FeatureCollection } from "geojson" -import type { IPrimitive } from "../widget/PrimitiveManager" - -const getHideWidgetOption = () => { - return { - infoBox: false, // 信息窗口 - geocoder: false, // 查询按钮 - homeButton: false, // home按钮 - sceneModePicker: false, // 地图模式 - baseLayerPicker: false, // 图层选择 - navigationHelpButton: false, // 帮助按钮 - animation: false, // 动画控制器 - timeline: false, // 时间轴 - fullscreenButton: false, // 全屏按钮 - } -} - -const hideWidget = (viewer: Cesium.Viewer) => { - viewer.infoBox.isDestroyed() || viewer.infoBox.destroy() - viewer.geocoder.isDestroyed() || viewer.geocoder.destroy() - viewer.homeButton.isDestroyed() || viewer.homeButton.destroy() - viewer.sceneModePicker.isDestroyed() || viewer.sceneModePicker.destroy() - viewer.baseLayerPicker.isDestroyed() || viewer.baseLayerPicker.destroy() - viewer.navigationHelpButton.isDestroyed() || viewer.navigationHelpButton.destroy() - viewer.animation.isDestroyed() || viewer.animation.destroy() - viewer.timeline.isDestroyed() || viewer.timeline.destroy() - viewer.fullscreenButton.isDestroyed() || viewer.fullscreenButton.destroy() - // @ts-ignore 隐藏 logo - viewer.cesiumWidget.creditContainer.style.display = "none" -} - -const fxaa = (viewer: Cesium.Viewer) => { - // const supportsImageRenderingPixelated = viewer.cesiumWidget._supportsImageRenderingPixelated - // @ts-ignore 是否支持图像渲染像素化处理 - if (Cesium.FeatureDetection.supportsImageRenderingPixelated()) { - viewer.resolutionScale = window.devicePixelRatio - } - viewer.scene.postProcessStages.fxaa.enabled = true -} - -const setSkyBox = (viewer: Cesium.Viewer, urls: string[]) => { - viewer.scene.skyBox = new Cesium.SkyBox({ - sources: { - positiveX: urls[0], - negativeX: urls[1], - positiveY: urls[2], - negativeY: urls[3], - positiveZ: urls[4], - negativeZ: urls[5], - }, - }) -} - -const getScreenRect = (viewer: Cesium.Viewer, type: "degree" | "radian" = "degree") => { - const rect = viewer.scene.camera.computeViewRectangle() - if (rect) { - if (type === "radian") return { minx: rect.west, maxx: rect.east, miny: rect.south, maxy: rect.north } - return { - minx: Cesium.Math.toDegrees(rect.west), - maxx: Cesium.Math.toDegrees(rect.east), - miny: Cesium.Math.toDegrees(rect.south), - maxy: Cesium.Math.toDegrees(rect.north), - } - } - - const canvas = viewer.scene.canvas - const ellipsoid = viewer.scene.globe.ellipsoid - - const topLeft = viewer.camera.pickEllipsoid(new Cesium.Cartesian2(0, 0), ellipsoid) - const bottomRight = viewer.camera.pickEllipsoid(new Cesium.Cartesian2(canvas.clientWidth, canvas.clientHeight), ellipsoid) - - const topLeftCartographic = ellipsoid.cartesianToCartographic(topLeft!) - const bottomRightCartographic = ellipsoid.cartesianToCartographic(bottomRight!) - - if (type === "radian") { - return { - minx: topLeftCartographic.longitude, - maxx: bottomRightCartographic.longitude, - miny: bottomRightCartographic.latitude, - maxy: topLeftCartographic.latitude, - } - } - return { - minx: Cesium.Math.toDegrees(topLeftCartographic.longitude), - maxx: Cesium.Math.toDegrees(bottomRightCartographic.longitude), - miny: Cesium.Math.toDegrees(bottomRightCartographic.latitude), - maxy: Cesium.Math.toDegrees(topLeftCartographic.latitude), - } -} - -const flyToPrimitive = (primitive: IPrimitive, viewer: Cesium.Viewer) => { - if (!primitive) return - if (primitive instanceof Cesium.Primitive) { - // @ts-ignore - const boundingSpheres = primitive._boundingSpheres - if (!boundingSpheres?.length) return - const boundingSphere = Cesium.BoundingSphere.fromBoundingSpheres(boundingSpheres as any) - viewer.camera.flyToBoundingSphere(boundingSphere) - return - } - // @ts-ignore - flyToPrimitive(primitive._primitive, viewer) -} - -interface GetDataSourceSyncProps { - viewer: Cesium.Viewer - name: string - autoCreate?: boolean -} - -const getCustomDataSource = (props: GetDataSourceSyncProps): Cesium.CustomDataSource | null => { - const { viewer, name, autoCreate = false } = props - let dataSource = viewer.dataSources.getByName(name)[0] - if (!dataSource && autoCreate) { - dataSource = new Cesium.CustomDataSource(name) - dataSource.show = true - viewer.dataSources.add(dataSource) - } - return dataSource ?? null -} - -const getGeojsonDataSource = ( - props: GetDataSourceSyncProps & { geojson?: string | FeatureCollection }, -): Cesium.GeoJsonDataSource | null => { - const { viewer, name, geojson, autoCreate = false } = props - let dataSource = viewer.dataSources.getByName(name)[0] as Cesium.GeoJsonDataSource - if (!dataSource && autoCreate) { - dataSource = new Cesium.GeoJsonDataSource(name) - geojson && dataSource.load(geojson) - dataSource.show = true - viewer.dataSources.add(dataSource) - } - return dataSource ?? null -} - -export const ViewerUtilSync = { - getHideWidgetOption, - hideWidget, - fxaa, - setSkyBox, - getScreenRect, - flyToPrimitive, - getCustomDataSource, - getGeojsonDataSource, -} diff --git a/packages/zxtool-cesium-utils/src/util/index.ts b/packages/zxtool-cesium-utils/src/util/index.ts index 90aefb4..ae50fa3 100644 --- a/packages/zxtool-cesium-utils/src/util/index.ts +++ b/packages/zxtool-cesium-utils/src/util/index.ts @@ -1,8 +1,7 @@ export * from "./CesiumUtil" export * from "./EntityUtil" -export * from "./EntityUtilSync" export * from "./MaterialUtil" export * from "./TilesetUtil" +export * from "./Tooltip" export * from "./ViewerUtil" -export * from "./ViewerUtilSync" export * from "./ZCUConfig" diff --git a/packages/zxtool-cesium-utils/src/v2/index.ts b/packages/zxtool-cesium-utils/src/v2/index.ts deleted file mode 100644 index 8a30fb0..0000000 --- a/packages/zxtool-cesium-utils/src/v2/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./CesiumHelper" -export * from "./plugins" diff --git a/packages/zxtool-cesium-utils/src/widget/CoordHelper.ts b/packages/zxtool-cesium-utils/src/widget/CoordHelper.ts deleted file mode 100644 index bb9951b..0000000 --- a/packages/zxtool-cesium-utils/src/widget/CoordHelper.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { Num3 } from "@zxtool/utils" -import * as Cesium from "cesium" -import { LonLat, LonLatHeight } from "../type" -import { genZCUInfo } from "../util/util" - -const genInfo = genZCUInfo("CoordHelper") -const noCoordInfo = genInfo("请先设置坐标") - -export class CoordHelper { - private c3: Cesium.Cartesian3 | null = null - private viewer: Cesium.Viewer - - constructor(viewer: Cesium.Viewer, c3?: Cesium.Cartesian3) { - this.viewer = viewer - if (c3) this.c3 = c3 - } - - static translate(c3: Cesium.Cartesian3, xyz: number[]) { - return Cesium.Matrix4.multiplyByPoint( - Cesium.Transforms.eastNorthUpToFixedFrame(c3), - new Cesium.Cartesian3(...xyz), - new Cesium.Cartesian3(), - ) - } - - setFromCartesian3(c3: Cesium.Cartesian3) { - this.c3 = c3 - return this - } - setFromLonLatHeight(lonLatHeight: LonLat | LonLatHeight) { - this.c3 = Cesium.Cartesian3.fromDegrees(...(lonLatHeight as Num3)) - return this - } - setFromLonLatHeightRadius(lonLatHeight: LonLat | LonLatHeight) { - this.c3 = Cesium.Cartesian3.fromRadians(...(lonLatHeight as Num3)) - return this - } - setFromScreenCoord(screen: Cesium.Cartesian2) { - const c3 = this.viewer.scene.globe.pick(this.viewer.camera.getPickRay(screen)!, this.viewer.scene) - if (c3) this.c3 = c3 - return this - } - - getCartesian3() { - if (!this.c3) throw new Error(noCoordInfo) - return this.c3 - } - getLonLatHeight() { - if (!this.c3) throw new Error(noCoordInfo) - const lonLatHeightRadian = this.getLonLatHeightRadian() - const lon = Cesium.Math.toDegrees(lonLatHeightRadian[0]) - const lat = Cesium.Math.toDegrees(lonLatHeightRadian[1]) - return [lon, lat, lonLatHeightRadian[2]] as Num3 - } - getLonLatHeightRadian() { - if (!this.c3) throw new Error(noCoordInfo) - const ellipsoid = this.viewer.scene.globe.ellipsoid - const cartographic = ellipsoid.cartesianToCartographic(this.c3) - return [cartographic.longitude, cartographic.latitude, cartographic.height] as Num3 - } - getScreenCoord() { - if (!this.c3) throw new Error(noCoordInfo) - return Cesium.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, this.c3) - } - - translate(xyz: number[]) { - if (!this.c3) throw new Error(noCoordInfo) - this.c3 = CoordHelper.translate(this.c3, xyz) - return this - } - setHeight(height: number) { - if (!this.c3) throw new Error(noCoordInfo) - const coord = this.getLonLatHeightRadian() - coord[2] = height - this.setFromLonLatHeightRadius(coord) - return this - } -} diff --git a/packages/zxtool-cesium-utils/src/widget/SyncViewerHelper.ts b/packages/zxtool-cesium-utils/src/widget/SyncViewerHelper.ts deleted file mode 100644 index 95a9f72..0000000 --- a/packages/zxtool-cesium-utils/src/widget/SyncViewerHelper.ts +++ /dev/null @@ -1,112 +0,0 @@ -import * as Cesium from "cesium" -import { genZCUInfo } from "../util/util" -import { _ScreenEventHelper } from "./ScreenEventHelper" - -const genInfo = genZCUInfo("SyncViewerHelper") - -interface SyncConfigItem { - doSync?: boolean - beSync?: boolean - control?: boolean -} - -class SyncViewerHelper { - private readonly KEY: PropertyKey - private readonly viewers: Map - - private _flag = false - private curViewer: Cesium.Viewer | null = null - private ScreenEventHelper = new _ScreenEventHelper() - private config: Record = {} - - constructor(KEY: PropertyKey, viewers: Map) { - this.KEY = KEY - this.viewers = viewers - } - - get flag() { - return this._flag - } - - private syncViewer = () => { - this.viewers.forEach((viewer, key) => { - const beSync = this.config[key]?.beSync ?? true - if (!beSync) return - if (this.curViewer && viewer !== this.curViewer) { - viewer.camera.flyTo({ - destination: this.curViewer.camera.position, - orientation: { - heading: this.curViewer.camera.heading, - pitch: this.curViewer.camera.pitch, - roll: this.curViewer.camera.roll, - }, - duration: 0.0, - }) - } - }) - } - - setConfig(config: Record) { - this.config = config - this.refreshSync() - } - - startSync() { - this._flag = true - - this.viewers.forEach((viewer, key) => { - const doSync = this.config[key]?.doSync ?? true - const control = this.config[key]?.control ?? true - this.ScreenEventHelper.addEvent({ - key, - viewer, - type: "MOUSE_MOVE", - cb: () => { - this.curViewer = viewer - }, - }) - - if (!control) { - const control = viewer.scene.screenSpaceCameraController - control.enableRotate = false - control.enableTranslate = false - control.enableZoom = false - control.enableTilt = false - control.enableLook = false - } - - if (doSync) { - this.ScreenEventHelper.addEvent({ - key, - viewer, - type: "MOUSE_MOVE", - cb: () => (this.curViewer = viewer), - }) - - // viewer.camera.changed.addEventListener(this.syncViewer) - // viewer.scene.preRender.addEventListener(this.syncViewer) - viewer.camera.changed.addEventListener(this.syncViewer) - viewer.camera.percentageChanged = 1e-10 - } - }) - } - - stopSync() { - this.viewers.forEach((viewer, key) => { - this.ScreenEventHelper.removeEvent({ key, type: "MOUSE_MOVE" }) - - viewer.camera.changed.removeEventListener(this.syncViewer) - viewer.camera.percentageChanged = 0.5 - // viewer.scene.preRender.removeEventListener(this.syncViewer) - }) - this._flag = false - } - - refreshSync() { - if (!this._flag) return - this.stopSync() - setTimeout(() => this.startSync()) - } -} - -export default SyncViewerHelper diff --git a/packages/zxtool-cesium-utils/src/widget/ViewerHelper.ts b/packages/zxtool-cesium-utils/src/widget/ViewerHelper.ts deleted file mode 100644 index 0111b2c..0000000 --- a/packages/zxtool-cesium-utils/src/widget/ViewerHelper.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { EmitterHelper } from "@zxtool/utils" -import * as Cesium from "cesium" -import { CesiumUtil } from "../util/CesiumUtil" -import { ViewerUtilSync } from "../util/ViewerUtilSync" -import { genZCUInfo } from "../util/util" -import SyncViewerHelper from "./SyncViewerHelper" - -const genInfo = genZCUInfo("ViewerHelper") - -export type InitViewerProps = Cesium.Viewer.ConstructorOptions & { - hideWidget?: boolean - fxaa?: boolean - enableIframe?: boolean - viewerKey?: PropertyKey -} - -class _ViewerHelper { - private readonly KEY: PropertyKey = Symbol("viewer") - private readonly viewers: Map = new Map() - private readonly emitter = new EmitterHelper({ maxCount: { history: 1 } }) - - readonly SyncHelper = new SyncViewerHelper(this.KEY, this.viewers) - - init = (container: string | Element, options: InitViewerProps = {}) => { - const { hideWidget, fxaa = true, viewerKey = this.KEY, enableIframe, ...rest } = options - if (this.viewers.has(viewerKey)) throw new Error(genInfo(`key 为 ${viewerKey.toString()} 的 viewer 已经初始化过`)) - - const viewer = new Cesium.Viewer(container, { ...(hideWidget ? ViewerUtilSync.getHideWidgetOption() : null), ...rest }) - // @ts-ignore - hideWidget && (viewer.cesiumWidget.creditContainer.style.display = "none") - fxaa && ViewerUtilSync.fxaa(viewer) - enableIframe && CesiumUtil.enableIframe() - viewer.scene.globe.depthTestAgainstTerrain = true - - this.SyncHelper.refreshSync() - this.viewers.set(viewerKey, viewer) - this.emitter.emit(viewerKey, viewer) - - return viewer - } - - setViewer = (viewer: Cesium.Viewer, viewerKey = this.KEY) => { - this.SyncHelper.refreshSync() - this.viewers.set(viewerKey, viewer) - this.emitter.emit(viewerKey, viewer) - } - - getViewer = (viewerKey = this.KEY) => { - return this.viewers.get(viewerKey) - } - - getViewerPromise = async (viewerKey = this.KEY, viewer?: Cesium.Viewer) => { - if (viewer) return viewer - if (this.viewers.has(viewerKey)) return this.viewers.get(viewerKey)! - return this.emitter.onceAsync(viewerKey, true).promise - } - - destroy = (viewerKey = this.KEY) => { - this.SyncHelper.refreshSync() - - const viewer = this.viewers.get(viewerKey) - if (viewer) { - viewer.destroy() - this.viewers.delete(viewerKey) - } - - this.emitter.clearHistory(viewerKey) - } -} - -export const ViewerHelper = new _ViewerHelper() diff --git a/packages/zxtool-three-utils/src/helper/threeHelper/plugins/MouseEventPlugin.ts b/packages/zxtool-three-utils/src/helper/threeHelper/plugins/MouseEventPlugin.ts index ecfdcaa..9d5b99f 100644 --- a/packages/zxtool-three-utils/src/helper/threeHelper/plugins/MouseEventPlugin.ts +++ b/packages/zxtool-three-utils/src/helper/threeHelper/plugins/MouseEventPlugin.ts @@ -68,7 +68,7 @@ export class MouseEventPlugin implements ThreeHelperPlugin { clearClick?: () => void private getIntersection = (screenX: number, screenY: number) => { - if (!this.addProps) throw new Error(genInfo("未添加插件")) + if (!this.addProps) return [null, []] as const const { threeHelper } = this.addProps const scene = threeHelper.getWidget("scene") diff --git a/packages/zxtool-utils/src/type/index.d.ts b/packages/zxtool-utils/src/type/index.d.ts index b5ae8fd..518b130 100644 --- a/packages/zxtool-utils/src/type/index.d.ts +++ b/packages/zxtool-utils/src/type/index.d.ts @@ -7,6 +7,7 @@ export type LikeDom = HTMLElement | typeof window | Document export type Num2 = [number, number] export type Num3 = [number, number, number] +export type Num4 = [number, number, number, number] export type Merge = { [K in keyof T]: T[K] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 43b9ce7..3597b4d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -722,6 +722,9 @@ importers: playground/study-webgl: dependencies: + '@dvgis/cesium-map': + specifier: ^3.1.0 + version: 3.1.0 '@turf/turf': specifier: 7.0.0-alpha.2 version: 7.0.0-alpha.2 @@ -2504,6 +2507,30 @@ packages: resolution: {integrity: sha512-h0OYmPR3A5Dfbetra/GzxBAzQk8sH7LhRkRUTdagX6nrtlUgJGYCTv4bBK33jsTQw9HDd8PE2x1Ma+iRKEDUsw==} dev: true + /@cesium/engine@2.4.1: + resolution: {integrity: sha512-8f0hBzhPUSimYLZ+cmQ0rSudm+8zYD3sBMvJcPSVt0wDpLT7tvBt9swC2v5qC2gOwGOeyFnJhXKes3ICE6SaDA==} + engines: {node: '>=14.0.0'} + dependencies: + '@tweenjs/tween.js': 18.6.4 + '@zip.js/zip.js': 2.4.26 + autolinker: 4.0.0 + bitmap-sdf: 1.0.4 + dompurify: 3.0.6 + earcut: 2.2.4 + grapheme-splitter: 1.0.4 + jsep: 1.3.8 + kdbush: 4.0.2 + ktx-parse: 0.5.0 + lerc: 2.0.0 + mersenne-twister: 1.1.0 + meshoptimizer: 0.19.0 + pako: 2.1.0 + protobufjs: 7.2.5 + rbush: 3.0.1 + topojson-client: 3.1.0 + urijs: 1.19.11 + dev: false + /@cesium/engine@6.1.0: resolution: {integrity: sha512-gufv2rfvbPVA3foYrMP+8RBOpH9z8zq6PaHqFOSCmNlRDPDsoAhsjGAog+sjFz1ybFiQ2/DY84YB/2cjs+DYxw==} engines: {node: '>=14.0.0'} @@ -2815,6 +2842,12 @@ packages: engines: {node: '>=10'} dev: false + /@dvgis/cesium-map@3.1.0: + resolution: {integrity: sha512-wBcpyeTMgOtQzumZGAP1tzN7mNkjJnsWKJ5og2cuNu5iBGjmMRmsnWkTlcl29jqH3JJs9Ued5IPz9XA1XLFg6g==} + dependencies: + '@cesium/engine': 2.4.1 + dev: false + /@emotion/hash@0.8.0: resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} dev: false @@ -5920,6 +5953,10 @@ packages: d3-voronoi: 1.1.2 dev: false + /@tweenjs/tween.js@18.6.4: + resolution: {integrity: sha512-lB9lMjuqjtuJrx7/kOkqQBtllspPIN+96OvTCeJ2j5FEzinoAXTdAMFnDAQT1KVPRlnYfBrqxtqP66vDM40xxQ==} + dev: false + /@tweenjs/tween.js@21.0.0: resolution: {integrity: sha512-qVfOiFh0U8ZSkLgA6tf7kj2MciqRbSCWaJZRwftVO7UbtVDNsZAXpWXqvCDtIefvjC83UJB+vHTDOGm5ibXjEA==} @@ -13909,6 +13946,10 @@ packages: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} dev: true + /ktx-parse@0.5.0: + resolution: {integrity: sha512-5IZrv5s1byUeDTIee1jjJQBiD5LPDB0w9pJJ0oT9BCKKJf16Tuj123vm1Ps0GOHSHmeWPgKM0zuViCVuTRpqaA==} + dev: false + /ktx-parse@0.6.0: resolution: {integrity: sha512-hYOJUI86N9+YPm0M3t8hVzW9t5FnFFibRalZCrqHs/qM2eNziqQzBtAaF0ErgkXm8F+5uE8CjPUYr32vWlXLkQ==}