@ -56,7 +56,7 @@
|
|||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"transformIgnorePatterns": [
|
"transformIgnorePatterns": [
|
||||||
"node_modules/(?!(three|allotment|@tauri-apps\/api)/)"
|
"node_modules/(?!(three|allotment|@tauri-apps/api)/)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
@ -93,6 +93,7 @@
|
|||||||
"@tauri-apps/cli": "^1.3.1",
|
"@tauri-apps/cli": "^1.3.1",
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@types/crypto-js": "^4.1.1",
|
||||||
"@types/three": "^0.146.0",
|
"@types/three": "^0.146.0",
|
||||||
|
"@types/uuid": "^9.0.2",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"postcss": "^8.4.19",
|
"postcss": "^8.4.19",
|
||||||
"prettier": "^2.8.0",
|
"prettier": "^2.8.0",
|
||||||
|
@ -209,7 +209,7 @@ function App() {
|
|||||||
<MemoryPanel />
|
<MemoryPanel />
|
||||||
<Logs />
|
<Logs />
|
||||||
</Allotment>
|
</Allotment>
|
||||||
<Allotment vertical defaultSizes={[400, 1]} minSize={20}>
|
<Allotment vertical defaultSizes={[1, 400]} minSize={20}>
|
||||||
<div className="h-full">
|
<div className="h-full">
|
||||||
<PanelHeader title="Drafting Board" />
|
<PanelHeader title="Drafting Board" />
|
||||||
<Toolbar />
|
<Toolbar />
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { useEffect, useRef } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
import { PanelHeader } from '../components/PanelHeader'
|
import { PanelHeader } from '../components/PanelHeader'
|
||||||
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
|
||||||
export const Stream = () => {
|
export const Stream = () => {
|
||||||
const videoRef = useRef<HTMLVideoElement>(null)
|
const videoRef = useRef<HTMLVideoElement>(null)
|
||||||
@ -11,6 +12,8 @@ export const Stream = () => {
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
const url = 'wss://api.dev.kittycad.io/ws/modeling/commands'
|
const url = 'wss://api.dev.kittycad.io/ws/modeling/commands'
|
||||||
|
const file_id = uuidv4()
|
||||||
|
let currentCmdId: null | string = null
|
||||||
const [pc, socket] = [new RTCPeerConnection(), new WebSocket(url)]
|
const [pc, socket] = [new RTCPeerConnection(), new WebSocket(url)]
|
||||||
// Connection opened
|
// Connection opened
|
||||||
socket.addEventListener('open', (event) => {
|
socket.addEventListener('open', (event) => {
|
||||||
@ -41,13 +44,13 @@ export const Stream = () => {
|
|||||||
if (message.type === 'SDPAnswer') {
|
if (message.type === 'SDPAnswer') {
|
||||||
pc.setRemoteDescription(new RTCSessionDescription(message.answer))
|
pc.setRemoteDescription(new RTCSessionDescription(message.answer))
|
||||||
} else if (message.type === 'TrickleIce') {
|
} else if (message.type === 'TrickleIce') {
|
||||||
console.log("got remote trickle ice");
|
console.log('got remote trickle ice')
|
||||||
pc.addIceCandidate(message.candidate);
|
pc.addIceCandidate(message.candidate)
|
||||||
} else if (message.type === 'IceServerInfo') {
|
} else if (message.type === 'IceServerInfo') {
|
||||||
console.log('received IceServerInfo')
|
console.log('received IceServerInfo')
|
||||||
pc.setConfiguration({
|
pc.setConfiguration({
|
||||||
iceServers: message.ice_servers,
|
iceServers: message.ice_servers,
|
||||||
iceTransportPolicy: "relay",
|
iceTransportPolicy: 'relay',
|
||||||
})
|
})
|
||||||
pc.ontrack = function (event) {
|
pc.ontrack = function (event) {
|
||||||
if (videoRef.current) {
|
if (videoRef.current) {
|
||||||
@ -69,14 +72,14 @@ export const Stream = () => {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
console.log("sending trickle ice candidate");
|
console.log('sending trickle ice candidate')
|
||||||
const {
|
const { candidate } = event
|
||||||
candidate
|
socket.send(
|
||||||
} = event;
|
JSON.stringify({
|
||||||
socket.send(JSON.stringify({
|
type: 'TrickleIce',
|
||||||
type: "TrickleIce",
|
|
||||||
candidate: candidate.toJSON(),
|
candidate: candidate.toJSON(),
|
||||||
}));
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,11 +90,13 @@ export const Stream = () => {
|
|||||||
pc.createOffer()
|
pc.createOffer()
|
||||||
.then((d) => pc.setLocalDescription(d))
|
.then((d) => pc.setLocalDescription(d))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log("sent SDPOffer begin");
|
console.log('sent SDPOffer begin')
|
||||||
socket.send(JSON.stringify({
|
socket.send(
|
||||||
type: "SDPOffer",
|
JSON.stringify({
|
||||||
|
type: 'SDPOffer',
|
||||||
offer: pc.localDescription,
|
offer: pc.localDescription,
|
||||||
}));
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
}
|
}
|
||||||
@ -101,15 +106,89 @@ export const Stream = () => {
|
|||||||
const debounceSocketSend = throttle((message) => {
|
const debounceSocketSend = throttle((message) => {
|
||||||
socket.send(JSON.stringify(message))
|
socket.send(JSON.stringify(message))
|
||||||
}, 100)
|
}, 100)
|
||||||
|
const handleClick = ({ clientX, clientY }: MouseEvent) => {
|
||||||
|
if (!videoRef.current) return
|
||||||
|
const { left, top } = videoRef.current.getBoundingClientRect()
|
||||||
|
const x = clientX - left
|
||||||
|
const y = clientY - top
|
||||||
|
console.log('click', x, y)
|
||||||
|
|
||||||
|
if (currentCmdId == null) {
|
||||||
|
currentCmdId = uuidv4()
|
||||||
|
|
||||||
|
debounceSocketSend({
|
||||||
|
type: 'ModelingCmdReq',
|
||||||
|
cmd: {
|
||||||
|
CameraDragStart: {
|
||||||
|
interaction: 'rotate',
|
||||||
|
window: {
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
file_id: file_id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMouseUp = ({ clientX, clientY }: MouseEvent) => {
|
||||||
|
if (!videoRef.current) return
|
||||||
|
const { left, top } = videoRef.current.getBoundingClientRect()
|
||||||
|
const x = clientX - left
|
||||||
|
const y = clientY - top
|
||||||
|
console.log('click', x, y)
|
||||||
|
|
||||||
|
if (currentCmdId == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
debounceSocketSend({
|
||||||
|
type: 'ModelingCmdReq',
|
||||||
|
cmd: {
|
||||||
|
CameraDragEnd: {
|
||||||
|
interaction: 'rotate',
|
||||||
|
window: {
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
file_id: file_id,
|
||||||
|
})
|
||||||
|
currentCmdId = null
|
||||||
|
}
|
||||||
const handleMouseMove = ({ clientX, clientY }: MouseEvent) => {
|
const handleMouseMove = ({ clientX, clientY }: MouseEvent) => {
|
||||||
if (!videoRef.current) return
|
if (!videoRef.current) return
|
||||||
const { left, top } = videoRef.current.getBoundingClientRect()
|
const { left, top } = videoRef.current.getBoundingClientRect()
|
||||||
const x = clientX - left
|
const x = clientX - left
|
||||||
const y = clientY - top
|
const y = clientY - top
|
||||||
debounceSocketSend({ type: 'MouseMove', x: x, y: y })
|
if (currentCmdId == null) {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
console.log('mouse move', x, y)
|
||||||
|
debounceSocketSend({
|
||||||
|
type: 'ModelingCmdReq',
|
||||||
|
cmd: {
|
||||||
|
CameraDragMove: {
|
||||||
|
interaction: 'rotate',
|
||||||
|
window: {
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
file_id: file_id,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (videoRef.current) {
|
if (videoRef.current) {
|
||||||
videoRef.current.addEventListener('mousemove', handleMouseMove)
|
videoRef.current.addEventListener('mousemove', handleMouseMove)
|
||||||
|
videoRef.current.addEventListener('mousedown', handleClick)
|
||||||
|
videoRef.current.addEventListener('mouseup', handleMouseUp)
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@ -117,6 +196,8 @@ export const Stream = () => {
|
|||||||
pc.close()
|
pc.close()
|
||||||
if (!videoRef.current) return
|
if (!videoRef.current) return
|
||||||
videoRef.current.removeEventListener('mousemove', handleMouseMove)
|
videoRef.current.removeEventListener('mousemove', handleMouseMove)
|
||||||
|
videoRef.current.removeEventListener('mousedown', handleClick)
|
||||||
|
videoRef.current.removeEventListener('mouseup', handleMouseUp)
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
@ -2695,6 +2695,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756"
|
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756"
|
||||||
integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==
|
integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==
|
||||||
|
|
||||||
|
"@types/uuid@^9.0.2":
|
||||||
|
version "9.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.2.tgz#ede1d1b1e451548d44919dc226253e32a6952c4b"
|
||||||
|
integrity sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==
|
||||||
|
|
||||||
"@types/webxr@*":
|
"@types/webxr@*":
|
||||||
version "0.5.0"
|
version "0.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/webxr/-/webxr-0.5.0.tgz#aae1cef3210d88fd4204f8c33385a0bbc4da07c9"
|
resolved "https://registry.yarnpkg.com/@types/webxr/-/webxr-0.5.0.tgz#aae1cef3210d88fd4204f8c33385a0bbc4da07c9"
|
||||||
|
Reference in New Issue
Block a user