Skip to content

Commit 9c6784b

Browse files
committed
update UI
1 parent da14391 commit 9c6784b

8 files changed

Lines changed: 186 additions & 17 deletions

File tree

apps/viewer/src/App.vue

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@ const themeOverrides: GlobalThemeOverrides = {
2727
Checkbox: {
2828
fontSizeSmall: '12px',
2929
},
30+
Dropdown: {
31+
fontSizeMedium: '12px',
32+
fontSizeSmall: '12px',
33+
padding: '0px',
34+
optionHeightSmall: '20px',
35+
optionPrefixWidthSmall: '4px',
36+
optionSuffixWidthSmall: '4px',
37+
optionIconSizeSmall: '0px',
38+
optionIconSuffixWidthSmall: '0px',
39+
optionIconPrefixWidthSmall: '0px',
40+
41+
},
3042
}
3143
</script>
3244

apps/viewer/src/components/InfoPanel.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ interface InfoState {
3232
}
3333
3434
const store = useAppStore()
35-
const { viewer, openDrive } = storeToRefs(store)
35+
const { viewer, openDrive, fileInfo } = storeToRefs(store)
3636
const state = reactive<InfoState>({
3737
basic: null,
3838
coordinate: null,
@@ -87,7 +87,7 @@ watch(viewer, () => {
8787
<NIcon size="12">
8888
<InfoCircle />
8989
</NIcon>
90-
<span>Info</span>
90+
<span>{{ fileInfo?.name }}</span>
9191
</div>
9292

9393
<NDivider />

apps/viewer/src/components/OperatePanel.vue

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<script setup lang="ts">
22
import type { TreeOption, UploadFileInfo } from 'naive-ui'
33
import type { Level } from '@/libs/types'
4-
import { CaretRight } from '@vicons/fa'
5-
import { NButton, NCard, NCheckbox, NCollapse, NCollapseItem, NGrid, NGridItem, NIcon, NScrollbar, NSelect, NTree, NUpload, useMessage } from 'naive-ui'
4+
import { CaretRight, EllipsisH } from '@vicons/fa'
5+
import { NButton, NCard, NCheckbox, NCollapse, NCollapseItem, NDropdown, NGrid, NGridItem, NIcon, NScrollbar, NSelect, NTree, NUpload, useMessage } from 'naive-ui'
66
import OpenDrive from 'opendrive-parser'
77
import { storeToRefs } from 'pinia'
8-
import { computed, ref, watch } from 'vue'
8+
import { computed, h, ref, watch } from 'vue'
99
import { useAppStore } from '@/store'
1010
import AppTitle from './AppTitle.vue'
1111
@@ -25,6 +25,26 @@ const precisionOptions = [{
2525
value: 1,
2626
}]
2727
28+
const dropdownOptions = [
29+
{
30+
label: 'Zoom to',
31+
key: 'zoom-to',
32+
},
33+
{
34+
label: 'Toogle',
35+
key: 'toogle',
36+
},
37+
]
38+
39+
function handleSelect(key: string | number, option: TreeOption) {
40+
if (key === 'zoom-to') {
41+
viewer.value?.zoomTo(option.level as Level, option.key as string)
42+
}
43+
// if (key === 'toogle') {
44+
// viewer.value?.toggle(option.level as Level, option.key as string)
45+
// }
46+
}
47+
2848
watch(precision, () => {
2949
if (!viewer.value)
3050
return
@@ -46,6 +66,10 @@ async function loadDefaultMap() {
4666
const res = await fetch('/data/Town04_Opt.xodr')
4767
const content = await res.text()
4868
if (content && viewer.value) {
69+
store.setFileInfo({
70+
name: 'Town04_Opt.xodr',
71+
size: 0,
72+
})
4973
store.setOpenDriveContent(content)
5074
const openDrive = parseOpenDrive(content, precision.value)
5175
store.setOpenDrive(openDrive)
@@ -71,12 +95,17 @@ function handleChange({ file }: { file: UploadFileInfo }) {
7195
loading.value = true
7296
const t1 = performance.now()
7397
const reader = new FileReader()
98+
99+
store.clear()
100+
store.setFileInfo({
101+
name: file.name,
102+
size: file.file?.size || 0,
103+
})
74104
reader.onload = (event) => {
75105
if (!viewer.value)
76106
return
77107
const content = event.target?.result as string
78108
if (content) {
79-
store.clear()
80109
store.setOpenDriveContent(content)
81110
const openDrive = parseOpenDrive(content, precision.value)
82111
store.setOpenDrive(openDrive)
@@ -106,6 +135,34 @@ function nodeProps({ option }: { option: TreeOption }) {
106135
}
107136
}
108137
138+
function renderSuffix({ option }: { option: TreeOption }) {
139+
return h(
140+
NDropdown,
141+
{
142+
trigger: 'click',
143+
size: 'small',
144+
placement: 'left-start',
145+
options: dropdownOptions,
146+
onSelect: (key: string | number) => handleSelect(key, option),
147+
},
148+
{
149+
default: () => h(
150+
NButton,
151+
{
152+
text: true,
153+
style: 'margin-left: 4px; display: flex; align-items: center;',
154+
onClick: (e: MouseEvent) => {
155+
e.stopPropagation()
156+
},
157+
},
158+
{
159+
default: () => h(NIcon, { size: 12 }, { default: () => h(EllipsisH) }),
160+
},
161+
),
162+
},
163+
)
164+
}
165+
109166
const roadNetworkData = computed(() => {
110167
if (!openDrive.value)
111168
return []
@@ -192,7 +249,8 @@ function handlePerfMonitor(v: boolean) {
192249
<NScrollbar style="max-height: 320px;">
193250
<NTree
194251
block-line :data="roadNetworkData" :indent="14" :show-line="true"
195-
:node-props="nodeProps"
252+
expand-on-click :selectable="false"
253+
:node-props="nodeProps" :render-suffix="renderSuffix"
196254
/>
197255
</NScrollbar>
198256
</NCollapseItem>

apps/viewer/src/libs/converter.ts

Whitespace-only changes.

apps/viewer/src/libs/view-manager.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,17 @@ class ViewManager {
8989
}
9090

9191
fitToCamera() {
92+
const box = this.calculateBoundingBox()
93+
this.fitToBox(box)
94+
}
95+
96+
fitToBox(box: Box3) {
97+
if (box.isEmpty())
98+
return
99+
92100
const camera = this.viewer.camera
93101
const controls = this.viewer.controls
94102

95-
const box = this.calculateBoundingBox()
96103
const size = box.getSize(new Vector3())
97104
const center = box.getCenter(new Vector3())
98105
const offset = 1.2
@@ -101,13 +108,11 @@ class ViewManager {
101108
const fov = MathUtils.degToRad(camera.fov)
102109
const distance = (maxSize / 2) / Math.tan(fov / 2) * offset
103110

104-
const dir = new Vector3()
105-
.subVectors(camera.position, controls?.target ?? center)
106-
.normalize()
111+
const currentPos = camera.position.clone()
112+
const currentTarget = controls?.target.clone() ?? new Vector3()
113+
const direction = currentPos.sub(currentTarget).normalize()
107114

108-
camera.position.copy(
109-
dir.multiplyScalar(distance).add(center),
110-
)
115+
camera.position.copy(direction.multiplyScalar(distance).add(center))
111116

112117
camera.near = distance / 100
113118
camera.far = distance * 100
@@ -118,7 +123,8 @@ class ViewManager {
118123
controls.update()
119124
}
120125

121-
this.addHelper(size, center)
126+
// Update helpers if needed, but maybe not for specific zoom
127+
// this.addHelper(size, center)
122128
}
123129

124130
clear() {

apps/viewer/src/libs/viewer.ts

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Lane, OpenDrive, ReferenceLine, Road } from 'opendrive-parser'
22
import type { Material } from 'three'
3-
import { BufferAttribute, BufferGeometry, DoubleSide, Float32BufferAttribute, Group, LineBasicMaterial, LineSegments, Mesh, MeshBasicMaterial, NormalBlending, PerspectiveCamera, Raycaster, Scene, Vector2, Vector3, WebGLRenderer } from 'three'
3+
import type { Level } from './types'
4+
import { Box3, BufferAttribute, BufferGeometry, DoubleSide, Float32BufferAttribute, Group, LineBasicMaterial, LineSegments, Mesh, MeshBasicMaterial, NormalBlending, PerspectiveCamera, Raycaster, Scene, Vector2, Vector3, WebGLRenderer } from 'three'
45
import { acceleratedRaycast, computeBoundsTree, disposeBoundsTree } from 'three-mesh-bvh'
56
import { BufferGeometryUtils, OrbitControls } from 'three/examples/jsm/Addons.js'
67
import HighlightManager from './highlight-manager'
@@ -164,6 +165,83 @@ class Viewer {
164165
this.addRoads(openDrive.getRoads())
165166
}
166167

168+
public getLaneFromId(laneId: string): Lane | undefined {
169+
const parts = laneId.split('_')
170+
if (parts.length < 3)
171+
return undefined
172+
173+
const lId = parts.pop()!
174+
const sS = parts.pop()!
175+
const rId = parts.join('_')
176+
177+
const road = this.openDrive?.getRoadById(rId)
178+
if (!road)
179+
return undefined
180+
181+
const section = road.getLaneSectionByS(Number(sS))
182+
if (!section)
183+
return undefined
184+
185+
return section.getLaneById(lId)
186+
}
187+
188+
public getElementBox(level: Level, key: string): Box3 {
189+
const box = new Box3()
190+
if (!this.openDrive)
191+
return box
192+
193+
if (level === 'lane') {
194+
const lane = this.getLaneFromId(key)
195+
if (lane) {
196+
const geom = this.createLaneGeometry(lane)
197+
if (geom) {
198+
geom.computeBoundingBox()
199+
box.union(geom.boundingBox!)
200+
geom.dispose()
201+
}
202+
}
203+
}
204+
else if (level === 'section') {
205+
const parts = key.split('_')
206+
const sS = parts.pop()
207+
const rId = parts.join('_')
208+
const road = this.openDrive.getRoadById(rId)
209+
const section = road?.getLaneSectionByS(Number(sS))
210+
if (section) {
211+
for (const lane of section.getLanes()) {
212+
const geom = this.createLaneGeometry(lane)
213+
if (geom) {
214+
geom.computeBoundingBox()
215+
box.union(geom.boundingBox!)
216+
geom.dispose()
217+
}
218+
}
219+
}
220+
}
221+
else if (level === 'road') {
222+
const road = this.openDrive.getRoadById(key)
223+
if (road) {
224+
for (const section of road.getLaneSections()) {
225+
for (const lane of section.getLanes()) {
226+
const geom = this.createLaneGeometry(lane)
227+
if (geom) {
228+
geom.computeBoundingBox()
229+
box.union(geom.boundingBox!)
230+
geom.dispose()
231+
}
232+
}
233+
}
234+
}
235+
}
236+
237+
return box
238+
}
239+
240+
public zoomTo(level: Level, key: string) {
241+
const box = this.getElementBox(level, key)
242+
this.vm.fitToBox(box)
243+
}
244+
167245
addReferenceLines(referenceLines: ReferenceLine[]): void {
168246
const material = new LineBasicMaterial({
169247
color: 0xE68816, // #e68816ff

apps/viewer/src/store.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { defineStore } from 'pinia'
44
import { ref } from 'vue'
55

66
export const useAppStore = defineStore('app', () => {
7+
const fileInfo = ref<{ name: string, size: number } | null>(null)
78
const openDriveContent = ref<string | null>(null)
89
const openDrive = ref<OpenDrive | null>(null)
910
const viewer = ref<Viewer | null>(null)
@@ -13,6 +14,10 @@ export const useAppStore = defineStore('app', () => {
1314
viewer.value = v
1415
}
1516

17+
function setFileInfo(info: { name: string, size: number }) {
18+
fileInfo.value = info
19+
}
20+
1621
function setSelectedLane(l: Lane | null) {
1722
selectedLane.value = l
1823
}
@@ -30,7 +35,8 @@ export const useAppStore = defineStore('app', () => {
3035
selectedLane.value = null
3136
openDrive.value = null
3237
openDriveContent.value = null
38+
fileInfo.value = null
3339
}
3440

35-
return { openDriveContent, setOpenDriveContent, viewer, setViewer, selectedLane, setSelectedLane, openDrive, setOpenDrive, clear }
41+
return { fileInfo, setFileInfo, openDriveContent, setOpenDriveContent, viewer, setViewer, selectedLane, setSelectedLane, openDrive, setOpenDrive, clear }
3642
})

apps/viewer/src/style.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,13 @@ body {
2525

2626
.n-divider:not(.n-divider--vertical) {
2727
margin: 8px 0px;
28+
}
29+
30+
.n-dropdown-menu .n-dropdown-option .n-dropdown-option-body .n-dropdown-option-body__suffix {
31+
display: none !important;
32+
padding: 0 !important;
33+
}
34+
35+
.n-dropdown-menu .n-dropdown-option .n-dropdown-option-body .n-dropdown-option-body__prefix {
36+
display: none !important;
2837
}

0 commit comments

Comments
 (0)