// ==UserScript==
// @name xieying3D_rollback
// @namespace http://tampermonkey.net/
// @version 2025.2.14.1
// @description try it
// @author You
// @match https://data-encoder.ruqimobility.com/tool/pc?*
// @icon https://www.google.com/s2/favicons?sz=64&domain=ruqimobility.com
// @grant none
// @license MIT
// ==/UserScript==
window.$ = Document.prototype.$ = Element.prototype.$ = $;
window.$$ = Document.prototype.$$ = Element.prototype.$$ = $$;
const _window = window
window.cccallback = []
window._ds = {
logAn: false,
logKeydown: false,
isDebug: true,
objSum: 0,
pointSize: 0.17,
callback: [],
viewBtnMap: {},
}
const _ds = new Proxy(window._ds, {
get(target, prop) {
return Reflect.get(target, prop)
},
set(target, prop, value) {
if(prop in trigger) {
trigger[prop](value)
} else {
Reflect.set(target, prop, value)
}
return true
}
})
const trigger = {
resultDir(newVal) {
window._ds.resultDir = newVal
},
taskId(newVal) {
window._ds.taskId = newVal
const logTitle = $('.log-title')
logTitle.style.cursor = 'pointer'
logTitle.onclick = function() {
copyToClipboard(newVal).then(res => showMessage('复制:任务id', {type: 'success'}))
}
logTitle.innerHTML = logTitle.textContent + ' '.repeat(2) + newVal
}
};
hijackXHR(function() {
const xhr = this;
let taskId
if(taskId = new RegExp(`^https://data-encoder.ruqimobility.com/annotation/dataset/info/(\\d*)`).exec(xhr.responseURL)?.[1]) {
document.title = taskId
_ds.taskId = taskId
}
});
const realAEL = EventTarget.prototype.addEventListener;
const realREL = EventTarget.prototype.removeEventListener;
const listenerMap = new WeakMap(); // 存储原始监听器和包装后的监听器的映射关系
EventTarget.prototype.addEventListener = function(type, listener, options) {
const wrappedListener = function(e) {
if(type == 'keydown' && [81, 87, 69, 65, 83, 68].includes(e.keyCode) && !e.ctrlKey) { // Q81 W87 E69 A65 S83 D68
// ;['keydown', 'keyup'].forEach((event) => {
// document.body.dispatchEvent(new KeyboardEvent(event, {
// code: "KeyZ",
// key: "z",
// keyCode: 90,
// ctrlKey: false,
// bubbles: true
// }))
// });
if(listener.name !== 'keydownCallback') return
}
if(type == 'keydown' && e.keyCode == 32 && listener.name !== 'keydownCallback') return
if(type == 'keydown' && e.keyCode >= 49 && e.keyCode <= 53) {
if(e.currentTarget == document.body && e.target == document.body) {
$$('.label-container2 .select-label').find(label => label.textContent === ['小车', 'SUV', '两轮车', '人', '隔离柱'][e.keyCode-49]).click()
}
return
}
listener.apply(this, arguments);
};
if (!listenerMap.has(this)) {
listenerMap.set(this, new Map());
}
listenerMap.get(this).set(listener, wrappedListener);
realAEL.call(this, type, wrappedListener, options);
};
EventTarget.prototype.removeEventListener = function(type, listener, options) {
const wrappedListener = listenerMap.get(this)?.get(listener);
if (wrappedListener) {
realREL.call(this, type, wrappedListener, options);
listenerMap.get(this).delete(listener);
}
};
;(function() {
document.addEventListener('contextmenu',function(e){
e.preventDefault();
})
})();
let frameQueue = ['caret-right', 'caret-left']
document.body.addEventListener('keyup', function(e) {
if([81, 87, 69, 65, 83, 68].includes(e.keyCode)) {
document.body.dispatchEvent(new MouseEvent('mouseup', {bubbles: true}))
}
})
document.body.addEventListener('keydown', function keydownCallback(e) {
const { keyCode } = e
_ds.logKeydown && console.log(keyCode, e)
const isInput = ['input', 'textarea'].find(sel => {
const activeEl = document.activeElement
if(!activeEl?.matches(sel)) return
if(sel == 'input' && activeEl.type !== 'text') return false
return true
})
if(isInput) return;
[
[
() => [81, 87, 69, 65, 83, 68].includes(keyCode),
() => {
e.preventDefault();
const triggerBtn = _ds.viewBtnMap[keyCode];
if(!triggerBtn) return;
triggerBtn.dispatchEvent(new MouseEvent('mousedown', {bubbles: true}))
if(e.altKey) document.body.dispatchEvent(new MouseEvent('mouseup', {bubbles: true}))
}
],
[
() => keyCode == 89,
() => {
setTimeout(() => $$('.item-warp-li').find(item => item.children[0].matches('.item.active'))?.click())
}
],
[
() => [9, 32].includes(keyCode), // 【Tab】
(e) => {
e.preventDefault()
if($('.main-view').style.display !== 'none') {
$(`span[aria-label="${keyCode == 9 ? 'caret-left' : 'caret-right'}"]`).parentElement.click()
} else {
$$('.page-turn-btn button').find(btn => btn.textContent == ` ${keyCode == 9 ? '上一页' : '下一页'} `).click()
}
}
],
[
() => keyCode == 192,
() => {
// showMessage('反转:Tap切帧方向')
// frameQueue.reverse()
const btns = $$('label').filter(item => ['单帧视图', '多帧视图'].some(text => item.textContent == text))
btns.find(btn => !btn.className.includes('checked'))?.click()
}
],
].forEach((item) => { item[0]() && item[1](e) })
})
const viewScheme = { //Q81 W87 E69 A65 S83 D68
'俯视图': {
81: 0,
87: 6,
69: 1,
65: 9,
83: 3,
68: 10,
},
'侧视图': {
81: 7,
87: 4,
69: 2,
65: 3,
83: 5,
68: 6,
},
'后视图': {
81: 11,
87: 4,
69: 8,
65: 9,
83: 5,
68: 10,
}
}
const appear = {
imgView: false,
maxView: false,
}
let roundCount = 1
Obs(document.body, mrs => {
mrs.forEach(mr => {
// console.log(mr)
[...mr.addedNodes].some(an => {
_ds.logAn && console.log(roundCount, an);
function bindView(viewWrap) { //键盘调整三视图
const btnItems = viewWrap.$$('.item')
viewWrap.$$('.side-view').forEach(item => {
item.addEventListener('mouseenter', (e) => {
const angle_view = item.$('.title1').textContent
const scheme = viewScheme[angle_view]
const viewBtnMap = {}
for(let keyCode in scheme) {
viewBtnMap[keyCode] = btnItems[scheme[keyCode]]
}
_ds.viewBtnMap = viewBtnMap
})
item.addEventListener('mouseleave', (e) => {
_ds.viewBtnMap = {}
})
})
}
function set2DImgPositionInfo(cameraNumEl) {
const imgPosition = ['后', '前远', '前广', '左后', '左前', '右后', '右前']
cameraNumEl.textContent = imgPosition[ /camera-(\d)/.exec(cameraNumEl.textContent)[1]-1 ]
}
[
[
() => ((appear.messageBoxWrap = $('.el-overlay.is-message-box')) && appear.messageBoxWrap?.textContent.startsWith('提示您已长时间未保存数据')),
() => {
const messageBoxWrap = appear.messageBoxWrap
const messageBox = messageBoxWrap.$('.el-message-box')
const close = createEl('div', {
innerText: '×',
style: {
position: 'absolute',
top: 0,
right: 0,
width: '50px',
height: '50px',
fontSize: '30px',
textAlign: 'center',
zIndex: '99999',
cursor: 'pointer',
},
onclick: function() {
messageBoxWrap.remove()
}
})
messageBox.append(close)
}
],
[
() => {
return (an.matches?.('.el-dialog') && an?.textContent.startsWith('物体批注')) ||
(an.matches?.('.comment-modal') && an?.parentElement?.previousElementSibling.textContent.startsWith('物体批注'))
},
() => {
const checkboxWrap = an.$('.ant-form-horizontal .ant-row')
const checkboxs = an.$$('input[type="checkbox"]')
const textarea = an.$('#description')
const footer = an.$('form+div')
const inputWraps = an.$$('.ant-checkbox-group .ant-col')
$('.el-overlay-dialog[aria-label^="物体批注"]').style.opacity = '.95'
;['批注原因', '批注描述'].forEach(tit => (an.$(`label[title="${tit}"]`).parentElement.style.display = 'none'))
an.$$('.ant-row').find(item => item.textContent.startsWith('涉及帧数')).style.display = 'none'
setStyle(footer, {
marginTop: 0,
marginBottom: '10px',
textAlign: 'center',
})
inputWraps.forEach(item => (item.style.marginBottom = 0))
const quickPhraseWrap = createEl('div', {
className: 'quickPhrase-wrap',
style: {
marginTop: '15px'
}
})
const scheme = {
'方位': {
'顶视': '顶视图',
'侧视': '侧视图',
'后视': '后视图',
'角度': '角度',
'←': '左边框',
'↑': '上边框',
'→': '右边框',
'↓': '下边框',
'车头': '车头贴合',
'车尾': '车尾贴合',
'车顶': '车顶贴合',
},
'贴合': {
'收': '往里收',
'扩': '往外扩',
'上移': '整体上移',
'下移': '整体下移',
'左移': '整体左移',
'右移': '整体右移',
'↶': '逆时针旋转',
'↷': '顺时针旋转',
'飘空': '飘空',
'下陷': '下陷',
'稳定边': '贴合稳定边',
'地线': '检查地线',
},
'尺寸': {
'长': '长度',
'宽': '宽度',
'高': '高度',
'脑补': '脑补',
'统一': '统一尺寸',
},
'类型': {
'小车': '小车',
'SUV': 'SUV',
'大车': '大车',
'两轮车': '两轮车',
'三轮车': '三轮车',
'人': '人',
'BUS': 'BUS',
'隔离柱': '隔离柱',
'锥桶': '锥桶',
'防撞桶': '防撞桶',
'防撞球': '防撞球',
'一般': '一般障碍物',
},
'转弯维度': {
'未知': '转弯属性:未知',
'不转': '转弯属性:不转',
'左': '转弯属性:左转',
'右': '转弯属性:右转',
'双闪': '双闪',
},
'刹车维度': {
'未知': '刹车属性:刹车',
'未刹车': '未刹车',
'刹车': '刹车',
},
'其他': {
'漏标': '漏标',
'前漏': '前续帧漏显示',
'后漏': '后续帧漏显示',
'漏点': '漏点',
'没框全': '没框全',
'舍弃点云': '适当舍弃点云',
},
'补充': {
'前后帧检查': '前后帧检查',
'伪3D': '参照伪3D',
}
}
const checkboxMap = {
'不贴合': ['贴合', '方位'],
'标签错误': ['类型'],
'方向错误': [],
'属性错误': ['转弯维度', '刹车维度'],
'尺寸不对': ['尺寸'],
'方向错误': [],
'多标': [],
'其他': ['其他'],
}
for(let k1 in scheme) {
const btnWrap = createEl('div', {
className: 'btn-wrap',
style: {
display: 'flex',
marginBottom: '12px'
}
})
const title = createEl('div', {
className: 'btn-title',
innerText: `${k1}:`,
style: {
fontSize: '13px'
}
})
btnWrap.append(title)
for(let k2 in scheme[k1]) {
const phrase = scheme[k1][k2]
const btn_style = {
padding: '0px 5px',
height: '20px',
lineHeight: '20px',
margin: '0 5px 0 0',
backgroundColor: 'rgb(136, 136, 136)',
color: 'rgb(255, 255, 255)',
fontSize: '12px',
cursor: 'pointer',
userSelect: 'none',
}
const btn = createEl('div', {
className: 'phrase-btn',
innerText: k2,
style: btn_style,
onmousedown: function (e) {
const findIdx = Object.values(checkboxMap).findIndex(arr => arr.includes(k1))
if(findIdx !== -1) {
checkboxs.forEach(checkbox => {
if(checkbox.value === Object.keys(checkboxMap)[findIdx] && !checkbox.checked) checkbox.click()
})
if(e.button === 2) {
setTimeout(() => {
checkboxs.forEach((checkbox, idx) => {
if(checkbox.value !== Object.keys(checkboxMap)[findIdx] && checkbox.checked) setTimeout(() => checkbox.click(), idx*100)
})
})
}
}
const value = textarea.value
if(e.button === 0) {
setTextAreaValue(textarea, `${value}${value ? ';' : ''}${phrase}`)
} else if(e.button === 2) {
setTextAreaValue(textarea, `${phrase}`)
}
},
})
btnWrap.append(btn)
}
quickPhraseWrap.append(btnWrap)
}
checkboxWrap.insertAdjacentElement('afterend', quickPhraseWrap)
quickPhraseWrap.insertAdjacentElement('afterend', footer)
}
],
[
() => {
return (an.matches?.('.el-dialog') && an?.textContent.startsWith('全局批注')) ||
(an.matches?.('.comment-modal') && an?.parentElement?.previousElementSibling.textContent.startsWith('全局批注'))
},
() => {
const checkboxs = an.$$('#reason input[type="checkbox"]')
if(checkboxs.every(item => !item.checked)) checkboxs.find(item => item.value == '漏标').click()
}
],
[
() => !appear.annoType && $('.key-frame-annotate-type .ant-dropdown-trigger'),
() => {
$('.key-frame-annotate-type .ant-dropdown-trigger').click()
appear.annoType = true
}
],
[
() => !appear.annoTypeDropdown && $('.ant-dropdown')?.textContent.startsWith('关闭关键帧继承'),
() => {
$('.ant-dropdown').style.display = 'none'
$('.key-frame-annotate-type .ant-dropdown-trigger').click()
setTimeout(() => ($('.ant-dropdown').style.display = null))
appear.annoTypeDropdown = true
}
],
[
() => !appear.keyFrameInterpolation && $('.ant-dropdown li[data-menu-id="keyFrameInterpolation"]:not(.ant-dropdown-menu-item-disabled)'),
() => {
$('.ant-dropdown li[data-menu-id="keyFrameInterpolation"]').click()
appear.keyFrameInterpolation = true
}
],
[
() => !appear.mulView && $('.main-view.mul-view'),
() => {
const mulView = $('.main-view.mul-view')
mulView.$$('.el-row.mul-row').forEach(treeView => {
bindView(treeView)
})
$$('.mul-title').forEach(el => {
el.onclick = function() {
// setInputValue($('.pageIndex input'), )
$('.timeline-scale').children[Number(el.textContent) - 1].click()
}
})
appear.mulView = true
}
],
[
() => !appear.sideviewWrap && $('.sideview-container'),
() => {
const sideviewWrap = $('.sideview-container')
bindView(sideviewWrap)
appear.sideviewWrap = true
}
],
[
() => !appear.sideviewWrap && $('.sideview-container'),
() => {
appear.sideviewWrap = true
}
],
[
() => !appear.commentPanel && $('.comment-container'),
() => {
const commentPanel = $('.comment-container');
commentPanel.style.opacity = 0.9
let isMoving = false;
let top = -15;
let right = -225
const obsTarget = commentPanel.$('.comment-panel').children[0]
Obs(obsTarget, (mrs)=> {
if(obsTarget.style.display == '') {
commentPanel.style.top = top + 'px'
commentPanel.style.right = right + 'px'
} else {
commentPanel.style.top = '0px'
commentPanel.style.right = '3px'
}
}, {childList: true, attributes: true, attributeOldValue: true})
commentPanel.addEventListener('mousedown', (e) => {
if(e.button == 1) {
isMoving = true
e.preventDefault()
}
})
document.body.addEventListener('mousemove', (e) => {
if(!isMoving) return
commentPanel.style.top = ((top += e.movementY) + 'px')
commentPanel.style.right = ((right -= e.movementX) + 'px')
})
commentPanel.addEventListener('mouseup', (e) => {
if(e.button == 1) isMoving = false
})
appear.commentPanel = true
}
],
[
() => an.matches?.('.ant-btn.select-label'),
() => (_ds.isDrawing = true)
],
[
() => !appear.waterMask && $('#waterMask'),
() => {
$('#waterMask').remove()
appear.waterMask = true
}
],
[
() => !appear.handleLineSide && $('.sideview-container .handle-line-side'),
() => {
const el = $('.sideview-container .handle-line-side')
const obs = Obs(el, mrs => {
mrs.forEach(mr => {
if(mr.attributeName === 'style' && el.style.diplay !== 'none') {
el.dispatchEvent(new MouseEvent('mousedown', {
screenY: 800
}))
document.body.dispatchEvent(new MouseEvent('mousemove', {
screenY: 620,
bubbles: true
}))
document.body.dispatchEvent(new MouseEvent('mouseup', {
bubbles: true
}))
}
})
}, {childList: true, attributes: true})
appear.handleLineSide = true
}
],
[
() => !appear.objListTitle && $('#pane-objectLabel .header-title'),
() => {
const objListTitle = $('#pane-objectLabel .header-title')
Obs(objListTitle, mrs => {
mrs.forEach(mr => {
[...mr.addedNodes].forEach(an => {
const curSum = /物体标签\((\d*)\)/.exec(an.textContent)[1]
if(_ds.isDrawing && +curSum === +_ds.objSum+1 ) {
_ds.isDrawing = false
_ds.isFocusObj = true
}
_ds.objSum = curSum
})
})
})
appear.objListTitle = true
}
],
[
() => an.matches?.('.el-tree-node.is-expanded.is-focusable') && an.$('.item-warp-li'),
() => {
const objItem = an.$('.item-warp-li')
if(_ds.isFocusObj && objItem.children[0].matches('.item.active')) {
['keydown', 'keyup'].forEach((event) => {
document.body.dispatchEvent(new KeyboardEvent(event, {
code: "Escape",
key: "Escape",
keyCode: 27,
bubbles: true
}))
});
objItem.click()
_ds.isFocusObj = false
}
}
],
[
() => an.matches?.('.pc-editor'),
() => {
setStyle($('.annotate-mode'), {
fontSize: '20px',
fontWeight: '800',
background: 'black',
})
}
],
[
() => !appear.imgView && $('.img-view'),
() => {
const views = $$('.img-view')
views.forEach(view => {
let isScroll = false
set2DImgPositionInfo(view.$('.camera-number'))
clickTrigger(view, (e) => {
view.dispatchEvent(new MouseEvent('dblclick', { bubbles: true }));
}, 3, 2)
view.addEventListener('mouseenter', (e)=> {
if(e.clientX < 20) return
view.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
isScroll = false
})
view.addEventListener('mousemove', (e)=> {
// console.log(e)
if(!isScroll && e.clientX < 20) {
view.dispatchEvent(new MouseEvent('mouseleave'))
isScroll = true
} else if(isScroll && e.clientX >= 20) {
view.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
isScroll = false
}
})
})
appear.imgView = true
},
],
[
() => !appear.maxView && (appear.maxView = $('.img-view-max')),
() => {
const positionInfoEl = [...appear.maxView.$('.info').children].at(-1)
set2DImgPositionInfo(positionInfoEl)
Obs(positionInfoEl, mrs => {
mrs.forEach(mr => {
[...mr.addedNodes].forEach(an => {
if(an.nodeName == '#text') set2DImgPositionInfo(an)
})
})
}, {childList: true})
clickTrigger(appear.maxView, (e) => {
appear.maxView.$('span[title="关闭"]').click()
}, 3, 2)
}
],
[
() => an.$?.('.rect-ground-line line'),
() => an.$$('.rect-ground-line line').forEach(line => (line.style.stroke = 'rgba(255, 0, 0, .2'))
],
[
() => !appear.attrPanel && $('.main-class-edit'),
() => {
$('.main-class-edit .view-class-wrap').style.minHeight = 'auto' //高度自适应
setStyle($('.main-class-edit'), {
bottom: 'auto',
top: '260px',
left: '1350px',
opacity: '0.9',
})
$('.main-class-edit').addEventListener('mousedown', (e) => {
if(e.which == 3) $('.main-class-edit i.el-icon.close').click()
})
appear.attrPanel = true
}
],
[
() => !appear.taskWrap && $('.item-wrap.title-task'),
() => {
const taskWrap = $('.item-wrap.title-task')
setStyle(taskWrap, {
position: 'relative',
})
taskWrap.$$('.title-text').forEach(item => {
item.style.cursor = 'pointer'
item.addEventListener('click', (e) => {
let cookieVal
document.cookie.split('; ').some(kv => {
const [k, v] = kv.split('=')
if(k === 'ruqimobility.com-prod token') {
cookieVal = v
return true
}
})
let URLParam = /https:\/\/data-encoder\.ruqimobility\.com\/tool\/pc\?(.*)/.exec(location.href)?.[1]
if(!cookieVal || !URLParam) return showMessage('复制失败', {type: 'error'})
const textToCopy = cookieVal + ' ' + URLParam
copyToClipboard(textToCopy).then(res => showMessage('已复制到剪切板'))
})
})
const btn_change = createEl('div', {
title: '切换跳转',
style: {
position: 'absolute',
right: '0px',
bottom: '0px',
width: '15px',
height: '15px',
background: 'gray',
cursor: 'pointer'
},
onclick: function() {
navigator.clipboard.readText().then((clipText) => {
const [cookieVal, URLParam] = clipText.split(' ')
if(cookieVal && URLParam) {
document.cookie = `ruqimobility.com-prod token=${cookieVal};domain=.ruqimobility.com;path=/`
location.href = `https://data-encoder.ruqimobility.com/tool/pc?${URLParam}`
} else {
showMessage('凭证不合法', {type: 'error'})
}
});
}
})
taskWrap.append(btn_change)
appear.taskWrap = true
},
],
[
() => {},
() => {},
]
].forEach((item) => { item[0]() && item[1]() })
if(an.matches?.('.object-item') && an.textContent?.startsWith('点云对象')) {
const btnStyle = {
position: 'absolute',
left: '10px',
borderRadius: '5px',
transform: 'scale(.9)',
padding: '0px 5px',
height: '20px',
lineHeight: '20px',
margin: '0 5px 0 0',
backgroundColor: 'rgb(136, 136, 136, .5)',
color: 'rgb(255, 255, 255)',
fontSize: '12px',
cursor: 'pointer',
userSelect: 'none',
}
const btn_display = createEl('div', {
className: 'displayAttr',
innerText: '显隐',
style: btnStyle,
onclick: display.bind(this, void 0)
})
const attrPanel = matchesWrapper(an, '.main-class-edit');
!$('.displayAttr') && attrPanel.$('.edit-class-common').insertAdjacentElement('afterbegin', btn_display)
display(false)
function display(isShow) {
const items = [...attrPanel.$('.el-collapse').children]
const display_items_0 = items[0].style.display
items.forEach(el => {
if(el.textContent.startsWith('Attributes')) return
if(isShow !== void 0) {
el.style.display = isShow ? null : 'none'
} else {
el.style.display = display_items_0 === '' ? 'none' : null
}
})
}
return true
}
})
})
++roundCount
}, { childList: true, subtree: true })
function setInputValue(input, value) {
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set
nativeInputValueSetter.call(input, value);
['input', 'change'].forEach((event) => input.dispatchEvent(new Event(event, { bubbles: true })))
};
function setTextAreaValue(textarea, value) {
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set
nativeInputValueSetter.call(textarea, value);
['input', 'change'].forEach((event) => textarea.dispatchEvent(new Event(event, { bubbles: true })))
};
function hijackXHR(change, send) {
const realXMLHttpRequest = window.XMLHttpRequest;
window.XMLHttpRequest = function() {
const xhr = new realXMLHttpRequest();
xhr.addEventListener('readystatechange', function () {
if (xhr.readyState !== 4) return
change.call(this)
});
send && (xhr.send = send.bind(xhr))
return xhr;
}
const realOpen = realXMLHttpRequest.prototype.open;
realXMLHttpRequest.prototype.open = function () {
const xhr = this
let curURL = arguments[1]
const getter = Object.getOwnPropertyDescriptor(realXMLHttpRequest.prototype, "responseText").get
Object.defineProperty(xhr, "responseText", {
get() {
let result = getter.call(xhr);
if(new RegExp("/annotation/dataset/info/\\d+").test(curURL)) {
let result_parse = JSON.parse(getter.call(xhr));
const classAttributes = result_parse.data.classAttributes
if(classAttributes) {
classAttributes.pointSize = _ds.pointSize
classAttributes.pointColorMode.pointColors = ["rgb(255, 255, 255)", "rgb(255, 132, 0)"];
} else {
result_parse.data.classAttributes = {
"contrast": 100,
"saturate": 100,
"lightness": 100,
"pointSize": 0.17,
"measureOpen": true,
"measureConfig": [
{
"id": 0,
"type": "circle",
"radius": 50
},
{
"id": 1,
"type": "rect",
"xPlus": 220,
"yPlus": 100,
"rotate": 0,
"xMinus": 220,
"yMinus": 100
},
{
"id": 2,
"type": "rect",
"xPlus": 100,
"yPlus": 50,
"rotate": 0,
"xMinus": 100,
"yMinus": 50
}
],
"pointColorMode": {
"mode": "height",
"pointHSL": "hsl(220, 100%, 50%)",
"pointColors": ["rgb(255, 255, 255)", "rgb(255, 132, 0)"],
"pointHeight": [
-0.2,
3.5
],
"pointIntensity": [
0,
255
]
},
"backgroundColor": "#000",
"in2DImageRender": {
"renderBox": true,
"renderRect": false,
"renderImgPolygon": true,
"renderProjectBox": true,
"renderImgKeyPoint": true,
"renderImgPolyLine": true
}
}
}
// console.log(result)
console.log(result_parse)
result = JSON.stringify(result_parse)
}
if(new RegExp("/annotation/projectLabelMatter/findAll/\\d+").test(curURL)) {
let result_parse = JSON.parse(getter.call(xhr));
const data = result_parse.data
const scheme = {
'小车': {
length: 4.6,
height: 1.45,
},
'SUV': {
length: 4.6,
width: 2.1,
height: 1.65,
},
'BUS': {
length: 10,
},
'两轮车': {
length: 1.6,
width: 0.7,
height: 1.5,
},
'三轮车': {
length: 2.45,
width: 1.10,
height: 1.55,
},
'人': {
height: 1.65,
},
'隔离柱': {
length: [0.1, 1, 0.12],
width: [0.1, 1, 0.12],
height: [0.1, 1, 0.5],
},
'防撞桶': {
length: [0.1, 999, 0.85],
width: [0.1, 999, 0.85],
height: [0.1, 999, 0.9],
},
'水马': {
length: 1.55,
width: 0.55,
height: 0.75,
},
'防撞球': {
length: [0.1, 999, 0.4],
width: [0.1, 999, 0.4],
height: [0.1, 999, 0.45],
},
'地锁开': {
length: [0.1, 1, 0.45],
width: [0.1, 1, 0.2],
height: [0.1, 1, 0.25],
},
'隔离栏': {
length: 1.65,
width: 0.45,
height: 1.15,
},
}
if(Array.isArray(data)) {
for(let type in scheme) {
const target = data.find(item => item.name == type)
if(!target) continue
for(const whd in scheme[type]) { //长宽高
const size = scheme[type][whd]
if(!Array.isArray(size)) {
target.toolTypeOptions[whd][2] = size
} else {
target.toolTypeOptions[whd] = size
target.toolTypeOptions.isConstraints = true
target.toolTypeOptions.isStandard = true
}
}
}
}
console.log(result_parse)
result = JSON.stringify(result_parse)
}
return result
},
configurable: true,
});
return realOpen.apply(xhr, arguments);
};
}
function copyToClipboard(textToCopy) {
// navigator clipboard 需要https等安全上下文
if (navigator.clipboard && window.isSecureContext) {
return navigator.clipboard.writeText(textToCopy);
} else {
let textArea = document.createElement("textarea");
textArea.value = textToCopy;
textArea.style.position = "absolute";
textArea.style.opacity = 0;
textArea.style.left = "-999999px";
textArea.style.top = "-999999px";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
return new Promise((res, rej) => {
document.execCommand('copy') ? res() : rej();
textArea.remove();
});
}
}
function matchesWrapper(el, sel) {
const parentEl = el.parentElement
if(!parentEl) return null
if(parentEl.matches(sel)) {
return parentEl
} else {
return matchesWrapper(parentEl, sel)
}
}
function clickTrigger(el, fn, button, moveThreshold = 0) {
let movement = 0
let allowTrigger = false
let isRightdown = false
el.addEventListener('mousedown', (e)=>{
e.preventDefault()
if(e.which !== button) return
movement = 0
isRightdown = true
allowTrigger = true
})
el.addEventListener('mousemove', (e)=>{
if(!isRightdown || (isRightdown && (movement+=(Math.sqrt(e.movementX**2 + e.movementY**2))) <= moveThreshold)) return
allowTrigger && (allowTrigger = false)
})
el.addEventListener('mouseup', (e)=>{
if(e.which !== button) return
if(allowTrigger) fn(e)
isRightdown = false
})
}
function Obs(target, callBack, options = { childList: true, subtree: true, attributes: true, attributeOldValue: true}) {
if(!target) return console.error('目标不存在')
const ob = new MutationObserver(callBack);
ob.observe(target, options);
return ob
}
function setStyle() {
[[Map, ()=> {
const styleMap = arguments[0]
for (const [el, styleObj] of styleMap) {
!Array.isArray(el) ? setStyleObj(el, styleObj) : el.forEach((el) => setStyleObj(el, styleObj))
}
}], [Element, () => {
const [el, styleObj] = arguments
setStyleObj(el, styleObj)
}], [Array, () => {
const [els, styleObj] = arguments
els.forEach((el) => setStyleObj(el, styleObj))
}]].some(([O, fn]) => O.prototype.isPrototypeOf(arguments[0]) ? (fn(), true) : false)
function setStyleObj(el, styleObj) {
for (const attr in styleObj) {
if (el.style[attr] !== undefined) {
el.style[attr] = styleObj[attr]
} else {
//将key转为标准css属性名
const formatAttr = attr.replace(/[A-Z]/, match => `-${match.toLowerCase()}`)
console.error(el, `的 ${formatAttr} CSS属性设置失败!`)
}
}
}
}
function createEl(elName, options) {
const el = document.createElement(elName)
for(let opt in options) {
if(opt !== 'style') {
el[opt] = options[opt]
} else {
let styles = options[opt]
setStyle(el, styles)
}
}
return el
}
function $(selector) {
const _this = Element.prototype.isPrototypeOf(this) ? this : document
const sel = String(selector).trim();
const id = /^#([^ +>~\[:]*)$/.exec(sel)?.[1]
return (id && _this === document) ? _this.getElementById(id) : _this.querySelector(sel)
}
function $$(selector) {
const _this = Element.prototype.isPrototypeOf(this) ? this : document
return Array.from(_this.querySelectorAll(selector))
}
function getParamValue(param) {
let r
location.href.split('?')[1].split('&').some(item => {
const param_value = item.split('=')
if(param_value[0] == param) {
r = param_value[1]
return true
}
})
return r
}
function showMessage(message, config) { //type = 'default', showTime = 3000, direction
let oldMessageWrap = document.querySelector(`.messageWrap-${config?.direction ? config.direction : 'top'}`)
let MessageWrap
if(!oldMessageWrap) {
MessageWrap = document.createElement('div')
MessageWrap.className = 'messageWrap'
setStyle(MessageWrap, {
position: 'absolute',
zIndex: '9999'
})
} else {
MessageWrap = oldMessageWrap
}
let MessageBox = document.createElement('div')
MessageBox.innerText = message
let closeBtn = document.createElement('div')
closeBtn.textContent = '×'
closeBtn.addEventListener('click', MessageBox.remove.bind(MessageBox)) //关闭消息提示
setStyle(MessageBox, {
position: 'relative',
minWidth: '200px',
marginTop: '5px',
padding: '6px 50px',
lineHeight: '25px',
backgroundColor: 'pink',
textAlign: 'center',
fontSize: '16px',
borderRadius: '5px',
transition: 'all 1s'
})
setStyle(closeBtn, {
position: 'absolute',
top: '-3px',
right: '3px',
width: '15px',
height: '15px',
zIndex: '999',
fontWeight: '800',
fontSize: '15px',
borderRadius: '5px',
cursor: 'pointer',
userSelect: 'none'
})
//控制方向
switch(config?.direction) {
case 'top': setStyle(MessageWrap, {top: '1%', left: '50%', transform: 'translateX(-50%)'}); break;
case 'top left': setStyle(MessageWrap, {top: '1%', left: '.5%'}); break;
case 'left': setStyle(MessageWrap, {top: '50%', left: '1%', transform: 'translateY(-50%)'}); break;
case 'top right': setStyle(MessageWrap, {top: '1%', right: '.5%', }); break;
case 'right': setStyle(MessageWrap, {top: '50%', right: '.5%', transform: 'translateY(-50%)'}); break;
case 'center': setStyle(MessageWrap, {top: '20%', left: '50%', transform: 'translate(-50%, -50%)'}); break;
case 'bottom': setStyle(MessageWrap, {bottom: '1%', left: '50%', transform: 'translateX(-50%)'}); break;
case 'bottom8': setStyle(MessageWrap, {bottom: '8%', left: '50%', transform: 'translate(-50%, -50%)'}); break;
case 'bottom left': setStyle(MessageWrap, {bottom: '1%'}); break;
case 'bottom right': setStyle(MessageWrap, {bottom: '1%', right: '.5%'}); break;
default: setStyle(MessageWrap, {top: '1%', left: '50%', transform: 'translateX(-50%)'}); break;
}
switch(config?.type) {
case 'success': setStyle(MessageBox, {border: '1.5px solid rgb(225, 243, 216)', backgroundColor: 'rgb(240, 249, 235)', color: 'rgb(103, 194, 58)'}); break;
case 'warning': setStyle(MessageBox, {border: '1.5px solid rgb(250, 236, 216)', backgroundColor: 'rgb(253, 246, 236)', color: 'rgb(230, 162, 60)'}); break;
case 'error': setStyle(MessageBox, {border: '1.5px solid rgb(253, 226, 226)', backgroundColor: 'rgb(254, 240, 240)', color: 'rgb(245, 108, 108)'}); break;
default: setStyle(MessageBox, {border: '1.5px solid rgba(202, 228, 255) ', backgroundColor: 'rgba(236, 245, 255)', color: 'rgb(64, 158, 255)'}); break;
}
MessageBox.appendChild(closeBtn)
if(oldMessageWrap) {
oldMessageWrap.appendChild(MessageBox)
} else {
MessageWrap.appendChild(MessageBox)
document.body.appendChild(MessageWrap)
}
let ani = MessageBox.animate([{
transform: "translate(0, -100%)" ,
opacity: 0.3,
},{
transform: "translate(0, 18px)",
opacity: 0.7,
offset: 0.9,
},{
transform: "translate(0, 15px)",
opacity: 1,
offset: 1,
}], {
duration: 300,
fill: 'forwards',
easing: 'ease-out',
})
//控制消失
let timer = setTimeout(() => {
ani.onfinish = () => {
MessageBox.remove()
}
ani.reverse()
}, (config?.showTime || 3000))
//鼠标悬停时不清除,离开时重新计时
MessageBox.addEventListener('mouseenter', () => clearTimeout(timer))
MessageBox.addEventListener('mouseleave', () => {
timer = setTimeout(() => {
ani.reverse()
ani.onfinish = () => {
MessageBox.remove()
}
}, (config?.showTime || 3000))
})
}
/**
2024/12/25:
- 新增:简化属性面板
2024/12/26:
- 新增:【Tab】前进一帧,【Tab+Shift】回退一帧
- 新增:右键缩放映射图,鼠标进入映射图后滚动调整缩放比,贴边滚动切换映射图。
- 调整:总框数的显示效果
- 优化:淡化吸附地线高度提示颜色
- 优化:隐藏右键菜单
- 新增:鼠标右键关闭属性面板
2024/12/27
- 新增:切包跳转
2025/1/5
- 优化:建框完成后退出绘制状态
- 优化:【Y】键聚焦视距
2025/1/6
- 调整:数据包跳转编码
2025/1/12
- 新增:title 展示提包ID
2025/1/15
- 新增:调整三视图高度
2025/2/9
- 调整:批注列表位置
- 调整:去除账号水印
- 新增:鼠标中键拖动批注列表
- 新增:作业记录title显示任务id
2025/2/10
- 新增:重写三视图调整快捷键
- 修复:无法进入未配置 classAttributes 的数据包
2025/2/11
- 【`】切换单帧/多帧视图
- 多帧视图下翻页
- 【空格】下一帧
- 【Tab】上一帧
- 【1】小车
- 【2】SUV
- 【3】二轮
- 【4】行人
- 【5】隔离柱
- 多帧视图点击序号切帧
2025/2/12
- 新增:调整现有默认尺寸,对无默认尺寸的类型进行补充
- 修复:空格的副作用(触发任意点击过的按钮)
- 新增:关键帧标注方案默认勾选[关键帧插值]
- 新增:全局批注默认勾选[漏标]
2025/2/13
- 新增:调整映射图视角名称
*/
Wrap
Beautify