bc-model-viewer 1.7.17 → 1.7.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,74 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>标注工具演示 - Bc-model-viewer</title>
8
+ <link rel="stylesheet" href="common-styles.css">
9
+ </head>
10
+
11
+ <body>
12
+ <div id="container"></div>
13
+ <a href="index.html" class="back-button">返回示例列表</a>
14
+ <div class="demo">
15
+ <h2>📝 标注工具演示</h2>
16
+ <p>点击模型上的点开始标注,再次点击确定文本位置,输入标注文字后按回车完成。支持删除功能。</p>
17
+ <div class="control-group">
18
+ <button onclick="tool.activate()">激活工具</button>
19
+ <button onclick="tool.deactivate()">停用工具</button>
20
+ <button onclick="tool.toggle()">切换状态</button>
21
+ </div>
22
+ <div class="control-group">
23
+ <button onclick="tool.removeLast()" class="secondary">清除上一次</button>
24
+ <button onclick="tool.clear()" class="danger">清除所有</button>
25
+ </div>
26
+ <div class="control-group">
27
+ <button onclick="console.log(tool.getAnnotations())">打印数据</button>
28
+ </div>
29
+ <div class="control-group">
30
+ <p style="font-size: 12px; color: #888; margin-top: 10px;">
31
+ 💡 使用提示:<br>
32
+ • 第一次点击:在模型上选择锚点<br>
33
+ • 第二次点击:确定文本位置(会显示输入框)<br>
34
+ • 输入文字后按回车完成标注<br>
35
+ • 按 ESC 键取消当前标注<br>
36
+ • 点击标签可选中标注,点击 × 可删除<br>
37
+ • 激活时相机控制会被禁用<br>
38
+ • 按 Delete 键删除选中的标注
39
+ </p>
40
+ </div>
41
+ </div>
42
+
43
+ <script type="module">
44
+
45
+ import { Viewer, AnnotationTool } from '../../index.ts'
46
+
47
+
48
+ const container = document.getElementById('container')
49
+
50
+ // 实例化 Viewer 对象
51
+ const viewer = new Viewer(container, {
52
+ load: {
53
+ cache: {
54
+ enabled: false
55
+ }
56
+ }
57
+ })
58
+
59
+ // 加载测试模型
60
+ await viewer.loadZipAsync('../../assets/dgz/建筑.dgz')
61
+ // await viewer.loadZipAsync('../../assets/new.dgz')
62
+
63
+ const tool = new AnnotationTool(viewer)
64
+
65
+ tool.activate()
66
+
67
+ window.tool = tool
68
+ window.viewer = viewer
69
+ </script>
70
+
71
+ </body>
72
+
73
+ </html>
74
+
@@ -60,6 +60,15 @@
60
60
 
61
61
  import { Viewer } from '../../index.ts'
62
62
 
63
+ // 尝试导入样式配置,如果失败则使用空对象
64
+ let styles = null;
65
+ try {
66
+ const styleModule = await import('../../src/options/style.ts');
67
+ styles = styleModule.default;
68
+ } catch (error) {
69
+ console.warn('无法导入样式配置,将从 viewer 中获取:', error);
70
+ }
71
+
63
72
  const container = document.getElementById('container')
64
73
 
65
74
  // 实例化 Viewer 对象
@@ -78,11 +87,73 @@
78
87
  // 获取样式管理器实例
79
88
  const { styleManager } = viewer;
80
89
 
