Compare commits
3 Commits
ad2dd979eb
...
986da8ce42
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
986da8ce42 | ||
|
|
2605656dc4 | ||
|
|
d7c0dba569 |
BIN
info/1111111111111.jpg
Normal file
|
After Width: | Height: | Size: 59 KiB |
21
info/迁移目标.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
1. 脚本系统 支持编写代码脚本进行逻辑交互支持(升级后的)
|
||||||
|
|
||||||
|
2. GLTF轻量化工具 对GLTF格式模型进行轻量化处理,轻量化率达50%以上(升级后的)
|
||||||
|
|
||||||
|
3. 数据组件 支持注册数据源,并驱动模型进行可视化展示
|
||||||
|
|
||||||
|
4. 分包打包 支持ktx2等格式纹理打包,拥有更快的打包速度和更小的包体积(升级后的)
|
||||||
|
|
||||||
|
5. 离线包 导出和加载场景离线包,支持项目二次开发
|
||||||
|
|
||||||
|
6. 扩展3D对象 热力图、U面板、动态路径、水池
|
||||||
|
|
||||||
|
|
||||||
|
```text
|
||||||
|
把 Astral项目的 Astral/3d/editor/src/views/editor/components/extraPane/resource/builtin/Expansion.vue
|
||||||
|
迁移到 TkAstral3D/packages/editor/src/views/editor/components/extraPane/resource/builtin 下
|
||||||
|
要保证扩展中所有相关功能正常
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -7,10 +7,10 @@ export const navbar = defineNavbarConfig([
|
|||||||
icon: 'icon-park-outline:guide-board',
|
icon: 'icon-park-outline:guide-board',
|
||||||
link: '/notes/guide/quick-start/介绍.md',
|
link: '/notes/guide/quick-start/介绍.md',
|
||||||
},
|
},
|
||||||
{ text: 'SDK', link: '/notes/sdk/README.md', icon: 'carbon:api' },
|
{ text: 'SDK', link: '/notes/sdk/迁移目标.md', icon: 'carbon:api' },
|
||||||
{ text: '示例', link: 'https://examples.astraljs.com/', icon: 'carbon:carbon-for-ibm-dotcom' },
|
{ text: '示例', link: 'https://examples.astraljs.com/', icon: 'carbon:carbon-for-ibm-dotcom' },
|
||||||
{ text: '推广中心', link: '/notes/promotion/README.md', icon: 'mdi:star-shooting-outline' },
|
{ text: '推广中心', link: '/notes/promotion/迁移目标.md', icon: 'mdi:star-shooting-outline' },
|
||||||
{ text: '商务合作', link: '/notes/cooperation/README.md', icon: 'carbon:partnership' },
|
{ text: '商务合作', link: '/notes/cooperation/迁移目标.md', icon: 'carbon:partnership' },
|
||||||
{
|
{
|
||||||
text: '更多',
|
text: '更多',
|
||||||
icon: 'icon-park-outline:more-three',
|
icon: 'icon-park-outline:more-three',
|
||||||
@ -22,8 +22,8 @@ export const navbar = defineNavbarConfig([
|
|||||||
text: `${version}`,
|
text: `${version}`,
|
||||||
icon: 'codicon:versions',
|
icon: 'codicon:versions',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
text: '更新日志',
|
text: '更新日志',
|
||||||
link: '/notes/update/logs/',
|
link: '/notes/update/logs/',
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 75 KiB |
@ -0,0 +1,8 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256">
|
||||||
|
<rect width="256" height="256" fill="#121212"/>
|
||||||
|
<rect x="24" y="24" width="208" height="208" rx="18" fill="#1f1f1f" stroke="#2d2d2d" stroke-width="4"/>
|
||||||
|
<rect x="40" y="40" width="176" height="56" rx="12" fill="#00b6a4"/>
|
||||||
|
<rect x="40" y="108" width="176" height="84" rx="12" fill="#262626"/>
|
||||||
|
<text x="128" y="78" text-anchor="middle" font-size="28" font-family="Arial, sans-serif" fill="#ffffff">UIPanel</text>
|
||||||
|
<text x="128" y="156" text-anchor="middle" font-size="18" font-family="Arial, sans-serif" fill="#cfd8dc">WYSIWYG</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 637 B |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 22 KiB |
@ -122,6 +122,9 @@ export default {
|
|||||||
world: '世界坐标',
|
world: '世界坐标',
|
||||||
"Status monitoring":"状态监控",
|
"Status monitoring":"状态监控",
|
||||||
},
|
},
|
||||||
|
path: {
|
||||||
|
"Path drawing: Left-click to add a point, and double-click to end the drawing": "路径绘制:左键单击添加点,双击结束绘制",
|
||||||
|
},
|
||||||
viewportInfo: {
|
viewportInfo: {
|
||||||
Objects: '物体',
|
Objects: '物体',
|
||||||
Vertices: '顶点',
|
Vertices: '顶点',
|
||||||
@ -539,6 +542,154 @@ export default {
|
|||||||
htmlPanel: {
|
htmlPanel: {
|
||||||
Content: "内容"
|
Content: "内容"
|
||||||
},
|
},
|
||||||
|
Heatmap: "热力图",
|
||||||
|
Path: "路径",
|
||||||
|
"UI panel": "UI面板",
|
||||||
|
"Water pool": "水池",
|
||||||
|
heatmap: {
|
||||||
|
"Heatmap Texture": "热力图纹理",
|
||||||
|
Blur: "模糊",
|
||||||
|
Gradient: "色阶",
|
||||||
|
Add: "添加",
|
||||||
|
Remove: "移除",
|
||||||
|
Data: "数据",
|
||||||
|
Points: "点",
|
||||||
|
"Controls the opacity of the darkest and brightest parts of a thermal texture": "控制热力纹理最暗处和最亮处的不透明度",
|
||||||
|
"Height Scale": "高度缩放",
|
||||||
|
"Heatmap Points": "热力图点",
|
||||||
|
Error: "错误",
|
||||||
|
"Points must be an array.": "点数据必须是数组。",
|
||||||
|
"Point[{index}] must be an object.": "点[{index}]必须是对象。",
|
||||||
|
"Point[{index}].x must be a number.": "点[{index}].x必须是数字。",
|
||||||
|
"Point[{index}].y must be a number.": "点[{index}].y必须是数字。",
|
||||||
|
"Point[{index}].value must be a number.": "点[{index}].value必须是数字。",
|
||||||
|
"Point[{index}].radius must be a number.": "点[{index}].radius必须是数字。",
|
||||||
|
},
|
||||||
|
path: {
|
||||||
|
"Path Params": "路径参数",
|
||||||
|
"Ribbon Params": "平面路径",
|
||||||
|
"Tube Params": "管状路径",
|
||||||
|
"Flow Params": "流动效果",
|
||||||
|
Type: "类型",
|
||||||
|
Ribbon: "平面",
|
||||||
|
Tube: "管状",
|
||||||
|
"Corner Radius": "拐角半径",
|
||||||
|
"Corner Split": "拐角分段",
|
||||||
|
Progress: "进度",
|
||||||
|
Arrow: "箭头",
|
||||||
|
Side: "绘制侧",
|
||||||
|
Both: "双侧",
|
||||||
|
Left: "左侧",
|
||||||
|
Right: "右侧",
|
||||||
|
"Start Rad": "起始弧度",
|
||||||
|
Flow: "流动",
|
||||||
|
"Flow Speed": "流动速度",
|
||||||
|
"Flow Direction": "流动方向",
|
||||||
|
},
|
||||||
|
uipanel: {
|
||||||
|
Node: "节点",
|
||||||
|
"Select a node to edit": "选择节点以编辑",
|
||||||
|
Type: "类型",
|
||||||
|
Text: "文本",
|
||||||
|
Color: "颜色",
|
||||||
|
Padding: "内边距",
|
||||||
|
Margin: "外边距",
|
||||||
|
Offset: "偏移",
|
||||||
|
"Font opacity": "字体透明度",
|
||||||
|
"Font supersampling": "字体超采样",
|
||||||
|
"Letter spacing": "字间距",
|
||||||
|
"Line height": "行高",
|
||||||
|
"White space": "空白处理",
|
||||||
|
"Break on": "换行字符",
|
||||||
|
"Background color": "背景色",
|
||||||
|
"Background opacity": "背景透明度",
|
||||||
|
"Background image URL": "背景图链接",
|
||||||
|
"Select local image": "选择本地图片",
|
||||||
|
"Image size must be <= {size}MB": "图片大小不能超过{size}MB",
|
||||||
|
"Background size": "背景图大小",
|
||||||
|
Cover: "覆盖",
|
||||||
|
Contain: "包含",
|
||||||
|
Stretch: "拉伸",
|
||||||
|
"Border radius": "圆角",
|
||||||
|
"Border width": "边框宽度",
|
||||||
|
"Border color": "边框颜色",
|
||||||
|
"Hidden overflow": "隐藏溢出",
|
||||||
|
"Flex direction": "布局方向",
|
||||||
|
Row: "横向",
|
||||||
|
"Row reverse": "横向反转",
|
||||||
|
Column: "纵向",
|
||||||
|
"Column reverse": "纵向反转",
|
||||||
|
"Justify content": "主轴对齐",
|
||||||
|
"Align items": "交叉轴对齐",
|
||||||
|
States: "状态样式",
|
||||||
|
Hover: "悬停",
|
||||||
|
Active: "点击",
|
||||||
|
"Hover background": "悬停背景色",
|
||||||
|
"Hover opacity": "悬停透明度",
|
||||||
|
"Active background": "点击背景色",
|
||||||
|
"Active opacity": "点击透明度",
|
||||||
|
"Hover color": "悬停颜色",
|
||||||
|
"Active color": "点击颜色",
|
||||||
|
Start: "开始",
|
||||||
|
Center: "居中",
|
||||||
|
End: "结束",
|
||||||
|
"Space between": "两端对齐",
|
||||||
|
"Space around": "环绕",
|
||||||
|
"Space evenly": "均分",
|
||||||
|
"Font size": "字号",
|
||||||
|
"Text align": "文字对齐",
|
||||||
|
Left: "左对齐",
|
||||||
|
Right: "右对齐",
|
||||||
|
"Add Block": "添加容器",
|
||||||
|
"Add Text": "添加文本",
|
||||||
|
},
|
||||||
|
waterPool: {
|
||||||
|
Basic: "基础",
|
||||||
|
Type: "水池类型",
|
||||||
|
Cylinder: "圆柱",
|
||||||
|
Square: "方形",
|
||||||
|
WallMode: "墙体模式",
|
||||||
|
WallNone: "无墙体",
|
||||||
|
Wall: "墙体",
|
||||||
|
Volume: "体积水",
|
||||||
|
WallOpacity: "墙体透明度",
|
||||||
|
ScreenRefraction: "屏幕空间折射",
|
||||||
|
Size: "尺寸",
|
||||||
|
Diameter: "直径",
|
||||||
|
Width: "宽度",
|
||||||
|
Depth: "深度",
|
||||||
|
Height: "高度",
|
||||||
|
Light: "光照",
|
||||||
|
Direction: "方向",
|
||||||
|
Color: "颜色",
|
||||||
|
VolumeColor: "体积水颜色",
|
||||||
|
SurfaceColor: "水面颜色",
|
||||||
|
Disturbance: "水波",
|
||||||
|
Enabled: "启用",
|
||||||
|
Mode: "模式",
|
||||||
|
ModeDrag: "拖动",
|
||||||
|
ModeUniform: "均匀",
|
||||||
|
DropsPerStep: "每步水滴数",
|
||||||
|
RadiusMin: "半径最小",
|
||||||
|
RadiusMax: "半径最大",
|
||||||
|
StrengthMin: "强度最小",
|
||||||
|
StrengthMax: "强度最大",
|
||||||
|
TravelRadius: "活动半径",
|
||||||
|
DriftSpeed: "漂移速度",
|
||||||
|
Jitter: "抖动强度",
|
||||||
|
Spread: "扩散范围",
|
||||||
|
Refraction: "折射与波纹",
|
||||||
|
SurfaceTransmittance: "透视强度",
|
||||||
|
NormalStrength: "法线强度",
|
||||||
|
RefractionStrength: "折射强度",
|
||||||
|
Texture: "贴图",
|
||||||
|
WallTexture: "墙体纹理",
|
||||||
|
Quality: "模拟精度",
|
||||||
|
SimulationSize: "模拟尺寸",
|
||||||
|
CausticsSize: "焦散尺寸",
|
||||||
|
WaterSegments: "水面分段",
|
||||||
|
WallSegments: "墙体分段",
|
||||||
|
},
|
||||||
"3D Tiles": "3D Tiles",
|
"3D Tiles": "3D Tiles",
|
||||||
tiles: {
|
tiles: {
|
||||||
"Color mode": "颜色模式",
|
"Color mode": "颜色模式",
|
||||||
@ -606,6 +757,7 @@ export default {
|
|||||||
'Query failed': "查询失败",
|
'Query failed': "查询失败",
|
||||||
'Related document': "相关文档",
|
'Related document': "相关文档",
|
||||||
Copy: "复制",
|
Copy: "复制",
|
||||||
|
Finish: "完成",
|
||||||
Focus: '聚焦',
|
Focus: '聚焦',
|
||||||
Support: '支持',
|
Support: '支持',
|
||||||
Upload: '上传',
|
Upload: '上传',
|
||||||
@ -1081,6 +1233,24 @@ export default {
|
|||||||
// 其他
|
// 其他
|
||||||
'Dragon':"龙",
|
'Dragon':"龙",
|
||||||
},
|
},
|
||||||
|
"Expansion": "扩展",
|
||||||
|
expansion: {
|
||||||
|
"Heat map": "热力图",
|
||||||
|
"Path": "路径",
|
||||||
|
"Flat heatmap": "平面热力图",
|
||||||
|
"Elevation heatmap": "高程热力图",
|
||||||
|
"Flow path": "流动路径",
|
||||||
|
"Flowing light path": "流光路径",
|
||||||
|
"Tube path": "管道路径",
|
||||||
|
"UI Panel": "UI面板",
|
||||||
|
"Water": "水体",
|
||||||
|
"Circular Water Surface": "圆形水面",
|
||||||
|
"Cylinder Water Pool": "圆柱水池",
|
||||||
|
"Cylinder Water Pool Volume": "圆柱水池体",
|
||||||
|
"Square Water Surface": "方形水面",
|
||||||
|
"Square Water Pool": "方形水池",
|
||||||
|
"Square Water Pool Volume": "方形水池体",
|
||||||
|
},
|
||||||
"Light": '灯光',
|
"Light": '灯光',
|
||||||
"Camera": '相机',
|
"Camera": '相机',
|
||||||
}
|
}
|
||||||
|
|||||||
60
packages/editor/src/store/modules/pathDrawing.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { store } from "@/store";
|
||||||
|
|
||||||
|
interface IPathDrawingState {
|
||||||
|
active: boolean;
|
||||||
|
template: Record<string, any> | null;
|
||||||
|
submit: ((payload: IPathDrawingResult) => void) | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPathDrawingResult {
|
||||||
|
worldPoints: Array<{ x: number; y: number; z: number }>;
|
||||||
|
origin: { x: number; y: number; z: number };
|
||||||
|
options: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPathDrawingRequest {
|
||||||
|
template?: Record<string, any> | null;
|
||||||
|
submit?: (payload: IPathDrawingResult) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPathDrawingRequest(payload: Record<string, any> | IPathDrawingRequest) {
|
||||||
|
return Object.prototype.hasOwnProperty.call(payload, "template") || Object.prototype.hasOwnProperty.call(payload, "submit");
|
||||||
|
}
|
||||||
|
|
||||||
|
export const usePathDrawingStore = defineStore({
|
||||||
|
id: "pathDrawing",
|
||||||
|
state: (): IPathDrawingState => ({
|
||||||
|
active: false,
|
||||||
|
template: null,
|
||||||
|
submit: null,
|
||||||
|
}),
|
||||||
|
getters: {
|
||||||
|
isActive: state => state.active,
|
||||||
|
getTemplate: state => state.template,
|
||||||
|
getSubmit: state => state.submit,
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
start(payload: Record<string, any> | IPathDrawingRequest) {
|
||||||
|
const request = isPathDrawingRequest(payload) ? payload : { template: payload };
|
||||||
|
|
||||||
|
this.template = request.template ? JSON.parse(JSON.stringify(request.template)) : null;
|
||||||
|
this.submit = typeof request.submit === "function" ? request.submit : null;
|
||||||
|
this.active = true;
|
||||||
|
},
|
||||||
|
cancel() {
|
||||||
|
this.active = false;
|
||||||
|
this.template = null;
|
||||||
|
this.submit = null;
|
||||||
|
},
|
||||||
|
finish() {
|
||||||
|
this.active = false;
|
||||||
|
this.template = null;
|
||||||
|
this.submit = null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export function usePathDrawingStoreWithOut() {
|
||||||
|
return usePathDrawingStore(store);
|
||||||
|
}
|
||||||
@ -0,0 +1,659 @@
|
|||||||
|
<template>
|
||||||
|
<div id="extra-resource-expansion" class="flex flex-col">
|
||||||
|
<div class="mb-3 flex flex-wrap items-center gap-2">
|
||||||
|
<n-button
|
||||||
|
v-for="subCategory in subCategories"
|
||||||
|
:key="subCategory.key"
|
||||||
|
strong
|
||||||
|
secondary
|
||||||
|
round
|
||||||
|
:type="activeSubCategory === subCategory.key ? 'primary' : 'default'"
|
||||||
|
@click="selectSubCategory(subCategory.key)"
|
||||||
|
>
|
||||||
|
{{ subCategory.name }}
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-1 overflow-y-auto">
|
||||||
|
<div class="grid grid-cols-[repeat(auto-fill,minmax(100px,1fr))] gap-3">
|
||||||
|
<n-card
|
||||||
|
size="small"
|
||||||
|
hoverable
|
||||||
|
v-for="item in filteredList"
|
||||||
|
:key="item.key"
|
||||||
|
@contextmenu.prevent="handlePreview(item)"
|
||||||
|
@dblclick="addToScene(item)"
|
||||||
|
draggable="true"
|
||||||
|
@dragstart="dragStart(item)"
|
||||||
|
@dragend="dragEnd"
|
||||||
|
>
|
||||||
|
<template #cover>
|
||||||
|
<img :src="item.image" :alt="item.key" draggable="false" class="w-full object-fill" />
|
||||||
|
</template>
|
||||||
|
<n-tooltip placement="bottom" trigger="hover">
|
||||||
|
<template #trigger>{{ getItemName(item) }}</template>
|
||||||
|
<span>{{ getItemName(item) }}</span>
|
||||||
|
</n-tooltip>
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, inject, computed, nextTick } from "vue";
|
||||||
|
import type { Ref } from "vue";
|
||||||
|
import { Box3, Vector3, type Object3D } from "three";
|
||||||
|
import { cpt } from "@/language";
|
||||||
|
import { useDragStore } from "@/store/modules/drag";
|
||||||
|
import { usePathDrawingStore } from "@/store/modules/pathDrawing";
|
||||||
|
import { screenToWorld } from "@/utils/common/scenes";
|
||||||
|
import { App, AddObjectCommand, Heatmap, Path, UIPanel, Water, type Preview } from "@astral3d/engine";
|
||||||
|
|
||||||
|
interface ExpansionItem {
|
||||||
|
key: string;
|
||||||
|
image: string;
|
||||||
|
name: any;
|
||||||
|
options: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchText = inject("searchText") as Ref<string>;
|
||||||
|
const previewInfo = inject("previewInfo") as any;
|
||||||
|
const previewRef = inject("previewRef") as any;
|
||||||
|
const pathDrawingStore = usePathDrawingStore();
|
||||||
|
|
||||||
|
const activeSubCategory = ref("heatmap");
|
||||||
|
const subCategories = ref([
|
||||||
|
{key: "heatmap", name: cpt("extra.resource.expansion.Heat map")},
|
||||||
|
{key: "path", name: cpt("extra.resource.expansion.Path")},
|
||||||
|
{key: "uipanel", name: cpt("extra.resource.expansion.UI Panel")},
|
||||||
|
{key: "water", name: cpt("extra.resource.expansion.Water")},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const pathPreviewPoints = [
|
||||||
|
{x: -3.5, y: 0, z: -1.8},
|
||||||
|
{x: -1.8, y: 0, z: 1.2},
|
||||||
|
{x: 0.4, y: 0, z: -0.8},
|
||||||
|
{x: 2.4, y: 0, z: 1.6},
|
||||||
|
{x: 3.6, y: 0, z: -0.4},
|
||||||
|
];
|
||||||
|
const pathPreviewPoints3D = [
|
||||||
|
{x: -3.2, y: 0.2, z: -1.4},
|
||||||
|
{x: -1.6, y: 1.1, z: 1.0},
|
||||||
|
{x: 0.3, y: 0.4, z: -0.6},
|
||||||
|
{x: 2.1, y: 1.3, z: 1.4},
|
||||||
|
{x: 3.1, y: 0.5, z: -0.3},
|
||||||
|
];
|
||||||
|
|
||||||
|
const allList: Record<string, ExpansionItem[]> = {
|
||||||
|
heatmap: [
|
||||||
|
{
|
||||||
|
key: "flatHeatmap",
|
||||||
|
image: "/static/images/resource/expansion/heatmap/flatHeatmap.jpg",
|
||||||
|
name: cpt("extra.resource.expansion.Flat heatmap"),
|
||||||
|
options: {
|
||||||
|
mode: "flat",
|
||||||
|
heatmap: {
|
||||||
|
maxOpacity: 0.8,
|
||||||
|
minOpacity: 0,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
max: 5,
|
||||||
|
min: 0,
|
||||||
|
points: [
|
||||||
|
{x: 0, y: 0, value: 5, radius: 1.6},
|
||||||
|
{x: 0.9, y: -0.5, value: 4.4, radius: 1.2},
|
||||||
|
{x: -0.8, y: 0.9, value: 4.0, radius: 1.1},
|
||||||
|
{x: 1.6, y: 0.8, value: 3.6, radius: 0.9},
|
||||||
|
{x: -1.7, y: -0.9, value: 3.2, radius: 0.9},
|
||||||
|
{x: 0.2, y: 1.8, value: 2.8, radius: 0.8},
|
||||||
|
{x: -0.4, y: -2.1, value: 4.6, radius: 0.8},
|
||||||
|
{x: 2.6, y: -1.9, value: 2.9, radius: 0.9},
|
||||||
|
{x: -2.4, y: 2.1, value: 3.7, radius: 0.9},
|
||||||
|
{x: 3.2, y: 2.9, value: 3.8, radius: 1.1},
|
||||||
|
{x: 4.1, y: 2.1, value: 3.0, radius: 0.9},
|
||||||
|
{x: 2.4, y: 3.9, value: 2.4, radius: 0.8},
|
||||||
|
{x: 4.5, y: 3.4, value: 3.8, radius: 0.7},
|
||||||
|
{x: -3.4, y: -2.7, value: 3.1, radius: 0.9},
|
||||||
|
{x: -4.2, y: -2.1, value: 2.3, radius: 0.8},
|
||||||
|
{x: -2.6, y: -3.9, value: 2.0, radius: 0.7},
|
||||||
|
{x: -4.6, y: -3.6, value: 1.4, radius: 0.6},
|
||||||
|
{x: -1.3, y: 3.1, value: 2.2, radius: 0.8},
|
||||||
|
{x: 1.5, y: 2.6, value: 2.7, radius: 0.8},
|
||||||
|
{x: 3.0, y: -3.7, value: 1.9, radius: 0.7},
|
||||||
|
{x: 4.2, y: -3.4, value: 4.4, radius: 0.6},
|
||||||
|
{x: -4.4, y: 1.4, value: 4.6, radius: 0.6},
|
||||||
|
{x: -3.7, y: 2.5, value: 2.1, radius: 0.7},
|
||||||
|
{x: -2.0, y: 4.4, value: 3.3, radius: 0.6},
|
||||||
|
{x: 0.0, y: 4.6, value: 1.2, radius: 0.6},
|
||||||
|
{x: 4.6, y: 0.2, value: 3.5, radius: 0.6},
|
||||||
|
{x: -4.8, y: 0.1, value: 4.0, radius: 0.5},
|
||||||
|
{x: 0.3, y: -4.5, value: 3.1, radius: 0.6},
|
||||||
|
{x: 1.8, y: -4.1, value: 1.4, radius: 0.6},
|
||||||
|
{x: -1.7, y: -4.2, value: 4.6, radius: 0.6},
|
||||||
|
{x: 3.6, y: -1.2, value: 2.5, radius: 0.8},
|
||||||
|
{x: -3.2, y: 0.6, value: 2.7, radius: 0.8},
|
||||||
|
{x: 0.0, y: -2.6, value: 4.3, radius: 0.8},
|
||||||
|
{x: -0.6, y: 2.2, value: 2.9, radius: 0.8},
|
||||||
|
{x: 2.4, y: 0.4, value: 3.1, radius: 0.9},
|
||||||
|
{x: -2.8, y: -0.6, value: 2.4, radius: 0.8},
|
||||||
|
{x: 1.0, y: 4.2, value: 4.6, radius: 0.6},
|
||||||
|
{x: -4.1, y: 4.1, value: 5.0, radius: 0.5},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "heightHeatmap",
|
||||||
|
image: "/static/images/resource/expansion/heatmap/heightHeatmap.jpg",
|
||||||
|
name: cpt("extra.resource.expansion.Elevation heatmap"),
|
||||||
|
options: {
|
||||||
|
mode: "height",
|
||||||
|
data: {
|
||||||
|
max: 5,
|
||||||
|
min: 0,
|
||||||
|
points: [
|
||||||
|
{x: 0, y: 0, value: 5, radius: 1.6},
|
||||||
|
{x: 0.9, y: -0.5, value: 4.4, radius: 1.2},
|
||||||
|
{x: -0.8, y: 0.9, value: 4.0, radius: 1.1},
|
||||||
|
{x: 1.6, y: 0.8, value: 3.6, radius: 0.9},
|
||||||
|
{x: -1.7, y: -0.9, value: 3.2, radius: 0.9},
|
||||||
|
{x: 0.2, y: 1.8, value: 2.8, radius: 0.8},
|
||||||
|
{x: -0.4, y: -2.1, value: 4.6, radius: 0.8},
|
||||||
|
{x: 2.6, y: -1.9, value: 2.9, radius: 0.9},
|
||||||
|
{x: -2.4, y: 2.1, value: 3.7, radius: 0.9},
|
||||||
|
{x: 3.2, y: 2.9, value: 3.8, radius: 1.1},
|
||||||
|
{x: 4.1, y: 2.1, value: 3.0, radius: 0.9},
|
||||||
|
{x: 2.4, y: 3.9, value: 2.4, radius: 0.8},
|
||||||
|
{x: 4.5, y: 3.4, value: 3.8, radius: 0.7},
|
||||||
|
{x: -3.4, y: -2.7, value: 3.1, radius: 0.9},
|
||||||
|
{x: -4.2, y: -2.1, value: 2.3, radius: 0.8},
|
||||||
|
{x: -2.6, y: -3.9, value: 2.0, radius: 0.7},
|
||||||
|
{x: -4.6, y: -3.6, value: 1.4, radius: 0.6},
|
||||||
|
{x: -1.3, y: 3.1, value: 2.2, radius: 0.8},
|
||||||
|
{x: 1.5, y: 2.6, value: 2.7, radius: 0.8},
|
||||||
|
{x: 3.0, y: -3.7, value: 1.9, radius: 0.7},
|
||||||
|
{x: 4.2, y: -3.4, value: 4.4, radius: 0.6},
|
||||||
|
{x: -4.4, y: 1.4, value: 4.6, radius: 0.6},
|
||||||
|
{x: -3.7, y: 2.5, value: 2.1, radius: 0.7},
|
||||||
|
{x: -2.0, y: 4.4, value: 3.3, radius: 0.6},
|
||||||
|
{x: 0.0, y: 4.6, value: 1.2, radius: 0.6},
|
||||||
|
{x: 4.6, y: 0.2, value: 3.5, radius: 0.6},
|
||||||
|
{x: -4.8, y: 0.1, value: 4.0, radius: 0.5},
|
||||||
|
{x: 0.3, y: -4.5, value: 3.1, radius: 0.6},
|
||||||
|
{x: 1.8, y: -4.1, value: 1.4, radius: 0.6},
|
||||||
|
{x: -1.7, y: -4.2, value: 4.6, radius: 0.6},
|
||||||
|
{x: 3.6, y: -1.2, value: 2.5, radius: 0.8},
|
||||||
|
{x: -3.2, y: 0.6, value: 2.7, radius: 0.8},
|
||||||
|
{x: 0.0, y: -2.6, value: 4.3, radius: 0.8},
|
||||||
|
{x: -0.6, y: 2.2, value: 2.9, radius: 0.8},
|
||||||
|
{x: 2.4, y: 0.4, value: 3.1, radius: 0.9},
|
||||||
|
{x: -2.8, y: -0.6, value: 2.4, radius: 0.8},
|
||||||
|
{x: 1.0, y: 4.2, value: 4.6, radius: 0.6},
|
||||||
|
{x: -4.1, y: 4.1, value: 5.0, radius: 0.5},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
path: [
|
||||||
|
{
|
||||||
|
key: "flowPath",
|
||||||
|
image: "/static/images/resource/expansion/path/flow.png",
|
||||||
|
name: cpt("extra.resource.expansion.Flow path"),
|
||||||
|
options: {
|
||||||
|
mode: "path",
|
||||||
|
cornerRadius: 1,
|
||||||
|
cornerSplit: 30,
|
||||||
|
path: {
|
||||||
|
width: 1,
|
||||||
|
arrow: false,
|
||||||
|
progress: 1,
|
||||||
|
side: "both",
|
||||||
|
},
|
||||||
|
flow: {
|
||||||
|
enabled: true,
|
||||||
|
speed: 0.35,
|
||||||
|
direction: [1, 0],
|
||||||
|
},
|
||||||
|
material: {
|
||||||
|
map: "/static/images/resource/expansion/path/flow.png",
|
||||||
|
},
|
||||||
|
points: pathPreviewPoints,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "flowingLightPath",
|
||||||
|
image: "/static/images/resource/expansion/path/light.png",
|
||||||
|
name: cpt("extra.resource.expansion.Flowing light path"),
|
||||||
|
options: {
|
||||||
|
mode: "path",
|
||||||
|
cornerRadius: 1,
|
||||||
|
cornerSplit: 30,
|
||||||
|
path: {
|
||||||
|
width: 1,
|
||||||
|
arrow: false,
|
||||||
|
progress: 1,
|
||||||
|
side: "both",
|
||||||
|
},
|
||||||
|
flow: {
|
||||||
|
enabled: true,
|
||||||
|
speed: 0.5,
|
||||||
|
direction: [1, 0],
|
||||||
|
},
|
||||||
|
material: {
|
||||||
|
map: "/static/images/resource/expansion/path/light.png",
|
||||||
|
},
|
||||||
|
points: pathPreviewPoints,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "tubePath",
|
||||||
|
image: "/static/images/resource/expansion/path/diffuse.jpg",
|
||||||
|
name: cpt("extra.resource.expansion.Tube path"),
|
||||||
|
options: {
|
||||||
|
mode: "tube",
|
||||||
|
cornerRadius: 0.15,
|
||||||
|
cornerSplit: 6,
|
||||||
|
tube: {
|
||||||
|
radius: 0.18,
|
||||||
|
radialSegments: 12,
|
||||||
|
progress: 1,
|
||||||
|
startRad: 0,
|
||||||
|
},
|
||||||
|
material: {
|
||||||
|
color: "#4c90f5",
|
||||||
|
map: "/static/images/resource/expansion/path/diffuse.jpg",
|
||||||
|
transparent: true,
|
||||||
|
repeat: [1, 1],
|
||||||
|
},
|
||||||
|
points: pathPreviewPoints3D,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
uipanel: [
|
||||||
|
{
|
||||||
|
key: "uiPanel",
|
||||||
|
image: "/static/images/resource/expansion/uipanel/uipanel.svg",
|
||||||
|
name: cpt("extra.resource.expansion.UI Panel"),
|
||||||
|
options: {
|
||||||
|
name: "UIPanel",
|
||||||
|
type: "block",
|
||||||
|
props: {
|
||||||
|
width: 1.6,
|
||||||
|
height: 0.9,
|
||||||
|
padding: 0.04,
|
||||||
|
backgroundColor: "#151515",
|
||||||
|
backgroundOpacity: 0.9,
|
||||||
|
borderRadius: 0.05,
|
||||||
|
flexDirection: "column",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "block",
|
||||||
|
name: "Header",
|
||||||
|
props: {
|
||||||
|
width: 1.5,
|
||||||
|
height: 0.18,
|
||||||
|
backgroundColor: "#00b6a4",
|
||||||
|
backgroundOpacity: 0.95,
|
||||||
|
borderRadius: 0.04,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
name: "HeaderTitle",
|
||||||
|
props: {
|
||||||
|
textContent: "UIPanel",
|
||||||
|
fontSize: 0.07,
|
||||||
|
color: "#ffffff",
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "block",
|
||||||
|
name: "Body",
|
||||||
|
props: {
|
||||||
|
width: 1.5,
|
||||||
|
height: 0.5,
|
||||||
|
padding: 0.03,
|
||||||
|
backgroundColor: "#007e82",
|
||||||
|
backgroundOpacity: 0.9,
|
||||||
|
borderRadius: 0.04,
|
||||||
|
flexDirection: "column",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
name: "BodyTitle",
|
||||||
|
props: {
|
||||||
|
textContent: "WYSIWYG",
|
||||||
|
fontSize: 0.06,
|
||||||
|
color: "#cfd8dc",
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
name: "BodySubTitle",
|
||||||
|
props: {
|
||||||
|
textContent: "Drag nodes to layout",
|
||||||
|
fontSize: 0.045,
|
||||||
|
color: "#9aa4ad",
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "block",
|
||||||
|
name: "Footer",
|
||||||
|
props: {
|
||||||
|
width: 1.5,
|
||||||
|
height: 0.12,
|
||||||
|
backgroundColor: "#101010",
|
||||||
|
backgroundOpacity: 0.9,
|
||||||
|
borderRadius: 0.04,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
name: "FooterText",
|
||||||
|
props: {
|
||||||
|
textContent: "Astral3D UI",
|
||||||
|
fontSize: 0.04,
|
||||||
|
color: "#7bd5cd",
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
water: [
|
||||||
|
{
|
||||||
|
key: "circularWaterSurface",
|
||||||
|
image: "/static/images/resource/expansion/water/circularWaterSurface.jpg",
|
||||||
|
name: cpt("extra.resource.expansion.Circular Water Surface"),
|
||||||
|
options: {
|
||||||
|
sky: window.viewer?.scene.environment,
|
||||||
|
name: "CircularWaterSurface",
|
||||||
|
type: "cylinder",
|
||||||
|
light: [0.7, 1, -0.3],
|
||||||
|
diameter: 5,
|
||||||
|
height: 5,
|
||||||
|
wallMode: "none",
|
||||||
|
wallOpacity: 0,
|
||||||
|
useSceneRefraction: 1,
|
||||||
|
surfaceTransmittance: 0.6,
|
||||||
|
normalStrength: 0.5,
|
||||||
|
refractionStrength: 0.035,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "cylinderWaterPool",
|
||||||
|
image: "/static/images/resource/expansion/water/cylinderWaterPool.jpg",
|
||||||
|
name: cpt("extra.resource.expansion.Cylinder Water Pool"),
|
||||||
|
options: {
|
||||||
|
sky: window.viewer?.scene.environment,
|
||||||
|
name: "CylinderWaterPool",
|
||||||
|
type: "cylinder",
|
||||||
|
light: [0.7, 1, -0.3],
|
||||||
|
diameter: 5,
|
||||||
|
height: 5,
|
||||||
|
wallMode: "wall",
|
||||||
|
wallOpacity: 1,
|
||||||
|
useSceneRefraction: 1,
|
||||||
|
surfaceTransmittance: 0.6,
|
||||||
|
normalStrength: 0.5,
|
||||||
|
refractionStrength: 0.035,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "cylinderWaterPoolVolume",
|
||||||
|
image: "/static/images/resource/expansion/water/cylinderWaterPoolVolume.jpg",
|
||||||
|
name: cpt("extra.resource.expansion.Cylinder Water Pool Volume"),
|
||||||
|
options: {
|
||||||
|
sky: window.viewer?.scene.environment,
|
||||||
|
name: "CylinderWaterPoolVolume",
|
||||||
|
type: "cylinder",
|
||||||
|
light: [0.7, 1, -0.3],
|
||||||
|
diameter: 5,
|
||||||
|
height: 5,
|
||||||
|
wallMode: "volume",
|
||||||
|
wallOpacity: 1,
|
||||||
|
useSceneRefraction: 1,
|
||||||
|
surfaceTransmittance: 0.6,
|
||||||
|
normalStrength: 0.5,
|
||||||
|
refractionStrength: 0.035,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "squareWaterSurface",
|
||||||
|
image: "/static/images/resource/expansion/water/squareWaterSurface.jpg",
|
||||||
|
name: cpt("extra.resource.expansion.Square Water Surface"),
|
||||||
|
options: {
|
||||||
|
sky: window.viewer?.scene.environment,
|
||||||
|
name: "SquareWaterSurface",
|
||||||
|
type: "square",
|
||||||
|
light: [0.7, 1, -0.3],
|
||||||
|
diameter: 5,
|
||||||
|
height: 5,
|
||||||
|
wallMode: "none",
|
||||||
|
wallOpacity: 0,
|
||||||
|
useSceneRefraction: 1,
|
||||||
|
surfaceTransmittance: 0.6,
|
||||||
|
normalStrength: 0.5,
|
||||||
|
refractionStrength: 0.035,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "squareWaterPool",
|
||||||
|
image: "/static/images/resource/expansion/water/squareWaterPool.jpg",
|
||||||
|
name: cpt("extra.resource.expansion.Square Water Pool"),
|
||||||
|
options: {
|
||||||
|
sky: window.viewer?.scene.environment,
|
||||||
|
name: "SquareWaterPool",
|
||||||
|
type: "square",
|
||||||
|
light: [0.7, 1, -0.3],
|
||||||
|
diameter: 5,
|
||||||
|
height: 5,
|
||||||
|
wallMode: "wall",
|
||||||
|
wallOpacity: 1,
|
||||||
|
useSceneRefraction: 1,
|
||||||
|
surfaceTransmittance: 0.6,
|
||||||
|
normalStrength: 0.5,
|
||||||
|
refractionStrength: 0.035,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "squareWaterPoolVolume",
|
||||||
|
image: "/static/images/resource/expansion/water/squareWaterPoolVolume.jpg",
|
||||||
|
name: cpt("extra.resource.expansion.Square Water Pool Volume"),
|
||||||
|
options: {
|
||||||
|
sky: window.viewer?.scene.environment,
|
||||||
|
name: "SquareWaterPoolVolume",
|
||||||
|
type: "square",
|
||||||
|
light: [0.7, 1, -0.3],
|
||||||
|
diameter: 5,
|
||||||
|
height: 5,
|
||||||
|
wallMode: "volume",
|
||||||
|
wallOpacity: 1,
|
||||||
|
useSceneRefraction: 1,
|
||||||
|
surfaceTransmittance: 0.6,
|
||||||
|
normalStrength: 0.5,
|
||||||
|
refractionStrength: 0.035,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const dragStore = useDragStore();
|
||||||
|
|
||||||
|
function getItemName(item: ExpansionItem): string {
|
||||||
|
return item.name?.value || item.name || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const filteredList = computed(() => {
|
||||||
|
const key = activeSubCategory.value;
|
||||||
|
const list = allList[key] || [];
|
||||||
|
return list.filter(item => getItemName(item).toLowerCase().includes(searchText.value.toLowerCase()));
|
||||||
|
});
|
||||||
|
|
||||||
|
function selectSubCategory(key: string) {
|
||||||
|
activeSubCategory.value = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startPathDrawing(item: ExpansionItem) {
|
||||||
|
const options = JSON.parse(JSON.stringify(item.options || {}));
|
||||||
|
if (!options.name) {
|
||||||
|
options.name = getItemName(item);
|
||||||
|
}
|
||||||
|
if (pathDrawingStore.isActive) {
|
||||||
|
pathDrawingStore.cancel();
|
||||||
|
}
|
||||||
|
pathDrawingStore.start(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDefaultAddPosition(): number[] | undefined {
|
||||||
|
const container = window.viewer?.container;
|
||||||
|
if (!container) return undefined;
|
||||||
|
|
||||||
|
const centerX = container.offsetWidth / 2;
|
||||||
|
const centerY = container.offsetHeight / 2;
|
||||||
|
const centerPosition = screenToWorld(centerX, centerY);
|
||||||
|
return centerPosition.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handlePreview(item: ExpansionItem) {
|
||||||
|
previewInfo.name = getItemName(item);
|
||||||
|
previewInfo.visible = true;
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
previewRef.value?.getPreviewer().then((previewer: Preview) => {
|
||||||
|
if (!previewer) return;
|
||||||
|
|
||||||
|
let object3d: Object3D | null = null;
|
||||||
|
|
||||||
|
switch (activeSubCategory.value) {
|
||||||
|
case "heatmap":
|
||||||
|
object3d = new Heatmap(item.options);
|
||||||
|
break;
|
||||||
|
case "path":
|
||||||
|
object3d = new Path(item.options);
|
||||||
|
break;
|
||||||
|
case "uipanel":
|
||||||
|
object3d = new UIPanel(item.options);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!object3d) return;
|
||||||
|
|
||||||
|
previewer.scene.add(object3d);
|
||||||
|
|
||||||
|
const box3 = new Box3();
|
||||||
|
if (activeSubCategory.value === "heatmap") {
|
||||||
|
box3.set(new Vector3(-5, 0, -5), new Vector3(5, 5, 5));
|
||||||
|
} else {
|
||||||
|
box3.setFromObject(object3d);
|
||||||
|
}
|
||||||
|
|
||||||
|
previewer.modules.controls.fitToBox(box3, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addToScene(item: ExpansionItem, position?: number[]) {
|
||||||
|
const options = JSON.parse(JSON.stringify(item.options || {}));
|
||||||
|
if (!options.name) {
|
||||||
|
options.name = getItemName(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
const initPosition = position || getDefaultAddPosition();
|
||||||
|
if (initPosition) {
|
||||||
|
options.position = initPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (activeSubCategory.value) {
|
||||||
|
case "heatmap": {
|
||||||
|
const heatmap = new Heatmap(options);
|
||||||
|
App.execute(new AddObjectCommand(heatmap), `Add Heatmap: ${options.name}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "path": {
|
||||||
|
startPathDrawing(item);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "uipanel": {
|
||||||
|
const uipanel = new UIPanel(options);
|
||||||
|
App.execute(new AddObjectCommand(uipanel), `Add UIPanel: ${options.name}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "water": {
|
||||||
|
Water.waterPool(options, options.position);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dragStart(item: ExpansionItem) {
|
||||||
|
dragStore.setData(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dragEnd() {
|
||||||
|
if (dragStore.getActionTarget !== "addToScene" || dragStore.endArea !== "Scene") return;
|
||||||
|
|
||||||
|
if (activeSubCategory.value === "path") {
|
||||||
|
startPathDrawing(dragStore.getData);
|
||||||
|
dragStore.setActionTarget("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const position = screenToWorld(dragStore.endPosition.x, dragStore.endPosition.y).toArray();
|
||||||
|
addToScene(dragStore.getData, position);
|
||||||
|
|
||||||
|
dragStore.setActionTarget("");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
#extra-resource-expansion {
|
||||||
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
.n-card {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
:deep(.n-card-cover) {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
img {
|
||||||
|
aspect-ratio: 1/1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.n-card__content) {
|
||||||
|
padding: 0.3rem 0;
|
||||||
|
font-size: 0.65rem;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.n-button {
|
||||||
|
font-size: 0.65rem !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -29,7 +29,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {computed, markRaw, reactive, ref, provide, useTemplateRef} from "vue";
|
import {computed, markRaw, reactive, ref, provide, useTemplateRef} from "vue";
|
||||||
import {cpt} from "@/language";
|
import {cpt} from "@/language";
|
||||||
import {Buoy, CameraAction, HeatMap, LocationCompany, LocationHeart,ChoroplethMap, ModelAlt, Opacity} from "@vicons/carbon";
|
import {Buoy, CameraAction, HeatMap, LocationCompany, LocationHeart,ChoroplethMap, EdtLoop, ModelAlt, Opacity} from "@vicons/carbon";
|
||||||
import CommonPreview from "@/components/preview/CommonPreview.vue";
|
import CommonPreview from "@/components/preview/CommonPreview.vue";
|
||||||
import Models from "@/views/editor/components/extraPane/resource/builtin/Models.vue";
|
import Models from "@/views/editor/components/extraPane/resource/builtin/Models.vue";
|
||||||
import Materials from "@/views/editor/components/extraPane/resource/builtin/Materials.vue";
|
import Materials from "@/views/editor/components/extraPane/resource/builtin/Materials.vue";
|
||||||
@ -37,6 +37,7 @@ import Particles from "@/views/editor/components/extraPane/resource/builtin/Part
|
|||||||
import Billboard from "@/views/editor/components/extraPane/resource/builtin/Billboard.vue";
|
import Billboard from "@/views/editor/components/extraPane/resource/builtin/Billboard.vue";
|
||||||
import HtmlPanel from "@/views/editor/components/extraPane/resource/builtin/HtmlPanel.vue";
|
import HtmlPanel from "@/views/editor/components/extraPane/resource/builtin/HtmlPanel.vue";
|
||||||
import Tiles from "@/views/editor/components/extraPane/resource/builtin/Tiles.vue";
|
import Tiles from "@/views/editor/components/extraPane/resource/builtin/Tiles.vue";
|
||||||
|
import Expansion from "@/views/editor/components/extraPane/resource/builtin/Expansion.vue";
|
||||||
import Lights from "@/views/editor/components/extraPane/resource/builtin/Lights.vue";
|
import Lights from "@/views/editor/components/extraPane/resource/builtin/Lights.vue";
|
||||||
import Cameras from "@/views/editor/components/extraPane/resource/builtin/Cameras.vue";
|
import Cameras from "@/views/editor/components/extraPane/resource/builtin/Cameras.vue";
|
||||||
|
|
||||||
@ -48,6 +49,7 @@ const categories = ref([
|
|||||||
{ key: 'billboard', name: cpt("home.assets.Billboard"), icon: markRaw(LocationHeart), component: markRaw(Billboard) },
|
{ key: 'billboard', name: cpt("home.assets.Billboard"), icon: markRaw(LocationHeart), component: markRaw(Billboard) },
|
||||||
{ key: 'htmlPanel', name: cpt("extra.resource.Html panel"), icon: markRaw(LocationCompany), component: markRaw(HtmlPanel) },
|
{ key: 'htmlPanel', name: cpt("extra.resource.Html panel"), icon: markRaw(LocationCompany), component: markRaw(HtmlPanel) },
|
||||||
{ key: 'tiles', name: "3D Tiles", icon: markRaw(ChoroplethMap), component: markRaw(Tiles) },
|
{ key: 'tiles', name: "3D Tiles", icon: markRaw(ChoroplethMap), component: markRaw(Tiles) },
|
||||||
|
{ key: 'expansion', name: cpt("extra.resource.Expansion"), icon: markRaw(EdtLoop), component: markRaw(Expansion) },
|
||||||
{ key: 'lights', name: cpt("extra.resource.Light"), icon: markRaw(Buoy), component: markRaw(Lights) },
|
{ key: 'lights', name: cpt("extra.resource.Light"), icon: markRaw(Buoy), component: markRaw(Lights) },
|
||||||
{ key: 'cameras', name: cpt("extra.resource.Camera"), icon: markRaw(CameraAction), component: markRaw(Cameras) },
|
{ key: 'cameras', name: cpt("extra.resource.Camera"), icon: markRaw(CameraAction), component: markRaw(Cameras) },
|
||||||
]);
|
]);
|
||||||
|
|||||||
@ -18,7 +18,10 @@ import {
|
|||||||
ImageReference,
|
ImageReference,
|
||||||
LocationHeart,
|
LocationHeart,
|
||||||
LocationCompany,
|
LocationCompany,
|
||||||
ChoroplethMap
|
ChoroplethMap,
|
||||||
|
DirectionLoopLeft,
|
||||||
|
Gui,
|
||||||
|
ChartWaterfall
|
||||||
} from "@vicons/carbon";
|
} from "@vicons/carbon";
|
||||||
|
|
||||||
import { t } from "@/language";
|
import { t } from "@/language";
|
||||||
@ -38,6 +41,10 @@ import SidebarParticle from "./sidebar/SidebarParticle.vue";
|
|||||||
import SidebarBillboard from "./sidebar/SidebarBillboard.vue";
|
import SidebarBillboard from "./sidebar/SidebarBillboard.vue";
|
||||||
import SidebarHtmlPanel from "./sidebar/SidebarHtmlPanel.vue";
|
import SidebarHtmlPanel from "./sidebar/SidebarHtmlPanel.vue";
|
||||||
import Sidebar3DTiles from "./sidebar/Sidebar3DTiles.vue";
|
import Sidebar3DTiles from "./sidebar/Sidebar3DTiles.vue";
|
||||||
|
import SidebarHeatmap from "./sidebar/SidebarHeatmap.vue";
|
||||||
|
import SidebarPath from "./sidebar/SidebarPath.vue";
|
||||||
|
import SidebarUIPanel from "./sidebar/SidebarUIPanel.vue";
|
||||||
|
import SidebarWaterPool from "./sidebar/SidebarWaterPool.vue";
|
||||||
|
|
||||||
const tabsInstRef = ref<TabsInst | null>(null);
|
const tabsInstRef = ref<TabsInst | null>(null);
|
||||||
const tabs = ref<Array<any>>([]);
|
const tabs = ref<Array<any>>([]);
|
||||||
@ -96,6 +103,30 @@ function setTabs(object){
|
|||||||
|
|
||||||
current.value = '3DTiles';
|
current.value = '3DTiles';
|
||||||
break;
|
break;
|
||||||
|
case "Heatmap":
|
||||||
|
object3DTabs.push({ name: 'heatmap', icon: { text: 'Heatmap',color:"#A9575F", component: markRaw(HeatMap) }, component: markRaw(SidebarHeatmap) })
|
||||||
|
|
||||||
|
current.value = 'heatmap';
|
||||||
|
break;
|
||||||
|
case "Path":
|
||||||
|
object3DTabs.push({ name: 'path', icon: { text: 'Path',color:"#A9575F", component: markRaw(DirectionLoopLeft) }, component: markRaw(SidebarPath) })
|
||||||
|
|
||||||
|
current.value = 'path';
|
||||||
|
break;
|
||||||
|
case "WaterPool":
|
||||||
|
object3DTabs.push({ name: 'waterPool', icon: { text: 'Water pool',color:"#4aa3b5", component: markRaw(ChartWaterfall) }, component: markRaw(SidebarWaterPool) })
|
||||||
|
|
||||||
|
current.value = 'waterPool';
|
||||||
|
break;
|
||||||
|
case "UIPanel":
|
||||||
|
case "UIPanelBlock":
|
||||||
|
case "UIPanelText":
|
||||||
|
case "UIPanelInline":
|
||||||
|
case "UIPanelInlineBlock":
|
||||||
|
object3DTabs.push({ name: 'uipanel', icon: { text: 'UI panel',color:"#A9575F", component: markRaw(Gui) }, component: markRaw(SidebarUIPanel) })
|
||||||
|
|
||||||
|
current.value = 'uipanel';
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,6 +161,30 @@ function setTabs(object){
|
|||||||
current.value = 'object';
|
current.value = 'object';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (current.value === 'heatmap') {
|
||||||
|
if (!(object && object.type === 'Heatmap')){
|
||||||
|
current.value = 'object';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current.value === 'path') {
|
||||||
|
if (!(object && object.type === 'Path')){
|
||||||
|
current.value = 'object';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current.value === 'waterPool') {
|
||||||
|
if (!(object && object.type === 'WaterPool')){
|
||||||
|
current.value = 'object';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current.value === 'uipanel') {
|
||||||
|
if (!(object && ["UIPanel", "UIPanelBlock", "UIPanelText", "UIPanelInline", "UIPanelInlineBlock"].includes(object.type))){
|
||||||
|
current.value = 'object';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(()=>{
|
onMounted(()=>{
|
||||||
|
|||||||
@ -0,0 +1,534 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from "vue";
|
||||||
|
import { CaretForwardOutline } from "@vicons/ionicons5";
|
||||||
|
import { t } from "@/language";
|
||||||
|
import { App, Hooks, Utils, getDefaultHeatmapOptions } from "@astral3d/engine";
|
||||||
|
import CodeEditor from "@/components/code/CodeEditor.vue";
|
||||||
|
import EsTip from "@/components/es/EsTip.vue";
|
||||||
|
|
||||||
|
type GradientStep = {
|
||||||
|
id: string;
|
||||||
|
step: number;
|
||||||
|
color: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultOptions = getDefaultHeatmapOptions();
|
||||||
|
const heatmapData = reactive(getDefaultHeatmapOptions() as any);
|
||||||
|
const opacityRange = ref<[number, number]>([heatmapData.heatmap.minOpacity, heatmapData.heatmap.maxOpacity]);
|
||||||
|
const isHeightMode = computed(() => heatmapData.mode === "height");
|
||||||
|
const pointsPreview = computed(() => JSON.stringify(heatmapData.data?.points || [], null, 2));
|
||||||
|
const pointsEditorShow = ref(false);
|
||||||
|
const pointsEditorRef = ref();
|
||||||
|
const pointsSource = ref("[]");
|
||||||
|
const pointsErrors = ref<string[]>([]);
|
||||||
|
const gradientSteps = ref<GradientStep[]>([]);
|
||||||
|
|
||||||
|
function getHeatmapObject() {
|
||||||
|
const object = App.selected;
|
||||||
|
if (!object) return null;
|
||||||
|
if (object.type !== "Heatmap" || !object.options) return null;
|
||||||
|
return object as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUI() {
|
||||||
|
const object = getHeatmapObject();
|
||||||
|
if (!object) return;
|
||||||
|
|
||||||
|
Utils.deepAssign(heatmapData, object.options);
|
||||||
|
if (object.options.heatmap?.gradient) {
|
||||||
|
heatmapData.heatmap.gradient = { ...object.options.heatmap.gradient };
|
||||||
|
}
|
||||||
|
opacityRange.value = [heatmapData.heatmap.minOpacity, heatmapData.heatmap.maxOpacity];
|
||||||
|
|
||||||
|
ensureDataShape();
|
||||||
|
syncGradientSteps();
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureDataShape() {
|
||||||
|
if (!heatmapData.data) {
|
||||||
|
heatmapData.data = JSON.parse(JSON.stringify(defaultOptions.data));
|
||||||
|
}
|
||||||
|
if (!Array.isArray(heatmapData.data.points)) {
|
||||||
|
heatmapData.data.points = [];
|
||||||
|
}
|
||||||
|
if (!heatmapData.height) {
|
||||||
|
heatmapData.height = JSON.parse(JSON.stringify(defaultOptions.height));
|
||||||
|
}
|
||||||
|
if (!heatmapData.heatmap) {
|
||||||
|
heatmapData.heatmap = JSON.parse(JSON.stringify(defaultOptions.heatmap));
|
||||||
|
}
|
||||||
|
if (!heatmapData.heatmap.gradient || typeof heatmapData.heatmap.gradient !== "object" || Array.isArray(heatmapData.heatmap.gradient)) {
|
||||||
|
heatmapData.heatmap.gradient = JSON.parse(JSON.stringify(defaultOptions.heatmap.gradient));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clampStep(value: number) {
|
||||||
|
if (!Number.isFinite(value)) return 0;
|
||||||
|
return Math.min(1, Math.max(0, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeGradientSteps(gradient: Record<string, string>) {
|
||||||
|
const steps: GradientStep[] = [];
|
||||||
|
let index = 0;
|
||||||
|
|
||||||
|
Object.entries(gradient).forEach(([step, color]) => {
|
||||||
|
const numericStep = Number(step);
|
||||||
|
if (!Number.isFinite(numericStep)) return;
|
||||||
|
steps.push({
|
||||||
|
id: `step-${index}-${numericStep}`,
|
||||||
|
step: clampStep(numericStep),
|
||||||
|
color: typeof color === "string" && color.trim() !== "" ? color : "#ffffff",
|
||||||
|
});
|
||||||
|
index += 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (steps.length === 0) {
|
||||||
|
steps.push({ id: `step-${Date.now()}`, step: 0, color: "#ffffff" });
|
||||||
|
}
|
||||||
|
|
||||||
|
steps.sort((a, b) => a.step - b.step);
|
||||||
|
return steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncGradientSteps() {
|
||||||
|
ensureDataShape();
|
||||||
|
gradientSteps.value = normalizeGradientSteps(heatmapData.heatmap.gradient || {});
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildGradientFromSteps() {
|
||||||
|
const gradient: Record<string, string> = {};
|
||||||
|
const sortedSteps = [...gradientSteps.value].sort((a, b) => a.step - b.step);
|
||||||
|
|
||||||
|
sortedSteps.forEach(step => {
|
||||||
|
const clamped = clampStep(Number(step.step));
|
||||||
|
gradient[clamped.toFixed(2)] = step.color || "#ffffff";
|
||||||
|
});
|
||||||
|
|
||||||
|
return gradient;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateGradient() {
|
||||||
|
ensureDataShape();
|
||||||
|
heatmapData.heatmap.gradient = buildGradientFromSteps();
|
||||||
|
update("heatmap");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNextGradientStep() {
|
||||||
|
const steps = gradientSteps.value
|
||||||
|
.map(step => step.step)
|
||||||
|
.filter(Number.isFinite)
|
||||||
|
.sort((a, b) => a - b);
|
||||||
|
if (steps.length === 0) return 0;
|
||||||
|
for (let i = 0; i < steps.length - 1; i += 1) {
|
||||||
|
const gap = steps[i + 1] - steps[i];
|
||||||
|
if (gap > 0.1) {
|
||||||
|
return Number(((steps[i] + steps[i + 1]) / 2).toFixed(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const last = steps[steps.length - 1];
|
||||||
|
if (last < 1) {
|
||||||
|
return Number(Math.min(1, last + 0.1).toFixed(2));
|
||||||
|
}
|
||||||
|
const first = steps[0];
|
||||||
|
return Number(Math.max(0, first - 0.1).toFixed(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addGradientStep() {
|
||||||
|
const step = getNextGradientStep();
|
||||||
|
const fallbackColor = gradientSteps.value.length ? gradientSteps.value[gradientSteps.value.length - 1].color : "#ffffff";
|
||||||
|
gradientSteps.value.push({ id: `step-${Date.now()}-${Math.random().toString(16).slice(2)}`, step, color: fallbackColor });
|
||||||
|
updateGradient();
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeGradientStep(index: number) {
|
||||||
|
if (gradientSteps.value.length <= 1) return;
|
||||||
|
gradientSteps.value.splice(index, 1);
|
||||||
|
updateGradient();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateGradientStep(index: number) {
|
||||||
|
const item = gradientSteps.value[index];
|
||||||
|
if (!item) return;
|
||||||
|
const step = clampStep(Number(item.step ?? 0));
|
||||||
|
item.step = step;
|
||||||
|
updateGradient();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateGradientColor(index: number, color: string | null) {
|
||||||
|
const item = gradientSteps.value[index];
|
||||||
|
if (!item) return;
|
||||||
|
item.color = color || "#ffffff";
|
||||||
|
updateGradient();
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestRender(object) {
|
||||||
|
Hooks.useDispatchSignal("objectChanged", object);
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(key: string) {
|
||||||
|
const object = getHeatmapObject();
|
||||||
|
if (!object) return;
|
||||||
|
|
||||||
|
const call = {
|
||||||
|
mode: () => {
|
||||||
|
object.setMode(heatmapData.mode);
|
||||||
|
},
|
||||||
|
size: () => {
|
||||||
|
object.setSize({ width: heatmapData.size.width, height: heatmapData.size.height });
|
||||||
|
},
|
||||||
|
heatmap: () => {
|
||||||
|
heatmapData.heatmap.minOpacity = opacityRange.value[0];
|
||||||
|
heatmapData.heatmap.maxOpacity = opacityRange.value[1];
|
||||||
|
|
||||||
|
object.updateHeatmapConfig(heatmapData.heatmap);
|
||||||
|
},
|
||||||
|
height: () => {
|
||||||
|
object.options.height = {
|
||||||
|
scale: heatmapData.height.scale,
|
||||||
|
segments: {
|
||||||
|
width: heatmapData.height.segments.width,
|
||||||
|
height: heatmapData.height.segments.height,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
object.setSize({ width: heatmapData.size.width, height: heatmapData.size.height });
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
call[key]?.();
|
||||||
|
requestRender(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateData() {
|
||||||
|
const object = getHeatmapObject();
|
||||||
|
if (!object) return;
|
||||||
|
|
||||||
|
ensureDataShape();
|
||||||
|
object.setData({
|
||||||
|
max: heatmapData.data.max,
|
||||||
|
min: heatmapData.data.min,
|
||||||
|
points: heatmapData.data.points || [],
|
||||||
|
});
|
||||||
|
requestRender(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openPointsEditor() {
|
||||||
|
pointsEditorShow.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validatePoints(value: unknown) {
|
||||||
|
const errors: string[] = [];
|
||||||
|
const points: Array<{ x: number; y: number; value: number; radius?: number }> = [];
|
||||||
|
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
return { errors: [t("layout.sider.heatmap.Points must be an array.")], points };
|
||||||
|
}
|
||||||
|
|
||||||
|
value.forEach((item, index) => {
|
||||||
|
if (!item || typeof item !== "object") {
|
||||||
|
errors.push(t("layout.sider.heatmap.Point[{index}] must be an object.", { index }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const point = item as Record<string, unknown>;
|
||||||
|
const x = Number(point.x);
|
||||||
|
const y = Number(point.y);
|
||||||
|
const v = Number(point.value);
|
||||||
|
const radius = point.radius === undefined ? undefined : Number(point.radius);
|
||||||
|
|
||||||
|
if (!Number.isFinite(x)) errors.push(t("layout.sider.heatmap.Point[{index}].x must be a number.", { index }));
|
||||||
|
if (!Number.isFinite(y)) errors.push(t("layout.sider.heatmap.Point[{index}].y must be a number.", { index }));
|
||||||
|
if (!Number.isFinite(v)) errors.push(t("layout.sider.heatmap.Point[{index}].value must be a number.", { index }));
|
||||||
|
if (radius !== undefined && !Number.isFinite(radius)) {
|
||||||
|
errors.push(t("layout.sider.heatmap.Point[{index}].radius must be a number.", { index }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Number.isFinite(x) && Number.isFinite(y) && Number.isFinite(v)) {
|
||||||
|
const normalized: { x: number; y: number; value: number; radius?: number } = { x, y, value: v };
|
||||||
|
if (radius !== undefined && Number.isFinite(radius)) {
|
||||||
|
normalized.radius = radius;
|
||||||
|
}
|
||||||
|
points.push(normalized);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { errors, points };
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitPoints(e: Event) {
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
pointsErrors.value = pointsEditorRef.value?.getErrors?.() || [];
|
||||||
|
if (pointsErrors.value.length > 0) return;
|
||||||
|
|
||||||
|
let parsed;
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(pointsSource.value);
|
||||||
|
} catch (error) {
|
||||||
|
pointsErrors.value = [String((error as Error).message || error)];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { errors, points } = validatePoints(parsed);
|
||||||
|
if (errors.length > 0) {
|
||||||
|
pointsErrors.value = errors;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureDataShape();
|
||||||
|
heatmapData.data.points = points;
|
||||||
|
updateData();
|
||||||
|
pointsEditorShow.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => pointsEditorShow.value,
|
||||||
|
show => {
|
||||||
|
if (!show) return;
|
||||||
|
ensureDataShape();
|
||||||
|
pointsSource.value = JSON.stringify(heatmapData.data.points || [], null, 2);
|
||||||
|
pointsErrors.value = [];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
Hooks.useAddSignal("objectSelected", updateUI);
|
||||||
|
updateUI();
|
||||||
|
});
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
Hooks.useRemoveSignal("objectSelected", updateUI);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-collapse display-directive="show" :default-expanded-names="['basic', 'heatmap', 'data', 'height']">
|
||||||
|
<template #arrow>
|
||||||
|
<n-icon>
|
||||||
|
<CaretForwardOutline />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 基础 -->
|
||||||
|
<n-collapse-item :title="t('extra.resource.expansion.Heat map')" name="basic">
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.particle.Type") }}</span>
|
||||||
|
<n-select
|
||||||
|
v-model:value="heatmapData.mode"
|
||||||
|
size="small"
|
||||||
|
:options="[
|
||||||
|
{ label: t('extra.resource.expansion.Flat heatmap'), value: 'flat' },
|
||||||
|
{ label: t('extra.resource.expansion.Elevation heatmap'), value: 'height' },
|
||||||
|
]"
|
||||||
|
@update:value="update('mode')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("other.Width") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="heatmapData.size.width"
|
||||||
|
size="small"
|
||||||
|
:min="0.1"
|
||||||
|
:max="Infinity"
|
||||||
|
:decimal="2"
|
||||||
|
:show-button="false"
|
||||||
|
@change="update('size')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("other.Height") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="heatmapData.size.height"
|
||||||
|
size="small"
|
||||||
|
:min="0.1"
|
||||||
|
:max="Infinity"
|
||||||
|
:decimal="2"
|
||||||
|
:show-button="false"
|
||||||
|
@change="update('size')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</n-collapse-item>
|
||||||
|
|
||||||
|
<!-- 高程 -->
|
||||||
|
<n-collapse-item v-if="isHeightMode" :title="t('extra.resource.expansion.Elevation heatmap')" name="height">
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.heatmap.Height Scale") }}</span>
|
||||||
|
<EsInputNumber v-model:value="heatmapData.height.scale" size="small" :min="0" :max="100" :decimal="2" :show-button="false" @change="update('height')" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.scene.Width segments") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="heatmapData.height.segments.width"
|
||||||
|
size="small"
|
||||||
|
:min="1"
|
||||||
|
:max="512"
|
||||||
|
:decimal="0"
|
||||||
|
:show-button="false"
|
||||||
|
@change="update('height')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.scene.Height segments") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="heatmapData.height.segments.height"
|
||||||
|
size="small"
|
||||||
|
:min="1"
|
||||||
|
:max="512"
|
||||||
|
:decimal="0"
|
||||||
|
:show-button="false"
|
||||||
|
@change="update('height')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</n-collapse-item>
|
||||||
|
|
||||||
|
<!-- 热力图纹理参数 -->
|
||||||
|
<n-collapse-item :title="t('layout.sider.heatmap.Heatmap Texture')" name="heatmap">
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.postProcessing.Radius") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="heatmapData.heatmap.radius"
|
||||||
|
size="small"
|
||||||
|
:min="1"
|
||||||
|
:max="Infinity"
|
||||||
|
:decimal="0"
|
||||||
|
:show-button="false"
|
||||||
|
@change="update('heatmap')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.heatmap.Blur") }}</span>
|
||||||
|
<n-slider v-model:value="heatmapData.heatmap.blur" :min="0" :max="1" :step="0.01" @update:value="update('heatmap')" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<n-text>
|
||||||
|
<EsTip class="!justify-start" content="Alpha">
|
||||||
|
{{ t("layout.sider.heatmap.Controls the opacity of the darkest and brightest parts of a thermal texture") }}
|
||||||
|
</EsTip>
|
||||||
|
</n-text>
|
||||||
|
<n-slider v-model:value="opacityRange" range :min="0" :max="1" :step="0.01" @update:value="update('heatmap')" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.heatmap.Gradient") }}</span>
|
||||||
|
<div class="heatmap-gradient-actions">
|
||||||
|
<n-button size="tiny" tertiary @click="addGradientStep">{{ t("layout.sider.heatmap.Add") }}</n-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="heatmap-gradient-item" v-for="(step, index) in gradientSteps" :key="step.id">
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="step.step"
|
||||||
|
size="small"
|
||||||
|
:min="0"
|
||||||
|
:max="1"
|
||||||
|
:decimal="2"
|
||||||
|
:show-button="false"
|
||||||
|
class="heatmap-gradient-step"
|
||||||
|
@change="updateGradientStep(index)"
|
||||||
|
/>
|
||||||
|
<n-color-picker
|
||||||
|
v-model:value="step.color"
|
||||||
|
:show-alpha="false"
|
||||||
|
:modes="['hex']"
|
||||||
|
size="small"
|
||||||
|
class="heatmap-gradient-color"
|
||||||
|
:render-label="() => ''"
|
||||||
|
@update:value="updateGradientColor(index, $event)"
|
||||||
|
/>
|
||||||
|
<n-button size="tiny" quaternary :disabled="gradientSteps.length <= 1" @click="removeGradientStep(index)">
|
||||||
|
{{ t("layout.sider.heatmap.Remove") }}
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
</n-collapse-item>
|
||||||
|
|
||||||
|
<!-- 数据 -->
|
||||||
|
<n-collapse-item :title="t('layout.sider.heatmap.Data')" name="data">
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("other.Maximum") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="heatmapData.data.max"
|
||||||
|
size="small"
|
||||||
|
:min="-Infinity"
|
||||||
|
:max="Infinity"
|
||||||
|
:decimal="2"
|
||||||
|
:show-button="false"
|
||||||
|
@change="updateData"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("other.Minimum") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="heatmapData.data.min"
|
||||||
|
size="small"
|
||||||
|
:min="-Infinity"
|
||||||
|
:max="Infinity"
|
||||||
|
:decimal="2"
|
||||||
|
:show-button="false"
|
||||||
|
@change="updateData"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<n-input class="!w-full" type="textarea" :value="pointsPreview" :rows="6" readonly round @click.stop="openPointsEditor" />
|
||||||
|
</div>
|
||||||
|
</n-collapse-item>
|
||||||
|
</n-collapse>
|
||||||
|
|
||||||
|
<n-modal
|
||||||
|
:show="pointsEditorShow"
|
||||||
|
@update:show="show => (pointsEditorShow = show)"
|
||||||
|
class="!w-60vw"
|
||||||
|
preset="dialog"
|
||||||
|
:title="t('layout.sider.heatmap.Heatmap Points')"
|
||||||
|
:showIcon="false"
|
||||||
|
>
|
||||||
|
<CodeEditor ref="pointsEditorRef" v-model:source="pointsSource" mode="json" class="!h-400px" />
|
||||||
|
|
||||||
|
<n-alert
|
||||||
|
:title="t('layout.sider.heatmap.Error')"
|
||||||
|
type="error"
|
||||||
|
v-if="pointsErrors.length"
|
||||||
|
closable
|
||||||
|
@close="pointsErrors = []"
|
||||||
|
class="absolute bottom-0 w-full z-9999"
|
||||||
|
>
|
||||||
|
<n-text depth="1" v-for="(error, index) in pointsErrors" :key="index" class="block">{{ error }}</n-text>
|
||||||
|
</n-alert>
|
||||||
|
<div class="float-right mt-10px">
|
||||||
|
<n-button size="small" @click="pointsEditorShow = false">{{ t("other.Cancel") }}</n-button>
|
||||||
|
<n-button class="ml-5px" type="primary" size="small" @click="submitPoints">{{ t("other.Ok") }}</n-button>
|
||||||
|
</div>
|
||||||
|
</n-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.heatmap-gradient-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heatmap-gradient-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0 1rem 0.5rem 1rem;
|
||||||
|
width: calc(100% - 2rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.heatmap-gradient-step {
|
||||||
|
width: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heatmap-gradient-color {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.n-input__textarea) {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
223
packages/editor/src/views/editor/layouts/sidebar/SidebarPath.vue
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, onBeforeUnmount, onMounted, reactive } from "vue";
|
||||||
|
import { CaretForwardOutline } from "@vicons/ionicons5";
|
||||||
|
import { t } from "@/language";
|
||||||
|
import { App, Hooks, Utils, getDefaultPathOptions } from "@astral3d/engine";
|
||||||
|
import EsInputNumber from "@/components/es/EsInputNumber.vue";
|
||||||
|
|
||||||
|
const defaultOptions = getDefaultPathOptions();
|
||||||
|
const pathData = reactive(getDefaultPathOptions() as any);
|
||||||
|
const isTubeMode = computed(() => pathData.mode === "tube");
|
||||||
|
|
||||||
|
function getPathObject() {
|
||||||
|
const object = App.selected;
|
||||||
|
if (!object) return null;
|
||||||
|
if (object.type !== "Path") return null;
|
||||||
|
return object as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureDataShape() {
|
||||||
|
if (!pathData.path) {
|
||||||
|
pathData.path = JSON.parse(JSON.stringify(defaultOptions.path));
|
||||||
|
}
|
||||||
|
if (!pathData.tube) {
|
||||||
|
pathData.tube = JSON.parse(JSON.stringify(defaultOptions.tube));
|
||||||
|
}
|
||||||
|
if (!pathData.flow) {
|
||||||
|
pathData.flow = JSON.parse(JSON.stringify(defaultOptions.flow));
|
||||||
|
}
|
||||||
|
if (!Array.isArray(pathData.flow.direction)) {
|
||||||
|
pathData.flow.direction = defaultOptions.flow.direction.slice();
|
||||||
|
}
|
||||||
|
if (typeof pathData.cornerRadius !== "number") {
|
||||||
|
pathData.cornerRadius = defaultOptions.cornerRadius;
|
||||||
|
}
|
||||||
|
if (typeof pathData.cornerSplit !== "number") {
|
||||||
|
pathData.cornerSplit = defaultOptions.cornerSplit;
|
||||||
|
}
|
||||||
|
if (typeof pathData.closed !== "boolean") {
|
||||||
|
pathData.closed = defaultOptions.closed;
|
||||||
|
}
|
||||||
|
if (!pathData.mode) {
|
||||||
|
pathData.mode = defaultOptions.mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUI() {
|
||||||
|
const object = getPathObject();
|
||||||
|
if (!object) return;
|
||||||
|
Utils.deepAssign(pathData, object.options);
|
||||||
|
ensureDataShape();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateOptions() {
|
||||||
|
const object = getPathObject();
|
||||||
|
if (!object) return;
|
||||||
|
|
||||||
|
object.updateOptions({
|
||||||
|
mode: pathData.mode,
|
||||||
|
closed: pathData.closed,
|
||||||
|
cornerRadius: pathData.cornerRadius,
|
||||||
|
cornerSplit: pathData.cornerSplit,
|
||||||
|
path: { ...pathData.path },
|
||||||
|
tube: { ...pathData.tube },
|
||||||
|
flow: {
|
||||||
|
...pathData.flow,
|
||||||
|
direction: Array.isArray(pathData.flow.direction) ? pathData.flow.direction.slice() : defaultOptions.flow.direction.slice(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Hooks.useDispatchSignal("objectChanged", object);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
Hooks.useAddSignal("objectSelected", updateUI);
|
||||||
|
updateUI();
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
Hooks.useRemoveSignal("objectSelected", updateUI);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-collapse display-directive="show" :default-expanded-names="['base', 'shape', 'flow']">
|
||||||
|
<template #arrow>
|
||||||
|
<n-icon>
|
||||||
|
<CaretForwardOutline />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<n-collapse-item :title="t('layout.sider.path.Path Params')" name="base">
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.path.Type") }}</span>
|
||||||
|
<n-select
|
||||||
|
v-model:value="pathData.mode"
|
||||||
|
size="small"
|
||||||
|
:options="[
|
||||||
|
{ label: t('layout.sider.path.Ribbon'), value: 'path' },
|
||||||
|
{ label: t('layout.sider.path.Tube'), value: 'tube' },
|
||||||
|
]"
|
||||||
|
@update:value="updateOptions"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.path.Corner Radius") }}</span>
|
||||||
|
<EsInputNumber v-model:value="pathData.cornerRadius" size="small" :min="0" :max="100" :decimal="2" :show-button="false" @change="updateOptions" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.path.Corner Split") }}</span>
|
||||||
|
<EsInputNumber v-model:value="pathData.cornerSplit" size="small" :min="0" :max="50" :decimal="0" :show-button="false" @change="updateOptions" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.scene.Closed") }}</span>
|
||||||
|
<n-checkbox size="small" v-model:checked="pathData.closed" @update:checked="updateOptions" />
|
||||||
|
</div>
|
||||||
|
</n-collapse-item>
|
||||||
|
|
||||||
|
<n-collapse-item v-if="!isTubeMode" :title="t('layout.sider.path.Ribbon Params')" name="shape">
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("other.Width") }}</span>
|
||||||
|
<EsInputNumber v-model:value="pathData.path.width" size="small" :min="0.01" :max="100" :decimal="2" :show-button="false" @change="updateOptions" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.path.Progress") }}</span>
|
||||||
|
<n-slider v-model:value="pathData.path.progress" :min="0" :max="1" :step="0.01" @update:value="updateOptions" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.path.Arrow") }}</span>
|
||||||
|
<n-switch size="small" v-model:value="pathData.path.arrow" @update:value="updateOptions" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.path.Side") }}</span>
|
||||||
|
<n-select
|
||||||
|
v-model:value="pathData.path.side"
|
||||||
|
size="small"
|
||||||
|
:options="[
|
||||||
|
{ label: t('layout.sider.path.Both'), value: 'both' },
|
||||||
|
{ label: t('layout.sider.path.Left'), value: 'left' },
|
||||||
|
{ label: t('layout.sider.path.Right'), value: 'right' },
|
||||||
|
]"
|
||||||
|
@update:value="updateOptions"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</n-collapse-item>
|
||||||
|
|
||||||
|
<n-collapse-item v-else :title="t('layout.sider.path.Tube Params')" name="shape">
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.scene.Radius") }}</span>
|
||||||
|
<EsInputNumber v-model:value="pathData.tube.radius" size="small" :min="0.01" :max="100" :decimal="2" :show-button="false" @change="updateOptions" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.scene['Radial segments']") }}</span>
|
||||||
|
<EsInputNumber v-model:value="pathData.tube.radialSegments" size="small" :min="2" :max="64" :decimal="0" :show-button="false" @change="updateOptions" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.path.Progress") }}</span>
|
||||||
|
<n-slider v-model:value="pathData.tube.progress" :min="0" :max="1" :step="0.01" @update:value="updateOptions" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.path.Start Rad") }}</span>
|
||||||
|
<EsInputNumber v-model:value="pathData.tube.startRad" size="small" :min="-6.28" :max="6.28" :decimal="2" :show-button="false" @change="updateOptions" />
|
||||||
|
</div>
|
||||||
|
</n-collapse-item>
|
||||||
|
|
||||||
|
<n-collapse-item :title="t('layout.sider.path.Flow Params')" name="flow">
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.path.Flow") }}</span>
|
||||||
|
<n-switch size="small" v-model:value="pathData.flow.enabled" @update:value="updateOptions" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.path.Flow Speed") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="pathData.flow.speed"
|
||||||
|
size="small"
|
||||||
|
:min="-10"
|
||||||
|
:max="10"
|
||||||
|
:decimal="2"
|
||||||
|
:show-button="false"
|
||||||
|
:disabled="!pathData.flow.enabled"
|
||||||
|
@change="updateOptions"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.path.Flow Direction") }}</span>
|
||||||
|
<div class="flex">
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="pathData.flow.direction[0]"
|
||||||
|
size="small"
|
||||||
|
:min="-1"
|
||||||
|
:max="1"
|
||||||
|
:decimal="2"
|
||||||
|
:show-button="false"
|
||||||
|
:disabled="!pathData.flow.enabled"
|
||||||
|
@change="updateOptions"
|
||||||
|
/>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="pathData.flow.direction[1]"
|
||||||
|
size="small"
|
||||||
|
:min="-1"
|
||||||
|
:max="1"
|
||||||
|
:decimal="2"
|
||||||
|
:show-button="false"
|
||||||
|
:disabled="!pathData.flow.enabled"
|
||||||
|
@change="updateOptions"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n-collapse-item>
|
||||||
|
</n-collapse>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="less"></style>
|
||||||
@ -0,0 +1,256 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, onBeforeUnmount, onMounted, reactive, ref } from "vue";
|
||||||
|
import { CaretForwardOutline } from "@vicons/ionicons5";
|
||||||
|
import { t } from "@/language";
|
||||||
|
import { App, Hooks, UIPanelBlock, UIPanelText, Utils } from "@astral3d/engine";
|
||||||
|
import SidebarUIPanelBlock from "./uipanel/Sidebar.UIPanel.Block.vue";
|
||||||
|
import SidebarUIPanelText from "./uipanel/Sidebar.UIPanel.Text.vue";
|
||||||
|
|
||||||
|
type UIPanelFormType = "Block" | "Text";
|
||||||
|
type UIPanelStateMap = Record<string, Record<string, any>>;
|
||||||
|
|
||||||
|
const UIPANEL_TYPES = new Set(["UIPanel", "UIPanelBlock", "UIPanelText", "UIPanelInline", "UIPanelInlineBlock"]);
|
||||||
|
// 只存储是否有选中对象的状态,3D 对象完全脱离 Vue 响应式系统
|
||||||
|
const hasSelection = ref(false);
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
type: "Block" as UIPanelFormType,
|
||||||
|
props: {} as Record<string, any>,
|
||||||
|
states: {} as UIPanelStateMap,
|
||||||
|
});
|
||||||
|
|
||||||
|
function getUIPanelContext() {
|
||||||
|
const object = App.selected as any;
|
||||||
|
if (!object || !UIPANEL_TYPES.has(object.type)) return null;
|
||||||
|
const panel = Utils.findUIPanelRoot(object);
|
||||||
|
if (!panel) return null;
|
||||||
|
return { panel, object };
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeFormData() {
|
||||||
|
if (!formData.props || typeof formData.props !== "object") formData.props = {};
|
||||||
|
if (!formData.states || typeof formData.states !== "object") formData.states = {};
|
||||||
|
|
||||||
|
if (formData.type === "Block") {
|
||||||
|
if (!Number.isFinite(formData.props.width)) formData.props.width = 1;
|
||||||
|
if (!Number.isFinite(formData.props.height)) formData.props.height = 0.4;
|
||||||
|
if (!Number.isFinite(formData.props.padding)) formData.props.padding = 0.04;
|
||||||
|
if (!Number.isFinite(formData.props.margin)) formData.props.margin = 0;
|
||||||
|
if (!Number.isFinite(formData.props.offset)) formData.props.offset = 0.005;
|
||||||
|
if (!Number.isFinite(formData.props.backgroundOpacity)) formData.props.backgroundOpacity = 0.8;
|
||||||
|
if (!Number.isFinite(formData.props.borderRadius)) formData.props.borderRadius = 0.04;
|
||||||
|
if (!Number.isFinite(formData.props.borderWidth)) formData.props.borderWidth = 0;
|
||||||
|
if (!formData.props.backgroundColor) formData.props.backgroundColor = "#2a2a2a";
|
||||||
|
if (!formData.props.borderColor) formData.props.borderColor = "#ffffff";
|
||||||
|
if (!formData.props.flexDirection) formData.props.flexDirection = "column";
|
||||||
|
if (!formData.props.justifyContent) formData.props.justifyContent = "center";
|
||||||
|
if (!formData.props.alignItems) formData.props.alignItems = "center";
|
||||||
|
if (!formData.props.backgroundSize) formData.props.backgroundSize = "cover";
|
||||||
|
if (!formData.props.overflow) formData.props.overflow = "visible";
|
||||||
|
if (!formData.props.bestFit) formData.props.bestFit = "none";
|
||||||
|
} else {
|
||||||
|
if (!formData.props.textContent) formData.props.textContent = "";
|
||||||
|
// Text 类型的 width/height 使用 "auto" 表示自动尺寸,
|
||||||
|
// 若 props 中未定义或为 "auto",则保持 "auto";仅当为有限数值时显示具体值
|
||||||
|
if (formData.props.width !== "auto" && !Number.isFinite(formData.props.width)) formData.props.width = "auto";
|
||||||
|
if (formData.props.height !== "auto" && !Number.isFinite(formData.props.height)) formData.props.height = "auto";
|
||||||
|
if (!Number.isFinite(formData.props.fontSize)) formData.props.fontSize = 0.05;
|
||||||
|
if (!Number.isFinite(formData.props.fontOpacity)) formData.props.fontOpacity = 1;
|
||||||
|
if (!Number.isFinite(formData.props.letterSpacing)) formData.props.letterSpacing = 0;
|
||||||
|
if (!Number.isFinite(formData.props.lineHeight)) formData.props.lineHeight = 1.2;
|
||||||
|
if (!Number.isFinite(formData.props.padding)) formData.props.padding = 0;
|
||||||
|
if (!Number.isFinite(formData.props.margin)) formData.props.margin = 0;
|
||||||
|
if (!Number.isFinite(formData.props.offset)) formData.props.offset = 0.005;
|
||||||
|
if (!formData.props.color) formData.props.color = "#ffffff";
|
||||||
|
if (!formData.props.textAlign) formData.props.textAlign = "center";
|
||||||
|
if (!formData.props.fontKerning) formData.props.fontKerning = "normal";
|
||||||
|
if (!formData.props.whiteSpace) formData.props.whiteSpace = "pre-line";
|
||||||
|
if (!formData.props.fontSmooth) formData.props.fontSmooth = "antialiased";
|
||||||
|
if (!formData.props.breakOn) formData.props.breakOn = "- ,.:?!\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUI() {
|
||||||
|
const context = getUIPanelContext();
|
||||||
|
if (!context) {
|
||||||
|
hasSelection.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasSelection.value = true;
|
||||||
|
|
||||||
|
const options = context.object.options || {};
|
||||||
|
formData.type = context.object.type === "UIPanelText" || context.object.type === "UIPanelInline" ? "Text" : "Block";
|
||||||
|
formData.props = JSON.parse(JSON.stringify(options.props || {}));
|
||||||
|
formData.states = JSON.parse(JSON.stringify(options.states || {}));
|
||||||
|
normalizeFormData();
|
||||||
|
}
|
||||||
|
|
||||||
|
const canAddNodes = computed(() => {
|
||||||
|
if (!hasSelection.value) return false;
|
||||||
|
const object = App.selected as any;
|
||||||
|
return object && (object.type === "UIPanel" || object.type === "UIPanelBlock");
|
||||||
|
});
|
||||||
|
|
||||||
|
function getAddParent() {
|
||||||
|
const object = App.selected as any;
|
||||||
|
if (object && (object.type === "UIPanel" || object.type === "UIPanelBlock")) {
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
return Utils.findUIPanelRoot(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBlock() {
|
||||||
|
return new UIPanelBlock({
|
||||||
|
name: "Block",
|
||||||
|
props: {
|
||||||
|
width: 1,
|
||||||
|
height: 0.4,
|
||||||
|
padding: 0.04,
|
||||||
|
backgroundColor: "#2a2a2a",
|
||||||
|
backgroundOpacity: 0.8,
|
||||||
|
borderRadius: 0.04,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createText() {
|
||||||
|
return new UIPanelText({
|
||||||
|
name: "Text",
|
||||||
|
props: {
|
||||||
|
textContent: "",
|
||||||
|
fontSize: 0.05,
|
||||||
|
color: "#ffffff",
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addNode(type: "Block" | "Text") {
|
||||||
|
const parent = getAddParent();
|
||||||
|
if (!parent) return;
|
||||||
|
const node = type === "Block" ? createBlock() : createText();
|
||||||
|
parent.add?.(node);
|
||||||
|
Hooks.useDispatchSignal("objectChanged", parent);
|
||||||
|
App.select(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateProps(patch: Record<string, any>) {
|
||||||
|
const object = App.selected as any;
|
||||||
|
if (!object) return;
|
||||||
|
object.setProps?.(patch);
|
||||||
|
Hooks.useDispatchSignal("objectChanged", object);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateStates() {
|
||||||
|
const object = App.selected as any;
|
||||||
|
if (!object) return;
|
||||||
|
object.setStates?.(formData.states);
|
||||||
|
Hooks.useDispatchSignal("objectChanged", object);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isStateEnabled(key: string) {
|
||||||
|
return Boolean(formData.states && formData.states[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleState(key: string, enabled: boolean) {
|
||||||
|
if (enabled) {
|
||||||
|
if (!formData.states) formData.states = {};
|
||||||
|
if (!formData.states[key]) {
|
||||||
|
// 首次启用状态时,根据类型设置不同的默认值
|
||||||
|
if (formData.type === "Block") {
|
||||||
|
// Block: 使用当前背景色和透明度 1
|
||||||
|
formData.states[key] = {
|
||||||
|
backgroundColor: formData.props.backgroundColor || "#2a2a2a",
|
||||||
|
backgroundOpacity: 1,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Text: 使用当前文字颜色
|
||||||
|
formData.states[key] = {
|
||||||
|
color: formData.props.color || "#ffffff",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (formData.states) {
|
||||||
|
delete formData.states[key];
|
||||||
|
if (Object.keys(formData.states).length === 0) {
|
||||||
|
formData.states = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateStateProps(key: string, patch: Record<string, any>) {
|
||||||
|
if (!formData.states) formData.states = {};
|
||||||
|
if (!formData.states[key]) formData.states[key] = {};
|
||||||
|
formData.states[key] = { ...formData.states[key], ...patch };
|
||||||
|
updateStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
Hooks.useAddSignal("objectSelected", updateUI);
|
||||||
|
Hooks.useAddSignal("objectChanged", updateUI);
|
||||||
|
updateUI();
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
Hooks.useRemoveSignal("objectSelected", updateUI);
|
||||||
|
Hooks.useRemoveSignal("objectChanged", updateUI);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="uipanel-actions">
|
||||||
|
<n-space size="small" align="center" justify="center">
|
||||||
|
<n-button v-if="canAddNodes" @click="addNode('Block')">{{ t("layout.sider.uipanel['Add Block']") }}</n-button>
|
||||||
|
<n-button v-if="canAddNodes" @click="addNode('Text')">{{ t("layout.sider.uipanel['Add Text']") }}</n-button>
|
||||||
|
</n-space>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<n-collapse display-directive="show" :default-expanded-names="['node']">
|
||||||
|
<template #arrow>
|
||||||
|
<n-icon>
|
||||||
|
<CaretForwardOutline />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<n-collapse-item :title="t('layout.sider.uipanel.Node')" name="node">
|
||||||
|
<div v-if="!hasSelection" class="text-12px opacity-70">
|
||||||
|
{{ t("layout.sider.uipanel['Select a node to edit']") }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel.Type") }}</span>
|
||||||
|
<div class="text-center">{{ formData.type }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SidebarUIPanelBlock
|
||||||
|
v-if="formData.type === 'Block'"
|
||||||
|
:form-data="formData"
|
||||||
|
:is-state-enabled="isStateEnabled"
|
||||||
|
:toggle-state="toggleState"
|
||||||
|
:update-props="updateProps"
|
||||||
|
:update-state-props="updateStateProps"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SidebarUIPanelText
|
||||||
|
v-else
|
||||||
|
:form-data="formData"
|
||||||
|
:is-state-enabled="isStateEnabled"
|
||||||
|
:toggle-state="toggleState"
|
||||||
|
:update-props="updateProps"
|
||||||
|
:update-state-props="updateStateProps"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</n-collapse-item>
|
||||||
|
</n-collapse>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.uipanel-actions {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,687 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, onBeforeUnmount, onMounted, reactive, ref, toRaw } from "vue";
|
||||||
|
import { CaretForwardOutline } from "@vicons/ionicons5";
|
||||||
|
import { Color, CubeTexture, Texture } from "three";
|
||||||
|
import { App, Hooks, Water, WaterPool } from "@astral3d/engine";
|
||||||
|
import EsInputNumber from "@/components/es/EsInputNumber.vue";
|
||||||
|
import EsTexture from "@/components/es/EsTexture.vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const poolData = reactive({
|
||||||
|
type: "cylinder",
|
||||||
|
light: { x: 0.7, y: 1, z: -0.3 },
|
||||||
|
wallMode: "volume",
|
||||||
|
wallOpacity: 0.3,
|
||||||
|
disturbanceEnabled: true,
|
||||||
|
disturbanceMode: "drag",
|
||||||
|
disturbanceDropsPerStep: 1,
|
||||||
|
disturbanceRadiusMin: 0.01,
|
||||||
|
disturbanceRadiusMax: 0.05,
|
||||||
|
disturbanceStrengthMin: 0.005,
|
||||||
|
disturbanceStrengthMax: 0.02,
|
||||||
|
disturbanceTravelRadius: 0.85,
|
||||||
|
disturbanceDriftSpeed: 0.015,
|
||||||
|
disturbanceJitter: 0.01,
|
||||||
|
disturbanceSpread: 0.08,
|
||||||
|
useSceneRefraction: true,
|
||||||
|
surfaceTransmittance: 0.65,
|
||||||
|
normalStrength: 0.6,
|
||||||
|
refractionStrength: 0.035,
|
||||||
|
simulationSize: 256,
|
||||||
|
causticsSize: 1024,
|
||||||
|
waterSegments: 200,
|
||||||
|
wallSegments: 64,
|
||||||
|
diameter: 2,
|
||||||
|
width: 2,
|
||||||
|
depth: 2,
|
||||||
|
height: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
const tilesTexture = ref<Color | Texture | CubeTexture | null>(null);
|
||||||
|
const volumeColor = ref("#2c5965");
|
||||||
|
const surfaceColor = ref("#4a8aa0");
|
||||||
|
const rebuilding = ref(false);
|
||||||
|
let qualityTimer: number | null = null;
|
||||||
|
|
||||||
|
const isCylinder = computed(() => poolData.type === "cylinder");
|
||||||
|
|
||||||
|
function getWaterPoolObject() {
|
||||||
|
const object = App.selected;
|
||||||
|
if (!object) return null;
|
||||||
|
if (object.type !== "WaterPool") return null;
|
||||||
|
return object as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncColors(object) {
|
||||||
|
const volume = object.volumeColor instanceof Color ? object.volumeColor : new Color(object.volumeColor ?? 0x2c5965);
|
||||||
|
const surface = object.surfaceColor instanceof Color ? object.surfaceColor : new Color(object.surfaceColor ?? volume.getHex());
|
||||||
|
volumeColor.value = `#${volume.getHexString()}`;
|
||||||
|
surfaceColor.value = `#${surface.getHexString()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUI() {
|
||||||
|
const object = getWaterPoolObject();
|
||||||
|
if (!object) return;
|
||||||
|
|
||||||
|
poolData.type = object.poolType === "cylinder" ? "cylinder" : "square";
|
||||||
|
poolData.light.x = object.light?.[0] ?? 0.7;
|
||||||
|
poolData.light.y = object.light?.[1] ?? 1;
|
||||||
|
poolData.light.z = object.light?.[2] ?? -0.3;
|
||||||
|
poolData.wallMode = object.wallMode ?? (object.hasWall ? "wall" : "none");
|
||||||
|
poolData.wallOpacity = Number.isFinite(object.wallOpacity) ? object.wallOpacity : 1;
|
||||||
|
const disturbance = object._disturbance || null;
|
||||||
|
if (disturbance) {
|
||||||
|
poolData.disturbanceEnabled = true;
|
||||||
|
poolData.disturbanceMode = disturbance.mode === "uniform" ? "uniform" : "drag";
|
||||||
|
poolData.disturbanceDropsPerStep = disturbance.dropsPerStep ?? 1;
|
||||||
|
poolData.disturbanceRadiusMin = disturbance.radiusMin ?? 0.01;
|
||||||
|
poolData.disturbanceRadiusMax = disturbance.radiusMax ?? 0.05;
|
||||||
|
poolData.disturbanceStrengthMin = disturbance.strengthMin ?? 0.005;
|
||||||
|
poolData.disturbanceStrengthMax = disturbance.strengthMax ?? 0.02;
|
||||||
|
poolData.disturbanceTravelRadius = disturbance.travelRadius ?? 0.85;
|
||||||
|
poolData.disturbanceDriftSpeed = disturbance.driftSpeed ?? 0.015;
|
||||||
|
poolData.disturbanceJitter = disturbance.jitter ?? 0.01;
|
||||||
|
poolData.disturbanceSpread = disturbance.spread ?? 0.08;
|
||||||
|
} else {
|
||||||
|
poolData.disturbanceEnabled = true;
|
||||||
|
poolData.disturbanceMode = "drag";
|
||||||
|
poolData.disturbanceDropsPerStep = 1;
|
||||||
|
poolData.disturbanceRadiusMin = 0.01;
|
||||||
|
poolData.disturbanceRadiusMax = 0.05;
|
||||||
|
poolData.disturbanceStrengthMin = 0.005;
|
||||||
|
poolData.disturbanceStrengthMax = 0.02;
|
||||||
|
poolData.disturbanceTravelRadius = 0.85;
|
||||||
|
poolData.disturbanceDriftSpeed = 0.015;
|
||||||
|
poolData.disturbanceJitter = 0.01;
|
||||||
|
poolData.disturbanceSpread = 0.08;
|
||||||
|
}
|
||||||
|
poolData.useSceneRefraction = !!object.useSceneRefraction;
|
||||||
|
poolData.surfaceTransmittance = Number.isFinite(object.surfaceTransmittance) ? object.surfaceTransmittance : 0.65;
|
||||||
|
poolData.normalStrength = Number.isFinite(object.normalStrength) ? object.normalStrength : 0.6;
|
||||||
|
poolData.refractionStrength = Number.isFinite(object.refractionStrength) ? object.refractionStrength : 0.035;
|
||||||
|
poolData.simulationSize = object.simulationSize ?? 256;
|
||||||
|
poolData.causticsSize = object.causticsSize ?? 1024;
|
||||||
|
poolData.waterSegments = object.waterSegments ?? 200;
|
||||||
|
poolData.wallSegments = object.wallSegments ?? 64;
|
||||||
|
poolData.height = Number.isFinite(object.height) ? object.height : (object._poolHeight ?? 2);
|
||||||
|
|
||||||
|
if (poolData.type === "cylinder") {
|
||||||
|
poolData.diameter = Number.isFinite(object.diameter) ? object.diameter : object._radius ? object._radius * 2 : 2;
|
||||||
|
} else {
|
||||||
|
poolData.width = Number.isFinite(object.width) ? object.width : 2;
|
||||||
|
poolData.depth = Number.isFinite(object.depth) ? object.depth : 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
tilesTexture.value = object.tiles || null;
|
||||||
|
syncColors(object);
|
||||||
|
applyDisturbance();
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestRender(object) {
|
||||||
|
Hooks.useDispatchSignal("objectChanged", object);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyLight() {
|
||||||
|
const object = getWaterPoolObject();
|
||||||
|
if (!object) return;
|
||||||
|
|
||||||
|
const light = [poolData.light.x, poolData.light.y, poolData.light.z];
|
||||||
|
object.light = light;
|
||||||
|
|
||||||
|
const materials = typeof object._getWaterMaterials === "function" ? object._getWaterMaterials() : [];
|
||||||
|
materials.forEach(material => {
|
||||||
|
if (material?.uniforms?.light) material.uniforms.light.value = light;
|
||||||
|
});
|
||||||
|
|
||||||
|
const wallMaterial = object.wall?._material;
|
||||||
|
if (wallMaterial?.uniforms?.light) wallMaterial.uniforms.light.value = light;
|
||||||
|
|
||||||
|
const causticsMaterial = typeof object._getCausticsMaterial === "function" ? object._getCausticsMaterial() : null;
|
||||||
|
if (causticsMaterial?.uniforms?.light) causticsMaterial.uniforms.light.value = light;
|
||||||
|
|
||||||
|
requestRender(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applySize() {
|
||||||
|
const object = getWaterPoolObject();
|
||||||
|
if (!object) return;
|
||||||
|
|
||||||
|
if (poolData.type === "cylinder") {
|
||||||
|
object.setSize({ diameter: poolData.diameter, height: poolData.height });
|
||||||
|
} else {
|
||||||
|
object.setSize({ width: poolData.width, depth: poolData.depth, height: poolData.height });
|
||||||
|
}
|
||||||
|
|
||||||
|
requestRender(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyTiles() {
|
||||||
|
const object = getWaterPoolObject();
|
||||||
|
if (!object) return;
|
||||||
|
if (poolData.wallMode !== "wall") return;
|
||||||
|
|
||||||
|
const texture = (toRaw(tilesTexture.value) as any) || null;
|
||||||
|
object.tiles = texture;
|
||||||
|
|
||||||
|
const materials = typeof object._getWaterMaterials === "function" ? object._getWaterMaterials() : [];
|
||||||
|
materials.forEach(material => {
|
||||||
|
if (material?.uniforms?.tiles) material.uniforms.tiles.value = texture;
|
||||||
|
if (material?.uniforms?.useTiles) material.uniforms.useTiles.value = texture ? 1 : 0;
|
||||||
|
if (material?.uniforms?.tilesColor) material.uniforms.tilesColor.value = object.volumeColor;
|
||||||
|
});
|
||||||
|
|
||||||
|
const wallMaterial = object.wall?._material;
|
||||||
|
if (wallMaterial?.uniforms?.tiles) wallMaterial.uniforms.tiles.value = texture;
|
||||||
|
if (wallMaterial?.uniforms?.useTiles) wallMaterial.uniforms.useTiles.value = texture ? 1 : 0;
|
||||||
|
if (wallMaterial?.uniforms?.tilesColor) wallMaterial.uniforms.tilesColor.value = object.volumeColor;
|
||||||
|
|
||||||
|
requestRender(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyColors() {
|
||||||
|
const object = getWaterPoolObject();
|
||||||
|
if (!object) return;
|
||||||
|
|
||||||
|
object.volumeColor = new Color(volumeColor.value || "#2c5965");
|
||||||
|
object.surfaceColor = new Color(surfaceColor.value || volumeColor.value || "#2c5965");
|
||||||
|
|
||||||
|
if (typeof object._applyUniforms === "function") {
|
||||||
|
object._applyUniforms();
|
||||||
|
}
|
||||||
|
|
||||||
|
requestRender(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyRefraction() {
|
||||||
|
const object = getWaterPoolObject();
|
||||||
|
if (!object) return;
|
||||||
|
|
||||||
|
object.useSceneRefraction = poolData.useSceneRefraction ? 1 : 0;
|
||||||
|
object.surfaceTransmittance = poolData.surfaceTransmittance;
|
||||||
|
object.normalStrength = poolData.normalStrength;
|
||||||
|
object.refractionStrength = poolData.refractionStrength;
|
||||||
|
|
||||||
|
if (typeof object._applyUniforms === "function") {
|
||||||
|
object._applyUniforms();
|
||||||
|
}
|
||||||
|
|
||||||
|
requestRender(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyDisturbance() {
|
||||||
|
const object = getWaterPoolObject();
|
||||||
|
if (!object) return;
|
||||||
|
|
||||||
|
if (!poolData.disturbanceEnabled) {
|
||||||
|
object.stopDisturbance?.();
|
||||||
|
requestRender(object);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
object.startDisturbance?.({
|
||||||
|
mode: poolData.disturbanceMode === "uniform" ? "uniform" : "drag",
|
||||||
|
dropsPerStep: Math.max(1, Math.round(poolData.disturbanceDropsPerStep)),
|
||||||
|
radiusMin: Math.max(0, poolData.disturbanceRadiusMin),
|
||||||
|
radiusMax: Math.max(poolData.disturbanceRadiusMin, poolData.disturbanceRadiusMax),
|
||||||
|
strengthMin: poolData.disturbanceStrengthMin,
|
||||||
|
strengthMax: poolData.disturbanceStrengthMax,
|
||||||
|
travelRadius: Math.max(0, poolData.disturbanceTravelRadius),
|
||||||
|
driftSpeed: Math.max(0, poolData.disturbanceDriftSpeed),
|
||||||
|
jitter: Math.max(0, poolData.disturbanceJitter),
|
||||||
|
spread: Math.max(0, poolData.disturbanceSpread),
|
||||||
|
});
|
||||||
|
|
||||||
|
requestRender(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyWallOpacity() {
|
||||||
|
const object = getWaterPoolObject();
|
||||||
|
if (!object) return;
|
||||||
|
|
||||||
|
object.wallOpacity = poolData.wallOpacity;
|
||||||
|
if (typeof object._applyUniforms === "function") {
|
||||||
|
object._applyUniforms();
|
||||||
|
}
|
||||||
|
|
||||||
|
const wallMaterial = object.wall?._material;
|
||||||
|
if (wallMaterial) {
|
||||||
|
const transparent = object.wallOpacity < 1;
|
||||||
|
wallMaterial.transparent = transparent;
|
||||||
|
wallMaterial.depthWrite = !transparent;
|
||||||
|
wallMaterial.needsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestRender(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildOptionsFromUI(object) {
|
||||||
|
const options: any = {
|
||||||
|
name: object?.name || "WaterPool",
|
||||||
|
type: poolData.type,
|
||||||
|
light: [poolData.light.x, poolData.light.y, poolData.light.z],
|
||||||
|
tiles: poolData.wallMode === "wall" ? (toRaw(tilesTexture.value) as any) || null : object?.tiles || null,
|
||||||
|
sky: object?.sky || null,
|
||||||
|
wallMode: poolData.wallMode,
|
||||||
|
wall: poolData.wallMode !== "none",
|
||||||
|
wallOpacity: poolData.wallOpacity,
|
||||||
|
useSceneRefraction: poolData.useSceneRefraction ? 1 : 0,
|
||||||
|
surfaceTransmittance: poolData.surfaceTransmittance,
|
||||||
|
normalStrength: poolData.normalStrength,
|
||||||
|
refractionStrength: poolData.refractionStrength,
|
||||||
|
volumeColor: new Color(volumeColor.value || "#2c5965"),
|
||||||
|
surfaceColor: new Color(surfaceColor.value || volumeColor.value || "#2c5965"),
|
||||||
|
simulationSize: Math.max(16, Math.round(poolData.simulationSize)),
|
||||||
|
causticsSize: Math.max(64, Math.round(poolData.causticsSize)),
|
||||||
|
waterSegments: Math.max(1, Math.round(poolData.waterSegments)),
|
||||||
|
wallSegments: Math.max(3, Math.round(poolData.wallSegments)),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (poolData.type === "cylinder") {
|
||||||
|
options.diameter = poolData.diameter;
|
||||||
|
} else {
|
||||||
|
options.width = poolData.width;
|
||||||
|
options.depth = poolData.depth;
|
||||||
|
}
|
||||||
|
options.height = poolData.height;
|
||||||
|
|
||||||
|
if (object && Number.isFinite(object._renderOrderBase)) {
|
||||||
|
options.renderOrder = object._renderOrderBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function rebuildPool() {
|
||||||
|
const object = getWaterPoolObject();
|
||||||
|
if (!object || rebuilding.value) return;
|
||||||
|
|
||||||
|
rebuilding.value = true;
|
||||||
|
try {
|
||||||
|
const options = buildOptionsFromUI(object);
|
||||||
|
|
||||||
|
const newPool = new WaterPool(options);
|
||||||
|
await newPool.loaded;
|
||||||
|
|
||||||
|
newPool.name = object.name;
|
||||||
|
newPool.position.copy(object.position);
|
||||||
|
newPool.quaternion.copy(object.quaternion);
|
||||||
|
newPool.scale.copy(object.scale);
|
||||||
|
newPool.visible = object.visible;
|
||||||
|
newPool.layers.mask = object.layers.mask;
|
||||||
|
newPool.userData = { ...object.userData };
|
||||||
|
|
||||||
|
const parent = object.parent ?? App.scene;
|
||||||
|
if (parent) {
|
||||||
|
const oldIndex = parent.children.indexOf(object);
|
||||||
|
parent.add(newPool);
|
||||||
|
const newIndex = parent.children.indexOf(newPool);
|
||||||
|
if (oldIndex >= 0 && newIndex >= 0 && newIndex !== oldIndex) {
|
||||||
|
parent.children.splice(newIndex, 1);
|
||||||
|
parent.children.splice(oldIndex, 0, newPool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Water.registerRendering();
|
||||||
|
|
||||||
|
object.parent?.remove(object);
|
||||||
|
|
||||||
|
App.select(newPool);
|
||||||
|
requestRender(newPool);
|
||||||
|
} catch (error) {
|
||||||
|
// 避免阻断侧边栏操作
|
||||||
|
console.warn("WaterPool 重建失败:", error);
|
||||||
|
} finally {
|
||||||
|
rebuilding.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyWallMode() {
|
||||||
|
rebuildPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyType() {
|
||||||
|
rebuildPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyQuality() {
|
||||||
|
rebuildPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
function scheduleQualityRebuild() {
|
||||||
|
if (qualityTimer !== null) {
|
||||||
|
clearTimeout(qualityTimer);
|
||||||
|
}
|
||||||
|
qualityTimer = window.setTimeout(() => {
|
||||||
|
qualityTimer = null;
|
||||||
|
applyQuality();
|
||||||
|
}, 600);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
Hooks.useAddSignal("objectSelected", updateUI);
|
||||||
|
updateUI();
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (qualityTimer !== null) {
|
||||||
|
clearTimeout(qualityTimer);
|
||||||
|
qualityTimer = null;
|
||||||
|
}
|
||||||
|
Hooks.useRemoveSignal("objectSelected", updateUI);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-collapse display-directive="show" :default-expanded-names="['basic', 'size', 'light', 'color', 'disturbance', 'refraction', 'texture', 'quality']">
|
||||||
|
<template #arrow>
|
||||||
|
<n-icon>
|
||||||
|
<CaretForwardOutline />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<n-collapse-item :title="t('layout.sider.waterPool.Basic')" name="basic">
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.Type") }}</span>
|
||||||
|
<n-select
|
||||||
|
v-model:value="poolData.type"
|
||||||
|
size="small"
|
||||||
|
:options="[
|
||||||
|
{ label: t('layout.sider.waterPool.Cylinder'), value: 'cylinder' },
|
||||||
|
{ label: t('layout.sider.waterPool.Square'), value: 'square' },
|
||||||
|
]"
|
||||||
|
:disabled="rebuilding"
|
||||||
|
@update:value="applyType"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.WallMode") }}</span>
|
||||||
|
<n-select
|
||||||
|
v-model:value="poolData.wallMode"
|
||||||
|
size="small"
|
||||||
|
:options="[
|
||||||
|
{ label: t('layout.sider.waterPool.WallNone'), value: 'none' },
|
||||||
|
{ label: t('layout.sider.waterPool.Wall'), value: 'wall' },
|
||||||
|
{ label: t('layout.sider.waterPool.Volume'), value: 'volume' },
|
||||||
|
]"
|
||||||
|
:disabled="rebuilding"
|
||||||
|
@update:value="applyWallMode"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.WallOpacity") }}</span>
|
||||||
|
<n-slider
|
||||||
|
v-model:value="poolData.wallOpacity"
|
||||||
|
:min="0"
|
||||||
|
:max="1"
|
||||||
|
:step="0.01"
|
||||||
|
:disabled="poolData.wallMode === 'none'"
|
||||||
|
@update:value="applyWallOpacity"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.ScreenRefraction") }}</span>
|
||||||
|
<n-switch size="small" v-model:value="poolData.useSceneRefraction" @update:value="applyRefraction" />
|
||||||
|
</div>
|
||||||
|
</n-collapse-item>
|
||||||
|
|
||||||
|
<n-collapse-item :title="t('layout.sider.waterPool.Size')" name="size">
|
||||||
|
<div class="sidebar-config-item" v-if="isCylinder">
|
||||||
|
<span>{{ t("layout.sider.waterPool.Diameter") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="poolData.diameter"
|
||||||
|
size="small"
|
||||||
|
:min="0.1"
|
||||||
|
:max="Infinity"
|
||||||
|
:decimal="2"
|
||||||
|
:show-button="false"
|
||||||
|
@change="applySize"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item" v-else>
|
||||||
|
<span>{{ t("layout.sider.waterPool.Width") }}</span>
|
||||||
|
<EsInputNumber v-model:value="poolData.width" size="small" :min="0.1" :max="Infinity" :decimal="2" :show-button="false" @change="applySize" />
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item" v-if="!isCylinder">
|
||||||
|
<span>{{ t("layout.sider.waterPool.Depth") }}</span>
|
||||||
|
<EsInputNumber v-model:value="poolData.depth" size="small" :min="0.1" :max="Infinity" :decimal="2" :show-button="false" @change="applySize" />
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.Height") }}</span>
|
||||||
|
<EsInputNumber v-model:value="poolData.height" size="small" :min="0.1" :max="Infinity" :decimal="2" :show-button="false" @change="applySize" />
|
||||||
|
</div>
|
||||||
|
</n-collapse-item>
|
||||||
|
|
||||||
|
<n-collapse-item :title="t('layout.sider.waterPool.Light')" name="light">
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.Direction") }}</span>
|
||||||
|
<div class="flex gap-6px">
|
||||||
|
<EsInputNumber v-model:value="poolData.light.x" size="small" :min="-1" :max="1" :decimal="2" :show-button="false" @change="applyLight" />
|
||||||
|
<EsInputNumber v-model:value="poolData.light.y" size="small" :min="-1" :max="1" :decimal="2" :show-button="false" @change="applyLight" />
|
||||||
|
<EsInputNumber v-model:value="poolData.light.z" size="small" :min="-1" :max="1" :decimal="2" :show-button="false" @change="applyLight" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n-collapse-item>
|
||||||
|
|
||||||
|
<n-collapse-item :title="t('layout.sider.waterPool.Color')" name="color">
|
||||||
|
<div class="sidebar-config-item" v-if="poolData.wallMode !== 'none'">
|
||||||
|
<span>{{ t("layout.sider.waterPool.VolumeColor") }}</span>
|
||||||
|
<n-color-picker v-model:value="volumeColor" :show-alpha="false" :modes="['hex']" size="small" @update:value="applyColors" />
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.SurfaceColor") }}</span>
|
||||||
|
<n-color-picker v-model:value="surfaceColor" :show-alpha="false" :modes="['hex']" size="small" @update:value="applyColors" />
|
||||||
|
</div>
|
||||||
|
</n-collapse-item>
|
||||||
|
|
||||||
|
<n-collapse-item :title="t('layout.sider.waterPool.Disturbance')" name="disturbance">
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.Enabled") }}</span>
|
||||||
|
<n-switch size="small" v-model:value="poolData.disturbanceEnabled" @update:value="applyDisturbance" />
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.Mode") }}</span>
|
||||||
|
<n-select
|
||||||
|
v-model:value="poolData.disturbanceMode"
|
||||||
|
size="small"
|
||||||
|
:options="[
|
||||||
|
{ label: t('layout.sider.waterPool.ModeDrag'), value: 'drag' },
|
||||||
|
{ label: t('layout.sider.waterPool.ModeUniform'), value: 'uniform' },
|
||||||
|
]"
|
||||||
|
@update:value="applyDisturbance"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.DropsPerStep") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="poolData.disturbanceDropsPerStep"
|
||||||
|
size="small"
|
||||||
|
:min="1"
|
||||||
|
:max="20"
|
||||||
|
:decimal="0"
|
||||||
|
:show-button="false"
|
||||||
|
@change="applyDisturbance"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.RadiusMin") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="poolData.disturbanceRadiusMin"
|
||||||
|
size="small"
|
||||||
|
:min="0"
|
||||||
|
:max="1"
|
||||||
|
:decimal="3"
|
||||||
|
:show-button="false"
|
||||||
|
@change="applyDisturbance"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.RadiusMax") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="poolData.disturbanceRadiusMax"
|
||||||
|
size="small"
|
||||||
|
:min="0"
|
||||||
|
:max="1"
|
||||||
|
:decimal="3"
|
||||||
|
:show-button="false"
|
||||||
|
@change="applyDisturbance"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.StrengthMin") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="poolData.disturbanceStrengthMin"
|
||||||
|
size="small"
|
||||||
|
:min="-1"
|
||||||
|
:max="1"
|
||||||
|
:decimal="3"
|
||||||
|
:show-button="false"
|
||||||
|
@change="applyDisturbance"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.StrengthMax") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="poolData.disturbanceStrengthMax"
|
||||||
|
size="small"
|
||||||
|
:min="-1"
|
||||||
|
:max="1"
|
||||||
|
:decimal="3"
|
||||||
|
:show-button="false"
|
||||||
|
@change="applyDisturbance"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.TravelRadius") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="poolData.disturbanceTravelRadius"
|
||||||
|
size="small"
|
||||||
|
:min="0"
|
||||||
|
:max="2"
|
||||||
|
:decimal="3"
|
||||||
|
:show-button="false"
|
||||||
|
@change="applyDisturbance"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.DriftSpeed") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="poolData.disturbanceDriftSpeed"
|
||||||
|
size="small"
|
||||||
|
:min="0"
|
||||||
|
:max="1"
|
||||||
|
:decimal="3"
|
||||||
|
:show-button="false"
|
||||||
|
@change="applyDisturbance"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.Jitter") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="poolData.disturbanceJitter"
|
||||||
|
size="small"
|
||||||
|
:min="0"
|
||||||
|
:max="1"
|
||||||
|
:decimal="3"
|
||||||
|
:show-button="false"
|
||||||
|
@change="applyDisturbance"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.Spread") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="poolData.disturbanceSpread"
|
||||||
|
size="small"
|
||||||
|
:min="0"
|
||||||
|
:max="1"
|
||||||
|
:decimal="3"
|
||||||
|
:show-button="false"
|
||||||
|
@change="applyDisturbance"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</n-collapse-item>
|
||||||
|
|
||||||
|
<n-collapse-item :title="t('layout.sider.waterPool.Refraction')" name="refraction">
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.SurfaceTransmittance") }}</span>
|
||||||
|
<n-slider v-model:value="poolData.surfaceTransmittance" :min="0" :max="1" :step="0.01" @update:value="applyRefraction" />
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.NormalStrength") }}</span>
|
||||||
|
<n-slider v-model:value="poolData.normalStrength" :min="0" :max="1" :step="0.01" @update:value="applyRefraction" />
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.RefractionStrength") }}</span>
|
||||||
|
<n-slider v-model:value="poolData.refractionStrength" :min="0" :max="1" :step="0.001" @update:value="applyRefraction" />
|
||||||
|
</div>
|
||||||
|
</n-collapse-item>
|
||||||
|
|
||||||
|
<n-collapse-item v-if="poolData.wallMode === 'wall'" :title="t('layout.sider.waterPool.Texture')" name="texture">
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.WallTexture") }}</span>
|
||||||
|
<EsTexture v-model:texture="tilesTexture" width="26px" height="26px" class="ml-5px" @change="applyTiles" />
|
||||||
|
</div>
|
||||||
|
</n-collapse-item>
|
||||||
|
|
||||||
|
<n-collapse-item :title="t('layout.sider.waterPool.Quality')" name="quality">
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.SimulationSize") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="poolData.simulationSize"
|
||||||
|
size="small"
|
||||||
|
:min="16"
|
||||||
|
:max="2048"
|
||||||
|
:decimal="0"
|
||||||
|
:show-button="false"
|
||||||
|
:disabled="rebuilding"
|
||||||
|
@update:value="scheduleQualityRebuild"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.CausticsSize") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="poolData.causticsSize"
|
||||||
|
size="small"
|
||||||
|
:min="64"
|
||||||
|
:max="4096"
|
||||||
|
:decimal="0"
|
||||||
|
:show-button="false"
|
||||||
|
:disabled="rebuilding"
|
||||||
|
@update:value="scheduleQualityRebuild"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.WaterSegments") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="poolData.waterSegments"
|
||||||
|
size="small"
|
||||||
|
:min="1"
|
||||||
|
:max="1024"
|
||||||
|
:decimal="0"
|
||||||
|
:show-button="false"
|
||||||
|
:disabled="rebuilding"
|
||||||
|
@update:value="scheduleQualityRebuild"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.waterPool.WallSegments") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="poolData.wallSegments"
|
||||||
|
size="small"
|
||||||
|
:min="3"
|
||||||
|
:max="512"
|
||||||
|
:decimal="0"
|
||||||
|
:show-button="false"
|
||||||
|
:disabled="rebuilding"
|
||||||
|
@update:value="scheduleQualityRebuild"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</n-collapse-item>
|
||||||
|
</n-collapse>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="less"></style>
|
||||||
@ -0,0 +1,342 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref, toRefs } from "vue";
|
||||||
|
import { t } from "@/language";
|
||||||
|
import EsInputNumber from "@/components/es/EsInputNumber.vue";
|
||||||
|
|
||||||
|
type UIPanelStateMap = Record<string, Record<string, any>>;
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
formData: { props: Record<string, any>; states: UIPanelStateMap };
|
||||||
|
updateProps: (patch: Record<string, any>) => void;
|
||||||
|
updateStateProps: (key: string, patch: Record<string, any>) => void;
|
||||||
|
isStateEnabled: (key: string) => boolean;
|
||||||
|
toggleState: (key: string, enabled: boolean) => void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { formData } = toRefs(props);
|
||||||
|
const backgroundImageInput = ref<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
|
const overflowHidden = computed({
|
||||||
|
get: () => formData.value.props.overflow === "hidden",
|
||||||
|
set: (value: boolean) => {
|
||||||
|
formData.value.props.overflow = value ? "hidden" : "visible";
|
||||||
|
props.updateProps({ overflow: formData.value.props.overflow });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const flexOptions = computed(() => [
|
||||||
|
{ label: t("layout.sider.uipanel.Row"), value: "row" },
|
||||||
|
{ label: t("layout.sider.uipanel.Row reverse"), value: "row-reverse" },
|
||||||
|
{ label: t("layout.sider.uipanel.Column"), value: "column" },
|
||||||
|
{ label: t("layout.sider.uipanel.Column reverse"), value: "column-reverse" },
|
||||||
|
]);
|
||||||
|
|
||||||
|
const justifyOptions = computed(() => [
|
||||||
|
{ label: t("layout.sider.uipanel.Start"), value: "start" },
|
||||||
|
{ label: t("layout.sider.uipanel.Center"), value: "center" },
|
||||||
|
{ label: t("layout.sider.uipanel.End"), value: "end" },
|
||||||
|
{ label: t("layout.sider.uipanel['Space between']"), value: "space-between" },
|
||||||
|
{ label: t("layout.sider.uipanel['Space around']"), value: "space-around" },
|
||||||
|
{ label: t("layout.sider.uipanel['Space evenly']"), value: "space-evenly" },
|
||||||
|
]);
|
||||||
|
|
||||||
|
const alignOptions = computed(() => [
|
||||||
|
{ label: t("layout.sider.uipanel.Start"), value: "start" },
|
||||||
|
{ label: t("layout.sider.uipanel.Center"), value: "center" },
|
||||||
|
{ label: t("layout.sider.uipanel.End"), value: "end" },
|
||||||
|
{ label: t("layout.sider.uipanel.Stretch"), value: "stretch" },
|
||||||
|
]);
|
||||||
|
|
||||||
|
const backgroundSizeOptions = computed(() => [
|
||||||
|
{ label: t("layout.sider.uipanel.Cover"), value: "cover" },
|
||||||
|
{ label: t("layout.sider.uipanel.Contain"), value: "contain" },
|
||||||
|
{ label: t("layout.sider.uipanel.Stretch"), value: "stretch" },
|
||||||
|
]);
|
||||||
|
|
||||||
|
function handleBackgroundImageUrl(value: string) {
|
||||||
|
formData.value.props.backgroundImage = value;
|
||||||
|
props.updateProps({ backgroundImage: value });
|
||||||
|
}
|
||||||
|
|
||||||
|
function openBackgroundImagePicker() {
|
||||||
|
backgroundImageInput.value?.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleBackgroundImageFile(event: Event) {
|
||||||
|
const input = event.target as HTMLInputElement;
|
||||||
|
const file = input.files?.[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
const maxSizeMb = 3;
|
||||||
|
if (file.size / (1024 * 1024) > maxSizeMb) {
|
||||||
|
console.log(file.size / (1024 * 1024));
|
||||||
|
const message = t("layout.sider.uipanel['Image size must be <= {size}MB']").replace("{size}", String(maxSizeMb));
|
||||||
|
window.$message?.warning(message);
|
||||||
|
input.value = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = () => {
|
||||||
|
if (typeof reader.result !== "string") return;
|
||||||
|
formData.value.props.backgroundImage = reader.result;
|
||||||
|
props.updateProps({ backgroundImage: reader.result });
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
input.value = "";
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.scene.Width") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="formData.props.width"
|
||||||
|
size="small"
|
||||||
|
:decimal="2"
|
||||||
|
:show-button="false"
|
||||||
|
@change="props.updateProps({ width: formData.props.width })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.scene.Height") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="formData.props.height"
|
||||||
|
size="small"
|
||||||
|
:decimal="2"
|
||||||
|
:show-button="false"
|
||||||
|
@change="props.updateProps({ height: formData.props.height })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel.Padding") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="formData.props.padding"
|
||||||
|
size="small"
|
||||||
|
:decimal="2"
|
||||||
|
:show-button="false"
|
||||||
|
@change="props.updateProps({ padding: formData.props.padding })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel.Margin") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="formData.props.margin"
|
||||||
|
size="small"
|
||||||
|
:decimal="2"
|
||||||
|
:show-button="false"
|
||||||
|
@change="props.updateProps({ margin: formData.props.margin })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel.Offset") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="formData.props.offset"
|
||||||
|
size="small"
|
||||||
|
:decimal="3"
|
||||||
|
:show-button="false"
|
||||||
|
@change="props.updateProps({ offset: formData.props.offset })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<n-divider class="!my-2" />
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Background color']") }}</span>
|
||||||
|
<n-color-picker
|
||||||
|
v-model:value="formData.props.backgroundColor"
|
||||||
|
:show-alpha="false"
|
||||||
|
size="small"
|
||||||
|
@update:value="props.updateProps({ backgroundColor: formData.props.backgroundColor })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Background opacity']") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="formData.props.backgroundOpacity"
|
||||||
|
size="small"
|
||||||
|
:min="0"
|
||||||
|
:max="1"
|
||||||
|
:decimal="2"
|
||||||
|
:show-button="false"
|
||||||
|
@change="props.updateProps({ backgroundOpacity: formData.props.backgroundOpacity })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Background image URL']") }}</span>
|
||||||
|
<n-input v-model:value="formData.props.backgroundImage" size="small" @update:value="handleBackgroundImageUrl" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Select local image']") }}</span>
|
||||||
|
<div class="uipanel-bg-image">
|
||||||
|
<n-button size="small" @click="openBackgroundImagePicker">
|
||||||
|
{{ t("layout.sider.uipanel['Select local image']") }}
|
||||||
|
</n-button>
|
||||||
|
<input ref="backgroundImageInput" class="uipanel-bg-image-input" type="file" accept="image/*" @change="handleBackgroundImageFile" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Background size']") }}</span>
|
||||||
|
<n-select
|
||||||
|
v-model:value="formData.props.backgroundSize"
|
||||||
|
size="small"
|
||||||
|
:options="backgroundSizeOptions"
|
||||||
|
@update:value="props.updateProps({ backgroundSize: formData.props.backgroundSize })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Border radius']") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="formData.props.borderRadius"
|
||||||
|
size="small"
|
||||||
|
:min="0"
|
||||||
|
:decimal="3"
|
||||||
|
:show-button="false"
|
||||||
|
@change="props.updateProps({ borderRadius: formData.props.borderRadius })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Border width']") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="formData.props.borderWidth"
|
||||||
|
size="small"
|
||||||
|
:min="0"
|
||||||
|
:decimal="3"
|
||||||
|
:show-button="false"
|
||||||
|
@change="props.updateProps({ borderWidth: formData.props.borderWidth })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Border color']") }}</span>
|
||||||
|
<n-color-picker
|
||||||
|
v-model:value="formData.props.borderColor"
|
||||||
|
:show-alpha="false"
|
||||||
|
size="small"
|
||||||
|
@update:value="props.updateProps({ borderColor: formData.props.borderColor })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Hidden overflow']") }}</span>
|
||||||
|
<n-switch v-model:value="overflowHidden" size="small" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<n-divider class="!my-2" />
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Flex direction']") }}</span>
|
||||||
|
<n-select
|
||||||
|
v-model:value="formData.props.flexDirection"
|
||||||
|
size="small"
|
||||||
|
:options="flexOptions"
|
||||||
|
@update:value="props.updateProps({ flexDirection: formData.props.flexDirection })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Justify content']") }}</span>
|
||||||
|
<n-select
|
||||||
|
v-model:value="formData.props.justifyContent"
|
||||||
|
size="small"
|
||||||
|
:options="justifyOptions"
|
||||||
|
@update:value="props.updateProps({ justifyContent: formData.props.justifyContent })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Align items']") }}</span>
|
||||||
|
<n-select
|
||||||
|
v-model:value="formData.props.alignItems"
|
||||||
|
size="small"
|
||||||
|
:options="alignOptions"
|
||||||
|
@update:value="props.updateProps({ alignItems: formData.props.alignItems })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<n-divider class="!my-2" />
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel.States") }}</span>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel.Hover") }}</span>
|
||||||
|
<n-switch size="small" :value="props.isStateEnabled('hover')" @update:value="(value: boolean) => props.toggleState('hover', value)" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-if="props.isStateEnabled('hover')">
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Hover background']") }}</span>
|
||||||
|
<n-color-picker
|
||||||
|
v-model:value="formData.states.hover.backgroundColor"
|
||||||
|
:show-alpha="false"
|
||||||
|
size="small"
|
||||||
|
@update:value="props.updateStateProps('hover', { backgroundColor: formData.states.hover.backgroundColor })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Hover opacity']") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="formData.states.hover.backgroundOpacity"
|
||||||
|
size="small"
|
||||||
|
:min="0"
|
||||||
|
:max="1"
|
||||||
|
:decimal="2"
|
||||||
|
:show-button="false"
|
||||||
|
@change="props.updateStateProps('hover', { backgroundOpacity: formData.states.hover.backgroundOpacity })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel.Active") }}</span>
|
||||||
|
<n-switch size="small" :value="props.isStateEnabled('active')" @update:value="(value: boolean) => props.toggleState('active', value)" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-if="props.isStateEnabled('active')">
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Active background']") }}</span>
|
||||||
|
<n-color-picker
|
||||||
|
v-model:value="formData.states.active.backgroundColor"
|
||||||
|
:show-alpha="false"
|
||||||
|
size="small"
|
||||||
|
@update:value="props.updateStateProps('active', { backgroundColor: formData.states.active.backgroundColor })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Active opacity']") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="formData.states.active.backgroundOpacity"
|
||||||
|
size="small"
|
||||||
|
:min="0"
|
||||||
|
:max="1"
|
||||||
|
:decimal="2"
|
||||||
|
:show-button="false"
|
||||||
|
@change="props.updateStateProps('active', { backgroundOpacity: formData.states.active.backgroundOpacity })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.uipanel-bg-image {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uipanel-bg-image-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,284 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, toRefs } from "vue";
|
||||||
|
import { t } from "@/language";
|
||||||
|
import EsInputNumber from "@/components/es/EsInputNumber.vue";
|
||||||
|
|
||||||
|
type UIPanelStateMap = Record<string, Record<string, any>>;
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
formData: { props: Record<string, any>; states: UIPanelStateMap };
|
||||||
|
updateProps: (patch: Record<string, any>) => void;
|
||||||
|
updateStateProps: (key: string, patch: Record<string, any>) => void;
|
||||||
|
isStateEnabled: (key: string) => boolean;
|
||||||
|
toggleState: (key: string, enabled: boolean) => void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { formData } = toRefs(props);
|
||||||
|
|
||||||
|
const fontSupersampling = computed({
|
||||||
|
get: () => formData.value.props.fontSmooth !== "none",
|
||||||
|
set: (value: boolean) => {
|
||||||
|
formData.value.props.fontSmooth = value ? "antialiased" : "none";
|
||||||
|
props.updateProps({ fontSmooth: formData.value.props.fontSmooth });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 宽度是否使用 auto
|
||||||
|
const widthIsAuto = computed({
|
||||||
|
get: () => formData.value.props.width === "auto",
|
||||||
|
set: (value: boolean) => {
|
||||||
|
formData.value.props.width = value ? "auto" : 0.5;
|
||||||
|
props.updateProps({ width: formData.value.props.width });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 高度是否使用 auto
|
||||||
|
const heightIsAuto = computed({
|
||||||
|
get: () => formData.value.props.height === "auto",
|
||||||
|
set: (value: boolean) => {
|
||||||
|
formData.value.props.height = value ? "auto" : 0.1;
|
||||||
|
props.updateProps({ height: formData.value.props.height });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 数值化的宽度(用于绑定数字输入)
|
||||||
|
const numericWidth = computed({
|
||||||
|
get: () => (typeof formData.value.props.width === "number" ? formData.value.props.width : 0),
|
||||||
|
set: (value: number) => {
|
||||||
|
formData.value.props.width = value;
|
||||||
|
props.updateProps({ width: value });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 数值化的高度(用于绑定数字输入)
|
||||||
|
const numericHeight = computed({
|
||||||
|
get: () => (typeof formData.value.props.height === "number" ? formData.value.props.height : 0),
|
||||||
|
set: (value: number) => {
|
||||||
|
formData.value.props.height = value;
|
||||||
|
props.updateProps({ height: value });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const textAlignOptions = computed(() => [
|
||||||
|
{ label: t("layout.sider.uipanel.Left"), value: "left" },
|
||||||
|
{ label: t("layout.sider.uipanel.Center"), value: "center" },
|
||||||
|
{ label: t("layout.sider.uipanel.Right"), value: "right" }
|
||||||
|
]);
|
||||||
|
|
||||||
|
const whiteSpaceOptions = [
|
||||||
|
{ label: "normal", value: "normal" },
|
||||||
|
{ label: "pre-line", value: "pre-line" },
|
||||||
|
{ label: "pre-wrap", value: "pre-wrap" },
|
||||||
|
{ label: "pre", value: "pre" },
|
||||||
|
{ label: "nowrap", value: "nowrap" },
|
||||||
|
];
|
||||||
|
|
||||||
|
function handleTextContent(value: string) {
|
||||||
|
formData.value.props.textContent = value;
|
||||||
|
props.updateProps({ textContent: value });
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel.Text") }}</span>
|
||||||
|
<n-input v-model:value="formData.props.textContent" type="textarea" size="small" :autosize="{ minRows: 1, maxRows: 3 }" @update:value="handleTextContent" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.scene.Width") }}</span>
|
||||||
|
<div class="uipanel-auto-size">
|
||||||
|
<n-switch v-model:value="widthIsAuto" size="small" />
|
||||||
|
<span class="uipanel-auto-label">auto</span>
|
||||||
|
<EsInputNumber v-if="!widthIsAuto" v-model:value="numericWidth" size="small" :min="0" :decimal="3" :show-button="false" @change="numericWidth = $event" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.scene.Height") }}</span>
|
||||||
|
<div class="uipanel-auto-size">
|
||||||
|
<n-switch v-model:value="heightIsAuto" size="small" />
|
||||||
|
<span class="uipanel-auto-label">auto</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-if="!heightIsAuto"
|
||||||
|
v-model:value="numericHeight"
|
||||||
|
size="small"
|
||||||
|
:min="0"
|
||||||
|
:decimal="3"
|
||||||
|
:show-button="false"
|
||||||
|
@change="numericHeight = $event"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Font size']") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="formData.props.fontSize"
|
||||||
|
size="small"
|
||||||
|
:decimal="3"
|
||||||
|
:show-button="false"
|
||||||
|
@change="props.updateProps({ fontSize: formData.props.fontSize })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel.Color") }}</span>
|
||||||
|
<n-color-picker v-model:value="formData.props.color" :show-alpha="false" size="small" @update:value="props.updateProps({ color: formData.props.color })" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Font opacity']") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="formData.props.fontOpacity"
|
||||||
|
size="small"
|
||||||
|
:min="0"
|
||||||
|
:max="1"
|
||||||
|
:decimal="2"
|
||||||
|
:show-button="false"
|
||||||
|
@change="props.updateProps({ fontOpacity: formData.props.fontOpacity })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Font supersampling']") }}</span>
|
||||||
|
<n-switch v-model:value="fontSupersampling" size="small" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Letter spacing']") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="formData.props.letterSpacing"
|
||||||
|
size="small"
|
||||||
|
:decimal="3"
|
||||||
|
:show-button="false"
|
||||||
|
@change="props.updateProps({ letterSpacing: formData.props.letterSpacing })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Line height']") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="formData.props.lineHeight"
|
||||||
|
size="small"
|
||||||
|
:decimal="2"
|
||||||
|
:show-button="false"
|
||||||
|
@change="props.updateProps({ lineHeight: formData.props.lineHeight })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel.Padding") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="formData.props.padding"
|
||||||
|
size="small"
|
||||||
|
:decimal="3"
|
||||||
|
:show-button="false"
|
||||||
|
@change="props.updateProps({ padding: formData.props.padding })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel.Margin") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="formData.props.margin"
|
||||||
|
size="small"
|
||||||
|
:decimal="3"
|
||||||
|
:show-button="false"
|
||||||
|
@change="props.updateProps({ margin: formData.props.margin })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel.Offset") }}</span>
|
||||||
|
<EsInputNumber
|
||||||
|
v-model:value="formData.props.offset"
|
||||||
|
size="small"
|
||||||
|
:decimal="3"
|
||||||
|
:show-button="false"
|
||||||
|
@change="props.updateProps({ offset: formData.props.offset })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<n-divider class="!my-2" />
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Text align']") }}</span>
|
||||||
|
<n-select
|
||||||
|
v-model:value="formData.props.textAlign"
|
||||||
|
size="small"
|
||||||
|
:options="textAlignOptions"
|
||||||
|
@update:value="props.updateProps({ textAlign: formData.props.textAlign })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['White space']") }}</span>
|
||||||
|
<n-select
|
||||||
|
v-model:value="formData.props.whiteSpace"
|
||||||
|
size="small"
|
||||||
|
:options="whiteSpaceOptions"
|
||||||
|
@update:value="props.updateProps({ whiteSpace: formData.props.whiteSpace })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Break on']") }}</span>
|
||||||
|
<n-input v-model:value="formData.props.breakOn" size="small" @update:value="(value: string) => props.updateProps({ breakOn: value })" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<n-divider class="!my-2" />
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel.States") }}</span>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel.Hover") }}</span>
|
||||||
|
<n-switch size="small" :value="props.isStateEnabled('hover')" @update:value="(value: boolean) => props.toggleState('hover', value)" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-if="props.isStateEnabled('hover')">
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Hover color']") }}</span>
|
||||||
|
<n-color-picker
|
||||||
|
v-model:value="formData.states.hover.color"
|
||||||
|
:show-alpha="false"
|
||||||
|
size="small"
|
||||||
|
@update:value="props.updateStateProps('hover', { color: formData.states.hover.color })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel.Active") }}</span>
|
||||||
|
<n-switch size="small" :value="props.isStateEnabled('active')" @update:value="(value: boolean) => props.toggleState('active', value)" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-if="props.isStateEnabled('active')">
|
||||||
|
<div class="sidebar-config-item">
|
||||||
|
<span>{{ t("layout.sider.uipanel['Active color']") }}</span>
|
||||||
|
<n-color-picker
|
||||||
|
v-model:value="formData.states.active.color"
|
||||||
|
:show-alpha="false"
|
||||||
|
size="small"
|
||||||
|
@update:value="props.updateStateProps('active', { color: formData.states.active.color })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.uipanel-auto-size {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uipanel-auto-label {
|
||||||
|
font-size: 12px;
|
||||||
|
opacity: 0.7;
|
||||||
|
min-width: 30px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,307 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, onBeforeUnmount, onMounted, watch } from "vue";
|
||||||
|
import * as THREE from "three";
|
||||||
|
import { AddObjectCommand, App, Hooks, Path, Utils, getDefaultPathOptions } from "@astral3d/engine";
|
||||||
|
import { t } from "@/language";
|
||||||
|
import type { IPathDrawingResult } from "@/store/modules/pathDrawing";
|
||||||
|
import { usePathDrawingStore } from "@/store/modules/pathDrawing";
|
||||||
|
|
||||||
|
const pathDrawingStore = usePathDrawingStore();
|
||||||
|
const isDrawing = computed(() => pathDrawingStore.isActive);
|
||||||
|
const submit = computed(() => pathDrawingStore.getSubmit);
|
||||||
|
let templateOptions: Record<string, any> | null = null;
|
||||||
|
let points: THREE.Vector3[] = [];
|
||||||
|
let origin: THREE.Vector3 | null = null;
|
||||||
|
let previewPath: Path | null = null;
|
||||||
|
let viewerRef: any = null;
|
||||||
|
let prevCursor = "";
|
||||||
|
const previewFlag = "__pathPreview";
|
||||||
|
|
||||||
|
const groundPlane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
|
||||||
|
const tempNdc = new THREE.Vector2();
|
||||||
|
const onDownPosition = new THREE.Vector2();
|
||||||
|
const onUpPosition = new THREE.Vector2();
|
||||||
|
|
||||||
|
let isPointerDown = false;
|
||||||
|
let lastClickTime = 0;
|
||||||
|
const signal = Hooks.useSignal();
|
||||||
|
|
||||||
|
function haltSelectionOnIntersect() {
|
||||||
|
if (!isDrawing.value) return;
|
||||||
|
signal.halt("intersectionsDetected");
|
||||||
|
}
|
||||||
|
|
||||||
|
function setViewer(viewer: any) {
|
||||||
|
viewerRef = viewer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cloneTemplate() {
|
||||||
|
const current = pathDrawingStore.getTemplate;
|
||||||
|
templateOptions = current ? JSON.parse(JSON.stringify(current)) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanupPreview() {
|
||||||
|
let removed = false;
|
||||||
|
|
||||||
|
if (previewPath) {
|
||||||
|
previewPath.parent?.remove(previewPath);
|
||||||
|
previewPath.dispose?.();
|
||||||
|
previewPath = null;
|
||||||
|
removed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const orphans: THREE.Object3D[] = [];
|
||||||
|
App.scene.traverse(child => {
|
||||||
|
if (child.userData?.[previewFlag]) {
|
||||||
|
orphans.push(child);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (orphans.length > 0) {
|
||||||
|
orphans.forEach(child => {
|
||||||
|
child.parent?.remove(child);
|
||||||
|
(child as any).dispose?.();
|
||||||
|
});
|
||||||
|
removed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removed) {
|
||||||
|
Hooks.useDispatchSignal("sceneGraphChanged");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetState() {
|
||||||
|
points = [];
|
||||||
|
origin = null;
|
||||||
|
isPointerDown = false;
|
||||||
|
lastClickTime = 0;
|
||||||
|
cleanupPreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildOptions(worldPoints: THREE.Vector3[], baseOrigin: THREE.Vector3) {
|
||||||
|
const options = getDefaultPathOptions();
|
||||||
|
if (templateOptions) {
|
||||||
|
Utils.deepAssign(options, templateOptions);
|
||||||
|
}
|
||||||
|
options.position = baseOrigin.toArray();
|
||||||
|
options.points = worldPoints.map(point => ({
|
||||||
|
x: point.x - baseOrigin.x,
|
||||||
|
y: point.y - baseOrigin.y,
|
||||||
|
z: point.z - baseOrigin.z,
|
||||||
|
}));
|
||||||
|
if (!options.name) options.name = "Path";
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildResult(worldPoints: THREE.Vector3[], baseOrigin: THREE.Vector3): IPathDrawingResult {
|
||||||
|
return {
|
||||||
|
worldPoints: worldPoints.map(point => ({ x: point.x, y: point.y, z: point.z })),
|
||||||
|
origin: {
|
||||||
|
x: baseOrigin.x,
|
||||||
|
y: baseOrigin.y,
|
||||||
|
z: baseOrigin.z,
|
||||||
|
},
|
||||||
|
options: buildOptions(worldPoints, baseOrigin),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePreview() {
|
||||||
|
if (!templateOptions) return;
|
||||||
|
if (points.length === 0) return;
|
||||||
|
|
||||||
|
if (!origin) {
|
||||||
|
origin = points[0].clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = buildOptions(points, origin);
|
||||||
|
if (!previewPath) {
|
||||||
|
const path = new Path(options);
|
||||||
|
path.ignore = true;
|
||||||
|
path.userData[previewFlag] = true;
|
||||||
|
previewPath = path;
|
||||||
|
App.scene.add(path);
|
||||||
|
} else {
|
||||||
|
previewPath.updateOptions({
|
||||||
|
mode: options.mode,
|
||||||
|
closed: options.closed,
|
||||||
|
cornerRadius: options.cornerRadius,
|
||||||
|
cornerSplit: options.cornerSplit,
|
||||||
|
path: options.path,
|
||||||
|
tube: options.tube,
|
||||||
|
position: options.position,
|
||||||
|
points: options.points,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Hooks.useDispatchSignal("sceneGraphChanged");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPickPoint(event: PointerEvent) {
|
||||||
|
const viewer = viewerRef || (window as any).viewer;
|
||||||
|
if (!viewer) return null;
|
||||||
|
|
||||||
|
const mouse = Utils.getMousePosition(viewer.container, event.clientX, event.clientY);
|
||||||
|
const intersects = viewer.getIntersects(new THREE.Vector2(mouse[0], mouse[1]));
|
||||||
|
if (intersects.length > 0) {
|
||||||
|
return intersects[0].point.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
tempNdc.set(mouse[0] * 2 - 1, -(mouse[1] * 2) + 1);
|
||||||
|
viewer.raycaster.setFromCamera(tempNdc, viewer.camera);
|
||||||
|
const hit = new THREE.Vector3();
|
||||||
|
return viewer.raycaster.ray.intersectPlane(groundPlane, hit) ? hit.clone() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addPoint(point: THREE.Vector3) {
|
||||||
|
points.push(point);
|
||||||
|
updatePreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishDrawing() {
|
||||||
|
if (!isDrawing.value) return;
|
||||||
|
if (points.length <= 2) {
|
||||||
|
cleanupPreview();
|
||||||
|
pathDrawingStore.cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseOrigin = origin || points[0];
|
||||||
|
const result = buildResult(points, baseOrigin);
|
||||||
|
if (submit.value) {
|
||||||
|
try {
|
||||||
|
submit.value(result);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[PathDrawingOverlay] path drawing submit failed", error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const path = new Path(result.options);
|
||||||
|
App.execute(new AddObjectCommand(path), `Add Path: ${result.options.name}`);
|
||||||
|
}
|
||||||
|
cleanupPreview();
|
||||||
|
pathDrawingStore.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelDrawing() {
|
||||||
|
if (!isDrawing.value) return;
|
||||||
|
cleanupPreview();
|
||||||
|
pathDrawingStore.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopDrawing() {
|
||||||
|
unbindEvents();
|
||||||
|
resetState();
|
||||||
|
|
||||||
|
const viewer = viewerRef || (window as any).viewer;
|
||||||
|
if (viewer) {
|
||||||
|
viewer.container.style.cursor = prevCursor || "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindEvents() {
|
||||||
|
const viewer = viewerRef || (window as any).viewer;
|
||||||
|
if (!viewer) return;
|
||||||
|
|
||||||
|
prevCursor = viewer.container.style.cursor || "";
|
||||||
|
viewer.container.style.cursor = "crosshair";
|
||||||
|
|
||||||
|
viewer.container.addEventListener("pointerdown", onPointerDown, true);
|
||||||
|
viewer.container.addEventListener("pointerup", onPointerUp, true);
|
||||||
|
viewer.container.addEventListener("dblclick", onDoubleClick, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unbindEvents() {
|
||||||
|
const viewer = viewerRef || (window as any).viewer;
|
||||||
|
if (!viewer) return;
|
||||||
|
viewer.container.removeEventListener("pointerdown", onPointerDown, true);
|
||||||
|
viewer.container.removeEventListener("pointerup", onPointerUp, true);
|
||||||
|
viewer.container.removeEventListener("dblclick", onDoubleClick, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPointerDown(event: PointerEvent) {
|
||||||
|
if (!isDrawing.value) return;
|
||||||
|
if (event.button !== 0) return;
|
||||||
|
const viewer = viewerRef || (window as any).viewer;
|
||||||
|
if (!viewer) return;
|
||||||
|
const array = Utils.getMousePosition(viewer.container, event.clientX, event.clientY);
|
||||||
|
onDownPosition.fromArray(array);
|
||||||
|
isPointerDown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPointerUp(event: PointerEvent) {
|
||||||
|
if (!isDrawing.value) return;
|
||||||
|
if (event.button !== 0) return;
|
||||||
|
if (!isPointerDown) return;
|
||||||
|
const viewer = viewerRef || (window as any).viewer;
|
||||||
|
if (!viewer) return;
|
||||||
|
const array = Utils.getMousePosition(viewer.container, event.clientX, event.clientY);
|
||||||
|
onUpPosition.fromArray(array);
|
||||||
|
isPointerDown = false;
|
||||||
|
|
||||||
|
if (onDownPosition.distanceTo(onUpPosition) !== 0) return;
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - lastClickTime < 200) return;
|
||||||
|
lastClickTime = now;
|
||||||
|
|
||||||
|
const point = getPickPoint(event);
|
||||||
|
if (!point) return;
|
||||||
|
addPoint(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDoubleClick(event: MouseEvent) {
|
||||||
|
if (!isDrawing.value) return;
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
finishDrawing();
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => isDrawing.value,
|
||||||
|
active => {
|
||||||
|
if (active) {
|
||||||
|
resetState();
|
||||||
|
cloneTemplate();
|
||||||
|
bindEvents();
|
||||||
|
} else {
|
||||||
|
stopDrawing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
Hooks.useAddSignal("intersectionsDetected", haltSelectionOnIntersect, undefined, 999);
|
||||||
|
Hooks.useAddSignal("viewerInitCompleted", setViewer);
|
||||||
|
if ((window as any).viewer) {
|
||||||
|
setViewer((window as any).viewer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
Hooks.useRemoveSignal("intersectionsDetected", haltSelectionOnIntersect);
|
||||||
|
Hooks.useRemoveSignal("viewerInitCompleted", setViewer);
|
||||||
|
stopDrawing();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-card v-if="isDrawing" size="small" class="path-drawing-overlay">
|
||||||
|
<n-space size="small" align="center" @pointerdown.stop>
|
||||||
|
<n-text strong>
|
||||||
|
{{ t("layout.scene.path['Path drawing: Left-click to add a point, and double-click to end the drawing']") }}
|
||||||
|
</n-text>
|
||||||
|
<n-button size="small" type="primary" @click="finishDrawing">{{ t("other.Finish") }}</n-button>
|
||||||
|
<n-button size="small" @click="cancelDrawing">{{ t("other.Cancel") }}</n-button>
|
||||||
|
</n-space>
|
||||||
|
</n-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.path-drawing-overlay {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 50%;
|
||||||
|
width: max-content;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: 20;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
<!-- IFC BIM 构件信息悬浮框 -->
|
<!-- IFC BIM 构件信息悬浮框 -->
|
||||||
<IFCProperties/>
|
<IFCProperties/>
|
||||||
|
|
||||||
|
<PathDrawingOverlay/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -18,6 +20,7 @@
|
|||||||
import {onMounted, ref, nextTick, onBeforeUnmount} from 'vue';
|
import {onMounted, ref, nextTick, onBeforeUnmount} from 'vue';
|
||||||
import {App,Viewer,Hooks} from "@astral3d/engine";
|
import {App,Viewer,Hooks} from "@astral3d/engine";
|
||||||
import Toolbar from "./Toolbar.vue";
|
import Toolbar from "./Toolbar.vue";
|
||||||
|
import PathDrawingOverlay from "./PathDrawingOverlay.vue";
|
||||||
import {useGlobalConfigStore} from "@/store/modules/globalConfig";
|
import {useGlobalConfigStore} from "@/store/modules/globalConfig";
|
||||||
import {usePluginStore} from "@/store/modules/plugin";
|
import {usePluginStore} from "@/store/modules/plugin";
|
||||||
import {installBuiltinPlugin} from "@/plugin";
|
import {installBuiltinPlugin} from "@/plugin";
|
||||||
|
|||||||
66
packages/editor/types/cesium/cesiumApp.d.ts
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import MapLayer from "@/cesium/modules/mapLayer";
|
||||||
|
import CameraUtils from "@/cesium/modules/cameraUtils";
|
||||||
|
import * as Cesium from 'cesium';
|
||||||
|
import * as THREE from 'three';
|
||||||
|
import { VNode } from "vue";
|
||||||
|
export default class CesiumApp {
|
||||||
|
dom: HTMLElement;
|
||||||
|
cesiumParentElement: HTMLElement;
|
||||||
|
viewer: Cesium.Viewer;
|
||||||
|
helper: Cesium.EventHelper;
|
||||||
|
module: {
|
||||||
|
mapLayer: MapLayer;
|
||||||
|
cameraUtils: CameraUtils;
|
||||||
|
};
|
||||||
|
_three: {
|
||||||
|
camera: THREE.PerspectiveCamera;
|
||||||
|
scene: THREE.Scene;
|
||||||
|
sceneHelpers: THREE.Scene;
|
||||||
|
showSceneHelpers: boolean;
|
||||||
|
renderer: THREE.WebGLRenderer;
|
||||||
|
};
|
||||||
|
constructor(dom: any);
|
||||||
|
getViewer(): Cesium.Viewer;
|
||||||
|
/**
|
||||||
|
* 场景事件监听
|
||||||
|
*/
|
||||||
|
eventListener(): void;
|
||||||
|
/**
|
||||||
|
* 鼠标按下处理
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
onMouseDown(event: any): void;
|
||||||
|
/**
|
||||||
|
* 鼠标抬起处理
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
onMouseUp(event: any): void;
|
||||||
|
/**
|
||||||
|
* three 场景点击事件
|
||||||
|
*/
|
||||||
|
handleClick(): void;
|
||||||
|
/**
|
||||||
|
* 初始化cesium 场景后的处理操作
|
||||||
|
*/
|
||||||
|
handleInitCesiumAfter(): void;
|
||||||
|
/**
|
||||||
|
* 添加中国地图遮罩
|
||||||
|
*/
|
||||||
|
addChinaMask(): void;
|
||||||
|
/**
|
||||||
|
* 获取与鼠标点击位置射线相交的对象数组
|
||||||
|
*/
|
||||||
|
getIntersects(point: any): THREE.Intersection<THREE.Object3D<THREE.Object3DEventMap>>[];
|
||||||
|
/**
|
||||||
|
* 添加VNode至 viewer._toolbar
|
||||||
|
*/
|
||||||
|
addVNodeToViewer(vNode: VNode): void;
|
||||||
|
/**
|
||||||
|
* 重置cesium场景
|
||||||
|
*/
|
||||||
|
reset(): void;
|
||||||
|
/**
|
||||||
|
* 销毁
|
||||||
|
*/
|
||||||
|
destroy(): void;
|
||||||
|
}
|
||||||
23
packages/editor/types/cesium/modules/cameraUtils.d.ts
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import * as Cesium from 'cesium';
|
||||||
|
/**
|
||||||
|
* @Date 2022-06-09
|
||||||
|
* @Author 二三
|
||||||
|
* @param {*} viewer Cesium.Viewer
|
||||||
|
* @Description: cesium相机类
|
||||||
|
*/
|
||||||
|
export default class CameraUtils {
|
||||||
|
viewer: Cesium.Viewer | null;
|
||||||
|
entity: Cesium.Entity | null;
|
||||||
|
constructor();
|
||||||
|
init(): void;
|
||||||
|
setViewer(viewer: any): void;
|
||||||
|
/**
|
||||||
|
* 修改鼠标操作方式
|
||||||
|
*/
|
||||||
|
changeMouseOperate(): void;
|
||||||
|
flyTo(lng: any, lat: any, distance: any, pitch?: number, heading?: number): void;
|
||||||
|
/**
|
||||||
|
* 销毁
|
||||||
|
*/
|
||||||
|
destroy(): void;
|
||||||
|
}
|
||||||
27
packages/editor/types/cesium/modules/mapLayer.d.ts
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import * as Cesium from 'cesium';
|
||||||
|
/**
|
||||||
|
* @Date 2023-03-07
|
||||||
|
* @Author 二三
|
||||||
|
* @Description: cesium地图底图图层管理
|
||||||
|
*/
|
||||||
|
export default class MapLayer {
|
||||||
|
viewer: Cesium.Viewer | null;
|
||||||
|
layers: {
|
||||||
|
[s: string]: {
|
||||||
|
satellite?: Cesium.UrlTemplateImageryProvider | Cesium.WebMapTileServiceImageryProvider;
|
||||||
|
mark?: Cesium.UrlTemplateImageryProvider;
|
||||||
|
vector?: Cesium.UrlTemplateImageryProvider;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
constructor();
|
||||||
|
setViewer(viewer: any): void;
|
||||||
|
/**
|
||||||
|
* 获取默认底图
|
||||||
|
* @param layer 底图类型,默认卫星影像图 enum: satellite | vector
|
||||||
|
*/
|
||||||
|
getDefaultLayer(layer?: 'satellite' | 'vector'): Cesium.UrlTemplateImageryProvider | Cesium.WebMapTileServiceImageryProvider | undefined;
|
||||||
|
/**
|
||||||
|
* 获取默认底图对应得标记图
|
||||||
|
*/
|
||||||
|
getMarkMapByDefaultLayer(): Cesium.UrlTemplateImageryProvider | undefined;
|
||||||
|
}
|
||||||
10
packages/editor/types/cesium/utils/utils.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* 笛卡尔坐标转换经纬度坐标
|
||||||
|
* @param {*} car3_ps
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export declare function getLngLatByCartesian3(car3_ps: any): {
|
||||||
|
longitude: number;
|
||||||
|
latitude: number;
|
||||||
|
elevation: number;
|
||||||
|
};
|
||||||
69
packages/editor/types/cesium/viewPort.d.ts
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import * as THREE from "three";
|
||||||
|
import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js';
|
||||||
|
import CesiumApp from "@/cesium/cesiumApp";
|
||||||
|
/**
|
||||||
|
* @Date 2023-02-06
|
||||||
|
* @Author 二三
|
||||||
|
* @Description: cesium视图出口
|
||||||
|
*/
|
||||||
|
export default class ViewPort {
|
||||||
|
app: CesiumApp;
|
||||||
|
cesiumParentElement: HTMLElement;
|
||||||
|
_three: {
|
||||||
|
camera: THREE.PerspectiveCamera;
|
||||||
|
scene: THREE.Scene;
|
||||||
|
sceneHelpers: THREE.Scene;
|
||||||
|
showSceneHelpers: boolean;
|
||||||
|
renderer: THREE.WebGLRenderer;
|
||||||
|
transformControls: TransformControls | null;
|
||||||
|
box: THREE.Box3;
|
||||||
|
};
|
||||||
|
threeSelectionBox: THREE.Box3Helper;
|
||||||
|
minWGS84: Array<number>;
|
||||||
|
maxWGS84: Array<number>;
|
||||||
|
animationFrameID: number | null;
|
||||||
|
constructor(dom: any);
|
||||||
|
init(): void;
|
||||||
|
/**
|
||||||
|
* 相关signals注册
|
||||||
|
*/
|
||||||
|
signalsRegister(isAdd?: boolean): void;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
addButtonToViewer(): void;
|
||||||
|
/**
|
||||||
|
* cesium 开始加载后的相关操作
|
||||||
|
*/
|
||||||
|
handleCesiumEvent(): void;
|
||||||
|
/**
|
||||||
|
* 初始化three变换控制器
|
||||||
|
*/
|
||||||
|
initThreeTransformControls(): void;
|
||||||
|
/**
|
||||||
|
* 计算场景中心位置等信息
|
||||||
|
*/
|
||||||
|
calcCenter(): void;
|
||||||
|
/**
|
||||||
|
* 处理three canvas 的 transformControls,以便于场景融合;
|
||||||
|
*/
|
||||||
|
handleThreeMouseMove(event: any): void;
|
||||||
|
/**
|
||||||
|
* 飞行至three场景
|
||||||
|
*/
|
||||||
|
flyToThree(): void;
|
||||||
|
/**
|
||||||
|
* cesium 渲染
|
||||||
|
*/
|
||||||
|
renderCesium(): void;
|
||||||
|
/**
|
||||||
|
* 两个画布相机融合
|
||||||
|
*/
|
||||||
|
fusionCanvas(): void;
|
||||||
|
/**
|
||||||
|
* three 渲染器的渲染
|
||||||
|
*/
|
||||||
|
renderThree(): void;
|
||||||
|
loop(): void;
|
||||||
|
destroy(): void;
|
||||||
|
}
|
||||||
9
packages/editor/types/config/cesium.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export declare const CESIUM_DEFAULT_MAP: {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
coordinateSystem: string;
|
||||||
|
}[];
|
||||||
|
export declare const CESIUM_DEFAULT_MAP_TYPE: {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}[];
|
||||||
34
packages/editor/types/config/service.d.ts
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/** 错误信息的显示时间 */
|
||||||
|
export declare const ERROR_MSG_DURATION: number;
|
||||||
|
/** 默认的请求错误code */
|
||||||
|
export declare const DEFAULT_REQUEST_ERROR_CODE = "DEFAULT";
|
||||||
|
/** 默认的请求错误文本 */
|
||||||
|
export declare const DEFAULT_REQUEST_ERROR_MSG = "\u8BF7\u6C42\u9519\u8BEF~";
|
||||||
|
/** 请求超时的错误code(为固定值:ECONNABORTED) */
|
||||||
|
export declare const REQUEST_TIMEOUT_CODE = "ECONNABORTED";
|
||||||
|
/** 请求超时的错误文本 */
|
||||||
|
export declare const REQUEST_TIMEOUT_MSG = "\u8BF7\u6C42\u8D85\u65F6~";
|
||||||
|
/** 网络不可用的code */
|
||||||
|
export declare const NETWORK_ERROR_CODE = "NETWORK_ERROR";
|
||||||
|
/** 网络不可用的错误文本 */
|
||||||
|
export declare const NETWORK_ERROR_MSG = "\u7F51\u7EDC\u4E0D\u53EF\u7528~";
|
||||||
|
/** 请求不成功各种状态的错误 */
|
||||||
|
export declare const ERROR_STATUS: {
|
||||||
|
400: string;
|
||||||
|
401: string;
|
||||||
|
403: string;
|
||||||
|
404: string;
|
||||||
|
405: string;
|
||||||
|
408: string;
|
||||||
|
500: string;
|
||||||
|
501: string;
|
||||||
|
502: string;
|
||||||
|
503: string;
|
||||||
|
504: string;
|
||||||
|
505: string;
|
||||||
|
DEFAULT: string;
|
||||||
|
};
|
||||||
|
/** 不弹出错误信息的code */
|
||||||
|
export declare const NO_ERROR_MSG_CODE: (string | number)[];
|
||||||
|
/** token失效需要刷新token的code */
|
||||||
|
export declare const REFRESH_TOKEN_CODE: (string | number)[];
|
||||||
15
packages/editor/types/core/AssetPreview.d.ts
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Preview } from "@astral3d/engine";
|
||||||
|
export declare class AssetPreview {
|
||||||
|
#private;
|
||||||
|
previewer: Preview;
|
||||||
|
private container;
|
||||||
|
static getInstance(container?: HTMLElement): AssetPreview;
|
||||||
|
constructor(container?: HTMLElement);
|
||||||
|
updateContainer(container: HTMLElement): void;
|
||||||
|
/**
|
||||||
|
* 加载预览项
|
||||||
|
*/
|
||||||
|
load(fileOrUrl: string | File, type?: string): Promise<unknown>;
|
||||||
|
clear(): void;
|
||||||
|
dispose(): void;
|
||||||
|
}
|
||||||
3
packages/editor/types/hooks/index.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import useBoolean from './useBoolean';
|
||||||
|
import useLoading from './useLoading';
|
||||||
|
export { useBoolean, useLoading };
|
||||||
11
packages/editor/types/hooks/useBoolean.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* boolean组合式函数
|
||||||
|
* @param initValue 初始值
|
||||||
|
*/
|
||||||
|
export default function useBoolean(initValue?: boolean): {
|
||||||
|
bool: import("vue").Ref<boolean, boolean>;
|
||||||
|
setBool: (value: boolean) => void;
|
||||||
|
setTrue: () => void;
|
||||||
|
setFalse: () => void;
|
||||||
|
toggle: () => void;
|
||||||
|
};
|
||||||
5
packages/editor/types/hooks/useLoading.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export default function useLoading(initValue?: boolean): {
|
||||||
|
loading: import("vue").Ref<boolean, boolean>;
|
||||||
|
startLoading: () => void;
|
||||||
|
endLoading: () => void;
|
||||||
|
};
|
||||||
25
packages/editor/types/hooks/useWebSocket.d.ts
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* 开启 WebSocket 链接,全局只需执行一次
|
||||||
|
* @param url
|
||||||
|
*/
|
||||||
|
export declare function connectWebSocket(url: any): void;
|
||||||
|
/**
|
||||||
|
* 添加 WebSocket 消息监听
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
export declare function onWebSocket(callback: (data: object) => any): void;
|
||||||
|
/**
|
||||||
|
* 解除 WebSocket 消息监听
|
||||||
|
*
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
export declare function offWebSocket(callback: (data: object) => any): void;
|
||||||
|
export declare function useWebSocket(): {
|
||||||
|
data: string | null;
|
||||||
|
status: import("@vueuse/core").WebSocketStatus;
|
||||||
|
close: WebSocket["close"];
|
||||||
|
open: import("@vueuse/core").Fn;
|
||||||
|
send: (data: string | ArrayBuffer | Blob, useBuffer?: boolean) => boolean;
|
||||||
|
ws: WebSocket | undefined;
|
||||||
|
} | null;
|
||||||
|
export declare function send(message: string | ArrayBuffer | Blob): void;
|
||||||
6
packages/editor/types/http/api/assetsCategory.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* 获取资产分类树
|
||||||
|
*/
|
||||||
|
export declare function fetchAssetsCategoryTreeList(params?: {
|
||||||
|
type: string;
|
||||||
|
}): Promise<Service.RequestResult<T>>;
|
||||||
20
packages/editor/types/http/api/assetsInfo.d.ts
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* 获取资产列表
|
||||||
|
*/
|
||||||
|
export declare function fetchGetAssetsList(params: Service.ListPageQueryParams): Promise<Service.RequestResult<T>>;
|
||||||
|
/**
|
||||||
|
* 新增资产
|
||||||
|
*/
|
||||||
|
export declare function fetchAddAsset(data: IAssets.Item): Promise<Service.RequestResult<T>>;
|
||||||
|
/**
|
||||||
|
* 更新资产
|
||||||
|
*/
|
||||||
|
export declare function fetchUpdateAsset(data: IAssets.Item): Promise<Service.RequestResult<T>>;
|
||||||
|
/**
|
||||||
|
* 移除资产
|
||||||
|
*/
|
||||||
|
export declare function fetchRemoveAsset(id: IAssets.Item['id']): Promise<Service.RequestResult<T>>;
|
||||||
|
/**
|
||||||
|
* 获取分类下的资产tags
|
||||||
|
*/
|
||||||
|
export declare function fetchGetAssetCategoryTags(type: IAssets.SupportType, category: string): Promise<Service.RequestResult<T>>;
|
||||||
12
packages/editor/types/http/api/bim.d.ts
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* 获取bim转换列表
|
||||||
|
*/
|
||||||
|
export declare function fetchGetBim2GltfList(params: any): Promise<Service.RequestResult<T>>;
|
||||||
|
/**
|
||||||
|
* 上传bim文件
|
||||||
|
*/
|
||||||
|
export declare function fetchUploadRvt(data: any): Promise<Service.RequestResult<T>>;
|
||||||
|
/**
|
||||||
|
* 添加数据并启动revit转换( 需传入接收结果的websocket uname)
|
||||||
|
*/
|
||||||
|
export declare function fetchAddBim2Gltf(data: any): Promise<Service.RequestResult<T>>;
|
||||||
8
packages/editor/types/http/api/cad.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* 获取cad列表
|
||||||
|
*/
|
||||||
|
export declare function fetchGetCadList(params: any): Promise<Service.RequestResult<T>>;
|
||||||
|
/**
|
||||||
|
* 添加数据并启动cad解析( 需传入接收结果的websocket uname)
|
||||||
|
*/
|
||||||
|
export declare function fetchAddDwg2dxf(data: any): Promise<Service.RequestResult<T>>;
|
||||||
17
packages/editor/types/http/api/sceneExample.d.ts
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* 获取所有示例场景
|
||||||
|
*/
|
||||||
|
export declare function fetchSceneExampleList(params: any): Promise<Service.RequestResult<T>>;
|
||||||
|
/**
|
||||||
|
* 获取示例场景
|
||||||
|
*/
|
||||||
|
export declare function fetchSceneExample(id: any): Promise<Service.RequestResult<T>>;
|
||||||
|
/**
|
||||||
|
* 新增示例场景
|
||||||
|
*/
|
||||||
|
export declare function fetchAddSceneExample(data: any): Promise<Service.RequestResult<T>>;
|
||||||
|
/**
|
||||||
|
* 删除示例场景
|
||||||
|
* @param {number} id
|
||||||
|
*/
|
||||||
|
export declare function fetchDeleteSceneExample(id: number): Promise<Service.RequestResult<T>>;
|
||||||
21
packages/editor/types/http/api/scenes.d.ts
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 获取所有工程信息
|
||||||
|
*/
|
||||||
|
export declare function fetchGetAllScenes(params: Service.ListPageQueryParams): Promise<Service.RequestResult<T>>;
|
||||||
|
/**
|
||||||
|
* 获取工程
|
||||||
|
*/
|
||||||
|
export declare function fetchGetOneScene(id: string): Promise<Service.RequestResult<T>>;
|
||||||
|
/**
|
||||||
|
* 保存工程
|
||||||
|
*/
|
||||||
|
export declare function fetchAddScene(data: any): Promise<Service.RequestResult<T>>;
|
||||||
|
/**
|
||||||
|
* 更新工程
|
||||||
|
*/
|
||||||
|
export declare function fetchUpdateScene(id: string, data: ISceneFetchData): Promise<Service.RequestResult<T>>;
|
||||||
|
/**
|
||||||
|
* 删除工程
|
||||||
|
* @param {number} id
|
||||||
|
*/
|
||||||
|
export declare function fetchDeleteScenes(id: string): Promise<Service.RequestResult<T>>;
|
||||||
8
packages/editor/types/http/api/sys.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* 上传
|
||||||
|
*/
|
||||||
|
export declare function fetchUpload(data: {
|
||||||
|
file: File;
|
||||||
|
biz: string;
|
||||||
|
type?: string;
|
||||||
|
}): Promise<Service.RequestResult<T>>;
|
||||||
16
packages/editor/types/http/request/ConcurrencyManager.d.ts
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
declare const ConcurrencyManager: (axios: any, MAX_CONCURRENT?: number) => {
|
||||||
|
queue: any;
|
||||||
|
running: any;
|
||||||
|
shiftInitial: () => void;
|
||||||
|
push: (reqHandler: any) => void;
|
||||||
|
shift: () => void;
|
||||||
|
requestHandler: (req: any) => Promise<unknown>;
|
||||||
|
responseHandler: (res: any) => any;
|
||||||
|
responseErrorHandler: (res: any) => Promise<never>;
|
||||||
|
interceptors: {
|
||||||
|
request: null;
|
||||||
|
response: null;
|
||||||
|
};
|
||||||
|
detach: () => void;
|
||||||
|
};
|
||||||
|
export default ConcurrencyManager;
|
||||||
6
packages/editor/types/http/request/index.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export declare const request: {
|
||||||
|
get: <T>(url: string, config?: import("axios").AxiosRequestConfig) => Promise<Service.RequestResult<T_1>>;
|
||||||
|
post: <T>(url: string, data?: any, config?: import("axios").AxiosRequestConfig) => Promise<Service.RequestResult<T_1>>;
|
||||||
|
put: <T>(url: string, data?: any, config?: import("axios").AxiosRequestConfig) => Promise<Service.RequestResult<T_1>>;
|
||||||
|
delete: <T>(url: string, config: import("axios").AxiosRequestConfig) => Promise<Service.RequestResult<T_1>>;
|
||||||
|
};
|
||||||
15
packages/editor/types/http/request/instance.d.ts
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import type { AxiosInstance, AxiosRequestConfig } from 'axios';
|
||||||
|
import { Service } from "../../../types/network";
|
||||||
|
export default class CustomAxiosInstance {
|
||||||
|
instance: AxiosInstance;
|
||||||
|
backendConfig: Service.BackendResultConfig;
|
||||||
|
manager: any;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param axiosConfig - axios配置
|
||||||
|
* @param backendConfig - 后端返回的数据配置
|
||||||
|
*/
|
||||||
|
constructor(axiosConfig: AxiosRequestConfig, backendConfig?: Service.BackendResultConfig);
|
||||||
|
/** 设置请求拦截器 */
|
||||||
|
setInterceptor(): void;
|
||||||
|
}
|
||||||
32
packages/editor/types/http/request/request.d.ts
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import type { Ref } from 'vue';
|
||||||
|
import type { AxiosRequestConfig } from 'axios';
|
||||||
|
import { Service } from "../../../types/network";
|
||||||
|
/**
|
||||||
|
* 创建请求
|
||||||
|
* @param axiosConfig - axios配置
|
||||||
|
* @param backendConfig - 后端接口字段配置
|
||||||
|
*/
|
||||||
|
export declare function createRequest(axiosConfig: AxiosRequestConfig, backendConfig?: Service.BackendResultConfig): {
|
||||||
|
get: <T>(url: string, config?: AxiosRequestConfig) => Promise<Service.RequestResult<T_1>>;
|
||||||
|
post: <T>(url: string, data?: any, config?: AxiosRequestConfig) => Promise<Service.RequestResult<T_1>>;
|
||||||
|
put: <T>(url: string, data?: any, config?: AxiosRequestConfig) => Promise<Service.RequestResult<T_1>>;
|
||||||
|
delete: <T>(url: string, config: AxiosRequestConfig) => Promise<Service.RequestResult<T_1>>;
|
||||||
|
};
|
||||||
|
interface RequestResultHook<T = any> {
|
||||||
|
data: Ref<T | null>;
|
||||||
|
error: Ref<Service.RequestError | null>;
|
||||||
|
loading: Ref<boolean>;
|
||||||
|
network: Ref<boolean>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 创建hooks请求
|
||||||
|
* @param axiosConfig - axios配置
|
||||||
|
* @param backendConfig - 后端接口字段配置
|
||||||
|
*/
|
||||||
|
export declare function createHookRequest(axiosConfig: AxiosRequestConfig, backendConfig?: Service.BackendResultConfig): {
|
||||||
|
get: <T>(url: string, config?: AxiosRequestConfig) => RequestResultHook<T>;
|
||||||
|
post: <T>(url: string, data?: any, config?: AxiosRequestConfig) => RequestResultHook<T>;
|
||||||
|
put: <T>(url: string, data?: any, config?: AxiosRequestConfig) => RequestResultHook<T>;
|
||||||
|
delete: <T>(url: string, config: AxiosRequestConfig) => RequestResultHook<T>;
|
||||||
|
};
|
||||||
|
export {};
|
||||||
6
packages/editor/types/language/index.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import type { App } from 'vue';
|
||||||
|
import { ComputedRef } from 'vue';
|
||||||
|
export declare function setupI18n(app: App): void;
|
||||||
|
export declare function t(key: string): string;
|
||||||
|
export declare function cpt(key: string): ComputedRef<string>;
|
||||||
|
export declare function setLocale(locale: IConfig.Locale): void;
|
||||||
1051
packages/editor/types/language/zh-CN-en-US.d.ts
vendored
Normal file
4
packages/editor/types/main.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import '@/utils/common/init';
|
||||||
|
import 'virtual:uno.css';
|
||||||
|
import "animate.css/animate.min.css";
|
||||||
|
import '@/assets/less/index.less';
|
||||||
21
packages/editor/types/plugin/glTFHandler/Listr.d.ts
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @author ErSan
|
||||||
|
* @email mlt131220@163.com
|
||||||
|
* @date 2024/9/18 22:24
|
||||||
|
* @description
|
||||||
|
*/
|
||||||
|
export declare class ListrTask {
|
||||||
|
private title;
|
||||||
|
private taskFn;
|
||||||
|
isFailed: boolean;
|
||||||
|
constructor(title: any, taskFn: any);
|
||||||
|
run(): Promise<void>;
|
||||||
|
}
|
||||||
|
export declare class Listr {
|
||||||
|
private tasks;
|
||||||
|
constructor(tasks: {
|
||||||
|
title: string;
|
||||||
|
task: (task: any) => Promise<void>;
|
||||||
|
}[]);
|
||||||
|
run(): Promise<void>;
|
||||||
|
}
|
||||||
28
packages/editor/types/plugin/glTFHandler/glTFHandler.d.ts
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import type { ModalReactive } from "naive-ui";
|
||||||
|
import type { Plugin } from "@astral3d/engine";
|
||||||
|
import { Logger, WebIO } from '@gltf-transform/core';
|
||||||
|
export declare const MICROMATCH_OPTIONS: {
|
||||||
|
nocase: boolean;
|
||||||
|
contains: boolean;
|
||||||
|
};
|
||||||
|
export default class GLTFHandler implements Plugin {
|
||||||
|
icon: string;
|
||||||
|
name: string;
|
||||||
|
version: number;
|
||||||
|
logger: Logger;
|
||||||
|
io: WebIO;
|
||||||
|
modalInstance: ModalReactive | undefined;
|
||||||
|
GLTFHandlerComponentRef: import("vue").Ref<any, any>;
|
||||||
|
dracoScript: {
|
||||||
|
encoder: boolean;
|
||||||
|
decoder: boolean;
|
||||||
|
failMsg: string;
|
||||||
|
};
|
||||||
|
install(): Promise<void>;
|
||||||
|
run(): Promise<void>;
|
||||||
|
finish(): void;
|
||||||
|
uninstall(): void;
|
||||||
|
setLogger(log: string): void;
|
||||||
|
registerDependencies(): Promise<void>;
|
||||||
|
optimize(opts: IPlugin.GLTFHandlerOptimizeModel, inputFile: File, outputFileName?: string): Promise<File | undefined>;
|
||||||
|
}
|
||||||
2
packages/editor/types/plugin/glTFHandler/optimizePng.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import type { Transform } from '@gltf-transform/core';
|
||||||
|
export declare const optimizePNG: () => Transform;
|
||||||
16
packages/editor/types/plugin/glTFHandler/session.d.ts
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { WebIO, Transform, Logger } from '@gltf-transform/core';
|
||||||
|
import GLTFHandler from "./glTFHandler";
|
||||||
|
export declare class Session {
|
||||||
|
private _io;
|
||||||
|
private _logger;
|
||||||
|
private setLogger;
|
||||||
|
private _input;
|
||||||
|
private _inputName;
|
||||||
|
private _output;
|
||||||
|
private _outputFormat;
|
||||||
|
private _display;
|
||||||
|
constructor(_io: WebIO, _logger: Logger, setLogger: (log: string) => void, _input: string, _inputName: string, _output: string);
|
||||||
|
static create(handler: GLTFHandler, inputFileUrl: string, inputName: string, output: string): Session;
|
||||||
|
setDisplay(display: boolean): this;
|
||||||
|
transform(...transforms: Transform[]): Promise<File>;
|
||||||
|
}
|
||||||
6
packages/editor/types/plugin/glTFHandler/util.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export declare const XMPContext: Record<string, string>;
|
||||||
|
export declare function formatLong(x: number): string;
|
||||||
|
export declare function formatBytes(bytes: number, decimals?: number): string;
|
||||||
|
export declare function dim(str: string): string;
|
||||||
|
export declare function encodeGLB(u8: Uint8Array, meta?: Record<string, any>): Promise<Uint8Array<ArrayBufferLike>>;
|
||||||
|
export declare function encodePNG(png: Uint8Array): Promise<Uint8Array<ArrayBufferLike>>;
|
||||||
1
packages/editor/types/plugin/index.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export declare const installBuiltinPlugin: (viewer: any) => void;
|
||||||
13
packages/editor/types/plugin/pointCloudReconstructor/PointCloudReconstructor.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import type { ModalReactive } from "naive-ui";
|
||||||
|
import type { Plugin } from "@astral3d/engine";
|
||||||
|
export default class PointCloudReconstructor implements Plugin {
|
||||||
|
icon: string;
|
||||||
|
name: string;
|
||||||
|
version: number;
|
||||||
|
modalInstance: ModalReactive | undefined;
|
||||||
|
componentRef: import("vue").Ref<any, any>;
|
||||||
|
install(): Promise<void>;
|
||||||
|
run(): Promise<void>;
|
||||||
|
finish(): void;
|
||||||
|
uninstall(): void;
|
||||||
|
}
|
||||||
3
packages/editor/types/router/index.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import type { App } from 'vue';
|
||||||
|
export declare const router: import("vue-router").Router;
|
||||||
|
export declare function setupRouter(app: App<Element>): void;
|
||||||
5
packages/editor/types/router/routes.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export declare const routes: {
|
||||||
|
path: string;
|
||||||
|
name: string;
|
||||||
|
component: () => Promise<typeof import("*.vue")>;
|
||||||
|
}[];
|
||||||
4
packages/editor/types/store/index.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import type { App } from 'vue';
|
||||||
|
declare const store: import("pinia").Pinia;
|
||||||
|
export declare function setupStore(app: App<Element>): void;
|
||||||
|
export { store };
|
||||||
372
packages/editor/types/store/modules/animation.d.ts
vendored
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
import type { TimelineTrack } from "@astral3d/engine";
|
||||||
|
export interface IAnimationItem {
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 模型动画(动画编辑器)
|
||||||
|
*/
|
||||||
|
export declare const useAnimationStore: import("pinia").StoreDefinition<"model-animation", Pick<{
|
||||||
|
getStepVal: () => number;
|
||||||
|
getFormattedDuration: import("vue").ComputedRef<string>;
|
||||||
|
getFormattedCurrentTime: import("vue").ComputedRef<string>;
|
||||||
|
setTimelineInstance: (instance: TimelineTrack) => void;
|
||||||
|
setList: (_list: Array<IAnimationItem>, _current?: IAnimationItem | null) => void;
|
||||||
|
setCurrent: (value: IAnimationItem | null) => void;
|
||||||
|
play: () => void;
|
||||||
|
pause: () => void;
|
||||||
|
stop: () => void;
|
||||||
|
jumpToStart: () => void;
|
||||||
|
jumpToEnd: () => void;
|
||||||
|
addKeyframe: (attr: string) => void;
|
||||||
|
setPlayTime: (time: number) => void;
|
||||||
|
list: import("vue").Ref<{
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
}[], {
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
}[]>;
|
||||||
|
current: import("vue").Ref<{
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
} | null, {
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
} | null>;
|
||||||
|
trackTree: import("vue").Ref<{
|
||||||
|
[x: string]: unknown;
|
||||||
|
key?: import("naive-ui/es/tree/src/interface").Key | undefined;
|
||||||
|
label?: string | undefined;
|
||||||
|
checkboxDisabled?: boolean | undefined;
|
||||||
|
disabled?: boolean | undefined;
|
||||||
|
isLeaf?: boolean | undefined;
|
||||||
|
children?: /*elided*/ any[] | undefined;
|
||||||
|
prefix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
suffix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
}[], {
|
||||||
|
[x: string]: unknown;
|
||||||
|
key?: import("naive-ui/es/tree/src/interface").Key | undefined;
|
||||||
|
label?: string | undefined;
|
||||||
|
checkboxDisabled?: boolean | undefined;
|
||||||
|
disabled?: boolean | undefined;
|
||||||
|
isLeaf?: boolean | undefined;
|
||||||
|
children?: /*elided*/ any[] | undefined;
|
||||||
|
prefix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
suffix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
}[]>;
|
||||||
|
mixerTimeScale: import("vue").Ref<number, number>;
|
||||||
|
currentTime: import("vue").Ref<number, number>;
|
||||||
|
duration: import("vue").Ref<number, number>;
|
||||||
|
}, "duration" | "list" | "current" | "trackTree" | "mixerTimeScale" | "currentTime">, Pick<{
|
||||||
|
getStepVal: () => number;
|
||||||
|
getFormattedDuration: import("vue").ComputedRef<string>;
|
||||||
|
getFormattedCurrentTime: import("vue").ComputedRef<string>;
|
||||||
|
setTimelineInstance: (instance: TimelineTrack) => void;
|
||||||
|
setList: (_list: Array<IAnimationItem>, _current?: IAnimationItem | null) => void;
|
||||||
|
setCurrent: (value: IAnimationItem | null) => void;
|
||||||
|
play: () => void;
|
||||||
|
pause: () => void;
|
||||||
|
stop: () => void;
|
||||||
|
jumpToStart: () => void;
|
||||||
|
jumpToEnd: () => void;
|
||||||
|
addKeyframe: (attr: string) => void;
|
||||||
|
setPlayTime: (time: number) => void;
|
||||||
|
list: import("vue").Ref<{
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
}[], {
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
}[]>;
|
||||||
|
current: import("vue").Ref<{
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
} | null, {
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
} | null>;
|
||||||
|
trackTree: import("vue").Ref<{
|
||||||
|
[x: string]: unknown;
|
||||||
|
key?: import("naive-ui/es/tree/src/interface").Key | undefined;
|
||||||
|
label?: string | undefined;
|
||||||
|
checkboxDisabled?: boolean | undefined;
|
||||||
|
disabled?: boolean | undefined;
|
||||||
|
isLeaf?: boolean | undefined;
|
||||||
|
children?: /*elided*/ any[] | undefined;
|
||||||
|
prefix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
suffix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
}[], {
|
||||||
|
[x: string]: unknown;
|
||||||
|
key?: import("naive-ui/es/tree/src/interface").Key | undefined;
|
||||||
|
label?: string | undefined;
|
||||||
|
checkboxDisabled?: boolean | undefined;
|
||||||
|
disabled?: boolean | undefined;
|
||||||
|
isLeaf?: boolean | undefined;
|
||||||
|
children?: /*elided*/ any[] | undefined;
|
||||||
|
prefix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
suffix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
}[]>;
|
||||||
|
mixerTimeScale: import("vue").Ref<number, number>;
|
||||||
|
currentTime: import("vue").Ref<number, number>;
|
||||||
|
duration: import("vue").Ref<number, number>;
|
||||||
|
}, "getFormattedDuration" | "getFormattedCurrentTime">, Pick<{
|
||||||
|
getStepVal: () => number;
|
||||||
|
getFormattedDuration: import("vue").ComputedRef<string>;
|
||||||
|
getFormattedCurrentTime: import("vue").ComputedRef<string>;
|
||||||
|
setTimelineInstance: (instance: TimelineTrack) => void;
|
||||||
|
setList: (_list: Array<IAnimationItem>, _current?: IAnimationItem | null) => void;
|
||||||
|
setCurrent: (value: IAnimationItem | null) => void;
|
||||||
|
play: () => void;
|
||||||
|
pause: () => void;
|
||||||
|
stop: () => void;
|
||||||
|
jumpToStart: () => void;
|
||||||
|
jumpToEnd: () => void;
|
||||||
|
addKeyframe: (attr: string) => void;
|
||||||
|
setPlayTime: (time: number) => void;
|
||||||
|
list: import("vue").Ref<{
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
}[], {
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
}[]>;
|
||||||
|
current: import("vue").Ref<{
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
} | null, {
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
} | null>;
|
||||||
|
trackTree: import("vue").Ref<{
|
||||||
|
[x: string]: unknown;
|
||||||
|
key?: import("naive-ui/es/tree/src/interface").Key | undefined;
|
||||||
|
label?: string | undefined;
|
||||||
|
checkboxDisabled?: boolean | undefined;
|
||||||
|
disabled?: boolean | undefined;
|
||||||
|
isLeaf?: boolean | undefined;
|
||||||
|
children?: /*elided*/ any[] | undefined;
|
||||||
|
prefix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
suffix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
}[], {
|
||||||
|
[x: string]: unknown;
|
||||||
|
key?: import("naive-ui/es/tree/src/interface").Key | undefined;
|
||||||
|
label?: string | undefined;
|
||||||
|
checkboxDisabled?: boolean | undefined;
|
||||||
|
disabled?: boolean | undefined;
|
||||||
|
isLeaf?: boolean | undefined;
|
||||||
|
children?: /*elided*/ any[] | undefined;
|
||||||
|
prefix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
suffix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
}[]>;
|
||||||
|
mixerTimeScale: import("vue").Ref<number, number>;
|
||||||
|
currentTime: import("vue").Ref<number, number>;
|
||||||
|
duration: import("vue").Ref<number, number>;
|
||||||
|
}, "pause" | "play" | "getStepVal" | "setTimelineInstance" | "setList" | "setCurrent" | "stop" | "jumpToStart" | "jumpToEnd" | "addKeyframe" | "setPlayTime">>;
|
||||||
|
export declare function useAnimationStoreWithOut(): import("pinia").Store<"model-animation", Pick<{
|
||||||
|
getStepVal: () => number;
|
||||||
|
getFormattedDuration: import("vue").ComputedRef<string>;
|
||||||
|
getFormattedCurrentTime: import("vue").ComputedRef<string>;
|
||||||
|
setTimelineInstance: (instance: TimelineTrack) => void;
|
||||||
|
setList: (_list: Array<IAnimationItem>, _current?: IAnimationItem | null) => void;
|
||||||
|
setCurrent: (value: IAnimationItem | null) => void;
|
||||||
|
play: () => void;
|
||||||
|
pause: () => void;
|
||||||
|
stop: () => void;
|
||||||
|
jumpToStart: () => void;
|
||||||
|
jumpToEnd: () => void;
|
||||||
|
addKeyframe: (attr: string) => void;
|
||||||
|
setPlayTime: (time: number) => void;
|
||||||
|
list: import("vue").Ref<{
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
}[], {
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
}[]>;
|
||||||
|
current: import("vue").Ref<{
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
} | null, {
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
} | null>;
|
||||||
|
trackTree: import("vue").Ref<{
|
||||||
|
[x: string]: unknown;
|
||||||
|
key?: import("naive-ui/es/tree/src/interface").Key | undefined;
|
||||||
|
label?: string | undefined;
|
||||||
|
checkboxDisabled?: boolean | undefined;
|
||||||
|
disabled?: boolean | undefined;
|
||||||
|
isLeaf?: boolean | undefined;
|
||||||
|
children?: /*elided*/ any[] | undefined;
|
||||||
|
prefix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
suffix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
}[], {
|
||||||
|
[x: string]: unknown;
|
||||||
|
key?: import("naive-ui/es/tree/src/interface").Key | undefined;
|
||||||
|
label?: string | undefined;
|
||||||
|
checkboxDisabled?: boolean | undefined;
|
||||||
|
disabled?: boolean | undefined;
|
||||||
|
isLeaf?: boolean | undefined;
|
||||||
|
children?: /*elided*/ any[] | undefined;
|
||||||
|
prefix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
suffix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
}[]>;
|
||||||
|
mixerTimeScale: import("vue").Ref<number, number>;
|
||||||
|
currentTime: import("vue").Ref<number, number>;
|
||||||
|
duration: import("vue").Ref<number, number>;
|
||||||
|
}, "duration" | "list" | "current" | "trackTree" | "mixerTimeScale" | "currentTime">, Pick<{
|
||||||
|
getStepVal: () => number;
|
||||||
|
getFormattedDuration: import("vue").ComputedRef<string>;
|
||||||
|
getFormattedCurrentTime: import("vue").ComputedRef<string>;
|
||||||
|
setTimelineInstance: (instance: TimelineTrack) => void;
|
||||||
|
setList: (_list: Array<IAnimationItem>, _current?: IAnimationItem | null) => void;
|
||||||
|
setCurrent: (value: IAnimationItem | null) => void;
|
||||||
|
play: () => void;
|
||||||
|
pause: () => void;
|
||||||
|
stop: () => void;
|
||||||
|
jumpToStart: () => void;
|
||||||
|
jumpToEnd: () => void;
|
||||||
|
addKeyframe: (attr: string) => void;
|
||||||
|
setPlayTime: (time: number) => void;
|
||||||
|
list: import("vue").Ref<{
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
}[], {
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
}[]>;
|
||||||
|
current: import("vue").Ref<{
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
} | null, {
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
} | null>;
|
||||||
|
trackTree: import("vue").Ref<{
|
||||||
|
[x: string]: unknown;
|
||||||
|
key?: import("naive-ui/es/tree/src/interface").Key | undefined;
|
||||||
|
label?: string | undefined;
|
||||||
|
checkboxDisabled?: boolean | undefined;
|
||||||
|
disabled?: boolean | undefined;
|
||||||
|
isLeaf?: boolean | undefined;
|
||||||
|
children?: /*elided*/ any[] | undefined;
|
||||||
|
prefix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
suffix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
}[], {
|
||||||
|
[x: string]: unknown;
|
||||||
|
key?: import("naive-ui/es/tree/src/interface").Key | undefined;
|
||||||
|
label?: string | undefined;
|
||||||
|
checkboxDisabled?: boolean | undefined;
|
||||||
|
disabled?: boolean | undefined;
|
||||||
|
isLeaf?: boolean | undefined;
|
||||||
|
children?: /*elided*/ any[] | undefined;
|
||||||
|
prefix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
suffix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
}[]>;
|
||||||
|
mixerTimeScale: import("vue").Ref<number, number>;
|
||||||
|
currentTime: import("vue").Ref<number, number>;
|
||||||
|
duration: import("vue").Ref<number, number>;
|
||||||
|
}, "getFormattedDuration" | "getFormattedCurrentTime">, Pick<{
|
||||||
|
getStepVal: () => number;
|
||||||
|
getFormattedDuration: import("vue").ComputedRef<string>;
|
||||||
|
getFormattedCurrentTime: import("vue").ComputedRef<string>;
|
||||||
|
setTimelineInstance: (instance: TimelineTrack) => void;
|
||||||
|
setList: (_list: Array<IAnimationItem>, _current?: IAnimationItem | null) => void;
|
||||||
|
setCurrent: (value: IAnimationItem | null) => void;
|
||||||
|
play: () => void;
|
||||||
|
pause: () => void;
|
||||||
|
stop: () => void;
|
||||||
|
jumpToStart: () => void;
|
||||||
|
jumpToEnd: () => void;
|
||||||
|
addKeyframe: (attr: string) => void;
|
||||||
|
setPlayTime: (time: number) => void;
|
||||||
|
list: import("vue").Ref<{
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
}[], {
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
}[]>;
|
||||||
|
current: import("vue").Ref<{
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
} | null, {
|
||||||
|
name: string;
|
||||||
|
uuid: string;
|
||||||
|
isRunning: boolean;
|
||||||
|
isPaused: boolean;
|
||||||
|
} | null>;
|
||||||
|
trackTree: import("vue").Ref<{
|
||||||
|
[x: string]: unknown;
|
||||||
|
key?: import("naive-ui/es/tree/src/interface").Key | undefined;
|
||||||
|
label?: string | undefined;
|
||||||
|
checkboxDisabled?: boolean | undefined;
|
||||||
|
disabled?: boolean | undefined;
|
||||||
|
isLeaf?: boolean | undefined;
|
||||||
|
children?: /*elided*/ any[] | undefined;
|
||||||
|
prefix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
suffix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
}[], {
|
||||||
|
[x: string]: unknown;
|
||||||
|
key?: import("naive-ui/es/tree/src/interface").Key | undefined;
|
||||||
|
label?: string | undefined;
|
||||||
|
checkboxDisabled?: boolean | undefined;
|
||||||
|
disabled?: boolean | undefined;
|
||||||
|
isLeaf?: boolean | undefined;
|
||||||
|
children?: /*elided*/ any[] | undefined;
|
||||||
|
prefix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
suffix?: (() => import("vue").VNodeChild) | undefined;
|
||||||
|
}[]>;
|
||||||
|
mixerTimeScale: import("vue").Ref<number, number>;
|
||||||
|
currentTime: import("vue").Ref<number, number>;
|
||||||
|
duration: import("vue").Ref<number, number>;
|
||||||
|
}, "pause" | "play" | "getStepVal" | "setTimelineInstance" | "setList" | "setCurrent" | "stop" | "jumpToStart" | "jumpToEnd" | "addKeyframe" | "setPlayTime">>;
|
||||||
185
packages/editor/types/store/modules/assets.d.ts
vendored
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
/**
|
||||||
|
* 资产管理
|
||||||
|
*/
|
||||||
|
export declare const useAssetsStore: import("pinia").StoreDefinition<"assets-manager", Pick<{
|
||||||
|
getCategoryChildren: (category: string) => IAssets.Category[];
|
||||||
|
fetchCategory: () => Promise<void>;
|
||||||
|
categories: import("vue").Ref<{
|
||||||
|
key: IAssets.SupportType;
|
||||||
|
label: string | ComputedRef<string>;
|
||||||
|
icon: any;
|
||||||
|
children: {
|
||||||
|
id: string | number;
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
pkey: string | null;
|
||||||
|
sortNum: number;
|
||||||
|
type: IAssets.SupportType;
|
||||||
|
children: /*elided*/ any[];
|
||||||
|
}[];
|
||||||
|
}[], {
|
||||||
|
key: IAssets.SupportType;
|
||||||
|
label: string | ComputedRef<string>;
|
||||||
|
icon: any;
|
||||||
|
children: {
|
||||||
|
id: string | number;
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
pkey: string | null;
|
||||||
|
sortNum: number;
|
||||||
|
type: IAssets.SupportType;
|
||||||
|
children: /*elided*/ any[];
|
||||||
|
}[];
|
||||||
|
}[]>;
|
||||||
|
}, "categories">, Pick<{
|
||||||
|
getCategoryChildren: (category: string) => IAssets.Category[];
|
||||||
|
fetchCategory: () => Promise<void>;
|
||||||
|
categories: import("vue").Ref<{
|
||||||
|
key: IAssets.SupportType;
|
||||||
|
label: string | ComputedRef<string>;
|
||||||
|
icon: any;
|
||||||
|
children: {
|
||||||
|
id: string | number;
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
pkey: string | null;
|
||||||
|
sortNum: number;
|
||||||
|
type: IAssets.SupportType;
|
||||||
|
children: /*elided*/ any[];
|
||||||
|
}[];
|
||||||
|
}[], {
|
||||||
|
key: IAssets.SupportType;
|
||||||
|
label: string | ComputedRef<string>;
|
||||||
|
icon: any;
|
||||||
|
children: {
|
||||||
|
id: string | number;
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
pkey: string | null;
|
||||||
|
sortNum: number;
|
||||||
|
type: IAssets.SupportType;
|
||||||
|
children: /*elided*/ any[];
|
||||||
|
}[];
|
||||||
|
}[]>;
|
||||||
|
}, never>, Pick<{
|
||||||
|
getCategoryChildren: (category: string) => IAssets.Category[];
|
||||||
|
fetchCategory: () => Promise<void>;
|
||||||
|
categories: import("vue").Ref<{
|
||||||
|
key: IAssets.SupportType;
|
||||||
|
label: string | ComputedRef<string>;
|
||||||
|
icon: any;
|
||||||
|
children: {
|
||||||
|
id: string | number;
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
pkey: string | null;
|
||||||
|
sortNum: number;
|
||||||
|
type: IAssets.SupportType;
|
||||||
|
children: /*elided*/ any[];
|
||||||
|
}[];
|
||||||
|
}[], {
|
||||||
|
key: IAssets.SupportType;
|
||||||
|
label: string | ComputedRef<string>;
|
||||||
|
icon: any;
|
||||||
|
children: {
|
||||||
|
id: string | number;
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
pkey: string | null;
|
||||||
|
sortNum: number;
|
||||||
|
type: IAssets.SupportType;
|
||||||
|
children: /*elided*/ any[];
|
||||||
|
}[];
|
||||||
|
}[]>;
|
||||||
|
}, "getCategoryChildren" | "fetchCategory">>;
|
||||||
|
export declare function useAssetsStoreWithOut(): import("pinia").Store<"assets-manager", Pick<{
|
||||||
|
getCategoryChildren: (category: string) => IAssets.Category[];
|
||||||
|
fetchCategory: () => Promise<void>;
|
||||||
|
categories: import("vue").Ref<{
|
||||||
|
key: IAssets.SupportType;
|
||||||
|
label: string | ComputedRef<string>;
|
||||||
|
icon: any;
|
||||||
|
children: {
|
||||||
|
id: string | number;
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
pkey: string | null;
|
||||||
|
sortNum: number;
|
||||||
|
type: IAssets.SupportType;
|
||||||
|
children: /*elided*/ any[];
|
||||||
|
}[];
|
||||||
|
}[], {
|
||||||
|
key: IAssets.SupportType;
|
||||||
|
label: string | ComputedRef<string>;
|
||||||
|
icon: any;
|
||||||
|
children: {
|
||||||
|
id: string | number;
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
pkey: string | null;
|
||||||
|
sortNum: number;
|
||||||
|
type: IAssets.SupportType;
|
||||||
|
children: /*elided*/ any[];
|
||||||
|
}[];
|
||||||
|
}[]>;
|
||||||
|
}, "categories">, Pick<{
|
||||||
|
getCategoryChildren: (category: string) => IAssets.Category[];
|
||||||
|
fetchCategory: () => Promise<void>;
|
||||||
|
categories: import("vue").Ref<{
|
||||||
|
key: IAssets.SupportType;
|
||||||
|
label: string | ComputedRef<string>;
|
||||||
|
icon: any;
|
||||||
|
children: {
|
||||||
|
id: string | number;
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
pkey: string | null;
|
||||||
|
sortNum: number;
|
||||||
|
type: IAssets.SupportType;
|
||||||
|
children: /*elided*/ any[];
|
||||||
|
}[];
|
||||||
|
}[], {
|
||||||
|
key: IAssets.SupportType;
|
||||||
|
label: string | ComputedRef<string>;
|
||||||
|
icon: any;
|
||||||
|
children: {
|
||||||
|
id: string | number;
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
pkey: string | null;
|
||||||
|
sortNum: number;
|
||||||
|
type: IAssets.SupportType;
|
||||||
|
children: /*elided*/ any[];
|
||||||
|
}[];
|
||||||
|
}[]>;
|
||||||
|
}, never>, Pick<{
|
||||||
|
getCategoryChildren: (category: string) => IAssets.Category[];
|
||||||
|
fetchCategory: () => Promise<void>;
|
||||||
|
categories: import("vue").Ref<{
|
||||||
|
key: IAssets.SupportType;
|
||||||
|
label: string | ComputedRef<string>;
|
||||||
|
icon: any;
|
||||||
|
children: {
|
||||||
|
id: string | number;
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
pkey: string | null;
|
||||||
|
sortNum: number;
|
||||||
|
type: IAssets.SupportType;
|
||||||
|
children: /*elided*/ any[];
|
||||||
|
}[];
|
||||||
|
}[], {
|
||||||
|
key: IAssets.SupportType;
|
||||||
|
label: string | ComputedRef<string>;
|
||||||
|
icon: any;
|
||||||
|
children: {
|
||||||
|
id: string | number;
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
pkey: string | null;
|
||||||
|
sortNum: number;
|
||||||
|
type: IAssets.SupportType;
|
||||||
|
children: /*elided*/ any[];
|
||||||
|
}[];
|
||||||
|
}[]>;
|
||||||
|
}, "getCategoryChildren" | "fetchCategory">>;
|
||||||
291
packages/editor/types/store/modules/drag.d.ts
vendored
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
import { Vector2 } from "three";
|
||||||
|
interface IDragState {
|
||||||
|
data: any;
|
||||||
|
actionTarget: "" | "addToScene";
|
||||||
|
endArea: "" | "Drawing" | "Scene";
|
||||||
|
endPosition: Vector2;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 拖拽相关
|
||||||
|
*/
|
||||||
|
export declare const useDragStore: import("pinia").StoreDefinition<"drag", IDragState, {
|
||||||
|
getData: (state: {
|
||||||
|
data: any;
|
||||||
|
actionTarget: "" | "addToScene";
|
||||||
|
endArea: "" | "Drawing" | "Scene";
|
||||||
|
endPosition: {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
readonly isVector2: true;
|
||||||
|
set: (x: number, y: number) => Vector2;
|
||||||
|
setScalar: (scalar: number) => Vector2;
|
||||||
|
setX: (x: number) => Vector2;
|
||||||
|
setY: (y: number) => Vector2;
|
||||||
|
setComponent: (index: number, value: number) => Vector2;
|
||||||
|
getComponent: (index: number) => number;
|
||||||
|
clone: () => Vector2;
|
||||||
|
copy: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
add: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
addScalar: (s: number) => Vector2;
|
||||||
|
addVectors: (a: import("three").Vector2Like, b: import("three").Vector2Like) => Vector2;
|
||||||
|
addScaledVector: (v: import("three").Vector2Like, s: number) => Vector2;
|
||||||
|
sub: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
subScalar: (s: number) => Vector2;
|
||||||
|
subVectors: (a: import("three").Vector2Like, b: import("three").Vector2Like) => Vector2;
|
||||||
|
multiply: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
multiplyScalar: (scalar: number) => Vector2;
|
||||||
|
divide: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
divideScalar: (s: number) => Vector2;
|
||||||
|
applyMatrix3: (m: import("three").Matrix3) => Vector2;
|
||||||
|
min: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
max: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
clamp: (min: import("three").Vector2Like, max: import("three").Vector2Like) => Vector2;
|
||||||
|
clampScalar: (min: number, max: number) => Vector2;
|
||||||
|
clampLength: (min: number, max: number) => Vector2;
|
||||||
|
floor: () => Vector2;
|
||||||
|
ceil: () => Vector2;
|
||||||
|
round: () => Vector2;
|
||||||
|
roundToZero: () => Vector2;
|
||||||
|
negate: () => Vector2;
|
||||||
|
dot: (v: import("three").Vector2Like) => number;
|
||||||
|
cross: (v: import("three").Vector2Like) => number;
|
||||||
|
lengthSq: () => number;
|
||||||
|
length: () => number;
|
||||||
|
manhattanLength: () => number;
|
||||||
|
normalize: () => Vector2;
|
||||||
|
angle: () => number;
|
||||||
|
angleTo: (v: Vector2) => number;
|
||||||
|
distanceTo: (v: import("three").Vector2Like) => number;
|
||||||
|
distanceToSquared: (v: import("three").Vector2Like) => number;
|
||||||
|
manhattanDistanceTo: (v: import("three").Vector2Like) => number;
|
||||||
|
setLength: (length: number) => Vector2;
|
||||||
|
lerp: (v: import("three").Vector2Like, alpha: number) => Vector2;
|
||||||
|
lerpVectors: (v1: import("three").Vector2Like, v2: import("three").Vector2Like, alpha: number) => Vector2;
|
||||||
|
equals: (v: import("three").Vector2Like) => boolean;
|
||||||
|
fromArray: (array: number[] | ArrayLike<number>, offset?: number) => Vector2;
|
||||||
|
toArray: {
|
||||||
|
(array?: number[], offset?: number): number[];
|
||||||
|
(array?: import("three").Vector2Tuple, offset?: 0): import("three").Vector2Tuple;
|
||||||
|
(array: ArrayLike<number>, offset?: number): ArrayLike<number>;
|
||||||
|
};
|
||||||
|
fromBufferAttribute: (attribute: import("three").BufferAttribute, index: number) => Vector2;
|
||||||
|
rotateAround: (center: import("three").Vector2Like, angle: number) => Vector2;
|
||||||
|
random: () => Vector2;
|
||||||
|
[Symbol.iterator]: () => Iterator<number>;
|
||||||
|
};
|
||||||
|
} & import("pinia").PiniaCustomStateProperties<IDragState>) => any;
|
||||||
|
getActionTarget: (state: {
|
||||||
|
data: any;
|
||||||
|
actionTarget: "" | "addToScene";
|
||||||
|
endArea: "" | "Drawing" | "Scene";
|
||||||
|
endPosition: {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
readonly isVector2: true;
|
||||||
|
set: (x: number, y: number) => Vector2;
|
||||||
|
setScalar: (scalar: number) => Vector2;
|
||||||
|
setX: (x: number) => Vector2;
|
||||||
|
setY: (y: number) => Vector2;
|
||||||
|
setComponent: (index: number, value: number) => Vector2;
|
||||||
|
getComponent: (index: number) => number;
|
||||||
|
clone: () => Vector2;
|
||||||
|
copy: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
add: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
addScalar: (s: number) => Vector2;
|
||||||
|
addVectors: (a: import("three").Vector2Like, b: import("three").Vector2Like) => Vector2;
|
||||||
|
addScaledVector: (v: import("three").Vector2Like, s: number) => Vector2;
|
||||||
|
sub: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
subScalar: (s: number) => Vector2;
|
||||||
|
subVectors: (a: import("three").Vector2Like, b: import("three").Vector2Like) => Vector2;
|
||||||
|
multiply: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
multiplyScalar: (scalar: number) => Vector2;
|
||||||
|
divide: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
divideScalar: (s: number) => Vector2;
|
||||||
|
applyMatrix3: (m: import("three").Matrix3) => Vector2;
|
||||||
|
min: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
max: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
clamp: (min: import("three").Vector2Like, max: import("three").Vector2Like) => Vector2;
|
||||||
|
clampScalar: (min: number, max: number) => Vector2;
|
||||||
|
clampLength: (min: number, max: number) => Vector2;
|
||||||
|
floor: () => Vector2;
|
||||||
|
ceil: () => Vector2;
|
||||||
|
round: () => Vector2;
|
||||||
|
roundToZero: () => Vector2;
|
||||||
|
negate: () => Vector2;
|
||||||
|
dot: (v: import("three").Vector2Like) => number;
|
||||||
|
cross: (v: import("three").Vector2Like) => number;
|
||||||
|
lengthSq: () => number;
|
||||||
|
length: () => number;
|
||||||
|
manhattanLength: () => number;
|
||||||
|
normalize: () => Vector2;
|
||||||
|
angle: () => number;
|
||||||
|
angleTo: (v: Vector2) => number;
|
||||||
|
distanceTo: (v: import("three").Vector2Like) => number;
|
||||||
|
distanceToSquared: (v: import("three").Vector2Like) => number;
|
||||||
|
manhattanDistanceTo: (v: import("three").Vector2Like) => number;
|
||||||
|
setLength: (length: number) => Vector2;
|
||||||
|
lerp: (v: import("three").Vector2Like, alpha: number) => Vector2;
|
||||||
|
lerpVectors: (v1: import("three").Vector2Like, v2: import("three").Vector2Like, alpha: number) => Vector2;
|
||||||
|
equals: (v: import("three").Vector2Like) => boolean;
|
||||||
|
fromArray: (array: number[] | ArrayLike<number>, offset?: number) => Vector2;
|
||||||
|
toArray: {
|
||||||
|
(array?: number[], offset?: number): number[];
|
||||||
|
(array?: import("three").Vector2Tuple, offset?: 0): import("three").Vector2Tuple;
|
||||||
|
(array: ArrayLike<number>, offset?: number): ArrayLike<number>;
|
||||||
|
};
|
||||||
|
fromBufferAttribute: (attribute: import("three").BufferAttribute, index: number) => Vector2;
|
||||||
|
rotateAround: (center: import("three").Vector2Like, angle: number) => Vector2;
|
||||||
|
random: () => Vector2;
|
||||||
|
[Symbol.iterator]: () => Iterator<number>;
|
||||||
|
};
|
||||||
|
} & import("pinia").PiniaCustomStateProperties<IDragState>) => "" | "addToScene";
|
||||||
|
}, {
|
||||||
|
setData(data: any): void;
|
||||||
|
setActionTarget(actionTarget: any): void;
|
||||||
|
setEndArea(area: any): void;
|
||||||
|
}>;
|
||||||
|
export declare function useDragStoreWithOut(): import("pinia").Store<"drag", IDragState, {
|
||||||
|
getData: (state: {
|
||||||
|
data: any;
|
||||||
|
actionTarget: "" | "addToScene";
|
||||||
|
endArea: "" | "Drawing" | "Scene";
|
||||||
|
endPosition: {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
readonly isVector2: true;
|
||||||
|
set: (x: number, y: number) => Vector2;
|
||||||
|
setScalar: (scalar: number) => Vector2;
|
||||||
|
setX: (x: number) => Vector2;
|
||||||
|
setY: (y: number) => Vector2;
|
||||||
|
setComponent: (index: number, value: number) => Vector2;
|
||||||
|
getComponent: (index: number) => number;
|
||||||
|
clone: () => Vector2;
|
||||||
|
copy: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
add: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
addScalar: (s: number) => Vector2;
|
||||||
|
addVectors: (a: import("three").Vector2Like, b: import("three").Vector2Like) => Vector2;
|
||||||
|
addScaledVector: (v: import("three").Vector2Like, s: number) => Vector2;
|
||||||
|
sub: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
subScalar: (s: number) => Vector2;
|
||||||
|
subVectors: (a: import("three").Vector2Like, b: import("three").Vector2Like) => Vector2;
|
||||||
|
multiply: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
multiplyScalar: (scalar: number) => Vector2;
|
||||||
|
divide: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
divideScalar: (s: number) => Vector2;
|
||||||
|
applyMatrix3: (m: import("three").Matrix3) => Vector2;
|
||||||
|
min: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
max: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
clamp: (min: import("three").Vector2Like, max: import("three").Vector2Like) => Vector2;
|
||||||
|
clampScalar: (min: number, max: number) => Vector2;
|
||||||
|
clampLength: (min: number, max: number) => Vector2;
|
||||||
|
floor: () => Vector2;
|
||||||
|
ceil: () => Vector2;
|
||||||
|
round: () => Vector2;
|
||||||
|
roundToZero: () => Vector2;
|
||||||
|
negate: () => Vector2;
|
||||||
|
dot: (v: import("three").Vector2Like) => number;
|
||||||
|
cross: (v: import("three").Vector2Like) => number;
|
||||||
|
lengthSq: () => number;
|
||||||
|
length: () => number;
|
||||||
|
manhattanLength: () => number;
|
||||||
|
normalize: () => Vector2;
|
||||||
|
angle: () => number;
|
||||||
|
angleTo: (v: Vector2) => number;
|
||||||
|
distanceTo: (v: import("three").Vector2Like) => number;
|
||||||
|
distanceToSquared: (v: import("three").Vector2Like) => number;
|
||||||
|
manhattanDistanceTo: (v: import("three").Vector2Like) => number;
|
||||||
|
setLength: (length: number) => Vector2;
|
||||||
|
lerp: (v: import("three").Vector2Like, alpha: number) => Vector2;
|
||||||
|
lerpVectors: (v1: import("three").Vector2Like, v2: import("three").Vector2Like, alpha: number) => Vector2;
|
||||||
|
equals: (v: import("three").Vector2Like) => boolean;
|
||||||
|
fromArray: (array: number[] | ArrayLike<number>, offset?: number) => Vector2;
|
||||||
|
toArray: {
|
||||||
|
(array?: number[], offset?: number): number[];
|
||||||
|
(array?: import("three").Vector2Tuple, offset?: 0): import("three").Vector2Tuple;
|
||||||
|
(array: ArrayLike<number>, offset?: number): ArrayLike<number>;
|
||||||
|
};
|
||||||
|
fromBufferAttribute: (attribute: import("three").BufferAttribute, index: number) => Vector2;
|
||||||
|
rotateAround: (center: import("three").Vector2Like, angle: number) => Vector2;
|
||||||
|
random: () => Vector2;
|
||||||
|
[Symbol.iterator]: () => Iterator<number>;
|
||||||
|
};
|
||||||
|
} & import("pinia").PiniaCustomStateProperties<IDragState>) => any;
|
||||||
|
getActionTarget: (state: {
|
||||||
|
data: any;
|
||||||
|
actionTarget: "" | "addToScene";
|
||||||
|
endArea: "" | "Drawing" | "Scene";
|
||||||
|
endPosition: {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
readonly isVector2: true;
|
||||||
|
set: (x: number, y: number) => Vector2;
|
||||||
|
setScalar: (scalar: number) => Vector2;
|
||||||
|
setX: (x: number) => Vector2;
|
||||||
|
setY: (y: number) => Vector2;
|
||||||
|
setComponent: (index: number, value: number) => Vector2;
|
||||||
|
getComponent: (index: number) => number;
|
||||||
|
clone: () => Vector2;
|
||||||
|
copy: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
add: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
addScalar: (s: number) => Vector2;
|
||||||
|
addVectors: (a: import("three").Vector2Like, b: import("three").Vector2Like) => Vector2;
|
||||||
|
addScaledVector: (v: import("three").Vector2Like, s: number) => Vector2;
|
||||||
|
sub: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
subScalar: (s: number) => Vector2;
|
||||||
|
subVectors: (a: import("three").Vector2Like, b: import("three").Vector2Like) => Vector2;
|
||||||
|
multiply: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
multiplyScalar: (scalar: number) => Vector2;
|
||||||
|
divide: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
divideScalar: (s: number) => Vector2;
|
||||||
|
applyMatrix3: (m: import("three").Matrix3) => Vector2;
|
||||||
|
min: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
max: (v: import("three").Vector2Like) => Vector2;
|
||||||
|
clamp: (min: import("three").Vector2Like, max: import("three").Vector2Like) => Vector2;
|
||||||
|
clampScalar: (min: number, max: number) => Vector2;
|
||||||
|
clampLength: (min: number, max: number) => Vector2;
|
||||||
|
floor: () => Vector2;
|
||||||
|
ceil: () => Vector2;
|
||||||
|
round: () => Vector2;
|
||||||
|
roundToZero: () => Vector2;
|
||||||
|
negate: () => Vector2;
|
||||||
|
dot: (v: import("three").Vector2Like) => number;
|
||||||
|
cross: (v: import("three").Vector2Like) => number;
|
||||||
|
lengthSq: () => number;
|
||||||
|
length: () => number;
|
||||||
|
manhattanLength: () => number;
|
||||||
|
normalize: () => Vector2;
|
||||||
|
angle: () => number;
|
||||||
|
angleTo: (v: Vector2) => number;
|
||||||
|
distanceTo: (v: import("three").Vector2Like) => number;
|
||||||
|
distanceToSquared: (v: import("three").Vector2Like) => number;
|
||||||
|
manhattanDistanceTo: (v: import("three").Vector2Like) => number;
|
||||||
|
setLength: (length: number) => Vector2;
|
||||||
|
lerp: (v: import("three").Vector2Like, alpha: number) => Vector2;
|
||||||
|
lerpVectors: (v1: import("three").Vector2Like, v2: import("three").Vector2Like, alpha: number) => Vector2;
|
||||||
|
equals: (v: import("three").Vector2Like) => boolean;
|
||||||
|
fromArray: (array: number[] | ArrayLike<number>, offset?: number) => Vector2;
|
||||||
|
toArray: {
|
||||||
|
(array?: number[], offset?: number): number[];
|
||||||
|
(array?: import("three").Vector2Tuple, offset?: 0): import("three").Vector2Tuple;
|
||||||
|
(array: ArrayLike<number>, offset?: number): ArrayLike<number>;
|
||||||
|
};
|
||||||
|
fromBufferAttribute: (attribute: import("three").BufferAttribute, index: number) => Vector2;
|
||||||
|
rotateAround: (center: import("three").Vector2Like, angle: number) => Vector2;
|
||||||
|
random: () => Vector2;
|
||||||
|
[Symbol.iterator]: () => Iterator<number>;
|
||||||
|
};
|
||||||
|
} & import("pinia").PiniaCustomStateProperties<IDragState>) => "" | "addToScene";
|
||||||
|
}, {
|
||||||
|
setData(data: any): void;
|
||||||
|
setActionTarget(actionTarget: any): void;
|
||||||
|
setEndArea(area: any): void;
|
||||||
|
}>;
|
||||||
|
export {};
|
||||||
66
packages/editor/types/store/modules/globalConfig.d.ts
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import type { GlobalTheme } from 'naive-ui';
|
||||||
|
/**
|
||||||
|
* 全局配置
|
||||||
|
*/
|
||||||
|
export declare const useGlobalConfigStore: import("pinia").StoreDefinition<"global-config", Pick<{
|
||||||
|
theme: import("@vueuse/core").RemovableRef<IConfig.Theme>;
|
||||||
|
locale: import("@vueuse/core").RemovableRef<IConfig.Locale>;
|
||||||
|
mainColor: import("@vueuse/core").RemovableRef<IConfig.Color>;
|
||||||
|
loading: import("vue").Ref<boolean, boolean>;
|
||||||
|
loadingText: import("vue").Ref<string, string>;
|
||||||
|
getProviderTheme: () => GlobalTheme;
|
||||||
|
setTheme: () => void;
|
||||||
|
setLocale: (local: IConfig.Locale) => void;
|
||||||
|
setPrimaryColor: (color: IConfig.Color) => void;
|
||||||
|
}, "theme" | "locale" | "mainColor" | "loading" | "loadingText">, Pick<{
|
||||||
|
theme: import("@vueuse/core").RemovableRef<IConfig.Theme>;
|
||||||
|
locale: import("@vueuse/core").RemovableRef<IConfig.Locale>;
|
||||||
|
mainColor: import("@vueuse/core").RemovableRef<IConfig.Color>;
|
||||||
|
loading: import("vue").Ref<boolean, boolean>;
|
||||||
|
loadingText: import("vue").Ref<string, string>;
|
||||||
|
getProviderTheme: () => GlobalTheme;
|
||||||
|
setTheme: () => void;
|
||||||
|
setLocale: (local: IConfig.Locale) => void;
|
||||||
|
setPrimaryColor: (color: IConfig.Color) => void;
|
||||||
|
}, never>, Pick<{
|
||||||
|
theme: import("@vueuse/core").RemovableRef<IConfig.Theme>;
|
||||||
|
locale: import("@vueuse/core").RemovableRef<IConfig.Locale>;
|
||||||
|
mainColor: import("@vueuse/core").RemovableRef<IConfig.Color>;
|
||||||
|
loading: import("vue").Ref<boolean, boolean>;
|
||||||
|
loadingText: import("vue").Ref<string, string>;
|
||||||
|
getProviderTheme: () => GlobalTheme;
|
||||||
|
setTheme: () => void;
|
||||||
|
setLocale: (local: IConfig.Locale) => void;
|
||||||
|
setPrimaryColor: (color: IConfig.Color) => void;
|
||||||
|
}, "getProviderTheme" | "setTheme" | "setLocale" | "setPrimaryColor">>;
|
||||||
|
export declare function useGlobalConfigStoreWithOut(): import("pinia").Store<"global-config", Pick<{
|
||||||
|
theme: import("@vueuse/core").RemovableRef<IConfig.Theme>;
|
||||||
|
locale: import("@vueuse/core").RemovableRef<IConfig.Locale>;
|
||||||
|
mainColor: import("@vueuse/core").RemovableRef<IConfig.Color>;
|
||||||
|
loading: import("vue").Ref<boolean, boolean>;
|
||||||
|
loadingText: import("vue").Ref<string, string>;
|
||||||
|
getProviderTheme: () => GlobalTheme;
|
||||||
|
setTheme: () => void;
|
||||||
|
setLocale: (local: IConfig.Locale) => void;
|
||||||
|
setPrimaryColor: (color: IConfig.Color) => void;
|
||||||
|
}, "theme" | "locale" | "mainColor" | "loading" | "loadingText">, Pick<{
|
||||||
|
theme: import("@vueuse/core").RemovableRef<IConfig.Theme>;
|
||||||
|
locale: import("@vueuse/core").RemovableRef<IConfig.Locale>;
|
||||||
|
mainColor: import("@vueuse/core").RemovableRef<IConfig.Color>;
|
||||||
|
loading: import("vue").Ref<boolean, boolean>;
|
||||||
|
loadingText: import("vue").Ref<string, string>;
|
||||||
|
getProviderTheme: () => GlobalTheme;
|
||||||
|
setTheme: () => void;
|
||||||
|
setLocale: (local: IConfig.Locale) => void;
|
||||||
|
setPrimaryColor: (color: IConfig.Color) => void;
|
||||||
|
}, never>, Pick<{
|
||||||
|
theme: import("@vueuse/core").RemovableRef<IConfig.Theme>;
|
||||||
|
locale: import("@vueuse/core").RemovableRef<IConfig.Locale>;
|
||||||
|
mainColor: import("@vueuse/core").RemovableRef<IConfig.Color>;
|
||||||
|
loading: import("vue").Ref<boolean, boolean>;
|
||||||
|
loadingText: import("vue").Ref<string, string>;
|
||||||
|
getProviderTheme: () => GlobalTheme;
|
||||||
|
setTheme: () => void;
|
||||||
|
setLocale: (local: IConfig.Locale) => void;
|
||||||
|
setPrimaryColor: (color: IConfig.Color) => void;
|
||||||
|
}, "getProviderTheme" | "setTheme" | "setLocale" | "setPrimaryColor">>;
|
||||||
62
packages/editor/types/store/modules/plugin.d.ts
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
export declare const usePluginStore: import("pinia").StoreDefinition<"plugin", Pick<{
|
||||||
|
getPluginsList: () => import("vue").ComputedRef<IPlugin.Item[]>;
|
||||||
|
setPlugins: (_plugins: IPlugin.Item[]) => void;
|
||||||
|
addPlugin: (plugin: IPlugin.Item) => void;
|
||||||
|
removePlugin: (pluginName: string) => void;
|
||||||
|
plugins: import("vue").Ref<{
|
||||||
|
[name: string]: IPlugin.Item;
|
||||||
|
}, {
|
||||||
|
[name: string]: IPlugin.Item;
|
||||||
|
}>;
|
||||||
|
}, "plugins">, Pick<{
|
||||||
|
getPluginsList: () => import("vue").ComputedRef<IPlugin.Item[]>;
|
||||||
|
setPlugins: (_plugins: IPlugin.Item[]) => void;
|
||||||
|
addPlugin: (plugin: IPlugin.Item) => void;
|
||||||
|
removePlugin: (pluginName: string) => void;
|
||||||
|
plugins: import("vue").Ref<{
|
||||||
|
[name: string]: IPlugin.Item;
|
||||||
|
}, {
|
||||||
|
[name: string]: IPlugin.Item;
|
||||||
|
}>;
|
||||||
|
}, never>, Pick<{
|
||||||
|
getPluginsList: () => import("vue").ComputedRef<IPlugin.Item[]>;
|
||||||
|
setPlugins: (_plugins: IPlugin.Item[]) => void;
|
||||||
|
addPlugin: (plugin: IPlugin.Item) => void;
|
||||||
|
removePlugin: (pluginName: string) => void;
|
||||||
|
plugins: import("vue").Ref<{
|
||||||
|
[name: string]: IPlugin.Item;
|
||||||
|
}, {
|
||||||
|
[name: string]: IPlugin.Item;
|
||||||
|
}>;
|
||||||
|
}, "getPluginsList" | "setPlugins" | "addPlugin" | "removePlugin">>;
|
||||||
|
export declare function usePluginStoreWithOut(): import("pinia").Store<"plugin", Pick<{
|
||||||
|
getPluginsList: () => import("vue").ComputedRef<IPlugin.Item[]>;
|
||||||
|
setPlugins: (_plugins: IPlugin.Item[]) => void;
|
||||||
|
addPlugin: (plugin: IPlugin.Item) => void;
|
||||||
|
removePlugin: (pluginName: string) => void;
|
||||||
|
plugins: import("vue").Ref<{
|
||||||
|
[name: string]: IPlugin.Item;
|
||||||
|
}, {
|
||||||
|
[name: string]: IPlugin.Item;
|
||||||
|
}>;
|
||||||
|
}, "plugins">, Pick<{
|
||||||
|
getPluginsList: () => import("vue").ComputedRef<IPlugin.Item[]>;
|
||||||
|
setPlugins: (_plugins: IPlugin.Item[]) => void;
|
||||||
|
addPlugin: (plugin: IPlugin.Item) => void;
|
||||||
|
removePlugin: (pluginName: string) => void;
|
||||||
|
plugins: import("vue").Ref<{
|
||||||
|
[name: string]: IPlugin.Item;
|
||||||
|
}, {
|
||||||
|
[name: string]: IPlugin.Item;
|
||||||
|
}>;
|
||||||
|
}, never>, Pick<{
|
||||||
|
getPluginsList: () => import("vue").ComputedRef<IPlugin.Item[]>;
|
||||||
|
setPlugins: (_plugins: IPlugin.Item[]) => void;
|
||||||
|
addPlugin: (plugin: IPlugin.Item) => void;
|
||||||
|
removePlugin: (pluginName: string) => void;
|
||||||
|
plugins: import("vue").Ref<{
|
||||||
|
[name: string]: IPlugin.Item;
|
||||||
|
}, {
|
||||||
|
[name: string]: IPlugin.Item;
|
||||||
|
}>;
|
||||||
|
}, "getPluginsList" | "setPlugins" | "addPlugin" | "removePlugin">>;
|
||||||
22
packages/editor/types/store/modules/previewOperation.d.ts
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import type { Component } from 'vue';
|
||||||
|
export interface IPreviewOperation {
|
||||||
|
name: string;
|
||||||
|
active?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
loading?: boolean;
|
||||||
|
show?: boolean;
|
||||||
|
icon: Component;
|
||||||
|
children?: {
|
||||||
|
[key: string]: IPreviewOperation;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
interface IPreviewOperationState {
|
||||||
|
menuList: {
|
||||||
|
[key: string]: IPreviewOperation;
|
||||||
|
};
|
||||||
|
autoRotateSpeed: number;
|
||||||
|
explodeScalar: number;
|
||||||
|
}
|
||||||
|
export declare const usePreviewOperationStore: import("pinia").StoreDefinition<"previewOperation", IPreviewOperationState, {}, {}>;
|
||||||
|
export declare function usePreviewOperationStoreWithOut(): import("pinia").Store<"previewOperation", IPreviewOperationState, {}, {}>;
|
||||||
|
export {};
|
||||||
131
packages/editor/types/store/modules/websocket.d.ts
vendored
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
/**
|
||||||
|
* websocket相关
|
||||||
|
*/
|
||||||
|
export declare const useWebsocketStore: import("pinia").StoreDefinition<"websocket", Pick<{
|
||||||
|
getIsOpen: () => boolean | null;
|
||||||
|
setWebsocket: (websocket: any) => void;
|
||||||
|
setUname: (uname: string) => void;
|
||||||
|
send: (message: string | ArrayBuffer | Blob) => void;
|
||||||
|
ws: import("vue").Ref<{
|
||||||
|
data: string | null;
|
||||||
|
status: import("@vueuse/core").WebSocketStatus;
|
||||||
|
close: WebSocket["close"];
|
||||||
|
open: import("@vueuse/core").Fn;
|
||||||
|
send: (data: string | ArrayBuffer | Blob, useBuffer?: boolean) => boolean;
|
||||||
|
ws: WebSocket | undefined;
|
||||||
|
} | null, {
|
||||||
|
data: string | null;
|
||||||
|
status: import("@vueuse/core").WebSocketStatus;
|
||||||
|
close: WebSocket["close"];
|
||||||
|
open: import("@vueuse/core").Fn;
|
||||||
|
send: (data: string | ArrayBuffer | Blob, useBuffer?: boolean) => boolean;
|
||||||
|
ws: WebSocket | undefined;
|
||||||
|
} | null>;
|
||||||
|
uname: import("vue").Ref<string, string>;
|
||||||
|
}, "ws" | "uname">, Pick<{
|
||||||
|
getIsOpen: () => boolean | null;
|
||||||
|
setWebsocket: (websocket: any) => void;
|
||||||
|
setUname: (uname: string) => void;
|
||||||
|
send: (message: string | ArrayBuffer | Blob) => void;
|
||||||
|
ws: import("vue").Ref<{
|
||||||
|
data: string | null;
|
||||||
|
status: import("@vueuse/core").WebSocketStatus;
|
||||||
|
close: WebSocket["close"];
|
||||||
|
open: import("@vueuse/core").Fn;
|
||||||
|
send: (data: string | ArrayBuffer | Blob, useBuffer?: boolean) => boolean;
|
||||||
|
ws: WebSocket | undefined;
|
||||||
|
} | null, {
|
||||||
|
data: string | null;
|
||||||
|
status: import("@vueuse/core").WebSocketStatus;
|
||||||
|
close: WebSocket["close"];
|
||||||
|
open: import("@vueuse/core").Fn;
|
||||||
|
send: (data: string | ArrayBuffer | Blob, useBuffer?: boolean) => boolean;
|
||||||
|
ws: WebSocket | undefined;
|
||||||
|
} | null>;
|
||||||
|
uname: import("vue").Ref<string, string>;
|
||||||
|
}, never>, Pick<{
|
||||||
|
getIsOpen: () => boolean | null;
|
||||||
|
setWebsocket: (websocket: any) => void;
|
||||||
|
setUname: (uname: string) => void;
|
||||||
|
send: (message: string | ArrayBuffer | Blob) => void;
|
||||||
|
ws: import("vue").Ref<{
|
||||||
|
data: string | null;
|
||||||
|
status: import("@vueuse/core").WebSocketStatus;
|
||||||
|
close: WebSocket["close"];
|
||||||
|
open: import("@vueuse/core").Fn;
|
||||||
|
send: (data: string | ArrayBuffer | Blob, useBuffer?: boolean) => boolean;
|
||||||
|
ws: WebSocket | undefined;
|
||||||
|
} | null, {
|
||||||
|
data: string | null;
|
||||||
|
status: import("@vueuse/core").WebSocketStatus;
|
||||||
|
close: WebSocket["close"];
|
||||||
|
open: import("@vueuse/core").Fn;
|
||||||
|
send: (data: string | ArrayBuffer | Blob, useBuffer?: boolean) => boolean;
|
||||||
|
ws: WebSocket | undefined;
|
||||||
|
} | null>;
|
||||||
|
uname: import("vue").Ref<string, string>;
|
||||||
|
}, "send" | "getIsOpen" | "setWebsocket" | "setUname">>;
|
||||||
|
export declare function useWebsocketStoreWithOut(): import("pinia").Store<"websocket", Pick<{
|
||||||
|
getIsOpen: () => boolean | null;
|
||||||
|
setWebsocket: (websocket: any) => void;
|
||||||
|
setUname: (uname: string) => void;
|
||||||
|
send: (message: string | ArrayBuffer | Blob) => void;
|
||||||
|
ws: import("vue").Ref<{
|
||||||
|
data: string | null;
|
||||||
|
status: import("@vueuse/core").WebSocketStatus;
|
||||||
|
close: WebSocket["close"];
|
||||||
|
open: import("@vueuse/core").Fn;
|
||||||
|
send: (data: string | ArrayBuffer | Blob, useBuffer?: boolean) => boolean;
|
||||||
|
ws: WebSocket | undefined;
|
||||||
|
} | null, {
|
||||||
|
data: string | null;
|
||||||
|
status: import("@vueuse/core").WebSocketStatus;
|
||||||
|
close: WebSocket["close"];
|
||||||
|
open: import("@vueuse/core").Fn;
|
||||||
|
send: (data: string | ArrayBuffer | Blob, useBuffer?: boolean) => boolean;
|
||||||
|
ws: WebSocket | undefined;
|
||||||
|
} | null>;
|
||||||
|
uname: import("vue").Ref<string, string>;
|
||||||
|
}, "ws" | "uname">, Pick<{
|
||||||
|
getIsOpen: () => boolean | null;
|
||||||
|
setWebsocket: (websocket: any) => void;
|
||||||
|
setUname: (uname: string) => void;
|
||||||
|
send: (message: string | ArrayBuffer | Blob) => void;
|
||||||
|
ws: import("vue").Ref<{
|
||||||
|
data: string | null;
|
||||||
|
status: import("@vueuse/core").WebSocketStatus;
|
||||||
|
close: WebSocket["close"];
|
||||||
|
open: import("@vueuse/core").Fn;
|
||||||
|
send: (data: string | ArrayBuffer | Blob, useBuffer?: boolean) => boolean;
|
||||||
|
ws: WebSocket | undefined;
|
||||||
|
} | null, {
|
||||||
|
data: string | null;
|
||||||
|
status: import("@vueuse/core").WebSocketStatus;
|
||||||
|
close: WebSocket["close"];
|
||||||
|
open: import("@vueuse/core").Fn;
|
||||||
|
send: (data: string | ArrayBuffer | Blob, useBuffer?: boolean) => boolean;
|
||||||
|
ws: WebSocket | undefined;
|
||||||
|
} | null>;
|
||||||
|
uname: import("vue").Ref<string, string>;
|
||||||
|
}, never>, Pick<{
|
||||||
|
getIsOpen: () => boolean | null;
|
||||||
|
setWebsocket: (websocket: any) => void;
|
||||||
|
setUname: (uname: string) => void;
|
||||||
|
send: (message: string | ArrayBuffer | Blob) => void;
|
||||||
|
ws: import("vue").Ref<{
|
||||||
|
data: string | null;
|
||||||
|
status: import("@vueuse/core").WebSocketStatus;
|
||||||
|
close: WebSocket["close"];
|
||||||
|
open: import("@vueuse/core").Fn;
|
||||||
|
send: (data: string | ArrayBuffer | Blob, useBuffer?: boolean) => boolean;
|
||||||
|
ws: WebSocket | undefined;
|
||||||
|
} | null, {
|
||||||
|
data: string | null;
|
||||||
|
status: import("@vueuse/core").WebSocketStatus;
|
||||||
|
close: WebSocket["close"];
|
||||||
|
open: import("@vueuse/core").Fn;
|
||||||
|
send: (data: string | ArrayBuffer | Blob, useBuffer?: boolean) => boolean;
|
||||||
|
ws: WebSocket | undefined;
|
||||||
|
} | null>;
|
||||||
|
uname: import("vue").Ref<string, string>;
|
||||||
|
}, "send" | "getIsOpen" | "setWebsocket" | "setUname">>;
|
||||||
12
packages/editor/types/utils/common/base64.d.ts
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* byte数组转换成base64 数据
|
||||||
|
* @param {Array<byte>} buffer
|
||||||
|
* @return {string} base64 string
|
||||||
|
*/
|
||||||
|
export declare function _arrayBufferToBase64(buffer: any): string;
|
||||||
|
/**
|
||||||
|
* base64数据转换成byte数组
|
||||||
|
* @param {string} base64
|
||||||
|
* @return {string} buffer
|
||||||
|
*/
|
||||||
|
export declare function _base64ToArrayBuffer(base64: any): ArrayBuffer;
|
||||||
1
packages/editor/types/utils/common/color.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export declare function decToRgb(number: number): string;
|
||||||
24
packages/editor/types/utils/common/constant.d.ts
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* @author ErSan
|
||||||
|
* @email mlt131220@163.com
|
||||||
|
* @date 2024/3/26 9:57
|
||||||
|
* @description 全局常量
|
||||||
|
*/
|
||||||
|
export declare const MODEL_SUPPORT_TYPE: string[];
|
||||||
|
export declare const ASSET_UPLOAD_SUPPORT_TYPE: {
|
||||||
|
Model: string[];
|
||||||
|
Material: string[];
|
||||||
|
Texture: string[];
|
||||||
|
Billboard: string[];
|
||||||
|
HDR: string[];
|
||||||
|
Tiles: string[];
|
||||||
|
};
|
||||||
|
export declare const NEED_CONVERT_BIM_MODEL: string[];
|
||||||
|
export declare const DRAWING_SUPPORT_TYPE: string[];
|
||||||
|
export declare const NEED_CONVERT_DRAWING: string[];
|
||||||
|
export declare const DOC_SUPPORT_TYPE: string[];
|
||||||
|
export declare const SCENE_TYPE: {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}[];
|
||||||
|
export declare const DefaultScreenshot = "/static/images/placeholder/\u622A\u5C4F\u5360\u4F4D\u56FE.png";
|
||||||
1
packages/editor/types/utils/common/dateTime.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export declare function dateTimeFormat(fmt: any): any;
|
||||||
13
packages/editor/types/utils/common/file.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* 通过Fetch API下载文件/图片(支持跨域/Blob处理)
|
||||||
|
* @param {string} url 文件地址
|
||||||
|
* @param {string} filename 保存的文件名
|
||||||
|
*/
|
||||||
|
export declare function downloadWithFetch(url: string, filename?: string): Promise<void>;
|
||||||
|
/**
|
||||||
|
* 文件大小 字节转换单位
|
||||||
|
* @param {number} size
|
||||||
|
* @returns {string|*}
|
||||||
|
*/
|
||||||
|
export declare const filterSize: (size: number) => string | any;
|
||||||
|
export declare const getServiceStaticFile: (url: string) => string;
|
||||||
1
packages/editor/types/utils/common/init.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
import "../signals/signalRegister";
|
||||||
9
packages/editor/types/utils/common/render.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import type { Component } from 'vue';
|
||||||
|
/**
|
||||||
|
* 渲染icon
|
||||||
|
* @param icon vicons
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export declare function renderIcon(icon: Component): () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
||||||
|
[key: string]: any;
|
||||||
|
}>;
|
||||||
25
packages/editor/types/utils/common/scenes.d.ts
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import * as THREE from 'three';
|
||||||
|
import { OrbitControls } from 'three/examples/jsm/Addons';
|
||||||
|
export interface IModel extends THREE.Object3D {
|
||||||
|
metadata: Object;
|
||||||
|
}
|
||||||
|
export declare function getMaterialName(material: any): any;
|
||||||
|
export declare function getObjectType(object: any): "Camera" | "Line" | "Scene" | "Light" | "Mesh" | "Points" | "Object3D";
|
||||||
|
/**
|
||||||
|
* 获取当前选中模型 path
|
||||||
|
*/
|
||||||
|
export declare function getSelectedModelPath(): string;
|
||||||
|
export declare function screenToWorld(x: number, y: number): THREE.Vector3;
|
||||||
|
export declare function reBufferGeometryUv(geometry: THREE.BufferGeometry): void;
|
||||||
|
export declare function setUserData(object: IModel, key: string, value: any): void;
|
||||||
|
export declare function setMetaData(object: IModel, key: string, value: any): void;
|
||||||
|
/**
|
||||||
|
* 创建基础场景(会用于轻量展示)
|
||||||
|
*/
|
||||||
|
export declare function createBasicScene(container: HTMLElement): {
|
||||||
|
scene: THREE.Scene;
|
||||||
|
camera: THREE.PerspectiveCamera;
|
||||||
|
renderer: THREE.WebGLRenderer;
|
||||||
|
controls: OrbitControls;
|
||||||
|
dispose: () => void;
|
||||||
|
};
|
||||||
41
packages/editor/types/utils/common/utils.d.ts
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import type { TreeOption } from 'naive-ui';
|
||||||
|
export declare function escapeHTML(html: string): string;
|
||||||
|
/**
|
||||||
|
* naive UI树结构寻找对应节点位置及所处父节点
|
||||||
|
* @param node 目标节点
|
||||||
|
* @param nodes 树数据
|
||||||
|
*/
|
||||||
|
export declare function findSiblingsAndIndex(node: TreeOption, nodes?: TreeOption[]): [TreeOption[], number] | [null, null];
|
||||||
|
export declare function base64ToFile(dataurl: any, filename: any): File;
|
||||||
|
/**
|
||||||
|
* 求次幂
|
||||||
|
*/
|
||||||
|
export declare function pow1024(num: any): number;
|
||||||
|
/**
|
||||||
|
* 动态添加script
|
||||||
|
* @param src
|
||||||
|
* @param async
|
||||||
|
*/
|
||||||
|
export declare function loadScript(src: string, async?: boolean): Promise<unknown>;
|
||||||
|
/**
|
||||||
|
* 复制到剪切板
|
||||||
|
* @param text
|
||||||
|
*/
|
||||||
|
export declare function copyToClipboard(text: string): Promise<unknown>;
|
||||||
|
/**
|
||||||
|
* 获取rem的px值
|
||||||
|
*/
|
||||||
|
export declare function remToPxNumber(rem: number): number;
|
||||||
|
/**
|
||||||
|
* 动态注入脚本,并监听执行完毕事件
|
||||||
|
* @param {string} src
|
||||||
|
*/
|
||||||
|
export declare function injectJS(src: any): Promise<unknown>;
|
||||||
|
/**
|
||||||
|
* 在树形结构中查找指定 key 的节点(深度优先搜索)
|
||||||
|
*/
|
||||||
|
export declare function findTreeNode(tree: TreeOption[], targetKey: string | number): TreeOption | null;
|
||||||
|
/**
|
||||||
|
* 使用深度优先遍历(DFS)递归算法,为没有子节点的节点添加 isLeaf: true 属性
|
||||||
|
*/
|
||||||
|
export declare function markLeafNodes(tree: TreeOption[]): void;
|
||||||
2
packages/editor/types/utils/common/verify.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export declare function formItemIsFile(_: any, value: File | null): Promise<void>;
|
||||||
|
export declare function formItemNotNil(_: any, value: any): Promise<void>;
|
||||||
78
packages/editor/types/utils/drawing/drawRect.d.ts
vendored
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* 画布中绘制矩形
|
||||||
|
* @param {HTMLCanvasElement} canvas 画布对象
|
||||||
|
* @param {Array<IDrawingMark>} list 矩形数组
|
||||||
|
**/
|
||||||
|
export declare class DrawRect {
|
||||||
|
private canvas;
|
||||||
|
private parentElement;
|
||||||
|
private ctx;
|
||||||
|
private readonly list;
|
||||||
|
selectRectIndex: number;
|
||||||
|
private hoverRectIndex;
|
||||||
|
private sX;
|
||||||
|
private sY;
|
||||||
|
private downClientX;
|
||||||
|
private downClientY;
|
||||||
|
private zoom;
|
||||||
|
private leftMouseDown;
|
||||||
|
private isCanvasDrag;
|
||||||
|
private canvasOffsetX;
|
||||||
|
private canvasOffsetY;
|
||||||
|
private isDrag;
|
||||||
|
private isDraged;
|
||||||
|
rectColor: string;
|
||||||
|
rectSelectColor: string;
|
||||||
|
private dragRect;
|
||||||
|
constructor(canvas: HTMLCanvasElement, parentElement: HTMLDivElement);
|
||||||
|
init(): void;
|
||||||
|
/**
|
||||||
|
* 准备开始画矩形标记框
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
addRect(): void;
|
||||||
|
exitRect(): void;
|
||||||
|
/**
|
||||||
|
* 删除rect
|
||||||
|
*/
|
||||||
|
deleteRect(): void;
|
||||||
|
/**
|
||||||
|
* 图纸复位
|
||||||
|
*/
|
||||||
|
canvasReset(): void;
|
||||||
|
/**
|
||||||
|
* 修改当前绘制的颜色
|
||||||
|
* @param {string} color 颜色
|
||||||
|
*/
|
||||||
|
setRectColor(color: string): void;
|
||||||
|
get selectRectColor(): string | undefined;
|
||||||
|
/**
|
||||||
|
* 高亮选中的模型对应的rect
|
||||||
|
* @param {string} uuid modelUuid
|
||||||
|
*/
|
||||||
|
selectRect(uuid: string): void;
|
||||||
|
private onmousemove;
|
||||||
|
/**
|
||||||
|
* 鼠标按下时
|
||||||
|
* @param ed
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private onmousedown;
|
||||||
|
private handleMouseDown;
|
||||||
|
/**
|
||||||
|
* 鼠标抬起时
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private onmouseup;
|
||||||
|
private onmouseleave;
|
||||||
|
private onmousewheel;
|
||||||
|
/**
|
||||||
|
* 重新绘制画布
|
||||||
|
*/
|
||||||
|
reDrawCanvas(showSelectLineColor?: boolean): void;
|
||||||
|
private onParentMouseDown;
|
||||||
|
private onParentMouseUp;
|
||||||
|
private onParentMouseMove;
|
||||||
|
private onParentMouseLeave;
|
||||||
|
dispose(): void;
|
||||||
|
}
|
||||||
44
packages/editor/types/utils/preview/menuOperation.d.ts
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import * as THREE from 'three';
|
||||||
|
import { ClippedEdgesBox, Measure, ModelExplode, Roaming, MiniMap } from "@astral3d/engine";
|
||||||
|
export declare class MenuOperation {
|
||||||
|
static InitControlsState: string;
|
||||||
|
static lastRoadCameraPos: THREE.Vector3;
|
||||||
|
static lastRoadCameraTarget: THREE.Vector3;
|
||||||
|
static explodeLayer: number;
|
||||||
|
static explodeModel: THREE.Object3D | null;
|
||||||
|
static _clippedEdgesBox: ClippedEdgesBox | null;
|
||||||
|
static _measure: Measure | null;
|
||||||
|
static _explode: ModelExplode | null;
|
||||||
|
static _roaming: Roaming | null;
|
||||||
|
static _miniMap: MiniMap | null;
|
||||||
|
static Init(key: string): void;
|
||||||
|
static get ClippedEdgesBox(): ClippedEdgesBox;
|
||||||
|
static get Measure(): Measure;
|
||||||
|
static get Explode(): ModelExplode;
|
||||||
|
static get Roaming(): Roaming;
|
||||||
|
static get MiniMap(): MiniMap;
|
||||||
|
/**
|
||||||
|
* 还原视角
|
||||||
|
*/
|
||||||
|
static toHome(): void;
|
||||||
|
/**
|
||||||
|
* 自动旋转
|
||||||
|
*/
|
||||||
|
static autoRotate(): void;
|
||||||
|
/**
|
||||||
|
* 剖切
|
||||||
|
*/
|
||||||
|
static cutting(): void;
|
||||||
|
static distance(): void;
|
||||||
|
static angle(): void;
|
||||||
|
static area(): void;
|
||||||
|
static clearMeasure(): void;
|
||||||
|
static explode(): void;
|
||||||
|
static roaming(): void;
|
||||||
|
static enterRoaming(): void;
|
||||||
|
static leaveRoaming(): void;
|
||||||
|
static miniMap(): void;
|
||||||
|
static settings(): void;
|
||||||
|
static fullscreen(): void;
|
||||||
|
static exitFullscreen(): void;
|
||||||
|
}
|
||||||
22
packages/editor/types/utils/service/error.d.ts
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import type { AxiosError, AxiosResponse } from 'axios';
|
||||||
|
import { Service } from "../../../types/network";
|
||||||
|
/**
|
||||||
|
* 策略模式
|
||||||
|
* @param actions 每一种可能执行的操作
|
||||||
|
*/
|
||||||
|
export declare function exeStrategyActions(actions: Common.StrategyAction[]): void;
|
||||||
|
/**
|
||||||
|
* 处理axios请求失败的错误
|
||||||
|
* @param axiosError - 错误
|
||||||
|
*/
|
||||||
|
export declare function handleAxiosError(axiosError: AxiosError): Service.RequestError;
|
||||||
|
/**
|
||||||
|
* 处理请求成功后的错误
|
||||||
|
* @param response - 请求的响应
|
||||||
|
*/
|
||||||
|
export declare function handleResponseError(response: AxiosResponse): Service.RequestError;
|
||||||
|
/**
|
||||||
|
* 处理后端返回的错误(业务错误)
|
||||||
|
* @param backendResult - 后端接口的响应数据
|
||||||
|
*/
|
||||||
|
export declare function handleBackendError(backendResult: Record<string, any>, config: Service.BackendResultConfig): Service.RequestError;
|
||||||
8
packages/editor/types/utils/service/fetchController.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* 创建一个handlerFetch
|
||||||
|
*
|
||||||
|
* @param limit 并发控制
|
||||||
|
* @param timeout 超时设
|
||||||
|
* @return function 返回一个函数
|
||||||
|
*/
|
||||||
|
export declare function fetchController(limit: number, timeout: number | boolean): (url: string, options?: any) => void;
|
||||||
5
packages/editor/types/utils/service/handler.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { Service } from "~/network";
|
||||||
|
/** 统一失败和成功的请求结果的数据类型 */
|
||||||
|
export declare function handleServiceResult(error: Service.RequestError | null, data: any, other?: any): Promise<any>;
|
||||||
|
/** 请求结果的适配器:用于接收适配器函数和请求结果 */
|
||||||
|
export declare function adapter<T extends Service.ServiceAdapter>(adapterFun: T, ...args: Service.MultiRequestResult<any>): Service.RequestResult<ReturnType<T>>;
|
||||||
2
packages/editor/types/utils/service/index.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './error';
|
||||||
|
export * from './handler';
|
||||||
5
packages/editor/types/utils/service/msg.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/**
|
||||||
|
* 显示错误信息
|
||||||
|
* @param error
|
||||||
|
*/
|
||||||
|
export declare function showErrorMsg(error: Service.RequestError): void;
|
||||||
1
packages/editor/types/utils/signals/signalRegister.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export {};
|
||||||
12
packages/editor/types/utils/storage/config.d.ts
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import type { Storage } from "@astral3d/engine";
|
||||||
|
export default class Config {
|
||||||
|
private static storage;
|
||||||
|
static config: {
|
||||||
|
[s: string]: any;
|
||||||
|
};
|
||||||
|
static initialize(storage: Storage): void;
|
||||||
|
static getKey(key: string): any;
|
||||||
|
static setKey(...args: any[]): void;
|
||||||
|
static clear(): void;
|
||||||
|
}
|
||||||
|
export declare const initializeConfig: (storage: any) => void;
|
||||||
4
packages/editor/types/utils/wasm/inject.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import "@/utils/wasm/wasm_exec.js";
|
||||||
|
export declare function injectWasm(opts: {
|
||||||
|
wasmUrl: string;
|
||||||
|
}): Promise<any>;
|
||||||
1
packages/editor/types/utils/wasm/optimize.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export declare function clearBuffer(): void;
|
||||||
0
packages/editor/types/utils/wasm/wasm_exec.d.ts
vendored
Normal file
6
packages/editor/types/views/editor/layouts/index.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import Header from "./Header.vue";
|
||||||
|
import Footer from "./Footer.vue";
|
||||||
|
import Scene from "./Scene.vue";
|
||||||
|
import Sidebar from "./Sidebar.vue";
|
||||||
|
import Assets from "./Assets.vue";
|
||||||
|
export { Header, Footer, Scene, Sidebar, Assets };
|
||||||
76
packages/sdk/lib/core/built-in/assets/Water.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import * as THREE from 'three';
|
||||||
|
import { WaterPool } from '@/core/objects';
|
||||||
|
import App from "@/core/app/App";
|
||||||
|
import { AddObjectCommand } from '@/core/commands/AddObjectCommand';
|
||||||
|
|
||||||
|
export class Water {
|
||||||
|
static DefaultTilesUrl = new URL(import.meta.env.BASE_URL + 'resource/textures/tiles.jpg', import.meta.url).href;
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
static loadFile(url: string) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const loader = new THREE.FileLoader();
|
||||||
|
loader.load(url, resolve, undefined, reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 水池/也可以做水面
|
||||||
|
*/
|
||||||
|
static waterPool(options?: IWaterPool.Options, position: number[] = [0, 0, 0]) {
|
||||||
|
return new Promise<WaterPool>((resolve, reject) => {
|
||||||
|
const _options: IWaterPool.Options = Object.assign({
|
||||||
|
name: "WaterPool",
|
||||||
|
type: 'cylinder',
|
||||||
|
light: [0.7, 1, -0.3],
|
||||||
|
diameter: 3.2,
|
||||||
|
height: 5,
|
||||||
|
// 仅显示水体体积,不显示池壁纹理
|
||||||
|
wallMode: 'volume',
|
||||||
|
wallOpacity: 0.3,
|
||||||
|
// 使用与原始着色接近的水体颜色(偏亮蓝绿)
|
||||||
|
volumeColor: new THREE.Color(0.4, 0.9, 1.0),
|
||||||
|
// 开启屏幕空间折射,保证能看到水下物体
|
||||||
|
useSceneRefraction: 1,
|
||||||
|
// 水面透视强度
|
||||||
|
surfaceTransmittance: 0.9,
|
||||||
|
// 降低波纹边缘对比度
|
||||||
|
normalStrength: 0.6,
|
||||||
|
refractionStrength: 0.035,
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
if (!_options.tiles && (_options.wallMode ? _options.wallMode === 'wall' : _options.wall === true)) {
|
||||||
|
_options.tiles = new THREE.TextureLoader().load(Water.DefaultTilesUrl);
|
||||||
|
_options.tiles.wrapS = THREE.RepeatWrapping;
|
||||||
|
_options.tiles.wrapT = THREE.RepeatWrapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cylinderPool = new WaterPool(_options);
|
||||||
|
|
||||||
|
Promise.all([cylinderPool.loaded]).then(() => {
|
||||||
|
cylinderPool.setPosition(...position);
|
||||||
|
WaterPool.register(cylinderPool);
|
||||||
|
|
||||||
|
const viewer = App.viewer;
|
||||||
|
if (viewer && !cylinderPool.parent) {
|
||||||
|
App.execute(new AddObjectCommand(cylinderPool), `Add WaterPool: ${_options.name}`);
|
||||||
|
}
|
||||||
|
if (viewer) {
|
||||||
|
Water.registerRendering();
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(cylinderPool);
|
||||||
|
}).catch((err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册渲染行为
|
||||||
|
*/
|
||||||
|
static registerRendering() {
|
||||||
|
WaterPool.registerRendering();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
//JSDoc related imports
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
import MeshUIBaseElement from './../../core/elements/MeshUIBaseElement';
|
||||||
|
/* eslint-enable no-unused-vars */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Job:
|
||||||
|
* - recording components required updates
|
||||||
|
* - trigger those updates when 'update' is called
|
||||||
|
*
|
||||||
|
* This module is a bit special. It is, with FontLibrary, one of the only modules in the 'component'
|
||||||
|
* directory not to be used in component composition (Object.assign).
|
||||||
|
*
|
||||||
|
* When MeshUIComponent is instanciated, it calls UpdateManager.register().
|
||||||
|
*
|
||||||
|
* Then when MeshUIComponent receives new attributes, it doesn't update the component right away.
|
||||||
|
* Instead, it calls UpdateManager.requestUpdate(), so that the component is updated when the user
|
||||||
|
* decides it (usually in the render loop).
|
||||||
|
*
|
||||||
|
* This is best for performance, because when a UI is created, thousands of componants can
|
||||||
|
* potentially be instantiated. If they called updates function on their ancestors right away,
|
||||||
|
* a given component could be updated thousands of times in one frame, which is very ineficient.
|
||||||
|
*
|
||||||
|
* Instead, redundant update request are moot, the component will update once when the use calls
|
||||||
|
* update() in their render loop.
|
||||||
|
*/
|
||||||
|
export default class UpdateManager {
|
||||||
|
|
||||||
|
|
||||||
|
static register( component ) {
|
||||||
|
|
||||||
|
if ( !this.elements.includes( component ) ) {
|
||||||
|
|
||||||
|
this.elements.push( component );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static remove( component ) {
|
||||||
|
|
||||||
|
const index = this.elements.indexOf( component );
|
||||||
|
if ( index !== -1 ) {
|
||||||
|
|
||||||
|
this.elements.splice( index, 1 );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static update() {
|
||||||
|
|
||||||
|
for ( const UIElement of this.elements ) {
|
||||||
|
UIElement.update();
|
||||||
|
|
||||||
|
UIElement.process(); // Natural process
|
||||||
|
UIElement.process(); // Actual process (optional) - For auto size and stretch
|
||||||
|
|
||||||
|
UIElement.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @type {Array.<MeshUIBaseElement>}
|
||||||
|
*/
|
||||||
|
UpdateManager.elements = [];
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
/** List the default values of the lib components */
|
||||||
|
const _values = {
|
||||||
|
fog: false,
|
||||||
|
fontFamily: null,
|
||||||
|
fontSize: 0.05,
|
||||||
|
fontKerning: 'auto',
|
||||||
|
fontStyle: 'normal',
|
||||||
|
fontWeight : 'normal',
|
||||||
|
offset: 0.005,
|
||||||
|
lineHeight: 1.2,
|
||||||
|
lineBreak: '- ,.:?!\n',// added '\n' to also acts as friendly breaks when white-space:normal
|
||||||
|
whiteSpace: 'pre-line',
|
||||||
|
flexDirection : 'column',
|
||||||
|
justifyContent : 'start',
|
||||||
|
alignItems : 'start',
|
||||||
|
backgroundImage: null,
|
||||||
|
textAlign : 'left',
|
||||||
|
boxSizing: 'content-box',
|
||||||
|
position: 'static',
|
||||||
|
color: 0xffffff,
|
||||||
|
fontColor: 0xffffff,
|
||||||
|
fontOpacity: 1,
|
||||||
|
opacity: 1,
|
||||||
|
fontPXRange: 4,
|
||||||
|
fontSupersampling: true,
|
||||||
|
fontSmooth: 'antialiased',
|
||||||
|
borderRadius: 0,
|
||||||
|
borderWidth: 0,
|
||||||
|
borderColor: 'black',
|
||||||
|
borderOpacity: 1,
|
||||||
|
backgroundSize: "cover",
|
||||||
|
backgroundColor: 0x000000,
|
||||||
|
backgroundOpacity: 0,
|
||||||
|
overflow: 'visible',
|
||||||
|
letterSpacing: 0,
|
||||||
|
invertAlpha : false,
|
||||||
|
segments: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('./../core/elements/MeshUIBaseElement').Options} overrideProperties
|
||||||
|
*/
|
||||||
|
export const set = function ( overrideProperties ) {
|
||||||
|
|
||||||
|
for ( const property in overrideProperties ) {
|
||||||
|
|
||||||
|
_values[property] = overrideProperties[property];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} property
|
||||||
|
* @return {any}
|
||||||
|
*/
|
||||||
|
export const get = function ( property ) {
|
||||||
|
|
||||||
|
if( !Object.prototype.hasOwnProperty.call( _values, property) ) {
|
||||||
|
|
||||||
|
console.warn( `ThreeMeshUI::DefaultValues is trying to retrieve non-existing property '${property}'`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return _values[property];
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,262 @@
|
|||||||
|
/**
|
||||||
|
* This is the abstract/base class / interface of any inline
|
||||||
|
* Inline can be positioned according to text rules
|
||||||
|
*/
|
||||||
|
export default class Inline {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
|
||||||
|
/** @protected */ this._offsetX = 0;
|
||||||
|
/** @protected */ this._offsetY = 0;
|
||||||
|
|
||||||
|
/** @protected */ this._lineBreak = null;
|
||||||
|
|
||||||
|
/** @protected */ this._kerning = 0;
|
||||||
|
|
||||||
|
/** @protected */ this._fontFactor = 1;
|
||||||
|
/** @protected */ this._fontSize = 0;
|
||||||
|
|
||||||
|
/** @protected */ this._cumulativeWidth = 0;
|
||||||
|
|
||||||
|
/** @protected */ this._paddingLeft = 0;
|
||||||
|
/** @protected */ this._paddingRight = 0;
|
||||||
|
|
||||||
|
/** @protected */ this._marginLeft = 0;
|
||||||
|
/** @protected */ this._marginRight = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
resetOffsets() {
|
||||||
|
|
||||||
|
this._offsetX = this._offsetY = 0;
|
||||||
|
this._cumulativeWidth = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The horizontal distance this inline fills
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get xadvance() { return 0 }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The offset x of this inline in a line
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get xoffset() { return 0 }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The offset y of this inline in a line
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get yoffset() { return 0 }
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get width() { return 0 }
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get height() { return 0 }
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string|null} value
|
||||||
|
*/
|
||||||
|
set lineBreak( value ) {
|
||||||
|
|
||||||
|
this._lineBreak = value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {string|null}
|
||||||
|
*/
|
||||||
|
get lineBreak() { return this._lineBreak; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get anchor() { return 0 }
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get kerning() { return this._kerning * this._fontFactor; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} value
|
||||||
|
*/
|
||||||
|
set kerning( value ) {
|
||||||
|
|
||||||
|
this._kerning = value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get fontSize() { return this._fontSize }
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} value
|
||||||
|
*/
|
||||||
|
set fontSize( value ) {
|
||||||
|
|
||||||
|
this._fontSize = value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get lineHeight() { return 0 }
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get offsetX() { return this._offsetX; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
set offsetX( value ){
|
||||||
|
|
||||||
|
this._offsetX = value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get offsetY() { return this._offsetY; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} value
|
||||||
|
*/
|
||||||
|
set offsetY( value ){
|
||||||
|
|
||||||
|
this._offsetY = value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
get cumulativeWidth() { return this._cumulativeWidth; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} value
|
||||||
|
*/
|
||||||
|
set cumulativeWidth( value ) {
|
||||||
|
|
||||||
|
this._cumulativeWidth = value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
get marginLeft() { return this._marginLeft; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} value
|
||||||
|
*/
|
||||||
|
set marginLeft( value ) {
|
||||||
|
|
||||||
|
this._marginLeft = value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
get marginRight() { return this._marginRight; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} value
|
||||||
|
*/
|
||||||
|
set marginRight( value ) {
|
||||||
|
|
||||||
|
this._marginRight = value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
get paddingLeft() { return this._paddingLeft; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} value
|
||||||
|
*/
|
||||||
|
set paddingLeft( value ) {
|
||||||
|
|
||||||
|
this._paddingLeft = value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
get paddingRight() { return this._paddingRight; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} value
|
||||||
|
*/
|
||||||
|
set paddingRight( value ) {
|
||||||
|
|
||||||
|
this._paddingRight = value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get lineBase() { return 0 }
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} value
|
||||||
|
*/
|
||||||
|
set fontFactor( value ){
|
||||||
|
|
||||||
|
this._fontFactor = value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get fontFactor() { return this._fontFactor }
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
//JSDoc related imports
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
import Inline from './Inline';
|
||||||
|
/* eslint-enable no-unused-vars */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Line represents an horizontal combination of positioned inlines with additional properties
|
||||||
|
*/
|
||||||
|
export default class Line extends Array {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Inline[]} items
|
||||||
|
*/
|
||||||
|
constructor(...items) {
|
||||||
|
super(...items);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The width of this line
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.width = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum lineBase of this line of inlines
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.lineBase = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum lineHeight of this line of inlines
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.lineHeight = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The vertical position of this line
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.y = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||