90
+ /**
91
+ * 更新页面上的参数输入框
92
+ * @param {Object} styleConfig 样式配置对象
93
+ */
94
+ function updateInputsFromStyle(styleConfig) {
95
+ if (!styleConfig) return;
96
+
97
+ const { background, edge } = styleConfig;
98
+
99
+ // 更新背景颜色输入框
100
+ if (background) {
101
+ // 更新纯色背景
102
+ const solidColorInput = document.getElementById('solidColor');
103
+ if (solidColorInput && background.color) {
104
+ solidColorInput.value = background.color;
105
+ }
106
+
107
+ // 更新渐变背景
108
+ const topColorInput = document.getElementById('topColor');
109
+ const middleColorInput = document.getElementById('middleColor');
110
+ const bottomColorInput = document.getElementById('bottomColor');
111
+
112
+ if (topColorInput && background.color) {
113
+ topColorInput.value = background.color;
114
+ }
115
+ if (middleColorInput && background.middleColor) {
116
+ middleColorInput.value = background.middleColor;
117
+ } else if (middleColorInput && background.color) {
118
+ // 如果没有中间色,使用基础色
119
+ middleColorInput.value = background.color;
120
+ }
121
+ if (bottomColorInput && background.gradientColor) {
122
+ bottomColorInput.value = background.gradientColor;
123
+ } else if (bottomColorInput && background.color) {
124
+ // 如果没有渐变色,使用基础色
125
+ bottomColorInput.value = background.color;
126
+ }
127
+ }
128
+
129
+ // 更新边缘颜色输入框
130
+ if (edge && edge.color) {
131
+ const edgeColorInput = document.getElementById('edgeColor');
132
+ if (edgeColorInput) {
133
+ edgeColorInput.value = edge.color;
134
+ }
135
+ }
136
+ }
137
+
81
138
  // 切换到预设样式
82
139
  function changeToPresetStyle(styleIndex) {
83
140
  console.log(`切换到预设样式 ${styleIndex}`);
141
+
142
+ // 优先使用导入的原始样式配置(更准确),如果不存在则使用回调中的 style
143
+ let styleConfig = null;
144
+ if (styles && styles[styleIndex]) {
145
+ styleConfig = styles[styleIndex];
146
+ }
147
+
148
+ // 切换样式
84
149
  styleManager.changeStyle(styleIndex, (style) => {
85
150
  console.log('样式应用成功:', style);
151
+
152
+ // 如果原始样式配置不存在,使用合并后的样式配置
153
+ const configToUse = styleConfig || style;
154
+
155
+ // 更新页面上的输入框
156
+ updateInputsFromStyle(configToUse);
86
157
  });
87
158
  }
88
159
 
@@ -125,23 +196,23 @@
125
196
  window.setSolidBackground = setSolidBackground;
126
197
  window.setGradientBackground = setGradientBackground;
127
198
  window.setEdgeColor = setEdgeColor;
128
-
199
+
129
200
  // 边线控制方法
130
201
  function toggleEdgeVisibility() {
131
202
  console.log('切换边线显隐状态');
132
203
  styleManager.toggleEdgeVisibility();
133
204
  }
134
-
205
+
135
206
  function showEdge() {
136
207
  console.log('显示边线');
137
208
  styleManager.showEdge();
138
209
  }
139
-
210
+
140
211
  function hideEdge() {
141
212
  console.log('隐藏边线');
142
213
  styleManager.hideEdge();
143
214
  }
144
-
215
+
145
216
  // 暴露边线控制方法到全局
146
217
  window.toggleEdgeVisibility = toggleEdgeVisibility;
147
218
  window.showEdge = showEdge;
@@ -14,10 +14,22 @@
14
14
  <div class="demo">
15
15
  <h2>💥 模型爆炸图演示</h2>
16
16
  <p>通过调整爆炸强度来分离和展示模型的不同部件,便于查看内部结构。</p>
17
+
18
+ <!-- 预设强度按钮 -->
19
+ <div class="control-group">
20
+ <label>预设强度:</label>
21
+ <button onclick="viewer.explosion.setPreset('small')">小爆炸 (25%)</button>
22
+ <button onclick="viewer.explosion.setPreset('medium')">中等 (50%)</button>
23
+ <button onclick="viewer.explosion.setPreset('large')">大爆炸 (75%)</button>
24
+ <button onclick="viewer.explosion.setPreset('max')">最大 (100%)</button>
25
+ </div>
26
+
27
+ <!-- 基础控制 -->
17
28
  <div class="control-group">
18
- <button onclick="viewer.explosion.applyExplosion(10)">爆炸</button>
19
29
  <button onclick="viewer.explosion.resetExplosion()" id="resetBtn" class="secondary">复位</button>
20
30
  </div>
31
+
32
+ <!-- 滑块控制 -->
21
33
  <div class="control-group">
22
34
  <label for="intensity">爆炸强度: <span id="intensityValue">0</span></label>
23
35
  <input type="range" id="intensity" step="0.1" min="0" value="0" title="爆炸强度">
@@ -37,21 +49,11 @@
37
49
  cache: {
38
50
  enabled: false
39
51
  }
40
- },
41
- style: {
42
- type: 1,
43
- axes: {
44
- // display: true,
45
- }
46
52
  }
47
53
  })
48
54
 
49
55
  // 加载测试模型
50
- // await viewer.loadZipAsync('../../assets/dgz/建筑.dgz')
51
- // await viewer.loadZipAsync('../../assets/dgz/机电综合.dgz')
52
56
  await viewer.loadZipAsync('../../assets/new.dgz')
53
- // await viewer.loadZipAsync('../../assets/new2.dgz')
54
- // await viewer.loadZipAsync('../../assets/new3.dgz')
55
57
 
56
58
  viewer.initExplosion()
57
59
 
@@ -66,10 +68,30 @@
66
68
  intensity.addEventListener('input', (e) => {
67
69
  const value = parseFloat(e.target.value);
68
70
  intensityValue.textContent = value.toFixed(1);
69
- viewer.setEdgeVisible(false)
70
71
  viewer.explosion.applyExplosion(value);
71
72
  });
72
73
 
74
+ // 更新滑块值(当使用预设时)
75
+ const updateSlider = () => {
76
+ const currentIntensity = viewer.explosion.getCurrentIntensity();
77
+ intensity.value = currentIntensity;
78
+ intensityValue.textContent = currentIntensity.toFixed(1);
79
+ };
80
+
81
+ // 重写预设方法以更新滑块
82
+ const originalSetPreset = viewer.explosion.setPreset.bind(viewer.explosion);
83
+ viewer.explosion.setPreset = function(presetName) {
84
+ originalSetPreset(presetName);
85
+ updateSlider();
86
+ };
87
+
88
+ // 重写复位方法以更新滑块
89
+ const originalReset = viewer.explosion.resetExplosion.bind(viewer.explosion);
90
+ viewer.explosion.resetExplosion = function() {
91
+ originalReset();
92
+ updateSlider();
93
+ };
94
+
73
95
  window.v = viewer
74
96
  window.viewer = viewer
75
97
  </script>
@@ -0,0 +1,76 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>直线工具演示 - Bc-model-viewer</title>
8
+ <link rel="stylesheet" href="common-styles.css">
9
+ </head>
10
+
11
+ <body>
12
+ <div id="container"></div>
13
+ <a href="index.html" class="back-button">返回示例列表</a>
14
+ <div class="demo">
15
+ <h2>📏 直线工具演示</h2>
16
+ <p>点击两点绘制直线。支持吸附到模型表面,按住 Shift 锁定轴(X/Y/Z),自动检测轴对齐并显示提示。默认连续绘制模式,点击标签可选中,点击删除按钮可删除。激活工具时会禁用相机控制,停用时恢复。</p>
17
+ <div class="control-group">
18
+ <button onclick="tool.activate()">激活工具</button>
19
+ <button onclick="tool.deactivate()">停用工具</button>
20
+ <button onclick="tool.toggle()">切换状态</button>
21
+ </div>
22
+ <div class="control-group">
23
+ <button onclick="tool.removeLast()" class="secondary">清除上一次</button>
24
+ <button onclick="tool.clear()" class="danger">清除所有</button>
25
+ </div>
26
+ <div class="control-group">
27
+ <button onclick="console.log(tool.getLines())">打印数据</button>
28
+ </div>
29
+ <div class="control-group">
30
+ <p style="font-size: 12px; color: #888; margin-top: 10px;">
31
+ 💡 使用提示:<br>
32
+ • 点击第一点设置起点<br>
33
+ • 点击第二点设置终点,完成直线绘制<br>
34
+ • 按住 Shift 键锁定到最近的轴(X/Y/Z)<br>
35
+ • 自动检测轴对齐并显示提示(平行 X/Y/Z 轴)<br>
36
+ • 自动吸附到模型表面顶点和边线<br>
37
+ • 默认连续绘制模式(完成一条后继续绘制)<br>
38
+ • 点击标签可选中直线,点击 × 可删除<br>
39
+ • 激活时相机控制会被禁用<br>
40
+ • 按 Delete 键删除选中的直线
41
+ </p>
42
+ </div>
43
+ </div>
44
+
45
+ <script type="module">
46
+
47
+ import { Viewer, LineTool } from '../../index.ts'
48
+
49
+
50
+ const container = document.getElementById('container')
51
+
52
+ // 实例化 Viewer 对象
53
+ const viewer = new Viewer(container, {
54
+ load: {
55
+ cache: {
56
+ enabled: false
57
+ }
58
+ }
59
+ })
60
+
61
+ // 加载测试模型
62
+ await viewer.loadZipAsync('../../assets/dgz/建筑.dgz')
63
+ // await viewer.loadZipAsync('../../assets/new.dgz')
64
+
65
+ const tool = new LineTool(viewer)
66
+
67
+ tool.activate()
68
+
69
+ window.tool = tool
70
+ window.viewer = viewer
71
+ </script>
72
+
73
+ </body>
74
+
75
+ </html>
76
+
@@ -6,6 +6,122 @@
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>模型剖切工具演示 - Bc-model-viewer</title>
8
8
  <link rel="stylesheet" href="common-styles.css">
9
+ <style>
10
+ .clipper-controls {
11
+ margin-top: 20px;
12
+ }
13
+
14
+ .plane-control {
15
+ margin-bottom: 15px;
16
+ padding: 12px;
17
+ background: rgba(255, 255, 255, 0.05);
18
+ border-radius: 6px;
19
+ border: 1px solid rgba(255, 255, 255, 0.1);
20
+ }
21
+
22
+ .plane-control-header {
23
+ display: flex;
24
+ justify-content: space-between;
25
+ align-items: center;
26
+ margin-bottom: 8px;
27
+ }
28
+
29
+ .plane-control-label {
30
+ font-weight: 500;
31
+ color: #fff;
32
+ font-size: 14px;
33
+ }
34
+
35
+ .plane-control-value {
36
+ color: #4CAF50;
37
+ font-size: 13px;
38
+ font-weight: 600;
39
+ min-width: 50px;
40
+ text-align: right;
41
+ }
42
+
43
+ .plane-control-slider {
44
+ width: 100%;
45
+ height: 6px;
46
+ border-radius: 3px;
47
+ background: rgba(255, 255, 255, 0.2);
48
+ outline: none;
49
+ -webkit-appearance: none;
50
+ appearance: none;
51
+ }
52
+
53
+ .plane-control-slider::-webkit-slider-thumb {
54
+ -webkit-appearance: none;
55
+ appearance: none;
56
+ width: 18px;
57
+ height: 18px;
58
+ border-radius: 50%;
59
+ background: #4CAF50;
60
+ cursor: pointer;
61
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
62
+ transition: background 0.2s;
63
+ }
64
+
65
+ .plane-control-slider::-webkit-slider-thumb:hover {
66
+ background: #45a049;
67
+ }
68
+
69
+ .plane-control-slider::-moz-range-thumb {
70
+ width: 18px;
71
+ height: 18px;
72
+ border-radius: 50%;
73
+ background: #4CAF50;
74
+ cursor: pointer;
75
+ border: none;
76
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
77
+ transition: background 0.2s;
78
+ }
79
+
80
+ .plane-control-slider::-moz-range-thumb:hover {
81
+ background: #45a049;
82
+ }
83
+
84
+ .plane-control-actions {
85
+ display: flex;
86
+ gap: 8px;
87
+ margin-top: 8px;
88
+ }
89
+
90
+ .plane-control-actions button {
91
+ flex: 1;
92
+ padding: 6px 12px;
93
+ font-size: 12px;
94
+ border: 1px solid rgba(255, 255, 255, 0.2);
95
+ background: rgba(255, 255, 255, 0.1);
96
+ color: #fff;
97
+ border-radius: 4px;
98
+ cursor: pointer;
99
+ transition: all 0.2s;
100
+ }
101
+
102
+ .plane-control-actions button:hover {
103
+ background: rgba(255, 255, 255, 0.2);
104
+ }
105
+
106
+ .plane-control-actions button.danger {
107
+ background: rgba(244, 67, 54, 0.2);
108
+ border-color: rgba(244, 67, 54, 0.4);
109
+ }
110
+
111
+ .plane-control-actions button.danger:hover {
112
+ background: rgba(244, 67, 54, 0.3);
113
+ }
114
+
115
+ .no-planes-message {
116
+ padding: 20px;
117
+ text-align: center;
118
+ color: rgba(255, 255, 255, 0.6);
119
+ font-size: 14px;
120
+ background: rgba(255, 255, 255, 0.05);
121
+ border-radius: 6px;
122
+ border: 1px dashed rgba(255, 255, 255, 0.2);
123
+ }
124
+ </style>
9
125
  </head>
10
126
 
11
127
  <body>
@@ -14,18 +130,26 @@
14
130
  <div class="demo">
15
131
  <h2>✂️ 模型剖切工具演示</h2>
16
132
  <p>对模型进行切片查看。支持多个剖切面,可以从不同角度查看模型内部结构。</p>
133
+
17
134
  <div class="control-group">
18
- <button onclick="viewer.clipper.activate()">激活工具</button>
19
- <button onclick="viewer.clipper.deactivate()">停用工具</button>
135
+ <button onclick="activateClipper()">激活工具</button>
136
+ <button onclick="deactivateClipper()">停用工具</button>
20
137
  </div>
138
+
21
139
  <div class="control-group">
22
- <button onclick="viewer.clipper.clear()" class="danger">移除所有剖切面</button>
140
+ <button onclick="clearAllPlanes()" class="danger">移除所有剖切面</button>
141
+ </div>
142
+
143
+ <div class="clipper-controls" id="clipperControls">
144
+ <div class="no-planes-message">
145
+ 点击模型表面创建剖切平面,然后使用拖动条调整剖切位置
146
+ </div>
23
147
  </div>
24
148
  </div>
25
149
 
26
150
  <script type="module">
27
151
 
28
- import { Viewer, HUDLabelTool, ModelClipperTool } from '../../index.ts'
152
+ import { Viewer, ModelClipperTool } from '../../index.ts'
29
153
 
30
154
  const container = document.getElementById('container')
31
155
 
@@ -51,9 +175,153 @@
51
175
  await viewer.loadZipAsync('./dgz/样板间.dgz')
52
176
  // await viewer.loadZipAsync('../../../assets/dgz/机电综合.dgz')
53
177
 
54
- viewer.clipper.activate()
178
+ // 更新剖切控制UI
179
+ function updateClipperControls() {
180
+ const controlsContainer = document.getElementById('clipperControls');
181
+ const clipper = viewer.clipper;
182
+
183
+ if (!clipper || clipper.clippingPlanes.length === 0) {
184
+ controlsContainer.innerHTML = `
185
+ <div class="no-planes-message">
186
+ 点击模型表面创建剖切平面,然后使用拖动条调整剖切位置
187
+ </div>
188
+ `;
189
+ return;
190
+ }
191
+
192
+ let html = '';
193
+ clipper.clippingPlanes.forEach((plane, index) => {
194
+ const position = clipper.getPlanePositionByIndex(index);
195
+ const percentage = Math.round(position * 100);
196
+
197
+ html += `
198
+ <div class="plane-control" data-index="${index}">
199
+ <div class="plane-control-header">
200
+ <span class="plane-control-label">剖切面 #${index + 1}</span>
201
+ <span class="plane-control-value">${percentage}%</span>
202
+ </div>
203
+ <input
204
+ type="range"
205
+ class="plane-control-slider"
206
+ min="0"
207
+ max="1"
208
+ step="0.01"
209
+ value="${position}"
210
+ data-index="${index}"
211
+ oninput="updatePlanePosition(${index}, this.value)"
212
+ />
213
+ <div class="plane-control-actions">
214
+ <button onclick="resetPlanePosition(${index})">重置</button>
215
+ <button onclick="removePlane(${index})" class="danger">删除</button>
216
+ </div>
217
+ </div>
218
+ `;
219
+ });
220
+
221
+ controlsContainer.innerHTML = html;
222
+ }
223
+
224
+ // 更新剖切平面位置
225
+ function updatePlanePosition(index, value) {
226
+ const clipper = viewer.clipper;
227
+ if (!clipper) return;
228
+
229
+ clipper.setPlanePositionByIndex(index, parseFloat(value));
230
+
231
+ // 更新显示的值
232
+ const control = document.querySelector(`.plane-control[data-index="${index}"]`);
233
+ if (control) {
234
+ const valueDisplay = control.querySelector('.plane-control-value');
235
+ if (valueDisplay) {
236
+ const percentage = Math.round(parseFloat(value) * 100);
237
+ valueDisplay.textContent = `${percentage}%`;
238
+ }
239
+ }
240
+ }
241
+
242
+ // 重置剖切平面位置到中间
243
+ function resetPlanePosition(index) {
244
+ updatePlanePosition(index, 0.5);
245
+ const slider = document.querySelector(`.plane-control-slider[data-index="${index}"]`);
246
+ if (slider) {
247
+ slider.value = 0.5;
248
+ }
249
+ }
250
+
251
+ // 删除剖切平面
252
+ function removePlane(index) {
253
+ const clipper = viewer.clipper;
254
+ if (!clipper) return;
255
+
256
+ clipper.removeByIndex(index);
257
+ updateClipperControls();
258
+ }
259
+
260
+ // 激活工具
261
+ function activateClipper() {
262
+ viewer.clipper.activate();
263
+ updateClipperControls();
264
+ }
265
+
266
+ // 停用工具
267
+ function deactivateClipper() {
268
+ viewer.clipper.deactivate();
269
+ }
270
+
271
+ // 清除所有剖切面
272
+ function clearAllPlanes() {
273
+ viewer.clipper.clear();
274
+ updateClipperControls();
275
+ }
276
+
277
+ // 监听鼠标抬起事件,当创建新平面时更新UI
278
+ let lastPlaneCount = 0;
279
+ function checkPlaneChanges() {
280
+ const clipper = viewer.clipper;
281
+ if (!clipper) return;
282
+
283
+ if (clipper.clippingPlanes.length !== lastPlaneCount) {
284
+ lastPlaneCount = clipper.clippingPlanes.length;
285
+ updateClipperControls();
286
+ }
287
+ }
288
+
289
+ // 使用 requestAnimationFrame 监听变化(比 setInterval 更高效)
290
+ function startMonitoring() {
291
+ function monitor() {
292
+ checkPlaneChanges();
293
+ requestAnimationFrame(monitor);
294
+ }
295
+ monitor();
296
+ }
297
+
298
+ // 监听工具事件
299
+ viewer.clipper.addEventListener('activate', () => {
300
+ lastPlaneCount = viewer.clipper.clippingPlanes.length;
301
+ updateClipperControls();
302
+ });
303
+
304
+ viewer.clipper.addEventListener('deactivate', () => {
305
+ // 停用时可以停止监听
306
+ });
307
+
308
+ // 初始激活工具
309
+ viewer.clipper.activate();
310
+
311
+ // 开始监听变化
312
+ startMonitoring();
313
+
314
+ // 暴露函数到全局
315
+ window.viewer = viewer;
316
+ window.updatePlanePosition = updatePlanePosition;
317
+ window.resetPlanePosition = resetPlanePosition;
318
+ window.removePlane = removePlane;
319
+ window.activateClipper = activateClipper;
320
+ window.deactivateClipper = deactivateClipper;
321
+ window.clearAllPlanes = clearAllPlanes;
55
322
 
56
- window.viewer = viewer
323
+ // 初始更新一次
324
+ updateClipperControls();
57
325
  </script>
58
326
 
59
327
  </body>