diff --git a/index.html b/index.html new file mode 100644 index 000000000..9e69c3963 --- /dev/null +++ b/index.html @@ -0,0 +1,18 @@ + + + + + + + + + + + + +
+ + + + + diff --git a/src/api/chat.js b/src/api/chat.js new file mode 100644 index 000000000..66acbabd1 --- /dev/null +++ b/src/api/chat.js @@ -0,0 +1,241 @@ + +import request from '@/utils/request'; + +export function postDataBd(data) { + return request({ + url: `/api/audio/bd?group=${data.group}&conversationId=${data.conversationId}`, + method: 'post', + data: data.file, + }); +} + +// 创建/获取会话id +export function getConversation(data) { + return request({ + url: `/api/audio/conversation`, + method: 'get', + params: { + group: data.group, + userId: data.userId, + code: data.code + } + }); +} + +export function postDataXf(data) { + return request({ + url: `/api/audio/xf`, + method: 'post', + data: data + }); +} + +// 获取语音历史记录 +export function getHistoryVoice(code) { + return request({ + url: `/api/audio/${code}/history`, + method: 'get' + }); +} + +// 文字聊天 发送文字 +export function chatWithText(data, group) { + return request({ + url: `/api/jointTraining/chatWithText?group=${group}`, + method: 'post', + data: { + message: data, + } + }); +} + +// 文字聊天 发送文字 +export function chatWithAudio(file, group) { + return request({ + url: `/api/jointTraining/chatWithAudio?group=${group}`, + method: 'post', + data: file + }); +} + +// 文字聊天 发送文字 +export function getJoinTrainCode(data, group) { + return request({ + url: `/api/jointTraining/qrCode?group=${group}`, + method: 'post', + data: data + }); +} + + +// 创建房间 +export function postCreateRoom(data) { + return request({ + url: `/api/jointTraining/room`, + method: 'post', + data: data + }); +} + +// 检查房间存在 +export function checkRoomExist(params) { + return request({ + url: `/api/simulationRoom`, + method: 'get', + params + }); +} + +// 获取房间详情 +export function postRoomDetail(group) { + return request({ + url: `/api/jointTraining/room/${group}`, + method: 'get', + }); +} + +// 销毁房间 +export function deljointTrainRoom(group) { + return request({ + url: `/api/jointTraining/room`, + method: 'delete', + params: { + group: group, + } + }); +} + +// 查询有权限房间列表 +export function getjointTrainList() { + return request({ + url: `/api/jointTraining/room/list`, + method: 'get' + }); +} + +// 加入房间 +export function getjointTraining(group) { + return request({ + url: `/api/jointTraining/room/join`, + method: 'put', + params: { + group: group, + } + }); +} + +// 设置人员角色 +export function putUserRoles(data, group) { + return request({ + url: `/api/jointTraining/room/user/role?group=${group}`, + method: 'put', + data: data + }); +} + +// 设置人员角色 +export function getJointTrainRoomUserList(group) { + return request({ + url: `/api/jointTraining/room/${group}/user/list`, + method: 'get', + }); +} + +// 离开房间接口 +export function putJointTrainingExit(group) { + return request({ + url: `/api/jointTraining/room/exit`, + method: 'put', + params: { + group: group, + } + }); +} + +// 开始联合演练 +export function startJointTraining(group) { + return request({ + url: `/api/jointTraining/room/simulation`, + method: 'post', + params: { + group: group + } + }); +} + +// 获取个人信息 +export function getUserRoles(group) { + return request({ + url: `/api/jointTraining/room/user/role`, + method: 'get', + params: { + group: group + } + }); +} + +// 返回房间 +export function putJointTrainingState() { + return request({ + url: `/api/jointTraining/room/back`, + method: 'put', + }); +} + +// 踢出用户 +export function putJointTrainingUserkicked(userId, group) { + return request({ + url: `/api/jointTraining/room/user`, + method: 'put', + params: { + userId: userId, + group: group, + } + }); +} + +// 管理员结束所有人的仿真 +export function putJointTrainingSimulation(group) { + return request({ + url: `/api/jointTraining/room/simulation/all`, + method: 'put', + params: { + group: group, + } + }); +} + +// 结束仿真返回房间 +export function putJointTrainingSimulationUser(group) { + return request({ + url: `/api/jointTraining/room/simulation/user/exit`, + method: 'put', + params: { + group: group, + } + }); +} + +// 进入仿真 +export function putJointTrainingSimulationEntrance(group) { + return request({ + url: `/api/jointTraining/room/simulation/user/entrance`, + method: 'put', + params: { + group: group, + } + }); +} + +/** + * 权限获取(房间权限) + */ +export function getPermissionJoint(group) { + return request({ + url: '/api/jointTraining/qrCode', + method: 'get', + params: { + group: group + } + }); +} \ No newline at end of file diff --git a/src/api/jlmap3d/load3ddata.js b/src/api/jlmap3d/load3ddata.js new file mode 100644 index 000000000..b003b30b6 --- /dev/null +++ b/src/api/jlmap3d/load3ddata.js @@ -0,0 +1,77 @@ +import request from '@/utils/request'; + +/** 获取地图版本信息*/ +export function getPublishMapVersion(skinStyle) { + return request({ + url: `/api/map/${skinStyle}/version`, + method: 'get' + }); +} + +/** 获取发布地图详细内容*/ +export function getPublishMapDetail(skinStyle) { + let datad = request({ + url: `/api/map/${skinStyle}/details`, + method: 'get' + }); + return datad.then(); +} + + +/** 根据地图id获取地图信息*/ +export function getPublishMapInfo(mapId) { + return request({ + url: `/api/map/${mapId}`, + method: 'get' + }); +} + + +/** 获取草稿地图详细内容*/ +export function getMapDetail(id) { + return request({ + url: `/api/mapBuild/${id}/mapDataDetail`, + method: 'get' + }); +} + +/** 创建地图3d数据*/ +export function set3dMapData(data) { + return request({ + url: `/api/mapBuild/3dMapData`, + method: 'post', + data: data + }); +} + +/**通过地图id获取地图3d数据*/ +export function get3dMapData(mapId) { + return request({ + url: `/api/mapBuild/3dMapData/${mapId}`, + method: 'get' + }); +} + +/** 更新地图3d数据*/ +export function update3dMapData(data) { + return request({ + url: `/api/mapBuild/3dMapData/${data.id}`, + method: 'put', + data: data + }); +} + +/** 获取模型资源列表*/ +export function loadmap3dModel() { + return request({ + url: `/api/map3dModel/all`, + method: 'get' + }); +} +export function getPublish3dMapDetail(skinStyle) { + let datad = request({ + url: `/api/map/${skinStyle}/3dMapData`, + method: 'get' + }); + return datad.then(); +} diff --git a/src/api/jmap/lesson.js b/src/api/jmap/lesson.js index fbb3187b4..5a55d45f4 100644 --- a/src/api/jmap/lesson.js +++ b/src/api/jmap/lesson.js @@ -2,67 +2,67 @@ import request from '@/utils/request'; /** 获取发布的课程列表*/ export function getPublishLessonList() { - return request({ - url: '/api/lesson', - method: 'get' - }); + return request({ + url: '/api/lesson', + method: 'get' + }); } /** 获取发布列表树*/ export function getPublishLessonTree(params) { - return request({ - url: '/api/lesson/tree', - method: 'get', - params: params || {} - }); + return request({ + url: '/api/lesson/tree', + method: 'get', + params: params || {} + }); } /** 获取发布课程列表*/ export function getPublishLessonDetail(data) { - return request({ - url: `/api/lesson/${data.id}`, - method: 'get' - }); + return request({ + url: `/api/lesson/${data.id}`, + method: 'get' + }); } /** 发布课程分页列表列表*/ export function publishLessonList(param) { - return request({ - url: `/api/lesson/publishedLesson`, - method: 'get', - params: param - }); + return request({ + url: `/api/lesson/publishedLesson`, + method: 'get', + params: param + }); } /** 删除发布课程*/ export function delPublishLesson(lessonId) { - return request({ - url: `/api/lesson/publishedLesson/${lessonId}`, - method: 'delete' - }); + return request({ + url: `/api/lesson/publishedLesson/${lessonId}`, + method: 'delete' + }); } -/** 发布课程上架*/ +/**发布课程上架*/ export function putLessonOnLine(id) { - return request({ - url: `/api/lesson/${id}/onLine`, - method: 'put' - }); + return request({ + url: `/api/lesson/${id}/onLine`, + method: 'put', + }); } -/** 发布课程下架*/ +/**发布课程下架*/ export function putLessonOffLine(id) { - return request({ - url: `/api/lesson/${id}/offLine`, - method: 'put' - }); + return request({ + url: `/api/lesson/${id}/offLine`, + method: 'put', + }); } /** * 获取地图产品下的课程列表 */ export function getCommodityProductLesson(prdCode) { - return request({ - url: `/api/lesson/${prdCode}/list`, - method: 'get' - }); -} + return request({ + url: `/api/lesson/${prdCode}/list`, + method: 'get', + }); +} \ No newline at end of file diff --git a/src/api/login.js b/src/api/login.js index eb1297b44..60f1e6463 100644 --- a/src/api/login.js +++ b/src/api/login.js @@ -1,77 +1,68 @@ import request from '@/utils/request'; - -// 修改密码 -export function changePassword(userId, data) { - return request({ - url: `/api/login/${userId}/password`, - method: 'put', - data: data - }); -} - // 账号密码 其他系统 export function login(params) { - return request({ - url: '/api/login', - method: 'post', - data: params - }); + return request({ + url: '/api/login', + method: 'post', + data: params + }); } // 获取登陆url 二维码 其他系统 export function getLoginUrl(params) { - return request({ - url: '/api/login/url', - method: 'get', - params: params - }); + return request({ + url: '/api/login/url', + method: 'get', + params: params + }); } + // 获取登录信息 export function getInfo(token) { - return request({ - url: '/api/login/getUserInfo', - method: 'get', - params: { token } - }); + return request({ + url: '/api/login/getUserInfo', + method: 'get', + params: { token } + }); } // 登出 export function logout(token) { - return request({ - url: '/api/login/logout', - method: 'get', - params: { - token - } - }); + return request({ + url: '/api/login/logout', + method: 'get', + params: { + token + } + }); } // 检查登陆状态 export function checkLoginStatus(sessionId) { - return new Promise((resolve, reject) => { - request({ - url: '/api/login/checkStatus', - method: 'get', - params: { - sessionId: sessionId - } - }).then(response => { - if (response.data.status === '2') { - resolve(response); - } else { - reject(response); - } - }).catch(error => { - reject(error); - }); - }); + return new Promise((resolve, reject) => { + request({ + url: '/api/login/checkStatus', + method: 'get', + params: { + sessionId: sessionId + } + }).then(response => { + if (response.data.status === '2') { + resolve(response); + } else { + reject(response); + } + }).catch(error => { + reject(error); + }); + }); } // 检测持续在线 防止掉线在大屏或者仿真系统下 export function checkLoginLine() { - return request({ - url: '/api/cache/heartBeat', - method: 'get' - }); + return request({ + url: '/api/cache/heartBeat', + method: 'get', + }); } diff --git a/src/api/management/mapskin.js b/src/api/management/mapskin.js index 1c17e8d0c..98cf524c4 100644 --- a/src/api/management/mapskin.js +++ b/src/api/management/mapskin.js @@ -2,68 +2,68 @@ import request from '@/utils/request'; /** 分页查询皮肤*/ export function getSkinStylePageList(params) { - return request({ - url: `/api/mapSkin`, - method: 'get', - params: params - }); + return request({ + url: `/api/mapSkin`, + method: 'get', + params: params + }); } /** 添加皮肤*/ export function addSkinStyle(data) { - return request({ - url: `/api/mapSkin`, - method: 'post', - data: data - }); + return request({ + url: `/api/mapSkin`, + method: 'post', + data: data + }); } /** 删除皮肤*/ export function delSkinStyle(id) { - return request({ - url: `/api/mapSkin/${id}`, - method: 'delete' - }); + return request({ + url: `/api/mapSkin/${id}`, + method: 'delete' + }); } /** 查询地图皮肤 */ export function querySkinStyle(id) { - return request({ - url: `/api/mapSkin/${id}`, - method: 'get' - }); + return request({ + url: `/api/mapSkin/${id}`, + method: 'get' + }); } /** 修改地图皮肤*/ export function updateSkinStyle(data) { - return request({ - url: `/api/mapSkin/${data.id}`, - method: 'put', - data: data - }); + return request({ + url: `/api/mapSkin/${data.id}`, + method: 'put', + data: data + }); } /** 通过皮肤Code更新地图皮肤*/ export function updateSkinStyleByCode(data) { - return request({ - url: `/api/mapSkin/${data.code}/update`, - method: 'put', - data: data - }); + return request({ + url: `/api/mapSkin/${data.code}/update`, + method: 'put', + data: data + }); } /** 查询皮肤是否存在*/ export function querySkinStyleExistByCode(code) { - return request({ - url: `/api/mapSkin/${code}/exist`, - method: 'get' - }); + return request({ + url: `/api/mapSkin/${code}/exist`, + method: 'get' + }); } /** 获取皮肤列表*/ export function getSkinStyleList() { - return request({ - url: `/api/mapSkin/list`, - method: 'get' - }); + return request({ + url: `/api/mapSkin/list`, + method: 'get' + }); } diff --git a/src/api/quest.js b/src/api/quest.js new file mode 100644 index 000000000..6491751e3 --- /dev/null +++ b/src/api/quest.js @@ -0,0 +1,40 @@ +import request from '@/utils/request'; + +/** 分页查找仿真任务*/ +export function getQuestPageList(params) { + return request({ + url: `/api/quest/paging`, + method: 'get', + params: params + }); +} +/** 创建任务 */ +export function createQuest(data) { + return request({ + url: `/api/quest`, + method: 'post', + data + }); +} +/** 根据任务id删除任务 */ +export function deleteQuest(id) { + return request({ + url: `/api/quest/${id}`, + method: 'delete' + }); +} +/** 根据id查询任务基础信息 */ +export function getQuestById(id) { + return request({ + url: `/api/quest/${id}`, + method: 'get', + }); +} +/** 更新任务基本信息 */ +export function updateQuest(id, data) { + return request({ + url: `/api/quest/${id}`, + method: 'put', + data + }); +} diff --git a/src/api/runplan.js b/src/api/runplan.js new file mode 100644 index 000000000..76165e6f0 --- /dev/null +++ b/src/api/runplan.js @@ -0,0 +1,379 @@ +import request from '@/utils/request'; + +/** + * 获取运行图列表 + */ +export function getRunPlanList() { + return request({ + url: '/api/rp/tree', + method: 'get' + }); +} + +/** + * 获取地图速度等级列表 + */ +export function getSpeedLevels(skinStyle) { + return request({ + url: `/api/rp/${skinStyle}/speed`, + method: 'get', + }); +} + +/** + * 新建地图速度等级列表 + */ +export function newSpeedLevels(data) { + return request({ + url: '/api/rp/speed', + method: 'post', + data: data + }); +} + +/** + * 获取运行图的车站列表 + */ +export function getStationList(mapId) { + return request({ + url: `/api/rp/station/${mapId}`, + method: 'get', + }); +} + +/** + * 通过皮肤获取运行图车站列表 + */ +export function getStationListBySkinStyle(skinStyle) { + return request({ + url: `/api/rp/station/${skinStyle}/bySkin`, + method: 'get', + }); +} + +/** + * 创建运行图 + */ +export function newRunPlan(data) { + return request({ + url: '/api/rp', + method: 'post', + data: data + }); +} + +/** + * 查询运行图获取数据 + */ +export function queryRunPlan(planId) { + return request({ + url: `/api/rp/${planId}`, + method: 'get' + }); +} + +/** + * 删除运行图 + */ +export function deleteRunPlan(planId) { + return request({ + url: `/api/rp/${planId}`, + method: 'delete' + }); +} + +/** + * 发布运行图 + */ +export function publishRunPlan(data) { + return request({ + url: `/api/rp/${data.planId}/publish`, + method: 'post', + data: data + }); +} + + +/** + * 导入真实运行图 + */ +export function importRunPlan(data) { + return request({ + url: `/api/rp/${data.skinStyle}/prdPlan`, + method: 'post', + data: data.runPlanList + }); +} + +/** 获取运行图停车点列表*/ +export function getRunPlanStopPointList(skinStyle) { + return request({ + url: `/api/rp/stopPoint/${skinStyle}`, + method: 'get', + }); +} + + +/** 运行图*/ +export function getRpListByMapId(mapId) { + return request({ + url: `/api/rp/${mapId}/list`, + method: 'get' + }); +} + +/** 获取站间运行时间*/ +export function getStationRunning(skinStyle) { + return request({ + url: `/api/rp/${skinStyle}/stationRunning`, + method: 'get' + }); +} + +/** 设置站间运行时间*/ +export function setStationRunning(skinStyle, data) { + return request({ + url: `/api/rp/${skinStyle}/stationRunning`, + method: 'put', + data: data + }); +} + +/** 创建运行图*/ +export function createEmptyPlan(data) { + return request({ + url: `/api/rp`, + method: 'post', + data: data + }); +} + +/** 查询运行图服务号是否存在*/ +export function checkServiceNumberExist({ planId, serviceNumber }) { + return request({ + url: `/api/rp/${planId}/${serviceNumber}/service`, + method: 'get' + }); +} + +/** 查询交路列表*/ +export function getRoutingList(planId) { + return request({ + url: `/api/rp/${planId}/routingList`, + method: 'get' + }); +} + +/** 根据交路查询交路区段列表*/ +export function querySectionListByRouting({ planId, routingCode }) { + return request({ + url: `/api/rp/${planId}/${routingCode}/routingSectionList`, + method: 'get' + }); +} + +/** 有效性检查*/ +export function planEffectiveCheck(planId) { + return request({ + url: `/api/rp/${planId}/check`, + method: 'get' + }); +} + +/** 增加计划*/ +export function addPlanService(data) { + return request({ + url: `/api/rp/${data.planId}/service`, + method: 'post', + data: data, + }); +} + +/** 删除计划*/ +export function deletePlanService(data) { + return request({ + url: `/api/rp/${data.planId}/service/${data.serviceNumber}`, + method: 'delete' + }); +} + +/** 复制计划*/ +export function duplicateService(data) { + return request({ + url: `/api/rp/${data.planId}/service/${data.serviceNumber}`, + method: 'post', + data: data + }); +} + +/** 修改计划*/ +// export function updatePlanService(data) { +// return request({ +// url: `/api/rp/${data.planId}/service/${data.serviceNumber}`, +// method: 'put', +// data: data +// }) +// } + +/** 增加任务*/ +export function addPlanTrip(data) { + return request({ + url: `/api/rp/${data.planId}/${data.serviceNumber}/trip`, + method: 'post', + data: data, + }); +} + +/** 删除任务*/ +export function deletePlanTrip(params) { + return request({ + url: `/api/rp/${params.planId}/trip/${params.SDTNumber}`, + method: 'delete', + params: { deleteBefore: params.deleteBefore } + }); +} + +/** 修改任务*/ +export function updatePlanTrip(data) { + return request({ + url: `/api/rp/${data.planId}/trip/${data.SDTNumber}`, + method: 'put', + data: data + }); +} + +/** 根据车次号查询交路*/ +export function getRoutingBySDTNumber(params) { + return request({ + url: `/api/rp/${params.planId}/routing`, + method: 'get', + params: { + SDTNumber: params.SDTNumber + } + }); +} + + +/** 运行图仿真测试*/ +export function runPlanNotify({ planId }) { + return request({ + url: `/api/rp/${planId}/simulation`, + method: 'get', + }); +} + +/** 获取运行计划模板列表*/ +export function runPlanTemplateList(params) { + return request({ + url: '/api/runPlan/template', + method: 'get', + params: params + }); +} + +/** 删除运行图模板*/ +export function deleteRunPlanTemplate(planId) { + return request({ + url: `/api/runPlan/template/${planId}`, + method: 'delete' + }); +} + +/** 生成通用每日运行图*/ +export function generateCommonRunPlanEveryDay(planId) { + return request({ + url: `/api/runPlan/template/generate/${planId}`, + method: 'post' + }); +} + +/** 生成用户每日运行图*/ +export function generateUserRunPlanEveryDay(planId, group) { + return request({ + url: `/api/runPlan/daily/privilege/${planId}?group=${group}`, + method: 'post' + }); +} +/** 获取运行计划每日列表*/ +export function runPlanEveryDayList(params) { + return request({ + url: '/api/runPlan/daily', + method: 'get', + params: params + }); +} + +/** 删除运行图每日计划*/ +export function deleteRunPlanEveryDay(planId) { + return request({ + url: `/api/runPlan/daily/${planId}`, + method: 'delete' + }); +} + +/** 获取地图运行图的车次号*/ +export function getPublishMapTrainNos(skinStyle) { + return request({ + url: `/api/runPlan/daily/${skinStyle}/trainNos`, + method: 'get' + }); +} + +/** 获取服务号、车组号*/ +export function getPublishMapTrainServerNos(skinStyle) { + return request({ + url: `/api/runPlan/daily/${skinStyle}/serverNos`, + method: 'get' + }); +} + +/** 分页查询加载计划*/ +export function getRunPlanLoadList(params) { + return request({ + url: `/api/runPlan/daily/runPlanLoad`, + method: 'get', + params: params + }); +} + +/** 创建加载计划*/ +export function createRunPlanLoad(data) { + return request({ + url: `/api/runPlan/daily/runPlanLoad`, + method: 'post', + data: data + }); +} + +/** 管理创建通用加载计划*/ +export function createRunPlanCommon(data) { + return request({ + url: `/api/runPlan/daily/runPlanLoad/common`, + method: 'post', + data: data + }); +} + +/** 删除加载计划*/ +export function deleteRunPlanLoad(planId) { + return request({ + url: `/api/runPlan/daily/runPlanLoad/${planId}`, + method: 'delete' + }); +} + +/** 查询模板运行图数据*/ +export function queryRunPlanTemplate(planId) { + return request({ + url: `/api/runPlan/template/${planId}`, + method: 'get' + }) +} + +/** 查询当日运行图数据*/ +export function queryRunPlanDaily(planId) { + return request({ + url: `/api/runPlan/daily/${planId}`, + method: 'get' + }) +} \ No newline at end of file diff --git a/src/api/simulation.js b/src/api/simulation.js new file mode 100644 index 000000000..8deaed2ec --- /dev/null +++ b/src/api/simulation.js @@ -0,0 +1,370 @@ +import request from '@/utils/request'; + +/** 获取故障规则列表*/ +export function getFailureGenerateRules(params) { + return request({ + url: `/api/simulation/failureGenerateRules`, + method: 'get', + params: params + }); +} + +/** 设置自动故障*/ +export function setFailureMode(data, group) { + return request({ + url: `/api/simulation/${group}/failureMode`, + method: 'post', + data: data, + }); +} + + +/** + * 仿真系统按计划行车 + */ +export function runDiagramStart(params, group) { + return request({ + url: `/api/simulation/${group}/start`, + method: 'put', + params: params + }); +} + +/** + * 仿真系统结束计划行车 + */ +export function runDiagramOver(group) { + return request({ + url: `/api/simulation/${group}/over`, + method: 'put', + }); +} + +/** + * 退出仿真系统 + */ +export function runDiagramQuit(group) { + return request({ + url: `/api/simulation/${group}/quit`, + method: 'put', + }); +} + +/** 获取仿真系统时间*/ +export function runDiagramGetTime(group) { + return request({ + url: `/api/simulation/${group}/systemTime`, + method: 'get' + }); +} + +// 查看是否开始按计划行车 +export function runDiagramIsStart(group) { + return request({ + url: `/api/simulation/${group}/isRunPlanStart`, + method: 'get' + }); +} + + +/** + * 仿真系统CBTC + * @param {*} mapId + */ +export function simulationNotify({ mapId, code }) { + return request({ + url: `/api/simulation/${mapId}/${code}`, + method: 'get' + }); +} + +/** + * 大屏系统CBTC + * @param {*} mapId + */ +export function bitScreenNotify({ mapId }) { + return request({ + url: `/api/simulation/bigScreen/${mapId}`, + method: 'get' + }); +} + +/** + * 实训系统CBTC + * @param {*} data + */ +export function trainingNotify({ trainingId }) { + return request({ + url: `/api/simulation/training/${trainingId}`, + method: 'get' + }); +} + +/** + * 考试系统CBTC + * @param {*} data + */ +export function examNotify({ examId }) { + return request({ + url: `/api/simulation/exam/${examId}`, + method: 'get' + }); +} + + +/**获取用户实训列表*/ +export function getSimulationList(data) { + return request({ + url: `/api/simulation/stats`, + method: 'get', + params: data + }); +} + +/**添加用户仿真数据*/ +export function postSimulationStats(data) { + return request({ + url: `/api/simulation/stats`, + method: 'post', + data: data + }); +} + +/**更新用户仿真数据*/ +export function putSimulationStats(data) { + return request({ + url: `/api/simulation/${data.id}/stats`, + method: 'put', + data: data + }); +} + +/**删除用户仿真数据*/ +export function deleteSimulationStats(statsId) { + return request({ + url: `/api/simulation/${statsId}`, + method: 'delete', + }); +} + + +/** 获取用户鼠标左键选中的设备信息*/ +export function letfMouseSelectDevice(deviceCode, group) { + return request({ + url: `/api/simulation/${group}/device/${deviceCode}`, + method: 'get', + }); +} + +/** 获取每日运行图*/ +export function getEveryDayRunPlanData(group) { + return request({ + url: `/api/simulation/${group}/runPlan`, + method: 'get', + }); +} + +/** 生成手机同步仿真二维码*/ +// export function getSimulationQrCodeUrl(group) { +// return request({ +// url: `/api/simulation/${group}/qrCode`, +// method: 'post' +// }); +// } + +/** 录制脚本仿真*/ +export function scriptRecordNotify(questId) { + return request({ + url: `/api/simulation/questRecord/${questId}`, + method: 'get' + }) +} + +/** 保存剧本背景*/ +export function saveScriptScenes(group) { + return request({ + url: `/api/simulation/${group}/questRecord/scenes`, + method: 'post' + }) +} + + +/** 保存录制任务数据*/ +export function saveScriptData(group) { + return request({ + url: `/api/simulation/${group}/questRecord`, + method: 'post', + }); +} + + + +/** 清除仿真剧本数据*/ +export function dumpScriptData(scriptId) { + return request({ + url: `/api/simulation/${scriptId}/clearScriptRecord`, + method: 'post' + }); +} + + +/** 查询录制剧本步骤*/ +export function queryScriptStep(group) { + return request({ + url: `/api/simulation/${group}/scriptRecordStage`, + method: 'get' + }); +} + +/** 获取指定时间里可加载列车的个数*/ +export function getDesignatedTimeTrainNum(params, group) { + return request({ + url: `/api/simulation/${group}/plan/trainNum`, + method: 'get', + params + }) +} + +/** 根据成员角色查询设备列表*/ +export function getDevicesByRole(group, params) { + return request({ + url: `/api/simulation/${group}/role/devices`, + method: 'get', + params + }) +} +/** 获取任务录制的成员角色列表 */ +export function getMembersByGroup(group) { + return request({ + url: `/api/simulation/${group}/questRecord/members`, + method: 'get', + }) +} + +/** 添加任务角色成员 */ +export function postQuestMember(group, data) { + return request({ + url: `/api/simulation/${group}/questRecord/member`, + method: 'post', + data + }) +} + +/** 删除任务角色成员 */ +export function deleteQuestMember(group, memberId) { + return request({ + url: `/api/simulation/${group}/questRecord/member/${memberId}`, + method: 'delete', + }) +} + +// /** 添加/修改任务角色行为 */ +export function postMemberBehavior(group, memberId, data) { + return request({ + url: `/api/simulation/${group}/questRecord/${memberId}/behavior`, + method: 'post', + data + }) +} +/** 删除任务角色行为 */ +export function deleteMemberBehavior(group, memberId, behaviorId) { + return request({ + url: `/api/simulation/${group}/questRecord/${memberId}/behavior/${behaviorId}`, + method: 'delete' + }) +} +/** 删除任务角色行为动作 */ +export function deleteMemberBehaviorAction(group, memberId, behaviorId, actionId) { + return request({ + url: `/api/simulation/${group}/questRecord/${memberId}/behavior/${behaviorId}/action/${actionId}`, + method: 'delete' + }) +} +/** 添加/修改任务角色行为动作 */ +export function postMemberBehaviorAction(group, memberId, behaviorId, data) { + return request({ + url: `/api/simulation/${group}/questRecord/${memberId}/behavior/${behaviorId}/action`, + method: 'post', + data + }) +} +/** 添加/修改任务目标条件*/ +export function postTargetConditionAction(group, data) { + return request({ + url: `/api/simulation/${group}/questRecord/targetCondition`, + method: 'post', + data + }) +} +/** 删除任务目标条件*/ +export function deleteTargetConditionAction(group, tcId) { + return request({ + url: `/api/simulation/${group}/questRecord/targetCondition/${tcId}`, + method: 'delete' + }) +} + +/** 分页查询存在的仿真 */ +export function getExistingSimulation(params) { + return request({ + url: `/api/simulation/manage/page`, + method: 'get', + params, + }); +} + +/** 删除存在的仿真 */ +export function deleteExistingSimulation(group) { + return request({ + url: `/api/simulation/manage/${group}`, + method: 'delete', + }); +} +/** 根据设备类型获取设备列表 */ +export function getDeviceCodeByDeviceType(group, params) { + return request({ + url: `/api/simulation/${group}/deviceType/devices`, + method: 'get', + params + }); +} +/** 根据设备类型获取设备条件列表 */ +export function getDeviceCoditionByDeviceType(params) { + return request({ + url: `/api/simulation/deviceType/conditions`, + method: 'get', + params + }); +} +/** 获取任务录制的数据*/ +export function getQuestRecord(group) { + return request({ + url: `/api/simulation/${group}/questRecord`, + method: 'get', + }); +} + + +/** 加载任务*/ +export function loadQuest(questId, group) { + return request({ + url: `/api/simulation/${group}/quest/${questId}`, + method: 'post', + }); +} + +/** 退出任务*/ +export function quitQuest(group) { + return request({ + url: `/api/simulation/${group}/quest`, + method: 'put' + }) +} + +/** 根据group获取仿真对象*/ +export function getSimulationInfo(group) { + return request({ + url: `/api/simulation/${group}`, + method: 'get' + }) +} \ No newline at end of file diff --git a/src/api/simulationRecord.js b/src/api/simulationRecord.js new file mode 100644 index 000000000..f5e68fb7f --- /dev/null +++ b/src/api/simulationRecord.js @@ -0,0 +1,67 @@ + +import request from '@/utils/request'; + +/** 分页查询仿真记录*/ +export function getSimulationRelpayList(params) { + return request({ + url: `/api/simulationRecord`, + method: 'get', + params: params + }); +} + +// 删除回放 +export function delSimulationRecord(replayId) { + return request({ + url: `/api/simulationRecord/${replayId}`, + method: 'delete', + }); +} + +/** 回放*/ +export function simulationRelpay(replayId) { + return request({ + url: `/api/simulationRecord/${replayId}/playBack`, + method: 'get' + }); +} + +// 结束回放 +export function putsSimulationRecord(replayId) { + return request({ + url: `/api/simulationRecord/${replayId}/over`, + method: 'put', + }); +} + +// 暂停回放 +export function putsSimulationRecordPause(replayId) { + return request({ + url: `/api/simulationRecord/${replayId}/pause`, + method: 'put', + }); +} + +// 播放回放 +export function putsSimulationRecordPlay(replayId) { + return request({ + url: `/api/simulationRecord/${replayId}/play`, + method: 'put', + }); +} + +// 设置速度回放 +export function putsSimulationRecordPlaySpeed(replayId, speed) { + return request({ + url: `/api/simulationRecord/${replayId}/playSpeed?playSpeed=${speed}`, + method: 'put', + }); +} + +// 播放 +export function putsSimulationRecordplayTime(replayId, offsetSeconds) { + return request({ + url: `/api/simulationRecord/${replayId}/playTime?offsetSeconds=${offsetSeconds}`, + method: 'put', + }); +} \ No newline at end of file diff --git a/src/api/table.js b/src/api/table.js new file mode 100644 index 000000000..10bac1b1b --- /dev/null +++ b/src/api/table.js @@ -0,0 +1,9 @@ +import request from '@/utils/request'; + +export function getList(params) { + return request({ + url: '/table/list', + method: 'get', + params + }); +} diff --git a/src/api/test.js b/src/api/test.js deleted file mode 100644 index a5b85f944..000000000 --- a/src/api/test.js +++ /dev/null @@ -1,9 +0,0 @@ -import request from '@/utils/request'; - -/** 获取发布地图详细内容*/ -export function getPublishMapDetail(skinStyle) { - return request({ - url: `/api/map/${skinStyle}/details`, - method: 'get' - }); -} \ No newline at end of file diff --git a/src/api/user.js b/src/api/user.js deleted file mode 100644 index dd4041fee..000000000 --- a/src/api/user.js +++ /dev/null @@ -1,99 +0,0 @@ -import request from '@/utils/request'; - - -/** 查询用户参数*/ -export function getUserConfigInfo() { - return request({ - url: '/api/user/config', - method: 'get' - }); -} - -// /** 注册用户*/ -// export function createUserInfo(data) { -// return request({ -// url: '/api/userinfo/create', -// method: 'put', -// data: data -// }); -// } -// /** 根据用户Id获取用户信息*/ -// export function getUserInfoByOpenId(params) { -// return request({ -// url: '/api/userinfo/getByOpenId', -// method: 'get', -// params: params -// }); -// } - -// /** 根据姓名或者手机号查询用户*/ -// export function getUserInfoByNameOrMobile(params) { -// return request({ -// url: '/api/userinfo/nameOrMobile', -// method: 'get', -// params: params -// }); -// } - -// /** 设置用户参数*/ -// export function setUserConfigInfo(data) { -// return request({ -// url: '/api/user/config', -// method: 'post', -// data: data -// }); -// } - -// /** 获取销售列表*/ -// export function getSellerList() { -// return request({ -// url: `/api/user/seller`, -// method: 'get' -// }); -// } - -// /** 查询用户列表*/ -// export function getUserList(params) { -// return request({ -// url: `/api/user`, -// method: 'get', -// params: params -// }); -// } - -// /** 模糊查询用户 昵称、名称、手机号*/ -// export function getDimUserList(params) { -// return request({ -// url: `/api/user/fuzzy`, -// method: 'get', -// params: params -// }); -// } - - -// /** 获取用户订阅地图列表*/ -// export function getUserSubscribe(userId) { -// return request({ -// url: `/api/user/subscribe/${userId}`, -// method: 'get' -// }); -// } - -// /** 保存用户订阅地图列表*/ -// export function saveUserSubscribe(data) { -// return request({ -// url: '/api/user/subscribe', -// method: 'post', -// data: data -// }); -// } - - -// // 修改用户权限 -// export function putRoles(data) { -// return request({ -// url: `/api/user/${data.id}/role`, -// method: 'put', -// data: data -// }); -// } diff --git a/src/assets/downSection.png b/src/assets/downSection.png new file mode 100644 index 000000000..58dc1d231 Binary files /dev/null and b/src/assets/downSection.png differ diff --git a/src/assets/home/demon1.jpg b/src/assets/home/demon1.jpg new file mode 100644 index 000000000..5fc72eb33 Binary files /dev/null and b/src/assets/home/demon1.jpg differ diff --git a/src/assets/home/demon2.jpg b/src/assets/home/demon2.jpg new file mode 100644 index 000000000..39b0c7915 Binary files /dev/null and b/src/assets/home/demon2.jpg differ diff --git a/src/assets/home/home1.jpg b/src/assets/home/home1.jpg new file mode 100644 index 000000000..0c79290b5 Binary files /dev/null and b/src/assets/home/home1.jpg differ diff --git a/src/assets/home/home1.png b/src/assets/home/home1.png new file mode 100644 index 000000000..ebc0a0ca5 Binary files /dev/null and b/src/assets/home/home1.png differ diff --git a/src/assets/home/home2.jpg b/src/assets/home/home2.jpg new file mode 100644 index 000000000..1bab106ef Binary files /dev/null and b/src/assets/home/home2.jpg differ diff --git a/src/assets/home/home2.png b/src/assets/home/home2.png new file mode 100644 index 000000000..6cf1cc6d8 Binary files /dev/null and b/src/assets/home/home2.png differ diff --git a/src/assets/home/home3.jpg b/src/assets/home/home3.jpg new file mode 100644 index 000000000..5a53945b6 Binary files /dev/null and b/src/assets/home/home3.jpg differ diff --git a/src/assets/home/home3.png b/src/assets/home/home3.png new file mode 100644 index 000000000..95fb124e7 Binary files /dev/null and b/src/assets/home/home3.png differ diff --git a/src/assets/home/home4.png b/src/assets/home/home4.png new file mode 100644 index 000000000..08d5b13cc Binary files /dev/null and b/src/assets/home/home4.png differ diff --git a/src/assets/home/plan1.png b/src/assets/home/plan1.png new file mode 100644 index 000000000..ab5383aca Binary files /dev/null and b/src/assets/home/plan1.png differ diff --git a/src/assets/home/plan2.png b/src/assets/home/plan2.png new file mode 100644 index 000000000..73247f7ee Binary files /dev/null and b/src/assets/home/plan2.png differ diff --git a/src/assets/home/plan3.png b/src/assets/home/plan3.png new file mode 100644 index 000000000..23fcecf27 Binary files /dev/null and b/src/assets/home/plan3.png differ diff --git a/src/assets/home/tring1.png b/src/assets/home/tring1.png new file mode 100644 index 000000000..9b5db769e Binary files /dev/null and b/src/assets/home/tring1.png differ diff --git a/src/assets/home/tring2.png b/src/assets/home/tring2.png new file mode 100644 index 000000000..e515a4de5 Binary files /dev/null and b/src/assets/home/tring2.png differ diff --git a/src/assets/home/tring3.jpg b/src/assets/home/tring3.jpg new file mode 100644 index 000000000..af23babf2 Binary files /dev/null and b/src/assets/home/tring3.jpg differ diff --git a/src/assets/home/tring4.jpg b/src/assets/home/tring4.jpg new file mode 100644 index 000000000..82a3a9485 Binary files /dev/null and b/src/assets/home/tring4.jpg differ diff --git a/src/assets/home/tring5.jpg b/src/assets/home/tring5.jpg new file mode 100644 index 000000000..8b07d2e57 Binary files /dev/null and b/src/assets/home/tring5.jpg differ diff --git a/src/assets/home/tring6.jpg b/src/assets/home/tring6.jpg new file mode 100644 index 000000000..570155aba Binary files /dev/null and b/src/assets/home/tring6.jpg differ diff --git a/src/assets/jl3d/mmi.png b/src/assets/jl3d/mmi.png new file mode 100644 index 000000000..d39e506f0 Binary files /dev/null and b/src/assets/jl3d/mmi.png differ diff --git a/src/assets/logo.png b/src/assets/logo.png new file mode 100644 index 000000000..08735de1b Binary files /dev/null and b/src/assets/logo.png differ diff --git a/src/assets/logo.svg b/src/assets/logo.svg new file mode 100644 index 000000000..d52bf7e15 --- /dev/null +++ b/src/assets/logo.svg @@ -0,0 +1 @@ +矢量版玖琏LOGO玖琏科技 \ No newline at end of file diff --git a/src/assets/logo1.png b/src/assets/logo1.png new file mode 100644 index 000000000..dbe8da485 Binary files /dev/null and b/src/assets/logo1.png differ diff --git a/src/assets/logo_.png b/src/assets/logo_.png new file mode 100644 index 000000000..e250916ab Binary files /dev/null and b/src/assets/logo_.png differ diff --git a/src/assets/pay_images/AliPayLogo.png b/src/assets/pay_images/AliPayLogo.png new file mode 100644 index 000000000..37123dfeb Binary files /dev/null and b/src/assets/pay_images/AliPayLogo.png differ diff --git a/src/assets/pay_images/AliPayLogoInfo.png b/src/assets/pay_images/AliPayLogoInfo.png new file mode 100644 index 000000000..78685cabc Binary files /dev/null and b/src/assets/pay_images/AliPayLogoInfo.png differ diff --git a/src/assets/pay_images/PayFail.png b/src/assets/pay_images/PayFail.png new file mode 100644 index 000000000..e9ab0dbc6 Binary files /dev/null and b/src/assets/pay_images/PayFail.png differ diff --git a/src/assets/pay_images/PaySuccess.png b/src/assets/pay_images/PaySuccess.png new file mode 100644 index 000000000..e33b05cb2 Binary files /dev/null and b/src/assets/pay_images/PaySuccess.png differ diff --git a/src/assets/pay_images/WePayLogo.png b/src/assets/pay_images/WePayLogo.png new file mode 100644 index 000000000..544e58a7d Binary files /dev/null and b/src/assets/pay_images/WePayLogo.png differ diff --git a/src/assets/pay_images/WePayLogoInfo.png b/src/assets/pay_images/WePayLogoInfo.png new file mode 100644 index 000000000..f2c66bf6c Binary files /dev/null and b/src/assets/pay_images/WePayLogoInfo.png differ diff --git a/src/assets/upSection.png b/src/assets/upSection.png new file mode 100644 index 000000000..c30ac58be Binary files /dev/null and b/src/assets/upSection.png differ diff --git a/src/directives.js b/src/directives.js new file mode 100644 index 000000000..dba79ca51 --- /dev/null +++ b/src/directives.js @@ -0,0 +1,283 @@ +/* eslint-disable no-useless-escape */ +import Vue from 'vue'; +import store from '@/store'; + +/** + *元素获取焦点:v-dialogDrag + * @param {*} el + * @param {*} binding + */ +Vue.directive('focus', { + // 当被绑定的元素插入到 DOM 中时 + inserted: function (el, obj) { + // 这是需要页面刚加载就能进行聚焦操作使用的钩子函数,可以省略的,视具体需求而定 + // 对值进行判断 + if (obj.value) { + // 聚焦元素 + el.focus(); + } + }, + // 当指令所在组件的 VNode 及其子 VNode 全部更新后调用 + // 这是每当绑定的值发生改变时触发的钩子函数 + componentUpdated: function (el, obj) { + if (obj.value) { + el.focus(); + } + } +}); + +/** + *弹窗拖拽:v-dialogDrag + * @param {*} el + * @param {*} binding + * @param {*} vnode + * @param {*} oldvNode + */ +Vue.directive('dialogDrag', { + bind(el) { + const dialogHeaderEl = el.querySelector('.el-dialog__header'); + const dragDom = el.querySelector('.el-dialog'); + dialogHeaderEl.style.cursor = 'move'; + + /** 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);*/ + const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null); + + dialogHeaderEl.onmousedown = (e) => { + /** 鼠标按下,计算当前元素距离可视区的距离*/ + const disX = e.clientX - dialogHeaderEl.offsetLeft; + const disY = e.clientY - dialogHeaderEl.offsetTop; + + /** 获取到的值带px 正则匹配替换*/ + let styL, styT; + + /** 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px*/ + if (sty.left.includes('%')) { + // eslint-disable-next-line no-useless-escape + styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100); + styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100); + } else { + // eslint-disable-next-line no-useless-escape + styL = +sty.left.replace(/\px/g, ''); + // eslint-disable-next-line no-useless-escape + styT = +sty.top.replace(/\px/g, ''); + } + + document.onmousemove = function (e) { + /** 通过事件委托,计算移动的距离*/ + const l = e.clientX - disX; + const t = e.clientY - disY; + + /** 移动当前元素*/ + dragDom.style.left = `${l + styL}px`; + dragDom.style.top = `${t + styT}px`; + + /** 刷新提示标签位置*/ + store.dispatch('training/emitTipFresh'); + + /** 将此时的位置传出去*/ + // binding.value({ x: e.pageX, y: e.pageY }); + }; + + document.onmouseup = function () { + document.onmousemove = null; + document.onmouseup = null; + }; + }; + } +}); + +/** + *弹窗宽度拖大,拖小:dialogDragWidth + * @param {*} el + * @param {*} binding + * @param {*} vnode + * @param {*} oldvNode + */ +Vue.directive('dialogDragWidth', { + bind(el, binding) { + const dragDom = binding.value.$el.querySelector('.el-dialog'); + + el.onmousedown = (e) => { + + /** 鼠标按下,计算当前元素距离可视区的距离*/ + const disX = e.clientX - el.offsetLeft; + + document.onmousemove = function (e) { + /** 移动时禁用默认事件*/ + e.preventDefault(); + + /** 通过事件委托,计算移动的距离*/ + const l = e.clientX - disX; + dragDom.style.width = `${l}px`; + }; + + document.onmouseup = function () { + document.onmousemove = null; + document.onmouseup = null; + }; + }; + } +}); + +/** + * 弹窗拖拽:v-quickEntryDrag + * @param {*} el + * @param {*} binding + * @param {*} vnode + * @param {*} oldvNode + */ +Vue.directive('quickMenuDrag', { + bind(el) { + const dragDom = el; + dragDom.style.cursor = 'move'; + + /** 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);*/ + const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null); + + dragDom.onmousedown = (e) => { + /** 鼠标按下,计算当前元素距离可视区的距离*/ + const disX = e.clientX; + const disY = e.clientY; + + /** 获取到的值带px 正则匹配替换*/ + let styL, styT; + + /** 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px*/ + if (sty.left.includes('%')) { + styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100); + styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100); + } else { + styL = +sty.left.replace(/\px/g, ''); + styT = +sty.top.replace(/\px/g, ''); + } + + document.onmousemove = function (e) { + /** 通过事件委托,计算移动的距离*/ + const l = e.clientX - disX; + const t = e.clientY - disY; + + /** 移动当前元素*/ + dragDom.style.left = `${l + styL}px`; + dragDom.style.top = `${t + styT}px`; + + /** 将此时的位置传出去*/ + // binding.value({ x: e.pageX, y: e.pageY }); + }; + + document.onmouseup = function () { + document.onmousemove = null; + document.onmouseup = null; + }; + }; + } +}); + +/** + *vue-自定义指令-拖拽 v-drag + */ +Vue.directive('drag', { + bind(el) { + const dragDom = el.querySelector('.reminder-box'); + const dragRight = el.querySelector('.drag-right'); + const dragLeft = el.querySelector('.drag-left'); + const dragBottom = el.querySelector('.drag-bottom'); + const dragTop = el.querySelector('.drag-top'); + const dragBody = el.querySelector('.tip-body'); + const body = el.querySelector('.tip-body-box'); + + dragRight.onmousedown = (e) => { + document.onselectstart = function () { + return false; + }; + // 宽度拖拽 + var iEvent = e || event; + var disX = iEvent.clientX; + var disW = dragDom.offsetWidth; + document.onmousemove = function (e) { + + var iEvent = e || event; + if (disW + (iEvent.clientX - disX) > 350) { + dragDom.style.width = disW + (iEvent.clientX - disX) + 'px'; + } + }; + + document.onmouseup = function () { + document.onmousemove = null; + document.onmouseup = null; + document.onselectstart = null; + }; + }; + dragLeft.onmousedown = (e) => { + document.onselectstart = function () { + return false; + }; + // 宽度拖拽 + var iEvent = e || event; + var disX = iEvent.clientX; + var disW = dragDom.offsetWidth; + var OFFLeft = dragDom.offsetLeft; + document.onmousemove = function (e) { + const iEvent = e || event; + const width = disW - (iEvent.clientX - disX); + if (width > 350) { + dragDom.style.width = disW - (iEvent.clientX - disX) + 'px'; + dragDom.style.left = OFFLeft + (iEvent.clientX - disX) + 'px'; + } + }; + + document.onmouseup = function () { + document.onmousemove = null; + document.onmouseup = null; + document.onselectstart = null; + }; + }; + dragBottom.onmousedown = (e) => { + document.onselectstart = function () { + return false; + }; + // 宽度拖拽 + var iEvent = e || event; + var disY = iEvent.clientY; + var disH = dragDom.offsetHeight; + document.onmousemove = function (e) { + var iEvent = e || event; + if (disH + (iEvent.clientY - disY) > 200) { + dragDom.style.height = disH + (iEvent.clientY - disY) + 'px'; + body.style.height = disH + (iEvent.clientY - disY) - 40 + 'px'; + dragBody.style.height = disH + (iEvent.clientY - disY) - 100 + 'px'; + } + }; + + document.onmouseup = function () { + document.onmousemove = null; + document.onmouseup = null; + document.onselectstart = null; + }; + }; + dragTop.onmousedown = (e) => { + document.onselectstart = function () { + return false; + }; + // 宽度拖拽 + var iEvent = e || event; + var disY = iEvent.clientY; + var disH = dragDom.offsetHeight; + var OOFTop = dragDom.offsetTop; + document.onmousemove = function (e) { + var iEvent = e || event; + if (disH - (iEvent.clientY - disY) > 200) { + dragDom.style.height = disH - (iEvent.clientY - disY) + 'px'; + body.style.height = disH - (iEvent.clientY - disY) - 40 + 'px'; + dragBody.style.height = disH - (iEvent.clientY - disY) - 100 + 'px'; + dragDom.style.top = OOFTop + (iEvent.clientY - disY) + 'px'; + } + }; + + document.onmouseup = function () { + document.onmousemove = null; + document.onmouseup = null; + document.onselectstart = null; + }; + }; + } +}); diff --git a/src/jlmap3d/component/Helper.js b/src/jlmap3d/component/Helper.js new file mode 100644 index 000000000..bc51e08be --- /dev/null +++ b/src/jlmap3d/component/Helper.js @@ -0,0 +1,30 @@ +export function Helper(scene){ + //创建辅助网格平面 + // let planeGeometry = new THREE.PlaneBufferGeometry( 40000, 40000 ); + // planeGeometry.rotateX( - Math.PI / 2 ); + // let planeMaterial = new THREE.ShadowMaterial( { opacity: 0.2 } ); + // let plane = new THREE.Mesh( planeGeometry, planeMaterial ); + // plane.position.y = 10; + // plane.receiveShadow = true; + // jlmap3dedit.meshplane = plane; + + // jlmap3dedit.scene.add(plane ); + // planeGeometry.dispose(); + // planeMaterial.dispose(); + //定义全局光 + let ambientLight = new THREE.AmbientLight( 0xC5C1AA, 1.3); + scene.add( ambientLight ); + + let light = new THREE.HemisphereLight( 0xffffff, 0x444444 ); + light.position.set( 0, 200, 0 ); + scene.add( light ); + + // //辅助网格 + // let helper = new THREE.GridHelper( 20000, 10 ); + // helper.position.y = -2; + // helper.material.opacity = 0.25; + // helper.material.transparent = true; + // jlmap3dedit.scene.add( helper ); + + +} diff --git a/src/jlmap3d/component/Materialload.js b/src/jlmap3d/component/Materialload.js new file mode 100644 index 000000000..20379acd1 --- /dev/null +++ b/src/jlmap3d/component/Materialload.js @@ -0,0 +1,36 @@ +export function Materialload(jlmap3dedit){ + + + settexture(jlmap3dedit.materiallist,"red",'../../static/material/signal/1.jpg'); + + settexture(jlmap3dedit.materiallist,"yellow",'../../static/material/signal/2.jpg'); + + settexture( jlmap3dedit.materiallist,"green",'../../static/material/signal/3.jpg'); + + settexture( jlmap3dedit.materiallist,"black",'../../static/material/signal/5.jpg'); + //console.log(jlmap3dedit.materiallist); +} + +function settexture(materiallist,name,textureurl){ + var loader = new THREE.TextureLoader(); + + // 加载一个资源 + loader.load( + // 资源URL + textureurl, + + // onLoad回调 + function ( texture ) { + texture.name = name; + materiallist.push(texture); + }, + + // 目前暂不支持onProgress的回调 + undefined, + + // onError回调 + function ( err ) { + console.error( 'An error happened.' ); + } + ); +} diff --git a/src/jlmap3d/config/Camera.js b/src/jlmap3d/config/Camera.js new file mode 100644 index 000000000..488066c58 --- /dev/null +++ b/src/jlmap3d/config/Camera.js @@ -0,0 +1,7 @@ +export function SetCamera(dom) { + var camera = new THREE.PerspectiveCamera(70, dom.clientWidth/dom.clientHeight, 1, 2000); + camera.position.set( 0, 0, 0 ); + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + return camera; +} diff --git a/src/jlmap3d/config/Render.js b/src/jlmap3d/config/Render.js new file mode 100644 index 000000000..afa9b038b --- /dev/null +++ b/src/jlmap3d/config/Render.js @@ -0,0 +1,6 @@ +export function SetRender(dom) { + var renderer = new THREE.WebGLRenderer({antialias: true}); + renderer.setSize(dom.clientWidth, dom.clientHeight); + renderer.setSize( window.innerWidth, window.innerHeight ); + return renderer; +} diff --git a/src/jlmap3d/config/Scene.js b/src/jlmap3d/config/Scene.js new file mode 100644 index 000000000..c2d61f955 --- /dev/null +++ b/src/jlmap3d/config/Scene.js @@ -0,0 +1,16 @@ +export function SetScene() { + var scene = new THREE.Scene(); + var cubeTextureLoader = new THREE.CubeTextureLoader(); + + cubeTextureLoader.setPath( '../../static/skybox/city/' ); + + var cubeTexture = cubeTextureLoader.load( [ + 'px.jpg', 'nx.jpg', + 'py.jpg', 'ny.jpg', + 'pz.jpg', 'nz.jpg', + ] ); + + scene.background = cubeTexture; + cubeTexture.dispose(); + return scene; +} diff --git a/src/jlmap3d/connect/TrainingConnect.js b/src/jlmap3d/connect/TrainingConnect.js new file mode 100644 index 000000000..6452115b3 --- /dev/null +++ b/src/jlmap3d/connect/TrainingConnect.js @@ -0,0 +1,685 @@ +import StompClient from '@/utils/sock'; + +import { getTrainingCbtcDemon, runDiagramStart, runDiagramOver, setTrainingCbtcInitTime } from '@/api/simulation'; + +import { creatSubscribe, clearSubscribe, displayTopic, screenTopic, } from '@/utils/stomp'; +import { handleToken } from '@/utils/auth'; +import router from '@/router'; + +//定于仿真socket接口 +export function Jlmap3dSubscribe(jlmap3d) { + + let scope = this; + this.map = null; + + var trainlisttest = null; + var sectionlist = null; + var signallist = null; + var stationstandlist = null; + var sectionlist = null; + var materials = null; + var actions = null; + var scenes = null; + + var code = null; + + var drivingcode = null; + var drivingspeed = null; + var drivingaptspeed = null; + + let driverswitch = false; + + let stoptimer = null; + let num = 30; + let pointstand = null; + + //run as plane = 01; + //reset = 02; + var datatype = "00"; + this.teststomp = new StompClient(); + this.topic = "/user/queue/simulation/jl3d"; + let header = {'X-Token': handleToken() }; + + this.updatamap = function(mapdata,materiallist,nowaction,scene){ + //console.log(mapdata); + + scope.map = mapdata; + trainlisttest = this.map.trainlisttest; + sectionlist = this.map.sectionlist; + signallist = this.map.signallist; + stationstandlist = this.map.stationstandlist; + sectionlist = this.map.sectionlist; + materials = materiallist; + scenes = scene; + actions = nowaction; + + } + + this.socketon = function(topic){ + try { + //console.log("teststomp"); + scope.teststomp.subscribe(topic, callback,header); + } catch (error) { + console.error('websocket订阅失败'); + } + + } + + this.socketoff = function(topic){ + scope.teststomp.unsubscribe(topic); + for(let i=0;i=0;i--){ + if(data.body[i]._type == "Train"){ + + //console.log(data.body[i]); + //遍历列车对象组 + if(trainlisttest){ + code = data.body[i].code; + //剔除不显示的车 + //找到对应列车 + + if( trainlisttest.list[code]){ + if(code == drivingcode){ + driverswitch = data.body[i].runMode; + jlmap3d.updatetrainnum(data.body[i].groupNumber); + // console.log(trainlisttest.list[code].progress); + let syncdata = { + type:"Train", + code:code, + sectionCode:data.body[i].sectionCode, + percent:0, + }; + + if(trainlisttest.list[code].isStandTrack == true && trainlisttest.list[code].progress>0.95 && trainlisttest.list[code].speed<=0){ + if(data.body[i].directionType == "02"){ + syncdata.percent = 1-(sectionlist.sections.datalist[data.body[i].sectionCode].rstop/trainlisttest.list[code].len); + }else{ + syncdata.percent = sectionlist.sections.datalist[data.body[i].sectionCode].lstop/trainlisttest.list[code].len; + } + scope.teststomp.send("/app/topic/simulation/wgu3d",syncdata); + }else{ + if(data.body[i].directionType == "02"){ + syncdata.percent = trainlisttest.list[code].progress; + }else{ + syncdata.percent = 1 - trainlisttest.list[code].progress; + } + scope.teststomp.send("/app/topic/simulation/wgu3d",syncdata); + } + + if(data.body[i].parkRemainTime>0){ + jlmap3d.updatestoptime("停站时间:"+parseInt(data.body[i].parkRemainTime)); + }else{ + if(data.body[i].runMode == "02"){ + jlmap3d.updatestoptime("列车自动驾驶中"); + }else{ + if(trainlisttest.list[code].isStandTrack == true && trainlisttest.list[code].progress>0.95 && trainlisttest.list[code].speed<=0){ + // if(stoptimer){ + // + // }else{ + // stoptimer = setInterval(function(){ + // if(num >=0){ + // jlmap3d.updatestoptime("停站时间:"+num); + // num --; + // } + // },1000); + // } + + }else{ + jlmap3d.updatestoptime("列车人工驾驶中"); + } + + } + } + // if(trainlisttest.list[code].speed > 0){ + // if(stoptimer){ + // clearInterval( stoptimer ); + // stoptimer = null; + // num = 30; + // } + // } + + } + trainlisttest.list[code].runMode = data.body[i].runMode; + //车门开关验证 + // if(data.body[i].directionType == "02"){ + if(trainlisttest.list[code].doorStatus != data.body[i].doorStatus && data.body[i].doorStatus == "01"){ + //console.log("close"); + trainlisttest.list[code].doorStatus = "01"; + for(let an=actions[code].top.length-1;an>=0;an--){ + actions[code].top[an].reset(); + actions[code].top[an].time = actions[code].top[an]._clip.duration; + actions[code].top[an].timeScale = -1; + actions[code].top[an].play(); + } + }else if(trainlisttest.list[code].doorStatus != data.body[i].doorStatus && data.body[i].doorStatus == "02"){ + //console.log("open"); + trainlisttest.list[code].doorStatus = "02"; + for(let an=actions[code].top.length-1;an>=0;an--){ + actions[code].top[an].reset(); + actions[code].top[an].time = 0; + actions[code].top[an].timeScale = 1; + actions[code].top[an].play(); + } + } + // }else{ + // if(trainlisttest.list[code].doorStatus != data.body[i].doorStatus && data.body[i].doorStatus == "01"){ + // //console.log("close"); + // trainlisttest.list[code].doorStatus = "01"; + // for(let an=actions[code].down.length-1;an>=0;an--){ + // actions[code].down[an].reset(); + // actions[code].down[an].time = actions[code].top[an]._clip.duration; + // actions[code].down[an].timeScale = -1; + // actions[code].down[an].play(); + // } + // }else if(trainlisttest.list[code].doorStatus != data.body[i].doorStatus && data.body[i].doorStatus == "02"){ + // //console.log("open"); + // trainlisttest.list[code].doorStatus = "02"; + // for(let an=actions[code].down.length-1;an>=0;an--){ + // actions[code].down[an].reset(); + // actions[code].down[an].time = 0; + // actions[code].down[an].timeScale = 1; + // actions[code].down[an].play(); + // } + // } + // } + //遍历获取所在轨道 + + if(trainlisttest.list[code].dispose != data.body[i].dispose && data.body[i].dispose == false){ + + if(sectionlist.sections.datalist[data.body[i].sectionCode].code){ + trainlisttest.group.add(trainlisttest.list[code]); + trainlisttest.list[code].position.y = 0; + trainlisttest.list[code].progress = 0; + trainlisttest.list[code].oldoffset = data.body[i].sectionOffsetPercent; + trainlisttest.list[code].dispose = false; + trainlisttest.list[code].nowcode = data.body[i].sectionCode; + trainlisttest.list[code].nextcode = data.body[i].nextSectionCode; + let vexlist = []; + let endrotation = null; + + if(data.body[i].directionType == "02"){//向右 + let offset = null; + let rotaposx = null; + + if(sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x>sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x){ + offset = sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x-sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x; + rotaposx = sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x+offset*data.body[i].sectionOffsetPercent; + }else{ + offset = sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x-sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x; + rotaposx = sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x+offset*data.body[i].sectionOffsetPercent; + } + + let rotaposz = sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].z; + trainlisttest.list[code].rotation.y = 0; + trainlisttest.list[code].position.x = rotaposx; + trainlisttest.list[code].position.y = 0; + for(let tl=0;tl<6;tl++){ + trainlisttest.list[code].children[tl].position.z = rotaposz; + } + if(sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x>sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x){ + vexlist.push(new THREE.Vector3(rotaposx,0,rotaposz)); + for(let m=sectionlist.sections.datalist[data.body[i].sectionCode].rail.length-1;m>=0;m--){ + if(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m].x>rotaposx){ + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + + } + }else{ + vexlist.push(new THREE.Vector3(rotaposx,0,rotaposz)); + for(let m=0;mrotaposx){ + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + } + } + trainlisttest.list[code].status = "02"; + + }else if(data.body[i].directionType == "03"){//向左 + + trainlisttest.list[code].dispose = false; + trainlisttest.group.add(trainlisttest.list[code]); + let offset = null; + let rotaposx = null; + + if(sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x>sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x){ + + offset = sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x-sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x; + rotaposx = sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x+offset*data.body[i].sectionOffsetPercent; + }else{ + + offset = sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x-sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x; + rotaposx = sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x+offset*data.body[i].sectionOffsetPercent; + } + let rotaposz = sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].z; + trainlisttest.list[code].rotation.y = Math.PI; + trainlisttest.list[code].position.x = rotaposx; + trainlisttest.list[code].position.y = 0; + for(let tl=0;tl<6;tl++){ + trainlisttest.list[code].children[tl].position.z = rotaposz; + } + if(data.body[i].groupNumber == "001"){ + } + + if(sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x=0;m--){ + if(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m].xsectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x){ + vexlist.push(new THREE.Vector3(trainlisttest.list[code].position.x,0,trainlisttest.list[code].children[0].matrixWorld.elements[14])); + for(let m=sectionlist.sections.datalist[data.body[i].sectionCode].rail.length-1;m>=0;m--){ + if(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m].x>trainlisttest.list[code].position.x){ + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + } + }else{ + vexlist.push(new THREE.Vector3(trainlisttest.list[code].position.x,0,trainlisttest.list[code].children[0].matrixWorld.elements[14])); + for(let m=0;mtrainlisttest.list[code].position.x){ + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + } + } + }else{ + if(sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x>sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x){ + for(let m=sectionlist.sections.datalist[data.body[i].sectionCode].rail.length-1;m>=0;m--){ + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + }else{ + for(let m=0;m=0;m--){ + if(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m].x=0;m--){ + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + + }else{ + for(let m=0;m=0;i--){ + //0xFFFFFF + //0xCD0000 红 + //0xEEEE00 黄 + //0x32CD32 绿 + if(data.body[i]._type == "Signal"){ + if(signallist){ + signalupdate(data.body[i]); + } + } + + if(data.body[i]._type == "StationStand"){ + if(actions){ + standupdate(data.body[i]); + } + } + + if(data.body[i]._type == "Switch"){ + if(sectionlist){ + switchupdate(data.body[i]); + } + } + + } + } + } + + function standupdate(data){ + code = data.code; + if( actions[code]){ + if(data.screenDoorOpenStatus == "02" && actions[code].status == "01"){ + actions[code].status = "00"; + } + if(data.screenDoorOpenStatus == "02" && actions[code].status == "00"){ + actions[code].status = "02"; + actions[code].action.reset(); + actions[code].action.time = 0; + actions[code].action.timeScale = 1; + actions[code].action.play(); + } + + if(data.screenDoorOpenStatus == "01" && actions[code].status == "02"){ + actions[code].status = "00"; + } + if(data.screenDoorOpenStatus == "01" && actions[code].status == "00"){ + actions[code].status = "01"; + actions[code].action.reset(); + actions[code].action.time = actions[code].action._clip.duration; + actions[code].action.timeScale = -1; + actions[code].action.play(); + } + } + + + } + + function signalupdate(data){ + code = data.code; + if(data.lightType == "01"){ + if(signallist.list[code].mesh.code){ + signallist.list[code].mesh.status = data.status; + + //55 + //33 + //77 + //关闭 + if(data.status == "01"){ + signallist.list[code].mesh.children[0].material.map = materials[3]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[0]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[3]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + //开放 + if(data.status == "02"){ + + if(data.switchLocateType == "01"){ + signallist.list[code].mesh.children[0].material.map = materials[2]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[3]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[3]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + + if(data.switchLocateType == "02"){ + signallist.list[code].mesh.children[0].material.map = materials[3]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[3]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[1]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + + } + //引导 + if(data.status == "03"){ + signallist.list[code].mesh.children[0].material.map = materials[3]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[0]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[1]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + //封锁 + if(data.status == "04"){ + signallist.list[code].mesh.children[0].material.map = materials[3]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[3]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[3]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + //故障 + if(data.status == "05"){ + signallist.list[code].mesh.children[0].material.map = materials[3]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[3]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[3]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + + } + + }else if(data.lightType == "01"){ + if(signallist.list[code].mesh.code){ + signallist.list[code].mesh.children[0].material.map = materials[3]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[3]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[3]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + + } + + } + + function switchupdate(data){ + code = data.code; + for(let j=sectionlist.switchs.modellist.length-1;j>=0;j--){ + if(sectionlist.switchs.modellist[j].code == code){ + if(sectionlist.switchs.modellist[j].locateType != data.locateType){ + if(data.locateType == "02"){ + if(actions[sectionlist.switchs.modellist[j].code]){ + sectionlist.switchs.modellist[j].locateType = data.locateType; + actions[sectionlist.switchs.modellist[j].code].reset(); + actions[sectionlist.switchs.modellist[j].code].time = 0; + actions[sectionlist.switchs.modellist[j].code].timeScale = 1; + actions[sectionlist.switchs.modellist[j].code].play(); + } + }else if(data.locateType == "01"){ + if(actions[sectionlist.switchs.modellist[j].code]){ + sectionlist.switchs.modellist[j].locateType = data.locateType; + actions[sectionlist.switchs.modellist[j].code].reset(); + actions[sectionlist.switchs.modellist[j].code].time = actions[sectionlist.switchs.modellist[j].code]._clip.duration; + actions[sectionlist.switchs.modellist[j].code].timeScale = -1; + actions[sectionlist.switchs.modellist[j].code].play(); + } + } + } + j = 0; + } + } + } + + +} diff --git a/src/jlmap3d/connect/TrainingConnectTest.js b/src/jlmap3d/connect/TrainingConnectTest.js new file mode 100644 index 000000000..2a683b12e --- /dev/null +++ b/src/jlmap3d/connect/TrainingConnectTest.js @@ -0,0 +1,452 @@ +import StompClient from '@/utils/sock'; +import { displayTopic } from '@/utils/stomp'; +import { handleToken } from '@/utils/auth'; +import router from '@/router'; + +//定于仿真socket接口 +export function Jlmap3dSubscribe(worker) { + + let scope = this; + this.map = null; + + let webworker = worker; + var trainlisttest = null; + var sectionlist = null; + var signallist = null; + var stationstandlist = null; + var sectionlist = null; + var materials = null; + var actions = null; + var scenes = null; + + var code = null; + + this.teststomp = new StompClient(); + this.topic = displayTopic; + let header = { group: router.currentRoute.query.group, 'X-Token': handleToken() }; + + this.updatamap = function (mapdata, materiallist, nowaction, scene) { + //console.log(mapdata); + scope.map = mapdata; + trainlisttest = this.map.trainlisttest; + sectionlist = this.map.sectionlist; + signallist = this.map.signallist; + stationstandlist = this.map.stationstandlist; + sectionlist = this.map.sectionlist; + materials = materiallist; + scenes = scene; + actions = nowaction; + } + + this.socketon = function (topic) { + try { + //console.log("teststomp"); + scope.teststomp.subscribe(topic, callback, header); + } catch (error) { + console.error('websocket订阅失败'); + } + + } + + this.socketoff = function (topic) { + scope.teststomp.unsubscribe(topic); + } + + // 仿真socket接口回调函数 + function callback(Response) { + //对象化数据 + let data = JSON.parse(Response.body); + //遍历后台数据 + + //判断消息类型 + // if(data.type == "Simulation_TrainPosition"){ + // + // + // } + if (data.type == "Simulation_DeviceStatus") { + + if (data.body.length < 200) { + + for (let i = data.body.length - 1; i >= 0; i--) { + + if (data.body[i]._type == "Train") { + + //console.log(data.body[i]); + //遍历列车对象组 + //console.log(data.body[i]); + if (trainlisttest) { + code = data.body[i].code; + //剔除不显示的车 + //找到对应列车 + + if (trainlisttest.list[code]) { + //车门开关验证 + // if(data.body[i].directionType == "02"){ + if (trainlisttest.list[code].doorStatus != data.body[i].doorStatus && data.body[i].doorStatus == "01") { + //console.log("close"); + trainlisttest.list[code].doorStatus = "01"; + for (let an = actions[code].top.length - 1; an >= 0; an--) { + actions[code].top[an].reset(); + actions[code].top[an].time = actions[code].top[an]._clip.duration; + actions[code].top[an].timeScale = -1; + actions[code].top[an].play(); + } + } else if (trainlisttest.list[code].doorStatus != data.body[i].doorStatus && data.body[i].doorStatus == "02") { + //console.log("open"); + trainlisttest.list[code].doorStatus = "02"; + for (let an = actions[code].top.length - 1; an >= 0; an--) { + actions[code].top[an].reset(); + actions[code].top[an].time = 0; + actions[code].top[an].timeScale = 1; + actions[code].top[an].play(); + } + } + // }else{ + // if(trainlisttest.list[code].doorStatus != data.body[i].doorStatus && data.body[i].doorStatus == "01"){ + // //console.log("close"); + // trainlisttest.list[code].doorStatus = "01"; + // for(let an=actions[code].down.length-1;an>=0;an--){ + // actions[code].down[an].reset(); + // actions[code].down[an].time = actions[code].top[an]._clip.duration; + // actions[code].down[an].timeScale = -1; + // actions[code].down[an].play(); + // } + // }else if(trainlisttest.list[code].doorStatus != data.body[i].doorStatus && data.body[i].doorStatus == "02"){ + // //console.log("open"); + // trainlisttest.list[code].doorStatus = "02"; + // for(let an=actions[code].down.length-1;an>=0;an--){ + // actions[code].down[an].reset(); + // actions[code].down[an].time = 0; + // actions[code].down[an].timeScale = 1; + // actions[code].down[an].play(); + // } + // } + // } + //遍历获取所在轨道 + if (trainlisttest.list[code].dispose != data.body[i].dispose && data.body[i].dispose == false) { + trainlisttest.group.add(trainlisttest.list[code]); + + if (sectionlist.sections.datalist[data.body[i].sectionCode].code) { + + trainlisttest.list[code].position.y = 0; + trainlisttest.list[code].progress = 0; + trainlisttest.list[code].oldoffset = data.body[i].sectionOffsetPercent; + trainlisttest.list[code].dispose = false; + + + if (data.body[i].directionType == "02") { + trainlisttest.list[code].status = "02"; + trainlisttest.list[code].rotation.y = 0; + } else if (data.body[i].directionType == "03") { + trainlisttest.list[code].status = "03"; + trainlisttest.list[code].rotation.y = Math.PI; + } + + } + + } else if (trainlisttest.list[code].dispose != data.body[i].dispose && data.body[i].dispose == true) { + trainlisttest.group.remove(trainlisttest.list[code]); + trainlisttest.list[code].progress = null; + trainlisttest.list[code].dispose = true; + + } else if (trainlisttest.list[code].dispose == data.body[i].dispose && data.body[i].dispose == false) { + + if (sectionlist.sections.datalist[data.body[i].sectionCode].code) { + if (trainlisttest.list[code].nowcode != data.body[i].sectionCode || trainlisttest.list[code].nextcode != data.body[i].nextSectionCode) { + trainlisttest.list[code].nowcode = data.body[i].sectionCode; + trainlisttest.list[code].nextcode = data.body[i].nextSectionCode; + let vexlist = []; + let endrotation = null; + if (data.body[i].directionType == "02") {//向右 + if (trainlisttest.list[code].status != data.body[i].directionType) { + console.log(trainlisttest.list[code].position); + console.log(trainlisttest.list[code].children[5].matrixWorld); + trainlisttest.list[code].position.x = trainlisttest.list[code].children[5].matrixWorld.elements[12]; + trainlisttest.list[code].children[0].position.z = trainlisttest.list[code].children[0].matrixWorld.elements[14]; + trainlisttest.list[code].children[1].position.z = trainlisttest.list[code].children[1].matrixWorld.elements[14]; + trainlisttest.list[code].children[2].position.z = trainlisttest.list[code].children[2].matrixWorld.elements[14]; + trainlisttest.list[code].children[3].position.z = trainlisttest.list[code].children[3].matrixWorld.elements[14]; + trainlisttest.list[code].children[4].position.z = trainlisttest.list[code].children[4].matrixWorld.elements[14]; + trainlisttest.list[code].children[5].position.z = trainlisttest.list[code].children[5].matrixWorld.elements[14]; + + if (sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x > sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x) { + vexlist.push(new THREE.Vector3(trainlisttest.list[code].position.x, 0, trainlisttest.list[code].children[0].matrixWorld.elements[14])); + for (let m = sectionlist.sections.datalist[data.body[i].sectionCode].rail.length - 1; m >= 0; m--) { + if (sectionlist.sections.datalist[data.body[i].sectionCode].rail[m].x > trainlisttest.list[code].position.x) { + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + + } + } else { + vexlist.push(new THREE.Vector3(trainlisttest.list[code].position.x, 0, trainlisttest.list[code].children[0].matrixWorld.elements[14])); + for (let m = 0; m < sectionlist.sections.datalist[data.body[i].sectionCode].rail.length; m++) { + if (sectionlist.sections.datalist[data.body[i].sectionCode].rail[m].x > trainlisttest.list[code].position.x) { + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + } + } + } else { + if (sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x > sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x) { + for (let m = sectionlist.sections.datalist[data.body[i].sectionCode].rail.length - 1; m >= 0; m--) { + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + } else { + for (let m = 0; m < sectionlist.sections.datalist[data.body[i].sectionCode].rail.length; m++) { + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + } + } + trainlisttest.list[code].status = "02"; + trainlisttest.list[code].rotation.y = 0; + + } else if (data.body[i].directionType == "03") {//向左 + + if (trainlisttest.list[code].status != data.body[i].directionType) { + trainlisttest.list[code].position.x = trainlisttest.list[code].children[5].matrixWorld.elements[12]; + trainlisttest.list[code].children[0].position.z = trainlisttest.list[code].children[0].matrixWorld.elements[14]; + trainlisttest.list[code].children[1].position.z = trainlisttest.list[code].children[1].matrixWorld.elements[14]; + trainlisttest.list[code].children[2].position.z = trainlisttest.list[code].children[2].matrixWorld.elements[14]; + trainlisttest.list[code].children[3].position.z = trainlisttest.list[code].children[3].matrixWorld.elements[14]; + trainlisttest.list[code].children[4].position.z = trainlisttest.list[code].children[4].matrixWorld.elements[14]; + trainlisttest.list[code].children[5].position.z = trainlisttest.list[code].children[5].matrixWorld.elements[14]; + // + if (sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x < sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x) { + vexlist.push(new THREE.Vector3(trainlisttest.list[code].position.x, 0, trainlisttest.list[code].children[0].matrixWorld.elements[14])); + for (let m = sectionlist.sections.datalist[data.body[i].sectionCode].rail.length - 1; m >= 0; m--) { + if (sectionlist.sections.datalist[data.body[i].sectionCode].rail[m].x < trainlisttest.list[code].position.x) { + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + } + } else { + vexlist.push(new THREE.Vector3(trainlisttest.list[code].position.x, 0, trainlisttest.list[code].children[0].matrixWorld.elements[14])); + for (let m = 0; m < sectionlist.sections.datalist[data.body[i].sectionCode].rail.length; m++) { + if (sectionlist.sections.datalist[data.body[i].sectionCode].rail[m].x < trainlisttest.list[code].position.x) { + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + } + } + } else { + // + if (sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x < sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x) { + for (let m = sectionlist.sections.datalist[data.body[i].sectionCode].rail.length - 1; m >= 0; m--) { + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + + } else { + for (let m = 0; m < sectionlist.sections.datalist[data.body[i].sectionCode].rail.length; m++) { + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + } + } + trainlisttest.list[code].status = "03"; + trainlisttest.list[code].rotation.y = Math.PI; + + } + + trainlisttest.list[code].curve = new THREE.CatmullRomCurve3(vexlist); + // console.log(trainlisttest.list[code].curve.points); + // console.log(data.body[i].speed) + // console.log(sectionlist.sections.datalist[data.body[i].sectionCode].distance); + trainlisttest.list[code].speeds = data.body[i].speed * 10 / 36 / 11 / sectionlist.sections.datalist[data.body[i].sectionCode].distance; + + trainlisttest.list[code].progress = 0; + } + // console.log("speed"); + // console.log(data.body[i].speed); + // console.log(trainlisttest.list[code].speed); + if (data.body[i].speed != trainlisttest.list[code].speed) { + trainlisttest.list[code].speed = data.body[i].speed; + trainlisttest.list[code].speeds = data.body[i].speed * 10 / 36 / 11 / sectionlist.sections.datalist[data.body[i].sectionCode].distance; + // if(trainlisttest.list[code].speeds > 0.1){ + // console.log(data.body[i]); + // console.log(data.body[i].speed) + // console.log(sectionlist.sections.datalist[data.body[i].sectionCode].distance); + // console.log(trainlisttest.list[code].speeds); + // } + } + + } + + } + + } + + } + } + + + //0xFFFFFF + //0xCD0000 红 + //0xEEEE00 黄 + //0x32CD32 绿 + if (data.body[i]._type == "Signal") { + + if (signallist) { + code = data.body[i].code; + if (data.body[i].lightType == "01") { + if (signallist.list[code].mesh.code) { + signallist.list[code].mesh.status = data.body[i].status; + + //55 + //33 + //77 + //关闭 + if (data.body[i].status == "01") { + signallist.list[code].mesh.children[0].material.map = materials[3]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[0]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[3]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + //开放 + if (data.body[i].status == "02") { + + if (data.body[i].switchLocateType == "01") { + signallist.list[code].mesh.children[0].material.map = materials[2]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[3]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[3]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + + if (data.body[i].switchLocateType == "02") { + signallist.list[code].mesh.children[0].material.map = materials[3]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[3]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[1]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + + } + //引导 + if (data.body[i].status == "03") { + signallist.list[code].mesh.children[0].material.map = materials[3]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[0]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[1]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + //封锁 + if (data.body[i].status == "04") { + signallist.list[code].mesh.children[0].material.map = materials[3]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[3]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[3]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + //故障 + if (data.body[i].status == "05") { + signallist.list[code].mesh.children[0].material.map = materials[3]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[3]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[3]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + + + + } + + } else if (data.body[i].lightType == "01") { + if (signallist.list[code].mesh.code) { + signallist.list[code].mesh.children[0].material.map = materials[3]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[3]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[3]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + + } + + } + + } + + + if (data.body[i]._type == "StationStand") { + if (actions) { + code = data.body[i].code; + if (data.body[i].screenDoorOpenStatus == "02" && actions[code].status == "01") { + actions[code].status = "00"; + } + if (data.body[i].screenDoorOpenStatus == "02" && actions[code].status == "00") { + actions[code].status = "02"; + actions[code].action.reset(); + actions[code].action.time = 0; + actions[code].action.timeScale = 1; + actions[code].action.play(); + } + + if (data.body[i].screenDoorOpenStatus == "01" && actions[code].status == "02") { + actions[code].status = "00"; + } + if (data.body[i].screenDoorOpenStatus == "01" && actions[code].status == "00") { + actions[code].status = "01"; + actions[code].action.reset(); + actions[code].action.time = actions[code].action._clip.duration; + actions[code].action.timeScale = -1; + actions[code].action.play(); + } + + } + + } + + if (data.body[i]._type == "Switch") { + + if (sectionlist) { + code = data.body[i].code; + for (let j = sectionlist.switchs.modellist.length - 1; j >= 0; j--) { + if (sectionlist.switchs.modellist[j].code == code) { + if (sectionlist.switchs.modellist[j].locateType != data.body[i].locateType) { + if (data.body[i].locateType == "02") { + sectionlist.switchs.modellist[j].locateType = data.body[i].locateType; + actions[sectionlist.switchs.modellist[j].code].reset(); + actions[sectionlist.switchs.modellist[j].code].time = 0; + actions[sectionlist.switchs.modellist[j].code].timeScale = 1; + actions[sectionlist.switchs.modellist[j].code].play(); + + } else if (data.body[i].locateType == "01") { + sectionlist.switchs.modellist[j].locateType = data.body[i].locateType; + actions[sectionlist.switchs.modellist[j].code].reset(); + actions[sectionlist.switchs.modellist[j].code].time = actions[sectionlist.switchs.modellist[j].code]._clip.duration; + actions[sectionlist.switchs.modellist[j].code].timeScale = -1; + actions[sectionlist.switchs.modellist[j].code].play(); + + } + } + + //console.log(sectionlist.group[j].name); + j = 0; + } + } + } + + } + } + + } else if (data.body.length > 200) { + + } + + + } + + + + } +} diff --git a/src/jlmap3d/control/FirstControls.js b/src/jlmap3d/control/FirstControls.js new file mode 100644 index 000000000..9ed0ce03b --- /dev/null +++ b/src/jlmap3d/control/FirstControls.js @@ -0,0 +1,390 @@ +/** + * 摄像机控制(用户交互),此类只实现键盘交互(移动),如果需要鼠标交互(转动视角)使用其子类PointerLockControls或者MouseControls + * @constructor + * @param {THREE.Camera} camera 需要控制的摄像机 + * @param {Number} height 高度,相当于观察者身高,默认1.6 + */ +function FreeControls(camera, height) { + + var _scope = this; + var _PI_2 = Math.PI / 2; + + var _forward = false; + var _backward = false; + var _right = false; + var _left = false; + var _up = false; + var _down = false; + + var _up_whirl = false; + var _down_whirl = false; + var _left_whirl = false; + var _right_whirl = false; + + this._camera = camera; + this._pitchObject = new THREE.Object3D(); + this._yawObject = new THREE.Object3D(); + + /** + * 控制器使能 + * @type {Boolean} + */ + this.enabled = false; + /** + * 摄像机移动速度,默认2 + * @type {Number} + */ + this.v_delta =0.5; + /** + * 上帝模式开关,默认false + * @type {Boolean} + */ + this.godView = false; + /** + * 摄像机高度(观察者身高) + * @type {Number} + */ + this.height = (height ? height : 1.6); + /** + * 移动控制按键 + * @type {Object} + * @property {Number} FORWARD 向前走按键的KEYCODE + * @property {Number} BACKWARD 向后走按键的KEYCODE + * @property {Number} LEFT 向左走按键的KEYCODE + * @property {Number} RIGHT 向右走按键的KEYCODE + */ + this.keys = { + FORWARD: 87, + BACKWARD: 83, + LEFT: 65, + RIGHT: 68 + } + + + + this._camera.rotation.set(0, 0, 0); + + this._pitchObject.add(this._camera); + + this._yawObject.position.y = this.height; + this._yawObject.add(this._pitchObject); + + this._pitchObject.rotation.set(0, 0, 0); + this._yawObject.rotation.set(0, 0, 0); + + document.addEventListener('keydown', onKeyDown, false); + document.addEventListener('keyup', onKeyUp, false); + + // Touch handling code + document.addEventListener('touchstart', onTouchStart, false); + document.addEventListener('touchend', onTouchEnd, false); + document.addEventListener('touchmove', onTouchMove, false); + //viewport.addEventListener('touchstart', function(event) { + function onTouchStart(event) { + var touches = event.touches; + switch(touches.length) { + case 1: // Single finger looks around + //startLook(touches[0].pageX, touches[0].pageY); + break; + case 2: // Two fingers moves + //startMove(touches[0].pageX, touches[0].pageY); + break; + case 3: // Three finger tap jumps + //playerMover.jump(); + break; + default: + return; + } + event.stopPropagation(); + event.preventDefault(); + } + //viewport.addEventListener('touchend', function(event) { + function onTouchEnd(event) { + //endLook(); + return false; + } + //viewport.addEventListener('touchmove', function(event) { + function onTouchMove(event) { + var touches = event.touches; + switch(touches.length) { + case 1: + //moveLook(touches[0].pageX, touches[0].pageY); + break; + case 2: + //moveUpdate(touches[0].pageX, touches[0].pageY, 16); + break; + default: + return; + } + event.stopPropagation(); + event.preventDefault(); + } + + /** + * 更新相机位置 + * @param {Number} delta 两次调用之间的时间差 + * @return {undefined} + */ + var rotation_delta_x = 0; + var rotation_delta_y = 0; + this.update = function (delta) { + delta = 4; + if (!_scope.enabled) return; + var rotation_ratio = 0.5; + + if ( !_up_whirl && !_down_whirl && !_left_whirl && !_right_whirl) { + + // if(mode == 2 ){ + // _scope._pitchObject.rotation.x = headControls.getPitchObject().rotation.x + rotation_delta_x; + // _scope._pitchObject.rotation.x = Math.max(-_PI_2, Math.min(_PI_2, _scope._pitchObject.rotation.x)); + // _scope._yawObject.rotation.y = headControls.getObject().rotation.y + rotation_delta_y; + // _scope._yawObject.rotation.z = headControls.getObject().rotation.z ; + // } + } + if (_forward || _backward || _left || _right || _up || _down + || _up_whirl || _down_whirl || _left_whirl || _right_whirl) { + var delta_new = 0.4; + var translation = new THREE.Vector3(0, 0, 0); + if (_scope.godView) { + if (_forward) { + + translation.z -= _scope.v_delta * Math.cos(_scope._pitchObject.rotation.x) * delta; + translation.y += _scope.v_delta * Math.sin(_scope._pitchObject.rotation.x) * delta; + } + if (_backward) { + + translation.z += _scope.v_delta * Math.cos(_scope._pitchObject.rotation.x) * delta; + translation.y -= _scope.v_delta * Math.sin(_scope._pitchObject.rotation.x) * delta; + } + } else { + if (_forward) + { + translation.z -= _scope.v_delta * delta * delta_new; + } + if (_backward) + { + translation.z += _scope.v_delta * delta * delta_new; + } + } + if (_left){ + translation.x -= _scope.v_delta * delta * delta_new; + } + if (_right){ + translation.x += _scope.v_delta * delta * delta_new; + } + if(_up){ + translation.y += _scope.v_delta * delta * delta_new; + } + if(_down){ + translation.y -= _scope.v_delta * delta * delta_new; + } + + if(_up_whirl) + { + _scope._pitchObject.rotation.x += _scope.v_delta * delta * rotation_ratio; + //限制向上抬头90度 + _scope._pitchObject.rotation.x = Math.max(-_PI_2, Math.min(_PI_2, _scope._pitchObject.rotation.x)); + rotation_delta_x += _scope.v_delta * delta * rotation_ratio; + } + if(_down_whirl) + { + _scope._pitchObject.rotation.x -= _scope.v_delta * delta * rotation_ratio; + //限制向下低头90度 + _scope._pitchObject.rotation.x = Math.max(-_PI_2, Math.min(_PI_2, _scope._pitchObject.rotation.x)); + rotation_delta_x -= _scope.v_delta * delta * rotation_ratio; + } + if(_left_whirl) + { + _scope._yawObject.rotation.y += _scope.v_delta * delta * rotation_ratio; + rotation_delta_y += _scope.v_delta * delta * rotation_ratio; + } + if(_right_whirl) + { + _scope._yawObject.rotation.y -= _scope.v_delta * delta * rotation_ratio; + rotation_delta_y -= _scope.v_delta * delta * rotation_ratio; + } + + _scope._yawObject.translateX(translation.x); + _scope._yawObject.translateY(translation.y); + _scope._yawObject.translateZ(translation.z); + //console.log(_scope._yawObject.position); + } + + if (!_scope.godView) { + // _scope._yawObject.position.y = _scope.height; + } + } + + /** + * 获取相机云台,关于相机位置的调整应该实施于由此函数返回的Object3D + * @return {THREE.Object3D} 相机云台 + */ + this.getObject = function () { + return this._yawObject; + }; + + /** + * 获取相机空间朝向,只读 + * @return {THREE.Vector3} 相机空间朝向 + */ + this.getDirection = function () { + var direction = new THREE.Vector3(0, 0, -1); + var rotation = new THREE.Euler(0, 0, 0, 'YXZ'); + var v = new THREE.Vector3(); + rotation.set(this._pitchObject.rotation.x, this._yawObject.rotation.y, 0); + v.copy(direction).applyEuler(rotation); + return v; + }; + + function onKeyDown(event) { + switch(event.keyCode) { + case _scope.keys.FORWARD: + _forward = true; + break; + case 38: // up + _up_whirl = true; + break; + + case _scope.keys.BACKWARD: + _backward = true; + break; + case 40: // down + _down_whirl = true; + break; + + case _scope.keys.LEFT: + _left = true; + break; + case 37: // _left + _left_whirl = true; + break; + + case _scope.keys.RIGHT: + _right = true; + break; + case 39: // _right + _right_whirl = true; + break; + + case 81: + _up = true; + break; + case 69: + _down = true; + break; + } + } + + function onKeyUp(event) { + switch(event.keyCode) { + case _scope.keys.FORWARD: + _forward = false; + break; + case 38: // up + _up_whirl = false; + break; + + case _scope.keys.BACKWARD: + _backward = false; + break; + case 40: // down + _down_whirl = false; + break; + + case _scope.keys.LEFT: + _left = false; + break; + case 37: // _left + _left_whirl = false; + break; + + case _scope.keys.RIGHT: + _right = false; + break; + case 39: // _right + _right_whirl = false; + break; + + case 81: + _up = false; + break; + case 69: + _down = false; + break; + } + } +} + +export function MouseControls(camera, height) { + var caozuo = 1; + var lastX,lastY; + FreeControls.call(this, camera, height); + + var _scope = this; + var _PI_2 = Math.PI / 2; + + var _mouseDown = false; + document.addEventListener('keydown', FreeControls.onKeyDown, false); + document.addEventListener('mousemove', onMouseMove, false); + document.addEventListener('mousedown', onMouseDown, false); + document.addEventListener('mouseup', onMouseUp, false); + + this.removeEventListeners = function (){ + document.removeEventListener('mousemove', onMouseMove, false); + document.removeEventListener('mousedown', onMouseDown, false); + document.removeEventListener('mouseup', onMouseUp, false); + document.removeEventListener('keydown', FreeControls.onKeyDown, false); + } + function onMouseMove(event) { + if(caozuo ==1){ + if (_scope.enabled === false || _mouseDown === false) return; + event.preventDefault(); + event.stopPropagation(); + + var currentX = event.clientX; + var currentY = event.clientY; + var movementX = currentX - lastX; + var movementY = currentY - lastY; + + _scope._yawObject.rotation.y -= movementX * 0.001; + _scope._pitchObject.rotation.x -= movementY * 0.001; + + _scope._pitchObject.rotation.x = Math.max(-_PI_2, Math.min(_PI_2, _scope._pitchObject.rotation.x)); + + lastX = currentX; + lastY = currentY; + }else{ + + + + } + } + + function onMouseDown (event) { + if(caozuo ==1){ + if (_scope.enabled === false) return; + + _mouseDown = true; + event.preventDefault(); + event.stopPropagation(); + + lastX = event.clientX; + lastY = event.clientY; + + }else{ + + + } + } + + function onMouseUp (event) { + if(caozuo ==1){ + _mouseDown = false; + event.preventDefault(); + event.stopPropagation(); + }else{ + + + } + } +} diff --git a/src/jlmap3d/control/OrbitControls.js b/src/jlmap3d/control/OrbitControls.js new file mode 100644 index 000000000..06ce9a338 --- /dev/null +++ b/src/jlmap3d/control/OrbitControls.js @@ -0,0 +1,1100 @@ +/** + * @author qiao / https://github.com/qiao + * @author mrdoob / http://mrdoob.com + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author erich666 / http://erichaines.com + */ + +// This set of controls performs orbiting, dollying (zooming), and panning. +// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). +// +// Orbit - left mouse / touch: one-finger move +// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish +// Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move + +THREE.OrbitControls = function ( object, domElement ) { + this.object = object; + + this.data = null; + + this.domElement = ( domElement !== undefined ) ? domElement : document; + + // Set to false to disable this control + this.enabled = true; + + // "target" sets the location of focus, where the object orbits around + this.target = new THREE.Vector3(); + + // How far you can dolly in and out ( PerspectiveCamera only ) + this.minDistance = 0; + this.maxDistance = Infinity; + + // How far you can zoom in and out ( OrthographicCamera only ) + this.minZoom = 0; + this.maxZoom = Infinity; + + // How far you can orbit vertically, upper and lower limits. + // Range is 0 to Math.PI radians. + this.minPolarAngle = 0; // radians + this.maxPolarAngle = Math.PI; // radians + + // How far you can orbit horizontally, upper and lower limits. + // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. + this.minAzimuthAngle = - Infinity; // radians + this.maxAzimuthAngle = Infinity; // radians + + // Set to true to enable damping (inertia) + // If damping is enabled, you must call controls.update() in your animation loop + this.enableDamping = false; + this.dampingFactor = 0.25; + + // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. + // Set to false to disable zooming + this.enableZoom = true; + this.zoomSpeed = 1.0; + + // Set to false to disable rotating + this.enableRotate = true; + this.rotateSpeed = 1.0; + + // Set to false to disable panning + this.enablePan = true; + this.panSpeed = 1.0; + this.screenSpacePanning = false; // if true, pan in screen-space + this.keyPanSpeed = 7.0; // pixels moved per arrow key push + + // Set to true to automatically rotate around the target + // If auto-rotate is enabled, you must call controls.update() in your animation loop + this.autoRotate = false; + this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 + + // Set to false to disable use of the keys + this.enableKeys = true; + + // The four arrow keys + this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; + + // Mouse buttons + this.mouseButtons = { LEFT: THREE.MOUSE.LEFT, MIDDLE: THREE.MOUSE.MIDDLE, RIGHT: THREE.MOUSE.RIGHT }; + + // for reset + this.target0 = this.target.clone(); + this.position0 = this.object.position.clone(); + this.zoom0 = this.object.zoom; + + // + // public methods + // + + this.getPolarAngle = function () { + + return spherical.phi; + + }; + + this.getAzimuthalAngle = function () { + + return spherical.theta; + + }; + + this.saveState = function () { + + scope.target0.copy( scope.target ); + scope.position0.copy( scope.object.position ); + scope.zoom0 = scope.object.zoom; + + }; + + this.reset = function () { + + scope.target.copy( scope.target0 ); + scope.object.position.copy( scope.position0 ); + scope.object.zoom = scope.zoom0; + + scope.object.updateProjectionMatrix(); + scope.dispatchEvent( changeEvent ); + + scope.update(); + + state = STATE.NONE; + + }; + + this.upmodeldirect = function(data){ + scope.data = data; + } + + // this method is exposed, but perhaps it would be better if we can make it private... + this.update = function () { + + + var offset = new THREE.Vector3(); + + // so camera.up is the orbit axis + var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) ); + var quatInverse = quat.clone().inverse(); + + var lastPosition = new THREE.Vector3(); + var lastQuaternion = new THREE.Quaternion(); + + return function update() { + + + if(scope.data){ + //站台牌自动跟随相机 + if(scope.data.stationstandlist){ + for(let i = scope.data.stationstandlist.textlist.length-1; i>=0; i --){ + scope.data.stationstandlist.textlist[i].lookAt(scope.object.position ); + } + } + + + if(scope.data.trainlisttest){ + //列车牌自动跟随相机 + for(let ii = scope.data.trainlisttest.textlist.length-1; ii>=0; ii --){ + scope.data.trainlisttest.textlist[ii].lookAt(scope.object.position ); + } + } + + } + + var position = scope.object.position; + + offset.copy( position ).sub( scope.target ); + + // rotate offset to "y-axis-is-up" space + offset.applyQuaternion( quat ); + + // angle from z-axis around y-axis + spherical.setFromVector3( offset ); + + if ( scope.autoRotate && state === STATE.NONE ) { + + rotateLeft( getAutoRotationAngle() ); + + } + + spherical.theta += sphericalDelta.theta; + spherical.phi += sphericalDelta.phi; + + // restrict theta to be between desired limits + spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) ); + + // restrict phi to be between desired limits + spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) ); + + spherical.makeSafe(); + + + spherical.radius *= scale; + + // restrict radius to be between desired limits + spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) ); + + // move target to panned location + scope.target.add( panOffset ); + + offset.setFromSpherical( spherical ); + + // rotate offset back to "camera-up-vector-is-up" space + offset.applyQuaternion( quatInverse ); + + position.copy( scope.target ).add( offset ); + + scope.object.lookAt( scope.target ); + + if ( scope.enableDamping === true ) { + + sphericalDelta.theta *= ( 1 - scope.dampingFactor ); + sphericalDelta.phi *= ( 1 - scope.dampingFactor ); + + panOffset.multiplyScalar( 1 - scope.dampingFactor ); + + } else { + + sphericalDelta.set( 0, 0, 0 ); + + panOffset.set( 0, 0, 0 ); + + } + + scale = 1; + + // update condition is: + // min(camera displacement, camera rotation in radians)^2 > EPS + // using small-angle approximation cos(x/2) = 1 - x^2 / 8 + + if ( zoomChanged || + lastPosition.distanceToSquared( scope.object.position ) > EPS || + 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) { + + scope.dispatchEvent( changeEvent ); + + lastPosition.copy( scope.object.position ); + lastQuaternion.copy( scope.object.quaternion ); + zoomChanged = false; + + return true; + + } + + return false; + + }; + + }(); + + this.dispose = function () { + + scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false ); + scope.domElement.removeEventListener( 'mousedown', onMouseDown, false ); + scope.domElement.removeEventListener( 'wheel', onMouseWheel, false ); + + scope.domElement.removeEventListener( 'touchstart', onTouchStart, false ); + scope.domElement.removeEventListener( 'touchend', onTouchEnd, false ); + scope.domElement.removeEventListener( 'touchmove', onTouchMove, false ); + + document.removeEventListener( 'mousemove', onMouseMove, false ); + document.removeEventListener( 'mouseup', onMouseUp, false ); + + window.removeEventListener( 'keydown', onKeyDown, false ); + + //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? + + }; + + // + // internals + // + + var scope = this; + + var changeEvent = { type: 'change' }; + var startEvent = { type: 'start' }; + var endEvent = { type: 'end' }; + + var STATE = { NONE: - 1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY_PAN: 4 }; + + var state = STATE.NONE; + + var EPS = 0.000001; + + // current position in spherical coordinates + var spherical = new THREE.Spherical(); + var sphericalDelta = new THREE.Spherical(); + + var scale = 1; + var panOffset = new THREE.Vector3(); + var zoomChanged = false; + + var rotateStart = new THREE.Vector2(); + var rotateEnd = new THREE.Vector2(); + var rotateDelta = new THREE.Vector2(); + + var panStart = new THREE.Vector2(); + var panEnd = new THREE.Vector2(); + var panDelta = new THREE.Vector2(); + + var dollyStart = new THREE.Vector2(); + var dollyEnd = new THREE.Vector2(); + var dollyDelta = new THREE.Vector2(); + + function getAutoRotationAngle() { + + return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; + + } + + function getZoomScale() { + + return Math.pow( 0.95, scope.zoomSpeed ); + + } + + function rotateLeft( angle ) { + + sphericalDelta.theta -= angle; + + } + + function rotateUp( angle ) { + + sphericalDelta.phi -= angle; + + } + + var panLeft = function () { + + var v = new THREE.Vector3(); + + return function panLeft( distance, objectMatrix ) { + + v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix + v.multiplyScalar( - distance ); + + panOffset.add( v ); + + }; + + }(); + + var panUp = function () { + + var v = new THREE.Vector3(); + + return function panUp( distance, objectMatrix ) { + + if ( scope.screenSpacePanning === true ) { + + v.setFromMatrixColumn( objectMatrix, 1 ); + + } else { + + v.setFromMatrixColumn( objectMatrix, 0 ); + v.crossVectors( scope.object.up, v ); + + } + + v.multiplyScalar( distance ); + + panOffset.add( v ); + + }; + + }(); + + // deltaX and deltaY are in pixels; right and down are positive + var pan = function () { + + var offset = new THREE.Vector3(); + + return function pan( deltaX, deltaY ) { + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + if ( scope.object.isPerspectiveCamera ) { + + // perspective + var position = scope.object.position; + offset.copy( position ).sub( scope.target ); + var targetDistance = offset.length(); + + // half of the fov is center to top of screen + targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); + + // we use only clientHeight here so aspect ratio does not distort speed + panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix ); + panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix ); + + } else if ( scope.object.isOrthographicCamera ) { + + // orthographic + panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix ); + panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix ); + + } else { + + // camera neither orthographic nor perspective + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); + scope.enablePan = false; + + } + + }; + + }(); + + function dollyIn( dollyScale ) { + + if ( scope.object.isPerspectiveCamera ) { + + scale /= dollyScale; + + } else if ( scope.object.isOrthographicCamera ) { + + scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) ); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + + } else { + + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); + scope.enableZoom = false; + + } + + } + + function dollyOut( dollyScale ) { + + if ( scope.object.isPerspectiveCamera ) { + + scale *= dollyScale; + + } else if ( scope.object.isOrthographicCamera ) { + + scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) ); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + + } else { + + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); + scope.enableZoom = false; + + } + + } + + // + // event callbacks - update the object state + // + + function handleMouseDownRotate( event ) { + + //console.log( 'handleMouseDownRotate' ); + + rotateStart.set( event.clientX, event.clientY ); + + } + + function handleMouseDownDolly( event ) { + + //console.log( 'handleMouseDownDolly' ); + + dollyStart.set( event.clientX, event.clientY ); + + } + + function handleMouseDownPan( event ) { + + //console.log( 'handleMouseDownPan' ); + + panStart.set( event.clientX, event.clientY ); + + } + + function handleMouseMoveRotate( event ) { + + //console.log( 'handleMouseMoveRotate' ); + + rotateEnd.set( event.clientX, event.clientY ); + + rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height + + rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); + + rotateStart.copy( rotateEnd ); + + scope.update(); + + } + + function handleMouseMoveDolly( event ) { + + //console.log( 'handleMouseMoveDolly' ); + + dollyEnd.set( event.clientX, event.clientY ); + + dollyDelta.subVectors( dollyEnd, dollyStart ); + + if ( dollyDelta.y > 0 ) { + + dollyIn( getZoomScale() ); + + } else if ( dollyDelta.y < 0 ) { + + dollyOut( getZoomScale() ); + + } + + dollyStart.copy( dollyEnd ); + + scope.update(); + + } + + function handleMouseMovePan( event ) { + + //console.log( 'handleMouseMovePan' ); + + panEnd.set( event.clientX, event.clientY ); + + panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + scope.update(); + + } + + function handleMouseUp( event ) { + + // console.log( 'handleMouseUp' ); + + } + + function handleMouseWheel( event ) { + + // console.log( 'handleMouseWheel' ); + + if ( event.deltaY < 0 ) { + + dollyOut( getZoomScale() ); + + } else if ( event.deltaY > 0 ) { + + dollyIn( getZoomScale() ); + + } + + scope.update(); + + } + + function handleKeyDown( event ) { + + // console.log( 'handleKeyDown' ); + + var needsUpdate = false; + + switch ( event.keyCode ) { + + case scope.keys.UP: + pan( 0, scope.keyPanSpeed ); + needsUpdate = true; + break; + + case scope.keys.BOTTOM: + pan( 0, - scope.keyPanSpeed ); + needsUpdate = true; + break; + + case scope.keys.LEFT: + pan( scope.keyPanSpeed, 0 ); + needsUpdate = true; + break; + + case scope.keys.RIGHT: + pan( - scope.keyPanSpeed, 0 ); + needsUpdate = true; + break; + + } + + if ( needsUpdate ) { + + // prevent the browser from scrolling on cursor keys + event.preventDefault(); + + scope.update(); + + } + + + } + + function handleTouchStartRotate( event ) { + + //console.log( 'handleTouchStartRotate' ); + + rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + + } + + function handleTouchStartDollyPan( event ) { + + //console.log( 'handleTouchStartDollyPan' ); + + if ( scope.enableZoom ) { + + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; + + var distance = Math.sqrt( dx * dx + dy * dy ); + + dollyStart.set( 0, distance ); + + } + + if ( scope.enablePan ) { + + var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); + var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); + + panStart.set( x, y ); + + } + + } + + function handleTouchMoveRotate( event ) { + + //console.log( 'handleTouchMoveRotate' ); + + rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + + rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height + + rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); + + rotateStart.copy( rotateEnd ); + + scope.update(); + + } + + function handleTouchMoveDollyPan( event ) { + + //console.log( 'handleTouchMoveDollyPan' ); + + if ( scope.enableZoom ) { + + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; + + var distance = Math.sqrt( dx * dx + dy * dy ); + + dollyEnd.set( 0, distance ); + + dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) ); + + dollyIn( dollyDelta.y ); + + dollyStart.copy( dollyEnd ); + + } + + if ( scope.enablePan ) { + + var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); + var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); + + panEnd.set( x, y ); + + panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + } + + scope.update(); + + } + + function handleTouchEnd( event ) { + + //console.log( 'handleTouchEnd' ); + + } + + // + // event handlers - FSM: listen for events and reset state + // + + function onMouseDown( event ) { + + if ( scope.enabled === false ) return; + + // Prevent the browser from scrolling. + + event.preventDefault(); + + // Manually set the focus since calling preventDefault above + // prevents the browser from setting it automatically. + + scope.domElement.focus ? scope.domElement.focus() : window.focus(); + + switch ( event.button ) { + + case scope.mouseButtons.RIGHT: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + if ( scope.enablePan === false ) return; + + handleMouseDownPan( event ); + + state = STATE.PAN; + + } else { + + if ( scope.enableRotate === false ) return; + + handleMouseDownRotate( event ); + + state = STATE.ROTATE; + + } + + break; + + case scope.mouseButtons.MIDDLE: + + if ( scope.enableZoom === false ) return; + + handleMouseDownDolly( event ); + + state = STATE.DOLLY; + + break; + + case scope.mouseButtons.LEFT: + + if ( scope.enablePan === false ) return; + + handleMouseDownPan( event ); + + state = STATE.PAN; + + break; + + } + + if ( state !== STATE.NONE ) { + + document.addEventListener( 'mousemove', onMouseMove, false ); + document.addEventListener( 'mouseup', onMouseUp, false ); + + scope.dispatchEvent( startEvent ); + + } + + } + + function onMouseMove( event ) { + if ( scope.enabled === false ) return; + + event.preventDefault(); + + switch ( state ) { + + case STATE.ROTATE: + + if ( scope.enableRotate === false ) return; + + handleMouseMoveRotate( event ); + + break; + + case STATE.DOLLY: + + if ( scope.enableZoom === false ) return; + + handleMouseMoveDolly( event ); + + break; + + case STATE.PAN: + + if ( scope.enablePan === false ) return; + + handleMouseMovePan( event ); + + break; + + } + + } + + function onMouseUp( event ) { + + if ( scope.enabled === false ) return; + + handleMouseUp( event ); + + document.removeEventListener( 'mousemove', onMouseMove, false ); + document.removeEventListener( 'mouseup', onMouseUp, false ); + + scope.dispatchEvent( endEvent ); + + state = STATE.NONE; + + } + + function onMouseWheel( event ) { + + if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return; + + if (event.cancelable) { + // 判断默认行为是否已经被禁用 + if (!event.defaultPrevented) { + event.preventDefault(); + } + } + + event.stopPropagation(); + + scope.dispatchEvent( startEvent ); + + handleMouseWheel( event ); + + scope.dispatchEvent( endEvent ); + + } + + function onKeyDown( event ) { + + if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return; + + handleKeyDown( event ); + + } + + function onTouchStart( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + + switch ( event.touches.length ) { + + case 1: // one-fingered touch: rotate + + if ( scope.enableRotate === false ) return; + + handleTouchStartRotate( event ); + + state = STATE.TOUCH_ROTATE; + + break; + + case 2: // two-fingered touch: dolly-pan + + if ( scope.enableZoom === false && scope.enablePan === false ) return; + + handleTouchStartDollyPan( event ); + + state = STATE.TOUCH_DOLLY_PAN; + + break; + + default: + + state = STATE.NONE; + + } + + if ( state !== STATE.NONE ) { + + scope.dispatchEvent( startEvent ); + + } + + } + + function onTouchMove( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + event.stopPropagation(); + + switch ( event.touches.length ) { + + case 1: // one-fingered touch: rotate + + if ( scope.enableRotate === false ) return; + if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed? + + handleTouchMoveRotate( event ); + + break; + + case 2: // two-fingered touch: dolly-pan + + if ( scope.enableZoom === false && scope.enablePan === false ) return; + if ( state !== STATE.TOUCH_DOLLY_PAN ) return; // is this needed? + + handleTouchMoveDollyPan( event ); + + break; + + default: + + state = STATE.NONE; + + } + + } + + function onTouchEnd( event ) { + + if ( scope.enabled === false ) return; + + handleTouchEnd( event ); + + scope.dispatchEvent( endEvent ); + + state = STATE.NONE; + + } + + function onContextMenu( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + + } + + // + + scope.domElement.addEventListener( 'contextmenu', onContextMenu, false ); + + scope.domElement.addEventListener( 'mousedown', onMouseDown, false ); + scope.domElement.addEventListener( 'wheel', onMouseWheel, { passive: false } ); + + scope.domElement.addEventListener( 'touchstart', onTouchStart, { passive: false } ); + scope.domElement.addEventListener( 'touchend', onTouchEnd, { passive: false } ); + scope.domElement.addEventListener( 'touchmove', onTouchMove, { passive: false } ); + + window.addEventListener( 'keydown', onKeyDown, false ); + + // force an update at start + + this.update(); + +}; + +THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); +THREE.OrbitControls.prototype.constructor = THREE.OrbitControls; + +Object.defineProperties( THREE.OrbitControls.prototype, { + + center: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .center has been renamed to .target' ); + return this.target; + + } + + }, + + // backward compatibility + + noZoom: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); + return ! this.enableZoom; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); + this.enableZoom = ! value; + + } + + }, + + noRotate: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); + return ! this.enableRotate; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); + this.enableRotate = ! value; + + } + + }, + + noPan: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); + return ! this.enablePan; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); + this.enablePan = ! value; + + } + + }, + + noKeys: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); + return ! this.enableKeys; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); + this.enableKeys = ! value; + + } + + }, + + staticMoving: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); + return ! this.enableDamping; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); + this.enableDamping = ! value; + + } + + }, + + dynamicDampingFactor: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); + return this.dampingFactor; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); + this.dampingFactor = value; + + } + + } + +} ); diff --git a/src/jlmap3d/control/OribitControlstest.js b/src/jlmap3d/control/OribitControlstest.js new file mode 100644 index 000000000..219cdff32 --- /dev/null +++ b/src/jlmap3d/control/OribitControlstest.js @@ -0,0 +1,1016 @@ +/** + * @author qiao / https://github.com/qiao + * @author mrdoob / http://mrdoob.com + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author erich666 / http://erichaines.com + */ + +// This set of controls performs orbiting, dollying (zooming), and panning. +// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). +// +// Orbit - left mouse / touch: one finger move +// Zoom - middle mouse, or mousewheel / touch: two finger spread or squish +// Pan - right mouse, or arrow keys / touch: three finger swipe + +THREE.OrbitControls = function ( object, domElement ) { + + this.object = object; + + this.domElement = ( domElement !== undefined ) ? domElement : document; + + // Set to false to disable this control + this.enabled = true; + + // "target" sets the location of focus, where the object orbits around + this.target = new THREE.Vector3(); + + // How far you can dolly in and out ( PerspectiveCamera only ) + this.minDistance = 0; + this.maxDistance = Infinity; + + // How far you can zoom in and out ( OrthographicCamera only ) + this.minZoom = 0; + this.maxZoom = Infinity; + + // How far you can orbit vertically, upper and lower limits. + // Range is 0 to Math.PI radians. + this.minPolarAngle = 0; // radians + this.maxPolarAngle = Math.PI; // radians + + // How far you can orbit horizontally, upper and lower limits. + // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. + this.minAzimuthAngle = - Infinity; // radians + this.maxAzimuthAngle = Infinity; // radians + + // Set to true to enable damping (inertia) + // If damping is enabled, you must call controls.update() in your animation loop + this.enableDamping = false; + this.dampingFactor = 0.25; + + // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. + // Set to false to disable zooming + this.enableZoom = true; + this.zoomSpeed = 1.0; + + // Set to false to disable rotating + this.enableRotate = true; + this.rotateSpeed = 1.0; + + // Set to false to disable panning + this.enablePan = true; + this.keyPanSpeed = 7.0; // pixels moved per arrow key push + + // Set to true to automatically rotate around the target + // If auto-rotate is enabled, you must call controls.update() in your animation loop + this.autoRotate = false; + this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 + + // Set to false to disable use of the keys + this.enableKeys = true; + + // The four arrow keys + this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; + + // Mouse buttons + this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT }; + + // for reset + this.target0 = this.target.clone(); + this.position0 = this.object.position.clone(); + this.zoom0 = this.object.zoom; + + // + // public methods + // + + this.getPolarAngle = function () { + + return spherical.phi; + + }; + + this.getAzimuthalAngle = function () { + + return spherical.theta; + + }; + + this.reset = function () { + + scope.target.copy( scope.target0 ); + scope.object.position.copy( scope.position0 ); + scope.object.zoom = scope.zoom0; + + scope.object.updateProjectionMatrix(); + scope.dispatchEvent( changeEvent ); + + scope.update(); + + state = STATE.NONE; + + }; + + // this method is exposed, but perhaps it would be better if we can make it private... + this.update = function () { + + var offset = new THREE.Vector3(); + + // so camera.up is the orbit axis + var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) ); + var quatInverse = quat.clone().inverse(); + + var lastPosition = new THREE.Vector3(); + var lastQuaternion = new THREE.Quaternion(); + + return function update() { + + var position = scope.object.position; + + offset.copy( position ).sub( scope.target ); + + // rotate offset to "y-axis-is-up" space + offset.applyQuaternion( quat ); + + // angle from z-axis around y-axis + spherical.setFromVector3( offset ); + + if ( scope.autoRotate && state === STATE.NONE ) { + + rotateLeft( getAutoRotationAngle() ); + + } + + spherical.theta += sphericalDelta.theta; + spherical.phi += sphericalDelta.phi; + + // restrict theta to be between desired limits + spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) ); + + // restrict phi to be between desired limits + spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) ); + + spherical.makeSafe(); + + + spherical.radius *= scale; + + // restrict radius to be between desired limits + spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) ); + + // move target to panned location + scope.target.add( panOffset ); + + offset.setFromSpherical( spherical ); + + // rotate offset back to "camera-up-vector-is-up" space + offset.applyQuaternion( quatInverse ); + + position.copy( scope.target ).add( offset ); + + scope.object.lookAt( scope.target ); + + if ( scope.enableDamping === true ) { + + sphericalDelta.theta *= ( 1 - scope.dampingFactor ); + sphericalDelta.phi *= ( 1 - scope.dampingFactor ); + + } else { + + sphericalDelta.set( 0, 0, 0 ); + + } + + scale = 1; + panOffset.set( 0, 0, 0 ); + + // update condition is: + // min(camera displacement, camera rotation in radians)^2 > EPS + // using small-angle approximation cos(x/2) = 1 - x^2 / 8 + + if ( zoomChanged || + lastPosition.distanceToSquared( scope.object.position ) > EPS || + 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) { + + scope.dispatchEvent( changeEvent ); + + lastPosition.copy( scope.object.position ); + lastQuaternion.copy( scope.object.quaternion ); + zoomChanged = false; + + return true; + + } + + return false; + + }; + + }(); + + this.dispose = function () { + + scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false ); + scope.domElement.removeEventListener( 'mousedown', onMouseDown, false ); + scope.domElement.removeEventListener( 'wheel', onMouseWheel, false ); + + scope.domElement.removeEventListener( 'touchstart', onTouchStart, false ); + scope.domElement.removeEventListener( 'touchend', onTouchEnd, false ); + scope.domElement.removeEventListener( 'touchmove', onTouchMove, false ); + + document.removeEventListener( 'mousemove', onMouseMove, false ); + document.removeEventListener( 'mouseup', onMouseUp, false ); + + //window.removeEventListener( 'keydown', onKeyDown, false ); + + //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? + + }; + + // + // internals + // + + var scope = this; + + var changeEvent = { type: 'change' }; + var startEvent = { type: 'start' }; + var endEvent = { type: 'end' }; + + var STATE = { NONE: - 1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY: 4, TOUCH_PAN: 5 }; + + var state = STATE.NONE; + + var EPS = 0.000001; + + // current position in spherical coordinates + var spherical = new THREE.Spherical(); + var sphericalDelta = new THREE.Spherical(); + + var scale = 1; + var panOffset = new THREE.Vector3(); + var zoomChanged = false; + + var rotateStart = new THREE.Vector2(); + var rotateEnd = new THREE.Vector2(); + var rotateDelta = new THREE.Vector2(); + + var panStart = new THREE.Vector2(); + var panEnd = new THREE.Vector2(); + var panDelta = new THREE.Vector2(); + + var dollyStart = new THREE.Vector2(); + var dollyEnd = new THREE.Vector2(); + var dollyDelta = new THREE.Vector2(); + + function getAutoRotationAngle() { + + return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; + + } + + function getZoomScale() { + + return Math.pow( 0.95, scope.zoomSpeed ); + + } + + function rotateLeft( angle ) { + + sphericalDelta.theta -= angle; + + } + + function rotateUp( angle ) { + + sphericalDelta.phi -= angle; + + } + + var panLeft = function () { + + var v = new THREE.Vector3(); + + return function panLeft( distance, objectMatrix ) { + + v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix + v.multiplyScalar( - distance ); + + panOffset.add( v ); + + }; + + }(); + + var panUp = function () { + + var v = new THREE.Vector3(); + + return function panUp( distance, objectMatrix ) { + + v.setFromMatrixColumn( objectMatrix, 1 ); // get Y column of objectMatrix + v.multiplyScalar( distance ); + + panOffset.add( v ); + + }; + + }(); + + // deltaX and deltaY are in pixels; right and down are positive + var pan = function () { + + var offset = new THREE.Vector3(); + + return function pan( deltaX, deltaY ) { + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + if ( scope.object instanceof THREE.PerspectiveCamera ) { + + // perspective + var position = scope.object.position; + offset.copy( position ).sub( scope.target ); + var targetDistance = offset.length(); + + // half of the fov is center to top of screen + targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); + + // we actually don't use screenWidth, since perspective camera is fixed to screen height + panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix ); + panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix ); + + } else if ( scope.object instanceof THREE.OrthographicCamera ) { + + // orthographic + panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix ); + panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix ); + + } else { + + // camera neither orthographic nor perspective + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); + scope.enablePan = false; + + } + + }; + + }(); + + function dollyIn( dollyScale ) { + + if ( scope.object instanceof THREE.PerspectiveCamera ) { + + scale /= dollyScale; + + } else if ( scope.object instanceof THREE.OrthographicCamera ) { + + scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) ); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + + } else { + + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); + scope.enableZoom = false; + + } + + } + + function dollyOut( dollyScale ) { + + if ( scope.object instanceof THREE.PerspectiveCamera ) { + + scale *= dollyScale; + + } else if ( scope.object instanceof THREE.OrthographicCamera ) { + + scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) ); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + + } else { + + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); + scope.enableZoom = false; + + } + + } + + // + // event callbacks - update the object state + // + + function handleMouseDownRotate( event ) { + + //console.log( 'handleMouseDownRotate' ); + + rotateStart.set( event.clientX, event.clientY ); + + } + + function handleMouseDownDolly( event ) { + + //console.log( 'handleMouseDownDolly' ); + + dollyStart.set( event.clientX, event.clientY ); + + } + + function handleMouseDownPan( event ) { + + //console.log( 'handleMouseDownPan' ); + + panStart.set( event.clientX, event.clientY ); + + } + + function handleMouseMoveRotate( event ) { + + //console.log( 'handleMouseMoveRotate' ); + + rotateEnd.set( event.clientX, event.clientY ); + rotateDelta.subVectors( rotateEnd, rotateStart ); + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + // rotating across whole screen goes 360 degrees around + rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); + + // rotating up and down along whole screen attempts to go 360, but limited to 180 + rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); + + rotateStart.copy( rotateEnd ); + + scope.update(); + + } + + function handleMouseMoveDolly( event ) { + + //console.log( 'handleMouseMoveDolly' ); + + dollyEnd.set( event.clientX, event.clientY ); + + dollyDelta.subVectors( dollyEnd, dollyStart ); + + if ( dollyDelta.y > 0 ) { + + dollyIn( getZoomScale() ); + + } else if ( dollyDelta.y < 0 ) { + + dollyOut( getZoomScale() ); + + } + + dollyStart.copy( dollyEnd ); + + scope.update(); + + } + + function handleMouseMovePan( event ) { + + //console.log( 'handleMouseMovePan' ); + + panEnd.set( event.clientX, event.clientY ); + + panDelta.subVectors( panEnd, panStart ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + scope.update(); + + } + + function handleMouseUp( event ) { + + // console.log( 'handleMouseUp' ); + + } + + function handleMouseWheel( event ) { + + // console.log( 'handleMouseWheel' ); + + if ( event.deltaY < 0 ) { + + dollyOut( getZoomScale() ); + + } else if ( event.deltaY > 0 ) { + + dollyIn( getZoomScale() ); + + } + + scope.update(); + + } + + function handleKeyDown( event ) { + + //console.log( 'handleKeyDown' ); + + switch ( event.keyCode ) { + + case scope.keys.UP: + pan( 0, scope.keyPanSpeed ); + scope.update(); + break; + + case scope.keys.BOTTOM: + pan( 0, - scope.keyPanSpeed ); + scope.update(); + break; + + case scope.keys.LEFT: + pan( scope.keyPanSpeed, 0 ); + scope.update(); + break; + + case scope.keys.RIGHT: + pan( - scope.keyPanSpeed, 0 ); + scope.update(); + break; + + } + + } + + function handleTouchStartRotate( event ) { + + //console.log( 'handleTouchStartRotate' ); + + rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + + } + + function handleTouchStartDolly( event ) { + + //console.log( 'handleTouchStartDolly' ); + + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; + + var distance = Math.sqrt( dx * dx + dy * dy ); + + dollyStart.set( 0, distance ); + + } + + function handleTouchStartPan( event ) { + + //console.log( 'handleTouchStartPan' ); + + panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + + } + + function handleTouchMoveRotate( event ) { + + //console.log( 'handleTouchMoveRotate' ); + + rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + rotateDelta.subVectors( rotateEnd, rotateStart ); + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + // rotating across whole screen goes 360 degrees around + rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); + + // rotating up and down along whole screen attempts to go 360, but limited to 180 + rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); + + rotateStart.copy( rotateEnd ); + + scope.update(); + + } + + function handleTouchMoveDolly( event ) { + + //console.log( 'handleTouchMoveDolly' ); + + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; + + var distance = Math.sqrt( dx * dx + dy * dy ); + + dollyEnd.set( 0, distance ); + + dollyDelta.subVectors( dollyEnd, dollyStart ); + + if ( dollyDelta.y > 0 ) { + + dollyOut( getZoomScale() ); + + } else if ( dollyDelta.y < 0 ) { + + dollyIn( getZoomScale() ); + + } + + dollyStart.copy( dollyEnd ); + + scope.update(); + + } + + function handleTouchMovePan( event ) { + + //console.log( 'handleTouchMovePan' ); + + panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + + panDelta.subVectors( panEnd, panStart ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + scope.update(); + + } + + function handleTouchEnd( event ) { + + //console.log( 'handleTouchEnd' ); + + } + + // + // event handlers - FSM: listen for events and reset state + // + + function onMouseDown( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + + if ( event.button === scope.mouseButtons.ORBIT ) { + + if ( scope.enableRotate === false ) return; + + handleMouseDownRotate( event ); + + state = STATE.ROTATE; + + } else if ( event.button === scope.mouseButtons.ZOOM ) { + + if ( scope.enableZoom === false ) return; + + handleMouseDownDolly( event ); + + state = STATE.DOLLY; + + } else if ( event.button === scope.mouseButtons.PAN ) { + + if ( scope.enablePan === false ) return; + + handleMouseDownPan( event ); + + state = STATE.PAN; + + } + + if ( state !== STATE.NONE ) { + + document.addEventListener( 'mousemove', onMouseMove, false ); + document.addEventListener( 'mouseup', onMouseUp, false ); + + scope.dispatchEvent( startEvent ); + + } + + } + + function onMouseMove( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + + if ( state === STATE.ROTATE ) { + + if ( scope.enableRotate === false ) return; + + handleMouseMoveRotate( event ); + + } else if ( state === STATE.DOLLY ) { + + if ( scope.enableZoom === false ) return; + + handleMouseMoveDolly( event ); + + } else if ( state === STATE.PAN ) { + + if ( scope.enablePan === false ) return; + + handleMouseMovePan( event ); + + } + + } + + function onMouseUp( event ) { + + if ( scope.enabled === false ) return; + + handleMouseUp( event ); + + document.removeEventListener( 'mousemove', onMouseMove, false ); + document.removeEventListener( 'mouseup', onMouseUp, false ); + + scope.dispatchEvent( endEvent ); + + state = STATE.NONE; + + } + + function onMouseWheel( event ) { + + if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return; + + event.preventDefault(); + event.stopPropagation(); + + handleMouseWheel( event ); + + scope.dispatchEvent( startEvent ); // not sure why these are here... + scope.dispatchEvent( endEvent ); + + } + + function onKeyDown( event ) { + + if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return; + + handleKeyDown( event ); + + } + + function onTouchStart( event ) { + + if ( scope.enabled === false ) return; + + switch ( event.touches.length ) { + + case 1: // one-fingered touch: rotate + + if ( scope.enableRotate === false ) return; + + handleTouchStartRotate( event ); + + state = STATE.TOUCH_ROTATE; + + break; + + case 2: // two-fingered touch: dolly + + if ( scope.enableZoom === false ) return; + + handleTouchStartDolly( event ); + + state = STATE.TOUCH_DOLLY; + + break; + + case 3: // three-fingered touch: pan + + if ( scope.enablePan === false ) return; + + handleTouchStartPan( event ); + + state = STATE.TOUCH_PAN; + + break; + + default: + + state = STATE.NONE; + + } + + if ( state !== STATE.NONE ) { + + scope.dispatchEvent( startEvent ); + + } + + } + + function onTouchMove( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + event.stopPropagation(); + + switch ( event.touches.length ) { + + case 1: // one-fingered touch: rotate + + if ( scope.enableRotate === false ) return; + if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed?... + + handleTouchMoveRotate( event ); + + break; + + case 2: // two-fingered touch: dolly + + if ( scope.enableZoom === false ) return; + if ( state !== STATE.TOUCH_DOLLY ) return; // is this needed?... + + handleTouchMoveDolly( event ); + + break; + + case 3: // three-fingered touch: pan + + if ( scope.enablePan === false ) return; + if ( state !== STATE.TOUCH_PAN ) return; // is this needed?... + + handleTouchMovePan( event ); + + break; + + default: + + state = STATE.NONE; + + } + + } + + function onTouchEnd( event ) { + + if ( scope.enabled === false ) return; + + handleTouchEnd( event ); + + scope.dispatchEvent( endEvent ); + + state = STATE.NONE; + + } + + function onContextMenu( event ) { + + event.preventDefault(); + + } + + // + + scope.domElement.addEventListener( 'contextmenu', onContextMenu, false ); + + scope.domElement.addEventListener( 'mousedown', onMouseDown, false ); + scope.domElement.addEventListener( 'wheel', onMouseWheel, false ); + + scope.domElement.addEventListener( 'touchstart', onTouchStart, false ); + scope.domElement.addEventListener( 'touchend', onTouchEnd, false ); + scope.domElement.addEventListener( 'touchmove', onTouchMove, false ); + + //window.addEventListener( 'keydown', onKeyDown, false ); + + // force an update at start + + this.update(); + +}; + +THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); +THREE.OrbitControls.prototype.constructor = THREE.OrbitControls; + +Object.defineProperties( THREE.OrbitControls.prototype, { + + center: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .center has been renamed to .target' ); + return this.target; + + } + + }, + + // backward compatibility + + noZoom: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); + return ! this.enableZoom; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); + this.enableZoom = ! value; + + } + + }, + + noRotate: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); + return ! this.enableRotate; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); + this.enableRotate = ! value; + + } + + }, + + noPan: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); + return ! this.enablePan; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); + this.enablePan = ! value; + + } + + }, + + noKeys: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); + return ! this.enableKeys; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); + this.enableKeys = ! value; + + } + + }, + + staticMoving: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); + return ! this.enableDamping; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); + this.enableDamping = ! value; + + } + + }, + + dynamicDampingFactor: { + + get: function () { + + console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); + return this.dampingFactor; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); + this.dampingFactor = value; + + } + + } + +} ); diff --git a/src/jlmap3d/edit/Jlmap3ddata.js b/src/jlmap3d/edit/Jlmap3ddata.js new file mode 100644 index 000000000..ae69109b3 --- /dev/null +++ b/src/jlmap3d/edit/Jlmap3ddata.js @@ -0,0 +1,164 @@ +import { getPublishMapDetail,getMapDetail,set3dMapData,get3dMapData } from '@/api/jlmap3d/load3ddata'; + +//原始信息obj化提供给三维建模 +import {SetObj} from '@/jlmap3d/edit/utils/SetObj.js'; +//componnent +import {SectionList} from '@/jlmap3d/edit/testmodel/SectionList.js'; +import {SignalList} from '@/jlmap3d/edit/testmodel/SignalList.js'; +import {StationStandList} from '@/jlmap3d/edit/testmodel/StationStandList.js'; +import {TrainList} from '@/jlmap3d/edit/testmodel/TrainList.js'; +import {TrainListTest} from '@/jlmap3d/edit/testmodel/TrainListTest.js'; +import {LinkList} from '@/jlmap3d/edit/testmodel/LinkList.js'; + +import {RealSectionList} from '@/jlmap3d/edit/testmodel/RealSectionList.js'; + +import axios from 'axios'; +import {getmodels} from '@/jlmap3d/edit/connect/getmodels'; +import { Loading } from 'element-ui'; +// import {SwitchModel} from '@/jlmap3d/model/SwitchModel.js'; + +export function Jlmap3ddata(mapid,scope){ + let editmapid = mapid; + let scene = scope.scene; + let jlmap3ddata = scope.mapdata; + let assetloader = scope.assetloader; + let loadingInstance = Loading.service({ fullscreen: true }); + get3dMapData(editmapid).then(data => { + //console.log(data); + if(data.code == "200"){ + + //console.log("数据存在"); + + let isSection = false; + //console.log(data.data); + if(data.data.assets){ + let assets = JSON.parse(data.data.assets); + for(let i=0;i { + //console.log(error); + if(error.code == "30001"){ + //console.log("没有三维数据"); + let params = {mapId:editmapid}; + + set3dMapData(params).then(data => { + if(data.code == "200"){ + //console.log("创建三维数据成功"); + //console.log(data); + initobj(editmapid,data.data.id); + + } + }).catch(error => { + //console.log(error); + }); + + } + }); + + function initobj(mapid,data3did){ + console.log(mapid); + getMapDetail(mapid).then(data => { + let mapdata = data.data; + //console.log(data3did); + jlmap3ddata.id = data3did; + jlmap3ddata.mapId = mapid; + console.log(jlmap3ddata.mapId); + //初始化轨道和道岔 暂时 + jlmap3ddata.sectionlist = new SectionList(); + jlmap3ddata.signallist = new SignalList(); + //初始化站台 + jlmap3ddata.stationstandlist = new StationStandList(); + jlmap3ddata.trainlisttest = new TrainListTest(); + + assetloader.assetinit(scene) + .then(function(data){ + //console.log(data); + return jlmap3ddata.sectionlist.initpromise(mapdata.sectionList,mapdata.switchList,scene); + }) + .then(function(data){ + //console.log(data); + return jlmap3ddata.signallist.initpromise(mapdata.signalList,scene,assetloader); + }) + .then(function(data){ + //console.log(data); + return jlmap3ddata.stationstandlist.initpromise(mapdata.stationList,mapdata.stationStandList,scene,assetloader); + }) + .then(function(data){ + //console.log(data); + return jlmap3ddata.trainlisttest.initpromise(mapdata.trainList,scene,assetloader); + }) + .then(function(data){ + //console.log(data); + loadingInstance.close(); + }); + //初始化信号 + SetObj(mapdata,scope); + + }); + } + + function init3d(mapid,netdata){ + getMapDetail(mapid).then(data => { + jlmap3ddata.id = netdata.id; + jlmap3ddata.mapId = mapid; + let mapdata = data.data; + + //初始化轨道和道岔 暂时 + jlmap3ddata.sectionlist = new SectionList(); + jlmap3ddata.signallist = new SignalList(); + //初始化站台 + jlmap3ddata.stationstandlist = new StationStandList(); + //初始化测试列车 + jlmap3ddata.trainlisttest = new TrainListTest(); + + jlmap3ddata.realsectionlist = new RealSectionList(); + + assetloader.setmodellist(netdata.assets); + + assetloader.assetpromise(scene) + .then(function(data){ + //console.log(data); + return jlmap3ddata.sectionlist.initpromise(mapdata.sectionList,mapdata.switchList,scene); + }) + .then(function(data){ + //console.log(data); + return jlmap3ddata.signallist.initpromise(mapdata.signalList,scene,assetloader,netdata.signals); + }) + .then(function(data){ + //console.log(data); + return jlmap3ddata.stationstandlist.initpromise(mapdata.stationList,mapdata.stationStandList,scene,assetloader,netdata.stands); + }) + .then(function(data){ + //console.log(data); + return jlmap3ddata.trainlisttest.initpromise(mapdata.trainList,scene,assetloader); + }) + .then(function(data){ + //console.log(data); + return jlmap3ddata.realsectionlist.initpromise(jlmap3ddata,scene,assetloader); + }) + .then(function(data){ + //console.log(data); + loadingInstance.close(); + }); + + }); + } + + + +} diff --git a/src/jlmap3d/edit/action/pathaction.js b/src/jlmap3d/edit/action/pathaction.js new file mode 100644 index 000000000..4d9332b25 --- /dev/null +++ b/src/jlmap3d/edit/action/pathaction.js @@ -0,0 +1,389 @@ +//轨道对象操作 +export function Pathaction(){ + + let scope = this; + //开关 + this.on = true; + + //修改名称 + this.setname = function(){ + if(scope.on == true){ + + } + } + //改变类型 + this.settype = function(){ + if(scope.on == true){ + + } + } + //改变坐标 + this.setpos = function(){ + if(scope.on == true){ + + } + } + //改变角度 + this.setrota = function(){ + if(scope.on == true){ + + } + } + //改变拉伸值 + this.setscle = function(){ + if(scope.on == true){ + + } + } + + this.drawline = function(jlmap3dedit,splineHelperObjects){ + + if(scope.on == true){ + + var rayhelper;//画图辅助线 + + let lineswitch = 0; + + var linenew; + + document.onmousedown = function(event){ + jlmap3dedit.selectswitch = false; + console.log(event.button); + + //辅助线段 + + let point1; + + if(event.button == 2){ + jlmap3dedit.selectswitch = true; + document.onmousedown = null; + + lineswitch = null; + + console.log(linenew); + + if(linenew){ + + jlmap3dedit.scene.remove( linenew ); + + } + + }else if(event.button == 0){ + + if(lineswitch == 0){ + + point1 = mouserray(event,jlmap3dedit); + + console.log("new"); + if(point1){ + linenew = addline(point1,1,jlmap3dedit.splineHelperObjects,jlmap3dedit.scene); + jlmap3dedit.mapdata.path.push(linenew); + jlmap3dedit.scene.add( linenew ); + + lineswitch = 1; + } + + + }else if(lineswitch == 1){ + + let point = { + x:(linenew.geometry.vertices[0].x+linenew.geometry.vertices[1].x)/2, + y:linenew.geometry.vertices[1].y, + z:(linenew.geometry.vertices[0].z+linenew.geometry.vertices[1].z)/2 + }; + var raycaster = new THREE.Raycaster(new THREE.Vector3(point.x,point.y,point.z),new THREE.Vector3(0,-1,0).normalize()); + + for(let i=0;i(point1.x-6)&&point2.x<(point1.x+6))&& point1.z != point2.z){ + + point2.x = point1.x; + + }else if((point2.z>(point1.z-6)&&point2.z<(point1.z+6)) && point1.x != point2.x){ + + point2.z = point1.z; + + } + + //更新辅助线段 + linenew.children[1].position.x = point2.x; + linenew.children[1].position.z = point2.z; + + linenew.geometry.vertices[1].x = point2.x; + + linenew.geometry.vertices[1].z = point2.z; + + linenew.geometry.verticesNeedUpdate = true; + } + + + } + + document.onmouseup = function(event){ + + if(lineswitch == 0){ + + document.onmousemove = null; + + document.onmouseup = null; + + } + + } + + } + } + } + } + } + + this.drawcurve = function(jlmap3dedit){ + + if(scope.on == true){ + + var rayhelper;//画图辅助线 + + let lineswitch = 0; + + var linenew; + + document.onmousedown = function(event){ + + console.log(event.button); + + //辅助线段 + + let point1; + + if(event.button == 2){ + + document.onmousedown = null; + + lineswitch = null; + + console.log(linenew); + + if(linenew){ + + jlmap3dedit.scene.remove( linenew ); + + } + + }else if(event.button == 0){ + + if(lineswitch == 0){ + + point1 = mouserray(event,jlmap3dedit); + + if(point1){ + linenew = addline(point1,2,jlmap3dedit.splineHelperObjects,jlmap3dedit.scene); + + + jlmap3dedit.scene.add( linenew ); + + lineswitch = 1; + } + + + + + }else if(lineswitch == 1){ + + let point = { + x:linenew.geometry.vertices[1].x, + y:linenew.geometry.vertices[1].y, + z:linenew.geometry.vertices[1].z + }; + var raycaster = new THREE.Raycaster(new THREE.Vector3(point.x,point.y,point.z),new THREE.Vector3(0,-1,0).normalize()); + + for(let i=0;i(window.innerWidth*0.8) ){ + + }else{ + mouse.x = (mousep.clientX / window.innerWidth) * 2 - 1; + mouse.y = -(mousep.clientY / window.innerHeight) * 2 + 1; + + raycaster.setFromCamera( mouse, jlmap3dedit.camera ); + console.log(jlmap3dedit.assetloader.modellist[3].mesh); + var intersects = raycaster.intersectObjects(jlmap3dedit.assetloader.modellist[3].mesh.children); + console.log(intersects); + if(intersects[0]){ + resultpoint = intersects[0].point; + return resultpoint; + } + } + + } + + function addline(point1,num,splineHelperObjects,scene){ + + let material = new THREE.LineBasicMaterial({color: 0xEEC900}); + + let geometry = new THREE.Geometry(); + + let object; + + if(num == 1){ + geometry.vertices.push(new THREE.Vector3(point1.x, 20, point1.z ),new THREE.Vector3( point1.x+1, 20, point1.z+1 )); + object = new THREE.Line( geometry, material ); + for(let i=0;i 0 ) { + + var object = intersects[ 0 ].object; + + _plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( object.matrixWorld ) ); + + if ( _hovered !== object ) { + + scope.dispatchEvent( { type: 'hoveron', object: object } ); + + _domElement.style.cursor = 'pointer'; + _hovered = object; + + } + + } else { + + if ( _hovered !== null ) { + + scope.dispatchEvent( { type: 'hoveroff', object: _hovered } ); + + _domElement.style.cursor = 'auto'; + _hovered = null; + + } + + } + + } + + function onDocumentMouseDown( event ) { + + event.preventDefault(); + + _raycaster.setFromCamera( _mouse, _camera ); + + var intersects = _raycaster.intersectObjects( _objects ); + + if ( intersects.length > 0 ) { + + _selected = intersects[ 0 ].object; + + if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) { + + _inverseMatrix.getInverse( _selected.parent.matrixWorld ); + _offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) ); + + } + + _domElement.style.cursor = 'move'; + + scope.dispatchEvent( { type: 'dragstart', object: _selected } ); + + } + + + } + + function onDocumentMouseCancel( event ) { + + event.preventDefault(); + + if ( _selected ) { + + scope.dispatchEvent( { type: 'dragend', object: _selected } ); + + _selected = null; + + } + + _domElement.style.cursor = _hovered ? 'pointer' : 'auto'; + + } + + function onDocumentTouchMove( event ) { + + event.preventDefault(); + event = event.changedTouches[ 0 ]; + + var rect = _domElement.getBoundingClientRect(); + + _mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1; + _mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1; + + _raycaster.setFromCamera( _mouse, _camera ); + + if ( _selected && scope.enabled ) { + + if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) { + + _selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) ); + + } + + scope.dispatchEvent( { type: 'drag', object: _selected } ); + + return; + + } + + } + + function onDocumentTouchStart( event ) { + + event.preventDefault(); + event = event.changedTouches[ 0 ]; + + var rect = _domElement.getBoundingClientRect(); + + _mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1; + _mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1; + + _raycaster.setFromCamera( _mouse, _camera ); + + var intersects = _raycaster.intersectObjects( _objects ); + + if ( intersects.length > 0 ) { + + _selected = intersects[ 0 ].object; + + _plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) ); + + if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) { + + _inverseMatrix.getInverse( _selected.parent.matrixWorld ); + _offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) ); + + } + + _domElement.style.cursor = 'move'; + + scope.dispatchEvent( { type: 'dragstart', object: _selected } ); + + } + + + } + + function onDocumentTouchEnd( event ) { + + event.preventDefault(); + + if ( _selected ) { + + scope.dispatchEvent( { type: 'dragend', object: _selected } ); + + _selected = null; + + } + + _domElement.style.cursor = 'auto'; + + } + + activate(); + + // API + + this.enabled = true; + + this.activate = activate; + this.deactivate = deactivate; + this.dispose = dispose; + + // Backward compatibility + + this.setObjects = function () { + + console.error( 'THREE.DragControls: setObjects() has been removed.' ); + + }; + + this.on = function ( type, listener ) { + + console.warn( 'THREE.DragControls: on() has been deprecated. Use addEventListener() instead.' ); + scope.addEventListener( type, listener ); + + }; + + this.off = function ( type, listener ) { + + console.warn( 'THREE.DragControls: off() has been deprecated. Use removeEventListener() instead.' ); + scope.removeEventListener( type, listener ); + + }; + + this.notify = function ( type ) { + + console.error( 'THREE.DragControls: notify() has been deprecated. Use dispatchEvent() instead.' ); + scope.dispatchEvent( { type: type } ); + + }; + +}; + +THREE.DragControls.prototype = Object.create( THREE.EventDispatcher.prototype ); +THREE.DragControls.prototype.constructor = THREE.DragControls; diff --git a/src/jlmap3d/edit/control/OrbitControlsEdit.js b/src/jlmap3d/edit/control/OrbitControlsEdit.js new file mode 100644 index 000000000..ca378b794 --- /dev/null +++ b/src/jlmap3d/edit/control/OrbitControlsEdit.js @@ -0,0 +1,1076 @@ +/** + * @author qiao / https://github.com/qiao + * @author mrdoob / http://mrdoob.com + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author erich666 / http://erichaines.com + */ + +// This set of controls performs orbiting, dollying (zooming), and panning. +// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). +// +// Orbit - left mouse / touch: one-finger move +// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish +// Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move + +THREE.OrbitControlsedit = function ( object, domElement ) { + this.object = object; + + this.data = null; + + this.domElement = ( domElement !== undefined ) ? domElement : document; + + // Set to false to disable this control + this.enabled = true; + + // "target" sets the location of focus, where the object orbits around + this.target = new THREE.Vector3(); + + // How far you can dolly in and out ( PerspectiveCamera only ) + this.minDistance = 0; + this.maxDistance = Infinity; + + // How far you can zoom in and out ( OrthographicCamera only ) + this.minZoom = 0; + this.maxZoom = Infinity; + + // How far you can orbit vertically, upper and lower limits. + // Range is 0 to Math.PI radians. + this.minPolarAngle = 0; // radians + this.maxPolarAngle = Math.PI; // radians + + // How far you can orbit horizontally, upper and lower limits. + // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. + this.minAzimuthAngle = - Infinity; // radians + this.maxAzimuthAngle = Infinity; // radians + + // Set to true to enable damping (inertia) + // If damping is enabled, you must call controls.update() in your animation loop + this.enableDamping = false; + this.dampingFactor = 0.25; + + // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. + // Set to false to disable zooming + this.enableZoom = true; + this.zoomSpeed = 1.0; + + // Set to false to disable rotating + this.enableRotate = true; + this.rotateSpeed = 1.0; + + // Set to false to disable panning + this.enablePan = true; + this.panSpeed = 1.0; + this.screenSpacePanning = false; // if true, pan in screen-space + this.keyPanSpeed = 7.0; // pixels moved per arrow key push + + // Set to true to automatically rotate around the target + // If auto-rotate is enabled, you must call controls.update() in your animation loop + this.autoRotate = false; + this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 + + // Set to false to disable use of the keys + this.enableKeys = true; + + // The four arrow keys + this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; + + // Mouse buttons + this.mouseButtons = { LEFT: THREE.MOUSE.LEFT, MIDDLE: THREE.MOUSE.MIDDLE, RIGHT: THREE.MOUSE.RIGHT }; + + // for reset + this.target0 = this.target.clone(); + this.position0 = this.object.position.clone(); + this.zoom0 = this.object.zoom; + + // + // public methods + // + + this.getPolarAngle = function () { + + return spherical.phi; + + }; + + this.getAzimuthalAngle = function () { + + return spherical.theta; + + }; + + this.saveState = function () { + + scope.target0.copy( scope.target ); + scope.position0.copy( scope.object.position ); + scope.zoom0 = scope.object.zoom; + + }; + + this.reset = function () { + + scope.target.copy( scope.target0 ); + scope.object.position.copy( scope.position0 ); + scope.object.zoom = scope.zoom0; + + scope.object.updateProjectionMatrix(); + scope.dispatchEvent( changeEvent ); + + scope.update(); + + state = STATE.NONE; + + }; + + this.upmodeldirect = function(data){ + scope.data = data; + } + + // this method is exposed, but perhaps it would be better if we can make it private... + this.update = function () { + + + var offset = new THREE.Vector3(); + + // so camera.up is the orbit axis + var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) ); + var quatInverse = quat.clone().inverse(); + + var lastPosition = new THREE.Vector3(); + var lastQuaternion = new THREE.Quaternion(); + + return function update() { + + var position = scope.object.position; + + offset.copy( position ).sub( scope.target ); + + // rotate offset to "y-axis-is-up" space + offset.applyQuaternion( quat ); + + // angle from z-axis around y-axis + spherical.setFromVector3( offset ); + + if ( scope.autoRotate && state === STATE.NONE ) { + rotateLeft( getAutoRotationAngle() ); + + } + + spherical.theta += sphericalDelta.theta; + spherical.phi += sphericalDelta.phi; + + // restrict theta to be between desired limits + spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) ); + + // restrict phi to be between desired limits + spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) ); + + spherical.makeSafe(); + + + spherical.radius *= scale; + + // restrict radius to be between desired limits + spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) ); + + // move target to panned location + scope.target.add( panOffset ); + + offset.setFromSpherical( spherical ); + + // rotate offset back to "camera-up-vector-is-up" space + offset.applyQuaternion( quatInverse ); + + position.copy( scope.target ).add( offset ); + + scope.object.lookAt( scope.target ); + + if ( scope.enableDamping === true ) { + sphericalDelta.theta *= ( 1 - scope.dampingFactor ); + sphericalDelta.phi *= ( 1 - scope.dampingFactor ); + + panOffset.multiplyScalar( 1 - scope.dampingFactor ); + + } else { + sphericalDelta.set( 0, 0, 0 ); + + panOffset.set( 0, 0, 0 ); + + } + + scale = 1; + + // update condition is: + // min(camera displacement, camera rotation in radians)^2 > EPS + // using small-angle approximation cos(x/2) = 1 - x^2 / 8 + + if ( zoomChanged || + lastPosition.distanceToSquared( scope.object.position ) > EPS || + 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) { + scope.dispatchEvent( changeEvent ); + + lastPosition.copy( scope.object.position ); + lastQuaternion.copy( scope.object.quaternion ); + zoomChanged = false; + + return true; + + } + + return false; + + }; + + }(); + + this.dispose = function () { + + scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false ); + scope.domElement.removeEventListener( 'mousedown', onMouseDown, false ); + scope.domElement.removeEventListener( 'wheel', onMouseWheel, false ); + + scope.domElement.removeEventListener( 'touchstart', onTouchStart, false ); + scope.domElement.removeEventListener( 'touchend', onTouchEnd, false ); + scope.domElement.removeEventListener( 'touchmove', onTouchMove, false ); + + document.removeEventListener( 'mousemove', onMouseMove, false ); + document.removeEventListener( 'mouseup', onMouseUp, false ); + + window.removeEventListener( 'keydown', onKeyDown, false ); + + //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? + + }; + + // + // internals + // + + var scope = this; + + var changeEvent = { type: 'change' }; + var startEvent = { type: 'start' }; + var endEvent = { type: 'end' }; + + var STATE = { NONE: - 1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY_PAN: 4 }; + + var state = STATE.NONE; + + var EPS = 0.000001; + + // current position in spherical coordinates + var spherical = new THREE.Spherical(); + var sphericalDelta = new THREE.Spherical(); + + var scale = 1; + var panOffset = new THREE.Vector3(); + var zoomChanged = false; + + var rotateStart = new THREE.Vector2(); + var rotateEnd = new THREE.Vector2(); + var rotateDelta = new THREE.Vector2(); + + var panStart = new THREE.Vector2(); + var panEnd = new THREE.Vector2(); + var panDelta = new THREE.Vector2(); + + var dollyStart = new THREE.Vector2(); + var dollyEnd = new THREE.Vector2(); + var dollyDelta = new THREE.Vector2(); + + function getAutoRotationAngle() { + + return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; + + } + + function getZoomScale() { + + return Math.pow( 0.95, scope.zoomSpeed ); + + } + + function rotateLeft( angle ) { + + sphericalDelta.theta -= angle; + + } + + function rotateUp( angle ) { + + sphericalDelta.phi -= angle; + + } + + var panLeft = function () { + + var v = new THREE.Vector3(); + + return function panLeft( distance, objectMatrix ) { + + v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix + v.multiplyScalar( - distance ); + + panOffset.add( v ); + + }; + + }(); + + var panUp = function () { + + var v = new THREE.Vector3(); + + return function panUp( distance, objectMatrix ) { + + if ( scope.screenSpacePanning === true ) { + + v.setFromMatrixColumn( objectMatrix, 1 ); + + } else { + + v.setFromMatrixColumn( objectMatrix, 0 ); + v.crossVectors( scope.object.up, v ); + + } + + v.multiplyScalar( distance ); + + panOffset.add( v ); + + }; + + }(); + + // deltaX and deltaY are in pixels; right and down are positive + var pan = function () { + + var offset = new THREE.Vector3(); + + return function pan( deltaX, deltaY ) { + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + if ( scope.object.isPerspectiveCamera ) { + + // perspective + var position = scope.object.position; + offset.copy( position ).sub( scope.target ); + var targetDistance = offset.length(); + + // half of the fov is center to top of screen + targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); + + // we use only clientHeight here so aspect ratio does not distort speed + panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix ); + panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix ); + + } else if ( scope.object.isOrthographicCamera ) { + + // orthographic + panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix ); + panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix ); + + } else { + + // camera neither orthographic nor perspective + console.warn( 'WARNING: OrbitControlsedit.js encountered an unknown camera type - pan disabled.' ); + scope.enablePan = false; + + } + + }; + + }(); + + function dollyIn( dollyScale ) { + + if ( scope.object.isPerspectiveCamera ) { + + scale /= dollyScale; + + } else if ( scope.object.isOrthographicCamera ) { + + scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) ); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + + } else { + + console.warn( 'WARNING: OrbitControlsedit.js encountered an unknown camera type - dolly/zoom disabled.' ); + scope.enableZoom = false; + + } + + } + + function dollyOut( dollyScale ) { + + if ( scope.object.isPerspectiveCamera ) { + + scale *= dollyScale; + + } else if ( scope.object.isOrthographicCamera ) { + + scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) ); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + + } else { + + console.warn( 'WARNING: OrbitControlsedit.js encountered an unknown camera type - dolly/zoom disabled.' ); + scope.enableZoom = false; + + } + + } + + // + // event callbacks - update the object state + // + + function handleMouseDownRotate( event ) { + + //console.log( 'handleMouseDownRotate' ); + + rotateStart.set( event.clientX, event.clientY ); + + } + + function handleMouseDownDolly( event ) { + + //console.log( 'handleMouseDownDolly' ); + + dollyStart.set( event.clientX, event.clientY ); + + } + + function handleMouseDownPan( event ) { + + //console.log( 'handleMouseDownPan' ); + + panStart.set( event.clientX, event.clientY ); + + } + + function handleMouseMoveRotate( event ) { + + //console.log( 'handleMouseMoveRotate' ); + + rotateEnd.set( event.clientX, event.clientY ); + + rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height + + rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); + + rotateStart.copy( rotateEnd ); + + scope.update(); + + } + + function handleMouseMoveDolly( event ) { + + //console.log( 'handleMouseMoveDolly' ); + + dollyEnd.set( event.clientX, event.clientY ); + + dollyDelta.subVectors( dollyEnd, dollyStart ); + + if ( dollyDelta.y > 0 ) { + + dollyIn( getZoomScale() ); + + } else if ( dollyDelta.y < 0 ) { + + dollyOut( getZoomScale() ); + + } + + dollyStart.copy( dollyEnd ); + + scope.update(); + + } + + function handleMouseMovePan( event ) { + + //console.log( 'handleMouseMovePan' ); + + panEnd.set( event.clientX, event.clientY ); + + panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + scope.update(); + + } + + function handleMouseUp( event ) { + + // console.log( 'handleMouseUp' ); + + } + + function handleMouseWheel( event ) { + + // console.log( 'handleMouseWheel' ); + + if ( event.deltaY < 0 ) { + + dollyOut( getZoomScale() ); + + } else if ( event.deltaY > 0 ) { + + dollyIn( getZoomScale() ); + + } + + scope.update(); + + } + + function handleKeyDown( event ) { + + // console.log( 'handleKeyDown' ); + + var needsUpdate = false; + + switch ( event.keyCode ) { + + case scope.keys.UP: + pan( 0, scope.keyPanSpeed ); + needsUpdate = true; + break; + + case scope.keys.BOTTOM: + pan( 0, - scope.keyPanSpeed ); + needsUpdate = true; + break; + + case scope.keys.LEFT: + pan( scope.keyPanSpeed, 0 ); + needsUpdate = true; + break; + + case scope.keys.RIGHT: + pan( - scope.keyPanSpeed, 0 ); + needsUpdate = true; + break; + + } + + if ( needsUpdate ) { + + // prevent the browser from scrolling on cursor keys + event.preventDefault(); + + scope.update(); + + } + + + } + + function handleTouchStartRotate( event ) { + + //console.log( 'handleTouchStartRotate' ); + + rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + + } + + function handleTouchStartDollyPan( event ) { + + //console.log( 'handleTouchStartDollyPan' ); + + if ( scope.enableZoom ) { + + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; + + var distance = Math.sqrt( dx * dx + dy * dy ); + + dollyStart.set( 0, distance ); + + } + + if ( scope.enablePan ) { + + var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); + var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); + + panStart.set( x, y ); + + } + + } + + function handleTouchMoveRotate( event ) { + + //console.log( 'handleTouchMoveRotate' ); + + rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + + rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height + + rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); + + rotateStart.copy( rotateEnd ); + + scope.update(); + + } + + function handleTouchMoveDollyPan( event ) { + + //console.log( 'handleTouchMoveDollyPan' ); + + if ( scope.enableZoom ) { + + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; + + var distance = Math.sqrt( dx * dx + dy * dy ); + + dollyEnd.set( 0, distance ); + + dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) ); + + dollyIn( dollyDelta.y ); + + dollyStart.copy( dollyEnd ); + + } + + if ( scope.enablePan ) { + + var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ); + var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ); + + panEnd.set( x, y ); + + panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + } + + scope.update(); + + } + + function handleTouchEnd( event ) { + + //console.log( 'handleTouchEnd' ); + + } + + // + // event handlers - FSM: listen for events and reset state + // + + function onMouseDown( event ) { + + if ( scope.enabled === false ) return; + + // Prevent the browser from scrolling. + + event.preventDefault(); + + // Manually set the focus since calling preventDefault above + // prevents the browser from setting it automatically. + + scope.domElement.focus ? scope.domElement.focus() : window.focus(); + + switch ( event.button ) { + + case scope.mouseButtons.RIGHT: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + if ( scope.enablePan === false ) return; + + handleMouseDownPan( event ); + + state = STATE.PAN; + + } else { + + if ( scope.enableRotate === false ) return; + + handleMouseDownRotate( event ); + + state = STATE.ROTATE; + + } + + break; + + case scope.mouseButtons.MIDDLE: + + if ( scope.enableZoom === false ) return; + + handleMouseDownDolly( event ); + + state = STATE.DOLLY; + + break; + + case scope.mouseButtons.LEFT: + + if ( scope.enablePan === false ) return; + + handleMouseDownPan( event ); + + state = STATE.PAN; + + break; + + } + + if ( state !== STATE.NONE ) { + + document.addEventListener( 'mousemove', onMouseMove, false ); + document.addEventListener( 'mouseup', onMouseUp, false ); + + scope.dispatchEvent( startEvent ); + + } + + } + + function onMouseMove( event ) { + if ( scope.enabled === false ) return; + + event.preventDefault(); + switch ( state ) { + + case STATE.ROTATE: + + if ( scope.enableRotate === false ) return; + + handleMouseMoveRotate( event ); + + break; + + case STATE.DOLLY: + + if ( scope.enableZoom === false ) return; + + handleMouseMoveDolly( event ); + + break; + + case STATE.PAN: + + if ( scope.enablePan === false ) return; + + handleMouseMovePan( event ); + + break; + + } + + } + + function onMouseUp( event ) { + + if ( scope.enabled === false ) return; + + handleMouseUp( event ); + + document.removeEventListener( 'mousemove', onMouseMove, false ); + document.removeEventListener( 'mouseup', onMouseUp, false ); + + scope.dispatchEvent( endEvent ); + + state = STATE.NONE; + + } + + function onMouseWheel( event ) { + + if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return; + + if (event.cancelable) { + // 判断默认行为是否已经被禁用 + if (!event.defaultPrevented) { + event.preventDefault(); + } + } + + event.stopPropagation(); + + scope.dispatchEvent( startEvent ); + + handleMouseWheel( event ); + + scope.dispatchEvent( endEvent ); + + } + + function onKeyDown( event ) { + + if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return; + + handleKeyDown( event ); + + } + + function onTouchStart( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + + switch ( event.touches.length ) { + + case 1: // one-fingered touch: rotate + + if ( scope.enableRotate === false ) return; + + handleTouchStartRotate( event ); + + state = STATE.TOUCH_ROTATE; + + break; + + case 2: // two-fingered touch: dolly-pan + + if ( scope.enableZoom === false && scope.enablePan === false ) return; + + handleTouchStartDollyPan( event ); + + state = STATE.TOUCH_DOLLY_PAN; + + break; + + default: + + state = STATE.NONE; + + } + + if ( state !== STATE.NONE ) { + + scope.dispatchEvent( startEvent ); + + } + + } + + function onTouchMove( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + event.stopPropagation(); + + switch ( event.touches.length ) { + + case 1: // one-fingered touch: rotate + + if ( scope.enableRotate === false ) return; + if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed? + + handleTouchMoveRotate( event ); + + break; + + case 2: // two-fingered touch: dolly-pan + + if ( scope.enableZoom === false && scope.enablePan === false ) return; + if ( state !== STATE.TOUCH_DOLLY_PAN ) return; // is this needed? + + handleTouchMoveDollyPan( event ); + + break; + + default: + + state = STATE.NONE; + + } + + } + + function onTouchEnd( event ) { + + if ( scope.enabled === false ) return; + + handleTouchEnd( event ); + + scope.dispatchEvent( endEvent ); + + state = STATE.NONE; + + } + + function onContextMenu( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + + } + + // + + scope.domElement.addEventListener( 'contextmenu', onContextMenu, false ); + + scope.domElement.addEventListener( 'mousedown', onMouseDown, false ); + scope.domElement.addEventListener( 'wheel', onMouseWheel, { passive: false } ); + + scope.domElement.addEventListener( 'touchstart', onTouchStart, { passive: false } ); + scope.domElement.addEventListener( 'touchend', onTouchEnd, { passive: false } ); + scope.domElement.addEventListener( 'touchmove', onTouchMove, { passive: false } ); + + window.addEventListener( 'keydown', onKeyDown, false ); + + // force an update at start + + this.update(); + +}; + +THREE.OrbitControlsedit.prototype = Object.create( THREE.EventDispatcher.prototype ); +THREE.OrbitControlsedit.prototype.constructor = THREE.OrbitControlsedit; + +Object.defineProperties( THREE.OrbitControlsedit.prototype, { + + center: { + + get: function () { + + console.warn( 'THREE.OrbitControlsedit: .center has been renamed to .target' ); + return this.target; + + } + + }, + + // backward compatibility + + noZoom: { + + get: function () { + + console.warn( 'THREE.OrbitControlsedit: .noZoom has been deprecated. Use .enableZoom instead.' ); + return ! this.enableZoom; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControlsedit: .noZoom has been deprecated. Use .enableZoom instead.' ); + this.enableZoom = ! value; + + } + + }, + + noRotate: { + + get: function () { + + console.warn( 'THREE.OrbitControlsedit: .noRotate has been deprecated. Use .enableRotate instead.' ); + return ! this.enableRotate; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControlsedit: .noRotate has been deprecated. Use .enableRotate instead.' ); + this.enableRotate = ! value; + + } + + }, + + noPan: { + + get: function () { + + console.warn( 'THREE.OrbitControlsedit: .noPan has been deprecated. Use .enablePan instead.' ); + return ! this.enablePan; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControlsedit: .noPan has been deprecated. Use .enablePan instead.' ); + this.enablePan = ! value; + + } + + }, + + noKeys: { + + get: function () { + + console.warn( 'THREE.OrbitControlsedit: .noKeys has been deprecated. Use .enableKeys instead.' ); + return ! this.enableKeys; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControlsedit: .noKeys has been deprecated. Use .enableKeys instead.' ); + this.enableKeys = ! value; + + } + + }, + + staticMoving: { + + get: function () { + + console.warn( 'THREE.OrbitControlsedit: .staticMoving has been deprecated. Use .enableDamping instead.' ); + return ! this.enableDamping; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControlsedit: .staticMoving has been deprecated. Use .enableDamping instead.' ); + this.enableDamping = ! value; + + } + + }, + + dynamicDampingFactor: { + + get: function () { + + console.warn( 'THREE.OrbitControlsedit: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); + return this.dampingFactor; + + }, + + set: function ( value ) { + + console.warn( 'THREE.OrbitControlsedit: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); + this.dampingFactor = value; + + } + + } + +} ); diff --git a/src/jlmap3d/edit/control/TransformControls.js b/src/jlmap3d/edit/control/TransformControls.js new file mode 100644 index 000000000..2770d3886 --- /dev/null +++ b/src/jlmap3d/edit/control/TransformControls.js @@ -0,0 +1,1460 @@ +/** + * @author arodic / https://github.com/arodic + */ + +THREE.TransformControls = function ( camera, domElement ) { + + THREE.Object3D.call( this ); + + domElement = ( domElement !== undefined ) ? domElement : document; + + this.visible = false; + + var _gizmo = new THREE.TransformControlsGizmo(); + this.add( _gizmo ); + + var _plane = new THREE.TransformControlsPlane(); + this.add( _plane ); + + var scope = this; + + // Define properties with getters/setter + // Setting the defined property will automatically trigger change event + // Defined properties are passed down to gizmo and plane + + defineProperty( "camera", camera ); + defineProperty( "object", undefined ); + defineProperty( "enabled", true ); + defineProperty( "axis", null ); + defineProperty( "mode", "translate" ); + defineProperty( "translationSnap", null ); + defineProperty( "rotationSnap", null ); + defineProperty( "space", "world" ); + defineProperty( "size", 1 ); + defineProperty( "dragging", false ); + defineProperty( "showX", true ); + defineProperty( "showY", true ); + defineProperty( "showZ", true ); + + var changeEvent = { type: "change" }; + var mouseDownEvent = { type: "mouseDown" }; + var mouseUpEvent = { type: "mouseUp", mode: scope.mode }; + var objectChangeEvent = { type: "objectChange" }; + + // Reusable utility variables + + var ray = new THREE.Raycaster(); + + var _tempVector = new THREE.Vector3(); + var _tempVector2 = new THREE.Vector3(); + var _tempQuaternion = new THREE.Quaternion(); + var _unit = { + X: new THREE.Vector3( 1, 0, 0 ), + Y: new THREE.Vector3( 0, 1, 0 ), + Z: new THREE.Vector3( 0, 0, 1 ) + }; + var _identityQuaternion = new THREE.Quaternion(); + var _alignVector = new THREE.Vector3(); + + var pointStart = new THREE.Vector3(); + var pointEnd = new THREE.Vector3(); + var offset = new THREE.Vector3(); + var rotationAxis = new THREE.Vector3(); + var startNorm = new THREE.Vector3(); + var endNorm = new THREE.Vector3(); + var rotationAngle = 0; + + var cameraPosition = new THREE.Vector3(); + var cameraQuaternion = new THREE.Quaternion(); + var cameraScale = new THREE.Vector3(); + + var parentPosition = new THREE.Vector3(); + var parentQuaternion = new THREE.Quaternion(); + var parentQuaternionInv = new THREE.Quaternion(); + var parentScale = new THREE.Vector3(); + + var worldPositionStart = new THREE.Vector3(); + var worldQuaternionStart = new THREE.Quaternion(); + var worldScaleStart = new THREE.Vector3(); + + var worldPosition = new THREE.Vector3(); + var worldQuaternion = new THREE.Quaternion(); + var worldQuaternionInv = new THREE.Quaternion(); + var worldScale = new THREE.Vector3(); + + var eye = new THREE.Vector3(); + + var positionStart = new THREE.Vector3(); + var quaternionStart = new THREE.Quaternion(); + var scaleStart = new THREE.Vector3(); + + // TODO: remove properties unused in plane and gizmo + + defineProperty( "worldPosition", worldPosition ); + defineProperty( "worldPositionStart", worldPositionStart ); + defineProperty( "worldQuaternion", worldQuaternion ); + defineProperty( "worldQuaternionStart", worldQuaternionStart ); + defineProperty( "cameraPosition", cameraPosition ); + defineProperty( "cameraQuaternion", cameraQuaternion ); + defineProperty( "pointStart", pointStart ); + defineProperty( "pointEnd", pointEnd ); + defineProperty( "rotationAxis", rotationAxis ); + defineProperty( "rotationAngle", rotationAngle ); + defineProperty( "eye", eye ); + + { + + domElement.addEventListener( "mousedown", onPointerDown, false ); + domElement.addEventListener( "touchstart", onPointerDown, false ); + domElement.addEventListener( "mousemove", onPointerHover, false ); + domElement.addEventListener( "touchmove", onPointerHover, false ); + domElement.addEventListener( "touchmove", onPointerMove, false ); + document.addEventListener( "mouseup", onPointerUp, false ); + domElement.addEventListener( "touchend", onPointerUp, false ); + domElement.addEventListener( "touchcancel", onPointerUp, false ); + domElement.addEventListener( "touchleave", onPointerUp, false ); + + } + + this.dispose = function () { + + domElement.removeEventListener( "mousedown", onPointerDown ); + domElement.removeEventListener( "touchstart", onPointerDown ); + domElement.removeEventListener( "mousemove", onPointerHover ); + domElement.removeEventListener( "touchmove", onPointerHover ); + domElement.removeEventListener( "touchmove", onPointerMove ); + document.removeEventListener( "mouseup", onPointerUp ); + domElement.removeEventListener( "touchend", onPointerUp ); + domElement.removeEventListener( "touchcancel", onPointerUp ); + domElement.removeEventListener( "touchleave", onPointerUp ); + + }; + + // Set current object + this.attach = function ( object ) { + + this.object = object; + this.visible = true; + + }; + + // Detatch from object + this.detach = function () { + + this.object = undefined; + this.visible = false; + this.axis = null; + + }; + + // Defined getter, setter and store for a property + function defineProperty( propName, defaultValue ) { + + var propValue = defaultValue; + + Object.defineProperty( scope, propName, { + + get: function() { + + return propValue !== undefined ? propValue : defaultValue; + + }, + + set: function( value ) { + + if ( propValue !== value ) { + + propValue = value; + _plane[ propName ] = value; + _gizmo[ propName ] = value; + + scope.dispatchEvent( { type: propName + "-changed", value: value } ); + scope.dispatchEvent( changeEvent ); + + } + + } + + }); + + scope[ propName ] = defaultValue; + _plane[ propName ] = defaultValue; + _gizmo[ propName ] = defaultValue; + + } + + // updateMatrixWorld updates key transformation variables + this.updateMatrixWorld = function () { + + if ( this.object !== undefined ) { + + this.object.updateMatrixWorld(); + this.object.parent.matrixWorld.decompose( parentPosition, parentQuaternion, parentScale ); + this.object.matrixWorld.decompose( worldPosition, worldQuaternion, worldScale ); + + parentQuaternionInv.copy( parentQuaternion ).inverse(); + worldQuaternionInv.copy( worldQuaternion ).inverse(); + + } + + this.camera.updateMatrixWorld(); + this.camera.matrixWorld.decompose( cameraPosition, cameraQuaternion, cameraScale ); + + if ( this.camera instanceof THREE.PerspectiveCamera ) { + + eye.copy( cameraPosition ).sub( worldPosition ).normalize(); + + } else if ( this.camera instanceof THREE.OrthographicCamera ) { + + eye.copy( cameraPosition ).normalize(); + + } + + THREE.Object3D.prototype.updateMatrixWorld.call( this ); + + }; + + this.pointerHover = function( pointer ) { + + if ( this.object === undefined || this.dragging === true || ( pointer.button !== undefined && pointer.button !== 0 ) ) return; + + ray.setFromCamera( pointer, this.camera ); + + var intersect = ray.intersectObjects( _gizmo.picker[ this.mode ].children, true )[ 0 ] || false; + + if ( intersect ) { + + this.axis = intersect.object.name; + + } else { + + this.axis = null; + + } + + }; + + this.pointerDown = function( pointer ) { + + if ( this.object === undefined || this.dragging === true || ( pointer.button !== undefined && pointer.button !== 0 ) ) return; + + if ( ( pointer.button === 0 || pointer.button === undefined ) && this.axis !== null ) { + + ray.setFromCamera( pointer, this.camera ); + + var planeIntersect = ray.intersectObjects( [ _plane ], true )[ 0 ] || false; + + if ( planeIntersect ) { + + var space = this.space; + + if ( this.mode === 'scale') { + + space = 'local'; + + } else if ( this.axis === 'E' || this.axis === 'XYZE' || this.axis === 'XYZ' ) { + + space = 'world'; + + } + + if ( space === 'local' && this.mode === 'rotate' ) { + + var snap = this.rotationSnap; + + if ( this.axis === 'X' && snap ) this.object.rotation.x = Math.round( this.object.rotation.x / snap ) * snap; + if ( this.axis === 'Y' && snap ) this.object.rotation.y = Math.round( this.object.rotation.y / snap ) * snap; + if ( this.axis === 'Z' && snap ) this.object.rotation.z = Math.round( this.object.rotation.z / snap ) * snap; + + } + + this.object.updateMatrixWorld(); + this.object.parent.updateMatrixWorld(); + + positionStart.copy( this.object.position ); + quaternionStart.copy( this.object.quaternion ); + scaleStart.copy( this.object.scale ); + + this.object.matrixWorld.decompose( worldPositionStart, worldQuaternionStart, worldScaleStart ); + + pointStart.copy( planeIntersect.point ).sub( worldPositionStart ); + + } + + this.dragging = true; + mouseDownEvent.mode = this.mode; + this.dispatchEvent( mouseDownEvent ); + + } + + }; + + this.pointerMove = function( pointer ) { + + var axis = this.axis; + var mode = this.mode; + var object = this.object; + var space = this.space; + + if ( mode === 'scale') { + + space = 'local'; + + } else if ( axis === 'E' || axis === 'XYZE' || axis === 'XYZ' ) { + + space = 'world'; + + } + + if ( object === undefined || axis === null || this.dragging === false || ( pointer.button !== undefined && pointer.button !== 0 ) ) return; + + ray.setFromCamera( pointer, this.camera ); + + var planeIntersect = ray.intersectObjects( [ _plane ], true )[ 0 ] || false; + + if ( planeIntersect === false ) return; + + pointEnd.copy( planeIntersect.point ).sub( worldPositionStart ); + + if ( mode === 'translate' ) { + + // Apply translate + + offset.copy( pointEnd ).sub( pointStart ); + + if ( space === 'local' && axis !== 'XYZ' ) { + offset.applyQuaternion( worldQuaternionInv ); + } + + if ( axis.indexOf( 'X' ) === -1 ) offset.x = 0; + if ( axis.indexOf( 'Y' ) === -1 ) offset.y = 0; + if ( axis.indexOf( 'Z' ) === -1 ) offset.z = 0; + + if ( space === 'local' && axis !== 'XYZ') { + offset.applyQuaternion( quaternionStart ).divide( parentScale ); + } else { + offset.applyQuaternion( parentQuaternionInv ).divide( parentScale ); + } + + object.position.copy( offset ).add( positionStart ); + + // Apply translation snap + + if ( this.translationSnap ) { + + if ( space === 'local' ) { + + object.position.applyQuaternion(_tempQuaternion.copy( quaternionStart ).inverse() ); + + if ( axis.search( 'X' ) !== -1 ) { + object.position.x = Math.round( object.position.x / this.translationSnap ) * this.translationSnap; + } + + if ( axis.search( 'Y' ) !== -1 ) { + object.position.y = Math.round( object.position.y / this.translationSnap ) * this.translationSnap; + } + + if ( axis.search( 'Z' ) !== -1 ) { + object.position.z = Math.round( object.position.z / this.translationSnap ) * this.translationSnap; + } + + object.position.applyQuaternion( quaternionStart ); + + } + + if ( space === 'world' ) { + + if ( object.parent ) { + object.position.add( _tempVector.setFromMatrixPosition( object.parent.matrixWorld ) ); + } + + if ( axis.search( 'X' ) !== -1 ) { + object.position.x = Math.round( object.position.x / this.translationSnap ) * this.translationSnap; + } + + if ( axis.search( 'Y' ) !== -1 ) { + object.position.y = Math.round( object.position.y / this.translationSnap ) * this.translationSnap; + } + + if ( axis.search( 'Z' ) !== -1 ) { + object.position.z = Math.round( object.position.z / this.translationSnap ) * this.translationSnap; + } + + if ( object.parent ) { + object.position.sub( _tempVector.setFromMatrixPosition( object.parent.matrixWorld ) ); + } + + } + + } + + } else if ( mode === 'scale' ) { + + if ( axis.search( 'XYZ' ) !== -1 ) { + + var d = pointEnd.length() / pointStart.length(); + + if ( pointEnd.dot( pointStart ) < 0 ) d *= -1; + + _tempVector.set( d, d, d ); + + } else { + + _tempVector.copy( pointEnd ).divide( pointStart ); + + if ( axis.search( 'X' ) === -1 ) { + _tempVector.x = 1; + } + if ( axis.search( 'Y' ) === -1 ) { + _tempVector.y = 1; + } + if ( axis.search( 'Z' ) === -1 ) { + _tempVector.z = 1; + } + + } + + // Apply scale + + object.scale.copy( scaleStart ).multiply( _tempVector ); + + } else if ( mode === 'rotate' ) { + + offset.copy( pointEnd ).sub( pointStart ); + + var ROTATION_SPEED = 20 / worldPosition.distanceTo( _tempVector.setFromMatrixPosition( this.camera.matrixWorld ) ); + + if ( axis === 'E' ) { + + rotationAxis.copy( eye ); + rotationAngle = pointEnd.angleTo( pointStart ); + + startNorm.copy( pointStart ).normalize(); + endNorm.copy( pointEnd ).normalize(); + + rotationAngle *= ( endNorm.cross( startNorm ).dot( eye ) < 0 ? 1 : -1); + + } else if ( axis === 'XYZE' ) { + + rotationAxis.copy( offset ).cross( eye ).normalize( ); + rotationAngle = offset.dot( _tempVector.copy( rotationAxis ).cross( this.eye ) ) * ROTATION_SPEED; + + } else if ( axis === 'X' || axis === 'Y' || axis === 'Z' ) { + + rotationAxis.copy( _unit[ axis ] ); + + _tempVector.copy( _unit[ axis ] ); + + if ( space === 'local' ) { + _tempVector.applyQuaternion( worldQuaternion ); + } + + rotationAngle = offset.dot( _tempVector.cross( eye ).normalize() ) * ROTATION_SPEED; + + } + + // Apply rotation snap + + if ( this.rotationSnap ) rotationAngle = Math.round( rotationAngle / this.rotationSnap ) * this.rotationSnap; + + this.rotationAngle = rotationAngle; + + // Apply rotate + if ( space === 'local' && axis !== 'E' && axis !== 'XYZE' ) { + + object.quaternion.copy( quaternionStart ); + object.quaternion.multiply( _tempQuaternion.setFromAxisAngle( rotationAxis, rotationAngle ) ).normalize(); + + } else { + + rotationAxis.applyQuaternion( parentQuaternionInv ); + object.quaternion.copy( _tempQuaternion.setFromAxisAngle( rotationAxis, rotationAngle ) ); + object.quaternion.multiply( quaternionStart ).normalize(); + + } + + } + + this.dispatchEvent( changeEvent ); + this.dispatchEvent( objectChangeEvent ); + + }; + + this.pointerUp = function( pointer ) { + + if ( pointer.button !== undefined && pointer.button !== 0 ) return; + + if ( this.dragging && ( this.axis !== null ) ) { + + mouseUpEvent.mode = this.mode; + this.dispatchEvent( mouseUpEvent ); + + } + + this.dragging = false; + + if ( pointer.button === undefined ) this.axis = null; + + }; + + // normalize mouse / touch pointer and remap {x,y} to view space. + + function getPointer( event ) { + + var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event; + + var rect = domElement.getBoundingClientRect(); + + return { + x: ( pointer.clientX - rect.left ) / rect.width * 2 - 1, + y: - ( pointer.clientY - rect.top ) / rect.height * 2 + 1, + button: event.button + }; + + } + + // mouse / touch event handlers + + function onPointerHover( event ) { + + if ( !scope.enabled ) return; + + scope.pointerHover( getPointer( event ) ); + + } + + function onPointerDown( event ) { + + if ( !scope.enabled ) return; + + document.addEventListener( "mousemove", onPointerMove, false ); + + scope.pointerHover( getPointer( event ) ); + scope.pointerDown( getPointer( event ) ); + + } + + function onPointerMove( event ) { + + if ( !scope.enabled ) return; + + scope.pointerMove( getPointer( event ) ); + + } + + function onPointerUp( event ) { + + if ( !scope.enabled ) return; + + document.removeEventListener( "mousemove", onPointerMove, false ); + + scope.pointerUp( getPointer( event ) ); + + } + + // TODO: depricate + + this.getMode = function () { + + return scope.mode; + + }; + + this.setMode = function ( mode ) { + + scope.mode = mode; + + }; + + this.setTranslationSnap = function ( translationSnap ) { + + scope.translationSnap = translationSnap; + + }; + + this.setRotationSnap = function ( rotationSnap ) { + + scope.rotationSnap = rotationSnap; + + }; + + this.setSize = function ( size ) { + + scope.size = size; + + }; + + this.setSpace = function ( space ) { + + scope.space = space; + + }; + + this.update = function () { + + console.warn( 'THREE.TransformControls: update function has been depricated.' ); + + }; + +}; + +THREE.TransformControls.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), { + + constructor: THREE.TransformControls, + + isTransformControls: true + +} ); + + +THREE.TransformControlsGizmo = function () { + + 'use strict'; + + THREE.Object3D.call( this ); + + this.type = 'TransformControlsGizmo'; + + // shared materials + + var gizmoMaterial = new THREE.MeshBasicMaterial({ + depthTest: false, + depthWrite: false, + transparent: true, + side: THREE.DoubleSide, + fog: false + }); + + var gizmoLineMaterial = new THREE.LineBasicMaterial({ + depthTest: false, + depthWrite: false, + transparent: true, + linewidth: 1, + fog: false + }); + + // Make unique material for each axis/color + + var matInvisible = gizmoMaterial.clone(); + matInvisible.opacity = 0.15; + + var matHelper = gizmoMaterial.clone(); + matHelper.opacity = 0.33; + + var matRed = gizmoMaterial.clone(); + matRed.color.set( 0xff0000 ); + + var matGreen = gizmoMaterial.clone(); + matGreen.color.set( 0x00ff00 ); + + var matBlue = gizmoMaterial.clone(); + matBlue.color.set( 0x0000ff ); + + var matWhiteTransperent = gizmoMaterial.clone(); + matWhiteTransperent.opacity = 0.25; + + var matYellowTransparent = matWhiteTransperent.clone(); + matYellowTransparent.color.set( 0xffff00 ); + + var matCyanTransparent = matWhiteTransperent.clone(); + matCyanTransparent.color.set( 0x00ffff ); + + var matMagentaTransparent = matWhiteTransperent.clone(); + matMagentaTransparent.color.set( 0xff00ff ); + + var matYellow = gizmoMaterial.clone(); + matYellow.color.set( 0xffff00 ); + + var matLineRed = gizmoLineMaterial.clone(); + matLineRed.color.set( 0xff0000 ); + + var matLineGreen = gizmoLineMaterial.clone(); + matLineGreen.color.set( 0x00ff00 ); + + var matLineBlue = gizmoLineMaterial.clone(); + matLineBlue.color.set( 0x0000ff ); + + var matLineCyan = gizmoLineMaterial.clone(); + matLineCyan.color.set( 0x00ffff ); + + var matLineMagenta = gizmoLineMaterial.clone(); + matLineMagenta.color.set( 0xff00ff ); + + var matLineYellow = gizmoLineMaterial.clone(); + matLineYellow.color.set( 0xffff00 ); + + var matLineGray = gizmoLineMaterial.clone(); + matLineGray.color.set( 0x787878); + + var matLineYellowTransparent = matLineYellow.clone(); + matLineYellowTransparent.opacity = 0.25; + + // reusable geometry + + var arrowGeometry = new THREE.CylinderBufferGeometry( 0, 0.05, 0.2, 12, 1, false); + + var scaleHandleGeometry = new THREE.BoxBufferGeometry( 0.125, 0.125, 0.125); + + var lineGeometry = new THREE.BufferGeometry( ); + lineGeometry.addAttribute('position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 1, 0, 0 ], 3 ) ); + + var CircleGeometry = function( radius, arc ) { + + var geometry = new THREE.BufferGeometry( ); + var vertices = []; + + for ( var i = 0; i <= 64 * arc; ++i ) { + + vertices.push( 0, Math.cos( i / 32 * Math.PI ) * radius, Math.sin( i / 32 * Math.PI ) * radius ); + + } + + geometry.addAttribute('position', new THREE.Float32BufferAttribute( vertices, 3 ) ); + + return geometry; + + }; + + // Special geometry for transform helper. If scaled with position vector it spans from [0,0,0] to position + + var TranslateHelperGeometry = function( radius, arc ) { + + var geometry = new THREE.BufferGeometry() + + geometry.addAttribute('position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 1, 1, 1 ], 3 ) ); + + return geometry; + + }; + + // Gizmo definitions - custom hierarchy definitions for setupGizmo() function + + var gizmoTranslate = { + X: [ + [ new THREE.Mesh( arrowGeometry, matRed ), [ 1, 0, 0 ], [ 0, 0, -Math.PI / 2 ], null, 'fwd' ], + [ new THREE.Mesh( arrowGeometry, matRed ), [ 1, 0, 0 ], [ 0, 0, Math.PI / 2 ], null, 'bwd' ], + [ new THREE.Line( lineGeometry, matLineRed ) ] + ], + Y: [ + [ new THREE.Mesh( arrowGeometry, matGreen ), [ 0, 1, 0 ], null, null, 'fwd' ], + [ new THREE.Mesh( arrowGeometry, matGreen ), [ 0, 1, 0 ], [ Math.PI, 0, 0 ], null, 'bwd' ], + [ new THREE.Line( lineGeometry, matLineGreen ), null, [ 0, 0, Math.PI / 2 ] ] + ], + Z: [ + [ new THREE.Mesh( arrowGeometry, matBlue ), [ 0, 0, 1 ], [ Math.PI / 2, 0, 0 ], null, 'fwd' ], + [ new THREE.Mesh( arrowGeometry, matBlue ), [ 0, 0, 1 ], [ -Math.PI / 2, 0, 0 ], null, 'bwd' ], + [ new THREE.Line( lineGeometry, matLineBlue ), null, [ 0, -Math.PI / 2, 0 ] ] + ], + XYZ: [ + [ new THREE.Mesh( new THREE.OctahedronBufferGeometry( 0.1, 0 ), matWhiteTransperent ), [ 0, 0, 0 ], [ 0, 0, 0 ] ] + ], + XY: [ + [ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.295, 0.295 ), matYellowTransparent ), [ 0.15, 0.15, 0 ] ], + [ new THREE.Line( lineGeometry, matLineYellow ), [ 0.18, 0.3, 0 ], null, [ 0.125, 1, 1 ] ], + [ new THREE.Line( lineGeometry, matLineYellow ), [ 0.3, 0.18, 0 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ] ] + ], + YZ: [ + [ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.295, 0.295 ), matCyanTransparent ), [ 0, 0.15, 0.15 ], [ 0, Math.PI / 2, 0 ] ], + [ new THREE.Line( lineGeometry, matLineCyan ), [ 0, 0.18, 0.3 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ] ], + [ new THREE.Line( lineGeometry, matLineCyan ), [ 0, 0.3, 0.18 ], [ 0, -Math.PI / 2, 0 ], [ 0.125, 1, 1 ] ] + ], + XZ: [ + [ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.295, 0.295 ), matMagentaTransparent ), [ 0.15, 0, 0.15 ], [ -Math.PI / 2, 0, 0 ] ], + [ new THREE.Line( lineGeometry, matLineMagenta ), [ 0.18, 0, 0.3 ], null, [ 0.125, 1, 1 ] ], + [ new THREE.Line( lineGeometry, matLineMagenta ), [ 0.3, 0, 0.18 ], [ 0, -Math.PI / 2, 0 ], [ 0.125, 1, 1 ] ] + ] + }; + + var pickerTranslate = { + X: [ + [ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.2, 0, 1, 4, 1, false ), matInvisible ), [ 0.6, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ] + ], + Y: [ + [ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.2, 0, 1, 4, 1, false ), matInvisible ), [ 0, 0.6, 0 ] ] + ], + Z: [ + [ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.2, 0, 1, 4, 1, false ), matInvisible ), [ 0, 0, 0.6 ], [ Math.PI / 2, 0, 0 ] ] + ], + XYZ: [ + [ new THREE.Mesh( new THREE.OctahedronBufferGeometry( 0.2, 0 ), matInvisible ) ] + ], + XY: [ + [ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), matInvisible ), [ 0.2, 0.2, 0 ] ] + ], + YZ: [ + [ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), matInvisible ), [ 0, 0.2, 0.2 ], [ 0, Math.PI / 2, 0 ] ] + ], + XZ: [ + [ new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.4, 0.4 ), matInvisible ), [ 0.2, 0, 0.2 ], [ -Math.PI / 2, 0, 0 ] ] + ] + }; + + var helperTranslate = { + START: [ + [ new THREE.Mesh( new THREE.OctahedronBufferGeometry( 0.01, 2 ), matHelper ), null, null, null, 'helper' ] + ], + END: [ + [ new THREE.Mesh( new THREE.OctahedronBufferGeometry( 0.01, 2 ), matHelper ), null, null, null, 'helper' ] + ], + DELTA: [ + [ new THREE.Line( TranslateHelperGeometry(), matHelper ), null, null, null, 'helper' ] + ], + X: [ + [ new THREE.Line( lineGeometry, matHelper.clone() ), [ -1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ] + ], + Y: [ + [ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, -1e3, 0 ], [ 0, 0, Math.PI / 2 ], [ 1e6, 1, 1 ], 'helper' ] + ], + Z: [ + [ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, 0, -1e3 ], [ 0, -Math.PI / 2, 0 ], [ 1e6, 1, 1 ], 'helper' ] + ] + }; + + var gizmoRotate = { + X: [ + [ new THREE.Line( CircleGeometry( 1, 0.5 ), matLineRed ) ], + [ new THREE.Mesh( new THREE.OctahedronBufferGeometry( 0.04, 0 ), matRed ), [ 0, 0, 0.99 ], null, [ 1, 3, 1 ] ], + ], + Y: [ + [ new THREE.Line( CircleGeometry( 1, 0.5 ), matLineGreen ), null, [ 0, 0, -Math.PI / 2 ] ], + [ new THREE.Mesh( new THREE.OctahedronBufferGeometry( 0.04, 0 ), matGreen ), [ 0, 0, 0.99 ], null, [ 3, 1, 1 ] ], + ], + Z: [ + [ new THREE.Line( CircleGeometry( 1, 0.5 ), matLineBlue ), null, [ 0, Math.PI / 2, 0 ] ], + [ new THREE.Mesh( new THREE.OctahedronBufferGeometry( 0.04, 0 ), matBlue ), [ 0.99, 0, 0 ], null, [ 1, 3, 1 ] ], + ], + E: [ + [ new THREE.Line( CircleGeometry( 1.25, 1 ), matLineYellowTransparent ), null, [ 0, Math.PI / 2, 0 ] ], + [ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ 1.17, 0, 0 ], [ 0, 0, -Math.PI / 2 ], [ 1, 1, 0.001 ]], + [ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ -1.17, 0, 0 ], [ 0, 0, Math.PI / 2 ], [ 1, 1, 0.001 ]], + [ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ 0, -1.17, 0 ], [ Math.PI, 0, 0 ], [ 1, 1, 0.001 ]], + [ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ 0, 1.17, 0 ], [ 0, 0, 0 ], [ 1, 1, 0.001 ]], + ], + XYZE: [ + [ new THREE.Line( CircleGeometry( 1, 1 ), matLineGray ), null, [ 0, Math.PI / 2, 0 ] ] + ] + }; + + var helperRotate = { + AXIS: [ + [ new THREE.Line( lineGeometry, matHelper.clone() ), [ -1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ] + ] + }; + + var pickerRotate = { + X: [ + [ new THREE.Mesh( new THREE.TorusBufferGeometry( 1, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ 0, -Math.PI / 2, -Math.PI / 2 ] ], + ], + Y: [ + [ new THREE.Mesh( new THREE.TorusBufferGeometry( 1, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ Math.PI / 2, 0, 0 ] ], + ], + Z: [ + [ new THREE.Mesh( new THREE.TorusBufferGeometry( 1, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ], + ], + E: [ + [ new THREE.Mesh( new THREE.TorusBufferGeometry( 1.25, 0.1, 2, 24 ), matInvisible ) ] + ], + XYZE: [ + [ new THREE.Mesh( new THREE.SphereBufferGeometry( 0.7, 10, 8 ), matInvisible ) ] + ] + }; + + var gizmoScale = { + X: [ + [ new THREE.Mesh( scaleHandleGeometry, matRed ), [ 0.8, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ], + [ new THREE.Line( lineGeometry, matLineRed ), null, null, [ 0.8, 1, 1 ] ] + ], + Y: [ + [ new THREE.Mesh( scaleHandleGeometry, matGreen ), [ 0, 0.8, 0 ] ], + [ new THREE.Line( lineGeometry, matLineGreen ), null, [ 0, 0, Math.PI / 2 ], [ 0.8, 1, 1 ] ] + ], + Z: [ + [ new THREE.Mesh( scaleHandleGeometry, matBlue ), [ 0, 0, 0.8 ], [ Math.PI / 2, 0, 0 ] ], + [ new THREE.Line( lineGeometry, matLineBlue ), null, [ 0, -Math.PI / 2, 0 ], [ 0.8, 1, 1 ] ] + ], + XY: [ + [ new THREE.Mesh( scaleHandleGeometry, matYellowTransparent ), [ 0.85, 0.85, 0 ], null, [ 2, 2, 0.2 ] ], + [ new THREE.Line( lineGeometry, matLineYellow ), [ 0.855, 0.98, 0 ], null, [ 0.125, 1, 1 ] ], + [ new THREE.Line( lineGeometry, matLineYellow ), [ 0.98, 0.855, 0 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ] ] + ], + YZ: [ + [ new THREE.Mesh( scaleHandleGeometry, matCyanTransparent ), [ 0, 0.85, 0.85 ], null, [ 0.2, 2, 2 ] ], + [ new THREE.Line( lineGeometry, matLineCyan ), [ 0, 0.855, 0.98 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ] ], + [ new THREE.Line( lineGeometry, matLineCyan ), [ 0, 0.98, 0.855 ], [ 0, -Math.PI / 2, 0 ], [ 0.125, 1, 1 ] ] + ], + XZ: [ + [ new THREE.Mesh( scaleHandleGeometry, matMagentaTransparent ), [ 0.85, 0, 0.85 ], null, [ 2, 0.2, 2 ] ], + [ new THREE.Line( lineGeometry, matLineMagenta ), [ 0.855, 0, 0.98 ], null, [ 0.125, 1, 1 ] ], + [ new THREE.Line( lineGeometry, matLineMagenta ), [ 0.98, 0, 0.855 ], [ 0, -Math.PI / 2, 0 ], [ 0.125, 1, 1 ] ] + ], + XYZX: [ + [ new THREE.Mesh( new THREE.BoxBufferGeometry( 0.125, 0.125, 0.125 ), matWhiteTransperent ), [ 1.1, 0, 0 ] ], + ], + XYZY: [ + [ new THREE.Mesh( new THREE.BoxBufferGeometry( 0.125, 0.125, 0.125 ), matWhiteTransperent ), [ 0, 1.1, 0 ] ], + ], + XYZZ: [ + [ new THREE.Mesh( new THREE.BoxBufferGeometry( 0.125, 0.125, 0.125 ), matWhiteTransperent ), [ 0, 0, 1.1 ] ], + ] + }; + + var pickerScale = { + X: [ + [ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.2, 0, 0.8, 4, 1, false ), matInvisible ), [ 0.5, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ] + ], + Y: [ + [ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.2, 0, 0.8, 4, 1, false ), matInvisible ), [ 0, 0.5, 0 ] ] + ], + Z: [ + [ new THREE.Mesh( new THREE.CylinderBufferGeometry( 0.2, 0, 0.8, 4, 1, false ), matInvisible ), [ 0, 0, 0.5 ], [ Math.PI / 2, 0, 0 ] ] + ], + XY: [ + [ new THREE.Mesh( scaleHandleGeometry, matInvisible ), [ 0.85, 0.85, 0 ], null, [ 3, 3, 0.2 ] ], + ], + YZ: [ + [ new THREE.Mesh( scaleHandleGeometry, matInvisible ), [ 0, 0.85, 0.85 ], null, [ 0.2, 3, 3 ] ], + ], + XZ: [ + [ new THREE.Mesh( scaleHandleGeometry, matInvisible ), [ 0.85, 0, 0.85 ], null, [ 3, 0.2, 3 ] ], + ], + XYZX: [ + [ new THREE.Mesh( new THREE.BoxBufferGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 1.1, 0, 0 ] ], + ], + XYZY: [ + [ new THREE.Mesh( new THREE.BoxBufferGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 0, 1.1, 0 ] ], + ], + XYZZ: [ + [ new THREE.Mesh( new THREE.BoxBufferGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 0, 0, 1.1 ] ], + ] + }; + + var helperScale = { + X: [ + [ new THREE.Line( lineGeometry, matHelper.clone() ), [ -1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ] + ], + Y: [ + [ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, -1e3, 0 ], [ 0, 0, Math.PI / 2 ], [ 1e6, 1, 1 ], 'helper' ] + ], + Z: [ + [ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, 0, -1e3 ], [ 0, -Math.PI / 2, 0 ], [ 1e6, 1, 1 ], 'helper' ] + ] + }; + + // Creates an Object3D with gizmos described in custom hierarchy definition. + + var setupGizmo = function( gizmoMap ) { + + var gizmo = new THREE.Object3D(); + + for ( var name in gizmoMap ) { + + for ( var i = gizmoMap[ name ].length; i --; ) { + + var object = gizmoMap[ name ][ i ][ 0 ].clone(); + var position = gizmoMap[ name ][ i ][ 1 ]; + var rotation = gizmoMap[ name ][ i ][ 2 ]; + var scale = gizmoMap[ name ][ i ][ 3 ]; + var tag = gizmoMap[ name ][ i ][ 4 ]; + + // name and tag properties are essential for picking and updating logic. + object.name = name; + object.tag = tag; + + if (position) { + object.position.set(position[ 0 ], position[ 1 ], position[ 2 ]); + } + if (rotation) { + object.rotation.set(rotation[ 0 ], rotation[ 1 ], rotation[ 2 ]); + } + if (scale) { + object.scale.set(scale[ 0 ], scale[ 1 ], scale[ 2 ]); + } + + object.updateMatrix(); + + var tempGeometry = object.geometry.clone(); + tempGeometry.applyMatrix(object.matrix); + object.geometry = tempGeometry; + + object.position.set( 0, 0, 0 ); + object.rotation.set( 0, 0, 0 ); + object.scale.set(1, 1, 1); + + gizmo.add(object); + + } + + } + + return gizmo; + + }; + + // Reusable utility variables + + var tempVector = new THREE.Vector3( 0, 0, 0 ); + var tempEuler = new THREE.Euler(); + var alignVector = new THREE.Vector3( 0, 1, 0 ); + var zeroVector = new THREE.Vector3( 0, 0, 0 ); + var lookAtMatrix = new THREE.Matrix4(); + var tempQuaternion = new THREE.Quaternion(); + var tempQuaternion2 = new THREE.Quaternion(); + var identityQuaternion = new THREE.Quaternion(); + + var unitX = new THREE.Vector3( 1, 0, 0 ); + var unitY = new THREE.Vector3( 0, 1, 0 ); + var unitZ = new THREE.Vector3( 0, 0, 1 ); + + // Gizmo creation + + this.gizmo = {}; + this.picker = {}; + this.helper = {}; + + this.add( this.gizmo[ "translate" ] = setupGizmo( gizmoTranslate ) ); + this.add( this.gizmo[ "rotate" ] = setupGizmo( gizmoRotate ) ); + this.add( this.gizmo[ "scale" ] = setupGizmo( gizmoScale ) ); + this.add( this.picker[ "translate" ] = setupGizmo( pickerTranslate ) ); + this.add( this.picker[ "rotate" ] = setupGizmo( pickerRotate ) ); + this.add( this.picker[ "scale" ] = setupGizmo( pickerScale ) ); + this.add( this.helper[ "translate" ] = setupGizmo( helperTranslate ) ); + this.add( this.helper[ "rotate" ] = setupGizmo( helperRotate ) ); + this.add( this.helper[ "scale" ] = setupGizmo( helperScale ) ); + + // Pickers should be hidden always + + this.picker[ "translate" ].visible = false; + this.picker[ "rotate" ].visible = false; + this.picker[ "scale" ].visible = false; + + // updateMatrixWorld will update transformations and appearance of individual handles + + this.updateMatrixWorld = function () { + + var space = this.space; + + if ( this.mode === 'scale' ) space = 'local'; // scale always oriented to local rotation + + var quaternion = space === "local" ? this.worldQuaternion : identityQuaternion; + + // Show only gizmos for current transform mode + + this.gizmo[ "translate" ].visible = this.mode === "translate"; + this.gizmo[ "rotate" ].visible = this.mode === "rotate"; + this.gizmo[ "scale" ].visible = this.mode === "scale"; + + this.helper[ "translate" ].visible = this.mode === "translate"; + this.helper[ "rotate" ].visible = this.mode === "rotate"; + this.helper[ "scale" ].visible = this.mode === "scale"; + + + var handles = []; + handles = handles.concat( this.picker[ this.mode ].children ); + handles = handles.concat( this.gizmo[ this.mode ].children ); + handles = handles.concat( this.helper[ this.mode ].children ); + + for ( var i = 0; i < handles.length; i++ ) { + + var handle = handles[i]; + + // hide aligned to camera + + handle.visible = true; + handle.rotation.set( 0, 0, 0 ); + handle.position.copy( this.worldPosition ); + + var eyeDistance = this.worldPosition.distanceTo( this.cameraPosition); + handle.scale.set( 1, 1, 1 ).multiplyScalar( eyeDistance * this.size / 7 ); + + // TODO: simplify helpers and consider decoupling from gizmo + + if ( handle.tag === 'helper' ) { + + handle.visible = false; + + if ( handle.name === 'AXIS' ) { + + handle.position.copy( this.worldPositionStart ); + handle.visible = !!this.axis; + + if ( this.axis === 'X' ) { + + tempQuaternion.setFromEuler( tempEuler.set( 0, 0, 0 ) ); + handle.quaternion.copy( quaternion ).multiply( tempQuaternion ); + + if ( Math.abs( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) { + handle.visible = false; + } + + } + + if ( this.axis === 'Y' ) { + + tempQuaternion.setFromEuler( tempEuler.set( 0, 0, Math.PI / 2 ) ); + handle.quaternion.copy( quaternion ).multiply( tempQuaternion ); + + if ( Math.abs( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) { + handle.visible = false; + } + + } + + if ( this.axis === 'Z' ) { + + tempQuaternion.setFromEuler( tempEuler.set( 0, Math.PI / 2, 0 ) ); + handle.quaternion.copy( quaternion ).multiply( tempQuaternion ); + + if ( Math.abs( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) { + handle.visible = false; + } + + } + + if ( this.axis === 'XYZE' ) { + + tempQuaternion.setFromEuler( tempEuler.set( 0, Math.PI / 2, 0 ) ); + alignVector.copy( this.rotationAxis ); + handle.quaternion.setFromRotationMatrix( lookAtMatrix.lookAt( zeroVector, alignVector, unitY ) ); + handle.quaternion.multiply( tempQuaternion ); + handle.visible = this.dragging; + + } + + if ( this.axis === 'E' ) { + + handle.visible = false; + + } + + + } else if ( handle.name === 'START' ) { + + handle.position.copy( this.worldPositionStart ); + handle.visible = this.dragging; + + } else if ( handle.name === 'END' ) { + + handle.position.copy( this.worldPosition ); + handle.visible = this.dragging; + + } else if ( handle.name === 'DELTA' ) { + + handle.position.copy( this.worldPositionStart ); + handle.quaternion.copy( this.worldQuaternionStart ); + tempVector.set( 1e-10, 1e-10, 1e-10 ).add( this.worldPositionStart ).sub( this.worldPosition ).multiplyScalar( -1 ); + tempVector.applyQuaternion( this.worldQuaternionStart.clone().inverse() ); + handle.scale.copy( tempVector ); + handle.visible = this.dragging; + + } else { + + handle.quaternion.copy( quaternion ); + + if ( this.dragging ) { + + handle.position.copy( this.worldPositionStart ); + + } else { + + handle.position.copy( this.worldPosition ); + + } + + if ( this.axis ) { + + handle.visible = this.axis.search( handle.name ) !== -1; + + } + + } + + // If updating helper, skip rest of the loop + continue; + + } + + // Align handles to current local or world rotation + + handle.quaternion.copy( quaternion ); + + if ( this.mode === 'translate' || this.mode === 'scale' ) { + + // Hide translate and scale axis facing the camera + + var AXIS_HIDE_TRESHOLD = 0.99; + var PLANE_HIDE_TRESHOLD = 0.2; + var AXIS_FLIP_TRESHOLD = -0.4; + + + if ( handle.name === 'X' || handle.name === 'XYZX' ) { + if ( Math.abs( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_TRESHOLD ) { + handle.scale.set( 1e-10, 1e-10, 1e-10 ); + handle.visible = false; + } + } + if ( handle.name === 'Y' || handle.name === 'XYZY' ) { + if ( Math.abs( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_TRESHOLD ) { + handle.scale.set( 1e-10, 1e-10, 1e-10 ); + handle.visible = false; + } + } + if ( handle.name === 'Z' || handle.name === 'XYZZ' ) { + if ( Math.abs( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_TRESHOLD ) { + handle.scale.set( 1e-10, 1e-10, 1e-10 ); + handle.visible = false; + } + } + if ( handle.name === 'XY' ) { + if ( Math.abs( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_TRESHOLD ) { + handle.scale.set( 1e-10, 1e-10, 1e-10 ); + handle.visible = false; + } + } + if ( handle.name === 'YZ' ) { + if ( Math.abs( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_TRESHOLD ) { + handle.scale.set( 1e-10, 1e-10, 1e-10 ); + handle.visible = false; + } + } + if ( handle.name === 'XZ' ) { + if ( Math.abs( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_TRESHOLD ) { + handle.scale.set( 1e-10, 1e-10, 1e-10 ); + handle.visible = false; + } + } + + // Flip translate and scale axis ocluded behind another axis + + if ( handle.name.search( 'X' ) !== -1 ) { + if ( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) < AXIS_FLIP_TRESHOLD ) { + if ( handle.tag === 'fwd' ) { + handle.visible = false; + } else { + handle.scale.x *= -1; + } + } else if ( handle.tag === 'bwd' ) { + handle.visible = false; + } + } + + if ( handle.name.search( 'Y' ) !== -1 ) { + if ( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) < AXIS_FLIP_TRESHOLD ) { + if ( handle.tag === 'fwd' ) { + handle.visible = false; + } else { + handle.scale.y *= -1; + } + } else if ( handle.tag === 'bwd' ) { + handle.visible = false; + } + } + + if ( handle.name.search( 'Z' ) !== -1 ) { + if ( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) < AXIS_FLIP_TRESHOLD ) { + if ( handle.tag === 'fwd' ) { + handle.visible = false; + } else { + handle.scale.z *= -1; + } + } else if ( handle.tag === 'bwd' ) { + handle.visible = false; + } + } + + } else if ( this.mode === 'rotate' ) { + + // Align handles to current local or world rotation + + tempQuaternion2.copy( quaternion ); + alignVector.copy( this.eye ).applyQuaternion( tempQuaternion.copy( quaternion ).inverse() ); + + if ( handle.name.search( "E" ) !== - 1 ) { + + handle.quaternion.setFromRotationMatrix( lookAtMatrix.lookAt( this.eye, zeroVector, unitY ) ); + + } + + if ( handle.name === 'X' ) { + + tempQuaternion.setFromAxisAngle( unitX, Math.atan2( -alignVector.y, alignVector.z ) ); + tempQuaternion.multiplyQuaternions( tempQuaternion2, tempQuaternion ); + handle.quaternion.copy( tempQuaternion ); + + } + + if ( handle.name === 'Y' ) { + + tempQuaternion.setFromAxisAngle( unitY, Math.atan2( alignVector.x, alignVector.z ) ); + tempQuaternion.multiplyQuaternions( tempQuaternion2, tempQuaternion ); + handle.quaternion.copy( tempQuaternion ); + + } + + if ( handle.name === 'Z' ) { + + tempQuaternion.setFromAxisAngle( unitZ, Math.atan2( alignVector.y, alignVector.x ) ); + tempQuaternion.multiplyQuaternions( tempQuaternion2, tempQuaternion ); + handle.quaternion.copy( tempQuaternion ); + + } + + } + + // Hide disabled axes + handle.visible = handle.visible && ( handle.name.indexOf( "X" ) === -1 || this.showX ); + handle.visible = handle.visible && ( handle.name.indexOf( "Y" ) === -1 || this.showY ); + handle.visible = handle.visible && ( handle.name.indexOf( "Z" ) === -1 || this.showZ ); + handle.visible = handle.visible && ( handle.name.indexOf( "E" ) === -1 || ( this.showX && this.showY && this.showZ ) ); + + // highlight selected axis + + handle.material._opacity = handle.material._opacity || handle.material.opacity; + handle.material._color = handle.material._color || handle.material.color.clone(); + + handle.material.color.copy( handle.material._color ); + handle.material.opacity = handle.material._opacity; + + if ( !this.enabled ) { + + handle.material.opacity *= 0.5; + handle.material.color.lerp( new THREE.Color( 1, 1, 1 ), 0.5 ); + + } else if ( this.axis ) { + + if ( handle.name === this.axis ) { + + handle.material.opacity = 1.0; + handle.material.color.lerp( new THREE.Color( 1, 1, 1 ), 0.5 ); + + } else if ( this.axis.split('').some( function( a ) { return handle.name === a; } ) ) { + + handle.material.opacity = 1.0; + handle.material.color.lerp( new THREE.Color( 1, 1, 1 ), 0.5 ); + + } else { + + handle.material.opacity *= 0.25; + handle.material.color.lerp( new THREE.Color( 1, 1, 1 ), 0.5 ); + + } + + } + + } + + THREE.Object3D.prototype.updateMatrixWorld.call( this ); + + }; + +}; + +THREE.TransformControlsGizmo.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), { + + constructor: THREE.TransformControlsGizmo, + + isTransformControlsGizmo: true + +} ); + + +THREE.TransformControlsPlane = function () { + + 'use strict'; + + THREE.Mesh.call( this, + new THREE.PlaneBufferGeometry( 100000, 100000, 2, 2 ), + new THREE.MeshBasicMaterial( { visible: false, wireframe: true, side: THREE.DoubleSide, transparent: true, opacity: 0.1 } ) + ); + + this.type = 'TransformControlsPlane'; + + var unitX = new THREE.Vector3( 1, 0, 0 ); + var unitY = new THREE.Vector3( 0, 1, 0 ); + var unitZ = new THREE.Vector3( 0, 0, 1 ); + + var tempVector = new THREE.Vector3(); + var dirVector = new THREE.Vector3(); + var alignVector = new THREE.Vector3(); + var tempMatrix = new THREE.Matrix4(); + var identityQuaternion = new THREE.Quaternion(); + + this.updateMatrixWorld = function() { + + var space = this.space; + + this.position.copy( this.worldPosition ); + + if ( this.mode === 'scale' ) space = 'local'; // scale always oriented to local rotation + + unitX.set( 1, 0, 0 ).applyQuaternion( space === "local" ? this.worldQuaternion : identityQuaternion ); + unitY.set( 0, 1, 0 ).applyQuaternion( space === "local" ? this.worldQuaternion : identityQuaternion ); + unitZ.set( 0, 0, 1 ).applyQuaternion( space === "local" ? this.worldQuaternion : identityQuaternion ); + + // Align the plane for current transform mode, axis and space. + + alignVector.copy( unitY ); + + switch ( this.mode ) { + case 'translate': + case 'scale': + switch ( this.axis ) { + case 'X': + alignVector.copy( this.eye ).cross( unitX ); + dirVector.copy( unitX ).cross( alignVector ); + break; + case 'Y': + alignVector.copy( this.eye ).cross( unitY ); + dirVector.copy( unitY ).cross( alignVector ); + break; + case 'Z': + alignVector.copy( this.eye ).cross( unitZ ); + dirVector.copy( unitZ ).cross( alignVector ); + break; + case 'XY': + dirVector.copy( unitZ ); + break; + case 'YZ': + dirVector.copy( unitX ); + break; + case 'XZ': + alignVector.copy( unitZ ); + dirVector.copy( unitY ); + break; + case 'XYZ': + case 'E': + dirVector.set( 0, 0, 0 ); + break; + } + break; + case 'rotate': + default: + // special case for rotate + dirVector.set( 0, 0, 0 ); + } + + if ( dirVector.length() === 0 ) { + + // If in rotate mode, make the plane parallel to camera + this.quaternion.copy( this.cameraQuaternion ); + + } else { + + tempMatrix.lookAt( tempVector.set( 0, 0, 0 ), dirVector, alignVector ); + + this.quaternion.setFromRotationMatrix( tempMatrix ); + + } + + THREE.Object3D.prototype.updateMatrixWorld.call( this ); + + }; + +}; + +THREE.TransformControlsPlane.prototype = Object.assign( Object.create( THREE.Mesh.prototype ), { + + constructor: THREE.TransformControlsPlane, + + isTransformControlsPlane: true + +} ); diff --git a/src/jlmap3d/edit/testmodel/LinkList.js b/src/jlmap3d/edit/testmodel/LinkList.js new file mode 100644 index 000000000..306393f51 --- /dev/null +++ b/src/jlmap3d/edit/testmodel/LinkList.js @@ -0,0 +1,31 @@ + +export function LinkList(data,scene){ + + let groups = new THREE.Group(); + let linklist = []; + for(let i=0;i=3){ + console.log(data.sectionList[i]); + + for(let j=1;jpoint2.y){ + section.rotation.z = rotenum; + }else { + section.rotation.z = -rotenum; + } + section.position.set(posx,1,posy); + group.add(section); + } + + }else{ + //获取区段长度 + let dx = Math.abs(point1.x - point2.x); +       let dy = Math.abs(point1.y - point2.y); +       //let distance = Math.sqrt(Math.pow(dx,2)+Math.pow(dy,2)); + let distance = data.sectionList[i].offsetRight - data.sectionList[i].offsetLeft; + //获取区段中心点坐标 + let posx = (point1.x+point2.x)/2; + let posy = (point1.y+point2.y)/2; + //判断是否道岔 + + //创建模型 + + let sectiongeometry = new THREE.PlaneBufferGeometry( distance, 5, 32 ); + let section = new THREE.Mesh( sectiongeometry, sectionmaterial ); + section.name = data.sectionList[i].code; + + section.rotation.x = -Math.PI/2; + + + //判断方向 PS:方法待提炼 + let axix = new THREE.Vector3(1,0,0); + let axixnow = new THREE.Vector3(point2.x-point1.x,0,point2.y-point1.y); + let rotenum = axixnow.angleTo(axix); + //不同坐标系方向值不同 + if(point1.y>point2.y){ + section.rotation.z = rotenum; + }else { + section.rotation.z = -rotenum; + } + section.position.set(posx,1,posy); + group.add(section); + + + } + } + + } + + + //定义区分道岔组 + let switchlist = []; + //遍历道岔数据 + for(let i=0;i { + ModelLoad(data,scope,netdata.data,mapdata,camera,controls3,scene); + }); + + //let stats = new Stats(); + //dom.appendChild( stats.dom ); + //开启渲染 + animate(); + startWorker(); + //动画时间 + let delta; + //循环渲染函数 + function animate() { + //循环渲染 + //requestAnimationFrame(animate); + //renderer.setAnimationLoop(animate); + requestAnimationFrame(animate); + //判断渲染是否开启 + if(scope.animateswitch == true){ + //根据相机渲染场景 + renderer.render(scene,camera2); + //updatcontrols(); + controls3.update(); + //检测动画构造器播放动画 + + } + + } + + function updatcontrols(){ + if(drivingcode){ + controls3.getObject().position.x = mapdata.trainlisttest.list[drivingcode].matrixWorld.elements[12]-27; + controls3.getObject().position.y=10; + controls3.getObject().position.z = mapdata.trainlisttest.list[drivingcode].children[0].matrixWorld.elements[14]; + + } + } + + function startWorker(){ + + if(typeof(Worker)!=="undefined"){ + + + scope.webwork.onmessage = function (event) { + //更新列车位置 + // stats.update(); + UpdateTrain(camera,mapdata.trainlisttest); + delta = clock.getDelta(); + for(let i=scope.mixers.length-1;i>=0;i--){ + if ( scope.mixers[i] ){ + scope.mixers[i].update( delta ); + } + } + + }; + } + + } + this.updatenowspeed = function(speed){ + scope.nowspeed = speed; + } + this.updatenowlen = function(maLen){ + scope.nowmxlen = maLen; + } + this.updateatpspeed = function(atpspeed){ + scope.atpspeed = atpspeed; + } + this.updateatospeed = function(atospeed){ + scope.atospeed = atospeed; + } + this.updatetrainnum = function(trainnum){ + scope.trainnum = trainnum; + } + this.updatestoptime = function(stime){ + scope.stime = stime; + } + + this.updatedrivingcode = function(code){ + drivingcode = code; + mapdata.trainlisttest.list[drivingcode].children[0].add(controls3.getObject()); + mapdata.trainlisttest.list[drivingcode].children[0].add(scope.assetloader.modellist[4].mesh); + controls3.getObject().position.x = 40; + controls3.getObject().position.y= 12.5; + controls3.getObject().rotation.y = -Math.PI/2; + scope.assetloader.modellist[4].mesh.position.x = 34; + scope.assetloader.modellist[4].mesh.position.y = 0; + } + + this.dispose = function(){ + renderer.setAnimationLoop(null); + renderer.dispose(); + scene.dispose(); + // controls.dispose(); + camera = null; + scope.assetloader = null; + + mapdata = null; + scope.selectmodel = null; + + scope.materiallist = null; + scope.selectmodel = null; + scope.helpbox = null; + scope.mixers = null; + scope.actions = null; + scope.Subscribe = null; + //console.log(scope); + //scope = null; + } + + this.rayswitch = function(value){ + this.raycasterswitch = value; + if(scope.helpbox){ + scene.remove( scope.helpbox ); + scope.helpbox = null; + } + }; + + this.showstationmsg = function(showtype){ + if(showtype == "show"){ + for(let st=0;st0){ + jlmap3d.updatenowspeed(data.body[tl].v); + }else{ + jlmap3d.updatenowspeed(Math.abs(data.body[tl].v)); + } + + if(data.body[tl].atpv){ + jlmap3d.updateatpspeed(data.body[tl].atpv); + }else{ + jlmap3d.updateatpspeed(-1); + } + if(data.body[tl].atov){ + jlmap3d.updateatospeed(data.body[tl].atov); + }else{ + jlmap3d.updateatospeed(-1); + } + } + } + } + } + + if(data.type == "SJL3D_TrainStatus"){ + for(let i=data.body.length-1;i>=0;i--){ + if(data.body[i]._type == "Train"){ + //遍历列车对象组 + if(trainlisttest){ + code = data.body[i].code; + //剔除不显示的车 + //找到对应列车 + + if( trainlisttest.list[code]){ + if(code == drivingcode){ + driverswitch = data.body[i].runMode; + jlmap3d.updatetrainnum(data.body[i].groupNumber); + // console.log(trainlisttest.list[code].progress); + let syncdata = { + type:"Train", + code:code, + sectionCode:data.body[i].sectionCode, + percent:0, + }; + + if(trainlisttest.list[code].isStandTrack == true && trainlisttest.list[code].progress>0.95 && trainlisttest.list[code].speed<=0){ + if(data.body[i].directionType == "02"){ + syncdata.percent = 1-(sectionlist.sections.datalist[data.body[i].sectionCode].rstop/trainlisttest.list[code].len); + }else{ + syncdata.percent = sectionlist.sections.datalist[data.body[i].sectionCode].lstop/trainlisttest.list[code].len; + } + scope.teststomp.send("/app/topic/simulation/wgu3d",syncdata); + }else{ + if(data.body[i].directionType == "02"){ + syncdata.percent = trainlisttest.list[code].progress; + }else{ + syncdata.percent = 1 - trainlisttest.list[code].progress; + } + scope.teststomp.send("/app/topic/simulation/wgu3d",syncdata); + } + + if(data.body[i].parkRemainTime>0){ + jlmap3d.updatestoptime("停站时间:"+parseInt(data.body[i].parkRemainTime)); + }else{ + if(data.body[i].runMode == "02"){ + jlmap3d.updatestoptime("列车自动驾驶中"); + }else{ + if(trainlisttest.list[code].isStandTrack == true && trainlisttest.list[code].progress>0.95 && trainlisttest.list[code].speed<=0){ + // if(stoptimer){ + // + // }else{ + // stoptimer = setInterval(function(){ + // if(num >=0){ + // jlmap3d.updatestoptime("停站时间:"+num); + // num --; + // } + // },1000); + // } + + }else{ + jlmap3d.updatestoptime("列车人工驾驶中"); + } + + } + } + // if(trainlisttest.list[code].speed > 0){ + // if(stoptimer){ + // clearInterval( stoptimer ); + // stoptimer = null; + // num = 30; + // } + // } + + } + trainlisttest.list[code].runMode = data.body[i].runMode; + //车门开关验证 + if(data.body[i].directionType == "02"){ + if(trainlisttest.list[code].doorStatus != data.body[i].doorStatus && data.body[i].doorStatus == "01"){ + //console.log("close"); + trainlisttest.list[code].doorStatus = "01"; + for(let an=actions[code].top.length-1;an>=0;an--){ + actions[code].top[an].reset(); + actions[code].top[an].time = actions[code].top[an]._clip.duration; + actions[code].top[an].timeScale = -1; + actions[code].top[an].play(); + } + }else if(trainlisttest.list[code].doorStatus != data.body[i].doorStatus && data.body[i].doorStatus == "02"){ + //console.log("open"); + trainlisttest.list[code].doorStatus = "02"; + for(let an=actions[code].top.length-1;an>=0;an--){ + actions[code].top[an].reset(); + actions[code].top[an].time = 0; + actions[code].top[an].timeScale = 1; + actions[code].top[an].play(); + } + } + }else{ + if(trainlisttest.list[code].doorStatus != data.body[i].doorStatus && data.body[i].doorStatus == "01"){ + //console.log("close"); + trainlisttest.list[code].doorStatus = "01"; + for(let an=actions[code].down.length-1;an>=0;an--){ + actions[code].down[an].reset(); + actions[code].down[an].time = actions[code].top[an]._clip.duration; + actions[code].down[an].timeScale = -1; + actions[code].down[an].play(); + } + }else if(trainlisttest.list[code].doorStatus != data.body[i].doorStatus && data.body[i].doorStatus == "02"){ + //console.log("open"); + trainlisttest.list[code].doorStatus = "02"; + for(let an=actions[code].down.length-1;an>=0;an--){ + actions[code].down[an].reset(); + actions[code].down[an].time = 0; + actions[code].down[an].timeScale = 1; + actions[code].down[an].play(); + } + } + } + //遍历获取所在轨道 + + if(trainlisttest.list[code].dispose != data.body[i].dispose && data.body[i].dispose == false){ + + if(sectionlist.sections.datalist[data.body[i].sectionCode].code){ + trainlisttest.group.add(trainlisttest.list[code]); + trainlisttest.list[code].position.y = 0; + trainlisttest.list[code].progress = 0; + trainlisttest.list[code].oldoffset = data.body[i].sectionOffsetPercent; + trainlisttest.list[code].dispose = false; + trainlisttest.list[code].nowcode = data.body[i].sectionCode; + trainlisttest.list[code].nextcode = data.body[i].nextSectionCode; + let vexlist = []; + let endrotation = null; + + if(data.body[i].directionType == "02"){//向右 + let offset = null; + let rotaposx = null; + + if(sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x>sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x){ + offset = sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x-sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x; + rotaposx = sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x+offset*data.body[i].sectionOffsetPercent; + }else{ + offset = sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x-sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x; + rotaposx = sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x+offset*data.body[i].sectionOffsetPercent; + } + + let rotaposz = sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].z; + trainlisttest.list[code].rotation.y = 0; + trainlisttest.list[code].position.x = rotaposx; + trainlisttest.list[code].position.y = 0; + for(let tl=0;tl<6;tl++){ + trainlisttest.list[code].children[tl].position.z = rotaposz; + } + if(sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x>sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x){ + vexlist.push(new THREE.Vector3(rotaposx,0,rotaposz)); + for(let m=sectionlist.sections.datalist[data.body[i].sectionCode].rail.length-1;m>=0;m--){ + if(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m].x>rotaposx){ + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + + } + }else{ + vexlist.push(new THREE.Vector3(rotaposx,0,rotaposz)); + for(let m=0;mrotaposx){ + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + } + } + trainlisttest.list[code].status = "02"; + + }else if(data.body[i].directionType == "03"){//向左 + + trainlisttest.list[code].dispose = false; + trainlisttest.group.add(trainlisttest.list[code]); + let offset = null; + let rotaposx = null; + + if(sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x>sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x){ + + offset = sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x-sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x; + rotaposx = sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x+offset*data.body[i].sectionOffsetPercent; + }else{ + + offset = sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x-sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x; + rotaposx = sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x+offset*data.body[i].sectionOffsetPercent; + } + let rotaposz = sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].z; + trainlisttest.list[code].rotation.y = Math.PI; + trainlisttest.list[code].position.x = rotaposx; + trainlisttest.list[code].position.y = 0; + for(let tl=0;tl<6;tl++){ + trainlisttest.list[code].children[tl].position.z = rotaposz; + } + if(data.body[i].groupNumber == "001"){ + } + + if(sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x=0;m--){ + if(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m].xsectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x){ + vexlist.push(new THREE.Vector3(trainlisttest.list[code].position.x,0,trainlisttest.list[code].children[0].matrixWorld.elements[14])); + for(let m=sectionlist.sections.datalist[data.body[i].sectionCode].rail.length-1;m>=0;m--){ + if(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m].x>trainlisttest.list[code].position.x){ + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + } + }else{ + vexlist.push(new THREE.Vector3(trainlisttest.list[code].position.x,0,trainlisttest.list[code].children[0].matrixWorld.elements[14])); + for(let m=0;mtrainlisttest.list[code].position.x){ + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + } + } + }else{ + if(sectionlist.sections.datalist[data.body[i].sectionCode].rail[0].x>sectionlist.sections.datalist[data.body[i].sectionCode].rail[1].x){ + for(let m=sectionlist.sections.datalist[data.body[i].sectionCode].rail.length-1;m>=0;m--){ + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + }else{ + for(let m=0;m=0;m--){ + if(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m].x=0;m--){ + vexlist.push(sectionlist.sections.datalist[data.body[i].sectionCode].rail[m]); + } + + }else{ + for(let m=0;m=0;i--){ + //0xFFFFFF + //0xCD0000 红 + //0xEEEE00 黄 + //0x32CD32 绿 + if(data.body[i]._type == "Signal"){ + if(signallist){ + signalupdate(data.body[i]); + } + } + + if(data.body[i]._type == "StationStand"){ + if(actions){ + standupdate(data.body[i]); + } + } + + if(data.body[i]._type == "Switch"){ + if(sectionlist){ + switchupdate(data.body[i]); + } + } + + } + } + } + + function standupdate(data){ + code = data.code; + if( actions[code]){ + if(data.screenDoorOpenStatus == "02" && actions[code].status == "01"){ + actions[code].status = "00"; + } + if(data.screenDoorOpenStatus == "02" && actions[code].status == "00"){ + actions[code].status = "02"; + actions[code].action.reset(); + actions[code].action.time = 0; + actions[code].action.timeScale = 1; + actions[code].action.play(); + } + + if(data.screenDoorOpenStatus == "01" && actions[code].status == "02"){ + actions[code].status = "00"; + } + if(data.screenDoorOpenStatus == "01" && actions[code].status == "00"){ + actions[code].status = "01"; + actions[code].action.reset(); + actions[code].action.time = actions[code].action._clip.duration; + actions[code].action.timeScale = -1; + actions[code].action.play(); + } + } + + + } + + function signalupdate(data){ + code = data.code; + if(data.lightType == "01"){ + if(signallist.list[code].mesh.code){ + signallist.list[code].mesh.status = data.status; + + //55 + //33 + //77 + //关闭 + if(data.status == "01"){ + signallist.list[code].mesh.children[0].material.map = materials[3]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[0]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[3]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + //开放 + if(data.status == "02"){ + + if(data.switchLocateType == "01"){ + signallist.list[code].mesh.children[0].material.map = materials[2]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[3]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[3]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + + if(data.switchLocateType == "02"){ + signallist.list[code].mesh.children[0].material.map = materials[3]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[3]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[1]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + + } + //引导 + if(data.status == "03"){ + signallist.list[code].mesh.children[0].material.map = materials[3]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[0]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[1]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + //封锁 + if(data.status == "04"){ + signallist.list[code].mesh.children[0].material.map = materials[3]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[3]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[3]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + //故障 + if(data.status == "05"){ + signallist.list[code].mesh.children[0].material.map = materials[3]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[3]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[3]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + + } + + }else if(data.lightType == "01"){ + if(signallist.list[code].mesh.code){ + signallist.list[code].mesh.children[0].material.map = materials[3]; + signallist.list[code].mesh.children[0].material.map.needsUpdate = true; + signallist.list[code].mesh.children[1].material.map = materials[3]; + signallist.list[code].mesh.children[1].material.map.needsUpdate = true; + signallist.list[code].mesh.children[2].material.map = materials[3]; + signallist.list[code].mesh.children[2].material.map.needsUpdate = true; + + } + + } + + } + + function switchupdate(data){ + code = data.code; + for(let j=sectionlist.switchs.modellist.length-1;j>=0;j--){ + if(sectionlist.switchs.modellist[j].code == code){ + if(sectionlist.switchs.modellist[j].locateType != data.locateType){ + if(data.locateType == "02"){ + if(actions[sectionlist.switchs.modellist[j].code]){ + sectionlist.switchs.modellist[j].locateType = data.locateType; + actions[sectionlist.switchs.modellist[j].code].reset(); + actions[sectionlist.switchs.modellist[j].code].time = 0; + actions[sectionlist.switchs.modellist[j].code].timeScale = 1; + actions[sectionlist.switchs.modellist[j].code].play(); + } + }else if(data.locateType == "01"){ + if(actions[sectionlist.switchs.modellist[j].code]){ + sectionlist.switchs.modellist[j].locateType = data.locateType; + actions[sectionlist.switchs.modellist[j].code].reset(); + actions[sectionlist.switchs.modellist[j].code].time = actions[sectionlist.switchs.modellist[j].code]._clip.duration; + actions[sectionlist.switchs.modellist[j].code].timeScale = -1; + actions[sectionlist.switchs.modellist[j].code].play(); + } + } + } + j = 0; + } + } + } + + +} diff --git a/src/jlmap3d/jl3dsimulation/moveupdate/UpdateTrain.js b/src/jlmap3d/jl3dsimulation/moveupdate/UpdateTrain.js new file mode 100644 index 000000000..5e6d272a6 --- /dev/null +++ b/src/jlmap3d/jl3dsimulation/moveupdate/UpdateTrain.js @@ -0,0 +1,275 @@ + +export function UpdateTrain(camera,traindata,control){ + if(traindata != undefined ){ + + for(let j=traindata.group.children.length-1;j>=0;j--){ + //判断是否有移动事件 + if(traindata.group.children[j].dispose == false){ + + if(traindata.group.children[j].progress != null){ + + let trainmodel = traindata.group.children[j]; + if(trainmodel.speeds > 0 && trainmodel.speeds){ + let speed = null; + if(traindata.group.children[j].progress<1){ + + let movecurve = trainmodel.curve; + + if(trainmodel.status == "03"){ + if(movecurve.points.length>1){ + let point = movecurve.getPointAt(traindata.group.children[j].progress); + if(Math.abs( point.z -trainmodel.children[0].matrixWorld.elements[14]) >0.1){ + trainmodel.children[0].up = new THREE.Vector3(-1,0,0); + let tangent = movecurve.getTangentAt(traindata.group.children[j].progress).normalize(); + trainmodel.children[0].axis.crossVectors(trainmodel.children[0].up, tangent).normalize(); + let radians = Math.acos(trainmodel.children[0].up.dot(tangent)); + trainmodel.children[0].quaternion.setFromAxisAngle(trainmodel.children[0].axis, radians); + let rotas = { + posr:point, + rota:trainmodel.children[0].rotation.y + } + trainmodel.children[1].rotalist.push(rotas); + + let offsetz = parseFloat(trainmodel.children[0].matrixWorld.elements[14]) - parseFloat(point.z); + trainmodel.children[0].position.z += offsetz; + //trainmodel.position.z = point.z; + } + + trainmodel.position.x = point.x; + trainmodel.position.y = 0; + + if(trainmodel.children[1].rotalist.length > 0 || trainmodel.children[2].rotalist.length > 0 || trainmodel.children[3].rotalist.length > 0 || trainmodel.children[4].rotalist.length > 0|| trainmodel.children[5].rotalist.length > 0){ + for(let rs = 1;rs<6;rs++){ + //console.log(rs); + if(trainmodel.children[rs].rotalist[0]){ + let offsetz = parseFloat(trainmodel.children[rs].matrixWorld.elements[14]) - parseFloat(trainmodel.children[rs].rotalist[0].posr.z); + + trainmodel.children[rs].position.z += offsetz; + + for(let xh=0;xh1){ + let point = movecurve.getPointAt(trainmodel.progress); + if(Math.abs( point.z -trainmodel.children[0].matrixWorld.elements[14]) >0.1){ + trainmodel.children[0].up = new THREE.Vector3(1,0,0); + let tangent = movecurve.getTangentAt(traindata.group.children[j].progress).normalize(); + trainmodel.children[0].axis.crossVectors(trainmodel.children[0].up, tangent).normalize(); + let radians = Math.acos(trainmodel.children[0].up.dot(tangent)); + trainmodel.children[0].quaternion.setFromAxisAngle(trainmodel.children[0].axis, radians); + + let rotas = { + posr:point, + rota:trainmodel.children[0].rotation.y + } + trainmodel.children[1].rotalist.push(rotas); + let offsetz = parseFloat(point.z) - parseFloat(trainmodel.children[0].matrixWorld.elements[14]); + trainmodel.children[0].position.z += offsetz; + //trainmodel.position.z = point.z; + } + + trainmodel.position.x = point.x; + trainmodel.position.y = 0; + if(trainmodel.children[1].rotalist.length > 0 || trainmodel.children[2].rotalist.length > 0 || trainmodel.children[3].rotalist.length > 0 || trainmodel.children[4].rotalist.length > 0|| trainmodel.children[5].rotalist.length > 0){ + + for(let rs = 1;rs<6;rs++){ + //console.log(rs); + if(trainmodel.children[rs].rotalist[0]){ + + let offsetz = parseFloat(trainmodel.children[rs].rotalist[0].posr.z) - parseFloat(trainmodel.children[rs].matrixWorld.elements[14]); + trainmodel.children[rs].position.z += offsetz; + + for(let xh=0;xh=trainmodel.children[rs].rotalist[0].posr.x){ + + if(rs != 5){ + let asd = trainmodel.children[rs].rotalist[0]; + trainmodel.children[rs+1].rotalist.push(asd); + + } + //let offsetx = trainmodel.children[1].matrixWorld.elements[12]-trainmodel.children[0].children[3].matrixWorld.elements[12]; + + trainmodel.children[rs].rotation.y = trainmodel.children[rs].rotalist[0].rota; + trainmodel.children[rs].rotalist.splice(0,1) + xh--; + }else{ + xh = trainmodel.children[rs].rotalist.length; + } + } + //console.log(trainmodel.children[rs].rotalist.length); + + } + + + } + // console.log(trainmodel.rotalist); + } + + + trainmodel.progress += trainmodel.speeds; + } + } + + } + + }else if(trainmodel.speeds < 0 && trainmodel.speeds){ + let speed = null; + if(traindata.group.children[j].progress<1){ + + let movecurve = trainmodel.curve; + + if(trainmodel.status == "03" && trainmodel.progress>0){ + if(movecurve.points.length>1){ + let point = movecurve.getPointAt(traindata.group.children[j].progress); + if(Math.abs( point.z -trainmodel.children[0].matrixWorld.elements[14]) >0.1){ + trainmodel.children[0].up = new THREE.Vector3(-1,0,0); + let tangent = movecurve.getTangentAt(traindata.group.children[j].progress).normalize(); + trainmodel.children[0].axis.crossVectors(trainmodel.children[0].up, tangent).normalize(); + let radians = Math.acos(trainmodel.children[0].up.dot(tangent)); + trainmodel.children[0].quaternion.setFromAxisAngle(trainmodel.children[0].axis, radians); + let rotas = { + posr:point, + rota:trainmodel.children[0].rotation.y + } + trainmodel.children[1].rotalist.push(rotas); + + let offsetz = parseFloat(trainmodel.children[0].matrixWorld.elements[14]) - parseFloat(point.z); + trainmodel.children[0].position.z += offsetz; + //trainmodel.position.z = point.z; + } + + trainmodel.position.x = point.x; + trainmodel.position.y = 0; + + if(trainmodel.children[1].rotalist.length > 0 || trainmodel.children[2].rotalist.length > 0 || trainmodel.children[3].rotalist.length > 0 || trainmodel.children[4].rotalist.length > 0|| trainmodel.children[5].rotalist.length > 0){ + for(let rs = 1;rs<6;rs++){ + //console.log(rs); + if(trainmodel.children[rs].rotalist[0]){ + let offsetz = parseFloat(trainmodel.children[rs].matrixWorld.elements[14]) - parseFloat(trainmodel.children[rs].rotalist[0].posr.z); + + trainmodel.children[rs].position.z += offsetz; + + for(let xh=0;xh -(trainmodel.speeds)){ + trainmodel.progress += trainmodel.speeds; + } + + } + } + + if(trainmodel.status == "02"){ + if(movecurve.points.length>1 && trainmodel.progress>0){ + + let point = movecurve.getPointAt(trainmodel.progress); + if(Math.abs( point.z -trainmodel.children[0].matrixWorld.elements[14]) >0.1){ + trainmodel.children[0].up = new THREE.Vector3(1,0,0); + let tangent = movecurve.getTangentAt(traindata.group.children[j].progress).normalize(); + trainmodel.children[0].axis.crossVectors(trainmodel.children[0].up, tangent).normalize(); + let radians = Math.acos(trainmodel.children[0].up.dot(tangent)); + trainmodel.children[0].quaternion.setFromAxisAngle(trainmodel.children[0].axis, radians); + + let rotas = { + posr:point, + rota:trainmodel.children[0].rotation.y + } + trainmodel.children[1].rotalist.push(rotas); + let offsetz = parseFloat(point.z) - parseFloat(trainmodel.children[0].matrixWorld.elements[14]); + trainmodel.children[0].position.z += offsetz; + //trainmodel.position.z = point.z; + } + + trainmodel.position.x = point.x; + trainmodel.position.y = 0; + if(trainmodel.children[1].rotalist.length > 0 || trainmodel.children[2].rotalist.length > 0 || trainmodel.children[3].rotalist.length > 0 || trainmodel.children[4].rotalist.length > 0|| trainmodel.children[5].rotalist.length > 0){ + + for(let rs = 1;rs<6;rs++){ + //console.log(rs); + if(trainmodel.children[rs].rotalist[0]){ + + let offsetz = parseFloat(trainmodel.children[rs].rotalist[0].posr.z) - parseFloat(trainmodel.children[rs].matrixWorld.elements[14]); + trainmodel.children[rs].position.z += offsetz; + + for(let xh=0;xh=trainmodel.children[rs].rotalist[0].posr.x){ + + if(rs != 5){ + let asd = trainmodel.children[rs].rotalist[0]; + trainmodel.children[rs+1].rotalist.push(asd); + + } + //let offsetx = trainmodel.children[1].matrixWorld.elements[12]-trainmodel.children[0].children[3].matrixWorld.elements[12]; + + trainmodel.children[rs].rotation.y = trainmodel.children[rs].rotalist[0].rota; + trainmodel.children[rs].rotalist.splice(0,1) + xh--; + }else{ + xh = trainmodel.children[rs].rotalist.length; + } + } + //console.log(trainmodel.children[rs].rotalist.length); + + } + + + } + // console.log(trainmodel.rotalist); + } + + + if(trainmodel.progress > -(trainmodel.speeds)){ + trainmodel.progress += trainmodel.speeds; + } + } + } + + } + } + } + } + + } + + } + +} diff --git a/src/jlmap3d/jl3dsimulation/simulationloader.js b/src/jlmap3d/jl3dsimulation/simulationloader.js new file mode 100644 index 000000000..c7dc41945 --- /dev/null +++ b/src/jlmap3d/jl3dsimulation/simulationloader.js @@ -0,0 +1,193 @@ +//componnent +import {SectionList} from '@/jlmap3d/model/SectionList.js'; +import {SignalList} from '@/jlmap3d/model/SignalList.js'; +import {StationStandList} from '@/jlmap3d/model/StationStandList.js'; +import {TrainList} from '@/jlmap3d/model/TrainList.js'; +import {RealSectionList} from '@/jlmap3d/model/RealSectionList.js'; + +import {Materialload} from '@/jlmap3d/component/Materialload.js'; + +import { Loading } from 'element-ui'; +// import {SwitchModel} from '@/jlmap3d/model/SwitchModel.js'; + +export function ModelLoad(data,scope,netdata,mapdata,camera,controls,scene){ + //console.log(mapdata); + Materialload(scope); + //console.log(data); + //console.log(scope); + let sceneload = scene; + let jlmap3ddata = mapdata; + let assetloader = scope.assetloader; + let animateswitch = scope.animateswitch; + + let mixers = scope.mixers; + let actions = scope.actions; + + let loadingInstance = Loading.service({ fullscreen: true }); + + + let isSection = false; + if(netdata.assets){ + let assetsdata = JSON.parse(netdata.assets); + for(let i=0;i { + ModelLoad(data,scope,netdata.data,mapdata,camera,controls,scene); + + + }); + // + // let stats = new Stats(); + // dom.appendChild( stats.dom ); + //开启渲染 + animate(); + startWorker(); + //动画时间 + let delta; + //循环渲染函数 + function animate() { + //循环渲染 + //requestAnimationFrame(animate); + //renderer.setAnimationLoop(animate); + requestAnimationFrame(animate); + //判断渲染是否开启 + if(scope.animateswitch == true){ + //根据相机渲染场景 + renderer.render(scene,camera); + //检测动画构造器播放动画 + //stats.update(); + } + + } + + function startWorker(){ + + if(typeof(Worker)!=="undefined"){ + + + scope.webwork.onmessage = function (event) { + //更新列车位置 + UpdateTrain(camera,mapdata.trainlisttest); + delta = clock.getDelta(); + for(let i=scope.mixers.length-1;i>=0;i--){ + if ( scope.mixers[i] ){ + scope.mixers[i].update( delta ); + } + } + + }; + } + + } + + this.dispose = function(){ + renderer.setAnimationLoop(null); + renderer.dispose(); + scene.dispose(); + controls.dispose(); + camera = null; + scope.assetloader = null; + + mapdata = null; + scope.selectmodel = null; + + scope.materiallist = null; + scope.selectmodel = null; + scope.helpbox = null; + scope.mixers = null; + scope.actions = null; + scope.Subscribe = null; + //console.log(scope); + //scope = null; + } + + this.rayswitch = function(value){ + this.raycasterswitch = value; + if(scope.helpbox){ + scene.remove( scope.helpbox ); + scope.helpbox = null; + } + }; + + this.showstationmsg = function(showtype){ + if(showtype == "show"){ + for(let st=0;sta&&(a=c[p]),c[p]>=1;x=g<<16|p;for(s=m;s>>=1;switch(c){case 0:var d=this.input,a=this.a,b=this.c,e=this.b,f=d.length,g=l,h=l,k=b.length,m=l;this.d=this.f=0;if(a+1>=f)throw Error("invalid uncompressed block header: LEN");g=d[a++]|d[a++]<<8;if(a+1>=f)throw Error("invalid uncompressed block header: NLEN");h=d[a++]|d[a++]<<8;if(g===~h)throw Error("invalid uncompressed block header: length verify");if(a+g>d.length)throw Error("input buffer is broken");switch(this.i){case A:for(;e+ +g>b.length;){m=k-e;g-=m;if(t)b.set(d.subarray(a,a+m),e),e+=m,a+=m;else for(;m--;)b[e++]=d[a++];this.b=e;b=this.e();e=this.b}break;case y:for(;e+g>b.length;)b=this.e({p:2});break;default:throw Error("invalid inflate mode");}if(t)b.set(d.subarray(a,a+g),e),e+=g,a+=g;else for(;g--;)b[e++]=d[a++];this.a=a;this.b=e;this.c=b;break;case 1:this.j(ba,ca);break;case 2:for(var n=C(this,5)+257,p=C(this,5)+1,s=C(this,4)+4,x=new (t?Uint8Array:Array)(D.length),S=l,T=l,U=l,u=l,M=l,F=l,z=l,q=l,V=l,q=0;q=P?8:255>=P?9:279>=P?7:8;var ba=v(O),Q=new (t?Uint8Array:Array)(30),R,ga;R=0;for(ga=Q.length;R=g)throw Error("input buffer is broken");a|=e[f++]<>>d;c.d=b-d;c.a=f;return h} +function E(c,d){for(var a=c.f,b=c.d,e=c.input,f=c.a,g=e.length,h=d[0],k=d[1],m,n;b=g);)a|=e[f++]<>>16;if(n>b)throw Error("invalid code length: "+n);c.f=a>>n;c.d=b-n;c.a=f;return m&65535} +w.prototype.j=function(c,d){var a=this.c,b=this.b;this.o=c;for(var e=a.length-258,f,g,h,k;256!==(f=E(this,c));)if(256>f)b>=e&&(this.b=b,a=this.e(),b=this.b),a[b++]=f;else{g=f-257;k=I[g];0=e&&(this.b=b,a=this.e(),b=this.b);for(;k--;)a[b]=a[b++-h]}for(;8<=this.d;)this.d-=8,this.a--;this.b=b}; +w.prototype.w=function(c,d){var a=this.c,b=this.b;this.o=c;for(var e=a.length,f,g,h,k;256!==(f=E(this,c));)if(256>f)b>=e&&(a=this.e(),e=a.length),a[b++]=f;else{g=f-257;k=I[g];0e&&(a=this.e(),e=a.length);for(;k--;)a[b]=a[b++-h]}for(;8<=this.d;)this.d-=8,this.a--;this.b=b}; +w.prototype.e=function(){var c=new (t?Uint8Array:Array)(this.b-32768),d=this.b-32768,a,b,e=this.c;if(t)c.set(e.subarray(32768,c.length));else{a=0;for(b=c.length;aa;++a)e[a]=e[d+a];this.b=32768;return e}; +w.prototype.z=function(c){var d,a=this.input.length/this.a+1|0,b,e,f,g=this.input,h=this.c;c&&("number"===typeof c.p&&(a=c.p),"number"===typeof c.u&&(a+=c.u));2>a?(b=(g.length-this.a)/this.o[2],f=258*(b/2)|0,e=fd&&(this.c.length=d),c=this.c);return this.buffer=c};function W(c,d){var a,b;this.input=c;this.a=0;if(d||!(d={}))d.index&&(this.a=d.index),d.verify&&(this.A=d.verify);a=c[this.a++];b=c[this.a++];switch(a&15){case ha:this.method=ha;break;default:throw Error("unsupported compression method");}if(0!==((a<<8)+b)%31)throw Error("invalid fcheck flag:"+((a<<8)+b)%31);if(b&32)throw Error("fdict flag is not supported");this.q=new w(c,{index:this.a,bufferSize:d.bufferSize,bufferType:d.bufferType,resize:d.resize})} +W.prototype.k=function(){var c=this.input,d,a;d=this.q.k();this.a=this.q.a;if(this.A){a=(c[this.a++]<<24|c[this.a++]<<16|c[this.a++]<<8|c[this.a++])>>>0;var b=d;if("string"===typeof b){var e=b.split(""),f,g;f=0;for(g=e.length;f>>0;b=e}for(var h=1,k=0,m=b.length,n,p=0;0>>0)throw Error("invalid adler-32 checksum");}return d};var ha=8;r("Zlib.Inflate",W);r("Zlib.Inflate.prototype.decompress",W.prototype.k);var X={ADAPTIVE:B.s,BLOCK:B.t},Y,Z,$,ia;if(Object.keys)Y=Object.keys(X);else for(Z in Y=[],$=0,X)Y[$++]=Z;$=0;for(ia=Y.length;$e+1E3&&(r.update(1E3*a/(c-e),100),e=c,a=0,t)){var d=performance.memory;t.update(d.usedJSHeapSize/1048576,d.jsHeapSizeLimit/1048576)}return c},update:function(){g=this.end()},domElement:c,setMode:k}}; +Stats.Panel=function(h,k,l){var c=Infinity,g=0,e=Math.round,a=e(window.devicePixelRatio||1),r=80*a,f=48*a,t=3*a,u=2*a,d=3*a,m=15*a,n=74*a,p=30*a,q=document.createElement("canvas");q.width=r;q.height=f;q.style.cssText="width:80px;height:48px";var b=q.getContext("2d");b.font="bold "+9*a+"px Helvetica,Arial,sans-serif";b.textBaseline="top";b.fillStyle=l;b.fillRect(0,0,r,f);b.fillStyle=k;b.fillText(h,t,u);b.fillRect(d,m,n,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d,m,n,p);return{dom:q,update:function(f, +v){c=Math.min(c,f);g=Math.max(g,f);b.fillStyle=l;b.globalAlpha=1;b.fillRect(0,0,r,m);b.fillStyle=k;b.fillText(e(f)+" "+h+" ("+e(c)+"-"+e(g)+")",t,u);b.drawImage(q,d+a,m,n-a,p,d,m,n-a,p);b.fillRect(d+n-a,m,a,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d+n-a,m,a,e((1-f/v)*p))}}};"object"===typeof module; diff --git a/src/jlmap3d/main/loaders/AssetLoader.js b/src/jlmap3d/main/loaders/AssetLoader.js new file mode 100644 index 000000000..d952e6d79 --- /dev/null +++ b/src/jlmap3d/main/loaders/AssetLoader.js @@ -0,0 +1,418 @@ +import { AssetModel } from '@/jlmap3d/main/loaders/model/Assetmodel'; +import { Assetmaterial } from '@/jlmap3d/main/loaders/model/Assetmaterial'; + +import { Loading } from 'element-ui'; + +let defaultsignal = { + id:"3", + name:"低位三灯", + deviceType:"signal", + type:"low", + picUrl:"", + assetUrl:"https://joylink.club/oss/models/signal/d3d.FBX" +} + +let defaulttrain = { + id:"9", + name:"6节列车", + deviceType:"train", + type:"num4", + picUrl:"", + assetUrl:"https://joylink.club/oss/models/train/train.FBX" +} + + +let defaultstation = { + id:"8", + name:"福州车站", + deviceType:"stand", + type:"num4", + picUrl:"", + assetUrl:"https://joylink.club/oss/models/station/fuzhou/fuzhou.FBX" +}//https://joylink.club/oss/models/station/fuzhou/fuzhou.FBX +//../../static/model/station/zhantai715(2).FBX + +let defaultswitch = { + id:"11", + name:"道岔", + deviceType:"switch", + type:"fuzhou", + picUrl:"", + assetUrl:"../../static/model/daocha/daocha.FBX" +} + +let defaultdriver = { + id:"15", + name:"驾驶台", + deviceType:"driver", + type:"driver", + picUrl:"", + assetUrl:"https://joylink.club/oss/models/driver/driver.FBX" +} + +let defaultsuidao = { + id:"16", + name:"suidao", + deviceType:"suidao", + type:"suidao", + picUrl:"", + assetUrl:"https://joylink.club/oss/models/suidao/suidao.FBX" +}//https://joylink.club/oss/models/suidao/suidao.FBX +//../../static/model/ + + + + + +export function AssetLoader(){ + + let scope = this; + + this.name = null; + + this.modellist = []; + + this.materiallist = []; + //初始化资源加载器 + this.setmodellist = function (data){ + + let defaultmodel1 = new AssetModel(defaultsignal); + scope.modellist.push(defaultmodel1); + + let defaultmodel2 = new AssetModel(defaulttrain); + scope.modellist.push(defaultmodel2); + + let defaultmodel3 = new AssetModel(defaultstation); + scope.modellist.push(defaultmodel3); + + let defaultmodel4 = new AssetModel(defaultswitch); + scope.modellist.push(defaultmodel4); + + let driver = new AssetModel(defaultdriver); + scope.modellist.push(driver); + + let suidao = new AssetModel(defaultsuidao); + scope.modellist.push(suidao); + + + let modeldata = JSON.parse(data); + + for(let j=0;j { + ////console.log(result); + resolve("loaderassets"); //['成功了', 'success'] + }).catch((error) => { + //console.log(error); + }); + + }); + } + + this.assetinit = function(scene){ + return new Promise(function(resolve, reject){ + let signal = new AssetModel(defaultsignal); + scope.modellist.push(signal); + + let train = new AssetModel(defaulttrain); + scope.modellist.push(train); + + let station = new AssetModel(defaultstation); + scope.modellist.push(station); + + fbxpromise(signal) + .then(function(data){ + ////console.log(data); + return fbxpromise(train); + }) + .then(function(data){ + ////console.log(data); + return fbxpromise(station); + }) + .then(function(data){ + ////console.log(scope.modellist); + resolve("loaderassets"); + }); + }); + } + + this.loadnewsection = function(newdata,jlmap3dedit){ + let loadingInstance = Loading.service({ fullscreen: true }); + let scene = jlmap3dedit.scene; + let newmodel = new AssetModel(newdata); + let jlmap3ddata = jlmap3dedit.mapdata; + scope.modellist.push(newmodel); + + scene.remove(jlmap3dedit.exportmodel); + var loader = new THREE.FBXLoader(); + loader.load( newdata.assetUrl, function ( object ) { + + let newmesh = object; + + //let mixer = new THREE.AnimationMixer( newmesh ); + ////console.log(jlmap3ddata.sectionlist); + for(let i=0;i0;j--){ + let name = "c"+j; + for(let i=0;i> 8 ) & 0xff, + ( value >> 16 ) & 0xff, + ( value >> 24 ) & 0xff + ); + + } + + function loadARGBMip( buffer, dataOffset, width, height ) { + + var dataLength = width * height * 4; + var srcBuffer = new Uint8Array( buffer, dataOffset, dataLength ); + var byteArray = new Uint8Array( dataLength ); + var dst = 0; + var src = 0; + for ( var y = 0; y < height; y ++ ) { + + for ( var x = 0; x < width; x ++ ) { + + var b = srcBuffer[ src ]; src ++; + var g = srcBuffer[ src ]; src ++; + var r = srcBuffer[ src ]; src ++; + var a = srcBuffer[ src ]; src ++; + byteArray[ dst ] = r; dst ++; //r + byteArray[ dst ] = g; dst ++; //g + byteArray[ dst ] = b; dst ++; //b + byteArray[ dst ] = a; dst ++; //a + + } + + } + return byteArray; + + } + + var FOURCC_DXT1 = fourCCToInt32( "DXT1" ); + var FOURCC_DXT3 = fourCCToInt32( "DXT3" ); + var FOURCC_DXT5 = fourCCToInt32( "DXT5" ); + var FOURCC_ETC1 = fourCCToInt32( "ETC1" ); + + var headerLengthInt = 31; // The header length in 32 bit ints + + // Offsets into the header array + + var off_magic = 0; + + var off_size = 1; + var off_flags = 2; + var off_height = 3; + var off_width = 4; + + var off_mipmapCount = 7; + + var off_pfFlags = 20; + var off_pfFourCC = 21; + var off_RGBBitCount = 22; + var off_RBitMask = 23; + var off_GBitMask = 24; + var off_BBitMask = 25; + var off_ABitMask = 26; + + var off_caps = 27; + var off_caps2 = 28; + var off_caps3 = 29; + var off_caps4 = 30; + + // Parse header + + var header = new Int32Array( buffer, 0, headerLengthInt ); + + if ( header[ off_magic ] !== DDS_MAGIC ) { + + console.error( 'THREE.DDSLoader.parse: Invalid magic number in DDS header.' ); + return dds; + + } + + if ( ! header[ off_pfFlags ] & DDPF_FOURCC ) { + + console.error( 'THREE.DDSLoader.parse: Unsupported format, must contain a FourCC code.' ); + return dds; + + } + + var blockBytes; + + var fourCC = header[ off_pfFourCC ]; + + var isRGBAUncompressed = false; + + switch ( fourCC ) { + + case FOURCC_DXT1: + + blockBytes = 8; + dds.format = THREE.RGB_S3TC_DXT1_Format; + break; + + case FOURCC_DXT3: + + blockBytes = 16; + dds.format = THREE.RGBA_S3TC_DXT3_Format; + break; + + case FOURCC_DXT5: + + blockBytes = 16; + dds.format = THREE.RGBA_S3TC_DXT5_Format; + break; + + case FOURCC_ETC1: + + blockBytes = 8; + dds.format = THREE.RGB_ETC1_Format; + break; + + default: + + if ( header[ off_RGBBitCount ] === 32 + && header[ off_RBitMask ] & 0xff0000 + && header[ off_GBitMask ] & 0xff00 + && header[ off_BBitMask ] & 0xff + && header[ off_ABitMask ] & 0xff000000 ) { + + isRGBAUncompressed = true; + blockBytes = 64; + dds.format = THREE.RGBAFormat; + + } else { + + console.error( 'THREE.DDSLoader.parse: Unsupported FourCC code ', int32ToFourCC( fourCC ) ); + return dds; + + } + + } + + dds.mipmapCount = 1; + + if ( header[ off_flags ] & DDSD_MIPMAPCOUNT && loadMipmaps !== false ) { + + dds.mipmapCount = Math.max( 1, header[ off_mipmapCount ] ); + + } + + var caps2 = header[ off_caps2 ]; + dds.isCubemap = caps2 & DDSCAPS2_CUBEMAP ? true : false; + if ( dds.isCubemap && ( + ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEX ) || + ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEX ) || + ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEY ) || + ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEY ) || + ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEZ ) || + ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEZ ) + ) ) { + + console.error( 'THREE.DDSLoader.parse: Incomplete cubemap faces' ); + return dds; + + } + + dds.width = header[ off_width ]; + dds.height = header[ off_height ]; + + var dataOffset = header[ off_size ] + 4; + + // Extract mipmaps buffers + + var faces = dds.isCubemap ? 6 : 1; + + for ( var face = 0; face < faces; face ++ ) { + + var width = dds.width; + var height = dds.height; + + for ( var i = 0; i < dds.mipmapCount; i ++ ) { + + if ( isRGBAUncompressed ) { + + var byteArray = loadARGBMip( buffer, dataOffset, width, height ); + var dataLength = byteArray.length; + + } else { + + var dataLength = Math.max( 4, width ) / 4 * Math.max( 4, height ) / 4 * blockBytes; + var byteArray = new Uint8Array( buffer, dataOffset, dataLength ); + + } + + var mipmap = { "data": byteArray, "width": width, "height": height }; + dds.mipmaps.push( mipmap ); + + dataOffset += dataLength; + + width = Math.max( width >> 1, 1 ); + height = Math.max( height >> 1, 1 ); + + } + + } + + return dds; + +}; diff --git a/src/jlmap3d/main/loaders/FBXLoader.js b/src/jlmap3d/main/loaders/FBXLoader.js new file mode 100644 index 000000000..d48df4bcc --- /dev/null +++ b/src/jlmap3d/main/loaders/FBXLoader.js @@ -0,0 +1,4134 @@ + +/** + * @author Kyle-Larson https://github.com/Kyle-Larson + * @author Takahiro https://github.com/takahirox + * @author Lewy Blue https://github.com/looeee + * + * Loader loads FBX file and generates Group representing FBX scene. + * Requires FBX file to be >= 7.0 and in ASCII or >= 6400 in Binary format + * Versions lower than this may load but will probably have errors + * + * Needs Support: + * Morph normals / blend shape normals + * + * FBX format references: + * https://wiki.blender.org/index.php/User:Mont29/Foundation/FBX_File_Structure + * http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_index_html (C++ SDK reference) + * + * Binary format specification: + * https://code.blender.org/2013/08/fbx-binary-file-format-specification/ + */ + + +THREE.FBXLoader = ( function () { + + var fbxTree; + var connections; + var sceneGraph; + + function FBXLoader( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + } + + FBXLoader.prototype = { + + constructor: FBXLoader, + + crossOrigin: 'anonymous', + + load: function ( url, onLoad, onProgress, onError ) { + + var self = this; + + var path = ( self.path === undefined ) ? THREE.LoaderUtils.extractUrlBase( url ) : self.path; + + var loader = new THREE.FileLoader( this.manager ); + loader.setPath( self.path ); + loader.setResponseType( 'arraybuffer' ); + + loader.load( url, function ( buffer ) { + + try { + + onLoad( self.parse( buffer, path ) ); + + } catch ( error ) { + + setTimeout( function () { + + if ( onError ) onError( error ); + + self.manager.itemError( url ); + + }, 0 ); + + } + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + setResourcePath: function ( value ) { + + this.resourcePath = value; + return this; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + return this; + + }, + + parse: function ( FBXBuffer, path ) { + + if ( isFbxFormatBinary( FBXBuffer ) ) { + + fbxTree = new BinaryParser().parse( FBXBuffer ); + + } else { + + var FBXText = convertArrayBufferToString( FBXBuffer ); + + if ( ! isFbxFormatASCII( FBXText ) ) { + + throw new Error( 'THREE.FBXLoader: Unknown format.' ); + + } + + if ( getFbxVersion( FBXText ) < 7000 ) { + + throw new Error( 'THREE.FBXLoader: FBX version not supported, FileVersion: ' + getFbxVersion( FBXText ) ); + + } + + fbxTree = new TextParser().parse( FBXText ); + + } + + var textureLoader = new THREE.TextureLoader( this.manager ).setPath( this.resourcePath || path ).setCrossOrigin( 'anonymous' ); + + return new FBXTreeParser( textureLoader ).parse( fbxTree ); + + } + + }; + + // Parse the FBXTree object returned by the BinaryParser or TextParser and return a THREE.Group + function FBXTreeParser( textureLoader ) { + + this.textureLoader = textureLoader; + + } + + FBXTreeParser.prototype = { + + constructor: FBXTreeParser, + + parse: function () { + + connections = this.parseConnections(); + + var images = this.parseImages(); + var textures = this.parseTextures( images ); + var materials = this.parseMaterials( textures ); + var deformers = this.parseDeformers(); + var geometryMap = new GeometryParser().parse( deformers ); + + this.parseScene( deformers, geometryMap, materials ); + + return sceneGraph; + + }, + + // Parses FBXTree.Connections which holds parent-child connections between objects (e.g. material -> texture, model->geometry ) + // and details the connection type + parseConnections: function () { + + var connectionMap = new Map(); + + if ( 'Connections' in fbxTree ) { + + var rawConnections = fbxTree.Connections.connections; + + rawConnections.forEach( function ( rawConnection ) { + + var fromID = rawConnection[ 0 ]; + var toID = rawConnection[ 1 ]; + var relationship = rawConnection[ 2 ]; + + if ( ! connectionMap.has( fromID ) ) { + + connectionMap.set( fromID, { + parents: [], + children: [] + } ); + + } + + var parentRelationship = { ID: toID, relationship: relationship }; + connectionMap.get( fromID ).parents.push( parentRelationship ); + + if ( ! connectionMap.has( toID ) ) { + + connectionMap.set( toID, { + parents: [], + children: [] + } ); + + } + + var childRelationship = { ID: fromID, relationship: relationship }; + connectionMap.get( toID ).children.push( childRelationship ); + + } ); + + } + + return connectionMap; + + }, + + // Parse FBXTree.Objects.Video for embedded image data + // These images are connected to textures in FBXTree.Objects.Textures + // via FBXTree.Connections. + parseImages: function () { + + var images = {}; + var blobs = {}; + + if ( 'Video' in fbxTree.Objects ) { + + var videoNodes = fbxTree.Objects.Video; + + for ( var nodeID in videoNodes ) { + + var videoNode = videoNodes[ nodeID ]; + + var id = parseInt( nodeID ); + + images[ id ] = videoNode.RelativeFilename || videoNode.Filename; + + // raw image data is in videoNode.Content + if ( 'Content' in videoNode ) { + + var arrayBufferContent = ( videoNode.Content instanceof ArrayBuffer ) && ( videoNode.Content.byteLength > 0 ); + var base64Content = ( typeof videoNode.Content === 'string' ) && ( videoNode.Content !== '' ); + + if ( arrayBufferContent || base64Content ) { + + var image = this.parseImage( videoNodes[ nodeID ] ); + + blobs[ videoNode.RelativeFilename || videoNode.Filename ] = image; + + } + + } + + } + + } + + for ( var id in images ) { + + var filename = images[ id ]; + + if ( blobs[ filename ] !== undefined ) images[ id ] = blobs[ filename ]; + else images[ id ] = images[ id ].split( '\\' ).pop(); + + } + + return images; + + }, + + // Parse embedded image data in FBXTree.Video.Content + parseImage: function ( videoNode ) { + + var content = videoNode.Content; + var fileName = videoNode.RelativeFilename || videoNode.Filename; + var extension = fileName.slice( fileName.lastIndexOf( '.' ) + 1 ).toLowerCase(); + + var type; + + switch ( extension ) { + + case 'bmp': + + type = 'image/bmp'; + break; + + case 'jpg': + case 'jpeg': + + type = 'image/jpeg'; + break; + + case 'png': + + type = 'image/png'; + break; + + case 'tif': + + type = 'image/tiff'; + break; + + case 'tga': + + if ( typeof THREE.TGALoader !== 'function' ) { + + console.warn( 'FBXLoader: THREE.TGALoader is required to load TGA textures' ); + return; + + } else { + + if ( THREE.Loader.Handlers.get( '.tga' ) === null ) { + + var tgaLoader = new THREE.TGALoader(); + tgaLoader.setPath( this.textureLoader.path ); + + THREE.Loader.Handlers.add( /\.tga$/i, tgaLoader ); + + } + + type = 'image/tga'; + break; + + } + + default: + + console.warn( 'FBXLoader: Image type "' + extension + '" is not supported.' ); + return; + + } + + if ( typeof content === 'string' ) { // ASCII format + + return 'data:' + type + ';base64,' + content; + + } else { // Binary Format + + var array = new Uint8Array( content ); + return window.URL.createObjectURL( new Blob( [ array ], { type: type } ) ); + + } + + }, + + // Parse nodes in FBXTree.Objects.Texture + // These contain details such as UV scaling, cropping, rotation etc and are connected + // to images in FBXTree.Objects.Video + parseTextures: function ( images ) { + + var textureMap = new Map(); + + if ( 'Texture' in fbxTree.Objects ) { + + var textureNodes = fbxTree.Objects.Texture; + for ( var nodeID in textureNodes ) { + + var texture = this.parseTexture( textureNodes[ nodeID ], images ); + textureMap.set( parseInt( nodeID ), texture ); + + } + + } + + return textureMap; + + }, + + // Parse individual node in FBXTree.Objects.Texture + parseTexture: function ( textureNode, images ) { + + var texture = this.loadTexture( textureNode, images ); + + texture.ID = textureNode.id; + + texture.name = textureNode.attrName; + + var wrapModeU = textureNode.WrapModeU; + var wrapModeV = textureNode.WrapModeV; + + var valueU = wrapModeU !== undefined ? wrapModeU.value : 0; + var valueV = wrapModeV !== undefined ? wrapModeV.value : 0; + + // http://download.autodesk.com/us/fbx/SDKdocs/FBX_SDK_Help/files/fbxsdkref/class_k_fbx_texture.html#889640e63e2e681259ea81061b85143a + // 0: repeat(default), 1: clamp + + texture.wrapS = valueU === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + texture.wrapT = valueV === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; + + if ( 'Scaling' in textureNode ) { + + var values = textureNode.Scaling.value; + + texture.repeat.x = values[ 0 ]; + texture.repeat.y = values[ 1 ]; + + } + + return texture; + + }, + + // load a texture specified as a blob or data URI, or via an external URL using THREE.TextureLoader + loadTexture: function ( textureNode, images ) { + + var fileName; + + var currentPath = this.textureLoader.path; + + var children = connections.get( textureNode.id ).children; + + if ( children !== undefined && children.length > 0 && images[ children[ 0 ].ID ] !== undefined ) { + + fileName = images[ children[ 0 ].ID ]; + + if ( fileName.indexOf( 'blob:' ) === 0 || fileName.indexOf( 'data:' ) === 0 ) { + + this.textureLoader.setPath( undefined ); + + } + + } + + var texture; + + var extension = textureNode.FileName.slice( - 3 ).toLowerCase(); + + if ( extension === 'tga' ) { + + var loader = THREE.Loader.Handlers.get( '.tga' ); + + if ( loader === null ) { + + console.warn( 'FBXLoader: TGALoader not found, creating empty placeholder texture for', fileName ); + texture = new THREE.Texture(); + + } else { + + texture = loader.load( fileName ); + + } + + } else if ( extension === 'psd' ) { + + console.warn( 'FBXLoader: PSD textures are not supported, creating empty placeholder texture for', fileName ); + texture = new THREE.Texture(); + + } else { + + texture = this.textureLoader.load( fileName ); + + } + + this.textureLoader.setPath( currentPath ); + + return texture; + + }, + + // Parse nodes in FBXTree.Objects.Material + parseMaterials: function ( textureMap ) { + + var materialMap = new Map(); + + if ( 'Material' in fbxTree.Objects ) { + + var materialNodes = fbxTree.Objects.Material; + + for ( var nodeID in materialNodes ) { + + var material = this.parseMaterial( materialNodes[ nodeID ], textureMap ); + + if ( material !== null ) materialMap.set( parseInt( nodeID ), material ); + + } + + } + + return materialMap; + + }, + + // Parse single node in FBXTree.Objects.Material + // Materials are connected to texture maps in FBXTree.Objects.Textures + // FBX format currently only supports Lambert and Phong shading models + parseMaterial: function ( materialNode, textureMap ) { + + var ID = materialNode.id; + var name = materialNode.attrName; + var type = materialNode.ShadingModel; + + // Case where FBX wraps shading model in property object. + if ( typeof type === 'object' ) { + + type = type.value; + + } + + // Ignore unused materials which don't have any connections. + if ( ! connections.has( ID ) ) return null; + + var parameters = this.parseParameters( materialNode, textureMap, ID ); + + var material; + + switch ( type.toLowerCase() ) { + + case 'phong': + material = new THREE.MeshPhongMaterial(); + break; + case 'lambert': + material = new THREE.MeshLambertMaterial(); + break; + default: + console.warn( 'THREE.FBXLoader: unknown material type "%s". Defaulting to MeshPhongMaterial.', type ); + material = new THREE.MeshPhongMaterial(); + break; + + } + + material.setValues( parameters ); + material.name = name; + + return material; + + }, + + // Parse FBX material and return parameters suitable for a three.js material + // Also parse the texture map and return any textures associated with the material + parseParameters: function ( materialNode, textureMap, ID ) { + + var parameters = {}; + + if ( materialNode.BumpFactor ) { + + parameters.bumpScale = materialNode.BumpFactor.value; + + } + if ( materialNode.Diffuse ) { + + parameters.color = new THREE.Color().fromArray( materialNode.Diffuse.value ); + + } else if ( materialNode.DiffuseColor && materialNode.DiffuseColor.type === 'Color' ) { + + // The blender exporter exports diffuse here instead of in materialNode.Diffuse + parameters.color = new THREE.Color().fromArray( materialNode.DiffuseColor.value ); + + } + + if ( materialNode.DisplacementFactor ) { + + parameters.displacementScale = materialNode.DisplacementFactor.value; + + } + + if ( materialNode.Emissive ) { + + parameters.emissive = new THREE.Color().fromArray( materialNode.Emissive.value ); + + } else if ( materialNode.EmissiveColor && materialNode.EmissiveColor.type === 'Color' ) { + + // The blender exporter exports emissive color here instead of in materialNode.Emissive + parameters.emissive = new THREE.Color().fromArray( materialNode.EmissiveColor.value ); + + } + + if ( materialNode.EmissiveFactor ) { + + parameters.emissiveIntensity = parseFloat( materialNode.EmissiveFactor.value ); + + } + + if ( materialNode.Opacity ) { + + parameters.opacity = parseFloat( materialNode.Opacity.value ); + + } + + if ( parameters.opacity < 1.0 ) { + + parameters.transparent = true; + } + + if ( materialNode.ReflectionFactor ) { + + parameters.reflectivity = materialNode.ReflectionFactor.value; + + } + + if ( materialNode.Shininess ) { + + parameters.shininess = materialNode.Shininess.value; + + } + + if ( materialNode.Specular ) { + + parameters.specular = new THREE.Color().fromArray( materialNode.Specular.value ); + + } else if ( materialNode.SpecularColor && materialNode.SpecularColor.type === 'Color' ) { + + // The blender exporter exports specular color here instead of in materialNode.Specular + parameters.specular = new THREE.Color().fromArray( materialNode.SpecularColor.value ); + + } + + var self = this; + connections.get( ID ).children.forEach( function ( child ) { + + var type = child.relationship; + + switch ( type ) { + + case 'Bump': + parameters.bumpMap = self.getTexture( textureMap, child.ID ); + break; + + case 'Maya|TEX_ao_map': + parameters.aoMap = self.getTexture( textureMap, child.ID ); + break; + + case 'DiffuseColor': + case 'Maya|TEX_color_map': + parameters.map = self.getTexture( textureMap, child.ID ); + break; + + case 'DisplacementColor': + parameters.displacementMap = self.getTexture( textureMap, child.ID ); + break; + + case 'EmissiveColor': + parameters.emissiveMap = self.getTexture( textureMap, child.ID ); + break; + + case 'NormalMap': + case 'Maya|TEX_normal_map': + parameters.normalMap = self.getTexture( textureMap, child.ID ); + break; + + case 'ReflectionColor': + parameters.envMap = self.getTexture( textureMap, child.ID ); + parameters.envMap.mapping = THREE.EquirectangularReflectionMapping; + break; + + case 'SpecularColor': + parameters.specularMap = self.getTexture( textureMap, child.ID ); + break; + + case 'TransparentColor': + parameters.alphaMap = self.getTexture( textureMap, child.ID ); + parameters.transparent = true; + break; + + case 'AmbientColor': + case 'ShininessExponent': // AKA glossiness map + case 'SpecularFactor': // AKA specularLevel + case 'VectorDisplacementColor': // NOTE: Seems to be a copy of DisplacementColor + default: + console.warn( 'THREE.FBXLoader: %s map is not supported in three.js, skipping texture.', type ); + break; + + } + + } ); + + return parameters; + + }, + + // get a texture from the textureMap for use by a material. + getTexture: function ( textureMap, id ) { + + // if the texture is a layered texture, just use the first layer and issue a warning + if ( 'LayeredTexture' in fbxTree.Objects && id in fbxTree.Objects.LayeredTexture ) { + + console.warn( 'THREE.FBXLoader: layered textures are not supported in three.js. Discarding all but first layer.' ); + id = connections.get( id ).children[ 0 ].ID; + + } + + return textureMap.get( id ); + + }, + + // Parse nodes in FBXTree.Objects.Deformer + // Deformer node can contain skinning or Vertex Cache animation data, however only skinning is supported here + // Generates map of Skeleton-like objects for use later when generating and binding skeletons. + parseDeformers: function () { + + var skeletons = {}; + var morphTargets = {}; + + if ( 'Deformer' in fbxTree.Objects ) { + + var DeformerNodes = fbxTree.Objects.Deformer; + + for ( var nodeID in DeformerNodes ) { + + var deformerNode = DeformerNodes[ nodeID ]; + + var relationships = connections.get( parseInt( nodeID ) ); + + if ( deformerNode.attrType === 'Skin' ) { + + var skeleton = this.parseSkeleton( relationships, DeformerNodes ); + skeleton.ID = nodeID; + + if ( relationships.parents.length > 1 ) console.warn( 'THREE.FBXLoader: skeleton attached to more than one geometry is not supported.' ); + skeleton.geometryID = relationships.parents[ 0 ].ID; + + skeletons[ nodeID ] = skeleton; + + } else if ( deformerNode.attrType === 'BlendShape' ) { + + var morphTarget = { + id: nodeID, + }; + + morphTarget.rawTargets = this.parseMorphTargets( relationships, DeformerNodes ); + morphTarget.id = nodeID; + + if ( relationships.parents.length > 1 ) console.warn( 'THREE.FBXLoader: morph target attached to more than one geometry is not supported.' ); + + morphTargets[ nodeID ] = morphTarget; + + } + + } + + } + + return { + + skeletons: skeletons, + morphTargets: morphTargets, + + }; + + }, + + // Parse single nodes in FBXTree.Objects.Deformer + // The top level skeleton node has type 'Skin' and sub nodes have type 'Cluster' + // Each skin node represents a skeleton and each cluster node represents a bone + parseSkeleton: function ( relationships, deformerNodes ) { + + var rawBones = []; + + relationships.children.forEach( function ( child ) { + + var boneNode = deformerNodes[ child.ID ]; + + if ( boneNode.attrType !== 'Cluster' ) return; + + var rawBone = { + + ID: child.ID, + indices: [], + weights: [], + transformLink: new THREE.Matrix4().fromArray( boneNode.TransformLink.a ), + // transform: new THREE.Matrix4().fromArray( boneNode.Transform.a ), + // linkMode: boneNode.Mode, + + }; + + if ( 'Indexes' in boneNode ) { + + rawBone.indices = boneNode.Indexes.a; + rawBone.weights = boneNode.Weights.a; + + } + + rawBones.push( rawBone ); + + } ); + + return { + + rawBones: rawBones, + bones: [] + + }; + + }, + + // The top level morph deformer node has type "BlendShape" and sub nodes have type "BlendShapeChannel" + parseMorphTargets: function ( relationships, deformerNodes ) { + + var rawMorphTargets = []; + + for ( var i = 0; i < relationships.children.length; i ++ ) { + + var child = relationships.children[ i ]; + + var morphTargetNode = deformerNodes[ child.ID ]; + + var rawMorphTarget = { + + name: morphTargetNode.attrName, + initialWeight: morphTargetNode.DeformPercent, + id: morphTargetNode.id, + fullWeights: morphTargetNode.FullWeights.a + + }; + + if ( morphTargetNode.attrType !== 'BlendShapeChannel' ) return; + + rawMorphTarget.geoID = connections.get( parseInt( child.ID ) ).children.filter( function ( child ) { + + return child.relationship === undefined; + + } )[ 0 ].ID; + + rawMorphTargets.push( rawMorphTarget ); + + } + + return rawMorphTargets; + + }, + + // create the main THREE.Group() to be returned by the loader + parseScene: function ( deformers, geometryMap, materialMap ) { + + sceneGraph = new THREE.Group(); + + var modelMap = this.parseModels( deformers.skeletons, geometryMap, materialMap ); + + var modelNodes = fbxTree.Objects.Model; + + var self = this; + modelMap.forEach( function ( model ) { + + var modelNode = modelNodes[ model.ID ]; + self.setLookAtProperties( model, modelNode ); + + var parentConnections = connections.get( model.ID ).parents; + + parentConnections.forEach( function ( connection ) { + + var parent = modelMap.get( connection.ID ); + if ( parent !== undefined ) parent.add( model ); + + } ); + + if ( model.parent === null ) { + + sceneGraph.add( model ); + + } + + + } ); + + this.bindSkeleton( deformers.skeletons, geometryMap, modelMap ); + + this.createAmbientLight(); + + this.setupMorphMaterials(); + + sceneGraph.traverse( function ( node ) { + + if ( node.userData.transformData ) { + + if ( node.parent ) node.userData.transformData.parentMatrixWorld = node.parent.matrix; + + var transform = generateTransform( node.userData.transformData ); + + node.applyMatrix( transform ); + + } + + } ); + + var animations = new AnimationParser().parse(); + + // if all the models where already combined in a single group, just return that + if ( sceneGraph.children.length === 1 && sceneGraph.children[ 0 ].isGroup ) { + + sceneGraph.children[ 0 ].animations = animations; + sceneGraph = sceneGraph.children[ 0 ]; + + } + + sceneGraph.animations = animations; + + }, + + // parse nodes in FBXTree.Objects.Model + parseModels: function ( skeletons, geometryMap, materialMap ) { + + var modelMap = new Map(); + var modelNodes = fbxTree.Objects.Model; + + for ( var nodeID in modelNodes ) { + + var id = parseInt( nodeID ); + var node = modelNodes[ nodeID ]; + var relationships = connections.get( id ); + + var model = this.buildSkeleton( relationships, skeletons, id, node.attrName ); + + if ( ! model ) { + + switch ( node.attrType ) { + + case 'Camera': + model = this.createCamera( relationships ); + break; + case 'Light': + model = this.createLight( relationships ); + break; + case 'Mesh': + model = this.createMesh( relationships, geometryMap, materialMap ); + break; + case 'NurbsCurve': + model = this.createCurve( relationships, geometryMap ); + break; + case 'LimbNode': + case 'Root': + model = new THREE.Bone(); + break; + case 'Null': + default: + model = new THREE.Group(); + break; + + } + + model.name = THREE.PropertyBinding.sanitizeNodeName( node.attrName ); + model.ID = id; + + } + + this.getTransformData( model, node ); + modelMap.set( id, model ); + + } + + return modelMap; + + }, + + buildSkeleton: function ( relationships, skeletons, id, name ) { + + var bone = null; + + relationships.parents.forEach( function ( parent ) { + + for ( var ID in skeletons ) { + + var skeleton = skeletons[ ID ]; + + skeleton.rawBones.forEach( function ( rawBone, i ) { + + if ( rawBone.ID === parent.ID ) { + + var subBone = bone; + bone = new THREE.Bone(); + + bone.matrixWorld.copy( rawBone.transformLink ); + + // set name and id here - otherwise in cases where "subBone" is created it will not have a name / id + bone.name = THREE.PropertyBinding.sanitizeNodeName( name ); + bone.ID = id; + + skeleton.bones[ i ] = bone; + + // In cases where a bone is shared between multiple meshes + // duplicate the bone here and and it as a child of the first bone + if ( subBone !== null ) { + + bone.add( subBone ); + + } + + } + + } ); + + } + + } ); + + return bone; + + }, + + // create a THREE.PerspectiveCamera or THREE.OrthographicCamera + createCamera: function ( relationships ) { + + var model; + var cameraAttribute; + + relationships.children.forEach( function ( child ) { + + var attr = fbxTree.Objects.NodeAttribute[ child.ID ]; + + if ( attr !== undefined ) { + + cameraAttribute = attr; + + } + + } ); + + if ( cameraAttribute === undefined ) { + + model = new THREE.Object3D(); + + } else { + + var type = 0; + if ( cameraAttribute.CameraProjectionType !== undefined && cameraAttribute.CameraProjectionType.value === 1 ) { + + type = 1; + + } + + var nearClippingPlane = 1; + if ( cameraAttribute.NearPlane !== undefined ) { + + nearClippingPlane = cameraAttribute.NearPlane.value / 1000; + + } + + var farClippingPlane = 1000; + if ( cameraAttribute.FarPlane !== undefined ) { + + farClippingPlane = cameraAttribute.FarPlane.value / 1000; + + } + + + var width = window.innerWidth; + var height = window.innerHeight; + + if ( cameraAttribute.AspectWidth !== undefined && cameraAttribute.AspectHeight !== undefined ) { + + width = cameraAttribute.AspectWidth.value; + height = cameraAttribute.AspectHeight.value; + + } + + var aspect = width / height; + + var fov = 45; + if ( cameraAttribute.FieldOfView !== undefined ) { + + fov = cameraAttribute.FieldOfView.value; + + } + + var focalLength = cameraAttribute.FocalLength ? cameraAttribute.FocalLength.value : null; + + switch ( type ) { + + case 0: // Perspective + model = new THREE.PerspectiveCamera( fov, aspect, nearClippingPlane, farClippingPlane ); + if ( focalLength !== null ) model.setFocalLength( focalLength ); + break; + + case 1: // Orthographic + model = new THREE.OrthographicCamera( - width / 2, width / 2, height / 2, - height / 2, nearClippingPlane, farClippingPlane ); + break; + + default: + console.warn( 'THREE.FBXLoader: Unknown camera type ' + type + '.' ); + model = new THREE.Object3D(); + break; + + } + + } + + return model; + + }, + + // Create a THREE.DirectionalLight, THREE.PointLight or THREE.SpotLight + createLight: function ( relationships ) { + + var model; + var lightAttribute; + + relationships.children.forEach( function ( child ) { + + var attr = fbxTree.Objects.NodeAttribute[ child.ID ]; + + if ( attr !== undefined ) { + + lightAttribute = attr; + + } + + } ); + + if ( lightAttribute === undefined ) { + + model = new THREE.Object3D(); + + } else { + + var type; + + // LightType can be undefined for Point lights + if ( lightAttribute.LightType === undefined ) { + + type = 0; + + } else { + + type = lightAttribute.LightType.value; + + } + + var color = 0xffffff; + + if ( lightAttribute.Color !== undefined ) { + + color = new THREE.Color().fromArray( lightAttribute.Color.value ); + + } + + var intensity = ( lightAttribute.Intensity === undefined ) ? 1 : lightAttribute.Intensity.value / 100; + + // light disabled + if ( lightAttribute.CastLightOnObject !== undefined && lightAttribute.CastLightOnObject.value === 0 ) { + + intensity = 0; + + } + + var distance = 0; + if ( lightAttribute.FarAttenuationEnd !== undefined ) { + + if ( lightAttribute.EnableFarAttenuation !== undefined && lightAttribute.EnableFarAttenuation.value === 0 ) { + + distance = 0; + + } else { + + distance = lightAttribute.FarAttenuationEnd.value; + + } + + } + + // TODO: could this be calculated linearly from FarAttenuationStart to FarAttenuationEnd? + var decay = 1; + + switch ( type ) { + + case 0: // Point + model = new THREE.PointLight( color, intensity, distance, decay ); + break; + + case 1: // Directional + model = new THREE.DirectionalLight( color, intensity ); + break; + + case 2: // Spot + var angle = Math.PI / 3; + + if ( lightAttribute.InnerAngle !== undefined ) { + + angle = THREE.Math.degToRad( lightAttribute.InnerAngle.value ); + + } + + var penumbra = 0; + if ( lightAttribute.OuterAngle !== undefined ) { + + // TODO: this is not correct - FBX calculates outer and inner angle in degrees + // with OuterAngle > InnerAngle && OuterAngle <= Math.PI + // while three.js uses a penumbra between (0, 1) to attenuate the inner angle + penumbra = THREE.Math.degToRad( lightAttribute.OuterAngle.value ); + penumbra = Math.max( penumbra, 1 ); + + } + + model = new THREE.SpotLight( color, intensity, distance, angle, penumbra, decay ); + break; + + default: + console.warn( 'THREE.FBXLoader: Unknown light type ' + lightAttribute.LightType.value + ', defaulting to a THREE.PointLight.' ); + model = new THREE.PointLight( color, intensity ); + break; + + } + + if ( lightAttribute.CastShadows !== undefined && lightAttribute.CastShadows.value === 1 ) { + + model.castShadow = true; + + } + + } + + return model; + + }, + + createMesh: function ( relationships, geometryMap, materialMap ) { + + var model; + var geometry = null; + var material = null; + var materials = []; + + // get geometry and materials(s) from connections + relationships.children.forEach( function ( child ) { + + if ( geometryMap.has( child.ID ) ) { + + geometry = geometryMap.get( child.ID ); + + } + + if ( materialMap.has( child.ID ) ) { + + materials.push( materialMap.get( child.ID ) ); + + } + + } ); + + if ( materials.length > 1 ) { + + material = materials; + + } else if ( materials.length > 0 ) { + + material = materials[ 0 ]; + + } else { + + material = new THREE.MeshPhongMaterial( { color: 0xcccccc } ); + materials.push( material ); + + } + + if ( 'color' in geometry.attributes ) { + + materials.forEach( function ( material ) { + + material.vertexColors = THREE.VertexColors; + + } ); + + } + + if ( geometry.FBX_Deformer ) { + + materials.forEach( function ( material ) { + + material.skinning = true; + + } ); + + model = new THREE.SkinnedMesh( geometry, material ); + model.normalizeSkinWeights(); + + } else { + + model = new THREE.Mesh( geometry, material ); + + } + + return model; + + }, + + createCurve: function ( relationships, geometryMap ) { + + var geometry = relationships.children.reduce( function ( geo, child ) { + + if ( geometryMap.has( child.ID ) ) geo = geometryMap.get( child.ID ); + + return geo; + + }, null ); + + // FBX does not list materials for Nurbs lines, so we'll just put our own in here. + var material = new THREE.LineBasicMaterial( { color: 0x3300ff, linewidth: 1 } ); + return new THREE.Line( geometry, material ); + + }, + + // parse the model node for transform data + getTransformData: function ( model, modelNode ) { + + var transformData = {}; + + if ( 'InheritType' in modelNode ) transformData.inheritType = parseInt( modelNode.InheritType.value ); + + if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = getEulerOrder( modelNode.RotationOrder.value ); + else transformData.eulerOrder = 'ZYX'; + + if ( 'Lcl_Translation' in modelNode ) transformData.translation = modelNode.Lcl_Translation.value; + + if ( 'PreRotation' in modelNode ) transformData.preRotation = modelNode.PreRotation.value; + if ( 'Lcl_Rotation' in modelNode ) transformData.rotation = modelNode.Lcl_Rotation.value; + if ( 'PostRotation' in modelNode ) transformData.postRotation = modelNode.PostRotation.value; + + if ( 'Lcl_Scaling' in modelNode ) transformData.scale = modelNode.Lcl_Scaling.value; + + if ( 'ScalingOffset' in modelNode ) transformData.scalingOffset = modelNode.ScalingOffset.value; + if ( 'ScalingPivot' in modelNode ) transformData.scalingPivot = modelNode.ScalingPivot.value; + + if ( 'RotationOffset' in modelNode ) transformData.rotationOffset = modelNode.RotationOffset.value; + if ( 'RotationPivot' in modelNode ) transformData.rotationPivot = modelNode.RotationPivot.value; + + model.userData.transformData = transformData; + + }, + + setLookAtProperties: function ( model, modelNode ) { + + if ( 'LookAtProperty' in modelNode ) { + + var children = connections.get( model.ID ).children; + + children.forEach( function ( child ) { + + if ( child.relationship === 'LookAtProperty' ) { + + var lookAtTarget = fbxTree.Objects.Model[ child.ID ]; + + if ( 'Lcl_Translation' in lookAtTarget ) { + + var pos = lookAtTarget.Lcl_Translation.value; + + // DirectionalLight, SpotLight + if ( model.target !== undefined ) { + + model.target.position.fromArray( pos ); + sceneGraph.add( model.target ); + + } else { // Cameras and other Object3Ds + + model.lookAt( new THREE.Vector3().fromArray( pos ) ); + + } + + } + + } + + } ); + + } + + }, + + bindSkeleton: function ( skeletons, geometryMap, modelMap ) { + + var bindMatrices = this.parsePoseNodes(); + + for ( var ID in skeletons ) { + + var skeleton = skeletons[ ID ]; + + var parents = connections.get( parseInt( skeleton.ID ) ).parents; + + parents.forEach( function ( parent ) { + + if ( geometryMap.has( parent.ID ) ) { + + var geoID = parent.ID; + var geoRelationships = connections.get( geoID ); + + geoRelationships.parents.forEach( function ( geoConnParent ) { + + if ( modelMap.has( geoConnParent.ID ) ) { + + var model = modelMap.get( geoConnParent.ID ); + + model.bind( new THREE.Skeleton( skeleton.bones ), bindMatrices[ geoConnParent.ID ] ); + + } + + } ); + + } + + } ); + + } + + }, + + parsePoseNodes: function () { + + var bindMatrices = {}; + + if ( 'Pose' in fbxTree.Objects ) { + + var BindPoseNode = fbxTree.Objects.Pose; + + for ( var nodeID in BindPoseNode ) { + + if ( BindPoseNode[ nodeID ].attrType === 'BindPose' ) { + + var poseNodes = BindPoseNode[ nodeID ].PoseNode; + + if ( Array.isArray( poseNodes ) ) { + + poseNodes.forEach( function ( poseNode ) { + + bindMatrices[ poseNode.Node ] = new THREE.Matrix4().fromArray( poseNode.Matrix.a ); + + } ); + + } else { + + bindMatrices[ poseNodes.Node ] = new THREE.Matrix4().fromArray( poseNodes.Matrix.a ); + + } + + } + + } + + } + + return bindMatrices; + + }, + + // Parse ambient color in FBXTree.GlobalSettings - if it's not set to black (default), create an ambient light + createAmbientLight: function () { + + if ( 'GlobalSettings' in fbxTree && 'AmbientColor' in fbxTree.GlobalSettings ) { + + var ambientColor = fbxTree.GlobalSettings.AmbientColor.value; + var r = ambientColor[ 0 ]; + var g = ambientColor[ 1 ]; + var b = ambientColor[ 2 ]; + + if ( r !== 0 || g !== 0 || b !== 0 ) { + + var color = new THREE.Color( r, g, b ); + sceneGraph.add( new THREE.AmbientLight( color, 1 ) ); + + } + + } + + }, + + setupMorphMaterials: function () { + + var self = this; + sceneGraph.traverse( function ( child ) { + + if ( child.isMesh ) { + + if ( child.geometry.morphAttributes.position && child.geometry.morphAttributes.position.length ) { + + if ( Array.isArray( child.material ) ) { + + child.material.forEach( function ( material, i ) { + + self.setupMorphMaterial( child, material, i ); + + } ); + + } else { + + self.setupMorphMaterial( child, child.material ); + + } + + } + + } + + } ); + + }, + + setupMorphMaterial: function ( child, material, index ) { + + var uuid = child.uuid; + var matUuid = material.uuid; + + // if a geometry has morph targets, it cannot share the material with other geometries + var sharedMat = false; + + sceneGraph.traverse( function ( node ) { + + if ( node.isMesh ) { + + if ( Array.isArray( node.material ) ) { + + node.material.forEach( function ( mat ) { + + if ( mat.uuid === matUuid && node.uuid !== uuid ) sharedMat = true; + + } ); + + } else if ( node.material.uuid === matUuid && node.uuid !== uuid ) sharedMat = true; + + } + + } ); + + if ( sharedMat === true ) { + + var clonedMat = material.clone(); + clonedMat.morphTargets = true; + + if ( index === undefined ) child.material = clonedMat; + else child.material[ index ] = clonedMat; + + } else material.morphTargets = true; + + } + + }; + + // parse Geometry data from FBXTree and return map of BufferGeometries + function GeometryParser() {} + + GeometryParser.prototype = { + + constructor: GeometryParser, + + // Parse nodes in FBXTree.Objects.Geometry + parse: function ( deformers ) { + + var geometryMap = new Map(); + + if ( 'Geometry' in fbxTree.Objects ) { + + var geoNodes = fbxTree.Objects.Geometry; + + for ( var nodeID in geoNodes ) { + + var relationships = connections.get( parseInt( nodeID ) ); + var geo = this.parseGeometry( relationships, geoNodes[ nodeID ], deformers ); + + geometryMap.set( parseInt( nodeID ), geo ); + + } + + } + + return geometryMap; + + }, + + // Parse single node in FBXTree.Objects.Geometry + parseGeometry: function ( relationships, geoNode, deformers ) { + + switch ( geoNode.attrType ) { + + case 'Mesh': + return this.parseMeshGeometry( relationships, geoNode, deformers ); + break; + + case 'NurbsCurve': + return this.parseNurbsGeometry( geoNode ); + break; + + } + + }, + + // Parse single node mesh geometry in FBXTree.Objects.Geometry + parseMeshGeometry: function ( relationships, geoNode, deformers ) { + + var skeletons = deformers.skeletons; + var morphTargets = deformers.morphTargets; + + var modelNodes = relationships.parents.map( function ( parent ) { + + return fbxTree.Objects.Model[ parent.ID ]; + + } ); + + // don't create geometry if it is not associated with any models + if ( modelNodes.length === 0 ) return; + + var skeleton = relationships.children.reduce( function ( skeleton, child ) { + + if ( skeletons[ child.ID ] !== undefined ) skeleton = skeletons[ child.ID ]; + + return skeleton; + + }, null ); + + var morphTarget = relationships.children.reduce( function ( morphTarget, child ) { + + if ( morphTargets[ child.ID ] !== undefined ) morphTarget = morphTargets[ child.ID ]; + + return morphTarget; + + }, null ); + + // Assume one model and get the preRotation from that + // if there is more than one model associated with the geometry this may cause problems + var modelNode = modelNodes[ 0 ]; + + var transformData = {}; + + if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = getEulerOrder( modelNode.RotationOrder.value ); + if ( 'InheritType' in modelNode ) transformData.inheritType = parseInt( modelNode.InheritType.value ); + + if ( 'GeometricTranslation' in modelNode ) transformData.translation = modelNode.GeometricTranslation.value; + if ( 'GeometricRotation' in modelNode ) transformData.rotation = modelNode.GeometricRotation.value; + if ( 'GeometricScaling' in modelNode ) transformData.scale = modelNode.GeometricScaling.value; + + var transform = generateTransform( transformData ); + + return this.genGeometry( geoNode, skeleton, morphTarget, transform ); + + }, + + // Generate a THREE.BufferGeometry from a node in FBXTree.Objects.Geometry + genGeometry: function ( geoNode, skeleton, morphTarget, preTransform ) { + + var geo = new THREE.BufferGeometry(); + if ( geoNode.attrName ) geo.name = geoNode.attrName; + + var geoInfo = this.parseGeoNode( geoNode, skeleton ); + var buffers = this.genBuffers( geoInfo ); + + var positionAttribute = new THREE.Float32BufferAttribute( buffers.vertex, 3 ); + + preTransform.applyToBufferAttribute( positionAttribute ); + + geo.addAttribute( 'position', positionAttribute ); + + if ( buffers.colors.length > 0 ) { + + geo.addAttribute( 'color', new THREE.Float32BufferAttribute( buffers.colors, 3 ) ); + + } + + if ( skeleton ) { + + geo.addAttribute( 'skinIndex', new THREE.Uint16BufferAttribute( buffers.weightsIndices, 4 ) ); + + geo.addAttribute( 'skinWeight', new THREE.Float32BufferAttribute( buffers.vertexWeights, 4 ) ); + + // used later to bind the skeleton to the model + geo.FBX_Deformer = skeleton; + + } + + if ( buffers.normal.length > 0 ) { + + var normalAttribute = new THREE.Float32BufferAttribute( buffers.normal, 3 ); + + var normalMatrix = new THREE.Matrix3().getNormalMatrix( preTransform ); + normalMatrix.applyToBufferAttribute( normalAttribute ); + + geo.addAttribute( 'normal', normalAttribute ); + + } + + buffers.uvs.forEach( function ( uvBuffer, i ) { + + // subsequent uv buffers are called 'uv1', 'uv2', ... + var name = 'uv' + ( i + 1 ).toString(); + + // the first uv buffer is just called 'uv' + if ( i === 0 ) { + + name = 'uv'; + + } + + geo.addAttribute( name, new THREE.Float32BufferAttribute( buffers.uvs[ i ], 2 ) ); + + } ); + + if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) { + + // Convert the material indices of each vertex into rendering groups on the geometry. + var prevMaterialIndex = buffers.materialIndex[ 0 ]; + var startIndex = 0; + + buffers.materialIndex.forEach( function ( currentIndex, i ) { + + if ( currentIndex !== prevMaterialIndex ) { + + geo.addGroup( startIndex, i - startIndex, prevMaterialIndex ); + + prevMaterialIndex = currentIndex; + startIndex = i; + + } + + } ); + + // the loop above doesn't add the last group, do that here. + if ( geo.groups.length > 0 ) { + + var lastGroup = geo.groups[ geo.groups.length - 1 ]; + var lastIndex = lastGroup.start + lastGroup.count; + + if ( lastIndex !== buffers.materialIndex.length ) { + + geo.addGroup( lastIndex, buffers.materialIndex.length - lastIndex, prevMaterialIndex ); + + } + + } + + // case where there are multiple materials but the whole geometry is only + // using one of them + if ( geo.groups.length === 0 ) { + + geo.addGroup( 0, buffers.materialIndex.length, buffers.materialIndex[ 0 ] ); + + } + + } + + this.addMorphTargets( geo, geoNode, morphTarget, preTransform ); + + return geo; + + }, + + parseGeoNode: function ( geoNode, skeleton ) { + + var geoInfo = {}; + + geoInfo.vertexPositions = ( geoNode.Vertices !== undefined ) ? geoNode.Vertices.a : []; + geoInfo.vertexIndices = ( geoNode.PolygonVertexIndex !== undefined ) ? geoNode.PolygonVertexIndex.a : []; + + if ( geoNode.LayerElementColor ) { + + geoInfo.color = this.parseVertexColors( geoNode.LayerElementColor[ 0 ] ); + + } + + if ( geoNode.LayerElementMaterial ) { + + geoInfo.material = this.parseMaterialIndices( geoNode.LayerElementMaterial[ 0 ] ); + + } + + if ( geoNode.LayerElementNormal ) { + + geoInfo.normal = this.parseNormals( geoNode.LayerElementNormal[ 0 ] ); + + } + + if ( geoNode.LayerElementUV ) { + + geoInfo.uv = []; + + var i = 0; + while ( geoNode.LayerElementUV[ i ] ) { + + geoInfo.uv.push( this.parseUVs( geoNode.LayerElementUV[ i ] ) ); + i ++; + + } + + } + + geoInfo.weightTable = {}; + + if ( skeleton !== null ) { + + geoInfo.skeleton = skeleton; + + skeleton.rawBones.forEach( function ( rawBone, i ) { + + // loop over the bone's vertex indices and weights + rawBone.indices.forEach( function ( index, j ) { + + if ( geoInfo.weightTable[ index ] === undefined ) geoInfo.weightTable[ index ] = []; + + geoInfo.weightTable[ index ].push( { + + id: i, + weight: rawBone.weights[ j ], + + } ); + + } ); + + } ); + + } + + return geoInfo; + + }, + + genBuffers: function ( geoInfo ) { + + var buffers = { + vertex: [], + normal: [], + colors: [], + uvs: [], + materialIndex: [], + vertexWeights: [], + weightsIndices: [], + }; + + var polygonIndex = 0; + var faceLength = 0; + var displayedWeightsWarning = false; + + // these will hold data for a single face + var facePositionIndexes = []; + var faceNormals = []; + var faceColors = []; + var faceUVs = []; + var faceWeights = []; + var faceWeightIndices = []; + + var self = this; + geoInfo.vertexIndices.forEach( function ( vertexIndex, polygonVertexIndex ) { + + var endOfFace = false; + + // Face index and vertex index arrays are combined in a single array + // A cube with quad faces looks like this: + // PolygonVertexIndex: *24 { + // a: 0, 1, 3, -3, 2, 3, 5, -5, 4, 5, 7, -7, 6, 7, 1, -1, 1, 7, 5, -4, 6, 0, 2, -5 + // } + // Negative numbers mark the end of a face - first face here is 0, 1, 3, -3 + // to find index of last vertex bit shift the index: ^ - 1 + if ( vertexIndex < 0 ) { + + vertexIndex = vertexIndex ^ - 1; // equivalent to ( x * -1 ) - 1 + endOfFace = true; + + } + + var weightIndices = []; + var weights = []; + + facePositionIndexes.push( vertexIndex * 3, vertexIndex * 3 + 1, vertexIndex * 3 + 2 ); + + if ( geoInfo.color ) { + + var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.color ); + + faceColors.push( data[ 0 ], data[ 1 ], data[ 2 ] ); + + } + + if ( geoInfo.skeleton ) { + + if ( geoInfo.weightTable[ vertexIndex ] !== undefined ) { + + geoInfo.weightTable[ vertexIndex ].forEach( function ( wt ) { + + weights.push( wt.weight ); + weightIndices.push( wt.id ); + + } ); + + + } + + if ( weights.length > 4 ) { + + if ( ! displayedWeightsWarning ) { + + console.warn( 'THREE.FBXLoader: Vertex has more than 4 skinning weights assigned to vertex. Deleting additional weights.' ); + displayedWeightsWarning = true; + + } + + var wIndex = [ 0, 0, 0, 0 ]; + var Weight = [ 0, 0, 0, 0 ]; + + weights.forEach( function ( weight, weightIndex ) { + + var currentWeight = weight; + var currentIndex = weightIndices[ weightIndex ]; + + Weight.forEach( function ( comparedWeight, comparedWeightIndex, comparedWeightArray ) { + + if ( currentWeight > comparedWeight ) { + + comparedWeightArray[ comparedWeightIndex ] = currentWeight; + currentWeight = comparedWeight; + + var tmp = wIndex[ comparedWeightIndex ]; + wIndex[ comparedWeightIndex ] = currentIndex; + currentIndex = tmp; + + } + + } ); + + } ); + + weightIndices = wIndex; + weights = Weight; + + } + + // if the weight array is shorter than 4 pad with 0s + while ( weights.length < 4 ) { + + weights.push( 0 ); + weightIndices.push( 0 ); + + } + + for ( var i = 0; i < 4; ++ i ) { + + faceWeights.push( weights[ i ] ); + faceWeightIndices.push( weightIndices[ i ] ); + + } + + } + + if ( geoInfo.normal ) { + + var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.normal ); + + faceNormals.push( data[ 0 ], data[ 1 ], data[ 2 ] ); + + } + + if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) { + + var materialIndex = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.material )[ 0 ]; + + } + + if ( geoInfo.uv ) { + + geoInfo.uv.forEach( function ( uv, i ) { + + var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, uv ); + + if ( faceUVs[ i ] === undefined ) { + + faceUVs[ i ] = []; + + } + + faceUVs[ i ].push( data[ 0 ] ); + faceUVs[ i ].push( data[ 1 ] ); + + } ); + + } + + faceLength ++; + + if ( endOfFace ) { + + self.genFace( buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength ); + + polygonIndex ++; + faceLength = 0; + + // reset arrays for the next face + facePositionIndexes = []; + faceNormals = []; + faceColors = []; + faceUVs = []; + faceWeights = []; + faceWeightIndices = []; + + } + + } ); + + return buffers; + + }, + + // Generate data for a single face in a geometry. If the face is a quad then split it into 2 tris + genFace: function ( buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength ) { + + for ( var i = 2; i < faceLength; i ++ ) { + + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 0 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 1 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 2 ] ] ); + + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 + 1 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 + 2 ] ] ); + + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 + 1 ] ] ); + buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 + 2 ] ] ); + + if ( geoInfo.skeleton ) { + + buffers.vertexWeights.push( faceWeights[ 0 ] ); + buffers.vertexWeights.push( faceWeights[ 1 ] ); + buffers.vertexWeights.push( faceWeights[ 2 ] ); + buffers.vertexWeights.push( faceWeights[ 3 ] ); + + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 ] ); + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 1 ] ); + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 2 ] ); + buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 3 ] ); + + buffers.vertexWeights.push( faceWeights[ i * 4 ] ); + buffers.vertexWeights.push( faceWeights[ i * 4 + 1 ] ); + buffers.vertexWeights.push( faceWeights[ i * 4 + 2 ] ); + buffers.vertexWeights.push( faceWeights[ i * 4 + 3 ] ); + + buffers.weightsIndices.push( faceWeightIndices[ 0 ] ); + buffers.weightsIndices.push( faceWeightIndices[ 1 ] ); + buffers.weightsIndices.push( faceWeightIndices[ 2 ] ); + buffers.weightsIndices.push( faceWeightIndices[ 3 ] ); + + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 ] ); + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 1 ] ); + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 2 ] ); + buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 3 ] ); + + buffers.weightsIndices.push( faceWeightIndices[ i * 4 ] ); + buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 1 ] ); + buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 2 ] ); + buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 3 ] ); + + } + + if ( geoInfo.color ) { + + buffers.colors.push( faceColors[ 0 ] ); + buffers.colors.push( faceColors[ 1 ] ); + buffers.colors.push( faceColors[ 2 ] ); + + buffers.colors.push( faceColors[ ( i - 1 ) * 3 ] ); + buffers.colors.push( faceColors[ ( i - 1 ) * 3 + 1 ] ); + buffers.colors.push( faceColors[ ( i - 1 ) * 3 + 2 ] ); + + buffers.colors.push( faceColors[ i * 3 ] ); + buffers.colors.push( faceColors[ i * 3 + 1 ] ); + buffers.colors.push( faceColors[ i * 3 + 2 ] ); + + } + + if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) { + + buffers.materialIndex.push( materialIndex ); + buffers.materialIndex.push( materialIndex ); + buffers.materialIndex.push( materialIndex ); + + } + + if ( geoInfo.normal ) { + + buffers.normal.push( faceNormals[ 0 ] ); + buffers.normal.push( faceNormals[ 1 ] ); + buffers.normal.push( faceNormals[ 2 ] ); + + buffers.normal.push( faceNormals[ ( i - 1 ) * 3 ] ); + buffers.normal.push( faceNormals[ ( i - 1 ) * 3 + 1 ] ); + buffers.normal.push( faceNormals[ ( i - 1 ) * 3 + 2 ] ); + + buffers.normal.push( faceNormals[ i * 3 ] ); + buffers.normal.push( faceNormals[ i * 3 + 1 ] ); + buffers.normal.push( faceNormals[ i * 3 + 2 ] ); + + } + + if ( geoInfo.uv ) { + + geoInfo.uv.forEach( function ( uv, j ) { + + if ( buffers.uvs[ j ] === undefined ) buffers.uvs[ j ] = []; + + buffers.uvs[ j ].push( faceUVs[ j ][ 0 ] ); + buffers.uvs[ j ].push( faceUVs[ j ][ 1 ] ); + + buffers.uvs[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 ] ); + buffers.uvs[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 + 1 ] ); + + buffers.uvs[ j ].push( faceUVs[ j ][ i * 2 ] ); + buffers.uvs[ j ].push( faceUVs[ j ][ i * 2 + 1 ] ); + + } ); + + } + + } + + }, + + addMorphTargets: function ( parentGeo, parentGeoNode, morphTarget, preTransform ) { + + if ( morphTarget === null ) return; + + parentGeo.morphAttributes.position = []; + // parentGeo.morphAttributes.normal = []; // not implemented + + var self = this; + morphTarget.rawTargets.forEach( function ( rawTarget ) { + + var morphGeoNode = fbxTree.Objects.Geometry[ rawTarget.geoID ]; + + if ( morphGeoNode !== undefined ) { + + self.genMorphGeometry( parentGeo, parentGeoNode, morphGeoNode, preTransform, rawTarget.name ); + + } + + } ); + + }, + + // a morph geometry node is similar to a standard node, and the node is also contained + // in FBXTree.Objects.Geometry, however it can only have attributes for position, normal + // and a special attribute Index defining which vertices of the original geometry are affected + // Normal and position attributes only have data for the vertices that are affected by the morph + genMorphGeometry: function ( parentGeo, parentGeoNode, morphGeoNode, preTransform, name ) { + + var morphGeo = new THREE.BufferGeometry(); + if ( morphGeoNode.attrName ) morphGeo.name = morphGeoNode.attrName; + + var vertexIndices = ( parentGeoNode.PolygonVertexIndex !== undefined ) ? parentGeoNode.PolygonVertexIndex.a : []; + + // make a copy of the parent's vertex positions + var vertexPositions = ( parentGeoNode.Vertices !== undefined ) ? parentGeoNode.Vertices.a.slice() : []; + + var morphPositions = ( morphGeoNode.Vertices !== undefined ) ? morphGeoNode.Vertices.a : []; + var indices = ( morphGeoNode.Indexes !== undefined ) ? morphGeoNode.Indexes.a : []; + + for ( var i = 0; i < indices.length; i ++ ) { + + var morphIndex = indices[ i ] * 3; + + // FBX format uses blend shapes rather than morph targets. This can be converted + // by additively combining the blend shape positions with the original geometry's positions + vertexPositions[ morphIndex ] += morphPositions[ i * 3 ]; + vertexPositions[ morphIndex + 1 ] += morphPositions[ i * 3 + 1 ]; + vertexPositions[ morphIndex + 2 ] += morphPositions[ i * 3 + 2 ]; + + } + + // TODO: add morph normal support + var morphGeoInfo = { + vertexIndices: vertexIndices, + vertexPositions: vertexPositions, + }; + + var morphBuffers = this.genBuffers( morphGeoInfo ); + + var positionAttribute = new THREE.Float32BufferAttribute( morphBuffers.vertex, 3 ); + positionAttribute.name = name || morphGeoNode.attrName; + + preTransform.applyToBufferAttribute( positionAttribute ); + + parentGeo.morphAttributes.position.push( positionAttribute ); + + }, + + // Parse normal from FBXTree.Objects.Geometry.LayerElementNormal if it exists + parseNormals: function ( NormalNode ) { + + var mappingType = NormalNode.MappingInformationType; + var referenceType = NormalNode.ReferenceInformationType; + var buffer = NormalNode.Normals.a; + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + if ( 'NormalIndex' in NormalNode ) { + + indexBuffer = NormalNode.NormalIndex.a; + + } else if ( 'NormalsIndex' in NormalNode ) { + + indexBuffer = NormalNode.NormalsIndex.a; + + } + + } + + return { + dataSize: 3, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Parse UVs from FBXTree.Objects.Geometry.LayerElementUV if it exists + parseUVs: function ( UVNode ) { + + var mappingType = UVNode.MappingInformationType; + var referenceType = UVNode.ReferenceInformationType; + var buffer = UVNode.UV.a; + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + indexBuffer = UVNode.UVIndex.a; + + } + + return { + dataSize: 2, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Parse Vertex Colors from FBXTree.Objects.Geometry.LayerElementColor if it exists + parseVertexColors: function ( ColorNode ) { + + var mappingType = ColorNode.MappingInformationType; + var referenceType = ColorNode.ReferenceInformationType; + var buffer = ColorNode.Colors.a; + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + indexBuffer = ColorNode.ColorIndex.a; + + } + + return { + dataSize: 4, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Parse mapping and material data in FBXTree.Objects.Geometry.LayerElementMaterial if it exists + parseMaterialIndices: function ( MaterialNode ) { + + var mappingType = MaterialNode.MappingInformationType; + var referenceType = MaterialNode.ReferenceInformationType; + + if ( mappingType === 'NoMappingInformation' ) { + + return { + dataSize: 1, + buffer: [ 0 ], + indices: [ 0 ], + mappingType: 'AllSame', + referenceType: referenceType + }; + + } + + var materialIndexBuffer = MaterialNode.Materials.a; + + // Since materials are stored as indices, there's a bit of a mismatch between FBX and what + // we expect.So we create an intermediate buffer that points to the index in the buffer, + // for conforming with the other functions we've written for other data. + var materialIndices = []; + + for ( var i = 0; i < materialIndexBuffer.length; ++ i ) { + + materialIndices.push( i ); + + } + + return { + dataSize: 1, + buffer: materialIndexBuffer, + indices: materialIndices, + mappingType: mappingType, + referenceType: referenceType + }; + + }, + + // Generate a NurbGeometry from a node in FBXTree.Objects.Geometry + parseNurbsGeometry: function ( geoNode ) { + + if ( THREE.NURBSCurve === undefined ) { + + console.error( 'THREE.FBXLoader: The loader relies on THREE.NURBSCurve for any nurbs present in the model. Nurbs will show up as empty geometry.' ); + return new THREE.BufferGeometry(); + + } + + var order = parseInt( geoNode.Order ); + + if ( isNaN( order ) ) { + + console.error( 'THREE.FBXLoader: Invalid Order %s given for geometry ID: %s', geoNode.Order, geoNode.id ); + return new THREE.BufferGeometry(); + + } + + var degree = order - 1; + + var knots = geoNode.KnotVector.a; + var controlPoints = []; + var pointsValues = geoNode.Points.a; + + for ( var i = 0, l = pointsValues.length; i < l; i += 4 ) { + + controlPoints.push( new THREE.Vector4().fromArray( pointsValues, i ) ); + + } + + var startKnot, endKnot; + + if ( geoNode.Form === 'Closed' ) { + + controlPoints.push( controlPoints[ 0 ] ); + + } else if ( geoNode.Form === 'Periodic' ) { + + startKnot = degree; + endKnot = knots.length - 1 - startKnot; + + for ( var i = 0; i < degree; ++ i ) { + + controlPoints.push( controlPoints[ i ] ); + + } + + } + + var curve = new THREE.NURBSCurve( degree, knots, controlPoints, startKnot, endKnot ); + var vertices = curve.getPoints( controlPoints.length * 7 ); + + var positions = new Float32Array( vertices.length * 3 ); + + vertices.forEach( function ( vertex, i ) { + + vertex.toArray( positions, i * 3 ); + + } ); + + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); + + return geometry; + + }, + + }; + + // parse animation data from FBXTree + function AnimationParser() {} + + AnimationParser.prototype = { + + constructor: AnimationParser, + + // take raw animation clips and turn them into three.js animation clips + parse: function () { + + var animationClips = []; + + var rawClips = this.parseClips(); + + if ( rawClips !== undefined ) { + + for ( var key in rawClips ) { + + var rawClip = rawClips[ key ]; + + var clip = this.addClip( rawClip ); + + animationClips.push( clip ); + + } + + } + + return animationClips; + + }, + + parseClips: function () { + + // since the actual transformation data is stored in FBXTree.Objects.AnimationCurve, + // if this is undefined we can safely assume there are no animations + if ( fbxTree.Objects.AnimationCurve === undefined ) return undefined; + + var curveNodesMap = this.parseAnimationCurveNodes(); + + this.parseAnimationCurves( curveNodesMap ); + + var layersMap = this.parseAnimationLayers( curveNodesMap ); + var rawClips = this.parseAnimStacks( layersMap ); + + return rawClips; + + }, + + // parse nodes in FBXTree.Objects.AnimationCurveNode + // each AnimationCurveNode holds data for an animation transform for a model (e.g. left arm rotation ) + // and is referenced by an AnimationLayer + parseAnimationCurveNodes: function () { + + var rawCurveNodes = fbxTree.Objects.AnimationCurveNode; + + var curveNodesMap = new Map(); + + for ( var nodeID in rawCurveNodes ) { + + var rawCurveNode = rawCurveNodes[ nodeID ]; + + if ( rawCurveNode.attrName.match( /S|R|T|DeformPercent/ ) !== null ) { + + var curveNode = { + + id: rawCurveNode.id, + attr: rawCurveNode.attrName, + curves: {}, + + }; + + curveNodesMap.set( curveNode.id, curveNode ); + + } + + } + + return curveNodesMap; + + }, + + // parse nodes in FBXTree.Objects.AnimationCurve and connect them up to + // previously parsed AnimationCurveNodes. Each AnimationCurve holds data for a single animated + // axis ( e.g. times and values of x rotation) + parseAnimationCurves: function ( curveNodesMap ) { + + var rawCurves = fbxTree.Objects.AnimationCurve; + + // TODO: Many values are identical up to roundoff error, but won't be optimised + // e.g. position times: [0, 0.4, 0. 8] + // position values: [7.23538335023477e-7, 93.67518615722656, -0.9982695579528809, 7.23538335023477e-7, 93.67518615722656, -0.9982695579528809, 7.235384487103147e-7, 93.67520904541016, -0.9982695579528809] + // clearly, this should be optimised to + // times: [0], positions [7.23538335023477e-7, 93.67518615722656, -0.9982695579528809] + // this shows up in nearly every FBX file, and generally time array is length > 100 + + for ( var nodeID in rawCurves ) { + + var animationCurve = { + + id: rawCurves[ nodeID ].id, + times: rawCurves[ nodeID ].KeyTime.a.map( convertFBXTimeToSeconds ), + values: rawCurves[ nodeID ].KeyValueFloat.a, + + }; + + var relationships = connections.get( animationCurve.id ); + + if ( relationships !== undefined ) { + + var animationCurveID = relationships.parents[ 0 ].ID; + var animationCurveRelationship = relationships.parents[ 0 ].relationship; + + if ( animationCurveRelationship.match( /X/ ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'x' ] = animationCurve; + + } else if ( animationCurveRelationship.match( /Y/ ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'y' ] = animationCurve; + + } else if ( animationCurveRelationship.match( /Z/ ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'z' ] = animationCurve; + + } else if ( animationCurveRelationship.match( /d|DeformPercent/ ) && curveNodesMap.has( animationCurveID ) ) { + + curveNodesMap.get( animationCurveID ).curves[ 'morph' ] = animationCurve; + + } + + } + + } + + }, + + // parse nodes in FBXTree.Objects.AnimationLayer. Each layers holds references + // to various AnimationCurveNodes and is referenced by an AnimationStack node + // note: theoretically a stack can have multiple layers, however in practice there always seems to be one per stack + parseAnimationLayers: function ( curveNodesMap ) { + + var rawLayers = fbxTree.Objects.AnimationLayer; + + var layersMap = new Map(); + + for ( var nodeID in rawLayers ) { + + var layerCurveNodes = []; + + var connection = connections.get( parseInt( nodeID ) ); + + if ( connection !== undefined ) { + + // all the animationCurveNodes used in the layer + var children = connection.children; + + children.forEach( function ( child, i ) { + + if ( curveNodesMap.has( child.ID ) ) { + + var curveNode = curveNodesMap.get( child.ID ); + + // check that the curves are defined for at least one axis, otherwise ignore the curveNode + if ( curveNode.curves.x !== undefined || curveNode.curves.y !== undefined || curveNode.curves.z !== undefined ) { + + if ( layerCurveNodes[ i ] === undefined ) { + + var modelID = connections.get( child.ID ).parents.filter( function ( parent ) { + + return parent.relationship !== undefined; + + } )[ 0 ].ID; + + if ( modelID !== undefined ) { + + var rawModel = fbxTree.Objects.Model[ modelID.toString() ]; + + var node = { + + modelName: THREE.PropertyBinding.sanitizeNodeName( rawModel.attrName ), + ID: rawModel.id, + initialPosition: [ 0, 0, 0 ], + initialRotation: [ 0, 0, 0 ], + initialScale: [ 1, 1, 1 ], + + }; + + sceneGraph.traverse( function ( child ) { + + if ( child.ID === rawModel.id ) { + + node.transform = child.matrix; + + if ( child.userData.transformData ) node.eulerOrder = child.userData.transformData.eulerOrder; + + } + + } ); + + if ( ! node.transform ) node.transform = new THREE.Matrix4(); + + // if the animated model is pre rotated, we'll have to apply the pre rotations to every + // animation value as well + if ( 'PreRotation' in rawModel ) node.preRotation = rawModel.PreRotation.value; + if ( 'PostRotation' in rawModel ) node.postRotation = rawModel.PostRotation.value; + + layerCurveNodes[ i ] = node; + + } + + } + + if ( layerCurveNodes[ i ] ) layerCurveNodes[ i ][ curveNode.attr ] = curveNode; + + } else if ( curveNode.curves.morph !== undefined ) { + + if ( layerCurveNodes[ i ] === undefined ) { + + var deformerID = connections.get( child.ID ).parents.filter( function ( parent ) { + + return parent.relationship !== undefined; + + } )[ 0 ].ID; + + var morpherID = connections.get( deformerID ).parents[ 0 ].ID; + var geoID = connections.get( morpherID ).parents[ 0 ].ID; + + // assuming geometry is not used in more than one model + var modelID = connections.get( geoID ).parents[ 0 ].ID; + + var rawModel = fbxTree.Objects.Model[ modelID ]; + + var node = { + + modelName: THREE.PropertyBinding.sanitizeNodeName( rawModel.attrName ), + morphName: fbxTree.Objects.Deformer[ deformerID ].attrName, + + }; + + layerCurveNodes[ i ] = node; + + } + + layerCurveNodes[ i ][ curveNode.attr ] = curveNode; + + } + + } + + } ); + + layersMap.set( parseInt( nodeID ), layerCurveNodes ); + + } + + } + + return layersMap; + + }, + + // parse nodes in FBXTree.Objects.AnimationStack. These are the top level node in the animation + // hierarchy. Each Stack node will be used to create a THREE.AnimationClip + parseAnimStacks: function ( layersMap ) { + + var rawStacks = fbxTree.Objects.AnimationStack; + + // connect the stacks (clips) up to the layers + var rawClips = {}; + + for ( var nodeID in rawStacks ) { + + var children = connections.get( parseInt( nodeID ) ).children; + + if ( children.length > 1 ) { + + // it seems like stacks will always be associated with a single layer. But just in case there are files + // where there are multiple layers per stack, we'll display a warning + console.warn( 'THREE.FBXLoader: Encountered an animation stack with multiple layers, this is currently not supported. Ignoring subsequent layers.' ); + + } + + var layer = layersMap.get( children[ 0 ].ID ); + + rawClips[ nodeID ] = { + + name: rawStacks[ nodeID ].attrName, + layer: layer, + + }; + + } + + return rawClips; + + }, + + addClip: function ( rawClip ) { + + var tracks = []; + + var self = this; + rawClip.layer.forEach( function ( rawTracks ) { + + tracks = tracks.concat( self.generateTracks( rawTracks ) ); + + } ); + + return new THREE.AnimationClip( rawClip.name, - 1, tracks ); + + }, + + generateTracks: function ( rawTracks ) { + + var tracks = []; + + var initialPosition = new THREE.Vector3(); + var initialRotation = new THREE.Quaternion(); + var initialScale = new THREE.Vector3(); + + if ( rawTracks.transform ) rawTracks.transform.decompose( initialPosition, initialRotation, initialScale ); + + initialPosition = initialPosition.toArray(); + initialRotation = new THREE.Euler().setFromQuaternion( initialRotation, rawTracks.eulerOrder ).toArray(); + initialScale = initialScale.toArray(); + + if ( rawTracks.T !== undefined && Object.keys( rawTracks.T.curves ).length > 0 ) { + + var positionTrack = this.generateVectorTrack( rawTracks.modelName, rawTracks.T.curves, initialPosition, 'position' ); + if ( positionTrack !== undefined ) tracks.push( positionTrack ); + + } + + if ( rawTracks.R !== undefined && Object.keys( rawTracks.R.curves ).length > 0 ) { + + var rotationTrack = this.generateRotationTrack( rawTracks.modelName, rawTracks.R.curves, initialRotation, rawTracks.preRotation, rawTracks.postRotation, rawTracks.eulerOrder ); + if ( rotationTrack !== undefined ) tracks.push( rotationTrack ); + + } + + if ( rawTracks.S !== undefined && Object.keys( rawTracks.S.curves ).length > 0 ) { + + var scaleTrack = this.generateVectorTrack( rawTracks.modelName, rawTracks.S.curves, initialScale, 'scale' ); + if ( scaleTrack !== undefined ) tracks.push( scaleTrack ); + + } + + if ( rawTracks.DeformPercent !== undefined ) { + + var morphTrack = this.generateMorphTrack( rawTracks ); + if ( morphTrack !== undefined ) tracks.push( morphTrack ); + + } + + return tracks; + + }, + + generateVectorTrack: function ( modelName, curves, initialValue, type ) { + + var times = this.getTimesForAllAxes( curves ); + var values = this.getKeyframeTrackValues( times, curves, initialValue ); + + return new THREE.VectorKeyframeTrack( modelName + '.' + type, times, values ); + + }, + + generateRotationTrack: function ( modelName, curves, initialValue, preRotation, postRotation, eulerOrder ) { + + if ( curves.x !== undefined ) { + + this.interpolateRotations( curves.x ); + curves.x.values = curves.x.values.map( THREE.Math.degToRad ); + + } + if ( curves.y !== undefined ) { + + this.interpolateRotations( curves.y ); + curves.y.values = curves.y.values.map( THREE.Math.degToRad ); + + } + if ( curves.z !== undefined ) { + + this.interpolateRotations( curves.z ); + curves.z.values = curves.z.values.map( THREE.Math.degToRad ); + + } + + var times = this.getTimesForAllAxes( curves ); + var values = this.getKeyframeTrackValues( times, curves, initialValue ); + + if ( preRotation !== undefined ) { + + preRotation = preRotation.map( THREE.Math.degToRad ); + preRotation.push( eulerOrder ); + + preRotation = new THREE.Euler().fromArray( preRotation ); + preRotation = new THREE.Quaternion().setFromEuler( preRotation ); + + } + + if ( postRotation !== undefined ) { + + postRotation = postRotation.map( THREE.Math.degToRad ); + postRotation.push( eulerOrder ); + + postRotation = new THREE.Euler().fromArray( postRotation ); + postRotation = new THREE.Quaternion().setFromEuler( postRotation ).inverse(); + + } + + var quaternion = new THREE.Quaternion(); + var euler = new THREE.Euler(); + + var quaternionValues = []; + + for ( var i = 0; i < values.length; i += 3 ) { + + euler.set( values[ i ], values[ i + 1 ], values[ i + 2 ], eulerOrder ); + + quaternion.setFromEuler( euler ); + + if ( preRotation !== undefined ) quaternion.premultiply( preRotation ); + if ( postRotation !== undefined ) quaternion.multiply( postRotation ); + + quaternion.toArray( quaternionValues, ( i / 3 ) * 4 ); + + } + + return new THREE.QuaternionKeyframeTrack( modelName + '.quaternion', times, quaternionValues ); + + }, + + generateMorphTrack: function ( rawTracks ) { + + var curves = rawTracks.DeformPercent.curves.morph; + var values = curves.values.map( function ( val ) { + + return val / 100; + + } ); + + var morphNum = sceneGraph.getObjectByName( rawTracks.modelName ).morphTargetDictionary[ rawTracks.morphName ]; + + return new THREE.NumberKeyframeTrack( rawTracks.modelName + '.morphTargetInfluences[' + morphNum + ']', curves.times, values ); + + }, + + // For all animated objects, times are defined separately for each axis + // Here we'll combine the times into one sorted array without duplicates + getTimesForAllAxes: function ( curves ) { + + var times = []; + + // first join together the times for each axis, if defined + if ( curves.x !== undefined ) times = times.concat( curves.x.times ); + if ( curves.y !== undefined ) times = times.concat( curves.y.times ); + if ( curves.z !== undefined ) times = times.concat( curves.z.times ); + + // then sort them and remove duplicates + times = times.sort( function ( a, b ) { + + return a - b; + + } ).filter( function ( elem, index, array ) { + + return array.indexOf( elem ) == index; + + } ); + + return times; + + }, + + getKeyframeTrackValues: function ( times, curves, initialValue ) { + + var prevValue = initialValue; + + var values = []; + + var xIndex = - 1; + var yIndex = - 1; + var zIndex = - 1; + + times.forEach( function ( time ) { + + if ( curves.x ) xIndex = curves.x.times.indexOf( time ); + if ( curves.y ) yIndex = curves.y.times.indexOf( time ); + if ( curves.z ) zIndex = curves.z.times.indexOf( time ); + + // if there is an x value defined for this frame, use that + if ( xIndex !== - 1 ) { + + var xValue = curves.x.values[ xIndex ]; + values.push( xValue ); + prevValue[ 0 ] = xValue; + + } else { + + // otherwise use the x value from the previous frame + values.push( prevValue[ 0 ] ); + + } + + if ( yIndex !== - 1 ) { + + var yValue = curves.y.values[ yIndex ]; + values.push( yValue ); + prevValue[ 1 ] = yValue; + + } else { + + values.push( prevValue[ 1 ] ); + + } + + if ( zIndex !== - 1 ) { + + var zValue = curves.z.values[ zIndex ]; + values.push( zValue ); + prevValue[ 2 ] = zValue; + + } else { + + values.push( prevValue[ 2 ] ); + + } + + } ); + + return values; + + }, + + // Rotations are defined as Euler angles which can have values of any size + // These will be converted to quaternions which don't support values greater than + // PI, so we'll interpolate large rotations + interpolateRotations: function ( curve ) { + + for ( var i = 1; i < curve.values.length; i ++ ) { + + var initialValue = curve.values[ i - 1 ]; + var valuesSpan = curve.values[ i ] - initialValue; + + var absoluteSpan = Math.abs( valuesSpan ); + + if ( absoluteSpan >= 180 ) { + + var numSubIntervals = absoluteSpan / 180; + + var step = valuesSpan / numSubIntervals; + var nextValue = initialValue + step; + + var initialTime = curve.times[ i - 1 ]; + var timeSpan = curve.times[ i ] - initialTime; + var interval = timeSpan / numSubIntervals; + var nextTime = initialTime + interval; + + var interpolatedTimes = []; + var interpolatedValues = []; + + while ( nextTime < curve.times[ i ] ) { + + interpolatedTimes.push( nextTime ); + nextTime += interval; + + interpolatedValues.push( nextValue ); + nextValue += step; + + } + + curve.times = inject( curve.times, i, interpolatedTimes ); + curve.values = inject( curve.values, i, interpolatedValues ); + + } + + } + + }, + + }; + + // parse an FBX file in ASCII format + function TextParser() {} + + TextParser.prototype = { + + constructor: TextParser, + + getPrevNode: function () { + + return this.nodeStack[ this.currentIndent - 2 ]; + + }, + + getCurrentNode: function () { + + return this.nodeStack[ this.currentIndent - 1 ]; + + }, + + getCurrentProp: function () { + + return this.currentProp; + + }, + + pushStack: function ( node ) { + + this.nodeStack.push( node ); + this.currentIndent += 1; + + }, + + popStack: function () { + + this.nodeStack.pop(); + this.currentIndent -= 1; + + }, + + setCurrentProp: function ( val, name ) { + + this.currentProp = val; + this.currentPropName = name; + + }, + + parse: function ( text ) { + + this.currentIndent = 0; + + this.allNodes = new FBXTree(); + this.nodeStack = []; + this.currentProp = []; + this.currentPropName = ''; + + var self = this; + + var split = text.split( /[\r\n]+/ ); + + split.forEach( function ( line, i ) { + + var matchComment = line.match( /^[\s\t]*;/ ); + var matchEmpty = line.match( /^[\s\t]*$/ ); + + if ( matchComment || matchEmpty ) return; + + var matchBeginning = line.match( '^\\t{' + self.currentIndent + '}(\\w+):(.*){', '' ); + var matchProperty = line.match( '^\\t{' + ( self.currentIndent ) + '}(\\w+):[\\s\\t\\r\\n](.*)' ); + var matchEnd = line.match( '^\\t{' + ( self.currentIndent - 1 ) + '}}' ); + + if ( matchBeginning ) { + + self.parseNodeBegin( line, matchBeginning ); + + } else if ( matchProperty ) { + + self.parseNodeProperty( line, matchProperty, split[ ++ i ] ); + + } else if ( matchEnd ) { + + self.popStack(); + + } else if ( line.match( /^[^\s\t}]/ ) ) { + + // large arrays are split over multiple lines terminated with a ',' character + // if this is encountered the line needs to be joined to the previous line + self.parseNodePropertyContinued( line ); + + } + + } ); + + return this.allNodes; + + }, + + parseNodeBegin: function ( line, property ) { + + var nodeName = property[ 1 ].trim().replace( /^"/, '' ).replace( /"$/, '' ); + + var nodeAttrs = property[ 2 ].split( ',' ).map( function ( attr ) { + + return attr.trim().replace( /^"/, '' ).replace( /"$/, '' ); + + } ); + + var node = { name: nodeName }; + var attrs = this.parseNodeAttr( nodeAttrs ); + + var currentNode = this.getCurrentNode(); + + // a top node + if ( this.currentIndent === 0 ) { + + this.allNodes.add( nodeName, node ); + + } else { // a subnode + + // if the subnode already exists, append it + if ( nodeName in currentNode ) { + + // special case Pose needs PoseNodes as an array + if ( nodeName === 'PoseNode' ) { + + currentNode.PoseNode.push( node ); + + } else if ( currentNode[ nodeName ].id !== undefined ) { + + currentNode[ nodeName ] = {}; + currentNode[ nodeName ][ currentNode[ nodeName ].id ] = currentNode[ nodeName ]; + + } + + if ( attrs.id !== '' ) currentNode[ nodeName ][ attrs.id ] = node; + + } else if ( typeof attrs.id === 'number' ) { + + currentNode[ nodeName ] = {}; + currentNode[ nodeName ][ attrs.id ] = node; + + } else if ( nodeName !== 'Properties70' ) { + + if ( nodeName === 'PoseNode' ) currentNode[ nodeName ] = [ node ]; + else currentNode[ nodeName ] = node; + + } + + } + + if ( typeof attrs.id === 'number' ) node.id = attrs.id; + if ( attrs.name !== '' ) node.attrName = attrs.name; + if ( attrs.type !== '' ) node.attrType = attrs.type; + + this.pushStack( node ); + + }, + + parseNodeAttr: function ( attrs ) { + + var id = attrs[ 0 ]; + + if ( attrs[ 0 ] !== '' ) { + + id = parseInt( attrs[ 0 ] ); + + if ( isNaN( id ) ) { + + id = attrs[ 0 ]; + + } + + } + + var name = '', type = ''; + + if ( attrs.length > 1 ) { + + name = attrs[ 1 ].replace( /^(\w+)::/, '' ); + type = attrs[ 2 ]; + + } + + return { id: id, name: name, type: type }; + + }, + + parseNodeProperty: function ( line, property, contentLine ) { + + var propName = property[ 1 ].replace( /^"/, '' ).replace( /"$/, '' ).trim(); + var propValue = property[ 2 ].replace( /^"/, '' ).replace( /"$/, '' ).trim(); + + // for special case: base64 image data follows "Content: ," line + // Content: , + // "/9j/4RDaRXhpZgAATU0A..." + if ( propName === 'Content' && propValue === ',' ) { + + propValue = contentLine.replace( /"/g, '' ).replace( /,$/, '' ).trim(); + + } + + var currentNode = this.getCurrentNode(); + var parentName = currentNode.name; + + if ( parentName === 'Properties70' ) { + + this.parseNodeSpecialProperty( line, propName, propValue ); + return; + + } + + // Connections + if ( propName === 'C' ) { + + var connProps = propValue.split( ',' ).slice( 1 ); + var from = parseInt( connProps[ 0 ] ); + var to = parseInt( connProps[ 1 ] ); + + var rest = propValue.split( ',' ).slice( 3 ); + + rest = rest.map( function ( elem ) { + + return elem.trim().replace( /^"/, '' ); + + } ); + + propName = 'connections'; + propValue = [ from, to ]; + append( propValue, rest ); + + if ( currentNode[ propName ] === undefined ) { + + currentNode[ propName ] = []; + + } + + } + + // Node + if ( propName === 'Node' ) currentNode.id = propValue; + + // connections + if ( propName in currentNode && Array.isArray( currentNode[ propName ] ) ) { + + currentNode[ propName ].push( propValue ); + + } else { + + if ( propName !== 'a' ) currentNode[ propName ] = propValue; + else currentNode.a = propValue; + + } + + this.setCurrentProp( currentNode, propName ); + + // convert string to array, unless it ends in ',' in which case more will be added to it + if ( propName === 'a' && propValue.slice( - 1 ) !== ',' ) { + + currentNode.a = parseNumberArray( propValue ); + + } + + }, + + parseNodePropertyContinued: function ( line ) { + + var currentNode = this.getCurrentNode(); + + currentNode.a += line; + + // if the line doesn't end in ',' we have reached the end of the property value + // so convert the string to an array + if ( line.slice( - 1 ) !== ',' ) { + + currentNode.a = parseNumberArray( currentNode.a ); + + } + + }, + + // parse "Property70" + parseNodeSpecialProperty: function ( line, propName, propValue ) { + + // split this + // P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1 + // into array like below + // ["Lcl Scaling", "Lcl Scaling", "", "A", "1,1,1" ] + var props = propValue.split( '",' ).map( function ( prop ) { + + return prop.trim().replace( /^\"/, '' ).replace( /\s/, '_' ); + + } ); + + var innerPropName = props[ 0 ]; + var innerPropType1 = props[ 1 ]; + var innerPropType2 = props[ 2 ]; + var innerPropFlag = props[ 3 ]; + var innerPropValue = props[ 4 ]; + + // cast values where needed, otherwise leave as strings + switch ( innerPropType1 ) { + + case 'int': + case 'enum': + case 'bool': + case 'ULongLong': + case 'double': + case 'Number': + case 'FieldOfView': + innerPropValue = parseFloat( innerPropValue ); + break; + + case 'Color': + case 'ColorRGB': + case 'Vector3D': + case 'Lcl_Translation': + case 'Lcl_Rotation': + case 'Lcl_Scaling': + innerPropValue = parseNumberArray( innerPropValue ); + break; + + } + + // CAUTION: these props must append to parent's parent + this.getPrevNode()[ innerPropName ] = { + + 'type': innerPropType1, + 'type2': innerPropType2, + 'flag': innerPropFlag, + 'value': innerPropValue + + }; + + this.setCurrentProp( this.getPrevNode(), innerPropName ); + + }, + + }; + + // Parse an FBX file in Binary format + function BinaryParser() {} + + BinaryParser.prototype = { + + constructor: BinaryParser, + + parse: function ( buffer ) { + + var reader = new BinaryReader( buffer ); + reader.skip( 23 ); // skip magic 23 bytes + + var version = reader.getUint32(); + + //console.log( 'THREE.FBXLoader: FBX binary version: ' + version ); + + var allNodes = new FBXTree(); + + while ( ! this.endOfContent( reader ) ) { + + var node = this.parseNode( reader, version ); + if ( node !== null ) allNodes.add( node.name, node ); + + } + + return allNodes; + + }, + + // Check if reader has reached the end of content. + endOfContent: function ( reader ) { + + // footer size: 160bytes + 16-byte alignment padding + // - 16bytes: magic + // - padding til 16-byte alignment (at least 1byte?) + // (seems like some exporters embed fixed 15 or 16bytes?) + // - 4bytes: magic + // - 4bytes: version + // - 120bytes: zero + // - 16bytes: magic + if ( reader.size() % 16 === 0 ) { + + return ( ( reader.getOffset() + 160 + 16 ) & ~ 0xf ) >= reader.size(); + + } else { + + return reader.getOffset() + 160 + 16 >= reader.size(); + + } + + }, + + // recursively parse nodes until the end of the file is reached + parseNode: function ( reader, version ) { + + var node = {}; + + // The first three data sizes depends on version. + var endOffset = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32(); + var numProperties = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32(); + + // note: do not remove this even if you get a linter warning as it moves the buffer forward + var propertyListLen = ( version >= 7500 ) ? reader.getUint64() : reader.getUint32(); + + var nameLen = reader.getUint8(); + var name = reader.getString( nameLen ); + + // Regards this node as NULL-record if endOffset is zero + if ( endOffset === 0 ) return null; + + var propertyList = []; + + for ( var i = 0; i < numProperties; i ++ ) { + + propertyList.push( this.parseProperty( reader ) ); + + } + + // Regards the first three elements in propertyList as id, attrName, and attrType + var id = propertyList.length > 0 ? propertyList[ 0 ] : ''; + var attrName = propertyList.length > 1 ? propertyList[ 1 ] : ''; + var attrType = propertyList.length > 2 ? propertyList[ 2 ] : ''; + + // check if this node represents just a single property + // like (name, 0) set or (name2, [0, 1, 2]) set of {name: 0, name2: [0, 1, 2]} + node.singleProperty = ( numProperties === 1 && reader.getOffset() === endOffset ) ? true : false; + + while ( endOffset > reader.getOffset() ) { + + var subNode = this.parseNode( reader, version ); + + if ( subNode !== null ) this.parseSubNode( name, node, subNode ); + + } + + node.propertyList = propertyList; // raw property list used by parent + + if ( typeof id === 'number' ) node.id = id; + if ( attrName !== '' ) node.attrName = attrName; + if ( attrType !== '' ) node.attrType = attrType; + if ( name !== '' ) node.name = name; + + return node; + + }, + + parseSubNode: function ( name, node, subNode ) { + + // special case: child node is single property + if ( subNode.singleProperty === true ) { + + var value = subNode.propertyList[ 0 ]; + + if ( Array.isArray( value ) ) { + + node[ subNode.name ] = subNode; + + subNode.a = value; + + } else { + + node[ subNode.name ] = value; + + } + + } else if ( name === 'Connections' && subNode.name === 'C' ) { + + var array = []; + + subNode.propertyList.forEach( function ( property, i ) { + + // first Connection is FBX type (OO, OP, etc.). We'll discard these + if ( i !== 0 ) array.push( property ); + + } ); + + if ( node.connections === undefined ) { + + node.connections = []; + + } + + node.connections.push( array ); + + } else if ( subNode.name === 'Properties70' ) { + + var keys = Object.keys( subNode ); + + keys.forEach( function ( key ) { + + node[ key ] = subNode[ key ]; + + } ); + + } else if ( name === 'Properties70' && subNode.name === 'P' ) { + + var innerPropName = subNode.propertyList[ 0 ]; + var innerPropType1 = subNode.propertyList[ 1 ]; + var innerPropType2 = subNode.propertyList[ 2 ]; + var innerPropFlag = subNode.propertyList[ 3 ]; + var innerPropValue; + + if ( innerPropName.indexOf( 'Lcl ' ) === 0 ) innerPropName = innerPropName.replace( 'Lcl ', 'Lcl_' ); + if ( innerPropType1.indexOf( 'Lcl ' ) === 0 ) innerPropType1 = innerPropType1.replace( 'Lcl ', 'Lcl_' ); + + if ( innerPropType1 === 'Color' || innerPropType1 === 'ColorRGB' || innerPropType1 === 'Vector' || innerPropType1 === 'Vector3D' || innerPropType1.indexOf( 'Lcl_' ) === 0 ) { + + innerPropValue = [ + subNode.propertyList[ 4 ], + subNode.propertyList[ 5 ], + subNode.propertyList[ 6 ] + ]; + + } else { + + innerPropValue = subNode.propertyList[ 4 ]; + + } + + // this will be copied to parent, see above + node[ innerPropName ] = { + + 'type': innerPropType1, + 'type2': innerPropType2, + 'flag': innerPropFlag, + 'value': innerPropValue + + }; + + } else if ( node[ subNode.name ] === undefined ) { + + if ( typeof subNode.id === 'number' ) { + + node[ subNode.name ] = {}; + node[ subNode.name ][ subNode.id ] = subNode; + + } else { + + node[ subNode.name ] = subNode; + + } + + } else { + + if ( subNode.name === 'PoseNode' ) { + + if ( ! Array.isArray( node[ subNode.name ] ) ) { + + node[ subNode.name ] = [ node[ subNode.name ] ]; + + } + + node[ subNode.name ].push( subNode ); + + } else if ( node[ subNode.name ][ subNode.id ] === undefined ) { + + node[ subNode.name ][ subNode.id ] = subNode; + + } + + } + + }, + + parseProperty: function ( reader ) { + + var type = reader.getString( 1 ); + + switch ( type ) { + + case 'C': + return reader.getBoolean(); + + case 'D': + return reader.getFloat64(); + + case 'F': + return reader.getFloat32(); + + case 'I': + return reader.getInt32(); + + case 'L': + return reader.getInt64(); + + case 'R': + var length = reader.getUint32(); + return reader.getArrayBuffer( length ); + + case 'S': + var length = reader.getUint32(); + return reader.getString( length ); + + case 'Y': + return reader.getInt16(); + + case 'b': + case 'c': + case 'd': + case 'f': + case 'i': + case 'l': + + var arrayLength = reader.getUint32(); + var encoding = reader.getUint32(); // 0: non-compressed, 1: compressed + var compressedLength = reader.getUint32(); + + if ( encoding === 0 ) { + + switch ( type ) { + + case 'b': + case 'c': + return reader.getBooleanArray( arrayLength ); + + case 'd': + return reader.getFloat64Array( arrayLength ); + + case 'f': + return reader.getFloat32Array( arrayLength ); + + case 'i': + return reader.getInt32Array( arrayLength ); + + case 'l': + return reader.getInt64Array( arrayLength ); + + } + + } + + if ( typeof Zlib === 'undefined' ) { + + console.error( 'THREE.FBXLoader: External library Inflate.min.js required, obtain or import from https://github.com/imaya/zlib.js' ); + + } + + var inflate = new Zlib.Inflate( new Uint8Array( reader.getArrayBuffer( compressedLength ) ) ); // eslint-disable-line no-undef + var reader2 = new BinaryReader( inflate.decompress().buffer ); + + switch ( type ) { + + case 'b': + case 'c': + return reader2.getBooleanArray( arrayLength ); + + case 'd': + return reader2.getFloat64Array( arrayLength ); + + case 'f': + return reader2.getFloat32Array( arrayLength ); + + case 'i': + return reader2.getInt32Array( arrayLength ); + + case 'l': + return reader2.getInt64Array( arrayLength ); + + } + + default: + throw new Error( 'THREE.FBXLoader: Unknown property type ' + type ); + + } + + } + + }; + + function BinaryReader( buffer, littleEndian ) { + + this.dv = new DataView( buffer ); + this.offset = 0; + this.littleEndian = ( littleEndian !== undefined ) ? littleEndian : true; + + } + + BinaryReader.prototype = { + + constructor: BinaryReader, + + getOffset: function () { + + return this.offset; + + }, + + size: function () { + + return this.dv.buffer.byteLength; + + }, + + skip: function ( length ) { + + this.offset += length; + + }, + + // seems like true/false representation depends on exporter. + // true: 1 or 'Y'(=0x59), false: 0 or 'T'(=0x54) + // then sees LSB. + getBoolean: function () { + + return ( this.getUint8() & 1 ) === 1; + + }, + + getBooleanArray: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getBoolean() ); + + } + + return a; + + }, + + getUint8: function () { + + var value = this.dv.getUint8( this.offset ); + this.offset += 1; + return value; + + }, + + getInt16: function () { + + var value = this.dv.getInt16( this.offset, this.littleEndian ); + this.offset += 2; + return value; + + }, + + getInt32: function () { + + var value = this.dv.getInt32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + getInt32Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getInt32() ); + + } + + return a; + + }, + + getUint32: function () { + + var value = this.dv.getUint32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + // JavaScript doesn't support 64-bit integer so calculate this here + // 1 << 32 will return 1 so using multiply operation instead here. + // There's a possibility that this method returns wrong value if the value + // is out of the range between Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER. + // TODO: safely handle 64-bit integer + getInt64: function () { + + var low, high; + + if ( this.littleEndian ) { + + low = this.getUint32(); + high = this.getUint32(); + + } else { + + high = this.getUint32(); + low = this.getUint32(); + + } + + // calculate negative value + if ( high & 0x80000000 ) { + + high = ~ high & 0xFFFFFFFF; + low = ~ low & 0xFFFFFFFF; + + if ( low === 0xFFFFFFFF ) high = ( high + 1 ) & 0xFFFFFFFF; + + low = ( low + 1 ) & 0xFFFFFFFF; + + return - ( high * 0x100000000 + low ); + + } + + return high * 0x100000000 + low; + + }, + + getInt64Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getInt64() ); + + } + + return a; + + }, + + // Note: see getInt64() comment + getUint64: function () { + + var low, high; + + if ( this.littleEndian ) { + + low = this.getUint32(); + high = this.getUint32(); + + } else { + + high = this.getUint32(); + low = this.getUint32(); + + } + + return high * 0x100000000 + low; + + }, + + getFloat32: function () { + + var value = this.dv.getFloat32( this.offset, this.littleEndian ); + this.offset += 4; + return value; + + }, + + getFloat32Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getFloat32() ); + + } + + return a; + + }, + + getFloat64: function () { + + var value = this.dv.getFloat64( this.offset, this.littleEndian ); + this.offset += 8; + return value; + + }, + + getFloat64Array: function ( size ) { + + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a.push( this.getFloat64() ); + + } + + return a; + + }, + + getArrayBuffer: function ( size ) { + + var value = this.dv.buffer.slice( this.offset, this.offset + size ); + this.offset += size; + return value; + + }, + + getString: function ( size ) { + + // note: safari 9 doesn't support Uint8Array.indexOf; create intermediate array instead + var a = []; + + for ( var i = 0; i < size; i ++ ) { + + a[ i ] = this.getUint8(); + + } + + var nullByte = a.indexOf( 0 ); + if ( nullByte >= 0 ) a = a.slice( 0, nullByte ); + + return THREE.LoaderUtils.decodeText( new Uint8Array( a ) ); + + } + + }; + + // FBXTree holds a representation of the FBX data, returned by the TextParser ( FBX ASCII format) + // and BinaryParser( FBX Binary format) + function FBXTree() {} + + FBXTree.prototype = { + + constructor: FBXTree, + + add: function ( key, val ) { + + this[ key ] = val; + + }, + + }; + + // ************** UTILITY FUNCTIONS ************** + + function isFbxFormatBinary( buffer ) { + + var CORRECT = 'Kaydara FBX Binary \0'; + + return buffer.byteLength >= CORRECT.length && CORRECT === convertArrayBufferToString( buffer, 0, CORRECT.length ); + + } + + function isFbxFormatASCII( text ) { + + var CORRECT = [ 'K', 'a', 'y', 'd', 'a', 'r', 'a', '\\', 'F', 'B', 'X', '\\', 'B', 'i', 'n', 'a', 'r', 'y', '\\', '\\' ]; + + var cursor = 0; + + function read( offset ) { + + var result = text[ offset - 1 ]; + text = text.slice( cursor + offset ); + cursor ++; + return result; + + } + + for ( var i = 0; i < CORRECT.length; ++ i ) { + + var num = read( 1 ); + if ( num === CORRECT[ i ] ) { + + return false; + + } + + } + + return true; + + } + + function getFbxVersion( text ) { + + var versionRegExp = /FBXVersion: (\d+)/; + var match = text.match( versionRegExp ); + if ( match ) { + + var version = parseInt( match[ 1 ] ); + return version; + + } + throw new Error( 'THREE.FBXLoader: Cannot find the version number for the file given.' ); + + } + + // Converts FBX ticks into real time seconds. + function convertFBXTimeToSeconds( time ) { + + return time / 46186158000; + + } + + var dataArray = []; + + // extracts the data from the correct position in the FBX array based on indexing type + function getData( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) { + + var index; + + switch ( infoObject.mappingType ) { + + case 'ByPolygonVertex' : + index = polygonVertexIndex; + break; + case 'ByPolygon' : + index = polygonIndex; + break; + case 'ByVertice' : + index = vertexIndex; + break; + case 'AllSame' : + index = infoObject.indices[ 0 ]; + break; + default : + console.warn( 'THREE.FBXLoader: unknown attribute mapping type ' + infoObject.mappingType ); + + } + + if ( infoObject.referenceType === 'IndexToDirect' ) index = infoObject.indices[ index ]; + + var from = index * infoObject.dataSize; + var to = from + infoObject.dataSize; + + return slice( dataArray, infoObject.buffer, from, to ); + + } + + var tempEuler = new THREE.Euler(); + var tempVec = new THREE.Vector3(); + + // generate transformation from FBX transform data + // ref: https://help.autodesk.com/view/FBX/2017/ENU/?guid=__files_GUID_10CDD63C_79C1_4F2D_BB28_AD2BE65A02ED_htm + // ref: http://docs.autodesk.com/FBX/2014/ENU/FBX-SDK-Documentation/index.html?url=cpp_ref/_transformations_2main_8cxx-example.html,topicNumber=cpp_ref__transformations_2main_8cxx_example_htmlfc10a1e1-b18d-4e72-9dc0-70d0f1959f5e + function generateTransform( transformData ) { + + var lTranslationM = new THREE.Matrix4(); + var lPreRotationM = new THREE.Matrix4(); + var lRotationM = new THREE.Matrix4(); + var lPostRotationM = new THREE.Matrix4(); + + var lScalingM = new THREE.Matrix4(); + var lScalingPivotM = new THREE.Matrix4(); + var lScalingOffsetM = new THREE.Matrix4(); + var lRotationOffsetM = new THREE.Matrix4(); + var lRotationPivotM = new THREE.Matrix4(); + + var lParentGX = new THREE.Matrix4(); + var lGlobalT = new THREE.Matrix4(); + + var inheritType = ( transformData.inheritType ) ? transformData.inheritType : 0; + + if ( transformData.translation ) lTranslationM.setPosition( tempVec.fromArray( transformData.translation ) ); + + if ( transformData.preRotation ) { + + var array = transformData.preRotation.map( THREE.Math.degToRad ); + array.push( transformData.eulerOrder ); + lPreRotationM.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + } + + if ( transformData.rotation ) { + + var array = transformData.rotation.map( THREE.Math.degToRad ); + array.push( transformData.eulerOrder ); + lRotationM.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + } + + if ( transformData.postRotation ) { + + var array = transformData.postRotation.map( THREE.Math.degToRad ); + array.push( transformData.eulerOrder ); + lPostRotationM.makeRotationFromEuler( tempEuler.fromArray( array ) ); + + } + + if ( transformData.scale ) lScalingM.scale( tempVec.fromArray( transformData.scale ) ); + + // Pivots and offsets + if ( transformData.scalingOffset ) lScalingOffsetM.setPosition( tempVec.fromArray( transformData.scalingOffset ) ); + if ( transformData.scalingPivot ) lScalingPivotM.setPosition( tempVec.fromArray( transformData.scalingPivot ) ); + if ( transformData.rotationOffset ) lRotationOffsetM.setPosition( tempVec.fromArray( transformData.rotationOffset ) ); + if ( transformData.rotationPivot ) lRotationPivotM.setPosition( tempVec.fromArray( transformData.rotationPivot ) ); + + // parent transform + if ( transformData.parentMatrixWorld ) lParentGX = transformData.parentMatrixWorld; + + // Global Rotation + var lLRM = lPreRotationM.multiply( lRotationM ).multiply( lPostRotationM ); + var lParentGRM = new THREE.Matrix4(); + lParentGX.extractRotation( lParentGRM ); + + // Global Shear*Scaling + var lParentTM = new THREE.Matrix4(); + var lLSM; + var lParentGSM; + var lParentGRSM; + + lParentTM.copyPosition( lParentGX ); + lParentGRSM = lParentTM.getInverse( lParentTM ).multiply( lParentGX ); + lParentGSM = lParentGRM.getInverse( lParentGRM ).multiply( lParentGRSM ); + lLSM = lScalingM; + + var lGlobalRS; + if ( inheritType === 0 ) { + + lGlobalRS = lParentGRM.multiply( lLRM ).multiply( lParentGSM ).multiply( lLSM ); + + } else if ( inheritType === 1 ) { + + lGlobalRS = lParentGRM.multiply( lParentGSM ).multiply( lLRM ).multiply( lLSM ); + + } else { + + var lParentLSM = new THREE.Matrix4().copy( lScalingM ); + + var lParentGSM_noLocal = lParentGSM.multiply( lParentLSM.getInverse( lParentLSM ) ); + + lGlobalRS = lParentGRM.multiply( lLRM ).multiply( lParentGSM_noLocal ).multiply( lLSM ); + + } + + // Calculate the local transform matrix + var lTransform = lTranslationM.multiply( lRotationOffsetM ).multiply( lRotationPivotM ).multiply( lPreRotationM ).multiply( lRotationM ).multiply( lPostRotationM ).multiply( lRotationPivotM.getInverse( lRotationPivotM ) ).multiply( lScalingOffsetM ).multiply( lScalingPivotM ).multiply( lScalingM ).multiply( lScalingPivotM.getInverse( lScalingPivotM ) ); + + var lLocalTWithAllPivotAndOffsetInfo = new THREE.Matrix4().copyPosition( lTransform ); + + var lGlobalTranslation = lParentGX.multiply( lLocalTWithAllPivotAndOffsetInfo ); + lGlobalT.copyPosition( lGlobalTranslation ); + + lTransform = lGlobalT.multiply( lGlobalRS ); + + return lTransform; + + } + + // Returns the three.js intrinsic Euler order corresponding to FBX extrinsic Euler order + // ref: http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_euler_html + function getEulerOrder( order ) { + + order = order || 0; + + var enums = [ + 'ZYX', // -> XYZ extrinsic + 'YZX', // -> XZY extrinsic + 'XZY', // -> YZX extrinsic + 'ZXY', // -> YXZ extrinsic + 'YXZ', // -> ZXY extrinsic + 'XYZ', // -> ZYX extrinsic + //'SphericXYZ', // not possible to support + ]; + + if ( order === 6 ) { + + console.warn( 'THREE.FBXLoader: unsupported Euler Order: Spherical XYZ. Animations and rotations may be incorrect.' ); + return enums[ 0 ]; + + } + + return enums[ order ]; + + } + + // Parses comma separated list of numbers and returns them an array. + // Used internally by the TextParser + function parseNumberArray( value ) { + + var array = value.split( ',' ).map( function ( val ) { + + return parseFloat( val ); + + } ); + + return array; + + } + + function convertArrayBufferToString( buffer, from, to ) { + + if ( from === undefined ) from = 0; + if ( to === undefined ) to = buffer.byteLength; + + return THREE.LoaderUtils.decodeText( new Uint8Array( buffer, from, to ) ); + + } + + function append( a, b ) { + + for ( var i = 0, j = a.length, l = b.length; i < l; i ++, j ++ ) { + + a[ j ] = b[ i ]; + + } + + } + + function slice( a, b, from, to ) { + + for ( var i = from, j = 0; i < to; i ++, j ++ ) { + + a[ j ] = b[ i ]; + + } + + return a; + + } + + // inject array a2 into array a1 at index + function inject( a1, index, a2 ) { + + return a1.slice( 0, index ).concat( a2 ).concat( a1.slice( index ) ); + + } + + return FBXLoader; + +} )(); diff --git a/src/jlmap3d/main/loaders/FBXLoader2.js b/src/jlmap3d/main/loaders/FBXLoader2.js new file mode 100644 index 000000000..fed5c2ed1 --- /dev/null +++ b/src/jlmap3d/main/loaders/FBXLoader2.js @@ -0,0 +1,3861 @@ +/** + * @author Kyle-Larson https://github.com/Kyle-Larson + * + * Loader loads FBX file and generates Group representing FBX scene. + * Requires FBX file to be >= 7.0 and in ASCII format. + * + * Supports: + * Mesh Generation (Positional Data) + * Normal Data (Per Vertex Drawing Instance) + * UV Data (Per Vertex Drawing Instance) + * Skinning + * Animation + * - Separated Animations based on stacks. + * - Skeletal & Non-Skeletal Animations + * + * Needs Support: + * Indexed Buffers + * PreRotation support. + */ + + +( function () { + + /** + * Generates a loader for loading FBX files from URL and parsing into + * a THREE.Group. + * @param {THREE.LoadingManager} manager - Loading Manager for loader to use. + */ + THREE.FBXLoader = function ( manager ) { + + THREE.Loader.call( this ); + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + this.fileLoader = new THREE.FileLoader( this.manager ); + this.textureLoader = new THREE.TextureLoader( this.manager ); + + }; + + Object.assign( THREE.FBXLoader.prototype, THREE.Loader.prototype ); + + THREE.FBXLoader.prototype.constructor = THREE.FBXLoader; + + Object.assign( THREE.FBXLoader.prototype, { + + /** + * Loads an ASCII FBX file from URL and parses into a THREE.Group. + * THREE.Group will have an animations property of AnimationClips + * of the different animations exported with the FBX. + * @param {string} url - URL of the FBX file. + * @param {function(THREE.Group):void} onLoad - Callback for when FBX file is loaded and parsed. + * @param {function(ProgressEvent):void} onProgress - Callback fired periodically when file is being retrieved from server. + * @param {function(Event):void} onError - Callback fired when error occurs (Currently only with retrieving file, not with parsing errors). + */ + load: function ( url, onLoad, onProgress, onError ) { + + var self = this; + + var resourceDirectory = url.split( /[\\\/]/ ); + resourceDirectory.pop(); + resourceDirectory = resourceDirectory.join( '/' ); + + this.fileLoader.load( url, function ( text ) { + + if ( ! isFbxFormatASCII( text ) ) { + + console.error( 'FBXLoader: FBX Binary format not supported.' ); + self.manager.itemError( url ); + return; + + } + if ( getFbxVersion( text ) < 7000 ) { + + console.error( 'FBXLoader: FBX version not supported for file at ' + url + ', FileVersion: ' + getFbxVersion( text ) ); + self.manager.itemError( url ); + return; + + } + + var scene = self.parse( text, resourceDirectory ); + onLoad( scene ); + + }, onProgress, onError ); + + }, + + /** + * Parses an ASCII FBX file and returns a THREE.Group. + * THREE.Group will have an animations property of AnimationClips + * of the different animations within the FBX file. + * @param {string} FBXText - Contents of FBX file to parse. + * @param {string} resourceDirectory - Directory to load external assets (e.g. textures ) from. + * @returns {THREE.Group} + */ + parse: function ( FBXText, resourceDirectory ) { + + var loader = this; + + var FBXTree = new TextParser().parse( FBXText ); + + var connections = parseConnections( FBXTree ); + + var textures = parseTextures( FBXTree ); + + var materials = parseMaterials( FBXTree, textures, connections ); + + var deformerMap = parseDeformers( FBXTree, connections ); + + var geometryMap = parseGeometries( FBXTree, connections, deformerMap ); + + var sceneGraph = parseScene( FBXTree, connections, deformerMap, geometryMap, materials ); + + return sceneGraph; + + + /** + * @typedef {{value: number}} FBXValue + */ + /** + * @typedef {{value: {x: string, y: string, z: string}}} FBXVector3 + */ + /** + * @typedef {{properties: {a: string}}} FBXArrayNode + */ + /** + * @typedef {{properties: {MappingInformationType: string, ReferenceInformationType: string }, subNodes: Object}} FBXMappedArrayNode + */ + /** + * @typedef {{id: number, name: string, properties: {FileName: string}}} FBXTextureNode + */ + /** + * @typedef {{id: number, attrName: string, properties: {ShadingModel: string, Diffuse: FBXVector3, Specular: FBXVector3, Shininess: FBXValue, Emissive: FBXVector3, EmissiveFactor: FBXValue, Opacity: FBXValue}}} FBXMaterialNode + */ + /** + * @typedef {{subNodes: {Indexes: FBXArrayNode, Weights: FBXArrayNode, Transform: FBXArrayNode, TransformLink: FBXArrayNode}, properties: { Mode: string }}} FBXSubDeformerNode + */ + /** + * @typedef {{id: number, attrName: string, attrType: string, subNodes: {Vertices: FBXArrayNode, PolygonVertexIndex: FBXArrayNode, LayerElementNormal: FBXMappedArrayNode[], LayerElementMaterial: FBXMappedArrayNode[], LayerElementUV: FBXMappedArrayNode[]}}} FBXGeometryNode + */ + /** + * @typedef {{id: number, attrName: string, attrType: string, properties: {Lcl_Translation: FBXValue, Lcl_Rotation: FBXValue, Lcl_Scaling: FBXValue}}} FBXModelNode + */ + + + + + + + + + /** + * Parses map of relationships between objects. + * @param {{Connections: { properties: { connections: [number, number, string][]}}}} FBXTree + * @returns {Map} + */ + function parseConnections( FBXTree ) { + + /** + * @type {Map} + */ + var connectionMap = new Map(); + + if ( 'Connections' in FBXTree ) { + + /** + * @type {[number, number, string][]} + */ + var connectionArray = FBXTree.Connections.properties.connections; + connectionArray.forEach( function ( connection ) { + + if ( ! connectionMap.has( connection[ 0 ] ) ) { + + connectionMap.set( connection[ 0 ], { + parents: [], + children: [] + } ); + + } + + var parentRelationship = { ID: connection[ 1 ], relationship: connection[ 2 ] }; + connectionMap.get( connection[ 0 ] ).parents.push( parentRelationship ); + + if ( ! connectionMap.has( connection[ 1 ] ) ) { + + connectionMap.set( connection[ 1 ], { + parents: [], + children: [] + } ); + + } + + var childRelationship = { ID: connection[ 0 ], relationship: connection[ 2 ] }; + connectionMap.get( connection[ 1 ] ).children.push( childRelationship ); + + } ); + + } + + return connectionMap; + + } + + /** + * Parses map of textures referenced in FBXTree. + * @param {{Objects: {subNodes: {Texture: Object.}}}} FBXTree + * @returns {Map} + */ + function parseTextures( FBXTree ) { + + /** + * @type {Map} + */ + var textureMap = new Map(); + + if ( 'Texture' in FBXTree.Objects.subNodes ) { + + var textureNodes = FBXTree.Objects.subNodes.Texture; + for ( var nodeID in textureNodes ) { + + var texture = parseTexture( textureNodes[ nodeID ] ); + textureMap.set( parseInt( nodeID ), texture ); + + } + + } + + return textureMap; + + /** + * @param {textureNode} textureNode - Node to get texture information from. + * @returns {THREE.Texture} + */ + function parseTexture( textureNode ) { + + var FBX_ID = textureNode.id; + var name = textureNode.name; + var filePath = textureNode.properties.FileName; + var split = filePath.split( /[\\\/]/ ); + if ( split.length > 0 ) { + + var fileName = split[ split.length - 1 ]; + + } else { + + var fileName = filePath; + + } + /** + * @type {THREE.Texture} + */ + var texture = loader.textureLoader.load( resourceDirectory + '/' + fileName ); + texture.name = name; + texture.FBX_ID = FBX_ID; + + return texture; + + } + + } + + /** + * Parses map of Material information. + * @param {{Objects: {subNodes: {Material: Object.}}}} FBXTree + * @param {Map} textureMap + * @param {Map} connections + * @returns {Map} + */ + function parseMaterials( FBXTree, textureMap, connections ) { + + var materialMap = new Map(); + + if ( 'Material' in FBXTree.Objects.subNodes ) { + + var materialNodes = FBXTree.Objects.subNodes.Material; + for ( var nodeID in materialNodes ) { + + var material = parseMaterial( materialNodes[ nodeID ], textureMap, connections ); + materialMap.set( parseInt( nodeID ), material ); + + } + + } + + return materialMap; + + /** + * Takes information from Material node and returns a generated THREE.Material + * @param {FBXMaterialNode} materialNode + * @param {Map} textureMap + * @param {Map} connections + * @returns {THREE.Material} + */ + function parseMaterial( materialNode, textureMap, connections ) { + + var FBX_ID = materialNode.id; + var name = materialNode.attrName; + var type = materialNode.properties.ShadingModel; + + var children = connections.get( FBX_ID ).children; + + var parameters = parseParameters( materialNode.properties, textureMap, children ); + + var material; + switch ( type ) { + + case 'phong': + material = new THREE.MeshPhongMaterial(); + break; + case 'lambert': + material = new THREE.MeshLambertMaterial(); + break; + default: + console.warn( 'No implementation given for material type ' + type + ' in FBXLoader.js. Defaulting to basic material' ); + material = new THREE.MeshBasicMaterial( { color: 0x3300ff } ); + break; + + } + + material.setValues( parameters ); + material.name = name; + + return material; + + /** + * @typedef {{Diffuse: FBXVector3, Specular: FBXVector3, Shininess: FBXValue, Emissive: FBXVector3, EmissiveFactor: FBXValue, Opacity: FBXValue}} FBXMaterialProperties + */ + /** + * @typedef {{color: THREE.Color=, specular: THREE.Color=, shininess: number=, emissive: THREE.Color=, emissiveIntensity: number=, opacity: number=, transparent: boolean=, map: THREE.Texture=}} THREEMaterialParameterPack + */ + /** + * @param {FBXMaterialProperties} properties + * @param {Map} textureMap + * @param {{ID: number, relationship: string}[]} childrenRelationships + * @returns {THREEMaterialParameterPack} + */ + function parseParameters( properties, textureMap, childrenRelationships ) { + + var parameters = {}; + + if ( properties.Diffuse ) { + + parameters.color = parseColor( properties.Diffuse ); + + } + if ( properties.Specular ) { + + parameters.specular = parseColor( properties.Specular ); + + } + if ( properties.Shininess ) { + + parameters.shininess = properties.Shininess.value; + + } + if ( properties.Emissive ) { + + parameters.emissive = parseColor( properties.Emissive ); + + } + if ( properties.EmissiveFactor ) { + + parameters.emissiveIntensity = properties.EmissiveFactor.value; + + } + if ( properties.Opacity ) { + + parameters.opacity = properties.Opacity.value; + + } + if ( parameters.opacity < 1.0 ) { + + parameters.transparent = true; + + } + + childrenRelationships.forEach( function ( relationship ) { + + var type = relationship.relationship; + switch ( type ) { + + case " \"AmbientColor": + //TODO: Support AmbientColor textures + break; + + case " \"DiffuseColor": + parameters.map = textureMap.get( relationship.ID ); + break; + + default: + console.warn( 'Unknown texture application of type ' + type + ', skipping texture' ); + break; + + } + + } ); + + return parameters; + + } + + } + + } + + /** + * Generates map of Skeleton-like objects for use later when generating and binding skeletons. + * @param {{Objects: {subNodes: {Deformer: Object.}}}} FBXTree + * @param {Map} connections + * @returns {Map, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[], skeleton: THREE.Skeleton|null}>} + */ + function parseDeformers( FBXTree, connections ) { + + var skeletonMap = new Map(); + + if ( 'Deformer' in FBXTree.Objects.subNodes ) { + + var DeformerNodes = FBXTree.Objects.subNodes.Deformer; + for ( var nodeID in DeformerNodes ) { + + var deformerNode = DeformerNodes[ nodeID ]; + if ( deformerNode.attrType === 'Skin' ) { + + var conns = connections.get( parseInt( nodeID ) ); + var skeleton = parseSkeleton( conns, DeformerNodes ); + skeleton.FBX_ID = parseInt( nodeID ); + skeletonMap.set( parseInt( nodeID ), skeleton ); + + } + + } + + } + + return skeletonMap; + + /** + * Generates a "Skeleton Representation" of FBX nodes based on an FBX Skin Deformer's connections and an object containing SubDeformer nodes. + * @param {{parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}} connections + * @param {Object.} DeformerNodes + * @returns {{map: Map, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[], skeleton: THREE.Skeleton|null}} + */ + function parseSkeleton( connections, DeformerNodes ) { + + var subDeformers = new Map(); + var subDeformerArray = []; + connections.children.forEach( function ( child ) { + + var subDeformerNode = DeformerNodes[ child.ID ]; + var subDeformer = { + FBX_ID: child.ID, + indices: parseIntArray( subDeformerNode.subNodes.Indexes.properties.a ), + weights: parseFloatArray( subDeformerNode.subNodes.Weights.properties.a ), + transform: parseMatrixArray( subDeformerNode.subNodes.Transform.properties.a ), + transformLink: parseMatrixArray( subDeformerNode.subNodes.TransformLink.properties.a ), + linkMode: subDeformerNode.properties.Mode + }; + subDeformers.set( child.ID, subDeformer ); + subDeformerArray.push( subDeformer ); + + } ); + + return { + map: subDeformers, + array: subDeformerArray, + bones: [] + }; + + } + + } + + /** + * Generates Buffer geometries from geometry information in FBXTree, and generates map of THREE.BufferGeometries + * @param {{Objects: {subNodes: {Geometry: Object.} connections + * @param {Map, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[], skeleton: THREE.Skeleton|null}>} deformerMap + * @returns {Map} + */ + function parseGeometries( FBXTree, connections, deformerMap ) { + + var geometryMap = new Map(); + + if ( 'Geometry' in FBXTree.Objects.subNodes ) { + + var geometryNodes = FBXTree.Objects.subNodes.Geometry; + for ( var nodeID in geometryNodes ) { + + var relationships = connections.get( parseInt( nodeID ) ); + var geo = parseGeometry( geometryNodes[ nodeID ], relationships, deformerMap ); + geometryMap.set( parseInt( nodeID ), geo ); + + } + + } + + return geometryMap; + + /** + * Generates BufferGeometry from FBXGeometryNode. + * @param {FBXGeometryNode} geometryNode + * @param {{parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}} relationships + * @param {Map, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[]}>} deformerMap + * @returns {THREE.BufferGeometry} + */ + function parseGeometry( geometryNode, relationships, deformerMap ) { + + switch ( geometryNode.attrType ) { + + case 'Mesh': + return parseMeshGeometry( geometryNode, relationships, deformerMap ); + break; + + case 'NurbsCurve': + return parseNurbsGeometry( geometryNode, relationships, deformerMap ); + break; + + } + + /** + * Specialty function for parsing Mesh based Geometry Nodes. + * @param {FBXGeometryNode} geometryNode + * @param {{parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}} relationships - Object representing relationships between specific geometry node and other nodes. + * @param {Map, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[]}>} deformerMap - Map object of deformers and subDeformers by ID. + * @returns {THREE.BufferGeometry} + */ + function parseMeshGeometry( geometryNode, relationships, deformerMap ) { + + var FBX_ID = geometryNode.id; + var name = geometryNode.attrName; + for ( var i = 0; i < relationships.children.length; ++ i ) { + + if ( deformerMap.has( relationships.children[ i ].ID ) ) { + + var deformer = deformerMap.get( relationships.children[ i ].ID ); + break; + + } + + } + + var geometry = genGeometry( geometryNode, deformer ); + + return geometry; + + /** + * @param {{map: Map, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[]}} deformer - Skeleton representation for geometry instance. + * @returns {THREE.BufferGeometry} + */ + function genGeometry( geometryNode, deformer ) { + + var geometry = new Geometry(); + + //First, each index is going to be its own vertex. + var vertexBuffer = parseFloatArray( geometryNode.subNodes.Vertices.properties.a ); + var indexBuffer = parseIntArray( geometryNode.subNodes.PolygonVertexIndex.properties.a ); + + if ( 'LayerElementNormal' in geometryNode.subNodes ) { + + var normalInfo = getNormals( geometryNode ); + + } + + if ( 'LayerElementUV' in geometryNode.subNodes ) { + + var uvInfo = getUVs( geometryNode ); + + } + + if ( 'LayerElementMaterial' in geometryNode.subNodes ) { + + var materialInfo = getMaterials( geometryNode ); + + } + + var faceVertexBuffer = []; + var polygonIndex = 0; + for ( var polygonVertexIndex = 0; polygonVertexIndex < indexBuffer.length; ++ polygonVertexIndex ) { + + var endOfFace; + var vertexIndex = indexBuffer[ polygonVertexIndex ]; + if ( indexBuffer[ polygonVertexIndex ] < 0 ) { + + vertexIndex = vertexIndex ^ - 1; + indexBuffer[ polygonVertexIndex ] = vertexIndex; + endOfFace = true; + + } + var vertex = new Vertex(); + var weightIndices = []; + var weights = []; + vertex.position.fromArray( vertexBuffer, vertexIndex * 3 ); + + // If we have a deformer for this geometry, get the skinIndex and skinWeights for this object. + // They are stored as vertex indices on each deformer, and we need them as deformer indices + // for each vertex. + if ( deformer ) { + + for ( var j = 0; j < deformer.array.length; ++ j ) { + + var index = deformer.array[ j ].indices.findIndex( function ( index ) { + + return index === indexBuffer[ polygonVertexIndex ]; + + } ); + + if ( index !== - 1 ) { + + weights.push( deformer.array[ j ].weights[ index ] ); + weightIndices.push( j ); + + } + + } + + if ( weights.length > 4 ) { + + console.warn( 'FBXLoader: Vertex has more than 4 skinning weights assigned to vertex. Deleting additional weights.' ); + + var WIndex = [ 0, 0, 0, 0 ]; + var Weight = [ 0, 0, 0, 0 ]; + + for ( var polygonVertexIndex = 0; polygonVertexIndex < weights.length; ++ polygonVertexIndex ) { + + var currentWeight = weights[ polygonVertexIndex ]; + var currentIndex = weightIndices[ polygonVertexIndex ]; + for ( var j = 0; j < Weight.length; ++ j ) { + + if ( currentWeight > Weight[ j ] ) { + + var tmp = Weight[ j ]; + Weight[ j ] = currentWeight; + currentWeight = tmp; + + tmp = WIndex[ j ]; + WIndex[ j ] = currentIndex; + currentIndex = tmp; + + } + + } + + } + + weightIndices = WIndex; + weights = Weight; + + } + + for ( var i = weights.length; i < 4; i ++ ) { + + weights[ i ] = 0; + weightIndices[ i ] = 0; + + } + + vertex.skinWeights.fromArray( weights ); + vertex.skinIndices.fromArray( weightIndices ); + + //vertex.skinWeights.normalize(); + + } + + if ( normalInfo ) { + + vertex.normal.fromArray( getData( polygonVertexIndex, polygonIndex, vertexIndex, normalInfo ) ); + + } + + if ( uvInfo ) { + + vertex.uv.fromArray( getData( polygonVertexIndex, polygonIndex, vertexIndex, uvInfo ) ); + + } + + + + //Add vertex to face buffer. + faceVertexBuffer.push( vertex ); + + // If index was negative to start with, we have finished this individual face + // and can generate the face data to the geometry. + if ( endOfFace ) { + + var face = new Face(); + var materials = getData( polygonVertexIndex, polygonIndex, vertexIndex, materialInfo ); + face.genTrianglesFromVertices( faceVertexBuffer ); + face.materialIndex = materials[ 0 ]; + geometry.faces.push( face ); + faceVertexBuffer = []; + polygonIndex ++; + endOfFace = false; + + } + + } + + /** + * @type {{vertexBuffer: number[], normalBuffer: number[], uvBuffer: number[], skinIndexBuffer: number[], skinWeightBuffer: number[], materialIndexBuffer: number[]}} + */ + var bufferInfo = geometry.flattenToBuffers(); + + var geo = new THREE.BufferGeometry(); + geo.name = geometryNode.name; + geo.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( bufferInfo.vertexBuffer ), 3 ) ); + + if ( bufferInfo.normalBuffer.length > 0 ) { + + geo.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( bufferInfo.normalBuffer ), 3 ) ); + + } + if ( bufferInfo.uvBuffer.length > 0 ) { + + geo.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( bufferInfo.uvBuffer ), 2 ) ); + + } + + if ( deformer ) { + + geo.addAttribute( 'skinIndex', new THREE.BufferAttribute( new Float32Array( bufferInfo.skinIndexBuffer ), 4 ) ); + + geo.addAttribute( 'skinWeight', new THREE.BufferAttribute( new Float32Array( bufferInfo.skinWeightBuffer ), 4 ) ); + + geo.FBX_Deformer = deformer; + + } + + // Convert the material indices of each vertex into rendering groups on the geometry. + var prevMaterialIndex = bufferInfo.materialIndexBuffer[ 0 ]; + var startIndex = 0; + for ( var materialBufferIndex = 0; materialBufferIndex < bufferInfo.materialIndexBuffer.length; ++ materialBufferIndex ) { + + if ( bufferInfo.materialIndexBuffer[ materialBufferIndex ] !== prevMaterialIndex ) { + + geo.addGroup( startIndex, materialBufferIndex - startIndex, prevMaterialIndex ); + startIndex = materialBufferIndex; + prevMaterialIndex = bufferInfo.materialIndexBuffer[ materialBufferIndex ]; + + } + + } + + return geo; + + /** + * Parses normal information for geometry. + * @param {FBXGeometryNode} geometryNode + * @returns {{dataSize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} + */ + function getNormals( geometryNode ) { + + var NormalNode = geometryNode.subNodes.LayerElementNormal[ 0 ]; + + var mappingType = NormalNode.properties.MappingInformationType; + var referenceType = NormalNode.properties.ReferenceInformationType; + var buffer = parseFloatArray( NormalNode.subNodes.Normals.properties.a ); + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + indexBuffer = parseIntArray( NormalNode.subNodes.NormalIndex.properties.a ); + + } + + return { + dataSize: 3, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + } + + /** + * Parses UV information for geometry. + * @param {FBXGeometryNode} geometryNode + * @returns {{dataSize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} + */ + function getUVs( geometryNode ) { + + var UVNode = geometryNode.subNodes.LayerElementUV[ 0 ]; + + var mappingType = UVNode.properties.MappingInformationType; + var referenceType = UVNode.properties.ReferenceInformationType; + var buffer = parseFloatArray( UVNode.subNodes.UV.properties.a ); + var indexBuffer = []; + if ( referenceType === 'IndexToDirect' ) { + + indexBuffer = parseIntArray( UVNode.subNodes.UVIndex.properties.a ); + + } + + return { + dataSize: 2, + buffer: buffer, + indices: indexBuffer, + mappingType: mappingType, + referenceType: referenceType + }; + + } + + /** + * Parses material application information for geometry. + * @param {FBXGeometryNode} + * @returns {{dataSize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} + */ + function getMaterials( geometryNode ) { + + var MaterialNode = geometryNode.subNodes.LayerElementMaterial[ 0 ]; + var mappingType = MaterialNode.properties.MappingInformationType; + var referenceType = MaterialNode.properties.ReferenceInformationType; + var materialIndexBuffer = parseIntArray( MaterialNode.subNodes.Materials.properties.a ); + + // Since materials are stored as indices, there's a bit of a mismatch between FBX and what + // we expect. So we create an intermediate buffer that points to the index in the buffer, + // for conforming with the other functions we've written for other data. + var materialIndices = []; + materialIndexBuffer.forEach( function ( materialIndex, index ) { + + materialIndices.push( index ); + + } ); + + return { + dataSize: 1, + buffer: materialIndexBuffer, + indices: materialIndices, + mappingType: mappingType, + referenceType: referenceType + }; + + } + + /** + * Function uses the infoObject and given indices to return value array of object. + * @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex). + * @param {number} polygonIndex - Index of polygon in geometry. + * @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore). + * @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data. + * @returns {number[]} + */ + function getData( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) { + + var GetData = { + + ByPolygonVertex: { + + /** + * Function uses the infoObject and given indices to return value array of object. + * @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex). + * @param {number} polygonIndex - Index of polygon in geometry. + * @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore). + * @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data. + * @returns {number[]} + */ + Direct: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) { + + return infoObject.buffer.slice( ( polygonVertexIndex * infoObject.dataSize ), ( polygonVertexIndex * infoObject.dataSize ) + infoObject.dataSize ); + + }, + + /** + * Function uses the infoObject and given indices to return value array of object. + * @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex). + * @param {number} polygonIndex - Index of polygon in geometry. + * @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore). + * @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data. + * @returns {number[]} + */ + IndexToDirect: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) { + + var index = infoObject.indices[ polygonVertexIndex ]; + return infoObject.buffer.slice( ( index * infoObject.dataSize ), ( index * infoObject.dataSize ) + infoObject.dataSize ); + + } + + }, + + ByPolygon: { + + /** + * Function uses the infoObject and given indices to return value array of object. + * @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex). + * @param {number} polygonIndex - Index of polygon in geometry. + * @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore). + * @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data. + * @returns {number[]} + */ + Direct: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) { + + return infoObject.buffer.slice( polygonIndex * infoObject.dataSize, polygonIndex * infoObject.dataSize + infoObject.dataSize ); + + }, + + /** + * Function uses the infoObject and given indices to return value array of object. + * @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex). + * @param {number} polygonIndex - Index of polygon in geometry. + * @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore). + * @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data. + * @returns {number[]} + */ + IndexToDirect: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) { + + var index = infoObject.indices[ polygonIndex ]; + return infoObject.buffer.slice( index * infoObject.dataSize, index * infoObject.dataSize + infoObject.dataSize ); + + } + + }, + + AllSame: { + + /** + * Function uses the infoObject and given indices to return value array of object. + * @param {number} polygonVertexIndex - Index of vertex in draw order (which index of the index buffer refers to this vertex). + * @param {number} polygonIndex - Index of polygon in geometry. + * @param {number} vertexIndex - Index of vertex inside vertex buffer (used because some data refers to old index buffer that we don't use anymore). + * @param {{datasize: number, buffer: number[], indices: number[], mappingType: string, referenceType: string}} infoObject - Object containing data and how to access data. + * @returns {number[]} + */ + IndexToDirect: function ( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) { + + return infoObject.buffer.slice( infoObject.indices[ 0 ] * infoObject.dataSize, infoObject.indices[ 0 ] * infoObject.dataSize + infoObject.dataSize ); + + } + + } + + }; + + return GetData[ infoObject.mappingType ][ infoObject.referenceType ]( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ); + + } + + } + + } + + /** + * Specialty function for parsing NurbsCurve based Geometry Nodes. + * @param {FBXGeometryNode} geometryNode + * @param {{parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}} relationships + * @returns {THREE.BufferGeometry} + */ + function parseNurbsGeometry( geometryNode, relationships ) { + + if ( THREE.NURBSCurve === undefined ) { + + console.error( "THREE.FBXLoader relies on THREE.NURBSCurve for any nurbs present in the model. Nurbs will show up as empty geometry." ); + return new THREE.BufferGeometry(); + + } + + var order = parseInt( geometryNode.properties.Order ); + + if ( isNaN( order ) ) { + + console.error( "FBXLoader: Invalid Order " + geometryNode.properties.Order + " given for geometry ID: " + geometryNode.id ); + return new THREE.BufferGeometry(); + + } + + var knots = parseFloatArray( geometryNode.subNodes.KnotVector.properties.a ); + var controlPoints = []; + var pointsValues = parseFloatArray( geometryNode.subNodes.Points.properties.a ); + + for ( var i = 0; i < pointsValues.length; i += 4 ) { + + controlPoints.push( new THREE.Vector4( pointsValues[ i ], pointsValues[ i + 1 ], pointsValues[ i + 2 ], pointsValues[ i + 3 ] ) ); + + } + + if ( geometryNode.properties.Form === 'Closed' ) { + + controlPoints.push( controlPoints[ 0 ] ); + + } + + var curve = new THREE.NURBSCurve( order - 1, knots, controlPoints ); + var vertices = curve.getPoints( controlPoints.length * 1.5 ); + + var vertexBuffer = []; + vertices.forEach( function ( position ) { + + var array = position.toArray(); + vertexBuffer = vertexBuffer.concat( array ); + + } ); + + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( vertexBuffer ), 3 ) ); + + return geometry; + + } + + } + + } + + /** + * Finally generates Scene graph and Scene graph Objects. + * @param {{Objects: {subNodes: {Model: Object.}}}} FBXTree + * @param {Map} connections + * @param {Map, array: {FBX_ID: number, indices: number[], weights: number[], transform: number[], transformLink: number[], linkMode: string}[], skeleton: THREE.Skeleton|null}>} deformerMap + * @param {Map} geometryMap + * @param {Map} materialMap + * @returns {THREE.Group} + */ + function parseScene( FBXTree, connections, deformerMap, geometryMap, materialMap ) { + + var sceneGraph = new THREE.Group(); + + var ModelNode = FBXTree.Objects.subNodes.Model; + + /** + * @type {Array.} + */ + var modelArray = []; + + /** + * @type {Map.} + */ + var modelMap = new Map(); + + for ( var nodeID in ModelNode ) { + + var id = parseInt( nodeID ); + var node = ModelNode[ nodeID ]; + var conns = connections.get( id ); + var model = null; + for ( var i = 0; i < conns.parents.length; ++ i ) { + + deformerMap.forEach( function ( deformer ) { + + if ( deformer.map.has( conns.parents[ i ].ID ) ) { + + model = new THREE.Bone(); + var index = deformer.array.findIndex( function ( subDeformer ) { + + return subDeformer.FBX_ID === conns.parents[ i ].ID; + + } ); + deformer.bones[ index ] = model; + + } + + } ); + + } + if ( ! model ) { + + switch ( node.attrType ) { + + case "Mesh": + /** + * @type {?THREE.BufferGeometry} + */ + var geometry = null; + + /** + * @type {THREE.MultiMaterial|THREE.Material} + */ + var material = null; + + /** + * @type {Array.} + */ + var materials = []; + + conns.children.forEach( function ( child ) { + + if ( geometryMap.has( child.ID ) ) { + + geometry = geometryMap.get( child.ID ); + + } + + if ( materialMap.has( child.ID ) ) { + + materials.push( materialMap.get( child.ID ) ); + + } + + } ); + if ( materials.length > 1 ) { + + material = new THREE.MultiMaterial( materials ); + + } else if ( materials.length > 0 ) { + + material = materials[ 0 ]; + + } else { + + material = new THREE.MeshBasicMaterial( { color: 0x3300ff } ); + + } + if ( geometry.FBX_Deformer ) { + + materials.forEach( function ( material ) { + + material.skinning = true; + + } ); + material.skinning = true; + model = new THREE.SkinnedMesh( geometry, material ); + + } else { + + model = new THREE.Mesh( geometry, material ); + + } + break; + + case "NurbsCurve": + var geometry = null; + + conns.children.forEach( function ( child ) { + + if ( geometryMap.has( child.ID ) ) { + + geometry = geometryMap.get( child.ID ); + + } + + } ); + + // FBX does not list materials for Nurbs lines, so we'll just put our own in here. + material = new THREE.LineBasicMaterial( { color: 0x3300ff, linewidth: 5 } ); + model = new THREE.Line( geometry, material ); + break; + + default: + model = new THREE.Object3D(); + break; + + } + + } + + model.name = node.attrName.replace( /:/, '' ).replace( /_/, '' ).replace( /-/, '' ); + model.FBX_ID = id; + + modelArray.push( model ); + modelMap.set( id, model ); + + } + + modelArray.forEach( function ( model ) { + + var node = ModelNode[ model.FBX_ID ]; + + if ( 'Lcl_Translation' in node.properties ) { + + model.position.fromArray( parseFloatArray( node.properties.Lcl_Translation.value ) ); + + } + + if ( 'Lcl_Rotation' in node.properties ) { + + var rotation = parseFloatArray( node.properties.Lcl_Rotation.value ).map( function ( value ) { + + return value * Math.PI / 180; + + } ); + rotation.push( 'ZYX' ); + model.rotation.fromArray( rotation ); + + } + + if ( 'Lcl_Scaling' in node.properties ) { + + model.scale.fromArray( parseFloatArray( node.properties.Lcl_Scaling.value ) ); + + } + + var conns = connections.get( model.FBX_ID ); + for ( var parentIndex = 0; parentIndex < conns.parents.length; parentIndex ++ ) { + + var pIndex = modelArray.findIndex( function ( mod ) { + + return mod.FBX_ID === conns.parents[ parentIndex ].ID; + + } ); + if ( pIndex > - 1 ) { + + modelArray[ pIndex ].add( model ); + break; + + } + + } + if ( model.parent === null ) { + + sceneGraph.add( model ); + + } + + } ); + + + // Now with the bones created, we can update the skeletons and bind them to the skinned meshes. + sceneGraph.updateMatrixWorld( true ); + + // Put skeleton into bind pose. + var BindPoseNode = FBXTree.Objects.subNodes.Pose; + for ( var nodeID in BindPoseNode ) { + + if ( BindPoseNode[ nodeID ].attrType === 'BindPose' ) { + + BindPoseNode = BindPoseNode[ nodeID ]; + break; + + } + + } + if ( BindPoseNode ) { + + var PoseNode = BindPoseNode.subNodes.PoseNode; + var worldMatrices = new Map(); + + PoseNode.forEach( function ( node ) { + + var rawMatWrd = parseMatrixArray( node.subNodes.Matrix.properties.a ); + + worldMatrices.set( parseInt( node.id ), rawMatWrd ); + + } ); + + } + + deformerMap.forEach( function ( deformer, FBX_ID ) { + + deformer.array.forEach( function ( subDeformer, subDeformerIndex ) { + + /** + * @type {THREE.Bone} + */ + var bone = deformer.bones[ subDeformerIndex ]; + if ( ! worldMatrices.has( bone.FBX_ID ) ) { + + return; + + } + var mat = worldMatrices.get( bone.FBX_ID ); + bone.matrixWorld.copy( mat ); + + } ); + + // Now that skeleton is in bind pose, bind to model. + deformer.skeleton = new THREE.Skeleton( deformer.bones ); + var conns = connections.get( FBX_ID ); + conns.parents.forEach( function ( parent ) { + + if ( geometryMap.has( parent.ID ) ) { + + var geoID = parent.ID; + var geoConns = connections.get( geoID ); + for ( var i = 0; i < geoConns.parents.length; ++ i ) { + + if ( modelMap.has( geoConns.parents[ i ].ID ) ) { + + var model = modelMap.get( geoConns.parents[ i ].ID ); + //ASSERT model typeof SkinnedMesh + model.bind( deformer.skeleton, model.matrixWorld ); + break; + + } + + } + + } + + } ); + + } ); + + // Skeleton is now bound, we are now free to set up the + // scene graph. + modelArray.forEach( function ( model ) { + + var node = ModelNode[ model.FBX_ID ]; + + if ( 'Lcl_Translation' in node.properties ) { + + model.position.fromArray( parseFloatArray( node.properties.Lcl_Translation.value ) ); + + } + + if ( 'Lcl_Rotation' in node.properties ) { + + var rotation = parseFloatArray( node.properties.Lcl_Rotation.value ).map( function ( value ) { + + return value * Math.PI / 180; + + } ); + rotation.push( 'ZYX' ); + model.rotation.fromArray( rotation ); + + } + + if ( 'Lcl_Scaling' in node.properties ) { + + model.scale.fromArray( parseFloatArray( node.properties.Lcl_Scaling.value ) ); + + } + + } ); + + // Silly hack with the animation parsing. We're gonna pretend the scene graph has a skeleton + // to attach animations to, since FBXs treat animations as animations for the entire scene, + // not just for individual objects. + sceneGraph.skeleton = { + bones: modelArray + }; + + var animations = parseAnimations( FBXTree, connections, sceneGraph ); + + addAnimations( sceneGraph, animations ); + + return sceneGraph; + + } + + /** + * Parses animation information from FBXTree and generates an AnimationInfoObject. + * @param {{Objects: {subNodes: {AnimationCurveNode: any, AnimationCurve: any, AnimationLayer: any, AnimationStack: any}}}} FBXTree + * @param {Map} connections + */ + function parseAnimations( FBXTree, connections, sceneGraph ) { + + var rawNodes = FBXTree.Objects.subNodes.AnimationCurveNode; + var rawCurves = FBXTree.Objects.subNodes.AnimationCurve; + var rawLayers = FBXTree.Objects.subNodes.AnimationLayer; + var rawStacks = FBXTree.Objects.subNodes.AnimationStack; + + /** + * @type {{ + curves: Map, + layers: Map, + stacks: Map, + length: number, + fps: number, + frames: number + }} + */ + var returnObject = { + curves: new Map(), + layers: new Map(), + stacks: new Map(), + length: 0, + fps: 30, + frames: 0 + }; + + /** + * @type {Array.<{ + id: number; + attr: string; + internalID: number; + attrX: boolean; + attrY: boolean; + attrZ: boolean; + containerBoneID: number; + containerID: number; + }>} + */ + var animationCurveNodes = []; + for ( var nodeID in rawNodes ) { + + if ( nodeID.match( /\d+/ ) ) { + + var animationNode = parseAnimationNode( FBXTree, rawNodes[ nodeID ], connections, sceneGraph ); + animationCurveNodes.push( animationNode ); + + } + + } + + /** + * @type {Map.} + */ + var tmpMap = new Map(); + for ( var animationCurveNodeIndex = 0; animationCurveNodeIndex < animationCurveNodes.length; ++ animationCurveNodeIndex ) { + + if ( animationCurveNodes[ animationCurveNodeIndex ] === null ) { + + continue; + + } + tmpMap.set( animationCurveNodes[ animationCurveNodeIndex ].id, animationCurveNodes[ animationCurveNodeIndex ] ); + + } + + + /** + * @type {{ + version: any, + id: number, + internalID: number, + times: number[], + values: number[], + attrFlag: number[], + attrData: number[], + }[]} + */ + var animationCurves = []; + for ( nodeID in rawCurves ) { + + if ( nodeID.match( /\d+/ ) ) { + + var animationCurve = parseAnimationCurve( rawCurves[ nodeID ] ); + animationCurves.push( animationCurve ); + + var firstParentConn = connections.get( animationCurve.id ).parents[ 0 ]; + var firstParentID = firstParentConn.ID; + var firstParentRelationship = firstParentConn.relationship; + var axis = ''; + + if ( firstParentRelationship.match( /X/ ) ) { + + axis = 'x'; + + } else if ( firstParentRelationship.match( /Y/ ) ) { + + axis = 'y'; + + } else if ( firstParentRelationship.match( /Z/ ) ) { + + axis = 'z'; + + } else { + + continue; + + } + + tmpMap.get( firstParentID ).curves[ axis ] = animationCurve; + + } + + } + + tmpMap.forEach( function ( curveNode ) { + + var id = curveNode.containerBoneID; + if ( ! returnObject.curves.has( id ) ) { + + returnObject.curves.set( id, { T: null, R: null, S: null } ); + + } + returnObject.curves.get( id )[ curveNode.attr ] = curveNode; + + } ); + + for ( var nodeID in rawLayers ) { + + /** + * @type {{ + T: { + id: number; + attr: string; + internalID: number; + attrX: boolean; + attrY: boolean; + attrZ: boolean; + containerBoneID: number; + containerID: number; + curves: { + x: { + version: any; + id: number; + internalID: number; + times: number[]; + values: number[]; + attrFlag: number[]; + attrData: number[]; + }; + y: { + version: any; + id: number; + internalID: number; + times: number[]; + values: number[]; + attrFlag: number[]; + attrData: number[]; + }; + z: { + version: any; + id: number; + internalID: number; + times: number[]; + values: number[]; + attrFlag: number[]; + attrData: number[]; + }; + }, + }, + R: { + id: number; + attr: string; + internalID: number; + attrX: boolean; + attrY: boolean; + attrZ: boolean; + containerBoneID: number; + containerID: number; + curves: { + x: { + version: any; + id: number; + internalID: number; + times: number[]; + values: number[]; + attrFlag: number[]; + attrData: number[]; + }; + y: { + version: any; + id: number; + internalID: number; + times: number[]; + values: number[]; + attrFlag: number[]; + attrData: number[]; + }; + z: { + version: any; + id: number; + internalID: number; + times: number[]; + values: number[]; + attrFlag: number[]; + attrData: number[]; + }; + }, + }, + S: { + id: number; + attr: string; + internalID: number; + attrX: boolean; + attrY: boolean; + attrZ: boolean; + containerBoneID: number; + containerID: number; + curves: { + x: { + version: any; + id: number; + internalID: number; + times: number[]; + values: number[]; + attrFlag: number[]; + attrData: number[]; + }; + y: { + version: any; + id: number; + internalID: number; + times: number[]; + values: number[]; + attrFlag: number[]; + attrData: number[]; + }; + z: { + version: any; + id: number; + internalID: number; + times: number[]; + values: number[]; + attrFlag: number[]; + attrData: number[]; + }; + }, + } + }[]} + */ + var layer = []; + var children = connections.get( parseInt( nodeID ) ).children; + for ( var childIndex = 0; childIndex < children.length; childIndex ++ ) { + + // Skip lockInfluenceWeights + if ( tmpMap.has( children[ childIndex ].ID ) ) { + + var curveNode = tmpMap.get( children[ childIndex ].ID ); + var boneID = curveNode.containerBoneID; + if ( layer[ boneID ] === undefined ) { + + layer[ boneID ] = { + T: null, + R: null, + S: null + }; + + } + + layer[ boneID ][ curveNode.attr ] = curveNode; + + } + + } + + returnObject.layers.set( parseInt( nodeID ), layer ); + + } + + for ( var nodeID in rawStacks ) { + + var layers = []; + var children = connections.get( parseInt( nodeID ) ).children; + var maxTimeStamp = 0; + var minTimeStamp = Number.MAX_VALUE; + for ( var childIndex = 0; childIndex < children.length; ++ childIndex ) { + + if ( returnObject.layers.has( children[ childIndex ].ID ) ) { + + var currentLayer = returnObject.layers.get( children[ childIndex ].ID ); + layers.push( currentLayer ); + + currentLayer.forEach( function ( layer ) { + + if ( layer ) { + + getCurveNodeMaxMinTimeStamps( layer ); + + } + + /** + * Sets the maxTimeStamp and minTimeStamp variables if it has timeStamps that are either larger or smaller + * than the max or min respectively. + * @param {{ + T: { + id: number, + attr: string, + internalID: number, + attrX: boolean, + attrY: boolean, + attrZ: boolean, + containerBoneID: number, + containerID: number, + curves: { + x: { + version: any, + id: number, + internalID: number, + times: number[], + values: number[], + attrFlag: number[], + attrData: number[], + }, + y: { + version: any, + id: number, + internalID: number, + times: number[], + values: number[], + attrFlag: number[], + attrData: number[], + }, + z: { + version: any, + id: number, + internalID: number, + times: number[], + values: number[], + attrFlag: number[], + attrData: number[], + }, + }, + }, + R: { + id: number, + attr: string, + internalID: number, + attrX: boolean, + attrY: boolean, + attrZ: boolean, + containerBoneID: number, + containerID: number, + curves: { + x: { + version: any, + id: number, + internalID: number, + times: number[], + values: number[], + attrFlag: number[], + attrData: number[], + }, + y: { + version: any, + id: number, + internalID: number, + times: number[], + values: number[], + attrFlag: number[], + attrData: number[], + }, + z: { + version: any, + id: number, + internalID: number, + times: number[], + values: number[], + attrFlag: number[], + attrData: number[], + }, + }, + }, + S: { + id: number, + attr: string, + internalID: number, + attrX: boolean, + attrY: boolean, + attrZ: boolean, + containerBoneID: number, + containerID: number, + curves: { + x: { + version: any, + id: number, + internalID: number, + times: number[], + values: number[], + attrFlag: number[], + attrData: number[], + }, + y: { + version: any, + id: number, + internalID: number, + times: number[], + values: number[], + attrFlag: number[], + attrData: number[], + }, + z: { + version: any, + id: number, + internalID: number, + times: number[], + values: number[], + attrFlag: number[], + attrData: number[], + }, + }, + }, + }} layer + */ + function getCurveNodeMaxMinTimeStamps( layer ) { + + /** + * Sets the maxTimeStamp and minTimeStamp if one of the curve's time stamps + * exceeds the maximum or minimum. + * @param {{ + x: { + version: any, + id: number, + internalID: number, + times: number[], + values: number[], + attrFlag: number[], + attrData: number[], + }, + y: { + version: any, + id: number, + internalID: number, + times: number[], + values: number[], + attrFlag: number[], + attrData: number[], + }, + z: { + version: any, + id: number, + internalID: number, + times: number[], + values: number[], + attrFlag: number[], + attrData: number[], + } + }} curve + */ + function getCurveMaxMinTimeStamp( curve ) { + + /** + * Sets the maxTimeStamp and minTimeStamp if one of its timestamps exceeds the maximum or minimum. + * @param {{times: number[]}} axis + */ + function getCurveAxisMaxMinTimeStamps( axis ) { + + maxTimeStamp = axis.times[ axis.times.length - 1 ] > maxTimeStamp ? axis.times[ axis.times.length - 1 ] : maxTimeStamp; + minTimeStamp = axis.times[ 0 ] < minTimeStamp ? axis.times[ 0 ] : minTimeStamp; + + } + + if ( curve.x ) { + + getCurveAxisMaxMinTimeStamps( curve.x ); + + } + if ( curve.y ) { + + getCurveAxisMaxMinTimeStamps( curve.y ); + + } + if ( curve.z ) { + + getCurveAxisMaxMinTimeStamps( curve.z ); + + } + + } + + if ( layer.R ) { + + getCurveMaxMinTimeStamp( layer.R.curves ); + + } + if ( layer.S ) { + + getCurveMaxMinTimeStamp( layer.S.curves ); + + } + if ( layer.T ) { + + getCurveMaxMinTimeStamp( layer.T.curves ); + + } + + } + + } ); + + } + + } + + // Do we have an animation clip with actual length? + if ( maxTimeStamp > minTimeStamp ) { + + returnObject.stacks.set( parseInt( nodeID ), { + name: rawStacks[ nodeID ].attrName, + layers: layers, + length: maxTimeStamp - minTimeStamp, + frames: ( maxTimeStamp - minTimeStamp ) * 30 + } ); + + } + + } + + return returnObject; + + /** + * @param {Object} FBXTree + * @param {{id: number, attrName: string, properties: Object}} animationCurveNode + * @param {Map} connections + * @param {{skeleton: {bones: {FBX_ID: number}[]}}} sceneGraph + */ + function parseAnimationNode( FBXTree, animationCurveNode, connections, sceneGraph ) { + + var returnObject = { + /** + * @type {number} + */ + id: animationCurveNode.id, + + /** + * @type {string} + */ + attr: animationCurveNode.attrName, + + /** + * @type {number} + */ + internalID: animationCurveNode.id, + + /** + * @type {boolean} + */ + attrX: false, + + /** + * @type {boolean} + */ + attrY: false, + + /** + * @type {boolean} + */ + attrZ: false, + + /** + * @type {number} + */ + containerBoneID: - 1, + + /** + * @type {number} + */ + containerID: - 1, + + curves: { + x: null, + y: null, + z: null + } + }; + + if ( returnObject.attr.match( /S|R|T/ ) ) { + + for ( var attributeKey in animationCurveNode.properties ) { + + if ( attributeKey.match( /X/ ) ) { + + returnObject.attrX = true; + + } + if ( attributeKey.match( /Y/ ) ) { + + returnObject.attrY = true; + + } + if ( attributeKey.match( /Z/ ) ) { + + returnObject.attrZ = true; + + } + + } + + } else { + + return null; + + } + + var conns = connections.get( returnObject.id ); + var containerIndices = conns.parents; + + for ( var containerIndicesIndex = containerIndices.length - 1; containerIndicesIndex >= 0; -- containerIndicesIndex ) { + + var boneID = sceneGraph.skeleton.bones.findIndex( function ( bone ) { + + return bone.FBX_ID === containerIndices[ containerIndicesIndex ].ID; + + } ); + if ( boneID > - 1 ) { + + returnObject.containerBoneID = boneID; + returnObject.containerID = containerIndices[ containerIndicesIndex ].ID; + break; + + } + + } + + return returnObject; + + } + + /** + * @param {{id: number, subNodes: {KeyTime: {properties: {a: string}}, KeyValueFloat: {properties: {a: string}}, KeyAttrFlags: {properties: {a: string}}, KeyAttrDataFloat: {properties: {a: string}}}}} animationCurve + */ + function parseAnimationCurve( animationCurve ) { + + return { + version: null, + id: animationCurve.id, + internalID: animationCurve.id, + times: parseFloatArray( animationCurve.subNodes.KeyTime.properties.a ).map( function ( time ) { + + return ConvertFBXTimeToSeconds( time ); + + } ), + values: parseFloatArray( animationCurve.subNodes.KeyValueFloat.properties.a ), + + attrFlag: parseIntArray( animationCurve.subNodes.KeyAttrFlags.properties.a ), + attrData: parseFloatArray( animationCurve.subNodes.KeyAttrDataFloat.properties.a ) + }; + + } + + } + + /** + * @param {{ + curves: Map; + layers: Map; + stacks: Map; + length: number; + fps: number; + frames: number; + }} animations, + * @param {{skeleton: { bones: THREE.Bone[]}}} group + */ + function addAnimations( group, animations ) { + + if ( group.animations === undefined ) { + + group.animations = []; + + } + + animations.stacks.forEach( function ( stack ) { + + var animationData = { + name: stack.name, + fps: 30, + length: stack.length, + hierarchy: [] + }; + + var bones = group.skeleton.bones; + + bones.forEach( function ( bone ) { + + var name = bone.name.replace( /.*:/, '' ); + var parentIndex = bones.findIndex( function ( parentBone ) { + + return bone.parent === parentBone; + + } ); + animationData.hierarchy.push( { parent: parentIndex, name: name, keys: [] } ); + + } ); + + for ( var frame = 0; frame < stack.frames; frame ++ ) { + + bones.forEach( function ( bone, boneIndex ) { + + var animationNode = stack.layers[ 0 ][ boneIndex ]; + + animationData.hierarchy.forEach( function ( node ) { + + if ( node.name === bone.name ) { + + node.keys.push( generateKey( animationNode, bone, frame ) ); + + } + + } ); + + } ); + + } + + group.animations.push( THREE.AnimationClip.parseAnimation( animationData, bones ) ); + + + /** + * @param {THREE.Bone} bone + */ + function generateKey( animationNode, bone, frame ) { + + var key = { + time: frame / animations.fps, + pos: bone.position.toArray(), + rot: bone.quaternion.toArray(), + scl: bone.scale.toArray() + }; + + if ( animationNode === undefined ) { + + return key; + + } + + try { + + if ( hasCurve( animationNode, 'T' ) && hasKeyOnFrame( animationNode.T, frame ) ) { + + key.pos = [ animationNode.T.curves.x.values[ frame ], animationNode.T.curves.y.values[ frame ], animationNode.T.curves.z.values[ frame ] ]; + + } + + if ( hasCurve( animationNode, 'R' ) && hasKeyOnFrame( animationNode.R, frame ) ) { + + var rotationX = degreeToRadian( animationNode.R.curves.x.values[ frame ] ); + var rotationY = degreeToRadian( animationNode.R.curves.y.values[ frame ] ); + var rotationZ = degreeToRadian( animationNode.R.curves.z.values[ frame ] ); + var euler = new THREE.Euler( rotationX, rotationY, rotationZ, 'ZYX' ); + key.rot = new THREE.Quaternion().setFromEuler( euler ).toArray(); + + } + + if ( hasCurve( animationNode, 'S' ) && hasKeyOnFrame( animationNode.S, frame ) ) { + + key.scl = [ animationNode.S.curves.x.values[ frame ], animationNode.S.curves.y.values[ frame ], animationNode.S.curves.z.values[ frame ] ]; + + } + + } catch ( error ) { + + // Curve is not fully plotted. + console.log( bone ); + console.log( error ); + + } + + return key; + + function hasCurve( animationNode, attribute ) { + + if ( animationNode === undefined ) { + + return false; + + } + + var attributeNode = animationNode[ attribute ]; + if ( ! attributeNode ) { + + return false; + + } + + return [ 'x', 'y', 'z' ].every( function ( key ) { + + return attributeNode.curves[ key ] !== undefined; + + } ); + + } + + function hasKeyOnFrame( attributeNode, frame ) { + + return [ 'x', 'y', 'z' ].every( function ( key ) { + + return isKeyExistOnFrame( attributeNode.curves[ key ], frame ); + + function isKeyExistOnFrame( curve, frame ) { + + return curve.values[ frame ] !== undefined; + + } + + } ); + + } + + } + + } ); + + } + + + + // UTILS + /** + * Parses Vector3 property from FBXTree. Property is given as .value.x, .value.y, etc. + * @param {FBXVector3} property - Property to parse as Vector3. + * @returns {THREE.Vector3} + */ + function parseVector3( property ) { + + return new THREE.Vector3( parseFloat( property.value.x ), parseFloat( property.value.y ), parseFloat( property.value.z ) ); + + } + + /** + * Parses Color property from FBXTree. Property is given as .value.x, .value.y, etc. + * @param {FBXVector3} property - Property to parse as Color. + * @returns {THREE.Color} + */ + function parseColor( property ) { + + return new THREE.Color().fromArray( parseVector3( property ).toArray() ); + + } + + } + + } ); + + /** + * An instance of a Vertex with data for drawing vertices to the screen. + * @constructor + */ + function Vertex() { + + /** + * Position of the vertex. + * @type {THREE.Vector3} + */ + this.position = new THREE.Vector3( ); + + /** + * Normal of the vertex + * @type {THREE.Vector3} + */ + this.normal = new THREE.Vector3( ); + + /** + * UV coordinates of the vertex. + * @type {THREE.Vector2} + */ + this.uv = new THREE.Vector2( ); + + /** + * Indices of the bones vertex is influenced by. + * @type {THREE.Vector4} + */ + this.skinIndices = new THREE.Vector4( 0, 0, 0, 0 ); + + /** + * Weights that each bone influences the vertex. + * @type {THREE.Vector4} + */ + this.skinWeights = new THREE.Vector4( 0, 0, 0, 0 ); + + } + + Object.assign( Vertex.prototype, { + + copy: function ( target ) { + + var returnVar = target || new Vertex(); + + returnVar.position.copy( this.position ); + returnVar.normal.copy( this.normal ); + returnVar.uv.copy( this.uv ); + returnVar.skinIndices.copy( this.skinIndices ); + returnVar.skinWeights.copy( this.skinWeights ); + + return returnVar; + + }, + + flattenToBuffers: function () { + + var vertexBuffer = this.position.toArray(); + var normalBuffer = this.normal.toArray(); + var uvBuffer = this.uv.toArray(); + var skinIndexBuffer = this.skinIndices.toArray(); + var skinWeightBuffer = this.skinWeights.toArray(); + + return { + vertexBuffer: vertexBuffer, + normalBuffer: normalBuffer, + uvBuffer: uvBuffer, + skinIndexBuffer: skinIndexBuffer, + skinWeightBuffer: skinWeightBuffer, + }; + + } + + } ); + + /** + * @constructor + */ + function Triangle() { + + /** + * @type {{position: THREE.Vector3, normal: THREE.Vector3, uv: THREE.Vector2, skinIndices: THREE.Vector4, skinWeights: THREE.Vector4}[]} + */ + this.vertices = [ ]; + + } + + Object.assign( Triangle.prototype, { + + copy: function ( target ) { + + var returnVar = target || new Triangle(); + + for ( var i = 0; i < this.vertices.length; ++ i ) { + + this.vertices[ i ].copy( returnVar.vertices[ i ] ); + + } + + return returnVar; + + }, + + flattenToBuffers: function () { + + var vertexBuffer = []; + var normalBuffer = []; + var uvBuffer = []; + var skinIndexBuffer = []; + var skinWeightBuffer = []; + + this.vertices.forEach( function ( vertex ) { + + var flatVertex = vertex.flattenToBuffers(); + vertexBuffer = vertexBuffer.concat( flatVertex.vertexBuffer ); + normalBuffer = normalBuffer.concat( flatVertex.normalBuffer ); + uvBuffer = uvBuffer.concat( flatVertex.uvBuffer ); + skinIndexBuffer = skinIndexBuffer.concat( flatVertex.skinIndexBuffer ); + skinWeightBuffer = skinWeightBuffer.concat( flatVertex.skinWeightBuffer ); + + } ); + + return { + vertexBuffer: vertexBuffer, + normalBuffer: normalBuffer, + uvBuffer: uvBuffer, + skinIndexBuffer: skinIndexBuffer, + skinWeightBuffer: skinWeightBuffer, + }; + + } + + } ); + + /** + * @constructor + */ + function Face() { + + /** + * @type {{vertices: {position: THREE.Vector3, normal: THREE.Vector3, uv: THREE.Vector2, skinIndices: THREE.Vector4, skinWeights: THREE.Vector4}[]}[]} + */ + this.triangles = [ ]; + this.materialIndex = 0; + + } + + Object.assign( Face.prototype, { + + copy: function ( target ) { + + var returnVar = target || new Face(); + + for ( var i = 0; i < this.triangles.length; ++ i ) { + + this.triangles[ i ].copy( returnVar.triangles[ i ] ); + + } + + returnVar.materialIndex = this.materialIndex; + + return returnVar; + + }, + + genTrianglesFromVertices: function ( vertexArray ) { + + for ( var i = 2; i < vertexArray.length; ++ i ) { + + var triangle = new Triangle(); + triangle.vertices[ 0 ] = vertexArray[ 0 ]; + triangle.vertices[ 1 ] = vertexArray[ i - 1 ]; + triangle.vertices[ 2 ] = vertexArray[ i ]; + this.triangles.push( triangle ); + + } + + }, + + flattenToBuffers: function () { + + var vertexBuffer = []; + var normalBuffer = []; + var uvBuffer = []; + var skinIndexBuffer = []; + var skinWeightBuffer = []; + + var materialIndexBuffer = []; + + var materialIndex = this.materialIndex; + + this.triangles.forEach( function ( triangle ) { + + var flatTriangle = triangle.flattenToBuffers(); + vertexBuffer = vertexBuffer.concat( flatTriangle.vertexBuffer ); + normalBuffer = normalBuffer.concat( flatTriangle.normalBuffer ); + uvBuffer = uvBuffer.concat( flatTriangle.uvBuffer ); + skinIndexBuffer = skinIndexBuffer.concat( flatTriangle.skinIndexBuffer ); + skinWeightBuffer = skinWeightBuffer.concat( flatTriangle.skinWeightBuffer ); + materialIndexBuffer = materialIndexBuffer.concat( [ materialIndex, materialIndex, materialIndex ] ); + + } ); + + return { + vertexBuffer: vertexBuffer, + normalBuffer: normalBuffer, + uvBuffer: uvBuffer, + skinIndexBuffer: skinIndexBuffer, + skinWeightBuffer: skinWeightBuffer, + materialIndexBuffer: materialIndexBuffer + }; + + } + + } ); + + /** + * @constructor + */ + function Geometry() { + + /** + * @type {{triangles: {vertices: {position: THREE.Vector3, normal: THREE.Vector3, uv: THREE.Vector2, skinIndices: THREE.Vector4, skinWeights: THREE.Vector4}[]}[], materialIndex: number}[]} + */ + this.faces = [ ]; + + /** + * @type {{}|THREE.Skeleton} + */ + this.skeleton = null; + + } + + Object.assign( Geometry.prototype, { + + /** + * @returns {{vertexBuffer: number[], normalBuffer: number[], uvBuffer: number[], skinIndexBuffer: number[], skinWeightBuffer: number[], materialIndexBuffer: number[]}} + */ + flattenToBuffers: function () { + + var vertexBuffer = []; + var normalBuffer = []; + var uvBuffer = []; + var skinIndexBuffer = []; + var skinWeightBuffer = []; + + var materialIndexBuffer = []; + + this.faces.forEach( function ( face ) { + + var flatFace = face.flattenToBuffers(); + vertexBuffer = vertexBuffer.concat( flatFace.vertexBuffer ); + normalBuffer = normalBuffer.concat( flatFace.normalBuffer ); + uvBuffer = uvBuffer.concat( flatFace.uvBuffer ); + skinIndexBuffer = skinIndexBuffer.concat( flatFace.skinIndexBuffer ); + skinWeightBuffer = skinWeightBuffer.concat( flatFace.skinWeightBuffer ); + materialIndexBuffer = materialIndexBuffer.concat( flatFace.materialIndexBuffer ); + + } ); + + return { + vertexBuffer: vertexBuffer, + normalBuffer: normalBuffer, + uvBuffer: uvBuffer, + skinIndexBuffer: skinIndexBuffer, + skinWeightBuffer: skinWeightBuffer, + materialIndexBuffer: materialIndexBuffer + }; + + } + + } ); + + function TextParser() {} + + Object.assign( TextParser.prototype, { + + getPrevNode: function () { + + return this.nodeStack[ this.currentIndent - 2 ]; + + }, + + getCurrentNode: function () { + + return this.nodeStack[ this.currentIndent - 1 ]; + + }, + + getCurrentProp: function () { + + return this.currentProp; + + }, + + pushStack: function ( node ) { + + this.nodeStack.push( node ); + this.currentIndent += 1; + + }, + + popStack: function () { + + this.nodeStack.pop(); + this.currentIndent -= 1; + + }, + + setCurrentProp: function ( val, name ) { + + this.currentProp = val; + this.currentPropName = name; + + }, + + // ----------parse --------------------------------------------------- + parse: function ( text ) { + + this.currentIndent = 0; + this.allNodes = new FBXTree(); + this.nodeStack = []; + this.currentProp = []; + this.currentPropName = ''; + + var split = text.split( "\n" ); + for ( var line in split ) { + + var l = split[ line ]; + + // short cut + if ( l.match( /^[\s\t]*;/ ) ) { + + continue; + + } // skip comment line + if ( l.match( /^[\s\t]*$/ ) ) { + + continue; + + } // skip empty line + + // beginning of node + var beginningOfNodeExp = new RegExp( "^\\t{" + this.currentIndent + "}(\\w+):(.*){", '' ); + var match = l.match( beginningOfNodeExp ); + if ( match ) { + + var nodeName = match[ 1 ].trim().replace( /^"/, '' ).replace( /"$/, "" ); + var nodeAttrs = match[ 2 ].split( ',' ).map( function ( element ) { + + return element.trim().replace( /^"/, '' ).replace( /"$/, '' ); + + } ); + + this.parseNodeBegin( l, nodeName, nodeAttrs || null ); + continue; + + } + + // node's property + var propExp = new RegExp( "^\\t{" + ( this.currentIndent ) + "}(\\w+):[\\s\\t\\r\\n](.*)" ); + var match = l.match( propExp ); + if ( match ) { + + var propName = match[ 1 ].replace( /^"/, '' ).replace( /"$/, "" ).trim(); + var propValue = match[ 2 ].replace( /^"/, '' ).replace( /"$/, "" ).trim(); + + this.parseNodeProperty( l, propName, propValue ); + continue; + + } + + // end of node + var endOfNodeExp = new RegExp( "^\\t{" + ( this.currentIndent - 1 ) + "}}" ); + if ( l.match( endOfNodeExp ) ) { + + this.nodeEnd(); + continue; + + } + + // for special case, + // + // Vertices: *8670 { + // a: 0.0356229953467846,13.9599733352661,-0.399196773.....(snip) + // -0.0612030513584614,13.960485458374,-0.409748703241348,-0.10..... + // 0.12490539252758,13.7450733184814,-0.454119384288788,0.09272..... + // 0.0836158767342567,13.5432004928589,-0.435397416353226,0.028..... + // + // these case the lines must contiue with previous line + if ( l.match( /^[^\s\t}]/ ) ) { + + this.parseNodePropertyContinued( l ); + + } + + } + + return this.allNodes; + + }, + + parseNodeBegin: function ( line, nodeName, nodeAttrs ) { + + // var nodeName = match[1]; + var node = { 'name': nodeName, properties: {}, 'subNodes': {} }; + var attrs = this.parseNodeAttr( nodeAttrs ); + var currentNode = this.getCurrentNode(); + + // a top node + if ( this.currentIndent === 0 ) { + + this.allNodes.add( nodeName, node ); + + } else { + + // a subnode + + // already exists subnode, then append it + if ( nodeName in currentNode.subNodes ) { + + var tmp = currentNode.subNodes[ nodeName ]; + + // console.log( "duped entry found\nkey: " + nodeName + "\nvalue: " + propValue ); + if ( this.isFlattenNode( currentNode.subNodes[ nodeName ] ) ) { + + + if ( attrs.id === '' ) { + + currentNode.subNodes[ nodeName ] = []; + currentNode.subNodes[ nodeName ].push( tmp ); + + } else { + + currentNode.subNodes[ nodeName ] = {}; + currentNode.subNodes[ nodeName ][ tmp.id ] = tmp; + + } + + } + + if ( attrs.id === '' ) { + + currentNode.subNodes[ nodeName ].push( node ); + + } else { + + currentNode.subNodes[ nodeName ][ attrs.id ] = node; + + } + + } else if ( typeof attrs.id === 'number' || attrs.id.match( /^\d+$/ ) ) { + + currentNode.subNodes[ nodeName ] = {}; + currentNode.subNodes[ nodeName ][ attrs.id ] = node; + + } else { + + currentNode.subNodes[ nodeName ] = node; + + } + + } + + // for this ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ + // NodeAttribute: 1001463072, "NodeAttribute::", "LimbNode" { + if ( nodeAttrs ) { + + node.id = attrs.id; + node.attrName = attrs.name; + node.attrType = attrs.type; + + } + + this.pushStack( node ); + + }, + + parseNodeAttr: function ( attrs ) { + + var id = attrs[ 0 ]; + + if ( attrs[ 0 ] !== "" ) { + + id = parseInt( attrs[ 0 ] ); + + if ( isNaN( id ) ) { + + // PolygonVertexIndex: *16380 { + id = attrs[ 0 ]; + + } + + } + + var name; + var type; + if ( attrs.length > 1 ) { + + name = attrs[ 1 ].replace( /^(\w+)::/, '' ); + type = attrs[ 2 ]; + + } + + return { id: id, name: name || '', type: type || '' }; + + }, + + parseNodeProperty: function ( line, propName, propValue ) { + + var currentNode = this.getCurrentNode(); + var parentName = currentNode.name; + + // special case parent node's is like "Properties70" + // these chilren nodes must treat with careful + if ( parentName !== undefined ) { + + var propMatch = parentName.match( /Properties(\d)+/ ); + if ( propMatch ) { + + this.parseNodeSpecialProperty( line, propName, propValue ); + return; + + } + + } + + // special case Connections + if ( propName == 'C' ) { + + var connProps = propValue.split( ',' ).slice( 1 ); + var from = parseInt( connProps[ 0 ] ); + var to = parseInt( connProps[ 1 ] ); + + var rest = propValue.split( ',' ).slice( 3 ); + + propName = 'connections'; + propValue = [ from, to ]; + propValue = propValue.concat( rest ); + + if ( currentNode.properties[ propName ] === undefined ) { + + currentNode.properties[ propName ] = []; + + } + + } + + // special case Connections + if ( propName == 'Node' ) { + + var id = parseInt( propValue ); + currentNode.properties.id = id; + currentNode.id = id; + + } + + // already exists in properties, then append this + if ( propName in currentNode.properties ) { + + // console.log( "duped entry found\nkey: " + propName + "\nvalue: " + propValue ); + if ( Array.isArray( currentNode.properties[ propName ] ) ) { + + currentNode.properties[ propName ].push( propValue ); + + } else { + + currentNode.properties[ propName ] += propValue; + + } + + } else { + + // console.log( propName + ": " + propValue ); + if ( Array.isArray( currentNode.properties[ propName ] ) ) { + + currentNode.properties[ propName ].push( propValue ); + + } else { + + currentNode.properties[ propName ] = propValue; + + } + + } + + this.setCurrentProp( currentNode.properties, propName ); + + }, + + // TODO: + parseNodePropertyContinued: function ( line ) { + + this.currentProp[ this.currentPropName ] += line; + + }, + + parseNodeSpecialProperty: function ( line, propName, propValue ) { + + // split this + // P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1 + // into array like below + // ["Lcl Scaling", "Lcl Scaling", "", "A", "1,1,1" ] + var props = propValue.split( '",' ).map( function ( element ) { + + return element.trim().replace( /^\"/, '' ).replace( /\s/, '_' ); + + } ); + + var innerPropName = props[ 0 ]; + var innerPropType1 = props[ 1 ]; + var innerPropType2 = props[ 2 ]; + var innerPropFlag = props[ 3 ]; + var innerPropValue = props[ 4 ]; + + /* + if ( innerPropValue === undefined ) { + innerPropValue = props[3]; + } + */ + + // cast value in its type + switch ( innerPropType1 ) { + + case "int": + innerPropValue = parseInt( innerPropValue ); + break; + + case "double": + innerPropValue = parseFloat( innerPropValue ); + break; + + case "ColorRGB": + case "Vector3D": + var tmp = innerPropValue.split( ',' ); + innerPropValue = new THREE.Vector3( tmp[ 0 ], tmp[ 1 ], tmp[ 2 ] ); + break; + + } + + // CAUTION: these props must append to parent's parent + this.getPrevNode().properties[ innerPropName ] = { + + 'type': innerPropType1, + 'type2': innerPropType2, + 'flag': innerPropFlag, + 'value': innerPropValue + + }; + + this.setCurrentProp( this.getPrevNode().properties, innerPropName ); + + }, + + nodeEnd: function () { + + this.popStack(); + + }, + + /* ---------------------------------------------------------------- */ + /* util */ + isFlattenNode: function ( node ) { + + return ( 'subNodes' in node && 'properties' in node ) ? true : false; + + } + + } ); + + function FBXTree() {} + + Object.assign( FBXTree.prototype, { + + add: function ( key, val ) { + + this[ key ] = val; + + }, + + searchConnectionParent: function ( id ) { + + if ( this.__cache_search_connection_parent === undefined ) { + + this.__cache_search_connection_parent = []; + + } + + if ( this.__cache_search_connection_parent[ id ] !== undefined ) { + + return this.__cache_search_connection_parent[ id ]; + + } else { + + this.__cache_search_connection_parent[ id ] = []; + + } + + var conns = this.Connections.properties.connections; + + var results = []; + for ( var i = 0; i < conns.length; ++ i ) { + + if ( conns[ i ][ 0 ] == id ) { + + // 0 means scene root + var res = conns[ i ][ 1 ] === 0 ? - 1 : conns[ i ][ 1 ]; + results.push( res ); + + } + + } + + if ( results.length > 0 ) { + + this.__cache_search_connection_parent[ id ] = this.__cache_search_connection_parent[ id ].concat( results ); + return results; + + } else { + + this.__cache_search_connection_parent[ id ] = [ - 1 ]; + return [ - 1 ]; + + } + + }, + + searchConnectionChildren: function ( id ) { + + if ( this.__cache_search_connection_children === undefined ) { + + this.__cache_search_connection_children = []; + + } + + if ( this.__cache_search_connection_children[ id ] !== undefined ) { + + return this.__cache_search_connection_children[ id ]; + + } else { + + this.__cache_search_connection_children[ id ] = []; + + } + + var conns = this.Connections.properties.connections; + + var res = []; + for ( var i = 0; i < conns.length; ++ i ) { + + if ( conns[ i ][ 1 ] == id ) { + + // 0 means scene root + res.push( conns[ i ][ 0 ] === 0 ? - 1 : conns[ i ][ 0 ] ); + // there may more than one kid, then search to the end + + } + + } + + if ( res.length > 0 ) { + + this.__cache_search_connection_children[ id ] = this.__cache_search_connection_children[ id ].concat( res ); + return res; + + } else { + + this.__cache_search_connection_children[ id ] = [ ]; + return [ ]; + + } + + }, + + searchConnectionType: function ( id, to ) { + + var key = id + ',' + to; // TODO: to hash + if ( this.__cache_search_connection_type === undefined ) { + + this.__cache_search_connection_type = {}; + + } + + if ( this.__cache_search_connection_type[ key ] !== undefined ) { + + return this.__cache_search_connection_type[ key ]; + + } else { + + this.__cache_search_connection_type[ key ] = ''; + + } + + var conns = this.Connections.properties.connections; + + for ( var i = 0; i < conns.length; ++ i ) { + + if ( conns[ i ][ 0 ] == id && conns[ i ][ 1 ] == to ) { + + // 0 means scene root + this.__cache_search_connection_type[ key ] = conns[ i ][ 2 ]; + return conns[ i ][ 2 ]; + + } + + } + + this.__cache_search_connection_type[ id ] = null; + return null; + + } + + } ); + + /** + * @returns {boolean} + */ + function isFbxFormatASCII( text ) { + + var CORRECT = [ 'K', 'a', 'y', 'd', 'a', 'r', 'a', '\\', 'F', 'B', 'X', '\\', 'B', 'i', 'n', 'a', 'r', 'y', '\\', '\\' ]; + + var cursor = 0; + var read = function ( offset ) { + + var result = text[ offset - 1 ]; + text = text.slice( cursor + offset ); + cursor ++; + return result; + + }; + + for ( var i = 0; i < CORRECT.length; ++ i ) { + + var num = read( 1 ); + if ( num == CORRECT[ i ] ) { + + return false; + + } + + } + + return true; + + } + + /** + * @returns {number} + */ + function getFbxVersion( text ) { + + var versionRegExp = /FBXVersion: (\d+)/; + var match = text.match( versionRegExp ); + if ( match ) { + + var version = parseInt( match[ 1 ] ); + return version; + + } + throw new Error( 'FBXLoader: Cannot find the version number for the file given.' ); + + } + + /** + * Converts FBX ticks into real time seconds. + * @param {number} time - FBX tick timestamp to convert. + * @returns {number} - FBX tick in real world time. + */ + function ConvertFBXTimeToSeconds( time ) { + + // Constant is FBX ticks per second. + return time / 46186158000; + + } + + /** + * Parses comma separated list of float numbers and returns them in an array. + * @example + * // Returns [ 5.6, 9.4, 2.5, 1.4 ] + * parseFloatArray( "5.6,9.4,2.5,1.4" ) + * @returns {number[]} + */ + function parseFloatArray( floatString ) { + + return floatString.split( ',' ).map( function ( stringValue ) { + + return parseFloat( stringValue ); + + } ); + + } + + /** + * Parses comma separated list of int numbers and returns them in an array. + * @example + * // Returns [ 5, 8, 2, 3 ] + * parseFloatArray( "5,8,2,3" ) + * @returns {number[]} + */ + function parseIntArray( intString ) { + + return intString.split( ',' ).map( function ( stringValue ) { + + return parseInt( stringValue ); + + } ); + + } + + function parseMatrixArray( floatString ) { + + return new THREE.Matrix4().fromArray( parseFloatArray( floatString ) ); + + } + + /** + * Converts number from degrees into radians. + * @param {number} value + * @returns {number} + */ + function degreeToRadian( value ) { + + return value * Math.PI / 180; + + } + +} )(); diff --git a/src/jlmap3d/main/loaders/MTLLoader.js b/src/jlmap3d/main/loaders/MTLLoader.js new file mode 100644 index 000000000..50de5b936 --- /dev/null +++ b/src/jlmap3d/main/loaders/MTLLoader.js @@ -0,0 +1,583 @@ +/** + * Loads a Wavefront .mtl file specifying materials + * + * @author angelxuanchang + */ + +THREE.MTLLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.MTLLoader.prototype = { + + constructor: THREE.MTLLoader, + + /** + * Loads and parses a MTL asset from a URL. + * + * @param {String} url - URL to the MTL file. + * @param {Function} [onLoad] - Callback invoked with the loaded object. + * @param {Function} [onProgress] - Callback for download progress. + * @param {Function} [onError] - Callback for download errors. + * + * @see setPath setResourcePath + * + * @note In order for relative texture references to resolve correctly + * you must call setResourcePath() explicitly prior to load. + */ + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var path = ( this.path === undefined ) ? THREE.LoaderUtils.extractUrlBase( url ) : this.path; + + var loader = new THREE.FileLoader( this.manager ); + loader.setPath( this.path ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text, path ) ); + + }, onProgress, onError ); + + }, + + /** + * Set base path for resolving references. + * If set this path will be prepended to each loaded and found reference. + * + * @see setResourcePath + * @param {String} path + * @return {THREE.MTLLoader} + * + * @example + * mtlLoader.setPath( 'assets/obj/' ); + * mtlLoader.load( 'my.mtl', ... ); + */ + setPath: function ( path ) { + + this.path = path; + return this; + + }, + + /** + * Set base path for additional resources like textures. + * + * @see setPath + * @param {String} path + * @return {THREE.MTLLoader} + * + * @example + * mtlLoader.setPath( 'assets/obj/' ); + * mtlLoader.setResourcePath( 'assets/textures/' ); + * mtlLoader.load( 'my.mtl', ... ); + */ + setResourcePath: function ( path ) { + + this.resourcePath = path; + return this; + + }, + + setTexturePath: function ( path ) { + + console.warn( 'THREE.MTLLoader: .setTexturePath() has been renamed to .setResourcePath().' ); + return this.setResourcePath( path ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + return this; + + }, + + setMaterialOptions: function ( value ) { + + this.materialOptions = value; + return this; + + }, + + /** + * Parses a MTL file. + * + * @param {String} text - Content of MTL file + * @return {THREE.MTLLoader.MaterialCreator} + * + * @see setPath setResourcePath + * + * @note In order for relative texture references to resolve correctly + * you must call setResourcePath() explicitly prior to parse. + */ + parse: function ( text, path ) { + + var lines = text.split( '\n' ); + var info = {}; + var delimiter_pattern = /\s+/; + var materialsInfo = {}; + + for ( var i = 0; i < lines.length; i ++ ) { + + var line = lines[ i ]; + line = line.trim(); + + if ( line.length === 0 || line.charAt( 0 ) === '#' ) { + + // Blank line or comment ignore + continue; + + } + + var pos = line.indexOf( ' ' ); + + var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line; + key = key.toLowerCase(); + + var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : ''; + value = value.trim(); + + if ( key === 'newmtl' ) { + + // New material + + info = { name: value }; + materialsInfo[ value ] = info; + + } else { + + if ( key === 'ka' || key === 'kd' || key === 'ks' || key ==='ke' ) { + + var ss = value.split( delimiter_pattern, 3 ); + info[ key ] = [ parseFloat( ss[ 0 ] ), parseFloat( ss[ 1 ] ), parseFloat( ss[ 2 ] ) ]; + + } else { + + info[ key ] = value; + + } + + } + + } + + var materialCreator = new THREE.MTLLoader.MaterialCreator( this.resourcePath || path, this.materialOptions ); + materialCreator.setCrossOrigin( this.crossOrigin ); + materialCreator.setManager( this.manager ); + materialCreator.setMaterials( materialsInfo ); + return materialCreator; + + } + +}; + +/** + * Create a new THREE-MTLLoader.MaterialCreator + * @param baseUrl - Url relative to which textures are loaded + * @param options - Set of options on how to construct the materials + * side: Which side to apply the material + * THREE.FrontSide (default), THREE.BackSide, THREE.DoubleSide + * wrap: What type of wrapping to apply for textures + * THREE.RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping + * normalizeRGB: RGBs need to be normalized to 0-1 from 0-255 + * Default: false, assumed to be already normalized + * ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's + * Default: false + * @constructor + */ + +THREE.MTLLoader.MaterialCreator = function ( baseUrl, options ) { + + this.baseUrl = baseUrl || ''; + this.options = options; + this.materialsInfo = {}; + this.materials = {}; + this.materialsArray = []; + this.nameLookup = {}; + + this.side = ( this.options && this.options.side ) ? this.options.side : THREE.FrontSide; + this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : THREE.RepeatWrapping; + +}; + +THREE.MTLLoader.MaterialCreator.prototype = { + + constructor: THREE.MTLLoader.MaterialCreator, + + crossOrigin: 'anonymous', + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + return this; + + }, + + setManager: function ( value ) { + + this.manager = value; + + }, + + setMaterials: function ( materialsInfo ) { + + this.materialsInfo = this.convert( materialsInfo ); + this.materials = {}; + this.materialsArray = []; + this.nameLookup = {}; + + }, + + convert: function ( materialsInfo ) { + + if ( ! this.options ) return materialsInfo; + + var converted = {}; + + for ( var mn in materialsInfo ) { + + // Convert materials info into normalized form based on options + + var mat = materialsInfo[ mn ]; + + var covmat = {}; + + converted[ mn ] = covmat; + + for ( var prop in mat ) { + + var save = true; + var value = mat[ prop ]; + var lprop = prop.toLowerCase(); + + switch ( lprop ) { + + case 'kd': + case 'ka': + case 'ks': + + // Diffuse color (color under white light) using RGB values + + if ( this.options && this.options.normalizeRGB ) { + + value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ]; + + } + + if ( this.options && this.options.ignoreZeroRGBs ) { + + if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 2 ] === 0 ) { + + // ignore + + save = false; + + } + + } + + break; + + default: + + break; + + } + + if ( save ) { + + covmat[ lprop ] = value; + + } + + } + + } + + return converted; + + }, + + preload: function () { + + for ( var mn in this.materialsInfo ) { + + this.create( mn ); + + } + + }, + + getIndex: function ( materialName ) { + + return this.nameLookup[ materialName ]; + + }, + + getAsArray: function () { + + var index = 0; + + for ( var mn in this.materialsInfo ) { + + this.materialsArray[ index ] = this.create( mn ); + this.nameLookup[ mn ] = index; + index ++; + + } + + return this.materialsArray; + + }, + + create: function ( materialName ) { + + if ( this.materials[ materialName ] === undefined ) { + + this.createMaterial_( materialName ); + + } + + return this.materials[ materialName ]; + + }, + + createMaterial_: function ( materialName ) { + + // Create material + + var scope = this; + var mat = this.materialsInfo[ materialName ]; + var params = { + + name: materialName, + side: this.side + + }; + + function resolveURL( baseUrl, url ) { + + if ( typeof url !== 'string' || url === '' ) + return ''; + + // Absolute URL + if ( /^https?:\/\//i.test( url ) ) return url; + + return baseUrl + url; + + } + + function setMapForType( mapType, value ) { + + if ( params[ mapType ] ) return; // Keep the first encountered texture + + var texParams = scope.getTextureParams( value, params ); + var map = scope.loadTexture( resolveURL( scope.baseUrl, texParams.url ) ); + + map.repeat.copy( texParams.scale ); + map.offset.copy( texParams.offset ); + + map.wrapS = scope.wrap; + map.wrapT = scope.wrap; + + params[ mapType ] = map; + + } + + for ( var prop in mat ) { + + var value = mat[ prop ]; + var n; + + if ( value === '' ) continue; + + switch ( prop.toLowerCase() ) { + + // Ns is material specular exponent + + case 'kd': + + // Diffuse color (color under white light) using RGB values + + params.color = new THREE.Color().fromArray( value ); + + break; + + case 'ks': + + // Specular color (color when light is reflected from shiny surface) using RGB values + params.specular = new THREE.Color().fromArray( value ); + + break; + + case 'ke': + + // Emissive using RGB values + params.emissive = new THREE.Color().fromArray( value ); + + break; + + case 'map_kd': + + // Diffuse texture map + + setMapForType( "map", value ); + + break; + + case 'map_ks': + + // Specular map + + setMapForType( "specularMap", value ); + + break; + + case 'map_ke': + + // Emissive map + + setMapForType( "emissiveMap", value ); + + break; + + case 'norm': + + setMapForType( "normalMap", value ); + + break; + + case 'map_bump': + case 'bump': + + // Bump texture map + + setMapForType( "bumpMap", value ); + + break; + + case 'map_d': + + // Alpha map + + setMapForType( "alphaMap", value ); + params.transparent = true; + + break; + + case 'ns': + + // The specular exponent (defines the focus of the specular highlight) + // A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000. + + params.shininess = parseFloat( value ); + + break; + + case 'd': + n = parseFloat( value ); + + if ( n < 1 ) { + + params.opacity = n; + params.transparent = true; + + } + + break; + + case 'tr': + n = parseFloat( value ); + + if ( this.options && this.options.invertTrProperty ) n = 1 - n; + + if ( n > 0 ) { + + params.opacity = 1 - n; + params.transparent = true; + + } + + break; + + default: + break; + + } + + } + + this.materials[ materialName ] = new THREE.MeshPhongMaterial( params ); + return this.materials[ materialName ]; + + }, + + getTextureParams: function ( value, matParams ) { + + var texParams = { + + scale: new THREE.Vector2( 1, 1 ), + offset: new THREE.Vector2( 0, 0 ) + + }; + + var items = value.split( /\s+/ ); + var pos; + + pos = items.indexOf( '-bm' ); + + if ( pos >= 0 ) { + + matParams.bumpScale = parseFloat( items[ pos + 1 ] ); + items.splice( pos, 2 ); + + } + + pos = items.indexOf( '-s' ); + + if ( pos >= 0 ) { + + texParams.scale.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) ); + items.splice( pos, 4 ); // we expect 3 parameters here! + + } + + pos = items.indexOf( '-o' ); + + if ( pos >= 0 ) { + + texParams.offset.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) ); + items.splice( pos, 4 ); // we expect 3 parameters here! + + } + + texParams.url = items.join( ' ' ).trim(); + return texParams; + + }, + + loadTexture: function ( url, mapping, onLoad, onProgress, onError ) { + + var texture; + var loader = THREE.Loader.Handlers.get( url ); + var manager = ( this.manager !== undefined ) ? this.manager : THREE.DefaultLoadingManager; + + if ( loader === null ) { + + loader = new THREE.TextureLoader( manager ); + + } + + if ( loader.setCrossOrigin ) loader.setCrossOrigin( this.crossOrigin ); + texture = loader.load( url, onLoad, onProgress, onError ); + + if ( mapping !== undefined ) texture.mapping = mapping; + + return texture; + + } + +}; diff --git a/src/jlmap3d/main/loaders/OBJLoader.js b/src/jlmap3d/main/loaders/OBJLoader.js new file mode 100644 index 000000000..1408c1b4d --- /dev/null +++ b/src/jlmap3d/main/loaders/OBJLoader.js @@ -0,0 +1,797 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.OBJLoader = ( function () { + + // o object_name | g group_name + var object_pattern = /^[og]\s*(.+)?/; + // mtllib file_reference + var material_library_pattern = /^mtllib /; + // usemtl material_name + var material_use_pattern = /^usemtl /; + + function ParserState() { + + var state = { + objects: [], + object: {}, + + vertices: [], + normals: [], + colors: [], + uvs: [], + + materialLibraries: [], + + startObject: function ( name, fromDeclaration ) { + + // If the current object (initial from reset) is not from a g/o declaration in the parsed + // file. We need to use it for the first parsed g/o to keep things in sync. + if ( this.object && this.object.fromDeclaration === false ) { + + this.object.name = name; + this.object.fromDeclaration = ( fromDeclaration !== false ); + return; + + } + + var previousMaterial = ( this.object && typeof this.object.currentMaterial === 'function' ? this.object.currentMaterial() : undefined ); + + if ( this.object && typeof this.object._finalize === 'function' ) { + + this.object._finalize( true ); + + } + + this.object = { + name: name || '', + fromDeclaration: ( fromDeclaration !== false ), + + geometry: { + vertices: [], + normals: [], + colors: [], + uvs: [] + }, + materials: [], + smooth: true, + + startMaterial: function ( name, libraries ) { + + var previous = this._finalize( false ); + + // New usemtl declaration overwrites an inherited material, except if faces were declared + // after the material, then it must be preserved for proper MultiMaterial continuation. + if ( previous && ( previous.inherited || previous.groupCount <= 0 ) ) { + + this.materials.splice( previous.index, 1 ); + + } + + var material = { + index: this.materials.length, + name: name || '', + mtllib: ( Array.isArray( libraries ) && libraries.length > 0 ? libraries[ libraries.length - 1 ] : '' ), + smooth: ( previous !== undefined ? previous.smooth : this.smooth ), + groupStart: ( previous !== undefined ? previous.groupEnd : 0 ), + groupEnd: - 1, + groupCount: - 1, + inherited: false, + + clone: function ( index ) { + + var cloned = { + index: ( typeof index === 'number' ? index : this.index ), + name: this.name, + mtllib: this.mtllib, + smooth: this.smooth, + groupStart: 0, + groupEnd: - 1, + groupCount: - 1, + inherited: false + }; + cloned.clone = this.clone.bind( cloned ); + return cloned; + + } + }; + + this.materials.push( material ); + + return material; + + }, + + currentMaterial: function () { + + if ( this.materials.length > 0 ) { + + return this.materials[ this.materials.length - 1 ]; + + } + + return undefined; + + }, + + _finalize: function ( end ) { + + var lastMultiMaterial = this.currentMaterial(); + if ( lastMultiMaterial && lastMultiMaterial.groupEnd === - 1 ) { + + lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3; + lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart; + lastMultiMaterial.inherited = false; + + } + + // Ignore objects tail materials if no face declarations followed them before a new o/g started. + if ( end && this.materials.length > 1 ) { + + for ( var mi = this.materials.length - 1; mi >= 0; mi -- ) { + + if ( this.materials[ mi ].groupCount <= 0 ) { + + this.materials.splice( mi, 1 ); + + } + + } + + } + + // Guarantee at least one empty material, this makes the creation later more straight forward. + if ( end && this.materials.length === 0 ) { + + this.materials.push( { + name: '', + smooth: this.smooth + } ); + + } + + return lastMultiMaterial; + + } + }; + + // Inherit previous objects material. + // Spec tells us that a declared material must be set to all objects until a new material is declared. + // If a usemtl declaration is encountered while this new object is being parsed, it will + // overwrite the inherited material. Exception being that there was already face declarations + // to the inherited material, then it will be preserved for proper MultiMaterial continuation. + + if ( previousMaterial && previousMaterial.name && typeof previousMaterial.clone === 'function' ) { + + var declared = previousMaterial.clone( 0 ); + declared.inherited = true; + this.object.materials.push( declared ); + + } + + this.objects.push( this.object ); + + }, + + finalize: function () { + + if ( this.object && typeof this.object._finalize === 'function' ) { + + this.object._finalize( true ); + + } + + }, + + parseVertexIndex: function ( value, len ) { + + var index = parseInt( value, 10 ); + return ( index >= 0 ? index - 1 : index + len / 3 ) * 3; + + }, + + parseNormalIndex: function ( value, len ) { + + var index = parseInt( value, 10 ); + return ( index >= 0 ? index - 1 : index + len / 3 ) * 3; + + }, + + parseUVIndex: function ( value, len ) { + + var index = parseInt( value, 10 ); + return ( index >= 0 ? index - 1 : index + len / 2 ) * 2; + + }, + + addVertex: function ( a, b, c ) { + + var src = this.vertices; + var dst = this.object.geometry.vertices; + + dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); + dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] ); + dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] ); + + }, + + addVertexPoint: function ( a ) { + + var src = this.vertices; + var dst = this.object.geometry.vertices; + + dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); + + }, + + addVertexLine: function ( a ) { + + var src = this.vertices; + var dst = this.object.geometry.vertices; + + dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); + + }, + + addNormal: function ( a, b, c ) { + + var src = this.normals; + var dst = this.object.geometry.normals; + + dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); + dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] ); + dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] ); + + }, + + addColor: function ( a, b, c ) { + + var src = this.colors; + var dst = this.object.geometry.colors; + + dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] ); + dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] ); + dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] ); + + }, + + addUV: function ( a, b, c ) { + + var src = this.uvs; + var dst = this.object.geometry.uvs; + + dst.push( src[ a + 0 ], src[ a + 1 ] ); + dst.push( src[ b + 0 ], src[ b + 1 ] ); + dst.push( src[ c + 0 ], src[ c + 1 ] ); + + }, + + addUVLine: function ( a ) { + + var src = this.uvs; + var dst = this.object.geometry.uvs; + + dst.push( src[ a + 0 ], src[ a + 1 ] ); + + }, + + addFace: function ( a, b, c, ua, ub, uc, na, nb, nc ) { + + var vLen = this.vertices.length; + + var ia = this.parseVertexIndex( a, vLen ); + var ib = this.parseVertexIndex( b, vLen ); + var ic = this.parseVertexIndex( c, vLen ); + + this.addVertex( ia, ib, ic ); + + if ( ua !== undefined && ua !== '' ) { + + var uvLen = this.uvs.length; + ia = this.parseUVIndex( ua, uvLen ); + ib = this.parseUVIndex( ub, uvLen ); + ic = this.parseUVIndex( uc, uvLen ); + this.addUV( ia, ib, ic ); + + } + + if ( na !== undefined && na !== '' ) { + + // Normals are many times the same. If so, skip function call and parseInt. + var nLen = this.normals.length; + ia = this.parseNormalIndex( na, nLen ); + + ib = na === nb ? ia : this.parseNormalIndex( nb, nLen ); + ic = na === nc ? ia : this.parseNormalIndex( nc, nLen ); + + this.addNormal( ia, ib, ic ); + + } + + if ( this.colors.length > 0 ) { + + this.addColor( ia, ib, ic ); + + } + + }, + + addPointGeometry: function ( vertices ) { + + this.object.geometry.type = 'Points'; + + var vLen = this.vertices.length; + + for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) { + + this.addVertexPoint( this.parseVertexIndex( vertices[ vi ], vLen ) ); + + } + + }, + + addLineGeometry: function ( vertices, uvs ) { + + this.object.geometry.type = 'Line'; + + var vLen = this.vertices.length; + var uvLen = this.uvs.length; + + for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) { + + this.addVertexLine( this.parseVertexIndex( vertices[ vi ], vLen ) ); + + } + + for ( var uvi = 0, l = uvs.length; uvi < l; uvi ++ ) { + + this.addUVLine( this.parseUVIndex( uvs[ uvi ], uvLen ) ); + + } + + } + + }; + + state.startObject( '', false ); + + return state; + + } + + // + + function OBJLoader( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + + this.materials = null; + + } + + OBJLoader.prototype = { + + constructor: OBJLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.FileLoader( scope.manager ); + loader.setPath( this.path ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( text ) ); + + }, onProgress, onError ); + + }, + + setPath: function ( value ) { + + this.path = value; + + return this; + + }, + + setMaterials: function ( materials ) { + + this.materials = materials; + + return this; + + }, + + parse: function ( text ) { + + //console.time( 'OBJLoader' ); + + var state = new ParserState(); + + if ( text.indexOf( '\r\n' ) !== - 1 ) { + + // This is faster than String.split with regex that splits on both + text = text.replace( /\r\n/g, '\n' ); + + } + + if ( text.indexOf( '\\\n' ) !== - 1 ) { + + // join lines separated by a line continuation character (\) + text = text.replace( /\\\n/g, '' ); + + } + + var lines = text.split( '\n' ); + var line = '', lineFirstChar = ''; + var lineLength = 0; + var result = []; + + // Faster to just trim left side of the line. Use if available. + var trimLeft = ( typeof ''.trimLeft === 'function' ); + + for ( var i = 0, l = lines.length; i < l; i ++ ) { + + line = lines[ i ]; + + line = trimLeft ? line.trimLeft() : line.trim(); + + lineLength = line.length; + + if ( lineLength === 0 ) continue; + + lineFirstChar = line.charAt( 0 ); + + // @todo invoke passed in handler if any + if ( lineFirstChar === '#' ) continue; + + if ( lineFirstChar === 'v' ) { + + var data = line.split( /\s+/ ); + + switch ( data[ 0 ] ) { + + case 'v': + state.vertices.push( + parseFloat( data[ 1 ] ), + parseFloat( data[ 2 ] ), + parseFloat( data[ 3 ] ) + ); + if ( data.length === 8 ) { + + state.colors.push( + parseFloat( data[ 4 ] ), + parseFloat( data[ 5 ] ), + parseFloat( data[ 6 ] ) + + ); + + } + break; + case 'vn': + state.normals.push( + parseFloat( data[ 1 ] ), + parseFloat( data[ 2 ] ), + parseFloat( data[ 3 ] ) + ); + break; + case 'vt': + state.uvs.push( + parseFloat( data[ 1 ] ), + parseFloat( data[ 2 ] ) + ); + break; + + } + + } else if ( lineFirstChar === 'f' ) { + + var lineData = line.substr( 1 ).trim(); + var vertexData = lineData.split( /\s+/ ); + var faceVertices = []; + + // Parse the face vertex data into an easy to work with format + + for ( var j = 0, jl = vertexData.length; j < jl; j ++ ) { + + var vertex = vertexData[ j ]; + + if ( vertex.length > 0 ) { + + var vertexParts = vertex.split( '/' ); + faceVertices.push( vertexParts ); + + } + + } + + // Draw an edge between the first vertex and all subsequent vertices to form an n-gon + + var v1 = faceVertices[ 0 ]; + + for ( var j = 1, jl = faceVertices.length - 1; j < jl; j ++ ) { + + var v2 = faceVertices[ j ]; + var v3 = faceVertices[ j + 1 ]; + + state.addFace( + v1[ 0 ], v2[ 0 ], v3[ 0 ], + v1[ 1 ], v2[ 1 ], v3[ 1 ], + v1[ 2 ], v2[ 2 ], v3[ 2 ] + ); + + } + + } else if ( lineFirstChar === 'l' ) { + + var lineParts = line.substring( 1 ).trim().split( " " ); + var lineVertices = [], lineUVs = []; + + if ( line.indexOf( "/" ) === - 1 ) { + + lineVertices = lineParts; + + } else { + + for ( var li = 0, llen = lineParts.length; li < llen; li ++ ) { + + var parts = lineParts[ li ].split( "/" ); + + if ( parts[ 0 ] !== "" ) lineVertices.push( parts[ 0 ] ); + if ( parts[ 1 ] !== "" ) lineUVs.push( parts[ 1 ] ); + + } + + } + state.addLineGeometry( lineVertices, lineUVs ); + + } else if ( lineFirstChar === 'p' ) { + + var lineData = line.substr( 1 ).trim(); + var pointData = lineData.split( " " ); + + state.addPointGeometry( pointData ); + + } else if ( ( result = object_pattern.exec( line ) ) !== null ) { + + // o object_name + // or + // g group_name + + // WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869 + // var name = result[ 0 ].substr( 1 ).trim(); + var name = ( " " + result[ 0 ].substr( 1 ).trim() ).substr( 1 ); + + state.startObject( name ); + + } else if ( material_use_pattern.test( line ) ) { + + // material + + state.object.startMaterial( line.substring( 7 ).trim(), state.materialLibraries ); + + } else if ( material_library_pattern.test( line ) ) { + + // mtl file + + state.materialLibraries.push( line.substring( 7 ).trim() ); + + } else if ( lineFirstChar === 's' ) { + + result = line.split( ' ' ); + + // smooth shading + + // @todo Handle files that have varying smooth values for a set of faces inside one geometry, + // but does not define a usemtl for each face set. + // This should be detected and a dummy material created (later MultiMaterial and geometry groups). + // This requires some care to not create extra material on each smooth value for "normal" obj files. + // where explicit usemtl defines geometry groups. + // Example asset: examples/models/obj/cerberus/Cerberus.obj + + /* + * http://paulbourke.net/dataformats/obj/ + * or + * http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf + * + * From chapter "Grouping" Syntax explanation "s group_number": + * "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off. + * Polygonal elements use group numbers to put elements in different smoothing groups. For free-form + * surfaces, smoothing groups are either turned on or off; there is no difference between values greater + * than 0." + */ + if ( result.length > 1 ) { + + var value = result[ 1 ].trim().toLowerCase(); + state.object.smooth = ( value !== '0' && value !== 'off' ); + + } else { + + // ZBrush can produce "s" lines #11707 + state.object.smooth = true; + + } + var material = state.object.currentMaterial(); + if ( material ) material.smooth = state.object.smooth; + + } else { + + // Handle null terminated files without exception + if ( line === '\0' ) continue; + + throw new Error( 'THREE.OBJLoader: Unexpected line: "' + line + '"' ); + + } + + } + + state.finalize(); + + var container = new THREE.Group(); + container.materialLibraries = [].concat( state.materialLibraries ); + + for ( var i = 0, l = state.objects.length; i < l; i ++ ) { + + var object = state.objects[ i ]; + var geometry = object.geometry; + var materials = object.materials; + var isLine = ( geometry.type === 'Line' ); + var isPoints = ( geometry.type === 'Points' ); + var hasVertexColors = false; + + // Skip o/g line declarations that did not follow with any faces + if ( geometry.vertices.length === 0 ) continue; + + var buffergeometry = new THREE.BufferGeometry(); + + buffergeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( geometry.vertices, 3 ) ); + + if ( geometry.normals.length > 0 ) { + + buffergeometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( geometry.normals, 3 ) ); + + } else { + + buffergeometry.computeVertexNormals(); + + } + + if ( geometry.colors.length > 0 ) { + + hasVertexColors = true; + buffergeometry.addAttribute( 'color', new THREE.Float32BufferAttribute( geometry.colors, 3 ) ); + + } + + if ( geometry.uvs.length > 0 ) { + + buffergeometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( geometry.uvs, 2 ) ); + + } + + // Create materials + + var createdMaterials = []; + + for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) { + + var sourceMaterial = materials[ mi ]; + var material = undefined; + + if ( this.materials !== null ) { + + material = this.materials.create( sourceMaterial.name ); + + // mtl etc. loaders probably can't create line materials correctly, copy properties to a line material. + if ( isLine && material && ! ( material instanceof THREE.LineBasicMaterial ) ) { + + var materialLine = new THREE.LineBasicMaterial(); + THREE.Material.prototype.copy.call( materialLine, material ); + materialLine.color.copy( material.color ); + materialLine.lights = false; + material = materialLine; + + } else if ( isPoints && material && ! ( material instanceof THREE.PointsMaterial ) ) { + + var materialPoints = new THREE.PointsMaterial( { size: 10, sizeAttenuation: false } ); + THREE.Material.prototype.copy.call( materialPoints, material ); + materialPoints.color.copy( material.color ); + materialPoints.map = material.map; + materialPoints.lights = false; + material = materialPoints; + + } + + } + + if ( ! material ) { + + if ( isLine ) { + + material = new THREE.LineBasicMaterial(); + + } else if ( isPoints ) { + + material = new THREE.PointsMaterial( { size: 1, sizeAttenuation: false } ); + + } else { + + material = new THREE.MeshPhongMaterial(); + + } + + material.name = sourceMaterial.name; + + } + + material.flatShading = sourceMaterial.smooth ? false : true; + material.vertexColors = hasVertexColors ? THREE.VertexColors : THREE.NoColors; + + createdMaterials.push( material ); + + } + + // Create mesh + + var mesh; + + if ( createdMaterials.length > 1 ) { + + for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) { + + var sourceMaterial = materials[ mi ]; + buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi ); + + } + + if ( isLine ) { + + mesh = new THREE.LineSegments( buffergeometry, createdMaterials ); + + } else if ( isPoints ) { + + mesh = new THREE.Points( buffergeometry, createdMaterials ); + + } else { + + mesh = new THREE.Mesh( buffergeometry, createdMaterials ); + + } + + } else { + + if ( isLine ) { + + mesh = new THREE.LineSegments( buffergeometry, createdMaterials[ 0 ] ); + + } else if ( isPoints ) { + + mesh = new THREE.Points( buffergeometry, createdMaterials[ 0 ] ); + + } else { + + mesh = new THREE.Mesh( buffergeometry, createdMaterials[ 0 ] ); + + } + + } + + mesh.name = object.name; + + container.add( mesh ); + + } + + //console.timeEnd( 'OBJLoader' ); + + return container; + + } + + }; + + return OBJLoader; + +} )(); diff --git a/src/jlmap3d/main/loaders/model/Assetmaterial.js b/src/jlmap3d/main/loaders/model/Assetmaterial.js new file mode 100644 index 000000000..4f103890c --- /dev/null +++ b/src/jlmap3d/main/loaders/model/Assetmaterial.js @@ -0,0 +1,23 @@ +export function Assetmaterial(){ + + let scope = this; + + this.uuid = null; + + this.name = null; + + this.classtype = null; + + this.modeltype = null; + + this.asseturl = null; + + this.create = function(){ + + } + + this.dispose = function (){ + + } + +} diff --git a/src/jlmap3d/main/loaders/model/Assetmodel.js b/src/jlmap3d/main/loaders/model/Assetmodel.js new file mode 100644 index 000000000..bb5c5ad2c --- /dev/null +++ b/src/jlmap3d/main/loaders/model/Assetmodel.js @@ -0,0 +1,27 @@ +export function AssetModel(data){ + + let scope = this; + + this.id = data.id; + + this.name = data.name; + + this.deviceType = data.deviceType; + + this.type = data.type; + + this.assetUrl = data.assetUrl; + + this.mesh = null; + + this.animate = null; + + this.create = function(){ + + } + + this.dispose = function(){ + + } + +} diff --git a/src/jlmap3d/main/three.min.js b/src/jlmap3d/main/three.min.js new file mode 100644 index 000000000..87bc1451e --- /dev/null +++ b/src/jlmap3d/main/three.min.js @@ -0,0 +1,971 @@ +// threejs.org/license +(function(l,ka){"object"===typeof exports&&"undefined"!==typeof module?ka(exports):"function"===typeof define&&define.amd?define(["exports"],ka):(l=l||self,ka(l.THREE={}))})(this,function(l){function ka(){}function B(a,b){this.x=a||0;this.y=b||0}function J(){this.elements=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1];0b&&(b=a[c]);return b}function C(){Object.defineProperty(this,"id",{value:Xf+=2});this.uuid=H.generateUUID();this.name="";this.type="BufferGeometry";this.index=null;this.attributes={};this.morphAttributes={};this.groups=[];this.boundingSphere=this.boundingBox=null;this.drawRange={start:0,count:Infinity};this.userData={}} +function Ob(a,b,c,d,e,f){G.call(this);this.type="BoxGeometry";this.parameters={width:a,height:b,depth:c,widthSegments:d,heightSegments:e,depthSegments:f};this.fromBufferGeometry(new rb(a,b,c,d,e,f));this.mergeVertices()}function rb(a,b,c,d,e,f){function g(a,b,c,d,e,f,g,l,fa,A,B){var r=f/fa,u=g/A,w=f/2,x=g/2,z=l/2;g=fa+1;var D=A+1,y=f=0,Q,Fa,ta=new n;for(Fa=0;Fam;m++){if(q=d[m])if(h=q[0],k=q[1]){p&&e.addAttribute("morphTarget"+m, +p[h]);f&&e.addAttribute("morphNormal"+m,f[h]);c[m]=k;continue}c[m]=0}g.getUniforms().setValue(a,"morphTargetInfluences",c)}}}function ig(a,b){var c={};return{update:function(d){var e=b.render.frame,f=d.geometry,g=a.get(d,f);c[g.id]!==e&&(f.isGeometry&&g.updateFromObject(d),a.update(g),c[g.id]=e);return g},dispose:function(){c={}}}}function $a(a,b,c,d,e,f,g,h,k,m){a=void 0!==a?a:[];V.call(this,a,void 0!==b?b:301,c,d,e,f,void 0!==g?g:1022,h,k,m);this.flipY=!1}function Qb(a,b,c,d){V.call(this,null); +this.image={data:a,width:b,height:c,depth:d};this.minFilter=this.magFilter=1003;this.wrapR=1001;this.flipY=this.generateMipmaps=!1}function Rb(a,b,c){var d=a[0];if(0>=d||0/gm, +function(a,c){a=U[c];if(void 0===a)throw Error("Can not resolve #include <"+c+">");return fe(a)})}function kf(a){return a.replace(/#pragma unroll_loop[\s]+?for \( int i = (\d+); i < (\d+); i \+\+ \) \{([\s\S]+?)(?=\})\}/g,function(a,c,d,e){a="";for(c=parseInt(c);cd||a.height>d)e=d/Math.max(a.width,a.height);if(1>e||!0===b){if(a instanceof ImageBitmap||a instanceof HTMLImageElement||a instanceof HTMLCanvasElement)return d=b?H.floorPowerOfTwo:Math.floor,b=d(e*a.width),e=d(e*a.height),void 0===B&&(B=h(b,e)),c=c?h(b,e):B,c.width=b,c.height=e,c.getContext("2d").drawImage(a,0,0,b,e),console.warn("THREE.WebGLRenderer: Texture has been resized from ("+ +a.width+"x"+a.height+") to ("+b+"x"+e+")."),F?c.transferToImageBitmap():c;"data"in a&&console.warn("THREE.WebGLRenderer: Image in DataTexture is too big ("+a.width+"x"+a.height+").")}return a}function m(a){return H.isPowerOfTwo(a.width)&&H.isPowerOfTwo(a.height)}function p(a,b){return a.generateMipmaps&&b&&1003!==a.minFilter&&1006!==a.minFilter}function q(b,c,e,f){a.generateMipmap(b);d.get(c).__maxMipLevel=Math.log(Math.max(e,f))*Math.LOG2E}function v(a,c){if(!e.isWebGL2)return a;var d=a;6403===a&& +(5126===c&&(d=33326),5131===c&&(d=33325),5121===c&&(d=33321));6407===a&&(5126===c&&(d=34837),5131===c&&(d=34843),5121===c&&(d=32849));6408===a&&(5126===c&&(d=34836),5131===c&&(d=34842),5121===c&&(d=32856));33325===d||33326===d||34842===d||34836===d?b.get("EXT_color_buffer_float"):(34843===d||34837===d)&&console.warn("THREE.WebGLRenderer: Floating point textures with RGB format not supported. Please use RGBA instead.");return d}function l(a){return 1003===a||1004===a||1005===a?9728:9729}function r(b){b= +b.target;b.removeEventListener("dispose",r);var c=d.get(b);void 0!==c.__webglInit&&(a.deleteTexture(c.__webglTexture),d.remove(b));b.isVideoTexture&&delete A[b.id];g.memory.textures--}function u(b){b=b.target;b.removeEventListener("dispose",u);var c=d.get(b),e=d.get(b.texture);if(b){void 0!==e.__webglTexture&&a.deleteTexture(e.__webglTexture);b.depthTexture&&b.depthTexture.dispose();if(b.isWebGLRenderTargetCube)for(e=0;6>e;e++)a.deleteFramebuffer(c.__webglFramebuffer[e]),c.__webglDepthbuffer&&a.deleteRenderbuffer(c.__webglDepthbuffer[e]); +else a.deleteFramebuffer(c.__webglFramebuffer),c.__webglDepthbuffer&&a.deleteRenderbuffer(c.__webglDepthbuffer);d.remove(b.texture);d.remove(b)}g.memory.textures--}function n(a,b){var e=d.get(a);if(a.isVideoTexture){var f=a.id,h=g.render.frame;A[f]!==h&&(A[f]=h,a.update())}if(0r;r++)t[r]=g||l?l?b.image[r].image:b.image[r]:k(b.image[r],!1,!0,e.maxCubemapSize);var u=t[0],n=m(u)||e.isWebGL2,w=f.convert(b.format),y=f.convert(b.type),D=v(w,y); +z(34067,b,n);for(r=0;6>r;r++)if(g)for(var A,fa=t[r].mipmaps,Q=0,Ac=fa.length;Qr;r++)h.__webglFramebuffer[r]=a.createFramebuffer();else if(h.__webglFramebuffer=a.createFramebuffer(),r)if(e.isWebGL2){h.__webglMultisampledFramebuffer=a.createFramebuffer();h.__webglColorRenderbuffer=a.createRenderbuffer();a.bindRenderbuffer(36161,h.__webglColorRenderbuffer);r=f.convert(b.texture.format);var w=f.convert(b.texture.type);r=v(r,w);w=fa(b);a.renderbufferStorageMultisample(36161,w,r,b.width, +b.height);a.bindFramebuffer(36160,h.__webglMultisampledFramebuffer);a.framebufferRenderbuffer(36160,36064,36161,h.__webglColorRenderbuffer);a.bindRenderbuffer(36161,null);b.depthBuffer&&(h.__webglDepthRenderbuffer=a.createRenderbuffer(),Q(h.__webglDepthRenderbuffer,b,!0));a.bindFramebuffer(36160,null)}else console.warn("THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.");if(l){c.bindTexture(34067,k.__webglTexture);z(34067,b.texture,t);for(r=0;6>r;r++)y(h.__webglFramebuffer[r], +b,36064,34069+r);p(b.texture,t)&&q(34067,b.texture,b.width,b.height);c.bindTexture(34067,null)}else c.bindTexture(3553,k.__webglTexture),z(3553,b.texture,t),y(h.__webglFramebuffer,b,36064,3553),p(b.texture,t)&&q(3553,b.texture,b.width,b.height),c.bindTexture(3553,null);if(b.depthBuffer){h=d.get(b);k=!0===b.isWebGLRenderTargetCube;if(b.depthTexture){if(k)throw Error("target.depthTexture not supported in Cube render targets");if(b&&b.isWebGLRenderTargetCube)throw Error("Depth Texture with cube render targets is not supported"); +a.bindFramebuffer(36160,h.__webglFramebuffer);if(!b.depthTexture||!b.depthTexture.isDepthTexture)throw Error("renderTarget.depthTexture must be an instance of THREE.DepthTexture");d.get(b.depthTexture).__webglTexture&&b.depthTexture.image.width===b.width&&b.depthTexture.image.height===b.height||(b.depthTexture.image.width=b.width,b.depthTexture.image.height=b.height,b.depthTexture.needsUpdate=!0);n(b.depthTexture,0);h=d.get(b.depthTexture).__webglTexture;if(1026===b.depthTexture.format)a.framebufferTexture2D(36160, +36096,3553,h,0);else if(1027===b.depthTexture.format)a.framebufferTexture2D(36160,33306,3553,h,0);else throw Error("Unknown depthTexture format");}else if(k)for(h.__webglDepthbuffer=[],k=0;6>k;k++)a.bindFramebuffer(36160,h.__webglFramebuffer[k]),h.__webglDepthbuffer[k]=a.createRenderbuffer(),Q(h.__webglDepthbuffer[k],b);else a.bindFramebuffer(36160,h.__webglFramebuffer),h.__webglDepthbuffer=a.createRenderbuffer(),Q(h.__webglDepthbuffer,b);a.bindFramebuffer(36160,null)}};this.updateRenderTargetMipmap= +function(a){var b=a.texture,f=m(a)||e.isWebGL2;if(p(b,f)){f=a.isWebGLRenderTargetCube?34067:3553;var g=d.get(b).__webglTexture;c.bindTexture(f,g);q(f,b,a.width,a.height);c.bindTexture(f,null)}};this.updateMultisampleRenderTarget=function(b){if(b.isWebGLMultisampleRenderTarget)if(e.isWebGL2){var c=d.get(b);a.bindFramebuffer(36008,c.__webglMultisampledFramebuffer);a.bindFramebuffer(36009,c.__webglFramebuffer);c=b.width;var f=b.height,g=16384;b.depthBuffer&&(g|=256);b.stencilBuffer&&(g|=1024);a.blitFramebuffer(0, +0,c,f,0,0,c,f,g,9728)}else console.warn("THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.")}}function of(a,b,c){return{convert:function(a){if(1E3===a)return 10497;if(1001===a)return 33071;if(1002===a)return 33648;if(1003===a)return 9728;if(1004===a)return 9984;if(1005===a)return 9986;if(1006===a)return 9729;if(1007===a)return 9985;if(1008===a)return 9987;if(1009===a)return 5121;if(1017===a)return 32819;if(1018===a)return 32820;if(1019===a)return 33635;if(1010===a)return 5120; +if(1011===a)return 5122;if(1012===a)return 5123;if(1013===a)return 5124;if(1014===a)return 5125;if(1015===a)return 5126;if(1016===a){if(c.isWebGL2)return 5131;var d=b.get("OES_texture_half_float");if(null!==d)return d.HALF_FLOAT_OES}if(1021===a)return 6406;if(1022===a)return 6407;if(1023===a)return 6408;if(1024===a)return 6409;if(1025===a)return 6410;if(1026===a)return 6402;if(1027===a)return 34041;if(1028===a)return 6403;if(100===a)return 32774;if(101===a)return 32778;if(102===a)return 32779;if(200=== +a)return 0;if(201===a)return 1;if(202===a)return 768;if(203===a)return 769;if(204===a)return 770;if(205===a)return 771;if(206===a)return 772;if(207===a)return 773;if(208===a)return 774;if(209===a)return 775;if(210===a)return 776;if(33776===a||33777===a||33778===a||33779===a)if(d=b.get("WEBGL_compressed_texture_s3tc"),null!==d){if(33776===a)return d.COMPRESSED_RGB_S3TC_DXT1_EXT;if(33777===a)return d.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(33778===a)return d.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(33779===a)return d.COMPRESSED_RGBA_S3TC_DXT5_EXT}if(35840=== +a||35841===a||35842===a||35843===a)if(d=b.get("WEBGL_compressed_texture_pvrtc"),null!==d){if(35840===a)return d.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;if(35841===a)return d.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;if(35842===a)return d.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;if(35843===a)return d.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}if(36196===a&&(d=b.get("WEBGL_compressed_texture_etc1"),null!==d))return d.COMPRESSED_RGB_ETC1_WEBGL;if(37808===a||37809===a||37810===a||37811===a||37812===a||37813===a||37814===a||37815===a||37816=== +a||37817===a||37818===a||37819===a||37820===a||37821===a)if(d=b.get("WEBGL_compressed_texture_astc"),null!==d)return a;if(103===a||104===a){if(c.isWebGL2){if(103===a)return 32775;if(104===a)return 32776}d=b.get("EXT_blend_minmax");if(null!==d){if(103===a)return d.MIN_EXT;if(104===a)return d.MAX_EXT}}if(1020===a){if(c.isWebGL2)return 34042;d=b.get("WEBGL_depth_texture");if(null!==d)return d.UNSIGNED_INT_24_8_WEBGL}return 0}}}function Sb(){E.call(this);this.type="Group"}function Ua(){E.call(this);this.type= +"Camera";this.matrixWorldInverse=new J;this.projectionMatrix=new J;this.projectionMatrixInverse=new J}function S(a,b,c,d){Ua.call(this);this.type="PerspectiveCamera";this.fov=void 0!==a?a:50;this.zoom=1;this.near=void 0!==c?c:.1;this.far=void 0!==d?d:2E3;this.focus=10;this.aspect=void 0!==b?b:1;this.view=null;this.filmGauge=35;this.filmOffset=0;this.updateProjectionMatrix()}function Ec(a){S.call(this);this.cameras=a||[]}function pf(a,b,c){qf.setFromMatrixPosition(b.matrixWorld);rf.setFromMatrixPosition(c.matrixWorld); +var d=qf.distanceTo(rf),e=b.projectionMatrix.elements,f=c.projectionMatrix.elements,g=e[14]/(e[10]-1);c=e[14]/(e[10]+1);var h=(e[9]+1)/e[5],k=(e[9]-1)/e[5],m=(e[8]-1)/e[0],p=(f[8]+1)/f[0];e=g*m;f=g*p;p=d/(-m+p);m=p*-m;b.matrixWorld.decompose(a.position,a.quaternion,a.scale);a.translateX(m);a.translateZ(p);a.matrixWorld.compose(a.position,a.quaternion,a.scale);a.matrixWorldInverse.getInverse(a.matrixWorld);b=g+p;g=c+p;a.projectionMatrix.makePerspective(e-m,f+(d-m),h*c/g*b,k*c/g*b,b,g)}function sf(a){function b(){return null!== +e&&!0===e.isPresenting}function c(){if(b()){var c=e.getEyeParameters("left"),f=c.renderWidth*p;c=c.renderHeight*p;D=a.getPixelRatio();a.getSize(x);a.setDrawingBufferSize(2*f,c,1);Q.start()}else d.enabled&&a.setDrawingBufferSize(x.width,x.height,D),Q.stop()}var d=this,e=null,f=null,g=null,h=[],k=new J,m=new J,p=1,q="stage";"undefined"!==typeof window&&"VRFrameData"in window&&(f=new window.VRFrameData,window.addEventListener("vrdisplaypresentchange",c,!1));var v=new J,l=new aa,r=new n,u=new S;u.bounds= +new ba(0,0,.5,1);u.layers.enable(1);var w=new S;w.bounds=new ba(.5,0,.5,1);w.layers.enable(2);var z=new Ec([u,w]);z.layers.enable(1);z.layers.enable(2);var x=new B,D,y=[];this.enabled=!1;this.getController=function(a){var b=h[a];void 0===b&&(b=new Sb,b.matrixAutoUpdate=!1,b.visible=!1,h[a]=b);return b};this.getDevice=function(){return e};this.setDevice=function(a){void 0!==a&&(e=a);Q.setContext(a)};this.setFramebufferScaleFactor=function(a){p=a};this.setFrameOfReferenceType=function(a){q=a};this.setPoseTarget= +function(a){void 0!==a&&(g=a)};this.getCamera=function(a){var c="stage"===q?1.6:0;if(!1===b())return a.position.set(0,c,0),a.rotation.set(0,0,0),a;e.depthNear=a.near;e.depthFar=a.far;e.getFrameData(f);if("stage"===q){var d=e.stageParameters;d?k.fromArray(d.sittingToStandingTransform):k.makeTranslation(0,c,0)}c=f.pose;d=null!==g?g:a;d.matrix.copy(k);d.matrix.decompose(d.position,d.quaternion,d.scale);null!==c.orientation&&(l.fromArray(c.orientation),d.quaternion.multiply(l));null!==c.position&&(l.setFromRotationMatrix(k), +r.fromArray(c.position),r.applyQuaternion(l),d.position.add(r));d.updateMatrixWorld();u.near=a.near;w.near=a.near;u.far=a.far;w.far=a.far;u.matrixWorldInverse.fromArray(f.leftViewMatrix);w.matrixWorldInverse.fromArray(f.rightViewMatrix);m.getInverse(k);"stage"===q&&(u.matrixWorldInverse.multiply(m),w.matrixWorldInverse.multiply(m));a=d.parent;null!==a&&(v.getInverse(a.matrixWorld),u.matrixWorldInverse.multiply(v),w.matrixWorldInverse.multiply(v));u.matrixWorld.getInverse(u.matrixWorldInverse);w.matrixWorld.getInverse(w.matrixWorldInverse); +u.projectionMatrix.fromArray(f.leftProjectionMatrix);w.projectionMatrix.fromArray(f.rightProjectionMatrix);pf(z,u,w);a=e.getLayers();a.length&&(a=a[0],null!==a.leftBounds&&4===a.leftBounds.length&&u.bounds.fromArray(a.leftBounds),null!==a.rightBounds&&4===a.rightBounds.length&&w.bounds.fromArray(a.rightBounds));a:for(a=0;af.matrixWorld.determinant();Z.setMaterial(e, +h);var k=l(a,c,e,f),m=!1;if(b!==d.id||U!==k.id||ta!==(!0===e.wireframe))b=d.id,U=k.id,ta=!0===e.wireframe,m=!0;f.morphTargetInfluences&&(xa.update(f,d,e,k),m=!0);h=d.index;var p=d.attributes.position;c=1;!0===e.wireframe&&(h=ua.getWireframeAttribute(d),c=2);a=Aa;if(null!==h){var q=ra.get(h);a=Ba;a.setIndex(q)}if(m){if(d&&d.isInstancedBufferGeometry&&!za.isWebGL2&&null===la.get("ANGLE_instanced_arrays"))console.error("THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays."); +else{Z.initAttributes();m=d.attributes;k=k.getAttributes();var v=e.defaultAttributeValues;for(A in k){var r=k[A];if(0<=r){var t=m[A];if(void 0!==t){var n=t.normalized,u=t.itemSize,w=ra.get(t);if(void 0!==w){var x=w.buffer,z=w.type;w=w.bytesPerElement;if(t.isInterleavedBufferAttribute){var y=t.data,D=y.stride;t=t.offset;y&&y.isInstancedInterleavedBuffer?(Z.enableAttributeAndDivisor(r,y.meshPerAttribute),void 0===d.maxInstancedCount&&(d.maxInstancedCount=y.meshPerAttribute*y.count)):Z.enableAttribute(r); +O.bindBuffer(34962,x);O.vertexAttribPointer(r,u,z,n,D*w,t*w)}else t.isInstancedBufferAttribute?(Z.enableAttributeAndDivisor(r,t.meshPerAttribute),void 0===d.maxInstancedCount&&(d.maxInstancedCount=t.meshPerAttribute*t.count)):Z.enableAttribute(r),O.bindBuffer(34962,x),O.vertexAttribPointer(r,u,z,n,0,0)}}else if(void 0!==v&&(n=v[A],void 0!==n))switch(n.length){case 2:O.vertexAttrib2fv(r,n);break;case 3:O.vertexAttrib3fv(r,n);break;case 4:O.vertexAttrib4fv(r,n);break;default:O.vertexAttrib1fv(r,n)}}}Z.disableUnusedAttributes()}null!== +h&&O.bindBuffer(34963,q.buffer)}q=Infinity;null!==h?q=h.count:void 0!==p&&(q=p.count);h=d.drawRange.start*c;p=null!==g?g.start*c:0;var A=Math.max(h,p);g=Math.max(0,Math.min(q,h+d.drawRange.count*c,p+(null!==g?g.count*c:Infinity))-1-A+1);if(0!==g){if(f.isMesh)if(!0===e.wireframe)Z.setLineWidth(e.wireframeLinewidth*(null===G?T:1)),a.setMode(1);else switch(f.drawMode){case 0:a.setMode(4);break;case 1:a.setMode(5);break;case 2:a.setMode(6)}else f.isLine?(e=e.linewidth,void 0===e&&(e=1),Z.setLineWidth(e* +(null===G?T:1)),f.isLineSegments?a.setMode(1):f.isLineLoop?a.setMode(2):a.setMode(3)):f.isPoints?a.setMode(0):f.isSprite&&a.setMode(4);d&&d.isInstancedBufferGeometry?0=za.maxTextures&&console.warn("THREE.WebGLRenderer: Trying to use "+a+" texture units while this GPU supports only "+za.maxTextures);ia+=1;return a};this.setTexture2D=function(){var a=!1;return function(b,c){b&&b.isWebGLRenderTarget&&(a||(console.warn("THREE.WebGLRenderer.setTexture2D: don't use render targets as textures. Use their .texture property instead."), +a=!0),b=b.texture);da.setTexture2D(b,c)}}();this.setTexture3D=function(){return function(a,b){da.setTexture3D(a,b)}}();this.setTexture=function(){var a=!1;return function(b,c){a||(console.warn("THREE.WebGLRenderer: .setTexture is deprecated, use setTexture2D instead."),a=!0);da.setTexture2D(b,c)}}();this.setTextureCube=function(){var a=!1;return function(b,c){b&&b.isWebGLRenderTargetCube&&(a||(console.warn("THREE.WebGLRenderer.setTextureCube: don't use cube render targets as textures. Use their .texture property instead."), +a=!0),b=b.texture);b&&b.isCubeTexture||Array.isArray(b.image)&&6===b.image.length?da.setTextureCube(b,c):da.setTextureCubeDynamic(b,c)}}();this.setFramebuffer=function(a){L=a};this.getRenderTarget=function(){return G};this.setRenderTarget=function(a,b,c){(G=a)&&void 0===Da.get(a).__webglFramebuffer&&da.setupRenderTarget(a);var d=L,e=!1;a?(d=Da.get(a).__webglFramebuffer,a.isWebGLRenderTargetCube?(d=d[b||0],e=!0):d=a.isWebGLMultisampleRenderTarget?Da.get(a).__webglMultisampledFramebuffer:d,V.copy(a.viewport), +W.copy(a.scissor),ca=a.scissorTest):(V.copy(ea).multiplyScalar(T),W.copy(aa).multiplyScalar(T),ca=pa);M!==d&&(O.bindFramebuffer(36160,d),M=d);Z.viewport(V);Z.scissor(W);Z.setScissorTest(ca);e&&(a=Da.get(a.texture),O.framebufferTexture2D(36160,36064,34069+b||0,a.__webglTexture,c||0))};this.readRenderTargetPixels=function(a,b,c,d,e,f){if(a&&a.isWebGLRenderTarget){var g=Da.get(a).__webglFramebuffer;if(g){var h=!1;g!==M&&(O.bindFramebuffer(36160,g),h=!0);try{var k=a.texture,m=k.format,p=k.type;1023!== +m&&ja.convert(m)!==O.getParameter(35739)?console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format."):1009===p||ja.convert(p)===O.getParameter(35738)||1015===p&&(za.isWebGL2||la.get("OES_texture_float")||la.get("WEBGL_color_buffer_float"))||1016===p&&(za.isWebGL2?la.get("EXT_color_buffer_float"):la.get("EXT_color_buffer_half_float"))?36053===O.checkFramebufferStatus(36160)?0<=b&&b<=a.width-d&&0<=c&&c<=a.height-e&&O.readPixels(b,c,d,e,ja.convert(m), +ja.convert(p),f):console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete."):console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.")}finally{h&&O.bindFramebuffer(36160,M)}}}else console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.")};this.copyFramebufferToTexture=function(a,b,c){var d=b.image.width,e=b.image.height, +f=ja.convert(b.format);this.setTexture2D(b,0);O.copyTexImage2D(3553,c||0,f,a.x,a.y,d,e,0)};this.copyTextureToTexture=function(a,b,c,d){var e=b.image.width,f=b.image.height,g=ja.convert(c.format),h=ja.convert(c.type);this.setTexture2D(c,0);b.isDataTexture?O.texSubImage2D(3553,d||0,a.x,a.y,e,f,g,h,b.image.data):O.texSubImage2D(3553,d||0,a.x,a.y,g,h,b.image)}}function yd(a,b){this.name="";this.color=new K(a);this.density=void 0!==b?b:2.5E-4}function zd(a,b,c){this.name="";this.color=new K(a);this.near= +void 0!==b?b:1;this.far=void 0!==c?c:1E3}function Ad(){E.call(this);this.type="Scene";this.overrideMaterial=this.fog=this.background=null;this.autoUpdate=!0}function ub(a,b){this.array=a;this.stride=b;this.count=void 0!==a?a.length/b:0;this.dynamic=!1;this.updateRange={offset:0,count:-1};this.version=0}function Fc(a,b,c,d){this.data=a;this.itemSize=b;this.offset=c;this.normalized=!0===d}function jb(a){M.call(this);this.type="SpriteMaterial";this.color=new K(16777215);this.map=null;this.rotation=0; +this.sizeAttenuation=!0;this.lights=!1;this.transparent=!0;this.setValues(a)}function Gc(a){E.call(this);this.type="Sprite";if(void 0===Tb){Tb=new C;var b=new Float32Array([-.5,-.5,0,0,0,.5,-.5,0,1,0,.5,.5,0,1,1,-.5,.5,0,0,1]);b=new ub(b,5);Tb.setIndex([0,1,2,0,2,3]);Tb.addAttribute("position",new Fc(b,3,0,!1));Tb.addAttribute("uv",new Fc(b,2,3,!1))}this.geometry=Tb;this.material=void 0!==a?a:new jb;this.center=new B(.5,.5)}function Hc(){E.call(this);this.type="LOD";Object.defineProperties(this,{levels:{enumerable:!0, +value:[]}})}function Ic(a,b){a&&a.isGeometry&&console.error("THREE.SkinnedMesh no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.");va.call(this,a,b);this.type="SkinnedMesh";this.bindMode="attached";this.bindMatrix=new J;this.bindMatrixInverse=new J}function Bd(a,b){a=a||[];this.bones=a.slice(0);this.boneMatrices=new Float32Array(16*this.bones.length);if(void 0===b)this.calculateInverses();else if(this.bones.length===b.length)this.boneInverses=b.slice(0);else for(console.warn("THREE.Skeleton boneInverses is the wrong length."), +this.boneInverses=[],a=0,b=this.bones.length;ac;c++){var q=p[h[c]];var l=p[h[(c+1)%3]];f[0]=Math.min(q,l);f[1]=Math.max(q,l);q=f[0]+ +","+f[1];void 0===g[q]&&(g[q]={index1:f[0],index2:f[1]})}}for(q in g)m=g[q],h=a.vertices[m.index1],b.push(h.x,h.y,h.z),h=a.vertices[m.index2],b.push(h.x,h.y,h.z)}else if(a&&a.isBufferGeometry)if(h=new n,null!==a.index){k=a.attributes.position;p=a.index;var t=a.groups;0===t.length&&(t=[{start:0,count:p.count,materialIndex:0}]);a=0;for(e=t.length;ac;c++)q=p.getX(m+c),l=p.getX(m+(c+1)%3),f[0]=Math.min(q,l),f[1]=Math.max(q,l),q=f[0]+","+ +f[1],void 0===g[q]&&(g[q]={index1:f[0],index2:f[1]});for(q in g)m=g[q],h.fromBufferAttribute(k,m.index1),b.push(h.x,h.y,h.z),h.fromBufferAttribute(k,m.index2),b.push(h.x,h.y,h.z)}else for(k=a.attributes.position,m=0,d=k.count/3;mc;c++)g=3*m+c,h.fromBufferAttribute(k,g),b.push(h.x,h.y,h.z),g=3*m+(c+1)%3,h.fromBufferAttribute(k,g),b.push(h.x,h.y,h.z);this.addAttribute("position",new F(b,3))}function Lc(a,b,c){G.call(this);this.type="ParametricGeometry";this.parameters={func:a,slices:b, +stacks:c};this.fromBufferGeometry(new Xb(a,b,c));this.mergeVertices()}function Xb(a,b,c){C.call(this);this.type="ParametricBufferGeometry";this.parameters={func:a,slices:b,stacks:c};var d=[],e=[],f=[],g=[],h=new n,k=new n,m=new n,p=new n,q=new n,l,t;3>a.length&&console.error("THREE.ParametricGeometry: Function must now modify a Vector3 as third parameter.");var r=b+1;for(l=0;l<=c;l++){var u=l/c;for(t=0;t<=b;t++){var w=t/b;a(w,u,k);e.push(k.x,k.y,k.z);0<=w-1E-5?(a(w-1E-5,u,m),p.subVectors(k,m)):(a(w+ +1E-5,u,m),p.subVectors(m,k));0<=u-1E-5?(a(w,u-1E-5,m),q.subVectors(k,m)):(a(w,u+1E-5,m),q.subVectors(m,k));h.crossVectors(p,q).normalize();f.push(h.x,h.y,h.z);g.push(w,u)}}for(l=0;ld&&1===a.x&&(k[b]=a.x-1);0===c.x&&0===c.z&&(k[b]=d/2/Math.PI+.5)}C.call(this);this.type="PolyhedronBufferGeometry";this.parameters={vertices:a,indices:b,radius:c,detail:d};c=c||1;d=d||0;var h=[],k=[];(function(a){for(var c=new n,d=new n,g=new n,h=0;he&&(.2>b&&(k[a+0]+=1),.2>c&&(k[a+2]+=1),.2>d&&(k[a+4]+=1))})();this.addAttribute("position",new F(h,3));this.addAttribute("normal",new F(h.slice(),3));this.addAttribute("uv",new F(k,2));0===d?this.computeVertexNormals():this.normalizeNormals()}function Nc(a,b){G.call(this);this.type="TetrahedronGeometry";this.parameters={radius:a,detail:b};this.fromBufferGeometry(new Yb(a,b));this.mergeVertices()} +function Yb(a,b){Aa.call(this,[1,1,1,-1,-1,1,-1,1,-1,1,-1,-1],[2,1,0,0,3,2,1,3,0,2,3,1],a,b);this.type="TetrahedronBufferGeometry";this.parameters={radius:a,detail:b}}function Oc(a,b){G.call(this);this.type="OctahedronGeometry";this.parameters={radius:a,detail:b};this.fromBufferGeometry(new vb(a,b));this.mergeVertices()}function vb(a,b){Aa.call(this,[1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1],[0,2,4,0,4,3,0,3,5,0,5,2,1,2,5,1,5,3,1,3,4,1,4,2],a,b);this.type="OctahedronBufferGeometry";this.parameters= +{radius:a,detail:b}}function Pc(a,b){G.call(this);this.type="IcosahedronGeometry";this.parameters={radius:a,detail:b};this.fromBufferGeometry(new Zb(a,b));this.mergeVertices()}function Zb(a,b){var c=(1+Math.sqrt(5))/2;Aa.call(this,[-1,c,0,1,c,0,-1,-c,0,1,-c,0,0,-1,c,0,1,c,0,-1,-c,0,1,-c,c,0,-1,c,0,1,-c,0,-1,-c,0,1],[0,11,5,0,5,1,0,1,7,0,7,10,0,10,11,1,5,9,5,11,4,11,10,2,10,7,6,7,1,8,3,9,4,3,4,2,3,2,6,3,6,8,3,8,9,4,9,5,2,4,11,6,2,10,8,6,7,9,8,1],a,b);this.type="IcosahedronBufferGeometry";this.parameters= +{radius:a,detail:b}}function Qc(a,b){G.call(this);this.type="DodecahedronGeometry";this.parameters={radius:a,detail:b};this.fromBufferGeometry(new $b(a,b));this.mergeVertices()}function $b(a,b){var c=(1+Math.sqrt(5))/2,d=1/c;Aa.call(this,[-1,-1,-1,-1,-1,1,-1,1,-1,-1,1,1,1,-1,-1,1,-1,1,1,1,-1,1,1,1,0,-d,-c,0,-d,c,0,d,-c,0,d,c,-d,-c,0,-d,c,0,d,-c,0,d,c,0,-c,0,-d,c,0,-d,-c,0,d,c,0,d],[3,11,7,3,7,15,3,15,13,7,19,17,7,17,6,7,6,15,17,4,8,17,8,10,17,10,6,8,0,16,8,16,2,8,2,10,0,12,1,0,1,18,0,18,16,6,10,2, +6,2,13,6,13,15,2,16,18,2,18,3,2,3,13,18,1,9,18,9,11,18,11,3,4,14,12,4,12,0,4,0,8,11,9,5,11,5,19,11,19,7,19,5,14,19,14,4,19,4,17,1,12,14,1,14,5,1,5,9],a,b);this.type="DodecahedronBufferGeometry";this.parameters={radius:a,detail:b}}function Rc(a,b,c,d,e,f){G.call(this);this.type="TubeGeometry";this.parameters={path:a,tubularSegments:b,radius:c,radialSegments:d,closed:e};void 0!==f&&console.warn("THREE.TubeGeometry: taper has been removed.");a=new wb(a,b,c,d,e);this.tangents=a.tangents;this.normals= +a.normals;this.binormals=a.binormals;this.fromBufferGeometry(a);this.mergeVertices()}function wb(a,b,c,d,e){function f(e){p=a.getPointAt(e/b,p);var f=g.normals[e];e=g.binormals[e];for(l=0;l<=d;l++){var m=l/d*Math.PI*2,q=Math.sin(m);m=-Math.cos(m);k.x=m*f.x+q*e.x;k.y=m*f.y+q*e.y;k.z=m*f.z+q*e.z;k.normalize();r.push(k.x,k.y,k.z);h.x=p.x+c*k.x;h.y=p.y+c*k.y;h.z=p.z+c*k.z;t.push(h.x,h.y,h.z)}}C.call(this);this.type="TubeBufferGeometry";this.parameters={path:a,tubularSegments:b,radius:c,radialSegments:d, +closed:e};b=b||64;c=c||1;d=d||8;e=e||!1;var g=a.computeFrenetFrames(b,e);this.tangents=g.tangents;this.normals=g.normals;this.binormals=g.binormals;var h=new n,k=new n,m=new B,p=new n,q,l,t=[],r=[],u=[],w=[];for(q=0;q=b;e-=d)f=vf(e,a[e],a[e+1],f);f&&xb(f,f.next)&&(Uc(f),f=f.next);return f}function Vc(a,b){if(!a)return a;b||(b=a);do{var c=!1;if(a.steiner||!xb(a,a.next)&&0!==qa(a.prev,a,a.next))a=a.next;else{Uc(a);a=b=a.prev;if(a===a.next)break;c=!0}}while(c||a!==b);return b}function Wc(a,b,c,d,e,f,g){if(a){if(!g&&f){var h=a,k=h;do null===k.z&&(k.z=ke(k.x,k.y,d,e,f)),k.prevZ=k.prev,k=k.nextZ= +k.next;while(k!==h);k.prevZ.nextZ=null;k.prevZ=null;h=k;var m,p,q,l,t=1;do{k=h;var r=h=null;for(p=0;k;){p++;var n=k;for(m=q=0;mq.x?p.x>t.x?p.x:t.x:q.x>t.x?q.x:t.x,D=p.y>q.y?p.y>t.y?p.y:t.y:q.y> +t.y?q.y:t.y;m=ke(p.x=m;){if(w!==r.prev&&w!==r.next&&Dd(p.x,p.y,q.x,q.y,t.x,t.y,w.x,w.y)&&0<=qa(w.prev,w,w.next)){r=!1;break a}w=w.prevZ}r=!0}}else a:if(r=a,p=r.prev,q=r,t=r.next,0<=qa(p,q,t))r=!1;else{for(m=r.next.next;m!==r.prev;){if(Dd(p.x,p.y, +q.x,q.y,t.x,t.y,m.x,m.y)&&0<=qa(m.prev,m,m.next)){r=!1;break a}m=m.next}r=!0}if(r)b.push(k.i/c),b.push(a.i/c),b.push(n.i/c),Uc(a),h=a=n.next;else if(a=n,a===h){if(!g)Wc(Vc(a),b,c,d,e,f,1);else if(1===g){g=b;h=c;k=a;do n=k.prev,r=k.next.next,!xb(n,r)&&wf(n,k,k.next,r)&&Xc(n,r)&&Xc(r,n)&&(g.push(n.i/h),g.push(k.i/h),g.push(r.i/h),Uc(k),Uc(k.next),k=a=r),k=k.next;while(k!==a);a=k;Wc(a,b,c,d,e,f,2)}else if(2===g)a:{g=a;do{for(h=g.next.next;h!==g.prev;){if(k=g.i!==h.i){k=g;n=h;if(r=k.next.i!==n.i&&k.prev.i!== +n.i){b:{r=k;do{if(r.i!==k.i&&r.next.i!==k.i&&r.i!==n.i&&r.next.i!==n.i&&wf(r,r.next,k,n)){r=!0;break b}r=r.next}while(r!==k);r=!1}r=!r}if(r=r&&Xc(k,n)&&Xc(n,k)){r=k;p=!1;q=(k.x+n.x)/2;n=(k.y+n.y)/2;do r.y>n!==r.next.y>n&&r.next.y!==r.y&&q<(r.next.x-r.x)*(n-r.y)/(r.next.y-r.y)+r.x&&(p=!p),r=r.next;while(r!==k);r=p}k=r}if(k){a=xf(g,h);g=Vc(g,g.next);a=Vc(a,a.next);Wc(g,b,c,d,e,f);Wc(a,b,c,d,e,f);break a}h=h.next}g=g.next}while(g!==a)}break}}}}function ah(a,b){return a.x-b.x}function bh(a,b){var c=b, +d=a.x,e=a.y,f=-Infinity;do{if(e<=c.y&&e>=c.next.y&&c.next.y!==c.y){var g=c.x+(e-c.y)*(c.next.x-c.x)/(c.next.y-c.y);if(g<=d&&g>f){f=g;if(g===d){if(e===c.y)return c;if(e===c.next.y)return c.next}var h=c.x=c.x&&c.x>=g&&d!==c.x&&Dd(eh.x)&&Xc(c,a)&&(h=c,m=p)}c=c.next}return h}function ke(a, +b,c,d,e){a=32767*(a-c)*e;b=32767*(b-d)*e;a=(a|a<<8)&16711935;a=(a|a<<4)&252645135;a=(a|a<<2)&858993459;b=(b|b<<8)&16711935;b=(b|b<<4)&252645135;b=(b|b<<2)&858993459;return(a|a<<1)&1431655765|((b|b<<1)&1431655765)<<1}function ch(a){var b=a,c=a;do b.xqa(a.prev,a,a.next)?0<=qa(a,b,a.next)&&0<=qa(a,a.prev,b):0>qa(a,b,a.prev)||0>qa(a,a.next,b)}function xf(a,b){var c=new le(a.i,a.x,a.y),d=new le(b.i,b.x,b.y),e=a.next,f=b.prev;a.next=b;b.prev=a;c.next=e;e.prev=c;d.next=c;c.prev=d;f.next=d;d.prev=f;return d}function vf(a,b,c,d){a=new le(a,b,c);d?(a.next=d.next,a.prev=d,d.next.prev=a,d.next=a): +(a.prev=a,a.next=a);return a}function Uc(a){a.next.prev=a.prev;a.prev.next=a.next;a.prevZ&&(a.prevZ.nextZ=a.nextZ);a.nextZ&&(a.nextZ.prevZ=a.prevZ)}function le(a,b,c){this.i=a;this.x=b;this.y=c;this.nextZ=this.prevZ=this.z=this.next=this.prev=null;this.steiner=!1}function yf(a){var b=a.length;2Number.EPSILON){var k=Math.sqrt(h),m=Math.sqrt(f*f+g*g);h=b.x-e/k;b=b.y+d/k;g=((c.x-g/m-h)*g-(c.y+f/m-b)*f)/(d*g-e*f);f=h+d*g-a.x;d=b+e*g-a.y;e=f*f+d*d;if(2>=e)return new B(f,d);e=Math.sqrt(e/2)}else a=!1,d>Number.EPSILON? +f>Number.EPSILON&&(a=!0):d<-Number.EPSILON?f<-Number.EPSILON&&(a=!0):Math.sign(e)===Math.sign(g)&&(a=!0),a?(f=-e,e=Math.sqrt(h)):(f=d,d=e,e=Math.sqrt(h/2));return new B(f/e,d/e)}function h(a,b){for(N=a.length;0<=--N;){var c=N;var f=N-1;0>f&&(f=a.length-1);var g,h=x+2*A;for(g=0;gp;p++){var l=m[f[p]];var n=m[f[(p+1)%3]];d[0]=Math.min(l,n);d[1]=Math.max(l,n);l=d[0]+","+d[1];void 0===e[l]?e[l]={index1:d[0],index2:d[1],face1:h,face2:void 0}:e[l].face2=h}for(l in e)if(d=e[l],void 0===d.face2||g[d.face1].normal.dot(g[d.face2].normal)<=b)f=a[d.index1],c.push(f.x,f.y,f.z),f=a[d.index2],c.push(f.x,f.y,f.z);this.addAttribute("position",new F(c,3))}function Cb(a,b,c,d,e,f,g,h){G.call(this);this.type="CylinderGeometry";this.parameters={radiusTop:a, +radiusBottom:b,height:c,radialSegments:d,heightSegments:e,openEnded:f,thetaStart:g,thetaLength:h};this.fromBufferGeometry(new bb(a,b,c,d,e,f,g,h));this.mergeVertices()}function bb(a,b,c,d,e,f,g,h){function k(c){var e,f=new B,k=new n,q=0,u=!0===c?a:b,x=!0===c?1:-1;var C=r;for(e=1;e<=d;e++)l.push(0,w*x,0),v.push(0,x,0),t.push(.5,.5),r++;var E=r;for(e=0;e<=d;e++){var F=e/d*h+g,H=Math.cos(F);F=Math.sin(F);k.x=u*F;k.y=w*x;k.z=u*H;l.push(k.x,k.y,k.z);v.push(0,x,0);f.x=.5*H+.5;f.y=.5*F*x+.5;t.push(f.x,f.y); +r++}for(e=0;ethis.duration&&this.resetDuration()}function eh(a){switch(a.toLowerCase()){case "scalar":case "double":case "float":case "number":case "integer":return ic;case "vector":case "vector2":case "vector3":case "vector4":return jc;case "color":return Hd;case "quaternion":return fd;case "bool":case "boolean":return Gd;case "string":return Jd}throw Error("THREE.KeyframeTrack: Unsupported typeName: "+ +a);}function fh(a){if(void 0===a.type)throw Error("THREE.KeyframeTrack: track type undefined, can not parse");var b=eh(a.type);if(void 0===a.times){var c=[],d=[];sa.flattenJSON(a.keys,c,d,"value");a.times=c;a.values=d}return void 0!==b.parse?b.parse(a):new b(a.name,a.times,a.values,a.interpolation)}function me(a,b,c){var d=this,e=!1,f=0,g=0,h=void 0;this.onStart=void 0;this.onLoad=a;this.onProgress=b;this.onError=c;this.itemStart=function(a){g++;if(!1===e&&void 0!==d.onStart)d.onStart(a,f,g);e=!0}; +this.itemEnd=function(a){f++;if(void 0!==d.onProgress)d.onProgress(a,f,g);if(f===g&&(e=!1,void 0!==d.onLoad))d.onLoad()};this.itemError=function(a){if(void 0!==d.onError)d.onError(a)};this.resolveURL=function(a){return h?h(a):a};this.setURLModifier=function(a){h=a;return this}}function Ka(a){this.manager=void 0!==a?a:Ba}function Cf(a){this.manager=void 0!==a?a:Ba}function Df(a){this.manager=void 0!==a?a:Ba;this._parser=null}function ne(a){this.manager=void 0!==a?a:Ba;this._parser=null}function gd(a){this.manager= +void 0!==a?a:Ba}function oe(a){this.manager=void 0!==a?a:Ba}function Kd(a){this.manager=void 0!==a?a:Ba}function L(){this.type="Curve";this.arcLengthDivisions=200}function Ea(a,b,c,d,e,f,g,h){L.call(this);this.type="EllipseCurve";this.aX=a||0;this.aY=b||0;this.xRadius=c||1;this.yRadius=d||1;this.aStartAngle=e||0;this.aEndAngle=f||2*Math.PI;this.aClockwise=g||!1;this.aRotation=h||0}function kc(a,b,c,d,e,f){Ea.call(this,a,b,c,c,d,e,f);this.type="ArcCurve"}function pe(){var a=0,b=0,c=0,d=0;return{initCatmullRom:function(e, +f,g,h,k){e=k*(g-e);h=k*(h-f);a=f;b=e;c=-3*f+3*g-2*e-h;d=2*f-2*g+e+h},initNonuniformCatmullRom:function(e,f,g,h,k,m,p){e=((f-e)/k-(g-e)/(k+m)+(g-f)/m)*m;h=((g-f)/m-(h-f)/(m+p)+(h-g)/p)*m;a=f;b=e;c=-3*f+3*g-2*e-h;d=2*f-2*g+e+h},calc:function(e){var f=e*e;return a+b*e+c*f+d*f*e}}}function oa(a,b,c,d){L.call(this);this.type="CatmullRomCurve3";this.points=a||[];this.closed=b||!1;this.curveType=c||"centripetal";this.tension=d||.5}function Ef(a,b,c,d,e){b=.5*(d-b);e=.5*(e-c);var f=a*a;return(2*c-2*d+b+e)* +a*f+(-3*c+3*d-2*b-e)*f+b*a+c}function hd(a,b,c,d){var e=1-a;return e*e*b+2*(1-a)*a*c+a*a*d}function id(a,b,c,d,e){var f=1-a,g=1-a;return f*f*f*b+3*g*g*a*c+3*(1-a)*a*a*d+a*a*a*e}function La(a,b,c,d){L.call(this);this.type="CubicBezierCurve";this.v0=a||new B;this.v1=b||new B;this.v2=c||new B;this.v3=d||new B}function Xa(a,b,c,d){L.call(this);this.type="CubicBezierCurve3";this.v0=a||new n;this.v1=b||new n;this.v2=c||new n;this.v3=d||new n}function ja(a,b){L.call(this);this.type="LineCurve";this.v1=a|| +new B;this.v2=b||new B}function Ma(a,b){L.call(this);this.type="LineCurve3";this.v1=a||new n;this.v2=b||new n}function Na(a,b,c){L.call(this);this.type="QuadraticBezierCurve";this.v0=a||new B;this.v1=b||new B;this.v2=c||new B}function Ya(a,b,c){L.call(this);this.type="QuadraticBezierCurve3";this.v0=a||new n;this.v1=b||new n;this.v2=c||new n}function Oa(a){L.call(this);this.type="SplineCurve";this.points=a||[]}function cb(){L.call(this);this.type="CurvePath";this.curves=[];this.autoClose=!1}function Pa(a){cb.call(this); +this.type="Path";this.currentPoint=new B;a&&this.setFromPoints(a)}function kb(a){Pa.call(this,a);this.uuid=H.generateUUID();this.type="Shape";this.holes=[]}function ia(a,b){E.call(this);this.type="Light";this.color=new K(a);this.intensity=void 0!==b?b:1;this.receiveShadow=void 0}function Ld(a,b,c){ia.call(this,a,c);this.type="HemisphereLight";this.castShadow=void 0;this.position.copy(E.DefaultUp);this.updateMatrix();this.groundColor=new K(b)}function Kb(a){this.camera=a;this.bias=0;this.radius=1; +this.mapSize=new B(512,512);this.map=null;this.matrix=new J}function Md(){Kb.call(this,new S(50,1,.5,500))}function Nd(a,b,c,d,e,f){ia.call(this,a,b);this.type="SpotLight";this.position.copy(E.DefaultUp);this.updateMatrix();this.target=new E;Object.defineProperty(this,"power",{get:function(){return this.intensity*Math.PI},set:function(a){this.intensity=a/Math.PI}});this.distance=void 0!==c?c:0;this.angle=void 0!==d?d:Math.PI/3;this.penumbra=void 0!==e?e:0;this.decay=void 0!==f?f:1;this.shadow=new Md} +function Od(a,b,c,d){ia.call(this,a,b);this.type="PointLight";Object.defineProperty(this,"power",{get:function(){return 4*this.intensity*Math.PI},set:function(a){this.intensity=a/(4*Math.PI)}});this.distance=void 0!==c?c:0;this.decay=void 0!==d?d:1;this.shadow=new Kb(new S(90,1,.5,500))}function jd(a,b,c,d,e,f){Ua.call(this);this.type="OrthographicCamera";this.zoom=1;this.view=null;this.left=void 0!==a?a:-1;this.right=void 0!==b?b:1;this.top=void 0!==c?c:1;this.bottom=void 0!==d?d:-1;this.near=void 0!== +e?e:.1;this.far=void 0!==f?f:2E3;this.updateProjectionMatrix()}function Pd(){Kb.call(this,new jd(-5,5,5,-5,.5,500))}function Qd(a,b){ia.call(this,a,b);this.type="DirectionalLight";this.position.copy(E.DefaultUp);this.updateMatrix();this.target=new E;this.shadow=new Pd}function Rd(a,b){ia.call(this,a,b);this.type="AmbientLight";this.castShadow=void 0}function Sd(a,b,c,d){ia.call(this,a,b);this.type="RectAreaLight";this.width=void 0!==c?c:10;this.height=void 0!==d?d:10}function Td(a){this.manager=void 0!== +a?a:Ba;this.textures={}}function qe(a){this.manager=void 0!==a?a:Ba}function re(a){this.manager=void 0!==a?a:Ba;this.resourcePath=""}function se(a){"undefined"===typeof createImageBitmap&&console.warn("THREE.ImageBitmapLoader: createImageBitmap() not supported.");"undefined"===typeof fetch&&console.warn("THREE.ImageBitmapLoader: fetch() not supported.");this.manager=void 0!==a?a:Ba;this.options=void 0}function te(){this.type="ShapePath";this.color=new K;this.subPaths=[];this.currentPath=null}function ue(a){this.type= +"Font";this.data=a}function Ff(a){this.manager=void 0!==a?a:Ba}function kd(){}function ve(a){this.manager=void 0!==a?a:Ba}function Gf(){this.type="StereoCamera";this.aspect=1;this.eyeSep=.064;this.cameraL=new S;this.cameraL.layers.enable(1);this.cameraL.matrixAutoUpdate=!1;this.cameraR=new S;this.cameraR.layers.enable(2);this.cameraR.matrixAutoUpdate=!1}function ld(a,b,c,d){E.call(this);this.type="CubeCamera";var e=new S(90,1,a,b);e.up.set(0,-1,0);e.lookAt(new n(1,0,0));this.add(e);var f=new S(90, +1,a,b);f.up.set(0,-1,0);f.lookAt(new n(-1,0,0));this.add(f);var g=new S(90,1,a,b);g.up.set(0,0,1);g.lookAt(new n(0,1,0));this.add(g);var h=new S(90,1,a,b);h.up.set(0,0,-1);h.lookAt(new n(0,-1,0));this.add(h);var k=new S(90,1,a,b);k.up.set(0,-1,0);k.lookAt(new n(0,0,1));this.add(k);var m=new S(90,1,a,b);m.up.set(0,-1,0);m.lookAt(new n(0,0,-1));this.add(m);d=d||{format:1022,magFilter:1006,minFilter:1006};this.renderTarget=new mb(c,c,d);this.renderTarget.texture.name="CubeCamera";this.update=function(a, +b){null===this.parent&&this.updateMatrixWorld();var c=a.getRenderTarget(),d=this.renderTarget,p=d.texture.generateMipmaps;d.texture.generateMipmaps=!1;a.setRenderTarget(d,0);a.render(b,e);a.setRenderTarget(d,1);a.render(b,f);a.setRenderTarget(d,2);a.render(b,g);a.setRenderTarget(d,3);a.render(b,h);a.setRenderTarget(d,4);a.render(b,k);d.texture.generateMipmaps=p;a.setRenderTarget(d,5);a.render(b,m);a.setRenderTarget(c)};this.clear=function(a,b,c,d){for(var e=a.getRenderTarget(),f=this.renderTarget, +g=0;6>g;g++)a.setRenderTarget(f,g),a.clear(b,c,d);a.setRenderTarget(e)}}function we(a){this.autoStart=void 0!==a?a:!0;this.elapsedTime=this.oldTime=this.startTime=0;this.running=!1}function xe(){E.call(this);this.type="AudioListener";this.context=ye.getContext();this.gain=this.context.createGain();this.gain.connect(this.context.destination);this.filter=null;this.timeDelta=0}function lc(a){E.call(this);this.type="Audio";this.listener=a;this.context=a.context;this.gain=this.context.createGain();this.gain.connect(a.getInput()); +this.autoplay=!1;this.buffer=null;this.detune=0;this.loop=!1;this.offset=this.startTime=0;this.playbackRate=1;this.isPlaying=!1;this.hasPlaybackControl=!0;this.sourceType="empty";this.filters=[]}function ze(a){lc.call(this,a);this.panner=this.context.createPanner();this.panner.connect(this.gain)}function Ae(a,b){this.analyser=a.context.createAnalyser();this.analyser.fftSize=void 0!==b?b:2048;this.data=new Uint8Array(this.analyser.frequencyBinCount);a.getOutput().connect(this.analyser)}function Be(a, +b,c){this.binding=a;this.valueSize=c;a=Float64Array;switch(b){case "quaternion":b=this._slerp;break;case "string":case "bool":a=Array;b=this._select;break;default:b=this._lerp}this.buffer=new a(4*c);this._mixBufferRegion=b;this.referenceCount=this.useCount=this.cumulativeWeight=0}function Hf(a,b,c){c=c||ma.parseTrackName(b);this._targetGroup=a;this._bindings=a.subscribe_(b,c)}function ma(a,b,c){this.path=b;this.parsedPath=c||ma.parseTrackName(b);this.node=ma.findNode(a,this.parsedPath.nodeName)|| +a;this.rootNode=a}function If(){this.uuid=H.generateUUID();this._objects=Array.prototype.slice.call(arguments);this.nCachedObjects_=0;var a={};this._indicesByUUID=a;for(var b=0,c=arguments.length;b!==c;++b)a[arguments[b].uuid]=b;this._paths=[];this._parsedPaths=[];this._bindings=[];this._bindingsIndicesByPath={};var d=this;this.stats={objects:{get total(){return d._objects.length},get inUse(){return this.total-d.nCachedObjects_}},get bindingsPerObject(){return d._bindings.length}}}function Jf(a,b, +c){this._mixer=a;this._clip=b;this._localRoot=c||null;a=b.tracks;b=a.length;c=Array(b);for(var d={endingStart:2400,endingEnd:2400},e=0;e!==b;++e){var f=a[e].createInterpolant(null);c[e]=f;f.settings=d}this._interpolantSettings=d;this._interpolants=c;this._propertyBindings=Array(b);this._weightInterpolant=this._timeScaleInterpolant=this._byClipCacheIndex=this._cacheIndex=null;this.loop=2201;this._loopCount=-1;this._startTime=null;this.time=0;this._effectiveWeight=this.weight=this._effectiveTimeScale= +this.timeScale=1;this.repetitions=Infinity;this.paused=!1;this.enabled=!0;this.clampWhenFinished=!1;this.zeroSlopeAtEnd=this.zeroSlopeAtStart=!0}function Ce(a){this._root=a;this._initMemoryManager();this.time=this._accuIndex=0;this.timeScale=1}function Ud(a,b){"string"===typeof a&&(console.warn("THREE.Uniform: Type parameter is no longer needed."),a=b);this.value=a}function De(){C.call(this);this.type="InstancedBufferGeometry";this.maxInstancedCount=void 0}function Ee(a,b,c){ub.call(this,a,b);this.meshPerAttribute= +c||1}function Fe(a,b,c,d){"number"===typeof c&&(d=c,c=!1,console.error("THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument."));P.call(this,a,b,c);this.meshPerAttribute=d||1}function Kf(a,b,c,d){this.ray=new tb(a,b);this.near=c||0;this.far=d||Infinity;this.params={Mesh:{},Line:{},LOD:{},Points:{threshold:1},Sprite:{}};Object.defineProperties(this.params,{PointCloud:{get:function(){console.warn("THREE.Raycaster: params.PointCloud has been renamed to params.Points."); +return this.Points}}})}function Lf(a,b){return a.distance-b.distance}function Ge(a,b,c,d){if(!1!==a.visible&&(a.raycast(b,c),!0===d)){a=a.children;d=0;for(var e=a.length;dc;c++,d++){var e=c/32*Math.PI*2,f=d/32*Math.PI*2;b.push(Math.cos(e),Math.sin(e),1,Math.cos(f),Math.sin(f),1)}a.addAttribute("position",new F(b,3));b=new R({fog:!1});this.cone=new W(a,b);this.add(this.cone);this.update()}function Of(a){var b=[];a&&a.isBone&&b.push(a);for(var c= +0;ca?-1:0b;b++)a[b]=(16>b?"0":"")+b.toString(16);return function(){var b=4294967295*Math.random()|0,d=4294967295*Math.random()|0,e=4294967295*Math.random()|0,f=4294967295*Math.random()|0;return(a[b&255]+a[b>>8&255]+a[b>>16&255]+a[b>>24&255]+"-"+a[d&255]+a[d>>8&255]+"-"+a[d>> +16&15|64]+a[d>>24&255]+"-"+a[e&63|128]+a[e>>8&255]+"-"+a[e>>16&255]+a[e>>24&255]+a[f&255]+a[f>>8&255]+a[f>>16&255]+a[f>>24&255]).toUpperCase()}}(),clamp:function(a,b,c){return Math.max(b,Math.min(c,a))},euclideanModulo:function(a,b){return(a%b+b)%b},mapLinear:function(a,b,c,d,e){return d+(a-b)*(e-d)/(c-b)},lerp:function(a,b,c){return(1-c)*a+c*b},smoothstep:function(a,b,c){if(a<=b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*(3-2*a)},smootherstep:function(a,b,c){if(a<=b)return 0;if(a>=c)return 1; +a=(a-b)/(c-b);return a*a*a*(a*(6*a-15)+10)},randInt:function(a,b){return a+Math.floor(Math.random()*(b-a+1))},randFloat:function(a,b){return a+Math.random()*(b-a)},randFloatSpread:function(a){return a*(.5-Math.random())},degToRad:function(a){return a*H.DEG2RAD},radToDeg:function(a){return a*H.RAD2DEG},isPowerOfTwo:function(a){return 0===(a&a-1)&&0!==a},ceilPowerOfTwo:function(a){return Math.pow(2,Math.ceil(Math.log(a)/Math.LN2))},floorPowerOfTwo:function(a){return Math.pow(2,Math.floor(Math.log(a)/ +Math.LN2))}};Object.defineProperties(B.prototype,{width:{get:function(){return this.x},set:function(a){this.x=a}},height:{get:function(){return this.y},set:function(a){this.y=a}}});Object.assign(B.prototype,{isVector2:!0,set:function(a,b){this.x=a;this.y=b;return this},setScalar:function(a){this.y=this.x=a;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;default:throw Error("index is out of range: "+ +a);}return this},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;default:throw Error("index is out of range: "+a);}},clone:function(){return new this.constructor(this.x,this.y)},copy:function(a){this.x=a.x;this.y=a.y;return this},add:function(a,b){if(void 0!==b)return console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;return this},addScalar:function(a){this.x+=a;this.y+=a;return this}, +addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;return this},addScaledVector:function(a,b){this.x+=a.x*b;this.y+=a.y*b;return this},sub:function(a,b){if(void 0!==b)return console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(a,b);this.x-=a.x;this.y-=a.y;return this},subScalar:function(a){this.x-=a;this.y-=a;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;return this},multiply:function(a){this.x*=a.x;this.y*= +a.y;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;return this},divide:function(a){this.x/=a.x;this.y/=a.y;return this},divideScalar:function(a){return this.multiplyScalar(1/a)},applyMatrix3:function(a){var b=this.x,c=this.y;a=a.elements;this.x=a[0]*b+a[3]*c+a[6];this.y=a[1]*b+a[4]*c+a[7];return this},min:function(a){this.x=Math.min(this.x,a.x);this.y=Math.min(this.y,a.y);return this},max:function(a){this.x=Math.max(this.x,a.x);this.y=Math.max(this.y,a.y);return this},clamp:function(a, +b){this.x=Math.max(a.x,Math.min(b.x,this.x));this.y=Math.max(a.y,Math.min(b.y,this.y));return this},clampScalar:function(){var a=new B,b=new B;return function(c,d){a.set(c,c);b.set(d,d);return this.clamp(a,b)}}(),clampLength:function(a,b){var c=this.length();return this.divideScalar(c||1).multiplyScalar(Math.max(a,Math.min(b,c)))},floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);return this},round:function(){this.x= +Math.round(this.x);this.y=Math.round(this.y);return this},roundToZero:function(){this.x=0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):Math.floor(this.y);return this},negate:function(){this.x=-this.x;this.y=-this.y;return this},dot:function(a){return this.x*a.x+this.y*a.y},cross:function(a){return this.x*a.y-this.y*a.x},lengthSq:function(){return this.x*this.x+this.y*this.y},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},manhattanLength:function(){return Math.abs(this.x)+ +Math.abs(this.y)},normalize:function(){return this.divideScalar(this.length()||1)},angle:function(){var a=Math.atan2(this.y,this.x);0>a&&(a+=2*Math.PI);return a},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b=this.x-a.x;a=this.y-a.y;return b*b+a*a},manhattanDistanceTo:function(a){return Math.abs(this.x-a.x)+Math.abs(this.y-a.y)},setLength:function(a){return this.normalize().multiplyScalar(a)},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+= +(a.y-this.y)*b;return this},lerpVectors:function(a,b,c){return this.subVectors(b,a).multiplyScalar(c).add(a)},equals:function(a){return a.x===this.x&&a.y===this.y},fromArray:function(a,b){void 0===b&&(b=0);this.x=a[b];this.y=a[b+1];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this.x;a[b+1]=this.y;return a},fromBufferAttribute:function(a,b,c){void 0!==c&&console.warn("THREE.Vector2: offset has been removed from .fromBufferAttribute().");this.x=a.getX(b);this.y=a.getY(b); +return this},rotateAround:function(a,b){var c=Math.cos(b);b=Math.sin(b);var d=this.x-a.x,e=this.y-a.y;this.x=d*c-e*b+a.x;this.y=d*b+e*c+a.y;return this}});Object.assign(J.prototype,{isMatrix4:!0,set:function(a,b,c,d,e,f,g,h,k,m,l,q,n,t,r,u){var p=this.elements;p[0]=a;p[4]=b;p[8]=c;p[12]=d;p[1]=e;p[5]=f;p[9]=g;p[13]=h;p[2]=k;p[6]=m;p[10]=l;p[14]=q;p[3]=n;p[7]=t;p[11]=r;p[15]=u;return this},identity:function(){this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1);return this},clone:function(){return(new J).fromArray(this.elements)}, +copy:function(a){var b=this.elements;a=a.elements;b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9];b[10]=a[10];b[11]=a[11];b[12]=a[12];b[13]=a[13];b[14]=a[14];b[15]=a[15];return this},copyPosition:function(a){var b=this.elements;a=a.elements;b[12]=a[12];b[13]=a[13];b[14]=a[14];return this},extractBasis:function(a,b,c){a.setFromMatrixColumn(this,0);b.setFromMatrixColumn(this,1);c.setFromMatrixColumn(this,2);return this},makeBasis:function(a,b,c){this.set(a.x, +b.x,c.x,0,a.y,b.y,c.y,0,a.z,b.z,c.z,0,0,0,0,1);return this},extractRotation:function(){var a=new n;return function(b){var c=this.elements,d=b.elements,e=1/a.setFromMatrixColumn(b,0).length(),f=1/a.setFromMatrixColumn(b,1).length();b=1/a.setFromMatrixColumn(b,2).length();c[0]=d[0]*e;c[1]=d[1]*e;c[2]=d[2]*e;c[3]=0;c[4]=d[4]*f;c[5]=d[5]*f;c[6]=d[6]*f;c[7]=0;c[8]=d[8]*b;c[9]=d[9]*b;c[10]=d[10]*b;c[11]=0;c[12]=0;c[13]=0;c[14]=0;c[15]=1;return this}}(),makeRotationFromEuler:function(a){a&&a.isEuler||console.error("THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order."); +var b=this.elements,c=a.x,d=a.y,e=a.z,f=Math.cos(c);c=Math.sin(c);var g=Math.cos(d);d=Math.sin(d);var h=Math.cos(e);e=Math.sin(e);if("XYZ"===a.order){a=f*h;var k=f*e,m=c*h,p=c*e;b[0]=g*h;b[4]=-g*e;b[8]=d;b[1]=k+m*d;b[5]=a-p*d;b[9]=-c*g;b[2]=p-a*d;b[6]=m+k*d;b[10]=f*g}else"YXZ"===a.order?(a=g*h,k=g*e,m=d*h,p=d*e,b[0]=a+p*c,b[4]=m*c-k,b[8]=f*d,b[1]=f*e,b[5]=f*h,b[9]=-c,b[2]=k*c-m,b[6]=p+a*c,b[10]=f*g):"ZXY"===a.order?(a=g*h,k=g*e,m=d*h,p=d*e,b[0]=a-p*c,b[4]=-f*e,b[8]=m+k*c,b[1]=k+m*c,b[5]=f*h,b[9]= +p-a*c,b[2]=-f*d,b[6]=c,b[10]=f*g):"ZYX"===a.order?(a=f*h,k=f*e,m=c*h,p=c*e,b[0]=g*h,b[4]=m*d-k,b[8]=a*d+p,b[1]=g*e,b[5]=p*d+a,b[9]=k*d-m,b[2]=-d,b[6]=c*g,b[10]=f*g):"YZX"===a.order?(a=f*g,k=f*d,m=c*g,p=c*d,b[0]=g*h,b[4]=p-a*e,b[8]=m*e+k,b[1]=e,b[5]=f*h,b[9]=-c*h,b[2]=-d*h,b[6]=k*e+m,b[10]=a-p*e):"XZY"===a.order&&(a=f*g,k=f*d,m=c*g,p=c*d,b[0]=g*h,b[4]=-e,b[8]=d*h,b[1]=a*e+p,b[5]=f*h,b[9]=k*e-m,b[2]=m*e-k,b[6]=c*h,b[10]=p*e+a);b[3]=0;b[7]=0;b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return this},makeRotationFromQuaternion:function(){var a= +new n(0,0,0),b=new n(1,1,1);return function(c){return this.compose(a,c,b)}}(),lookAt:function(){var a=new n,b=new n,c=new n;return function(d,e,f){var g=this.elements;c.subVectors(d,e);0===c.lengthSq()&&(c.z=1);c.normalize();a.crossVectors(f,c);0===a.lengthSq()&&(1===Math.abs(f.z)?c.x+=1E-4:c.z+=1E-4,c.normalize(),a.crossVectors(f,c));a.normalize();b.crossVectors(c,a);g[0]=a.x;g[4]=b.x;g[8]=c.x;g[1]=a.y;g[5]=b.y;g[9]=c.y;g[2]=a.z;g[6]=b.z;g[10]=c.z;return this}}(),multiply:function(a,b){return void 0!== +b?(console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."),this.multiplyMatrices(a,b)):this.multiplyMatrices(this,a)},premultiply:function(a){return this.multiplyMatrices(a,this)},multiplyMatrices:function(a,b){var c=a.elements,d=b.elements;b=this.elements;a=c[0];var e=c[4],f=c[8],g=c[12],h=c[1],k=c[5],m=c[9],p=c[13],l=c[2],n=c[6],t=c[10],r=c[14],u=c[3],w=c[7],z=c[11];c=c[15];var x=d[0],D=d[4],y=d[8],Q=d[12],B=d[1],A=d[5],C=d[9],F=d[13],E=d[2], +H=d[6],G=d[10],I=d[14],L=d[3],J=d[7],K=d[11];d=d[15];b[0]=a*x+e*B+f*E+g*L;b[4]=a*D+e*A+f*H+g*J;b[8]=a*y+e*C+f*G+g*K;b[12]=a*Q+e*F+f*I+g*d;b[1]=h*x+k*B+m*E+p*L;b[5]=h*D+k*A+m*H+p*J;b[9]=h*y+k*C+m*G+p*K;b[13]=h*Q+k*F+m*I+p*d;b[2]=l*x+n*B+t*E+r*L;b[6]=l*D+n*A+t*H+r*J;b[10]=l*y+n*C+t*G+r*K;b[14]=l*Q+n*F+t*I+r*d;b[3]=u*x+w*B+z*E+c*L;b[7]=u*D+w*A+z*H+c*J;b[11]=u*y+w*C+z*G+c*K;b[15]=u*Q+w*F+z*I+c*d;return this},multiplyScalar:function(a){var b=this.elements;b[0]*=a;b[4]*=a;b[8]*=a;b[12]*=a;b[1]*=a;b[5]*= +a;b[9]*=a;b[13]*=a;b[2]*=a;b[6]*=a;b[10]*=a;b[14]*=a;b[3]*=a;b[7]*=a;b[11]*=a;b[15]*=a;return this},applyToBufferAttribute:function(){var a=new n;return function(b){for(var c=0,d=b.count;cthis.determinant()&&(g=-g);c.x=f[12];c.y=f[13];c.z=f[14];b.copy(this);c=1/g;f=1/h;var m=1/k;b.elements[0]*=c;b.elements[1]*=c;b.elements[2]*=c;b.elements[4]*=f;b.elements[5]*=f;b.elements[6]*=f;b.elements[8]*=m;b.elements[9]*=m;b.elements[10]*=m;d.setFromRotationMatrix(b); +e.x=g;e.y=h;e.z=k;return this}}(),makePerspective:function(a,b,c,d,e,f){void 0===f&&console.warn("THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.");var g=this.elements;g[0]=2*e/(b-a);g[4]=0;g[8]=(b+a)/(b-a);g[12]=0;g[1]=0;g[5]=2*e/(c-d);g[9]=(c+d)/(c-d);g[13]=0;g[2]=0;g[6]=0;g[10]=-(f+e)/(f-e);g[14]=-2*f*e/(f-e);g[3]=0;g[7]=0;g[11]=-1;g[15]=0;return this},makeOrthographic:function(a,b,c,d,e,f){var g=this.elements,h=1/(b-a),k=1/(c-d),m=1/(f-e);g[0]= +2*h;g[4]=0;g[8]=0;g[12]=-((b+a)*h);g[1]=0;g[5]=2*k;g[9]=0;g[13]=-((c+d)*k);g[2]=0;g[6]=0;g[10]=-2*m;g[14]=-((f+e)*m);g[3]=0;g[7]=0;g[11]=0;g[15]=1;return this},equals:function(a){var b=this.elements;a=a.elements;for(var c=0;16>c;c++)if(b[c]!==a[c])return!1;return!0},fromArray:function(a,b){void 0===b&&(b=0);for(var c=0;16>c;c++)this.elements[c]=a[c+b];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);var c=this.elements;a[b]=c[0];a[b+1]=c[1];a[b+2]=c[2];a[b+3]=c[3];a[b+4]=c[4]; +a[b+5]=c[5];a[b+6]=c[6];a[b+7]=c[7];a[b+8]=c[8];a[b+9]=c[9];a[b+10]=c[10];a[b+11]=c[11];a[b+12]=c[12];a[b+13]=c[13];a[b+14]=c[14];a[b+15]=c[15];return a}});Object.assign(aa,{slerp:function(a,b,c,d){return c.copy(a).slerp(b,d)},slerpFlat:function(a,b,c,d,e,f,g){var h=c[d+0],k=c[d+1],m=c[d+2];c=c[d+3];d=e[f+0];var l=e[f+1],q=e[f+2];e=e[f+3];if(c!==e||h!==d||k!==l||m!==q){f=1-g;var n=h*d+k*l+m*q+c*e,t=0<=n?1:-1,r=1-n*n;r>Number.EPSILON&&(r=Math.sqrt(r),n=Math.atan2(r,n*t),f=Math.sin(f*n)/r,g=Math.sin(g* +n)/r);t*=g;h=h*f+d*t;k=k*f+l*t;m=m*f+q*t;c=c*f+e*t;f===1-g&&(g=1/Math.sqrt(h*h+k*k+m*m+c*c),h*=g,k*=g,m*=g,c*=g)}a[b]=h;a[b+1]=k;a[b+2]=m;a[b+3]=c}});Object.defineProperties(aa.prototype,{x:{get:function(){return this._x},set:function(a){this._x=a;this.onChangeCallback()}},y:{get:function(){return this._y},set:function(a){this._y=a;this.onChangeCallback()}},z:{get:function(){return this._z},set:function(a){this._z=a;this.onChangeCallback()}},w:{get:function(){return this._w},set:function(a){this._w= +a;this.onChangeCallback()}}});Object.assign(aa.prototype,{isQuaternion:!0,set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._w=d;this.onChangeCallback();return this},clone:function(){return new this.constructor(this._x,this._y,this._z,this._w)},copy:function(a){this._x=a.x;this._y=a.y;this._z=a.z;this._w=a.w;this.onChangeCallback();return this},setFromEuler:function(a,b){if(!a||!a.isEuler)throw Error("THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order."); +var c=a._x,d=a._y,e=a._z;a=a.order;var f=Math.cos,g=Math.sin,h=f(c/2),k=f(d/2);f=f(e/2);c=g(c/2);d=g(d/2);e=g(e/2);"XYZ"===a?(this._x=c*k*f+h*d*e,this._y=h*d*f-c*k*e,this._z=h*k*e+c*d*f,this._w=h*k*f-c*d*e):"YXZ"===a?(this._x=c*k*f+h*d*e,this._y=h*d*f-c*k*e,this._z=h*k*e-c*d*f,this._w=h*k*f+c*d*e):"ZXY"===a?(this._x=c*k*f-h*d*e,this._y=h*d*f+c*k*e,this._z=h*k*e+c*d*f,this._w=h*k*f-c*d*e):"ZYX"===a?(this._x=c*k*f-h*d*e,this._y=h*d*f+c*k*e,this._z=h*k*e-c*d*f,this._w=h*k*f+c*d*e):"YZX"===a?(this._x= +c*k*f+h*d*e,this._y=h*d*f+c*k*e,this._z=h*k*e-c*d*f,this._w=h*k*f-c*d*e):"XZY"===a&&(this._x=c*k*f-h*d*e,this._y=h*d*f-c*k*e,this._z=h*k*e+c*d*f,this._w=h*k*f+c*d*e);if(!1!==b)this.onChangeCallback();return this},setFromAxisAngle:function(a,b){b/=2;var c=Math.sin(b);this._x=a.x*c;this._y=a.y*c;this._z=a.z*c;this._w=Math.cos(b);this.onChangeCallback();return this},setFromRotationMatrix:function(a){var b=a.elements,c=b[0];a=b[4];var d=b[8],e=b[1],f=b[5],g=b[9],h=b[2],k=b[6];b=b[10];var m=c+f+b;0f&&c>b?(c=2*Math.sqrt(1+c-f-b),this._w=(k-g)/c,this._x=.25*c,this._y=(a+e)/c,this._z=(d+h)/c):f>b?(c=2*Math.sqrt(1+f-c-b),this._w=(d-h)/c,this._x=(a+e)/c,this._y=.25*c,this._z=(g+k)/c):(c=2*Math.sqrt(1+b-c-f),this._w=(e-a)/c,this._x=(d+h)/c,this._y=(g+k)/c,this._z=.25*c);this.onChangeCallback();return this},setFromUnitVectors:function(){var a=new n,b;return function(c,d){void 0===a&&(a=new n);b=c.dot(d)+1;1E-6>b? +(b=0,Math.abs(c.x)>Math.abs(c.z)?a.set(-c.y,c.x,0):a.set(0,-c.z,c.y)):a.crossVectors(c,d);this._x=a.x;this._y=a.y;this._z=a.z;this._w=b;return this.normalize()}}(),angleTo:function(a){return 2*Math.acos(Math.abs(H.clamp(this.dot(a),-1,1)))},rotateTowards:function(a,b){var c=this.angleTo(a);if(0===c)return this;this.slerp(a,Math.min(1,b/c));return this},inverse:function(){return this.conjugate()},conjugate:function(){this._x*=-1;this._y*=-1;this._z*=-1;this.onChangeCallback();return this},dot:function(a){return this._x* +a._x+this._y*a._y+this._z*a._z+this._w*a._w},lengthSq:function(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w},length:function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)},normalize:function(){var a=this.length();0===a?(this._z=this._y=this._x=0,this._w=1):(a=1/a,this._x*=a,this._y*=a,this._z*=a,this._w*=a);this.onChangeCallback();return this},multiply:function(a,b){return void 0!==b?(console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."), +this.multiplyQuaternions(a,b)):this.multiplyQuaternions(this,a)},premultiply:function(a){return this.multiplyQuaternions(a,this)},multiplyQuaternions:function(a,b){var c=a._x,d=a._y,e=a._z;a=a._w;var f=b._x,g=b._y,h=b._z;b=b._w;this._x=c*b+a*f+d*h-e*g;this._y=d*b+a*g+e*f-c*h;this._z=e*b+a*h+c*g-d*f;this._w=a*b-c*f-d*g-e*h;this.onChangeCallback();return this},slerp:function(a,b){if(0===b)return this;if(1===b)return this.copy(a);var c=this._x,d=this._y,e=this._z,f=this._w,g=f*a._w+c*a._x+d*a._y+e*a._z; +0>g?(this._w=-a._w,this._x=-a._x,this._y=-a._y,this._z=-a._z,g=-g):this.copy(a);if(1<=g)return this._w=f,this._x=c,this._y=d,this._z=e,this;a=1-g*g;if(a<=Number.EPSILON)return g=1-b,this._w=g*f+b*this._w,this._x=g*c+b*this._x,this._y=g*d+b*this._y,this._z=g*e+b*this._z,this.normalize();a=Math.sqrt(a);var h=Math.atan2(a,g);g=Math.sin((1-b)*h)/a;b=Math.sin(b*h)/a;this._w=f*g+this._w*b;this._x=c*g+this._x*b;this._y=d*g+this._y*b;this._z=e*g+this._z*b;this.onChangeCallback();return this},equals:function(a){return a._x=== +this._x&&a._y===this._y&&a._z===this._z&&a._w===this._w},fromArray:function(a,b){void 0===b&&(b=0);this._x=a[b];this._y=a[b+1];this._z=a[b+2];this._w=a[b+3];this.onChangeCallback();return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this._x;a[b+1]=this._y;a[b+2]=this._z;a[b+3]=this._w;return a},onChange:function(a){this.onChangeCallback=a;return this},onChangeCallback:function(){}});Object.assign(n.prototype,{isVector3:!0,set:function(a,b,c){this.x=a;this.y=b;this.z=c;return this}, +setScalar:function(a){this.z=this.y=this.x=a;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setZ:function(a){this.z=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;case 2:this.z=b;break;default:throw Error("index is out of range: "+a);}return this},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw Error("index is out of range: "+a);}},clone:function(){return new this.constructor(this.x, +this.y,this.z)},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;return this},add:function(a,b){if(void 0!==b)return console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;return this},addScaledVector:function(a,b){this.x+=a.x*b;this.y+=a.y*b;this.z+= +a.z*b;return this},sub:function(a,b){if(void 0!==b)return console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(a,b);this.x-=a.x;this.y-=a.y;this.z-=a.z;return this},subScalar:function(a){this.x-=a;this.y-=a;this.z-=a;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;return this},multiply:function(a,b){if(void 0!==b)return console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."), +this.multiplyVectors(a,b);this.x*=a.x;this.y*=a.y;this.z*=a.z;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;this.z*=a;return this},multiplyVectors:function(a,b){this.x=a.x*b.x;this.y=a.y*b.y;this.z=a.z*b.z;return this},applyEuler:function(){var a=new aa;return function(b){b&&b.isEuler||console.error("THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.");return this.applyQuaternion(a.setFromEuler(b))}}(),applyAxisAngle:function(){var a=new aa;return function(b, +c){return this.applyQuaternion(a.setFromAxisAngle(b,c))}}(),applyMatrix3:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[3]*c+a[6]*d;this.y=a[1]*b+a[4]*c+a[7]*d;this.z=a[2]*b+a[5]*c+a[8]*d;return this},applyMatrix4:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;var e=1/(a[3]*b+a[7]*c+a[11]*d+a[15]);this.x=(a[0]*b+a[4]*c+a[8]*d+a[12])*e;this.y=(a[1]*b+a[5]*c+a[9]*d+a[13])*e;this.z=(a[2]*b+a[6]*c+a[10]*d+a[14])*e;return this},applyQuaternion:function(a){var b=this.x, +c=this.y,d=this.z,e=a.x,f=a.y,g=a.z;a=a.w;var h=a*b+f*d-g*c,k=a*c+g*b-e*d,m=a*d+e*c-f*b;b=-e*b-f*c-g*d;this.x=h*a+b*-e+k*-g-m*-f;this.y=k*a+b*-f+m*-e-h*-g;this.z=m*a+b*-g+h*-f-k*-e;return this},project:function(a){return this.applyMatrix4(a.matrixWorldInverse).applyMatrix4(a.projectionMatrix)},unproject:function(){var a=new J;return function(b){return this.applyMatrix4(a.getInverse(b.projectionMatrix)).applyMatrix4(b.matrixWorld)}}(),transformDirection:function(a){var b=this.x,c=this.y,d=this.z;a= +a.elements;this.x=a[0]*b+a[4]*c+a[8]*d;this.y=a[1]*b+a[5]*c+a[9]*d;this.z=a[2]*b+a[6]*c+a[10]*d;return this.normalize()},divide:function(a){this.x/=a.x;this.y/=a.y;this.z/=a.z;return this},divideScalar:function(a){return this.multiplyScalar(1/a)},min:function(a){this.x=Math.min(this.x,a.x);this.y=Math.min(this.y,a.y);this.z=Math.min(this.z,a.z);return this},max:function(a){this.x=Math.max(this.x,a.x);this.y=Math.max(this.y,a.y);this.z=Math.max(this.z,a.z);return this},clamp:function(a,b){this.x=Math.max(a.x, +Math.min(b.x,this.x));this.y=Math.max(a.y,Math.min(b.y,this.y));this.z=Math.max(a.z,Math.min(b.z,this.z));return this},clampScalar:function(){var a=new n,b=new n;return function(c,d){a.set(c,c,c);b.set(d,d,d);return this.clamp(a,b)}}(),clampLength:function(a,b){var c=this.length();return this.divideScalar(c||1).multiplyScalar(Math.max(a,Math.min(b,c)))},floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);this.z=Math.floor(this.z);return this},ceil:function(){this.x=Math.ceil(this.x); +this.y=Math.ceil(this.y);this.z=Math.ceil(this.z);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);this.z=Math.round(this.z);return this},roundToZero:function(){this.x=0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):Math.floor(this.y);this.z=0>this.z?Math.ceil(this.z):Math.floor(this.z);return this},negate:function(){this.x=-this.x;this.y=-this.y;this.z=-this.z;return this},dot:function(a){return this.x*a.x+this.y*a.y+this.z*a.z},lengthSq:function(){return this.x* +this.x+this.y*this.y+this.z*this.z},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},manhattanLength:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)},normalize:function(){return this.divideScalar(this.length()||1)},setLength:function(a){return this.normalize().multiplyScalar(a)},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;this.z+=(a.z-this.z)*b;return this},lerpVectors:function(a,b,c){return this.subVectors(b,a).multiplyScalar(c).add(a)}, +cross:function(a,b){return void 0!==b?(console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."),this.crossVectors(a,b)):this.crossVectors(this,a)},crossVectors:function(a,b){var c=a.x,d=a.y;a=a.z;var e=b.x,f=b.y;b=b.z;this.x=d*b-a*f;this.y=a*e-c*b;this.z=c*f-d*e;return this},projectOnVector:function(a){var b=a.dot(this)/a.lengthSq();return this.copy(a).multiplyScalar(b)},projectOnPlane:function(){var a=new n;return function(b){a.copy(this).projectOnVector(b); +return this.sub(a)}}(),reflect:function(){var a=new n;return function(b){return this.sub(a.copy(b).multiplyScalar(2*this.dot(b)))}}(),angleTo:function(a){a=this.dot(a)/Math.sqrt(this.lengthSq()*a.lengthSq());return Math.acos(H.clamp(a,-1,1))},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b=this.x-a.x,c=this.y-a.y;a=this.z-a.z;return b*b+c*c+a*a},manhattanDistanceTo:function(a){return Math.abs(this.x-a.x)+Math.abs(this.y-a.y)+Math.abs(this.z- +a.z)},setFromSpherical:function(a){return this.setFromSphericalCoords(a.radius,a.phi,a.theta)},setFromSphericalCoords:function(a,b,c){var d=Math.sin(b)*a;this.x=d*Math.sin(c);this.y=Math.cos(b)*a;this.z=d*Math.cos(c);return this},setFromCylindrical:function(a){return this.setFromCylindricalCoords(a.radius,a.theta,a.y)},setFromCylindricalCoords:function(a,b,c){this.x=a*Math.sin(b);this.y=c;this.z=a*Math.cos(b);return this},setFromMatrixPosition:function(a){a=a.elements;this.x=a[12];this.y=a[13];this.z= +a[14];return this},setFromMatrixScale:function(a){var b=this.setFromMatrixColumn(a,0).length(),c=this.setFromMatrixColumn(a,1).length();a=this.setFromMatrixColumn(a,2).length();this.x=b;this.y=c;this.z=a;return this},setFromMatrixColumn:function(a,b){return this.fromArray(a.elements,4*b)},equals:function(a){return a.x===this.x&&a.y===this.y&&a.z===this.z},fromArray:function(a,b){void 0===b&&(b=0);this.x=a[b];this.y=a[b+1];this.z=a[b+2];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0=== +b&&(b=0);a[b]=this.x;a[b+1]=this.y;a[b+2]=this.z;return a},fromBufferAttribute:function(a,b,c){void 0!==c&&console.warn("THREE.Vector3: offset has been removed from .fromBufferAttribute().");this.x=a.getX(b);this.y=a.getY(b);this.z=a.getZ(b);return this}});Object.assign(pa.prototype,{isMatrix3:!0,set:function(a,b,c,d,e,f,g,h,k){var m=this.elements;m[0]=a;m[1]=d;m[2]=g;m[3]=b;m[4]=e;m[5]=h;m[6]=c;m[7]=f;m[8]=k;return this},identity:function(){this.set(1,0,0,0,1,0,0,0,1);return this},clone:function(){return(new this.constructor).fromArray(this.elements)}, +copy:function(a){var b=this.elements;a=a.elements;b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];return this},setFromMatrix4:function(a){a=a.elements;this.set(a[0],a[4],a[8],a[1],a[5],a[9],a[2],a[6],a[10]);return this},applyToBufferAttribute:function(){var a=new n;return function(b){for(var c=0,d=b.count;cc;c++)if(b[c]!==a[c])return!1;return!0},fromArray:function(a,b){void 0===b&&(b=0);for(var c=0;9>c;c++)this.elements[c]=a[c+b];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);var c= +this.elements;a[b]=c[0];a[b+1]=c[1];a[b+2]=c[2];a[b+3]=c[3];a[b+4]=c[4];a[b+5]=c[5];a[b+6]=c[6];a[b+7]=c[7];a[b+8]=c[8];return a}});var tc,lb={getDataURL:function(a){if("undefined"==typeof HTMLCanvasElement)return a.src;if(!(a instanceof HTMLCanvasElement)){void 0===tc&&(tc=document.createElementNS("http://www.w3.org/1999/xhtml","canvas"));tc.width=a.width;tc.height=a.height;var b=tc.getContext("2d");a instanceof ImageData?b.putImageData(a,0,0):b.drawImage(a,0,0,a.width,a.height);a=tc}return 2048< +a.width||2048a.x||1a.x?0:1;break;case 1002:a.x=1===Math.abs(Math.floor(a.x)%2)?Math.ceil(a.x)-a.x:a.x-Math.floor(a.x)}if(0>a.y||1a.y?0:1;break;case 1002:a.y=1===Math.abs(Math.floor(a.y)%2)?Math.ceil(a.y)-a.y:a.y-Math.floor(a.y)}this.flipY&&(a.y=1-a.y);return a}});Object.defineProperty(V.prototype, +"needsUpdate",{set:function(a){!0===a&&this.version++}});Object.assign(ba.prototype,{isVector4:!0,set:function(a,b,c,d){this.x=a;this.y=b;this.z=c;this.w=d;return this},setScalar:function(a){this.w=this.z=this.y=this.x=a;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setZ:function(a){this.z=a;return this},setW:function(a){this.w=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;case 2:this.z=b;break;case 3:this.w= +b;break;default:throw Error("index is out of range: "+a);}return this},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw Error("index is out of range: "+a);}},clone:function(){return new this.constructor(this.x,this.y,this.z,this.w)},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;this.w=void 0!==a.w?a.w:1;return this},add:function(a,b){if(void 0!==b)return console.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."), +this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;this.w+=a.w;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;this.w+=a;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;this.w=a.w+b.w;return this},addScaledVector:function(a,b){this.x+=a.x*b;this.y+=a.y*b;this.z+=a.z*b;this.w+=a.w*b;return this},sub:function(a,b){if(void 0!==b)return console.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(a, +b);this.x-=a.x;this.y-=a.y;this.z-=a.z;this.w-=a.w;return this},subScalar:function(a){this.x-=a;this.y-=a;this.z-=a;this.w-=a;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;this.w=a.w-b.w;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;this.z*=a;this.w*=a;return this},applyMatrix4:function(a){var b=this.x,c=this.y,d=this.z,e=this.w;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d+a[12]*e;this.y=a[1]*b+a[5]*c+a[9]*d+a[13]*e;this.z=a[2]*b+a[6]*c+a[10]*d+a[14]* +e;this.w=a[3]*b+a[7]*c+a[11]*d+a[15]*e;return this},divideScalar:function(a){return this.multiplyScalar(1/a)},setAxisAngleFromQuaternion:function(a){this.w=2*Math.acos(a.w);var b=Math.sqrt(1-a.w*a.w);1E-4>b?(this.x=1,this.z=this.y=0):(this.x=a.x/b,this.y=a.y/b,this.z=a.z/b);return this},setAxisAngleFromRotationMatrix:function(a){a=a.elements;var b=a[0];var c=a[4];var d=a[8],e=a[1],f=a[5],g=a[9];var h=a[2];var k=a[6];var m=a[10];if(.01>Math.abs(c-e)&&.01>Math.abs(d-h)&&.01>Math.abs(g-k)){if(.1>Math.abs(c+ +e)&&.1>Math.abs(d+h)&&.1>Math.abs(g+k)&&.1>Math.abs(b+f+m-3))return this.set(1,0,0,0),this;a=Math.PI;b=(b+1)/2;f=(f+1)/2;m=(m+1)/2;c=(c+e)/4;d=(d+h)/4;g=(g+k)/4;b>f&&b>m?.01>b?(k=0,c=h=.707106781):(k=Math.sqrt(b),h=c/k,c=d/k):f>m?.01>f?(k=.707106781,h=0,c=.707106781):(h=Math.sqrt(f),k=c/h,c=g/h):.01>m?(h=k=.707106781,c=0):(c=Math.sqrt(m),k=d/c,h=g/c);this.set(k,h,c,a);return this}a=Math.sqrt((k-g)*(k-g)+(d-h)*(d-h)+(e-c)*(e-c));.001>Math.abs(a)&&(a=1);this.x=(k-g)/a;this.y=(d-h)/a;this.z=(e-c)/a; +this.w=Math.acos((b+f+m-1)/2);return this},min:function(a){this.x=Math.min(this.x,a.x);this.y=Math.min(this.y,a.y);this.z=Math.min(this.z,a.z);this.w=Math.min(this.w,a.w);return this},max:function(a){this.x=Math.max(this.x,a.x);this.y=Math.max(this.y,a.y);this.z=Math.max(this.z,a.z);this.w=Math.max(this.w,a.w);return this},clamp:function(a,b){this.x=Math.max(a.x,Math.min(b.x,this.x));this.y=Math.max(a.y,Math.min(b.y,this.y));this.z=Math.max(a.z,Math.min(b.z,this.z));this.w=Math.max(a.w,Math.min(b.w, +this.w));return this},clampScalar:function(){var a,b;return function(c,d){void 0===a&&(a=new ba,b=new ba);a.set(c,c,c,c);b.set(d,d,d,d);return this.clamp(a,b)}}(),clampLength:function(a,b){var c=this.length();return this.divideScalar(c||1).multiplyScalar(Math.max(a,Math.min(b,c)))},floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);this.z=Math.floor(this.z);this.w=Math.floor(this.w);return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);this.z=Math.ceil(this.z); +this.w=Math.ceil(this.w);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);this.z=Math.round(this.z);this.w=Math.round(this.w);return this},roundToZero:function(){this.x=0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):Math.floor(this.y);this.z=0>this.z?Math.ceil(this.z):Math.floor(this.z);this.w=0>this.w?Math.ceil(this.w):Math.floor(this.w);return this},negate:function(){this.x=-this.x;this.y=-this.y;this.z=-this.z;this.w=-this.w;return this}, +dot:function(a){return this.x*a.x+this.y*a.y+this.z*a.z+this.w*a.w},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)},manhattanLength:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)+Math.abs(this.w)},normalize:function(){return this.divideScalar(this.length()||1)},setLength:function(a){return this.normalize().multiplyScalar(a)},lerp:function(a,b){this.x+= +(a.x-this.x)*b;this.y+=(a.y-this.y)*b;this.z+=(a.z-this.z)*b;this.w+=(a.w-this.w)*b;return this},lerpVectors:function(a,b,c){return this.subVectors(b,a).multiplyScalar(c).add(a)},equals:function(a){return a.x===this.x&&a.y===this.y&&a.z===this.z&&a.w===this.w},fromArray:function(a,b){void 0===b&&(b=0);this.x=a[b];this.y=a[b+1];this.z=a[b+2];this.w=a[b+3];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this.x;a[b+1]=this.y;a[b+2]=this.z;a[b+3]=this.w;return a},fromBufferAttribute:function(a, +b,c){void 0!==c&&console.warn("THREE.Vector4: offset has been removed from .fromBufferAttribute().");this.x=a.getX(b);this.y=a.getY(b);this.z=a.getZ(b);this.w=a.getW(b);return this}});Ra.prototype=Object.assign(Object.create(ka.prototype),{constructor:Ra,isWebGLRenderTarget:!0,setSize:function(a,b){if(this.width!==a||this.height!==b)this.width=a,this.height=b,this.dispose();this.viewport.set(0,0,a,b);this.scissor.set(0,0,a,b)},clone:function(){return(new this.constructor).copy(this)},copy:function(a){this.width= +a.width;this.height=a.height;this.viewport.copy(a.viewport);this.texture=a.texture.clone();this.depthBuffer=a.depthBuffer;this.stencilBuffer=a.stencilBuffer;this.depthTexture=a.depthTexture;return this},dispose:function(){this.dispatchEvent({type:"dispose"})}});Zd.prototype=Object.assign(Object.create(Ra.prototype),{constructor:Zd,isWebGLMultisampleRenderTarget:!0,copy:function(a){Ra.prototype.copy.call(this,a);this.samples=a.samples;return this}});mb.prototype=Object.create(Ra.prototype);mb.prototype.constructor= +mb;mb.prototype.isWebGLRenderTargetCube=!0;nb.prototype=Object.create(V.prototype);nb.prototype.constructor=nb;nb.prototype.isDataTexture=!0;Object.assign(Za.prototype,{isBox3:!0,set:function(a,b){this.min.copy(a);this.max.copy(b);return this},setFromArray:function(a){for(var b=Infinity,c=Infinity,d=Infinity,e=-Infinity,f=-Infinity,g=-Infinity,h=0,k=a.length;he&&(e=m);l>f&&(f=l);q>g&&(g=q)}this.min.set(b,c,d);this.max.set(e, +f,g);return this},setFromBufferAttribute:function(a){for(var b=Infinity,c=Infinity,d=Infinity,e=-Infinity,f=-Infinity,g=-Infinity,h=0,k=a.count;he&&(e=m);l>f&&(f=l);q>g&&(g=q)}this.min.set(b,c,d);this.max.set(e,f,g);return this},setFromPoints:function(a){this.makeEmpty();for(var b=0,c=a.length;bthis.max.x||a.ythis.max.y||a.zthis.max.z?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y&&this.min.z<=a.min.z&&a.max.z<=this.max.z},getParameter:function(a,b){void 0===b&&(console.warn("THREE.Box3: .getParameter() target is now required"),b=new n);return b.set((a.x-this.min.x)/(this.max.x-this.min.x),(a.y-this.min.y)/(this.max.y-this.min.y),(a.z-this.min.z)/(this.max.z-this.min.z))}, +intersectsBox:function(a){return a.max.xthis.max.x||a.max.ythis.max.y||a.max.zthis.max.z?!1:!0},intersectsSphere:function(){var a=new n;return function(b){this.clampPoint(b.center,a);return a.distanceToSquared(b.center)<=b.radius*b.radius}}(),intersectsPlane:function(a){if(0=-a.constant},intersectsTriangle:function(){function a(a){var e;var f=0;for(e=a.length-3;f<=e;f+=3){h.fromArray(a,f);var g=m.x*Math.abs(h.x)+m.y*Math.abs(h.y)+m.z*Math.abs(h.z),k=b.dot(h),l=c.dot(h),p=d.dot(h);if(Math.max(-Math.max(k,l,p),Math.min(k,l,p))>g)return!1}return!0}var b=new n, +c=new n,d=new n,e=new n,f=new n,g=new n,h=new n,k=new n,m=new n,l=new n;return function(h){if(this.isEmpty())return!1;this.getCenter(k);m.subVectors(this.max,k);b.subVectors(h.a,k);c.subVectors(h.b,k);d.subVectors(h.c,k);e.subVectors(c,b);f.subVectors(d,c);g.subVectors(b,d);h=[0,-e.z,e.y,0,-f.z,f.y,0,-g.z,g.y,e.z,0,-e.x,f.z,0,-f.x,g.z,0,-g.x,-e.y,e.x,0,-f.y,f.x,0,-g.y,g.x,0];if(!a(h))return!1;h=[1,0,0,0,1,0,0,0,1];if(!a(h))return!1;l.crossVectors(e,f);h=[l.x,l.y,l.z];return a(h)}}(),clampPoint:function(a, +b){void 0===b&&(console.warn("THREE.Box3: .clampPoint() target is now required"),b=new n);return b.copy(a).clamp(this.min,this.max)},distanceToPoint:function(){var a=new n;return function(b){return a.copy(b).clamp(this.min,this.max).sub(b).length()}}(),getBoundingSphere:function(){var a=new n;return function(b){void 0===b&&(console.warn("THREE.Box3: .getBoundingSphere() target is now required"),b=new Ha);this.getCenter(b.center);b.radius=.5*this.getSize(a).length();return b}}(),intersect:function(a){this.min.max(a.min); +this.max.min(a.max);this.isEmpty()&&this.makeEmpty();return this},union:function(a){this.min.min(a.min);this.max.max(a.max);return this},applyMatrix4:function(){var a=[new n,new n,new n,new n,new n,new n,new n,new n];return function(b){if(this.isEmpty())return this;a[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(b);a[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(b);a[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(b);a[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(b); +a[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(b);a[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(b);a[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(b);a[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(b);this.setFromPoints(a);return this}}(),translate:function(a){this.min.add(a);this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&&a.max.equals(this.max)}});Object.assign(Ha.prototype,{set:function(a,b){this.center.copy(a);this.radius=b;return this}, +setFromPoints:function(){var a=new Za;return function(b,c){var d=this.center;void 0!==c?d.copy(c):a.setFromPoints(b).getCenter(d);for(var e=c=0,f=b.length;e=this.radius},containsPoint:function(a){return a.distanceToSquared(this.center)<=this.radius*this.radius}, +distanceToPoint:function(a){return a.distanceTo(this.center)-this.radius},intersectsSphere:function(a){var b=this.radius+a.radius;return a.center.distanceToSquared(this.center)<=b*b},intersectsBox:function(a){return a.intersectsSphere(this)},intersectsPlane:function(a){return Math.abs(a.distanceToPoint(this.center))<=this.radius},clampPoint:function(a,b){var c=this.center.distanceToSquared(a);void 0===b&&(console.warn("THREE.Sphere: .clampPoint() target is now required"),b=new n);b.copy(a);c>this.radius* +this.radius&&(b.sub(this.center).normalize(),b.multiplyScalar(this.radius).add(this.center));return b},getBoundingBox:function(a){void 0===a&&(console.warn("THREE.Sphere: .getBoundingBox() target is now required"),a=new Za);a.set(this.center,this.center);a.expandByScalar(this.radius);return a},applyMatrix4:function(a){this.center.applyMatrix4(a);this.radius*=a.getMaxScaleOnAxis();return this},translate:function(a){this.center.add(a);return this},equals:function(a){return a.center.equals(this.center)&& +a.radius===this.radius}});Object.assign(Sa.prototype,{set:function(a,b){this.normal.copy(a);this.constant=b;return this},setComponents:function(a,b,c,d){this.normal.set(a,b,c);this.constant=d;return this},setFromNormalAndCoplanarPoint:function(a,b){this.normal.copy(a);this.constant=-b.dot(this.normal);return this},setFromCoplanarPoints:function(){var a=new n,b=new n;return function(c,d,e){d=a.subVectors(e,d).cross(b.subVectors(c,d)).normalize();this.setFromNormalAndCoplanarPoint(d,c);return this}}(), +clone:function(){return(new this.constructor).copy(this)},copy:function(a){this.normal.copy(a.normal);this.constant=a.constant;return this},normalize:function(){var a=1/this.normal.length();this.normal.multiplyScalar(a);this.constant*=a;return this},negate:function(){this.constant*=-1;this.normal.negate();return this},distanceToPoint:function(a){return this.normal.dot(a)+this.constant},distanceToSphere:function(a){return this.distanceToPoint(a.center)-a.radius},projectPoint:function(a,b){void 0=== +b&&(console.warn("THREE.Plane: .projectPoint() target is now required"),b=new n);return b.copy(this.normal).multiplyScalar(-this.distanceToPoint(a)).add(a)},intersectLine:function(){var a=new n;return function(b,c){void 0===c&&(console.warn("THREE.Plane: .intersectLine() target is now required"),c=new n);var d=b.delta(a),e=this.normal.dot(d);if(0===e){if(0===this.distanceToPoint(b.start))return c.copy(b.start)}else if(e=-(b.start.dot(this.normal)+this.constant)/e,!(0>e||1b&&0a&&0c;c++)b[c].copy(a.planes[c]);return this},setFromMatrix:function(a){var b=this.planes,c=a.elements;a=c[0];var d=c[1],e=c[2],f=c[3],g=c[4],h=c[5],k=c[6],m=c[7],l=c[8],q=c[9],n=c[10],t=c[11],r=c[12],u=c[13],w=c[14];c=c[15];b[0].setComponents(f-a,m-g,t-l,c-r).normalize();b[1].setComponents(f+a,m+g,t+l,c+r).normalize();b[2].setComponents(f+d,m+h,t+q,c+u).normalize();b[3].setComponents(f-d,m-h,t-q,c-u).normalize();b[4].setComponents(f-e,m-k,t-n,c-w).normalize();b[5].setComponents(f+e, +m+k,t+n,c+w).normalize();return this},intersectsObject:function(){var a=new Ha;return function(b){var c=b.geometry;null===c.boundingSphere&&c.computeBoundingSphere();a.copy(c.boundingSphere).applyMatrix4(b.matrixWorld);return this.intersectsSphere(a)}}(),intersectsSprite:function(){var a=new Ha;return function(b){a.center.set(0,0,0);a.radius=.7071067811865476;a.applyMatrix4(b.matrixWorld);return this.intersectsSphere(a)}}(),intersectsSphere:function(a){var b=this.planes,c=a.center;a=-a.radius;for(var d= +0;6>d;d++)if(b[d].distanceToPoint(c)d;d++){var e=c[d];a.x=0e.distanceToPoint(a))return!1}return!0}}(),containsPoint:function(a){for(var b=this.planes,c=0;6>c;c++)if(0>b[c].distanceToPoint(a))return!1;return!0}});var U={alphamap_fragment:"#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif", +alphamap_pars_fragment:"#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif",alphatest_fragment:"#ifdef ALPHATEST\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n#endif",aomap_fragment:"#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( PHYSICAL )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n\t#endif\n#endif", +aomap_pars_fragment:"#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif",begin_vertex:"vec3 transformed = vec3( position );",beginnormal_vertex:"vec3 objectNormal = vec3( normal );\n#ifdef USE_TANGENT\n\tvec3 objectTangent = vec3( tangent.xyz );\n#endif",bsdfs:"vec2 integrateSpecularBRDF( const in float dotNV, const in float roughness ) {\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\treturn vec2( -1.04, 1.04 ) * a004 + r.zw;\n}\nfloat punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\tif( cutoffDistance > 0.0 ) {\n\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t}\n\treturn distanceFalloff;\n#else\n\tif( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\t}\n\treturn 1.0;\n#endif\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\nvec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\treturn specularColor * brdf.x + brdf.y;\n}\nvoid BRDF_Specular_Multiscattering_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tvec3 F = F_Schlick( specularColor, dotNV );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\tvec3 FssEss = F * brdf.x + brdf.y;\n\tfloat Ess = brdf.x + brdf.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}", +bumpmap_pars_fragment:"#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\t\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\t\tfDet *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif", +clipping_planes_fragment:"#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vViewPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vViewPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\tif ( clipped ) discard;\n\t#endif\n#endif", +clipping_planes_pars_fragment:"#if NUM_CLIPPING_PLANES > 0\n\t#if ! defined( PHYSICAL ) && ! defined( PHONG ) && ! defined( MATCAP )\n\t\tvarying vec3 vViewPosition;\n\t#endif\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif",clipping_planes_pars_vertex:"#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG ) && ! defined( MATCAP )\n\tvarying vec3 vViewPosition;\n#endif",clipping_planes_vertex:"#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG ) && ! defined( MATCAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif", +color_fragment:"#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif",color_pars_fragment:"#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif",color_pars_vertex:"#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif",color_vertex:"#ifdef USE_COLOR\n\tvColor.xyz = color.xyz;\n#endif",common:"#define PI 3.14159265359\n#define PI2 6.28318530718\n#define PI_HALF 1.5707963267949\n#define RECIPROCAL_PI 0.31830988618\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat linearToRelativeLuminance( const in vec3 color ) {\n\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\n\treturn dot( weights, color.rgb );\n}", +cube_uv_reflection_fragment:"#ifdef ENVMAP_TYPE_CUBE_UV\n#define cubeUV_textureSize (1024.0)\nint getFaceFromDirection(vec3 direction) {\n\tvec3 absDirection = abs(direction);\n\tint face = -1;\n\tif( absDirection.x > absDirection.z ) {\n\t\tif(absDirection.x > absDirection.y )\n\t\t\tface = direction.x > 0.0 ? 0 : 3;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\telse {\n\t\tif(absDirection.z > absDirection.y )\n\t\t\tface = direction.z > 0.0 ? 2 : 5;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\treturn face;\n}\n#define cubeUV_maxLods1 (log2(cubeUV_textureSize*0.25) - 1.0)\n#define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0))\nvec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) {\n\tfloat scale = exp2(cubeUV_maxLods1 - roughnessLevel);\n\tfloat dxRoughness = dFdx(roughness);\n\tfloat dyRoughness = dFdy(roughness);\n\tvec3 dx = dFdx( vec * scale * dxRoughness );\n\tvec3 dy = dFdy( vec * scale * dyRoughness );\n\tfloat d = max( dot( dx, dx ), dot( dy, dy ) );\n\td = clamp(d, 1.0, cubeUV_rangeClamp);\n\tfloat mipLevel = 0.5 * log2(d);\n\treturn vec2(floor(mipLevel), fract(mipLevel));\n}\n#define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0)\n#define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize)\nvec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) {\n\tmipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel;\n\tfloat a = 16.0 * cubeUV_rcpTextureSize;\n\tvec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) );\n\tvec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed;\n\tfloat powScale = exp2_packed.x * exp2_packed.y;\n\tfloat scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25;\n\tfloat mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x;\n\tbool bRes = mipLevel == 0.0;\n\tscale = bRes && (scale < a) ? a : scale;\n\tvec3 r;\n\tvec2 offset;\n\tint face = getFaceFromDirection(direction);\n\tfloat rcpPowScale = 1.0 / powScale;\n\tif( face == 0) {\n\t\tr = vec3(direction.x, -direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 1) {\n\t\tr = vec3(direction.y, direction.x, direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 2) {\n\t\tr = vec3(direction.z, direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 3) {\n\t\tr = vec3(direction.x, direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse if( face == 4) {\n\t\tr = vec3(direction.y, direction.x, -direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse {\n\t\tr = vec3(direction.z, -direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\tr = normalize(r);\n\tfloat texelOffset = 0.5 * cubeUV_rcpTextureSize;\n\tvec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5;\n\tvec2 base = offset + vec2( texelOffset );\n\treturn base + s * ( scale - 2.0 * texelOffset );\n}\n#define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0)\nvec4 textureCubeUV( sampler2D envMap, vec3 reflectedDirection, float roughness ) {\n\tfloat roughnessVal = roughness* cubeUV_maxLods3;\n\tfloat r1 = floor(roughnessVal);\n\tfloat r2 = r1 + 1.0;\n\tfloat t = fract(roughnessVal);\n\tvec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness);\n\tfloat s = mipInfo.y;\n\tfloat level0 = mipInfo.x;\n\tfloat level1 = level0 + 1.0;\n\tlevel1 = level1 > 5.0 ? 5.0 : level1;\n\tlevel0 += min( floor( s + 0.5 ), 5.0 );\n\tvec2 uv_10 = getCubeUV(reflectedDirection, r1, level0);\n\tvec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));\n\tvec2 uv_20 = getCubeUV(reflectedDirection, r2, level0);\n\tvec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));\n\tvec4 result = mix(color10, color20, t);\n\treturn vec4(result.rgb, 1.0);\n}\n#endif", +defaultnormal_vertex:"vec3 transformedNormal = normalMatrix * objectNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n#ifdef USE_TANGENT\n\tvec3 transformedTangent = normalMatrix * objectTangent;\n\t#ifdef FLIP_SIDED\n\t\ttransformedTangent = - transformedTangent;\n\t#endif\n#endif",displacementmap_pars_vertex:"#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif",displacementmap_vertex:"#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n#endif", +emissivemap_fragment:"#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif",emissivemap_pars_fragment:"#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif",encodings_fragment:"gl_FragColor = linearToOutputTexel( gl_FragColor );",encodings_pars_fragment:"\nvec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( gammaFactor ) ), value.a );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( 1.0 / gammaFactor ) ), value.a );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n\tfloat maxComponent = max( max( value.r, value.g ), value.b );\n\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * value.a * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n\tM = ceil( M * 255.0 ) / 255.0;\n\treturn vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat D = max( maxRange / maxRGB, 1.0 );\n\tD = min( floor( D ) / 255.0, 1.0 );\n\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n\tvec3 Xp_Y_XYZp = cLogLuvM * value.rgb;\n\tXp_Y_XYZp = max( Xp_Y_XYZp, vec3( 1e-6, 1e-6, 1e-6 ) );\n\tvec4 vResult;\n\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n\tvResult.w = fract( Le );\n\tvResult.z = ( Le - ( floor( vResult.w * 255.0 ) ) / 255.0 ) / 255.0;\n\treturn vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n\tfloat Le = value.z * 255.0 + value.w;\n\tvec3 Xp_Y_XYZp;\n\tXp_Y_XYZp.y = exp2( ( Le - 127.0 ) / 2.0 );\n\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n\tvec3 vRGB = cLogLuvInverseM * Xp_Y_XYZp.rgb;\n\treturn vec4( max( vRGB, 0.0 ), 1.0 );\n}", +envmap_fragment:"#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\tvec2 sampleUV;\n\t\treflectVec = normalize( reflectVec );\n\t\tsampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\t\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\tvec4 envColor = texture2D( envMap, sampleUV );\n\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\treflectVec = normalize( reflectVec );\n\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) );\n\t\tvec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\tenvColor = envMapTexelToLinear( envColor );\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif", +envmap_pars_fragment:"#if defined( USE_ENVMAP ) || defined( PHYSICAL )\n\tuniform float reflectivity;\n\tuniform float envMapIntensity;\n#endif\n#ifdef USE_ENVMAP\n\t#if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) )\n\t\tvarying vec3 vWorldPosition;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\tuniform float flipEnvMap;\n\tuniform int maxMipLevel;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL )\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif", +envmap_pars_vertex:"#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif",envmap_physical_pars_fragment:"#if defined( USE_ENVMAP ) && defined( PHYSICAL )\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, queryVec, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar + 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -geometry.viewDir, geometry.normal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, queryReflectVec, BlinnExponentToGGXRoughness(blinnShininessExponent ));\n\t\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\t\tvec2 sampleUV;\n\t\t\tsampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\t\t\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0,0.0,1.0 ) );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif", +envmap_vertex:"#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif", +fog_vertex:"#ifdef USE_FOG\n\tfogDepth = -mvPosition.z;\n#endif",fog_pars_vertex:"#ifdef USE_FOG\n\tvarying float fogDepth;\n#endif",fog_fragment:"#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 ) );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif",fog_pars_fragment:"#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float fogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif", +gradientmap_pars_fragment:"#ifdef TOON\n\tuniform sampler2D gradientMap;\n\tvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\t\tfloat dotNL = dot( normal, lightDirection );\n\t\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t\t#ifdef USE_GRADIENTMAP\n\t\t\treturn texture2D( gradientMap, coord ).rgb;\n\t\t#else\n\t\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t\t#endif\n\t}\n#endif",lightmap_fragment:"#ifdef USE_LIGHTMAP\n\treflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n#endif", +lightmap_pars_fragment:"#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif",lights_lambert_vertex:"vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\nvIndirectFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n\tvIndirectBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n#endif", +lights_pars_begin:"uniform vec3 ambientLightColor;\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treturn irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t\tfloat shadowCameraNear;\n\t\tfloat shadowCameraFar;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif", +lights_phong_fragment:"BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;",lights_phong_pars_fragment:"varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3\tdiffuseColor;\n\tvec3\tspecularColor;\n\tfloat\tspecularShininess;\n\tfloat\tspecularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifdef TOON\n\t\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#else\n\t\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\t\tvec3 irradiance = dotNL * directLight.color;\n\t#endif\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)", +lights_physical_fragment:"PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nmaterial.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );\n#ifdef STANDARD\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n\tmaterial.clearCoat = saturate( clearCoat );\tmaterial.clearCoatRoughness = clamp( clearCoatRoughness, 0.04, 1.0 );\n#endif", +lights_physical_pars_fragment:"struct PhysicalMaterial {\n\tvec3\tdiffuseColor;\n\tfloat\tspecularRoughness;\n\tvec3\tspecularColor;\n\t#ifndef STANDARD\n\t\tfloat clearCoat;\n\t\tfloat clearCoatRoughness;\n\t#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearCoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.specularRoughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifndef STANDARD\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\treflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness );\n\treflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\t#ifndef STANDARD\n\t\treflectedLight.directSpecular += irradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifndef ENVMAP_TYPE_CUBE_UV\n\t\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\t#endif\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifndef STANDARD\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\tfloat dotNL = dotNV;\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\tfloat clearCoatInv = 1.0 - clearCoatDHR;\n\t#if defined( ENVMAP_TYPE_CUBE_UV )\n\t\tvec3 singleScattering = vec3( 0.0 );\n\t\tvec3 multiScattering = vec3( 0.0 );\n\t\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\t\tBRDF_Specular_Multiscattering_Environment( geometry, material.specularColor, material.specularRoughness, singleScattering, multiScattering );\n\t\tvec3 diffuse = material.diffuseColor;\n\t\treflectedLight.indirectSpecular += clearCoatInv * radiance * singleScattering;\n\t\treflectedLight.indirectDiffuse += multiScattering * cosineWeightedIrradiance;\n\t\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\n\t#else\n\t\treflectedLight.indirectSpecular += clearCoatInv * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness );\n\t#endif\n\t#ifndef STANDARD\n\t\treflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\n#define Material_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.specularRoughness )\n#define Material_ClearCoat_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.clearCoatRoughness )\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}", +lights_fragment_begin:"\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = normalize( vViewPosition );\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearCoatRadiance = vec3( 0.0 );\n#endif", +lights_fragment_maps:"#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec3 lightMapIrradiance = texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( PHYSICAL ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tirradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getLightProbeIndirectRadiance( geometry, Material_BlinnShininessExponent( material ), maxMipLevel );\n\t#ifndef STANDARD\n\t\tclearCoatRadiance += getLightProbeIndirectRadiance( geometry, Material_ClearCoat_BlinnShininessExponent( material ), maxMipLevel );\n\t#endif\n#endif", +lights_fragment_end:"#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, irradiance, clearCoatRadiance, geometry, material, reflectedLight );\n#endif",logdepthbuf_fragment:"#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tgl_FragDepthEXT = log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif",logdepthbuf_pars_fragment:"#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tuniform float logDepthBufFC;\n\tvarying float vFragDepth;\n#endif", +logdepthbuf_pars_vertex:"#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#else\n\t\tuniform float logDepthBufFC;\n\t#endif\n#endif",logdepthbuf_vertex:"#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t#else\n\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\tgl_Position.z *= gl_Position.w;\n\t#endif\n#endif",map_fragment:"#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif", +map_pars_fragment:"#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif",map_particle_fragment:"#ifdef USE_MAP\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n\tvec4 mapTexel = texture2D( map, uv );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif",map_particle_pars_fragment:"#ifdef USE_MAP\n\tuniform mat3 uvTransform;\n\tuniform sampler2D map;\n#endif",metalnessmap_fragment:"float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif", +metalnessmap_pars_fragment:"#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif",morphnormal_vertex:"#ifdef USE_MORPHNORMALS\n\tobjectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n\tobjectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n\tobjectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n\tobjectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n#endif",morphtarget_pars_vertex:"#ifdef USE_MORPHTARGETS\n\t#ifndef USE_MORPHNORMALS\n\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif", +morphtarget_vertex:"#ifdef USE_MORPHTARGETS\n\ttransformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n\ttransformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n\ttransformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n\ttransformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\ttransformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n\ttransformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n\ttransformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n\ttransformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\t#endif\n#endif", +normal_fragment_begin:"#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n\t#ifdef USE_TANGENT\n\t\tvec3 tangent = normalize( vTangent );\n\t\tvec3 bitangent = normalize( vBitangent );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\ttangent = tangent * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t\tbitangent = bitangent * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t#endif\n\t#endif\n#endif", +normal_fragment_maps:"#ifdef USE_NORMALMAP\n\t#ifdef OBJECTSPACE_NORMALMAP\n\t\tnormal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\t#ifdef FLIP_SIDED\n\t\t\tnormal = - normal;\n\t\t#endif\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t#endif\n\t\tnormal = normalize( normalMatrix * normal );\n\t#else\n\t\t#ifdef USE_TANGENT\n\t\t\tmat3 vTBN = mat3( tangent, bitangent, normal );\n\t\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\t\tmapN.xy = normalScale * mapN.xy;\n\t\t\tnormal = normalize( vTBN * mapN );\n\t\t#else\n\t\t\tnormal = perturbNormal2Arb( -vViewPosition, normal );\n\t\t#endif\n\t#endif\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif", +normalmap_pars_fragment:"#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n\t#ifdef OBJECTSPACE_NORMALMAP\n\t\tuniform mat3 normalMatrix;\n\t#else\n\t\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\t\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\t\tvec2 st0 = dFdx( vUv.st );\n\t\t\tvec2 st1 = dFdy( vUv.st );\n\t\t\tfloat scale = sign( st1.t * st0.s - st0.t * st1.s );\n\t\t\tvec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );\n\t\t\tvec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );\n\t\t\tvec3 N = normalize( surf_norm );\n\t\t\tmat3 tsn = mat3( S, T, N );\n\t\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\t\tmapN.xy *= normalScale;\n\t\t\tmapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t\treturn normalize( tsn * mapN );\n\t\t}\n\t#endif\n#endif", +packing:"vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}", +premultiplied_alpha_fragment:"#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif",project_vertex:"vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\ngl_Position = projectionMatrix * mvPosition;",dithering_fragment:"#if defined( DITHERING )\n\tgl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif",dithering_pars_fragment:"#if defined( DITHERING )\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif", +roughnessmap_fragment:"float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif",roughnessmap_pars_fragment:"#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif",shadowmap_pars_fragment:"#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tfloat texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) {\n\t\tconst vec2 offset = vec2( 0.0, 1.0 );\n\t\tvec2 texelSize = vec2( 1.0 ) / size;\n\t\tvec2 centroidUV = floor( uv * size + 0.5 ) / size;\n\t\tfloat lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare );\n\t\tfloat lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare );\n\t\tfloat rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare );\n\t\tfloat rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare );\n\t\tvec2 f = fract( uv * size + 0.5 );\n\t\tfloat a = mix( lb, lt, f.y );\n\t\tfloat b = mix( rb, rt, f.y );\n\t\tfloat c = mix( a, b, f.x );\n\t\treturn c;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tshadow = (\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\tdp += shadowBias;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif", +shadowmap_pars_vertex:"#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n#endif", +shadowmap_vertex:"#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n#endif", +shadowmask_pars_fragment:"float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\tDirectionalLight directionalLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tshadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\tSpotLight spotLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tshadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\tPointLight pointLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tshadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#endif\n\t#endif\n\treturn shadow;\n}", +skinbase_vertex:"#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif",skinning_pars_vertex:"#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif", +skinning_vertex:"#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif",skinnormal_vertex:"#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\t#ifdef USE_TANGENT\n\t\tobjectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#endif\n#endif", +specularmap_fragment:"float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif",specularmap_pars_fragment:"#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif",tonemapping_fragment:"#if defined( TONE_MAPPING )\n\tgl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif",tonemapping_pars_fragment:"#ifndef saturate\n\t#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nuniform float toneMappingWhitePoint;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\nvec3 Uncharted2ToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( ( color * ( 2.51 * color + 0.03 ) ) / ( color * ( 2.43 * color + 0.59 ) + 0.14 ) );\n}", +uv_pars_fragment:"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n#endif",uv_pars_vertex:"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n\tuniform mat3 uvTransform;\n#endif", +uv_vertex:"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n#endif",uv2_pars_fragment:"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif",uv2_pars_vertex:"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n#endif", +uv2_vertex:"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = uv2;\n#endif",worldpos_vertex:"#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n#endif",background_frag:"uniform sampler2D t2D;\nvarying vec2 vUv;\nvoid main() {\n\tvec4 texColor = texture2D( t2D, vUv );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}",background_vert:"varying vec2 vUv;\nuniform mat3 uvTransform;\nvoid main() {\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n}", +cube_frag:"uniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\nvarying vec3 vWorldDirection;\nvoid main() {\n\tvec4 texColor = textureCube( tCube, vec3( tFlip * vWorldDirection.x, vWorldDirection.yz ) );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\tgl_FragColor.a *= opacity;\n\t#include \n\t#include \n}",cube_vert:"varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}", +depth_frag:"#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - gl_FragCoord.z ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( gl_FragCoord.z );\n\t#endif\n}", +depth_vert:"#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}", +distanceRGBA_frag:"#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}", +distanceRGBA_vert:"#define DISTANCE\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition.xyz;\n}", +equirect_frag:"uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV;\n\tsampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\tsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\n\tvec4 texColor = texture2D( tEquirect, sampleUV );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}", +equirect_vert:"varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}",linedashed_frag:"uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}", +linedashed_vert:"uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvLineDistance = scale * lineDistance;\n\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}", +meshbasic_frag:"uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\treflectedLight.indirectDiffuse += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}", +meshbasic_vert:"#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef USE_ENVMAP\n\t#include \n\t#include \n\t#include \n\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}", +meshlambert_frag:"uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\treflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;\n\t#else\n\t\treflectedLight.indirectDiffuse += vIndirectFront;\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n\t#else\n\t\treflectedLight.directDiffuse = vLightFront;\n\t#endif\n\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}", +meshlambert_vert:"#define LAMBERT\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}", +meshmatcap_frag:"#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\n\t#ifdef USE_MATCAP\n\t\tvec4 matcapColor = texture2D( matcap, uv );\n\t\tmatcapColor = matcapTexelToLinear( matcapColor );\n\t#else\n\t\tvec4 matcapColor = vec4( 1.0 );\n\t#endif\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}", +meshmatcap_vert:"#define MATCAP\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifndef FLAT_SHADED\n\t\tvNormal = normalize( transformedNormal );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n}", +meshphong_frag:"#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}", +meshphong_vert:"#define PHONG\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}", +meshphysical_frag:"#define PHYSICAL\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifndef STANDARD\n\tuniform float clearCoat;\n\tuniform float clearCoatRoughness;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}", +meshphysical_vert:"#define PHYSICAL\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}", +normal_frag:"#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || ( defined( USE_NORMALMAP ) && ! defined( OBJECTSPACE_NORMALMAP ) )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}", +normal_vert:"#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || ( defined( USE_NORMALMAP ) && ! defined( OBJECTSPACE_NORMALMAP ) )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || ( defined( USE_NORMALMAP ) && ! defined( OBJECTSPACE_NORMALMAP ) )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}", +points_frag:"uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}", +points_vert:"uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}", +shadow_frag:"uniform vec3 color;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include \n}",shadow_vert:"#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}", +sprite_frag:"uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n}", +sprite_vert:"uniform float rotation;\nuniform vec2 center;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\t#ifndef USE_SIZEATTENUATION\n\t\tbool isPerspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 );\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\t#endif\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\tmvPosition.xy += rotatedPosition;\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}"}, +gh={clone:Mb,merge:xa},hh={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643, +darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055, +grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184, +lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130, +palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780, +teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};Object.assign(K.prototype,{isColor:!0,r:1,g:1,b:1,set:function(a){a&&a.isColor?this.copy(a):"number"===typeof a?this.setHex(a):"string"===typeof a&&this.setStyle(a);return this},setScalar:function(a){this.b=this.g=this.r=a;return this},setHex:function(a){a=Math.floor(a);this.r=(a>>16&255)/255;this.g=(a>>8&255)/255;this.b=(a&255)/255; +return this},setRGB:function(a,b,c){this.r=a;this.g=b;this.b=c;return this},setHSL:function(){function a(a,c,d){0>d&&(d+=1);1d?c:d<2/3?a+6*(c-a)*(2/3-d):a}return function(b,c,d){b=H.euclideanModulo(b,1);c=H.clamp(c,0,1);d=H.clamp(d,0,1);0===c?this.r=this.g=this.b=d:(c=.5>=d?d*(1+c):d+c-d*c,d=2*d-c,this.r=a(d,c,b+1/3),this.g=a(d,c,b),this.b=a(d,c,b-1/3));return this}}(),setStyle:function(a){function b(b){void 0!==b&&1>parseFloat(b)&&console.warn("THREE.Color: Alpha component of "+ +a+" will be ignored.")}var c;if(c=/^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec(a)){var d=c[2];switch(c[1]){case "rgb":case "rgba":if(c=/^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(d))return this.r=Math.min(255,parseInt(c[1],10))/255,this.g=Math.min(255,parseInt(c[2],10))/255,this.b=Math.min(255,parseInt(c[3],10))/255,b(c[5]),this;if(c=/^(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(d))return this.r=Math.min(100,parseInt(c[1],10))/100,this.g=Math.min(100,parseInt(c[2], +10))/100,this.b=Math.min(100,parseInt(c[3],10))/100,b(c[5]),this;break;case "hsl":case "hsla":if(c=/^([0-9]*\.?[0-9]+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec(d)){d=parseFloat(c[1])/360;var e=parseInt(c[2],10)/100,f=parseInt(c[3],10)/100;b(c[5]);return this.setHSL(d,e,f)}}}else if(c=/^#([A-Fa-f0-9]+)$/.exec(a)){c=c[1];d=c.length;if(3===d)return this.r=parseInt(c.charAt(0)+c.charAt(0),16)/255,this.g=parseInt(c.charAt(1)+c.charAt(1),16)/255,this.b=parseInt(c.charAt(2)+c.charAt(2), +16)/255,this;if(6===d)return this.r=parseInt(c.charAt(0)+c.charAt(1),16)/255,this.g=parseInt(c.charAt(2)+c.charAt(3),16)/255,this.b=parseInt(c.charAt(4)+c.charAt(5),16)/255,this}a&&0a?.0773993808*a:Math.pow(.9478672986*a+.0521327014,2.4)}return function(b){this.r=a(b.r);this.g=a(b.g);this.b= +a(b.b);return this}}(),copyLinearToSRGB:function(){function a(a){return.0031308>a?12.92*a:1.055*Math.pow(a,.41666)-.055}return function(b){this.r=a(b.r);this.g=a(b.g);this.b=a(b.b);return this}}(),convertSRGBToLinear:function(){this.copySRGBToLinear(this);return this},convertLinearToSRGB:function(){this.copyLinearToSRGB(this);return this},getHex:function(){return 255*this.r<<16^255*this.g<<8^255*this.b<<0},getHexString:function(){return("000000"+this.getHex().toString(16)).slice(-6)},getHSL:function(a){void 0=== +a&&(console.warn("THREE.Color: .getHSL() target is now required"),a={h:0,s:0,l:0});var b=this.r,c=this.g,d=this.b,e=Math.max(b,c,d),f=Math.min(b,c,d),g,h=(f+e)/2;if(f===e)f=g=0;else{var k=e-f;f=.5>=h?k/(e+f):k/(2-e-f);switch(e){case b:g=(c-d)/k+(cMath.abs(g)?(this._x=Math.atan2(-m,e),this._z=Math.atan2(-f,a)):(this._x=Math.atan2(q,k),this._z=0)):"YXZ"===b?(this._x=Math.asin(-d(m,-1,1)),.99999>Math.abs(m)?(this._y=Math.atan2(g,e),this._z=Math.atan2(h,k)):(this._y=Math.atan2(-l,a),this._z=0)):"ZXY"===b?(this._x=Math.asin(d(q,-1,1)),.99999>Math.abs(q)?(this._y=Math.atan2(-l,e),this._z=Math.atan2(-f,k)):(this._y=0,this._z=Math.atan2(h,a))):"ZYX"===b?(this._y=Math.asin(-d(l, +-1,1)),.99999>Math.abs(l)?(this._x=Math.atan2(q,e),this._z=Math.atan2(h,a)):(this._x=0,this._z=Math.atan2(-f,k))):"YZX"===b?(this._z=Math.asin(d(h,-1,1)),.99999>Math.abs(h)?(this._x=Math.atan2(-m,k),this._y=Math.atan2(-l,a)):(this._x=0,this._y=Math.atan2(g,e))):"XZY"===b?(this._z=Math.asin(-d(f,-1,1)),.99999>Math.abs(f)?(this._x=Math.atan2(q,k),this._y=Math.atan2(g,a)):(this._x=Math.atan2(-m,e),this._y=0)):console.warn("THREE.Euler: .setFromRotationMatrix() given unsupported order: "+b);this._order= +b;if(!1!==c)this.onChangeCallback();return this},setFromQuaternion:function(){var a=new J;return function(b,c,d){a.makeRotationFromQuaternion(b);return this.setFromRotationMatrix(a,c,d)}}(),setFromVector3:function(a,b){return this.set(a.x,a.y,a.z,b||this._order)},reorder:function(){var a=new aa;return function(b){a.setFromEuler(this);return this.setFromQuaternion(a,b)}}(),equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._order===this._order},fromArray:function(a){this._x= +a[0];this._y=a[1];this._z=a[2];void 0!==a[3]&&(this._order=a[3]);this.onChangeCallback();return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this._x;a[b+1]=this._y;a[b+2]=this._z;a[b+3]=this._order;return a},toVector3:function(a){return a?a.set(this._x,this._y,this._z):new n(this._x,this._y,this._z)},onChange:function(a){this.onChangeCallback=a;return this},onChangeCallback:function(){}});Object.assign(ae.prototype,{set:function(a){this.mask=1<g;g++)if(d[g]===d[(g+1)%3]){a.push(f);break}for(f=a.length-1;0<=f;f--)for(d=a[f],this.faces.splice(d,1),c=0,e= +this.faceVertexUvs.length;cthis.opacity&&(d.opacity=this.opacity);!0===this.transparent&&(d.transparent=this.transparent);d.depthFunc=this.depthFunc;d.depthTest=this.depthTest;d.depthWrite=this.depthWrite;0!==this.rotation&&(d.rotation=this.rotation);!0===this.polygonOffset&&(d.polygonOffset=!0);0!==this.polygonOffsetFactor&&(d.polygonOffsetFactor=this.polygonOffsetFactor);0!==this.polygonOffsetUnits&&(d.polygonOffsetUnits=this.polygonOffsetUnits); +1!==this.linewidth&&(d.linewidth=this.linewidth);void 0!==this.dashSize&&(d.dashSize=this.dashSize);void 0!==this.gapSize&&(d.gapSize=this.gapSize);void 0!==this.scale&&(d.scale=this.scale);!0===this.dithering&&(d.dithering=!0);0a?b.copy(this.origin):b.copy(this.direction).multiplyScalar(a).add(this.origin)},distanceToPoint:function(a){return Math.sqrt(this.distanceSqToPoint(a))},distanceSqToPoint:function(){var a=new n;return function(b){var c=a.subVectors(b,this.origin).dot(this.direction);if(0>c)return this.origin.distanceToSquared(b);a.copy(this.direction).multiplyScalar(c).add(this.origin);return a.distanceToSquared(b)}}(),distanceSqToSegment:function(){var a= +new n,b=new n,c=new n;return function(d,e,f,g){a.copy(d).add(e).multiplyScalar(.5);b.copy(e).sub(d).normalize();c.copy(this.origin).sub(a);var h=.5*d.distanceTo(e),k=-this.direction.dot(b),m=c.dot(this.direction),l=-c.dot(b),n=c.lengthSq(),v=Math.abs(1-k*k);if(0=-t?e<=t?(h=1/v,d*=h,e*=h,k=d*(d+k*e+2*m)+e*(k*d+e+2*l)+n):(e=h,d=Math.max(0,-(k*e+m)),k=-d*d+e*(e+2*l)+n):(e=-h,d=Math.max(0,-(k*e+m)),k=-d*d+e*(e+2*l)+n):e<=-t?(d=Math.max(0,-(-k*h+m)),e=0b)return null; +b=Math.sqrt(b-e);e=d-b;d+=b;return 0>e&&0>d?null:0>e?this.at(d,c):this.at(e,c)}}(),intersectsSphere:function(a){return this.distanceSqToPoint(a.center)<=a.radius*a.radius},distanceToPlane:function(a){var b=a.normal.dot(this.direction);if(0===b)return 0===a.distanceToPoint(this.origin)?0:null;a=-(this.origin.dot(a.normal)+a.constant)/b;return 0<=a?a:null},intersectPlane:function(a,b){a=this.distanceToPlane(a);return null===a?null:this.at(a,b)},intersectsPlane:function(a){var b=a.distanceToPoint(this.origin); +return 0===b||0>a.normal.dot(this.direction)*b?!0:!1},intersectBox:function(a,b){var c=1/this.direction.x;var d=1/this.direction.y;var e=1/this.direction.z,f=this.origin;if(0<=c){var g=(a.min.x-f.x)*c;c*=a.max.x-f.x}else g=(a.max.x-f.x)*c,c*=a.min.x-f.x;if(0<=d){var h=(a.min.y-f.y)*d;d*=a.max.y-f.y}else h=(a.max.y-f.y)*d,d*=a.min.y-f.y;if(g>d||h>c)return null;if(h>g||g!==g)g=h;if(da||h>c)return null; +if(h>g||g!==g)g=h;if(ac?null:this.at(0<=g?g:c,b)},intersectsBox:function(){var a=new n;return function(b){return null!==this.intersectBox(b,a)}}(),intersectTriangle:function(){var a=new n,b=new n,c=new n,d=new n;return function(e,f,g,h,k){b.subVectors(f,e);c.subVectors(g,e);d.crossVectors(b,c);f=this.direction.dot(d);if(0f)h=-1,f=-f;else return null;a.subVectors(this.origin,e);e=h*this.direction.dot(c.crossVectors(a,c));if(0>e)return null; +g=h*this.direction.dot(b.cross(a));if(0>g||e+g>f)return null;e=-h*a.dot(d);return 0>e?null:this.at(e/f,k)}}(),applyMatrix4:function(a){this.origin.applyMatrix4(a);this.direction.transformDirection(a);return this},equals:function(a){return a.origin.equals(this.origin)&&a.direction.equals(this.direction)}});Object.assign(ua,{getNormal:function(){var a=new n;return function(b,c,d,e){void 0===e&&(console.warn("THREE.Triangle: .getNormal() target is now required"),e=new n);e.subVectors(d,c);a.subVectors(b, +c);e.cross(a);b=e.lengthSq();return 0=a.x+a.y}}(),getUV:function(){var a=new n;return function(b,c,d,e,f,g,h,k){this.getBarycoord(b,c,d,e,a);k.set(0,0);k.addScaledVector(f,a.x);k.addScaledVector(g,a.y);k.addScaledVector(h,a.z);return k}}()});Object.assign(ua.prototype,{set:function(a,b,c){this.a.copy(a);this.b.copy(b);this.c.copy(c);return this},setFromPointsAndIndices:function(a,b,c,d){this.a.copy(a[b]);this.b.copy(a[c]);this.c.copy(a[d]);return this}, +clone:function(){return(new this.constructor).copy(this)},copy:function(a){this.a.copy(a.a);this.b.copy(a.b);this.c.copy(a.c);return this},getArea:function(){var a=new n,b=new n;return function(){a.subVectors(this.c,this.b);b.subVectors(this.a,this.b);return.5*a.cross(b).length()}}(),getMidpoint:function(a){void 0===a&&(console.warn("THREE.Triangle: .getMidpoint() target is now required"),a=new n);return a.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)},getNormal:function(a){return ua.getNormal(this.a, +this.b,this.c,a)},getPlane:function(a){void 0===a&&(console.warn("THREE.Triangle: .getPlane() target is now required"),a=new n);return a.setFromCoplanarPoints(this.a,this.b,this.c)},getBarycoord:function(a,b){return ua.getBarycoord(a,this.a,this.b,this.c,b)},containsPoint:function(a){return ua.containsPoint(a,this.a,this.b,this.c)},getUV:function(a,b,c,d,e){return ua.getUV(a,this.a,this.b,this.c,b,c,d,e)},intersectsBox:function(a){return a.intersectsTriangle(this)},closestPointToPoint:function(){var a= +new n,b=new n,c=new n,d=new n,e=new n,f=new n;return function(g,h){void 0===h&&(console.warn("THREE.Triangle: .closestPointToPoint() target is now required"),h=new n);var k=this.a,m=this.b,l=this.c;a.subVectors(m,k);b.subVectors(l,k);d.subVectors(g,k);var q=a.dot(d),v=b.dot(d);if(0>=q&&0>=v)return h.copy(k);e.subVectors(g,m);var t=a.dot(e),r=b.dot(e);if(0<=t&&r<=t)return h.copy(m);var u=q*r-t*v;if(0>=u&&0<=q&&0>=t)return m=q/(q-t),h.copy(k).addScaledVector(a,m);f.subVectors(g,l);g=a.dot(f);var w= +b.dot(f);if(0<=w&&g<=w)return h.copy(l);q=g*v-q*w;if(0>=q&&0<=v&&0>=w)return u=v/(v-w),h.copy(k).addScaledVector(b,u);v=t*w-g*r;if(0>=v&&0<=r-t&&0<=g-w)return c.subVectors(l,m),u=(r-t)/(r-t+(g-w)),h.copy(m).addScaledVector(c,u);l=1/(v+q+u);m=q*l;u*=l;return h.copy(k).addScaledVector(a,m).addScaledVector(b,u)}}(),equals:function(a){return a.a.equals(this.a)&&a.b.equals(this.b)&&a.c.equals(this.c)}});ya.prototype=Object.create(M.prototype);ya.prototype.constructor=ya;ya.prototype.isMeshBasicMaterial= +!0;ya.prototype.copy=function(a){M.prototype.copy.call(this,a);this.color.copy(a.color);this.map=a.map;this.lightMap=a.lightMap;this.lightMapIntensity=a.lightMapIntensity;this.aoMap=a.aoMap;this.aoMapIntensity=a.aoMapIntensity;this.specularMap=a.specularMap;this.alphaMap=a.alphaMap;this.envMap=a.envMap;this.combine=a.combine;this.reflectivity=a.reflectivity;this.refractionRatio=a.refractionRatio;this.wireframe=a.wireframe;this.wireframeLinewidth=a.wireframeLinewidth;this.wireframeLinecap=a.wireframeLinecap; +this.wireframeLinejoin=a.wireframeLinejoin;this.skinning=a.skinning;this.morphTargets=a.morphTargets;return this};va.prototype=Object.assign(Object.create(E.prototype),{constructor:va,isMesh:!0,setDrawMode:function(a){this.drawMode=a},copy:function(a){E.prototype.copy.call(this,a);this.drawMode=a.drawMode;void 0!==a.morphTargetInfluences&&(this.morphTargetInfluences=a.morphTargetInfluences.slice());void 0!==a.morphTargetDictionary&&(this.morphTargetDictionary=Object.assign({},a.morphTargetDictionary)); +return this},updateMorphTargets:function(){var a=this.geometry;if(a.isBufferGeometry){a=a.morphAttributes;var b=Object.keys(a);if(0c.far?null:{distance:b,point:u.clone(),object:a}}function b(b,c,d,e,k,m,l,p,n){f.fromBufferAttribute(k,l);g.fromBufferAttribute(k,p);h.fromBufferAttribute(k,n);if(b=a(b,c,d,e,f,g,h,r))m&&(q.fromBufferAttribute(m,l),v.fromBufferAttribute(m,p),t.fromBufferAttribute(m, +n),b.uv=ua.getUV(r,f,g,h,q,v,t,new B)),m=new Nb(l,p,n),ua.getNormal(f,g,h,m.normal),b.face=m;return b}var c=new J,d=new tb,e=new Ha,f=new n,g=new n,h=new n,k=new n,m=new n,l=new n,q=new B,v=new B,t=new B,r=new n,u=new n;return function(p,n){var u=this.geometry,w=this.material,y=this.matrixWorld;if(void 0!==w&&(null===u.boundingSphere&&u.computeBoundingSphere(),e.copy(u.boundingSphere),e.applyMatrix4(y),!1!==p.ray.intersectsSphere(e)&&(c.getInverse(y),d.copy(p.ray).applyMatrix4(c),null===u.boundingBox|| +!1!==d.intersectsBox(u.boundingBox))))if(u.isBufferGeometry){var z=u.index,C=u.attributes.position,A=u.attributes.uv,F=u.groups;u=u.drawRange;var E,H;if(null!==z)if(Array.isArray(w)){var G=0;for(E=F.length;Ge.far||f.push({distance:r,point:b.clone(),uv:ua.getUV(b,h,k,m,l,q,v,new B),face:null,object:this})}}(),clone:function(){return(new this.constructor(this.material)).copy(this)},copy:function(a){E.prototype.copy.call(this,a);void 0!==a.center&&this.center.copy(a.center);return this}}); +Hc.prototype=Object.assign(Object.create(E.prototype),{constructor:Hc,copy:function(a){E.prototype.copy.call(this,a,!1);a=a.levels;for(var b=0,c=a.length;b=d[e].distance)d[e-1].object.visible=!1,d[e].object.visible=!0;else break; +for(;ef||(l.applyMatrix4(this.matrixWorld),u=d.ray.origin.distanceTo(l),ud.far||e.push({distance:u,point:h.clone().applyMatrix4(this.matrixWorld),index:g,face:null,faceIndex:null,object:this}))}}else for(g= +0,r=t.length/3-1;gf||(l.applyMatrix4(this.matrixWorld),u=d.ray.origin.distanceTo(l),ud.far||e.push({distance:u,point:h.clone().applyMatrix4(this.matrixWorld),index:g,face:null,faceIndex:null,object:this}))}else if(g.isGeometry)for(k=g.vertices,m=k.length,g=0;gf||(l.applyMatrix4(this.matrixWorld),u=d.ray.origin.distanceTo(l),ud.far||e.push({distance:u, +point:h.clone().applyMatrix4(this.matrixWorld),index:g,face:null,faceIndex:null,object:this}))}}}(),copy:function(a){E.prototype.copy.call(this,a);this.geometry.copy(a.geometry);this.material.copy(a.material);return this},clone:function(){return(new this.constructor).copy(this)}});W.prototype=Object.assign(Object.create(da.prototype),{constructor:W,isLineSegments:!0,computeLineDistances:function(){var a=new n,b=new n;return function(){var c=this.geometry;if(c.isBufferGeometry)if(null===c.index){for(var d= +c.attributes.position,e=[],f=0,g=d.count;fd.far||e.push({distance:a,distanceToRay:Math.sqrt(f),point:q.clone(),index:c,face:null,object:g}))}var g=this,h=this.geometry,k=this.matrixWorld,m=d.params.Points.threshold;null===h.boundingSphere&&h.computeBoundingSphere();c.copy(h.boundingSphere);c.applyMatrix4(k);c.radius+=m;if(!1!==d.ray.intersectsSphere(c)){a.getInverse(k);b.copy(d.ray).applyMatrix4(a); +m/=(this.scale.x+this.scale.y+this.scale.z)/3;var l=m*m;m=new n;var q=new n;if(h.isBufferGeometry){var v=h.index;h=h.attributes.position.array;if(null!==v){var t=v.array;v=0;for(var r=t.length;v=a.HAVE_CURRENT_DATA&&(this.needsUpdate=!0)}});Vb.prototype=Object.create(V.prototype);Vb.prototype.constructor=Vb;Vb.prototype.isCompressedTexture=!0;Jc.prototype=Object.create(V.prototype);Jc.prototype.constructor=Jc;Jc.prototype.isCanvasTexture=!0;Kc.prototype=Object.create(V.prototype);Kc.prototype.constructor=Kc;Kc.prototype.isDepthTexture=!0;Wb.prototype=Object.create(C.prototype);Wb.prototype.constructor=Wb;Lc.prototype= +Object.create(G.prototype);Lc.prototype.constructor=Lc;Xb.prototype=Object.create(C.prototype);Xb.prototype.constructor=Xb;Mc.prototype=Object.create(G.prototype);Mc.prototype.constructor=Mc;Aa.prototype=Object.create(C.prototype);Aa.prototype.constructor=Aa;Nc.prototype=Object.create(G.prototype);Nc.prototype.constructor=Nc;Yb.prototype=Object.create(Aa.prototype);Yb.prototype.constructor=Yb;Oc.prototype=Object.create(G.prototype);Oc.prototype.constructor=Oc;vb.prototype=Object.create(Aa.prototype); +vb.prototype.constructor=vb;Pc.prototype=Object.create(G.prototype);Pc.prototype.constructor=Pc;Zb.prototype=Object.create(Aa.prototype);Zb.prototype.constructor=Zb;Qc.prototype=Object.create(G.prototype);Qc.prototype.constructor=Qc;$b.prototype=Object.create(Aa.prototype);$b.prototype.constructor=$b;Rc.prototype=Object.create(G.prototype);Rc.prototype.constructor=Rc;wb.prototype=Object.create(C.prototype);wb.prototype.constructor=wb;wb.prototype.toJSON=function(){var a=C.prototype.toJSON.call(this); +a.path=this.parameters.path.toJSON();return a};Sc.prototype=Object.create(G.prototype);Sc.prototype.constructor=Sc;ac.prototype=Object.create(C.prototype);ac.prototype.constructor=ac;Tc.prototype=Object.create(G.prototype);Tc.prototype.constructor=Tc;bc.prototype=Object.create(C.prototype);bc.prototype.constructor=bc;var ih={triangulate:function(a,b,c){c=c||2;var d=b&&b.length,e=d?b[0]*c:a.length,f=uf(a,0,e,c,!0),g=[];if(!f)return g;var h;if(d){var k=c;d=[];var m;var l=0;for(m=b.length;l80*c){var t=h=a[0];var r=d=a[1];for(k=c;kh&&(h=l),b>d&&(d=b);h=Math.max(h-t,d-r);h=0!==h?1/h:0}Wc(f,g,c,t,r,h);return g}},ab={area:function(a){for(var b=a.length,c=0,d=b-1,e=0;e +ab.area(a)},triangulateShape:function(a,b){var c=[],d=[],e=[];yf(a);zf(c,a);var f=a.length;b.forEach(yf);for(a=0;aMath.abs(g-k)?[new B(a,1-c),new B(h,1-d), +new B(m,1-e),new B(n,1-b)]:[new B(g,1-c),new B(k,1-d),new B(l,1-e),new B(v,1-b)]}};Yc.prototype=Object.create(G.prototype);Yc.prototype.constructor=Yc;cc.prototype=Object.create(Va.prototype);cc.prototype.constructor=cc;Zc.prototype=Object.create(G.prototype);Zc.prototype.constructor=Zc;zb.prototype=Object.create(C.prototype);zb.prototype.constructor=zb;$c.prototype=Object.create(G.prototype);$c.prototype.constructor=$c;dc.prototype=Object.create(C.prototype);dc.prototype.constructor=dc;ad.prototype= +Object.create(G.prototype);ad.prototype.constructor=ad;ec.prototype=Object.create(C.prototype);ec.prototype.constructor=ec;Ab.prototype=Object.create(G.prototype);Ab.prototype.constructor=Ab;Ab.prototype.toJSON=function(){var a=G.prototype.toJSON.call(this);return Bf(this.parameters.shapes,a)};Bb.prototype=Object.create(C.prototype);Bb.prototype.constructor=Bb;Bb.prototype.toJSON=function(){var a=C.prototype.toJSON.call(this);return Bf(this.parameters.shapes,a)};fc.prototype=Object.create(C.prototype); +fc.prototype.constructor=fc;Cb.prototype=Object.create(G.prototype);Cb.prototype.constructor=Cb;bb.prototype=Object.create(C.prototype);bb.prototype.constructor=bb;bd.prototype=Object.create(Cb.prototype);bd.prototype.constructor=bd;cd.prototype=Object.create(bb.prototype);cd.prototype.constructor=cd;dd.prototype=Object.create(G.prototype);dd.prototype.constructor=dd;gc.prototype=Object.create(C.prototype);gc.prototype.constructor=gc;var na=Object.freeze({WireframeGeometry:Wb,ParametricGeometry:Lc, +ParametricBufferGeometry:Xb,TetrahedronGeometry:Nc,TetrahedronBufferGeometry:Yb,OctahedronGeometry:Oc,OctahedronBufferGeometry:vb,IcosahedronGeometry:Pc,IcosahedronBufferGeometry:Zb,DodecahedronGeometry:Qc,DodecahedronBufferGeometry:$b,PolyhedronGeometry:Mc,PolyhedronBufferGeometry:Aa,TubeGeometry:Rc,TubeBufferGeometry:wb,TorusKnotGeometry:Sc,TorusKnotBufferGeometry:ac,TorusGeometry:Tc,TorusBufferGeometry:bc,TextGeometry:Yc,TextBufferGeometry:cc,SphereGeometry:Zc,SphereBufferGeometry:zb,RingGeometry:$c, +RingBufferGeometry:dc,PlaneGeometry:Bc,PlaneBufferGeometry:sb,LatheGeometry:ad,LatheBufferGeometry:ec,ShapeGeometry:Ab,ShapeBufferGeometry:Bb,ExtrudeGeometry:yb,ExtrudeBufferGeometry:Va,EdgesGeometry:fc,ConeGeometry:bd,ConeBufferGeometry:cd,CylinderGeometry:Cb,CylinderBufferGeometry:bb,CircleGeometry:dd,CircleBufferGeometry:gc,BoxGeometry:Ob,BoxBufferGeometry:rb});Db.prototype=Object.create(M.prototype);Db.prototype.constructor=Db;Db.prototype.isShadowMaterial=!0;Db.prototype.copy=function(a){M.prototype.copy.call(this, +a);this.color.copy(a.color);return this};hc.prototype=Object.create(Ca.prototype);hc.prototype.constructor=hc;hc.prototype.isRawShaderMaterial=!0;Wa.prototype=Object.create(M.prototype);Wa.prototype.constructor=Wa;Wa.prototype.isMeshStandardMaterial=!0;Wa.prototype.copy=function(a){M.prototype.copy.call(this,a);this.defines={STANDARD:""};this.color.copy(a.color);this.roughness=a.roughness;this.metalness=a.metalness;this.map=a.map;this.lightMap=a.lightMap;this.lightMapIntensity=a.lightMapIntensity; +this.aoMap=a.aoMap;this.aoMapIntensity=a.aoMapIntensity;this.emissive.copy(a.emissive);this.emissiveMap=a.emissiveMap;this.emissiveIntensity=a.emissiveIntensity;this.bumpMap=a.bumpMap;this.bumpScale=a.bumpScale;this.normalMap=a.normalMap;this.normalMapType=a.normalMapType;this.normalScale.copy(a.normalScale);this.displacementMap=a.displacementMap;this.displacementScale=a.displacementScale;this.displacementBias=a.displacementBias;this.roughnessMap=a.roughnessMap;this.metalnessMap=a.metalnessMap;this.alphaMap= +a.alphaMap;this.envMap=a.envMap;this.envMapIntensity=a.envMapIntensity;this.refractionRatio=a.refractionRatio;this.wireframe=a.wireframe;this.wireframeLinewidth=a.wireframeLinewidth;this.wireframeLinecap=a.wireframeLinecap;this.wireframeLinejoin=a.wireframeLinejoin;this.skinning=a.skinning;this.morphTargets=a.morphTargets;this.morphNormals=a.morphNormals;return this};Eb.prototype=Object.create(Wa.prototype);Eb.prototype.constructor=Eb;Eb.prototype.isMeshPhysicalMaterial=!0;Eb.prototype.copy=function(a){Wa.prototype.copy.call(this, +a);this.defines={PHYSICAL:""};this.reflectivity=a.reflectivity;this.clearCoat=a.clearCoat;this.clearCoatRoughness=a.clearCoatRoughness;return this};Ja.prototype=Object.create(M.prototype);Ja.prototype.constructor=Ja;Ja.prototype.isMeshPhongMaterial=!0;Ja.prototype.copy=function(a){M.prototype.copy.call(this,a);this.color.copy(a.color);this.specular.copy(a.specular);this.shininess=a.shininess;this.map=a.map;this.lightMap=a.lightMap;this.lightMapIntensity=a.lightMapIntensity;this.aoMap=a.aoMap;this.aoMapIntensity= +a.aoMapIntensity;this.emissive.copy(a.emissive);this.emissiveMap=a.emissiveMap;this.emissiveIntensity=a.emissiveIntensity;this.bumpMap=a.bumpMap;this.bumpScale=a.bumpScale;this.normalMap=a.normalMap;this.normalMapType=a.normalMapType;this.normalScale.copy(a.normalScale);this.displacementMap=a.displacementMap;this.displacementScale=a.displacementScale;this.displacementBias=a.displacementBias;this.specularMap=a.specularMap;this.alphaMap=a.alphaMap;this.envMap=a.envMap;this.combine=a.combine;this.reflectivity= +a.reflectivity;this.refractionRatio=a.refractionRatio;this.wireframe=a.wireframe;this.wireframeLinewidth=a.wireframeLinewidth;this.wireframeLinecap=a.wireframeLinecap;this.wireframeLinejoin=a.wireframeLinejoin;this.skinning=a.skinning;this.morphTargets=a.morphTargets;this.morphNormals=a.morphNormals;return this};Fb.prototype=Object.create(Ja.prototype);Fb.prototype.constructor=Fb;Fb.prototype.isMeshToonMaterial=!0;Fb.prototype.copy=function(a){Ja.prototype.copy.call(this,a);this.gradientMap=a.gradientMap; +return this};Gb.prototype=Object.create(M.prototype);Gb.prototype.constructor=Gb;Gb.prototype.isMeshNormalMaterial=!0;Gb.prototype.copy=function(a){M.prototype.copy.call(this,a);this.bumpMap=a.bumpMap;this.bumpScale=a.bumpScale;this.normalMap=a.normalMap;this.normalMapType=a.normalMapType;this.normalScale.copy(a.normalScale);this.displacementMap=a.displacementMap;this.displacementScale=a.displacementScale;this.displacementBias=a.displacementBias;this.wireframe=a.wireframe;this.wireframeLinewidth= +a.wireframeLinewidth;this.skinning=a.skinning;this.morphTargets=a.morphTargets;this.morphNormals=a.morphNormals;return this};Hb.prototype=Object.create(M.prototype);Hb.prototype.constructor=Hb;Hb.prototype.isMeshLambertMaterial=!0;Hb.prototype.copy=function(a){M.prototype.copy.call(this,a);this.color.copy(a.color);this.map=a.map;this.lightMap=a.lightMap;this.lightMapIntensity=a.lightMapIntensity;this.aoMap=a.aoMap;this.aoMapIntensity=a.aoMapIntensity;this.emissive.copy(a.emissive);this.emissiveMap= +a.emissiveMap;this.emissiveIntensity=a.emissiveIntensity;this.specularMap=a.specularMap;this.alphaMap=a.alphaMap;this.envMap=a.envMap;this.combine=a.combine;this.reflectivity=a.reflectivity;this.refractionRatio=a.refractionRatio;this.wireframe=a.wireframe;this.wireframeLinewidth=a.wireframeLinewidth;this.wireframeLinecap=a.wireframeLinecap;this.wireframeLinejoin=a.wireframeLinejoin;this.skinning=a.skinning;this.morphTargets=a.morphTargets;this.morphNormals=a.morphNormals;return this};Ib.prototype= +Object.create(M.prototype);Ib.prototype.constructor=Ib;Ib.prototype.isMeshMatcapMaterial=!0;Ib.prototype.copy=function(a){M.prototype.copy.call(this,a);this.defines={MATCAP:""};this.color.copy(a.color);this.matcap=a.matcap;this.map=a.map;this.bumpMap=a.bumpMap;this.bumpScale=a.bumpScale;this.normalMap=a.normalMap;this.normalMapType=a.normalMapType;this.normalScale.copy(a.normalScale);this.displacementMap=a.displacementMap;this.displacementScale=a.displacementScale;this.displacementBias=a.displacementBias; +this.alphaMap=a.alphaMap;this.skinning=a.skinning;this.morphTargets=a.morphTargets;this.morphNormals=a.morphNormals;return this};Jb.prototype=Object.create(R.prototype);Jb.prototype.constructor=Jb;Jb.prototype.isLineDashedMaterial=!0;Jb.prototype.copy=function(a){R.prototype.copy.call(this,a);this.scale=a.scale;this.dashSize=a.dashSize;this.gapSize=a.gapSize;return this};var jh=Object.freeze({ShadowMaterial:Db,SpriteMaterial:jb,RawShaderMaterial:hc,ShaderMaterial:Ca,PointsMaterial:Ia,MeshPhysicalMaterial:Eb, +MeshStandardMaterial:Wa,MeshPhongMaterial:Ja,MeshToonMaterial:Fb,MeshNormalMaterial:Gb,MeshLambertMaterial:Hb,MeshDepthMaterial:gb,MeshDistanceMaterial:hb,MeshBasicMaterial:ya,MeshMatcapMaterial:Ib,LineDashedMaterial:Jb,LineBasicMaterial:R,Material:M}),sa={arraySlice:function(a,b,c){return sa.isTypedArray(a)?new a.constructor(a.subarray(b,void 0!==c?c:a.length)):a.slice(b,c)},convertArray:function(a,b,c){return!a||!c&&a.constructor===b?a:"number"===typeof b.BYTES_PER_ELEMENT?new b(a):Array.prototype.slice.call(a)}, +isTypedArray:function(a){return ArrayBuffer.isView(a)&&!(a instanceof DataView)},getKeyframeOrder:function(a){for(var b=a.length,c=Array(b),d=0;d!==b;++d)c[d]=d;c.sort(function(b,c){return a[b]-a[c]});return c},sortedArray:function(a,b,c){for(var d=a.length,e=new a.constructor(d),f=0,g=0;g!==d;++f)for(var h=c[f]*b,k=0;k!==b;++k)e[g++]=a[h+k];return e},flattenJSON:function(a,b,c,d){for(var e=1,f=a[0];void 0!==f&&void 0===f[d];)f=a[e++];if(void 0!==f){var g=f[d];if(void 0!==g)if(Array.isArray(g)){do g= +f[d],void 0!==g&&(b.push(f.time),c.push.apply(c,g)),f=a[e++];while(void 0!==f)}else if(void 0!==g.toArray){do g=f[d],void 0!==g&&(b.push(f.time),g.toArray(c,c.length)),f=a[e++];while(void 0!==f)}else{do g=f[d],void 0!==g&&(b.push(f.time),c.push(g)),f=a[e++];while(void 0!==f)}}}};Object.assign(wa.prototype,{evaluate:function(a){var b=this.parameterPositions,c=this._cachedIndex,d=b[c],e=b[c-1];a:{b:{c:{d:if(!(a=e)break a;else{f=b[1];a=e)break b}d=c;c=0}}for(;c>>1,ab;)--f;++f;if(0!==e||f!==d)e>=f&&(f=Math.max(f,1),e=f-1),a=this.getValueSize(),this.times=sa.arraySlice(c,e,f),this.values=sa.arraySlice(this.values,e*a,f*a);return this},validate:function(){var a=!0,b=this.getValueSize();0!==b-Math.floor(b)&&(console.error("THREE.KeyframeTrack: Invalid value size in track.",this),a=!1);var c=this.times;b=this.values;var d=c.length;0===d&&(console.error("THREE.KeyframeTrack: Track is empty.", +this),a=!1);for(var e=null,f=0;f!==d;f++){var g=c[f];if("number"===typeof g&&isNaN(g)){console.error("THREE.KeyframeTrack: Time is not a valid number.",this,f,g);a=!1;break}if(null!==e&&e>g){console.error("THREE.KeyframeTrack: Out of order keys.",this,f,g,e);a=!1;break}e=g}if(void 0!==b&&sa.isTypedArray(b))for(f=0,c=b.length;f!==c;++f)if(d=b[f],isNaN(d)){console.error("THREE.KeyframeTrack: Value is not a valid number.",this,f,d);a=!1;break}return a},optimize:function(){for(var a=this.times,b=this.values, +c=this.getValueSize(),d=2302===this.getInterpolation(),e=1,f=a.length-1,g=1;gg)e=a+1;else if(0b&&(b=0);1Number.EPSILON&&(g.normalize(),c=Math.acos(H.clamp(d[k-1].dot(d[k]),-1,1)),e[k].applyMatrix4(h.makeRotationAxis(g,c))),f[k].crossVectors(d[k],e[k]);if(!0===b)for(c=Math.acos(H.clamp(e[0].dot(e[a]),-1,1)),c/=a,0d;)d+=c;for(;d>c;)d-=c;de&&(e=1);1E-4>d&&(d=e);1E-4>k&&(k=e);Le.initNonuniformCatmullRom(f.x,g.x,h.x,c.x,d,e,k);Me.initNonuniformCatmullRom(f.y,g.y,h.y,c.y,d,e,k);Ne.initNonuniformCatmullRom(f.z,g.z,h.z,c.z,d,e,k)}else"catmullrom"===this.curveType&&(Le.initCatmullRom(f.x,g.x,h.x,c.x,this.tension),Me.initCatmullRom(f.y,g.y,h.y,c.y,this.tension),Ne.initCatmullRom(f.z,g.z,h.z,c.z,this.tension));b.set(Le.calc(a), +Me.calc(a),Ne.calc(a));return b};oa.prototype.copy=function(a){L.prototype.copy.call(this,a);this.points=[];for(var b=0,c=a.points.length;bc.length-2?c.length-1:a+1];c=c[a>c.length-3?c.length-1:a+2];b.set(Ef(d,e.x,f.x,g.x,c.x),Ef(d,e.y,f.y,g.y,c.y));return b};Oa.prototype.copy=function(a){L.prototype.copy.call(this,a);this.points=[];for(var b=0,c=a.points.length;b=b)return b=c[a]-b,a=this.curves[a],c=a.getLength(),a.getPointAt(0===c?0:1-b/c);a++}return null},getLength:function(){var a=this.getCurveLengths(); +return a[a.length-1]},updateArcLengths:function(){this.needsUpdate=!0;this.cacheLengths=null;this.getCurveLengths()},getCurveLengths:function(){if(this.cacheLengths&&this.cacheLengths.length===this.curves.length)return this.cacheLengths;for(var a=[],b=0,c=0,d=this.curves.length;cNumber.EPSILON){if(0>l&&(g=b[f],k=-k,h=b[e],l=-l),!(a.yh.y))if(a.y===g.y){if(a.x===g.x)return!0}else{e=l*(a.x-g.x)-k*(a.y-g.y);if(0===e)return!0;0>e||(d=!d)}}else if(a.y===g.y&&(h.x<=a.x&&a.x<=g.x||g.x<=a.x&&a.x<=h.x))return!0}return d}var e=ab.isClockWise,f=this.subPaths;if(0===f.length)return[];if(!0===b)return c(f);b=[];if(1===f.length){var g=f[0];var h=new kb;h.curves=g.curves;b.push(h);return b}var k=!e(f[0].getPoints()); +k=a?!k:k;h=[];var l=[],n=[],q=0;l[q]=void 0;n[q]=[];for(var v=0,t=f.length;vl.opacity&&(l.transparent=!0);d.setTextures(k);return d.parse(l)}}()});var Yd,ye={getContext:function(){void 0===Yd&&(Yd=new (window.AudioContext||window.webkitAudioContext));return Yd},setContext:function(a){Yd=a}};Object.assign(ve.prototype,{load:function(a,b,c,d){var e=new Ka(this.manager);e.setResponseType("arraybuffer");e.setPath(this.path);e.load(a,function(a){a=a.slice(0);ye.getContext().decodeAudioData(a,function(a){b(a)})},c,d)},setPath:function(a){this.path=a; +return this}});Object.assign(Gf.prototype,{update:function(){var a,b,c,d,e,f,g,h,k=new J,l=new J;return function(m){if(a!==this||b!==m.focus||c!==m.fov||d!==m.aspect*this.aspect||e!==m.near||f!==m.far||g!==m.zoom||h!==this.eyeSep){a=this;b=m.focus;c=m.fov;d=m.aspect*this.aspect;e=m.near;f=m.far;g=m.zoom;var n=m.projectionMatrix.clone();h=this.eyeSep/2;var p=h*e/b,t=e*Math.tan(H.DEG2RAD*c*.5)/g;l.elements[12]=-h;k.elements[12]=h;var r=-t*d+p;var u=t*d+p;n.elements[0]=2*e/(u-r);n.elements[8]=(u+r)/ +(u-r);this.cameraL.projectionMatrix.copy(n);r=-t*d-p;u=t*d-p;n.elements[0]=2*e/(u-r);n.elements[8]=(u+r)/(u-r);this.cameraR.projectionMatrix.copy(n)}this.cameraL.matrixWorld.copy(m.matrixWorld).multiply(l);this.cameraR.matrixWorld.copy(m.matrixWorld).multiply(k)}}()});ld.prototype=Object.create(E.prototype);ld.prototype.constructor=ld;Object.assign(we.prototype,{start:function(){this.oldTime=this.startTime=("undefined"===typeof performance?Date:performance).now();this.elapsedTime=0;this.running=!0}, +stop:function(){this.getElapsedTime();this.autoStart=this.running=!1},getElapsedTime:function(){this.getDelta();return this.elapsedTime},getDelta:function(){var a=0;if(this.autoStart&&!this.running)return this.start(),0;if(this.running){var b=("undefined"===typeof performance?Date:performance).now();a=(b-this.oldTime)/1E3;this.oldTime=b;this.elapsedTime+=a}return a}});xe.prototype=Object.assign(Object.create(E.prototype),{constructor:xe,getInput:function(){return this.gain},removeFilter:function(){null!== +this.filter&&(this.gain.disconnect(this.filter),this.filter.disconnect(this.context.destination),this.gain.connect(this.context.destination),this.filter=null);return this},getFilter:function(){return this.filter},setFilter:function(a){null!==this.filter?(this.gain.disconnect(this.filter),this.filter.disconnect(this.context.destination)):this.gain.disconnect(this.context.destination);this.filter=a;this.gain.connect(this.filter);this.filter.connect(this.context.destination);return this},getMasterVolume:function(){return this.gain.gain.value}, +setMasterVolume:function(a){this.gain.gain.setTargetAtTime(a,this.context.currentTime,.01);return this},updateMatrixWorld:function(){var a=new n,b=new aa,c=new n,d=new n,e=new we;return function(f){E.prototype.updateMatrixWorld.call(this,f);f=this.context.listener;var g=this.up;this.timeDelta=e.getDelta();this.matrixWorld.decompose(a,b,c);d.set(0,0,-1).applyQuaternion(b);if(f.positionX){var h=this.context.currentTime+this.timeDelta;f.positionX.linearRampToValueAtTime(a.x,h);f.positionY.linearRampToValueAtTime(a.y, +h);f.positionZ.linearRampToValueAtTime(a.z,h);f.forwardX.linearRampToValueAtTime(d.x,h);f.forwardY.linearRampToValueAtTime(d.y,h);f.forwardZ.linearRampToValueAtTime(d.z,h);f.upX.linearRampToValueAtTime(g.x,h);f.upY.linearRampToValueAtTime(g.y,h);f.upZ.linearRampToValueAtTime(g.z,h)}else f.setPosition(a.x,a.y,a.z),f.setOrientation(d.x,d.y,d.z,g.x,g.y,g.z)}}()});lc.prototype=Object.assign(Object.create(E.prototype),{constructor:lc,getOutput:function(){return this.gain},setNodeSource:function(a){this.hasPlaybackControl= +!1;this.sourceType="audioNode";this.source=a;this.connect();return this},setMediaElementSource:function(a){this.hasPlaybackControl=!1;this.sourceType="mediaNode";this.source=this.context.createMediaElementSource(a);this.connect();return this},setBuffer:function(a){this.buffer=a;this.sourceType="buffer";this.autoplay&&this.play();return this},play:function(){if(!0===this.isPlaying)console.warn("THREE.Audio: Audio is already playing.");else if(!1===this.hasPlaybackControl)console.warn("THREE.Audio: this Audio has no playback control."); +else{var a=this.context.createBufferSource();a.buffer=this.buffer;a.loop=this.loop;a.onended=this.onEnded.bind(this);this.startTime=this.context.currentTime;a.start(this.startTime,this.offset);this.isPlaying=!0;this.source=a;this.setDetune(this.detune);this.setPlaybackRate(this.playbackRate);return this.connect()}},pause:function(){if(!1===this.hasPlaybackControl)console.warn("THREE.Audio: this Audio has no playback control.");else return!0===this.isPlaying&&(this.source.stop(),this.source.onended= +null,this.offset+=(this.context.currentTime-this.startTime)*this.playbackRate,this.isPlaying=!1),this},stop:function(){if(!1===this.hasPlaybackControl)console.warn("THREE.Audio: this Audio has no playback control.");else return this.source.stop(),this.source.onended=null,this.offset=0,this.isPlaying=!1,this},connect:function(){if(0d&&this._mixBufferRegion(c,a,3*b,1-d,b);d=b;for(var f=b+b;d!==f;++d)if(c[d]!==c[d+b]){e.setValue(c,a);break}},saveOriginalState:function(){var a=this.buffer,b=this.valueSize,c=3*b;this.binding.getValue(a,c);for(var d=b;d!==c;++d)a[d]=a[c+d%b];this.cumulativeWeight=0},restoreOriginalState:function(){this.binding.setValue(this.buffer,3*this.valueSize)},_select:function(a, +b,c,d,e){if(.5<=d)for(d=0;d!==e;++d)a[b+d]=a[c+d]},_slerp:function(a,b,c,d){aa.slerpFlat(a,b,a,b,a,c,d)},_lerp:function(a,b,c,d,e){for(var f=1-d,g=0;g!==e;++g){var h=b+g;a[h]=a[h]*f+a[c+g]*d}}});Object.assign(Hf.prototype,{getValue:function(a,b){this.bind();var c=this._bindings[this._targetGroup.nCachedObjects_];void 0!==c&&c.getValue(a,b)},setValue:function(a,b){for(var c=this._bindings,d=this._targetGroup.nCachedObjects_,e=c.length;d!==e;++d)c[d].setValue(a,b)},bind:function(){for(var a=this._bindings, +b=this._targetGroup.nCachedObjects_,c=a.length;b!==c;++b)a[b].bind()},unbind:function(){for(var a=this._bindings,b=this._targetGroup.nCachedObjects_,c=a.length;b!==c;++b)a[b].unbind()}});Object.assign(ma,{Composite:Hf,create:function(a,b,c){return a&&a.isAnimationObjectGroup?new ma.Composite(a,b,c):new ma(a,b,c)},sanitizeNodeName:function(){var a=/[\[\]\.:\/]/g;return function(b){return b.replace(/\s/g,"_").replace(a,"")}}(),parseTrackName:function(){var a="[^"+"\\[\\]\\.:\\/".replace("\\.","")+"]", +b=/((?:WC+[\/:])*)/.source.replace("WC","[^\\[\\]\\.:\\/]");a=/(WCOD+)?/.source.replace("WCOD",a);var c=/(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace("WC","[^\\[\\]\\.:\\/]"),d=/\.(WC+)(?:\[(.+)\])?/.source.replace("WC","[^\\[\\]\\.:\\/]"),e=new RegExp("^"+b+a+c+d+"$"),f=["material","materials","bones"];return function(a){var b=e.exec(a);if(!b)throw Error("PropertyBinding: Cannot parse trackName: "+a);b={nodeName:b[2],objectName:b[3],objectIndex:b[4],propertyName:b[5],propertyIndex:b[6]};var c=b.nodeName&& +b.nodeName.lastIndexOf(".");if(void 0!==c&&-1!==c){var d=b.nodeName.substring(c+1);-1!==f.indexOf(d)&&(b.nodeName=b.nodeName.substring(0,c),b.objectName=d)}if(null===b.propertyName||0===b.propertyName.length)throw Error("PropertyBinding: can not parse propertyName from trackName: "+a);return b}}(),findNode:function(a,b){if(!b||""===b||"root"===b||"."===b||-1===b||b===a.name||b===a.uuid)return a;if(a.skeleton){var c=a.skeleton.getBoneByName(b);if(void 0!==c)return c}if(a.children){var d=function(a){for(var c= +0;c=b){var n=b++,q=a[n];c[q.uuid]=l;a[l]=q;c[k]=n;a[n]=h;h=0;for(k=e;h!==k;++h){q=d[h];var v=q[l];q[l]=q[n];q[n]=v}}}this.nCachedObjects_=b},uncache:function(){for(var a=this._objects,b=a.length,c=this.nCachedObjects_,d=this._indicesByUUID,e=this._bindings,f=e.length,g=0,h=arguments.length;g!==h;++g){var k=arguments[g].uuid,l=d[k];if(void 0!==l)if(delete d[k],lb||0===c)return;this._startTime=null;b*=c}b*=this._updateTimeScale(a);c=this._updateTime(b);a=this._updateWeight(a);if(0c.parameterPositions[1]&&(this.stopFading(),0===d&&(this.enabled=!1))}}return this._effectiveWeight=b},_updateTimeScale:function(a){var b=0;if(!this.paused){b=this.timeScale;var c=this._timeScaleInterpolant;if(null!==c){var d=c.evaluate(a)[0]; +b*=d;a>c.parameterPositions[1]&&(this.stopWarping(),0===b?this.paused=!0:this.timeScale=b)}}return this._effectiveTimeScale=b},_updateTime:function(a){var b=this.time+a,c=this._clip.duration,d=this.loop,e=this._loopCount,f=2202===d;if(0===a)return-1===e?b:f&&1===(e&1)?c-b:b;if(2200===d)a:{if(-1===e&&(this._loopCount=0,this._setEndings(!0,!0,!1)),b>=c)b=c;else if(0>b)b=0;else break a;this.clampWhenFinished?this.paused=!0:this.enabled=!1;this._mixer.dispatchEvent({type:"finished",action:this,direction:0> +a?-1:1})}else{-1===e&&(0<=a?(e=0,this._setEndings(!0,0===this.repetitions,f)):this._setEndings(0===this.repetitions,!0,f));if(b>=c||0>b){d=Math.floor(b/c);b-=c*d;e+=Math.abs(d);var g=this.repetitions-e;0>=g?(this.clampWhenFinished?this.paused=!0:this.enabled=!1,b=0a,this._setEndings(a,!a,f)):this._setEndings(!1,!1,f),this._loopCount=e,this._mixer.dispatchEvent({type:"loop",action:this,loopDelta:d}))}if(f&& +1===(e&1))return this.time=b,c-b}return this.time=b},_setEndings:function(a,b,c){var d=this._interpolantSettings;c?(d.endingStart=2401,d.endingEnd=2401):(d.endingStart=a?this.zeroSlopeAtStart?2401:2400:2402,d.endingEnd=b?this.zeroSlopeAtEnd?2401:2400:2402)},_scheduleFading:function(a,b,c){var d=this._mixer,e=d.time,f=this._weightInterpolant;null===f&&(this._weightInterpolant=f=d._lendControlInterpolant());d=f.parameterPositions;f=f.sampleValues;d[0]=e;f[0]=b;d[1]=e+a;f[1]=c;return this}});Ce.prototype= +Object.assign(Object.create(ka.prototype),{constructor:Ce,_bindAction:function(a,b){var c=a._localRoot||this._root,d=a._clip.tracks,e=d.length,f=a._propertyBindings;a=a._interpolants;var g=c.uuid,h=this._bindingsByRootAndName,k=h[g];void 0===k&&(k={},h[g]=k);for(h=0;h!==e;++h){var l=d[h],n=l.name,q=k[n];if(void 0===q){q=f[h];if(void 0!==q){null===q._cacheIndex&&(++q.referenceCount,this._addInactiveBinding(q,g,n));continue}q=new Be(ma.create(c,n,b&&b._propertyBindings[h].binding.parsedPath),l.ValueTypeName, +l.getValueSize());++q.referenceCount;this._addInactiveBinding(q,g,n)}f[h]=q;a[h].resultBuffer=q.buffer}},_activateAction:function(a){if(!this._isActiveAction(a)){if(null===a._cacheIndex){var b=(a._localRoot||this._root).uuid,c=a._clip.uuid,d=this._actionsByClip[c];this._bindAction(a,d&&d.knownActions[0]);this._addInactiveAction(a,c,b)}b=a._propertyBindings;c=0;for(d=b.length;c!==d;++c){var e=b[c];0===e.useCount++&&(this._lendBinding(e),e.saveOriginalState())}this._lendAction(a)}},_deactivateAction:function(a){if(this._isActiveAction(a)){for(var b= +a._propertyBindings,c=0,d=b.length;c!==d;++c){var e=b[c];0===--e.useCount&&(e.restoreOriginalState(),this._takeBackBinding(e))}this._takeBackAction(a)}},_initMemoryManager:function(){this._actions=[];this._nActiveActions=0;this._actionsByClip={};this._bindings=[];this._nActiveBindings=0;this._bindingsByRootAndName={};this._controlInterpolants=[];this._nActiveControlInterpolants=0;var a=this;this.stats={actions:{get total(){return a._actions.length},get inUse(){return a._nActiveActions}},bindings:{get total(){return a._bindings.length}, +get inUse(){return a._nActiveBindings}},controlInterpolants:{get total(){return a._controlInterpolants.length},get inUse(){return a._nActiveControlInterpolants}}}},_isActiveAction:function(a){a=a._cacheIndex;return null!==a&&athis.max.x||a.ythis.max.y?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y},getParameter:function(a,b){void 0===b&&(console.warn("THREE.Box2: .getParameter() target is now required"),b=new B); +return b.set((a.x-this.min.x)/(this.max.x-this.min.x),(a.y-this.min.y)/(this.max.y-this.min.y))},intersectsBox:function(a){return a.max.xthis.max.x||a.max.ythis.max.y?!1:!0},clampPoint:function(a,b){void 0===b&&(console.warn("THREE.Box2: .clampPoint() target is now required"),b=new B);return b.copy(a).clamp(this.min,this.max)},distanceToPoint:function(){var a=new B;return function(b){return a.copy(b).clamp(this.min,this.max).sub(b).length()}}(),intersect:function(a){this.min.max(a.min); +this.max.min(a.max);return this},union:function(a){this.min.min(a.min);this.max.max(a.max);return this},translate:function(a){this.min.add(a);this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&&a.max.equals(this.max)}});Object.assign(Ie.prototype,{set:function(a,b){this.start.copy(a);this.end.copy(b);return this},clone:function(){return(new this.constructor).copy(this)},copy:function(a){this.start.copy(a.start);this.end.copy(a.end);return this},getCenter:function(a){void 0=== +a&&(console.warn("THREE.Line3: .getCenter() target is now required"),a=new n);return a.addVectors(this.start,this.end).multiplyScalar(.5)},delta:function(a){void 0===a&&(console.warn("THREE.Line3: .delta() target is now required"),a=new n);return a.subVectors(this.end,this.start)},distanceSq:function(){return this.start.distanceToSquared(this.end)},distance:function(){return this.start.distanceTo(this.end)},at:function(a,b){void 0===b&&(console.warn("THREE.Line3: .at() target is now required"),b= +new n);return this.delta(b).multiplyScalar(a).add(this.start)},closestPointToPointParameter:function(){var a=new n,b=new n;return function(c,d){a.subVectors(c,this.start);b.subVectors(this.end,this.start);c=b.dot(b);c=b.dot(a)/c;d&&(c=H.clamp(c,0,1));return c}}(),closestPointToPoint:function(a,b,c){a=this.closestPointToPointParameter(a,b);void 0===c&&(console.warn("THREE.Line3: .closestPointToPoint() target is now required"),c=new n);return this.delta(c).multiplyScalar(a).add(this.start)},applyMatrix4:function(a){this.start.applyMatrix4(a); +this.end.applyMatrix4(a);return this},equals:function(a){return a.start.equals(this.start)&&a.end.equals(this.end)}});md.prototype=Object.create(E.prototype);md.prototype.constructor=md;md.prototype.isImmediateRenderObject=!0;nd.prototype=Object.create(W.prototype);nd.prototype.constructor=nd;nd.prototype.update=function(){var a=new n,b=new n,c=new pa;return function(){var d=["a","b","c"];this.object.updateMatrixWorld(!0);c.getNormalMatrix(this.object.matrixWorld);var e=this.object.matrixWorld,f= +this.geometry.attributes.position,g=this.object.geometry;if(g&&g.isGeometry)for(var h=g.vertices,k=g.faces,l=g=0,n=k.length;lMath.abs(b)&&(b=1E-8);this.scale.set(.5*this.size,.5*this.size,b);this.children[0].material.side=0>b?1:0;this.lookAt(this.plane.normal);E.prototype.updateMatrixWorld.call(this,a)};var Wd,Je;eb.prototype=Object.create(E.prototype);eb.prototype.constructor=eb;eb.prototype.setDirection=function(){var a=new n,b;return function(c){.99999c.y?this.quaternion.set(1,0,0,0):(a.set(c.z,0,-c.x).normalize(),b=Math.acos(c.y), +this.quaternion.setFromAxisAngle(a,b))}}();eb.prototype.setLength=function(a,b,c){void 0===b&&(b=.2*a);void 0===c&&(c=.2*b);this.line.scale.set(1,Math.max(0,a-b),1);this.line.updateMatrix();this.cone.scale.set(c,b,c);this.cone.position.y=a;this.cone.updateMatrix()};eb.prototype.setColor=function(a){this.line.material.color.copy(a);this.cone.material.color.copy(a)};eb.prototype.copy=function(a){E.prototype.copy.call(this,a,!1);this.line.copy(a.line);this.cone.copy(a.cone);return this};eb.prototype.clone= +function(){return(new this.constructor).copy(this)};td.prototype=Object.create(W.prototype);td.prototype.constructor=td;L.create=function(a,b){console.log("THREE.Curve.create() has been deprecated");a.prototype=Object.create(L.prototype);a.prototype.constructor=a;a.prototype.getPoint=b;return a};Object.assign(cb.prototype,{createPointsGeometry:function(a){console.warn("THREE.CurvePath: .createPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.");a=this.getPoints(a); +return this.createGeometry(a)},createSpacedPointsGeometry:function(a){console.warn("THREE.CurvePath: .createSpacedPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.");a=this.getSpacedPoints(a);return this.createGeometry(a)},createGeometry:function(a){console.warn("THREE.CurvePath: .createGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.");for(var b=new G,c=0,d=a.length;c=0;n--){ + if(assetloader.modellist[n].deviceType == "train"){ + newmesh = assetloader.modellist[n].mesh.clone(true); + + ntracks1 = assetloader.modellist[n].animations.slice(24,52); + + tclip = new THREE.AnimationClip("three",2,ntracks1); + + ntracks2 = assetloader.modellist[n].animations.slice(0,24); + + fclip = new THREE.AnimationClip("four",2,ntracks2); + + n = 0; + } + } + actionss[data[i].code] = { + top:[], + down:[] + }; + for(let j=0;j=1;n--){ + // newmesh.children[n].position.x = -76; + // newmesh.children[n-1].add(newmesh.children[n]); + // } + //创建列车字牌 + let textgeometry = new THREE.PlaneBufferGeometry( 150, 100, 1 ); + let textt = new THREE.CanvasTexture(getTextCanvas(data[i])); + let textmaterial = new THREE.MeshBasicMaterial( { side: THREE.DoubleSide,map:textt ,transparent: true} ); + let textplane = new THREE.Mesh( textgeometry, textmaterial ); + textplane.name = data[i].code; + textplane.position.x = -50; + textplane.position.y = 130; + textplane.tcode = data[i].code; + //textplane.rotation.x = Math.PI; + scope.textlist.push(textplane); + newmesh.children[0].add(textplane); + + textgeometry.dispose(); + textmaterial.dispose(); + textt.dispose(); + //group.add(textplane); + newmesh.first = false; + newmesh.name = data[i].code; + newmesh.rname = data[i].groupNumber; + newmesh.groupNumber = data[i].groupNumber; + + newmesh.position.set(0,-50000,0); + newmesh.children[0].position.y = 0; + //newmesh.rotation.y = Math.PI/2; + newmesh.status = "00"; + newmesh.nowcode = null; + newmesh.curve = null; + newmesh.doorStatus = "01"; + newmesh.dispose = true; + newmesh.nowsection = null; + newmesh.nextsection = null; + newmesh.speed = 0; + newmesh.speeds = 0; + newmesh.progress = null; + + + scope.list[data[i].code] = newmesh; + //scene.add(newmesh); + } + scene.add(scope.group); + resolve("loadedtrain"); + }); + } + +} + +let onProgress = function ( xhr ) { + + if ( xhr.lengthComputable ) { + + let percentComplete = xhr.loaded / xhr.total * 100; + console.log( Math.round( percentComplete, 2 ) + '% downloaded' ); + + } + +}; + +let onError = function () { }; + +var beauty = new Image(); +beauty.src = "../../static/texture/guide.png"; +//canvas文字贴图方法 +//PS:待提炼 增强功能 +function getTextCanvas(text){ + var canvas = document.getElementById('canvastexture'); + + canvas.width = 128; + canvas.height = 96; + + var ctx = canvas.getContext('2d'); + + //var bg = canvas.createPattern(img, "no-repeat"); + //ctx.fillStyle = bg; + ctx.fillRect(0, 0,128,96); + ctx.font = "10px Verdana"; + ctx.fillStyle = '#000000'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.clearRect(0,0,128,96); + //console.log(text.groupNumber); + ctx.drawImage(beauty,0,0,128, 96); + ctx.fillText("车组号:"+text.groupNumber, 60,15); + ctx.fillText("车组人员:XXX", 60,31); + ctx.fillText("速度:XXX.XXX", 60,43); + //ctx.fillText(text.trainModel.name, width/2,height*3/4); + let data = ctx.getImageData(0, 0,128,96); + return data; +} diff --git a/src/jlmap3d/model/TrainModel.js b/src/jlmap3d/model/TrainModel.js new file mode 100644 index 000000000..7bf6a92cb --- /dev/null +++ b/src/jlmap3d/model/TrainModel.js @@ -0,0 +1,8 @@ +export function TrainModel(opts) { + + var scope = this; + + this.mesh = null; + this.meshurl = null; + this.picurl = null; +} diff --git a/src/jlmap3d/utils/ReStart.js b/src/jlmap3d/utils/ReStart.js new file mode 100644 index 000000000..83472d2c4 --- /dev/null +++ b/src/jlmap3d/utils/ReStart.js @@ -0,0 +1,55 @@ +export function ReStart(mapdata){ + + let trainlist = mapdata.trainlisttest; + let signallist = mapdata.signallist; + for(let i=0;i5){ + let j = trainlist.list[i].movelist.length-1; + trainlist.list[i].position.x = (trainlist.list[i].movelist[j].points[0].x+trainlist.list[i].movelist[j].points[1].x)/2; + trainlist.list[i].position.z = (trainlist.list[i].movelist[j].points[0].z+trainlist.list[i].movelist[j].points[1].z)/2; + trainlist.list[i].movelist = []; + } + } + + for(var j=0;j=0;j--){ + //判断是否有移动事件 + if(traindata.group.children[j].dispose == false){ + + if(traindata.group.children[j].progress != null){ + + let trainmodel = traindata.group.children[j]; + + if(trainmodel.speeds > 0 && trainmodel.speeds){ + + let speed = null; + if(traindata.group.children[j].progress<1){ + // console.log(trainmodel.rname); + // console.log(trainmodel.speeds); + // console.log(trainmodel.progress); + let movecurve = trainmodel.curve; + + if(trainmodel.status == "03"){//向左 + // console.log(traindata.group.children[j].rname); + // console.log(traindata.group.children[j]); + if(movecurve){ + let point = movecurve.getPointAt(trainmodel.progress); + if(Math.abs( point.z -trainmodel.children[0].matrixWorld.elements[14]) >0.05){ + + trainmodel.children[0].up = new THREE.Vector3(-1,0,0); + let tangent = movecurve.getTangentAt(traindata.group.children[j].progress).normalize(); + trainmodel.children[0].axis.crossVectors(trainmodel.children[0].up, tangent).normalize(); + let radians = Math.acos(trainmodel.children[0].up.dot(tangent)); + trainmodel.children[0].quaternion.setFromAxisAngle(trainmodel.children[0].axis, radians); + + if(trainmodel.children[0].rotation.y>(Math.PI-0.05) &&trainmodel.children[0].rotation.y<(Math.PI+0.05)){ + trainmodel.children[0].rotation.y = Math.PI; + } + + let offsetz = parseFloat(trainmodel.children[0].matrixWorld.elements[14]) - parseFloat(point.z); + trainmodel.children[0].position.z += offsetz; + let rotas = { + posr:point, + rota:trainmodel.children[0].rotation.y, + posz:trainmodel.children[0].position.z + } + trainmodel.children[1].rotalist.push(rotas); + //trainmodel.position.z = point.z; + } + + if(trainmodel.children[1].rotalist.length > 0 || trainmodel.children[2].rotalist.length > 0 || trainmodel.children[3].rotalist.length > 0 || trainmodel.children[4].rotalist.length > 0|| trainmodel.children[5].rotalist.length > 0){ + for(let rs = 1;rs<6;rs++){ + // console.log("train:"+rs); + // console.log(trainmodel.children[rs].rotalist.length); + if(trainmodel.children[rs].rotalist[0]){ + + + for(let xh=0;xh0.1){ + + trainmodel.children[0].up = new THREE.Vector3(1,0,0); + let tangent = movecurve.getTangentAt(traindata.group.children[j].progress).normalize(); + trainmodel.children[0].axis.crossVectors(trainmodel.children[0].up, tangent).normalize(); + let radians = Math.acos(trainmodel.children[0].up.dot(tangent)); + trainmodel.children[0].quaternion.setFromAxisAngle(trainmodel.children[0].axis, radians); + if(trainmodel.children[0].rotation.y<0.05 && trainmodel.children[0].rotation.y>-0.05){ + trainmodel.children[0].rotation.y = 0; + } + + + let offsetz = parseFloat(point.z) - parseFloat(trainmodel.children[0].matrixWorld.elements[14]); + trainmodel.children[0].position.z += offsetz; + let rotas = { + posr:point, + rota:trainmodel.children[0].rotation.y, + posz:trainmodel.children[0].position.z + } + trainmodel.children[1].rotalist.push(rotas); + //trainmodel.position.z = point.z; + } + + + if(trainmodel.children[1].rotalist.length > 0 || trainmodel.children[2].rotalist.length > 0 || trainmodel.children[3].rotalist.length > 0 || trainmodel.children[4].rotalist.length > 0|| trainmodel.children[5].rotalist.length > 0){ + + for(let rs = 1;rs<6;rs++){ + + if(trainmodel.children[rs].rotalist[0]){ + + for(let xh=0;xh=trainmodel.children[rs].rotalist[0].posr.x){ + let offsetz = parseFloat(trainmodel.children[rs].rotalist[0].posr.z) - parseFloat(trainmodel.children[rs].matrixWorld.elements[14]); + + trainmodel.children[rs].position.z = trainmodel.children[rs].rotalist[0].posz; + if(rs != 5){ + let asd = trainmodel.children[rs].rotalist[0]; + trainmodel.children[rs+1].rotalist.push(asd); + } + //let offsetx = trainmodel.children[1].matrixWorld.elements[12]-trainmodel.children[0].children[3].matrixWorld.elements[12]; + + trainmodel.children[rs].rotation.y = trainmodel.children[rs].rotalist[0].rota; + trainmodel.children[rs].rotalist.splice(0,1) + xh--; + }else{ + xh = trainmodel.children[rs].rotalist.length; + } + } + //console.log(trainmodel.children[rs].rotalist.length); + + } + + + } + + } + trainmodel.position.x = point.x; + trainmodel.position.y = 0; + + + if(trainmodel.isStandTrack == true){ + trainmodel.progress += (trainmodel.speeds*2); + }else{ + trainmodel.progress += trainmodel.speeds; + } + } + } + + } + + } + } + } + + } + + } + +} diff --git a/src/jlmap3d/utils/UpdateTrainTest.js b/src/jlmap3d/utils/UpdateTrainTest.js new file mode 100644 index 000000000..a5d6c7e6a --- /dev/null +++ b/src/jlmap3d/utils/UpdateTrainTest.js @@ -0,0 +1,144 @@ + +export function UpdateTrain(camera,traindata){ + if(traindata != undefined ){ + for(let j=traindata.group.children.length-1;j>=0;j--){ + //判断是否有移动事件 + if(traindata.group.children[j].dispose == false){ + + if(traindata.group.children[j].progress != null){ + + let trainmodel = traindata.group.children[j]; + + if(trainmodel.speeds > 0 && trainmodel.speeds){ + + let speed = null; + if(traindata.group.children[j].progress<1){ + // console.log(trainmodel.rname); + // console.log(trainmodel.speeds); + // console.log(trainmodel.progress); + let movecurve = trainmodel.curve; + + if(trainmodel.status == "03"){//向左 + let point = movecurve.getPointAt(traindata.group.children[j].progress); + if(Math.abs( point.z -trainmodel.children[0].matrixWorld.elements[14]) >0.1){ + trainmodel.children[0].up = new THREE.Vector3(-1,0,0); + let tangent = movecurve.getTangentAt(traindata.group.children[j].progress).normalize(); + trainmodel.children[0].axis.crossVectors(trainmodel.children[0].up, tangent).normalize(); + let radians = Math.acos(trainmodel.children[0].up.dot(tangent)); + trainmodel.children[0].quaternion.setFromAxisAngle(trainmodel.children[0].axis, radians); + let rotas = { + posr:point, + rota:trainmodel.children[0].rotation.y + } + trainmodel.children[1].rotalist.push(rotas); + + let offsetz = parseFloat(trainmodel.children[0].matrixWorld.elements[14]) - parseFloat(point.z); + trainmodel.children[0].position.z += offsetz; + //trainmodel.position.z = point.z; + } + + trainmodel.position.x = point.x; + trainmodel.position.y = 0; + + if(trainmodel.children[1].rotalist.length > 0 || trainmodel.children[2].rotalist.length > 0 || trainmodel.children[3].rotalist.length > 0 || trainmodel.children[4].rotalist.length > 0|| trainmodel.children[5].rotalist.length > 0){ + for(let rs = 1;rs<6;rs++){ + //console.log(rs); + if(trainmodel.children[rs].rotalist[0]){ + let offsetz = parseFloat(trainmodel.children[rs].matrixWorld.elements[14]) - parseFloat(trainmodel.children[rs].rotalist[0].posr.z); + + trainmodel.children[rs].position.z += offsetz; + + for(let xh=0;xh0.1){ + trainmodel.children[0].up = new THREE.Vector3(1,0,0); + let tangent = movecurve.getTangentAt(traindata.group.children[j].progress).normalize(); + trainmodel.children[0].axis.crossVectors(trainmodel.children[0].up, tangent).normalize(); + let radians = Math.acos(trainmodel.children[0].up.dot(tangent)); + trainmodel.children[0].quaternion.setFromAxisAngle(trainmodel.children[0].axis, radians); + + let rotas = { + posr:point, + rota:trainmodel.children[0].rotation.y + } + trainmodel.children[1].rotalist.push(rotas); + let offsetz = parseFloat(point.z) - parseFloat(trainmodel.children[0].matrixWorld.elements[14]); + trainmodel.children[0].position.z += offsetz; + //trainmodel.position.z = point.z; + } + + trainmodel.position.x = point.x; + trainmodel.position.y = 0; + if(trainmodel.children[1].rotalist.length > 0 || trainmodel.children[2].rotalist.length > 0 || trainmodel.children[3].rotalist.length > 0 || trainmodel.children[4].rotalist.length > 0|| trainmodel.children[5].rotalist.length > 0){ + + for(let rs = 1;rs<6;rs++){ + //console.log(rs); + if(trainmodel.children[rs].rotalist[0]){ + + let offsetz = parseFloat(trainmodel.children[rs].rotalist[0].posr.z) - parseFloat(trainmodel.children[rs].matrixWorld.elements[14]); + trainmodel.children[rs].position.z += offsetz; + + for(let xh=0;xh=trainmodel.children[rs].rotalist[0].posr.x){ + + if(rs != 5){ + let asd = trainmodel.children[rs].rotalist[0]; + trainmodel.children[rs+1].rotalist.push(asd); + + } + //let offsetx = trainmodel.children[1].matrixWorld.elements[12]-trainmodel.children[0].children[3].matrixWorld.elements[12]; + + trainmodel.children[rs].rotation.y = trainmodel.children[rs].rotalist[0].rota; + trainmodel.children[rs].rotalist.splice(0,1) + xh--; + }else{ + xh = trainmodel.children[rs].rotalist.length; + } + } + //console.log(trainmodel.children[rs].rotalist.length); + + } + + + } + // console.log(trainmodel.rotalist); + } + + trainmodel.progress += trainmodel.speeds; + } + + } + + } + } + } + + } + + } + +} diff --git a/src/main.js b/src/main.js index 1538547fa..a645e483f 100644 --- a/src/main.js +++ b/src/main.js @@ -16,12 +16,13 @@ import router from './router'; import '@/icons'; // icon import '@/permission'; // permission control import '@/scripts/GlobalPlugin'; +import '@/directives'; Vue.use(ElementUI); Vue.config.productionTip = false; -new Vue({ +window.eventBus = new Vue({ el: '#app', router, store, diff --git a/src/permission.js b/src/permission.js index 85c3b4912..a863cda63 100644 --- a/src/permission.js +++ b/src/permission.js @@ -66,7 +66,7 @@ function handleRoute(to, from, next, routeInfo) { }).catch(() => { store.dispatch('FedLogOut', routeInfo.clientId).then(() => { - Vue.$messageBox('验证失败,请重新登陆!'); + Vue.prototype.$messageBox('验证失败,请重新登陆!'); next({ path: routeInfo.loginPath }); }); }); diff --git a/src/router/index.js b/src/router/index.js index eae622547..1fde59b45 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -49,7 +49,71 @@ export const UrlConfig = { runPlan: '/map/runPlan', runPlanView: '/map/runPlan/view', product: '/map/product', - productDraft: '/map/product/draft' + productDraft: '/map/product/draft', + }, + lesson: { + prefix: '/lesson', + record: '/lesson/record/training', + manage: '/lesson/manage/training' + }, + teach: { + prefix: '/teach', + detail: '/teach/detail', + practical: '/teach/practical', + pay: '/teach/pay', + }, + exam: { + prefix: '/exam', + detail: '/exam/detail', + course: '/exam/course', + examRuleManage: '/exam/examRule/manage', + pay: '/exam/pay' + }, + demonstration: { + prefix: '/demonstration', + detail: '/demonstration/detail', + pay: '/demonstration/pay' + }, + dp: { + prefix: '/dp', + detail: '/dp/detail', + pay: '/dp/pay' + + }, + plan: { + prefix: '/plan', + tool: '/plan/tool', + detail: '/plan/detail', + pay: '/plan/pay' + }, + replay: { + prefix: '/replay' + }, + permission: { + prefix: '/permission', + permissionList: '/permission/manage', + permissionDraft: '/permission/draft', + permission: '/permission/restore' + }, + publish: { + prefix: '/publish', + map: '/publish/map', + mapDraft: '/publish/map/draft', + runPlanEveryDay: '/publish/runPlan/everyDay', + runPlanTemplate: '/publish/runPlan/template', + runPlanCommon: '/publish/runPlan/common', + runPlanView: '/publish/runPlan/view', + }, + orderauthor: { + prefix: '/orderauthor', + commodityManage: '/orderauthor/commodity/manage', + commodityDraft: '/orderauthor/commodity/draft', + orderManage: '/orderauthor/order/manage', + orderDraft: '/orderauthor/order/draft', + authorMange: '/orderauthor/author/manage', + authorDraft: '/orderauthor/author/draft', + transferDetail: '/orderauthor/transfer/detail', + trainferPackage: '/orderauthor/transfer/package' } }; @@ -70,7 +134,35 @@ export const constantRoutes = [ component: () => import('@/views/login/index'), hidden: true }, - + // 扫码登录 + { + path: '/dp/login', + component: resolve => require(['@/views/login/loginNewScreen'], resolve), + hidden: true + }, + // 密码登录 + { + path: '/dp/login1', + component: resolve => require(['@/views/login/loginScreen'], resolve), + hidden: true + }, + // 扫码登录 + { + path: '/plan/login', + component: resolve => require(['@/views/login/loginNewPlan'], resolve), + hidden: true + }, + // 密码登录 + { + path: '/plan/login1', + component: resolve => require(['@/views/login/loginPlan'], resolve), + hidden: true + }, + { + path: '/jlmap3d/edit', + component: resolve => require(['@/views/jlmap3d/jlmap3dedit'], resolve), + hidden: true + }, { path: '/401', component: () => import('@/views/error-page/401'), @@ -93,91 +185,718 @@ export const constantRoutes = [ component: () => import('@/views/dashboard/index'), meta: { title: '首页', icon: 'dashboard' } }] - } + }, + + { path: '*', redirect: '/404', hidden: true } ]; -export function getUrlByPath(path, router) { -} - -export const routerMap = { - path: '/map', - name: '地图管理', - meta: { - title: '地图管理', - roles: [admin] - }, - component: Layout, - children: [ - { - path: 'manage', - name: '地图管理', - component: () => import('@/views/jmapTest/index'), - meta: { title: '地图管理', icon: 'map-mange' } - }, - // { - // path: 'skinStyle', - // name: '皮肤管理', - // component: resolve => require(['@/views/map/skinStyle/index'], resolve), - // meta: { - // title: '皮肤管理' - // } - // }, - // { - // path: 'skinStyle/draft/:mode/:skinStyleId', - // name: '操作权限', - // hidden: true, - // component: resolve => require(['@/views/map/skinStyle/draft'], resolve), - // meta: { - // title: '操作权限' - // } - // }, - { - path: 'draw', - redirect: '/map/draw/0/draft', - component: resolve => require(['@/views/map/mapdraft/index'], resolve), - meta: { - title: '地图绘制' - }, - children: [ - { - path: ':mapId/:view', - component: resolve => require(['@/views/map/mapdraft/mapedit/index'], resolve), - meta: { - title: '地图绘制' - }, - hidden: true - } - ] - } - // { - // path: 'runPlan', - // redirect: '/map/runPlan/view/draft', - // name: '运行图管理', - // component: resolve => require(['@/views/map/runplan/index'], resolve), - // meta: { - // title: '运行图管理' - // }, - // children: [{ - // path: 'view/:mode', - // component: resolve => require(['@/views/map/runplan/chart'], resolve), - // hidden: true - // }] - // } - // { - // path: 'product', - // name: '产品编辑', - // component: resolve => require(['@/views/lesson/trainingcategory/index'], resolve), - // meta: { - // title: '产品编辑' - // } - // } - ] -}; -export const router404 = { path: '*', redirect: '/404', hidden: true }; - export const asyncRouter = [ - routerMap, - router404 + { + path: '/', + component: Layout, + redirect: '/dashboard', + name: 'Dashboard', + hidden: true, + children: [ + { + path: 'dashboard', + component: resolve => require(['@/views/dashboard/index.vue'], resolve) + } + ] + }, + { + path: '/display/:mode', + component: resolve => require(['@/views/display/index'], resolve), + meta: { + title: '实训视图' + }, + hidden: true + }, + { + path: '/trainroom', + component: resolve => require(['@/views/trainRoom/index'], resolve), + meta: { + title: '综合演练房间' + }, + hidden: true + }, + { + path: '/jointTraining', + name: '联合演练试图', + component: resolve => require(['@/views/jointTraining/index'], resolve), + hidden: true + }, + { + path: '/jlmap3d', + name: '三维test', + component: resolve => require(['@/views/jlmap3d/index'], resolve), + meta: { + title: '三维test' + }, + hidden: true + }, + { + path: '/map', + name: '地图管理', + meta: { + title: '地图管理', + roles: [admin, mapCreater] + }, + component: Layout, + children: [ + { + path: 'skinStyle', + name: '皮肤管理', + component: resolve => require(['@/views/map/skinStyle/index'], resolve), + meta: { + title: '皮肤管理' + } + }, + { + path: 'skinStyle/draft/:mode/:skinStyleId', + name: '操作权限', + hidden: true, + component: resolve => require(['@/views/map/skinStyle/draft'], resolve), + meta: { + title: '操作权限' + } + }, + { + path: 'draw', + redirect: '/map/draw/0/draft', + component: resolve => require(['@/views/map/mapdraft/index'], resolve), + meta: { + title: '地图绘制' + }, + children: [ + { + path: ':mapId/:view', + component: resolve => require(['@/views/map/mapdraft/mapedit/index'], resolve), + meta: { + title: '地图绘制' + }, + hidden: true + } + ] + }, + { + path: 'runPlan', + redirect: '/map/runPlan/view/draft', + name: '运行图管理', + component: resolve => require(['@/views/map/runplan/index'], resolve), + meta: { + title: '运行图管理' + }, + children: [{ + path: 'view/:mode', + component: resolve => require(['@/views/map/runplan/chart'], resolve), + hidden: true + }] + }, + { + path: 'product', + name: '产品编辑', + component: resolve => require(['@/views/lesson/trainingcategory/index'], resolve), + meta: { + title: '产品编辑' + } + } + ] + }, + { + path: '/lesson', + component: Layout, + name: '课程管理', + meta: { + title: '课程管理', + roles: [admin, lessonCreater] + }, + children: [ + { + path: 'record', + redirect: '/lesson/record/training/0/null', + component: resolve => require(['@/views/lesson/trainingrecord/index'], resolve), + meta: { + title: '实训录制' + }, + children: [ + { + path: 'training/:trainingId/:trainingName', + component: resolve => require(['@/views/lesson/trainingrecord/manage/index'], resolve), + meta: { + title: '实训录制' + }, + hidden: true + } + ] + }, + { + path: 'manage/taskmanage', + name: '任务管理', + component: resolve => require(['@/views/lesson/taskmanage/list'], resolve), + meta: { + title: '任务管理' + } + }, + { + path: 'manage/trainingRule', + name: '操作定义', + component: resolve => require(['@/views/lesson/trainingRule/list'], resolve), + meta: { + title: '操作定义' + } + }, + { + path: 'manage/trainingRule/detail', + name: '操作定义明细', + hidden: true, + component: resolve => require(['@/views/lesson/trainingRule/detail/index'], resolve), + meta: { + title: '操作定义明细' + } + }, + { + path: 'manage/training', + name: '实训管理', + component: resolve => require(['@/views/lesson/trainingmanage/index'], resolve), + meta: { + title: '实训管理' + } + }, + { + path: 'edit', + name: '课程编辑', + component: resolve => require(['@/views/lesson/lessoncategory/index'], resolve), + meta: { + title: '课程编辑' + } + }, + { + path: 'manage/scriptmanage', + name: '剧本管理', + component: resolve => require(['@/views/lesson/scriptmanage/list'], resolve), + meta: { + title: '剧本管理' + } + } + ] + }, + { + path: '/teach', + redirect: '/teach/home', + name: '教学系统', + component: resolve => require(['@/views/teach/index'], resolve), + meta: { + roles: [admin, userLesson, user] + }, + children: [ + { + path: 'home', + component: resolve => require(['@/views/teach/home'], resolve), + meta: { + title: '教学系统' + } + }, + { + path: 'detail/:lessonId', + name: '课程详情', + component: resolve => require(['@/views/teach/detail/index'], resolve), + meta: { + title: '课程详情' + }, + hidden: true + }, + { + path: 'practical/:trainingId/:lessonId', + name: '实训详情', + component: resolve => require(['@/views/teach/practical/index'], resolve), + meta: { + title: '实训详情' + }, + hidden: true + }, + { + path: 'pay/:lessonId', + component: resolve => require(['@/views/components/pay/index'], resolve), + meta: { + }, + hidden: true + } + ] + }, + { + path: '/exam', + redirect: '/exam/home', + component: resolve => require(['@/views/exam/index'], resolve), + name: '考试系统', + meta: { + roles: [admin, userExam, user] + }, + children: [ + { + path: 'home', + component: resolve => require(['@/views/exam/home'], resolve), + meta: { + title: '考试系统' + } + }, + { + path: 'detail/:examId', + name: '试卷详情', + component: resolve => require(['@/views/exam/detail/examDetail'], resolve), + meta: { + title: '试卷详情' + }, + hidden: true + }, + { + path: 'course/:lessonId', + component: resolve => require(['@/views/exam/detail/courseDetail'], resolve), + meta: { + }, + hidden: true + }, + { + path: 'examRule/manage', + name: '试题设置', + component: resolve => require(['@/views/publish/examRule/index'], resolve), + meta: { + title: '试题设置' + }, + hidden: true + }, + { + path: 'examRule/draft/:mode/:ruleId/:lessonId', + hidden: true, + component: resolve => require(['@/views/publish/examRule/draft/index'], resolve), + meta: { + title: '添加订单' + } + }, + { + path: 'pay/:lessonId', + component: resolve => require(['@/views/components/pay/index'], resolve), + meta: { + }, + hidden: true + }, + { + path: 'questionDetail/:examQuestionId', + name: '开始考试', + component: resolve => require(['@/views/exam/detail/questionDetail'], resolve), + meta: { + title: '开始考试' + }, + hidden: true + }, + { + path: 'result/:userExamId', + name: '考试结果', + component: resolve => require(['@/views/exam/result'], resolve), + meta: { + title: '考试结果' + }, + hidden: true + } + ] + }, + { + path: '/demonstration', + redirect: '/demonstration/home', + component: resolve => require(['@/views/demonstration/index'], resolve), + name: '仿真系统', + meta: { + roles: [admin, userSimulation, user] + }, + children: [ + { + path: 'home', + component: resolve => require(['@/views/demonstration/home'], resolve), + meta: { + title: '仿真系统' + } + }, + { + path: 'detail/:mapId', + name: '地图详情', + component: resolve => require(['@/views/demonstration/detail/index'], resolve), + meta: { + title: '地图详情' + }, + hidden: true + }, + { + path: 'pay/:lessonId', + component: resolve => require(['@/views/components/pay/index'], resolve), + meta: { + }, + hidden: true + } + ] + }, + { + path: '/dp', + redirect: '/dp/home', + component: resolve => require(['@/views/screenMonitor/index'], resolve), + name: '大屏系统', + meta: { + roles: [admin, userScreen] + }, + children: [ + { + path: 'home', + component: resolve => require(['@/views/screenMonitor/home'], resolve), + meta: { + title: '大屏系统' + }, + target: true + }, + { + path: 'detail/:lessonId', + component: resolve => require(['@/views/screenMonitor/detail/index'], resolve), + meta: { + }, + hidden: true + }, + { + path: 'pay/:lessonId', + component: resolve => require(['@/views/components/pay/index'], resolve), + meta: { + }, + hidden: true + } + ] + }, + { + path: '/plan/tool', + component: resolve => require(['@/views/planMonitor/editTool/index'], resolve), + meta: { + }, + hidden: true + }, + { + path: '/plan', + redirect: '/plan/home', + component: resolve => require(['@/views/planMonitor/index'], resolve), + name: '琏计划', + meta: { + roles: [admin, user] + }, + children: [ + { + path: 'home', + component: resolve => require(['@/views/planMonitor/home'], resolve), + meta: { + title: '琏计划' + }, + target: true + }, + { + path: 'detail/:lessonId', + component: resolve => require(['@/views/planMonitor/detail'], resolve), + meta: { + }, + hidden: true + }, + { + path: 'pay/:lessonId', + component: resolve => require(['@/views/components/pay/index'], resolve), + meta: { + }, + hidden: true + } + ] + }, + { + path: '/replay', + component: Layout, + meta: { + roles: [admin] + }, + children: [ + { + path: 'manage', + component: resolve => require(['@/views/replay/index'], resolve), + meta: { + title: '回放管理' + } + } + ] + }, + { + path: '/permission', + component: Layout, + meta: { + roles: [admin, user] + }, + children: [ + { + path: 'manage', + component: resolve => require(['@/views/package/index'], resolve), + meta: { + title: '权限管理' + } + }, + { + path: 'draft', + component: resolve => require(['@/views/package/draft/ruleForm'], resolve), + meta: { + }, + hidden: true + } + ] + }, + { + path: '/publish', + name: '发布内容管理', + component: Layout, + meta: { + title: '发布内容管理', + roles: [admin] + }, + children: [ + { + path: 'map', + name: '发布地图管理', + component: resolve => require(['@/views/publish/publishMap/index'], resolve), + meta: { + title: '发布地图管理' + } + }, + { + path: 'product', + name: '产品状态管理', + component: resolve => require(['@/views/publish/productStatus/index'], resolve), + meta: { + title: '产品状态管理' + } + }, + { + path: 'lesson', + name: '发布课程管理', + component: resolve => require(['@/views/publish/publishLesson/index'], resolve), + meta: { + title: '发布课程管理' + } + }, + // { + // path: 'script', + // name: '仿真脚本管理', + // component: resolve => require(['@/views/publish/simulationScript/index'], resolve), + // meta: { + // title: '仿真脚本管理', + // } + // }, + { + path: 'runPlan/template', + component: resolve => require(['@/views/publish/runPlanTemplate/index'], resolve), + meta: { + title: '运行图模板管理' + } + }, + { + path: 'runPlan/common', + component: resolve => require(['@/views/publish/runPlanCommon/index'], resolve), + meta: { + title: '运行图通用管理' + } + }, + { + path: 'runPlan/everyDay', + component: resolve => require(['@/views/publish/runPlanEveryDay/index'], resolve), + meta: { + title: '运行图每日管理' + } + }, + { + path: 'examRule/manage', + name: '试题管理', + component: resolve => require(['@/views/publish/examRule/index'], resolve), + meta: { + title: '试题管理' + } + }, + { + path: 'map/draft/:mode/:mapId', + component: resolve => require(['@/views/publish/publishMap/draft'], resolve), + meta: { + }, + hidden: true + }, + { + path: 'examRule/draft/:mode/:ruleId/:lessonId', + hidden: true, + component: resolve => require(['@/views/publish/examRule/draft/index'], resolve), + meta: { + } + }, + { + path: 'runPlan/common/:mode', + component: resolve => require(['@/views/publish/runPlanCommon/draft'], resolve), + meta: { + }, + hidden: true + }, + { + path: 'runPlan/view/:mode', + component: resolve => require(['@/views/map/runplan/chart'], resolve), + meta: { + }, + hidden: true + } + ] + }, + { + path: '/orderauthor', + component: Layout, + name: '订单权限管理', + meta: { + title: '订单权限管理', + roles: [admin] + }, + children: [ + { + path: 'commodity/manage', + name: '商品管理', + component: resolve => require(['@/views/orderauthor/commodity/index'], resolve), + meta: { + title: '商品管理' + } + }, + { + path: 'order/manage', + name: '订单管理', + component: resolve => require(['@/views/orderauthor/order/list'], resolve), + meta: { + title: '订单管理' + } + }, + { + path: 'author/manage', + name: '权限管理', + component: resolve => require(['@/views/orderauthor/author/index'], resolve), + meta: { + title: '权限管理' + } + }, + { + path: 'transfer/manage', + name: '权限分发管理', + component: resolve => require(['@/views/orderauthor/permission/index'], resolve), + meta: { + title: '权限分发管理' + } + }, + { + path: 'commodity/draft/:mode/:commodityId', + name: '添加商品', + hidden: true, + component: resolve => require(['@/views/orderauthor/commodity/draft'], resolve), + meta: { + title: '添加商品' + } + }, + { + path: 'order/draft/:mode/:orderId', + name: '添加订单', + hidden: true, + component: resolve => require(['@/views/orderauthor/order/draft'], resolve), + meta: { + title: '添加订单' + } + }, + { + path: 'author/draft/:mode/:userId', + name: '添加课程权限', + hidden: true, + component: resolve => require(['@/views/orderauthor/author/draft'], resolve), + meta: { + title: '添加课程权限' + } + }, + { + path: 'transfer/detail/:permissionId', + component: resolve => require(['@/views/orderauthor/permission/detail'], resolve), + meta: { + }, + hidden: true + }, + { + path: 'transfer/package', + component: resolve => require(['@/views/orderauthor/permission/draft/ruleForm'], resolve), + meta: { + }, + hidden: true + } + ] + }, + { + path: '/system', + component: Layout, + meta: { + title: '系统管理', + roles: [admin] + }, + children: [ + { + path: 'dictionary', + name: 'Dictionary', + component: resolve => require(['@/views/management/dictionary/index'], resolve), + meta: { + title: '数据字典' + } + }, + { + path: 'dictionary/detail', + name: '数据字典明细', + hidden: true, + component: resolve => require(['@/views/management/dictionaryDetail/index'], resolve), + meta: { + title: '数据字典明细' + } + }, + { + path: 'userManage', + name: '用户管理', + component: resolve => require(['@/views/management/userControl/index'], resolve), + meta: { + title: '用户管理' + } + }, + { + path: 'userTrainingManage', + name: '用户实训管理', + component: resolve => require(['@/views/management/userTraining/index'], resolve), + meta: { + title: '用户实训管理' + } + }, + { + path: 'userExamManage', + name: '用户考试管理', + component: resolve => require(['@/views/management/userExam/index'], resolve), + meta: { + title: '用户考试管理' + } + }, + { + path: 'userSimulationManage', + name: '用户仿真管理', + component: resolve => require(['@/views/management/userSimulation/index'], resolve), + meta: { + title: '用户仿真管理' + } + }, + { + path: 'existingSimulation', + name: '存在仿真管理', + component: resolve => require(['@/views/management/existingSimulation/index'], resolve), + meta: { + title: '存在仿真管理' + } + } + ] + } ]; const createRouter = () => new Router({ diff --git a/src/scripts/ConstConfig.js b/src/scripts/ConstConfig.js index e283692e9..7bc549125 100644 --- a/src/scripts/ConstConfig.js +++ b/src/scripts/ConstConfig.js @@ -8,6 +8,95 @@ export default { Whether: [ { label: '否', value: false }, { label: '是', value: true } + ], + + LinkTypeList: [ + { label: '起始Link', value: '0' }, + { label: '正向Link', value: '1' }, + { label: '侧向Link', value: '2' } + ], + + LinkDriectTypeList: [ + { label: '左侧', value: 'L' }, + { label: '右侧', value: 'R' } + ], + + DirectionCodeList: [ + { value: '2', label: '上行' }, + { value: '1', label: '下行' } + ], + + SignalLeftOrRightList: [ + { label: '左侧', value: '0' }, + { label: '右侧', value: '1' } + ], + + roleList: [ + { label: '实训用户', value: '01' }, + { label: '地图生产者', value: '02' }, + { label: '课程生成者', value: '03' }, + { label: '系统管理员', value: '04' }, + { label: '超级管理员', value: '05' }, + { label: '销售用户', value: '06' } + ], + + examResultList: [ + { label: '未计算', value: '01' }, + { label: '通过', value: '02' }, + { label: '未通过', value: '03' }, + { label: '已放弃', value: '04' } + ], + + PermissionUseList: [ + { label: '公用', value: true }, + { label: '专用', value: false } + ], + + RegionTypeList: [ + { label: '零点西上行', value: '01' }, + { label: '零点西下行', value: '02' }, + { label: '零西渡线/', value: '03' }, + { label: '零西渡线\\', value: '04' }, + { label: 'PGY一联段线', value: '05' }, + { label: '零点东上行', value: '06' }, + { label: '零点东下行', value: '07' }, + { label: '零东渡线/', value: '08' }, + { label: '零东渡线\\', value: '09' }, + { label: '库线1', value: '10' }, + { label: '库线2', value: '11' }, + { label: '库线渡线/', value: '12' }, + { label: '库线渡线\\', value: '13' }, + { label: '联络线1', value: '14' }, + { label: '联络线2', value: '15' }, + { label: 'SHZ一联段线', value: '16' }, + { label: 'SHZ二联段线', value: '17' }, + { label: 'SHD三联段线', value: '18' }, + { label: 'SHD四联段线', value: '19' } + ], + roleType:[ + {label:'管理员',value:'Admin'}, + {label:'教员',value:'Instructor'}, + {label:'行调',value:'Dispatcher'}, + {label:'车站',value:'Attendant'}, + {label:'观众',value:'Audience'}, + {label:'司机',value:'Driver'}, + ], + + SimulationType: [ + { label: '实训', value: 'Training'}, + { label: '仿真(行调/现地/司机)', value: 'Simulation'}, + { label: '综合演练', value: 'Joint'}, + { label: '大屏', value: 'BigScreen'}, + { label: '琏计划', value: 'RunPlan'}, + { label: '任务录制', value: 'ScriptRecord'}, + ], + + skinCode: [ + { label: '福州一号线', value: '02'}, + { label: '北京一号线', value: '03'}, + { label: '成都三号线', value: '04'}, + { label: '北京八通线', value: '05'}, ] + } -} +}; diff --git a/src/scripts/DeviceTypeDic.js b/src/scripts/DeviceTypeDic.js new file mode 100644 index 000000000..ce014bfd8 --- /dev/null +++ b/src/scripts/DeviceTypeDic.js @@ -0,0 +1,15 @@ +export default { + ConstSelect: { + deviceType: [ + {label: 'Link', value: 'Link'}, + {label: '进路', value: 'Route'}, + {label: '区段', value: 'Section'}, + {label: '信号机', value: 'Signal'}, + {label: '站台', value: 'StationStand'}, + {label: '集中站', value: 'Station'}, + {label: '控制模式', value: 'StationControl'}, + {label: '道岔', value: 'Switch'}, + {label: '列车', value: 'Train'}, + ] + } +}; \ No newline at end of file diff --git a/src/scripts/DictionaryData.js b/src/scripts/DictionaryData.js index 93ed03e50..d44de2c7c 100644 --- a/src/scripts/DictionaryData.js +++ b/src/scripts/DictionaryData.js @@ -222,7 +222,7 @@ function loadDictionary() { return queryData('train_position_type', this, '_TrainPositionType'); }, - lityType: function () { + cityType: function () { return queryData('city_type', this, '_CityType'); }, diff --git a/src/scripts/FaultDic.js b/src/scripts/FaultDic.js new file mode 100644 index 000000000..e72fa534a --- /dev/null +++ b/src/scripts/FaultDic.js @@ -0,0 +1,28 @@ +export const FaultStatusEnum = { + Section: { + '01': '占用', + }, + Switch: { + + }, + Signal: { + + }, + Station: { + + }, + StationControl: { + + }, + StationDelayUnlock: { + + }, + StationStand: { + + }, + Train: { + + }, + TrainWindow: { + } +}; \ No newline at end of file diff --git a/src/scripts/OperationConfig.js b/src/scripts/OperationConfig.js new file mode 100644 index 000000000..7376a3ec4 --- /dev/null +++ b/src/scripts/OperationConfig.js @@ -0,0 +1,1281 @@ +// 操作规则定义 +// {id: "1", trainingType: "01", name: "车站名称"} +// {id: "2", trainingType: "01", name: "车站控制模式编号"} +// {id: "3", trainingType: "02", name: "进路名称"} +// {id: "4", trainingType: "02", name: "进路编号"} +// {id: "5", trainingType: "02", name: "信号机名称"} +// {id: "6", trainingType: "02", name: "信号机编号"} +// {id: "7", trainingType: "03", name: "道岔名称"} +// {id: "8", trainingType: "04", name: "物理区段名称"} +// {id: "9", trainingType: "04", name: "逻辑区段名称"} +// {id: "10", trainingType: "05", name: "车站名称"} +// {id: "11", trainingType: "05", name: "站台行驶方向编号"} +// {id: "12", trainingType: "05", name: "站台行驶方向"} +// {id: "13", trainingType: "05", name: "站台行驶方向编号(反)"} +// {id: "14", trainingType: "05", name: "站台行驶方向(反)"} +// {id: "15", trainingType: "03", name: "道岔位置"} +// {id: "16", trainingType: "03", name: "道岔位置(反)"} +export const OperationList = { + '01': { + list: [], + }, + '02': { + list: [ + { + maxDuration: 20, + minDuration: 15, + operateType: '0103', + skinStyle: '02', + trainingName: '转为中控({1})', + trainingRemark: '控制权限转换,站控转中控', + trainingType: '01', + productTypes: ['02'], + stepVOList: [ + { deviceType: 'bar', orderNum: 1, operateCode: '0024', tip: '鼠标左键点击顶部菜单栏【控制模式转换】' }, + { deviceType: 'bar', orderNum: 2, operateCode: '2041', tip: '鼠标左键点击【转为中控】' }, + { deviceType: '05', orderNum: 3, operateCode: '2042', tip: '鼠标左键选择所需要转换的控制区域【{1}】', val: '{2}' }, + { deviceType: '05', orderNum: 4, operateCode: '204', tip: '鼠标左键点击【请求中控】', val: '{2}' }, + { deviceType: '05', orderNum: 5, operateCode: '2043', tip: '鼠标左键点击【确认】按钮', val: '{2}' }, + { deviceType: '05', orderNum: 6, operateCode: '0013', tip: '鼠标左键点击【确定】按钮' }, + { deviceType: 'bar', orderNum: 7, operateCode: '000', tip: '鼠标左键点击【关闭】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0101', + skinStyle: '02', + trainingName: '转为站控({1})', + trainingRemark: '控制权限转换,中控转站控', + trainingType: '01', + productTypes: ['01'], + stepVOList: [ + { deviceType: 'bar', orderNum: 1, operateCode: '0024', tip: '鼠标左键点击顶部菜单栏【控制模式转换】' }, + { deviceType: 'bar', orderNum: 2, operateCode: '2021', tip: '鼠标左键点击【转为站控】' }, + { deviceType: '05', orderNum: 3, operateCode: '2022', tip: '鼠标左键选择所需要转换的控制区域【{1}】', val: '{2}' }, + { deviceType: '05', orderNum: 4, operateCode: '202', tip: '鼠标左键点击【请求站控】', val: '{2}' }, + { deviceType: '05', orderNum: 5, operateCode: '2023', tip: '鼠标左键点击【确认】按钮', val: '{2}' }, + { deviceType: '05', orderNum: 6, operateCode: '0013', tip: '鼠标左键点击【确定】按钮' }, + { deviceType: 'bar', orderNum: 7, operateCode: '000', tip: '鼠标左键点击【关闭】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0102', + skinStyle: '02', + trainingName: '强制站控({1})', + trainingRemark: '控制权限转换,强制站控', + trainingType: '01', + productTypes: ['01'], + stepVOList: [ + { deviceType: 'bar', orderNum: 1, operateCode: '0024', tip: '鼠标左键点击顶部菜单栏【控制模式转换】' }, + { deviceType: 'bar', orderNum: 2, operateCode: '2034', tip: '鼠标左键点击【强制站控】' }, + { deviceType: 'bar', orderNum: 3, operateCode: '2035', tip: '鼠标左键输入默认密码【123456】后,点击【确定】按钮' }, + { deviceType: '05', orderNum: 4, operateCode: '2032', tip: '鼠标左键选择所需要转换的控制区域【{1}】', val: '{2}' }, + { deviceType: '05', orderNum: 5, operateCode: '203', tip: '鼠标左键点击【强制站控】', val: '{2}' }, + { deviceType: '05', orderNum: 6, operateCode: '2033', tip: '鼠标左键点击【确认】按钮', val: '{2}' }, + { deviceType: '05', orderNum: 7, operateCode: '0013', tip: '鼠标左键点击【确定】按钮' }, + { deviceType: 'bar', orderNum: 8, operateCode: '000', tip: '鼠标左键点击【关闭】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0501', + skinStyle: '02', + trainingName: '站台扣车({10}-{12}站台)', + trainingRemark: '设置扣车功能', + trainingType: '05', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '504', tip: '鼠标右键菜单选择【扣车】' }, + { deviceType: '06', orderNum: 2, operateCode: '504', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0502', + skinStyle: '02', + trainingName: '站台取消扣车({10}-{12}站台)', + trainingRemark: '设置取消扣车功能', + trainingType: '05', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '505', tip: '鼠标右键菜单选择【取消扣车】' }, + { deviceType: '06', orderNum: 2, operateCode: '505', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0503', + skinStyle: '02', + trainingName: '强制取消扣车({10}-{12}站台)', + trainingRemark: '强制取消扣车功能', + trainingType: '05', + productTypes: ['01'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '506', tip: '鼠标右键菜单选择【强制取消扣车】' }, + { deviceType: '06', orderNum: 2, operateCode: '506', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0504', + skinStyle: '02', + trainingName: '全线取消扣车({10}-{12}站台)', + trainingRemark: '全线取消扣车功能(默认上行全线/下行全线,不做选择)', + trainingType: '05', + productTypes: ['02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '508', tip: '鼠标右键菜单选择【全线取消扣车】' }, + { deviceType: '06', orderNum: 2, operateCode: '508', tip: '鼠标左键点击【确定】按钮', val: '{11}' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0504', + skinStyle: '02', + trainingName: '全线取消扣车({10}-{12}站台)', + trainingRemark: '全线取消扣车功能(选择上/下行全线)', + trainingType: '05', + productTypes: ['02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '508', tip: '鼠标右键菜单选择【全线取消扣车】' }, + { deviceType: '06', orderNum: 2, operateCode: '5081', tip: '鼠标右键菜单选择【{14}全线】', val: '{13}' }, + { deviceType: '06', orderNum: 3, operateCode: '508', tip: '鼠标左键点击【确定】按钮', val: '{13}' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0505', + skinStyle: '02', + trainingName: '站台跳停({10}-{12}站台)', + trainingRemark: '设置跳停功能', + trainingType: '05', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '502', tip: '鼠标右键菜单选择【跳停】' }, + { deviceType: '06', orderNum: 2, operateCode: '502', tip: '鼠标左键点击【确定】按钮', val: '{11}' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0506', + skinStyle: '02', + trainingName: '取消跳停({10}-{12}站台)', + trainingRemark: '设置取消跳停功能', + trainingType: '05', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '503', tip: '鼠标右键菜单选择【取消跳停】' }, + { deviceType: '06', orderNum: 2, operateCode: '503', tip: '鼠标左键点击【确定】按钮', val: '{11}' }, + ] + }, + { + maxDuration: 8, + minDuration: 5, + operateType: '0507', + skinStyle: '02', + trainingName: '查询站台状态({10}-{12}站台)', + trainingRemark: '查询站台状态功能', + trainingType: '05', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '507', tip: '鼠标右键菜单选择【查询站台状态】' }, + { deviceType: '06', orderNum: 2, operateCode: '0012', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0508', + skinStyle: '02', + trainingName: '设置停站时间({10}-{12}站台)', + trainingRemark: '设置停站时间(自动, 一直有效)', + trainingType: '05', + productTypes: ['02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '509', tip: '鼠标右键菜单选择【设置停站时间】' }, + { deviceType: '06', orderNum: 2, operateCode: '5092', tip: '鼠标左键点击,选择【自动】', val: '01' }, + { deviceType: '06', orderNum: 3, operateCode: '509', tip: '鼠标左键点击【确认】按钮', val: '01::20::true' }, + { deviceType: '06', orderNum: 4, operateCode: '5091', tip: '鼠标左键点击【确认】按钮', val: '01::20::true' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0508', + skinStyle: '02', + trainingName: '设置停站时间({10}-{12}站台)', + trainingRemark: '设置停站时间(人工, 20秒, 一直有效)', + trainingType: '05', + productTypes: ['02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '509', tip: '鼠标右键菜单选择【设置停站时间】' }, + { deviceType: '06', orderNum: 2, operateCode: '5092', tip: '鼠标左键点击,选择【人工】', val: '02' }, + { deviceType: '06', orderNum: 3, operateCode: '5094', tip: '输入或鼠标点击,调整为【20】', val: '20' }, + { deviceType: '06', orderNum: 4, operateCode: '509', tip: '鼠标左键点击【确认】按钮', val: '02::20::true' }, + { deviceType: '06', orderNum: 5, operateCode: '5091', tip: '鼠标左键点击【确认】按钮', val: '02::20::true' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0508', + skinStyle: '02', + trainingName: '设置停站时间({10}-{12}站台)', + trainingRemark: '设置停站时间(人工, 20秒, 一次有效)', + trainingType: '05', + productTypes: ['02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '509', tip: '鼠标右键菜单选择【设置停站时间】' }, + { deviceType: '06', orderNum: 2, operateCode: '5092', tip: '鼠标左键点击,选择【人工】', val: '02' }, + { deviceType: '06', orderNum: 3, operateCode: '5094', tip: '输入或鼠标点击,调整为【20】', val: '20' }, + { deviceType: '06', orderNum: 4, operateCode: '5093', tip: '鼠标左键点击,选择【一次有效】', val: 'false' }, + { deviceType: '06', orderNum: 5, operateCode: '509', tip: '鼠标左键点击【确认】按钮', val: '02::20::false' }, + { deviceType: '06', orderNum: 6, operateCode: '5091', tip: '鼠标左键点击【确认】按钮', val: '02::20::false' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0509', + skinStyle: '02', + trainingName: '设置运行等级({10}-{12}站台)', + trainingRemark: '设置运行等级(设置区间运行时间为60,一直有效)', + trainingType: '05', + productTypes: ['02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '510', tip: '鼠标右键菜单选择【设置运行等级】' }, + { deviceType: '06', orderNum: 2, operateCode: '5101', tip: '鼠标左键点击,选择【60】', val: '60' }, + { deviceType: '06', orderNum: 3, operateCode: '510', tip: '鼠标左键点击【确认】按钮', val: '60::true' }, + { deviceType: '06', orderNum: 4, operateCode: '5102', tip: '鼠标左键点击【确认】按钮', val: '60::true' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0509', + skinStyle: '02', + trainingName: '设置运行等级({10}-{12}站台)', + trainingRemark: '设置运行等级(设置区间运行时间为60,一次有效)', + trainingType: '05', + productTypes: ['02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '510', tip: '鼠标右键菜单选择【设置运行等级】' }, + { deviceType: '06', orderNum: 2, operateCode: '5101', tip: '鼠标左键点击,选择【60】', val: '60' }, + { deviceType: '06', orderNum: 3, operateCode: '5103', tip: '鼠标左键点击,取消选择【一直有效】', val: 'false' }, + { deviceType: '06', orderNum: 4, operateCode: '510', tip: '鼠标左键点击【确认】按钮', val: '60::false' }, + { deviceType: '06', orderNum: 5, operateCode: '5102', tip: '鼠标左键点击【确认】按钮', val: '60::false' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0510', + skinStyle: '02', + trainingName: '设置提前发车({10}-{12}站台)', + trainingRemark: '设置提前发车功能', + trainingType: '05', + productTypes: ['02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '501', tip: '鼠标右键菜单选择【设置提前发车】' }, + { deviceType: '06', orderNum: 2, operateCode: '501', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0511', + skinStyle: '02', + trainingName: '人工折返策略设置({10}-{12}站台)', + trainingRemark: '人工折返策略设置功能', + trainingType: '05', + productTypes: ['02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '511', tip: '鼠标右键菜单选择【人工折返策略设置】' }, + { deviceType: '06', orderNum: 2, operateCode: '5111', tip: '鼠标左键点击,选择【无折返】', val: '01' }, + { deviceType: '06', orderNum: 3, operateCode: '511', tip: '鼠标左键点击【确定】按钮', val: '01' }, + ] + }, + + { + maxDuration: 15, + minDuration: 8, + operateType: '0401', + skinStyle: '02', + trainingName: '区段故障解锁({8}{9})', + trainingRemark: '故障解锁功能', + trainingType: '04', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '03', orderNum: 1, operateCode: '402', tip: '鼠标右键菜单选择【区段故障解锁】' }, + { deviceType: '03', orderNum: 2, operateCode: '4026', tip: '鼠标左键点击【下达】按钮' }, + { deviceType: '03', orderNum: 3, operateCode: '4024', tip: '鼠标左键点击【确认1】按钮' }, + { deviceType: '03', orderNum: 4, operateCode: '4025', tip: '鼠标左键点击【确认2】按钮' }, + { deviceType: '03', orderNum: 5, operateCode: '001', tip: '鼠标左键点击【关闭】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0402', + skinStyle: '02', + trainingName: '区段切除({8}{9})', + trainingRemark: '区段切除', + trainingType: '04', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '03', orderNum: 1, operateCode: '405', tip: '鼠标右键菜单选择【区段切除】' }, + { deviceType: '03', orderNum: 2, operateCode: '405', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0403', + skinStyle: '02', + trainingName: '区段激活({8}{9})', + trainingRemark: '区段激活功能', + trainingType: '04', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '03', orderNum: 1, operateCode: '406', tip: '鼠标右键菜单选择【区段激活】' }, + { deviceType: '03', orderNum: 2, operateCode: '406', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0404', + skinStyle: '02', + trainingName: '计轴预复位({8}{9})', + trainingRemark: '计轴预复位功能', + trainingType: '04', + productTypes: ['01'], + stepVOList: [ + { deviceType: '03', orderNum: 1, operateCode: '409', tip: '鼠标右键菜单选择【区段计轴预复位】' }, + { deviceType: '03', orderNum: 2, operateCode: '4091', tip: '鼠标左键点击【下达】按钮' }, + { deviceType: '03', orderNum: 3, operateCode: '4093', tip: '鼠标左键点击【确认1】按钮' }, + { deviceType: '03', orderNum: 4, operateCode: '4094', tip: '鼠标左键点击【确认2】按钮' }, + { deviceType: '03', orderNum: 5, operateCode: '001', tip: '鼠标左键点击【关闭】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0405', + skinStyle: '02', + trainingName: '区段封锁({8}{9})', + trainingRemark: '区段封锁功能', + trainingType: '04', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '03', orderNum: 1, operateCode: '403', tip: '鼠标右键菜单选择【区段封锁】' }, + { deviceType: '03', orderNum: 2, operateCode: '403', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0406', + skinStyle: '02', + trainingName: '区段解封({8}{9})', + trainingRemark: '区段解封功能', + trainingType: '04', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '03', orderNum: 1, operateCode: '404', tip: '鼠标右键菜单选择【区段解封】' }, + { deviceType: '03', orderNum: 2, operateCode: '4041', tip: '鼠标左键点击【下达】按钮' }, + { deviceType: '03', orderNum: 3, operateCode: '4043', tip: '鼠标左键点击【确认1】按钮' }, + { deviceType: '03', orderNum: 4, operateCode: '4044', tip: '鼠标左键点击【确认2】按钮' }, + { deviceType: '03', orderNum: 5, operateCode: '001', tip: '鼠标左键点击【关闭】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0407', + skinStyle: '02', + trainingName: '区段设置限速({8}{9})', + trainingRemark: '区段设置限速功能(限速值:5)', + trainingType: '04', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '03', orderNum: 1, operateCode: '407', tip: '鼠标右键菜单选择【区段设置限速】' }, + { deviceType: '03', orderNum: 2, operateCode: '4076', tip: '鼠标左键选择【限速值5】', val: '5' }, + { deviceType: '03', orderNum: 3, operateCode: '4071', tip: '鼠标左键点击【下达】按钮' }, + { deviceType: '03', orderNum: 4, operateCode: '4072', tip: '鼠标左键点击【确认】按钮' }, + { deviceType: '03', orderNum: 5, operateCode: '4073', tip: '鼠标左键点击【确认1】按钮' }, + { deviceType: '03', orderNum: 6, operateCode: '4074', tip: '鼠标左键点击【确认2】按钮', val: '5' }, + { deviceType: '03', orderNum: 7, operateCode: '001', tip: '鼠标左键点击【关闭】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0408', + skinStyle: '02', + trainingName: '区段取消限速({8}{9})', + trainingRemark: '区段取消限速功能', + trainingType: '04', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '03', orderNum: 1, operateCode: '408', tip: '鼠标右键菜单选择【区段取消限速】' }, + { deviceType: '03', orderNum: 2, operateCode: '4081', tip: '鼠标左键点击【下达】按钮' }, + { deviceType: '03', orderNum: 3, operateCode: '4082', tip: '鼠标左键点击【确认】按钮' }, + { deviceType: '03', orderNum: 4, operateCode: '4083', tip: '鼠标左键点击【确认1】按钮' }, + { deviceType: '03', orderNum: 5, operateCode: '4084', tip: '鼠标左键点击【确认2】按钮', val: '5' }, + { deviceType: '03', orderNum: 6, operateCode: '001', tip: '鼠标左键点击【关闭】按钮' }, + ] + }, + + { + maxDuration: 15, + minDuration: 8, + operateType: '0301', + skinStyle: '02', + trainingName: '道岔单锁({7})', + trainingRemark: '道岔单锁功能', + trainingType: '03', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '02', orderNum: 1, operateCode: '103', tip: '鼠标右键菜单选择【道岔单锁】' }, + { deviceType: '02', orderNum: 2, operateCode: '103', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0302', + skinStyle: '02', + trainingName: '道岔单解({7})', + trainingRemark: '道岔单解功能', + trainingType: '03', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '02', orderNum: 1, operateCode: '104', tip: '鼠标右键菜单选择【道岔单解】' }, + { deviceType: '02', orderNum: 2, operateCode: '1041', tip: '鼠标左键点击【下达】按钮' }, + { deviceType: '02', orderNum: 3, operateCode: '1043', tip: '鼠标左键点击【确认1】按钮' }, + { deviceType: '02', orderNum: 4, operateCode: '1044', tip: '鼠标左键点击【确认2】按钮' }, + { deviceType: '02', orderNum: 5, operateCode: '001', tip: '鼠标左键点击【关闭】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0303', + skinStyle: '02', + trainingName: '道岔区段封闭({7})', + trainingRemark: '道岔区段封闭功能', + trainingType: '03', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '02', orderNum: 1, operateCode: '105', tip: '鼠标右键菜单选择【道岔区段封闭】' }, + { deviceType: '02', orderNum: 2, operateCode: '105', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0304', + skinStyle: '02', + trainingName: '道岔区段解封({7})', + trainingRemark: '道岔区段解封功能', + trainingType: '03', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '02', orderNum: 1, operateCode: '106', tip: '鼠标右键菜单选择【道岔区段解封】' }, + { deviceType: '02', orderNum: 2, operateCode: '1061', tip: '鼠标左键点击【下达】按钮' }, + { deviceType: '02', orderNum: 3, operateCode: '1063', tip: '鼠标左键点击【确认1】按钮' }, + { deviceType: '02', orderNum: 4, operateCode: '1064', tip: '鼠标左键点击【确认2】按钮' }, + { deviceType: '02', orderNum: 5, operateCode: '001', tip: '鼠标左键点击【关闭】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0305', + skinStyle: '02', + trainingName: '道岔转动({7})', + trainingRemark: '道岔转动功能({15}转{16})', + trainingType: '03', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '02', orderNum: 1, operateCode: '107', tip: '鼠标右键菜单选择【道岔转动】' }, + { deviceType: '02', orderNum: 2, operateCode: '107', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 20, + minDuration: 10, + operateType: '0306', + skinStyle: '02', + trainingName: '道岔区段故障解锁({7})', + trainingRemark: '道岔区段故障解锁功能', + trainingType: '03', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '02', orderNum: 1, operateCode: '109', tip: '鼠标右键菜单选择【道岔区段故障解锁】' }, + { deviceType: '02', orderNum: 2, operateCode: '1091', tip: '鼠标左键点击【下达】按钮' }, + { deviceType: '02', orderNum: 3, operateCode: '1093', tip: '鼠标左键点击【确认1】按钮' }, + { deviceType: '02', orderNum: 4, operateCode: '1094', tip: '鼠标左键点击【确认2】按钮' }, + { deviceType: '02', orderNum: 5, operateCode: '001', tip: '鼠标左键点击【关闭】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0307', + skinStyle: '02', + trainingName: '道岔区段计轴预复位({7})', + trainingRemark: '道岔区段计轴预复位功能', + trainingType: '03', + productTypes: ['01'], + stepVOList: [ + { deviceType: '02', orderNum: 1, operateCode: '110', tip: '鼠标右键菜单选择【道岔区段计轴预复位】' }, + { deviceType: '02', orderNum: 2, operateCode: '1101', tip: '鼠标左键点击【下达】按钮' }, + { deviceType: '02', orderNum: 3, operateCode: '1103', tip: '鼠标左键点击【确认1】按钮' }, + { deviceType: '02', orderNum: 4, operateCode: '1104', tip: '鼠标左键点击【确认2】按钮' }, + { deviceType: '02', orderNum: 5, operateCode: '001', tip: '鼠标左键点击【关闭】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0308', + skinStyle: '02', + trainingName: '区段切除({7})', + trainingRemark: '区段切除', + trainingType: '03', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '02', orderNum: 1, operateCode: '111', tip: '鼠标右键菜单选择【区段切除】' }, + { deviceType: '02', orderNum: 2, operateCode: '111', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0309', + skinStyle: '02', + trainingName: '区段激活({7})', + trainingRemark: '区段激活功能', + trainingType: '03', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '02', orderNum: 1, operateCode: '112', tip: '鼠标右键菜单选择【区段激活】' }, + { deviceType: '02', orderNum: 2, operateCode: '112', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0310', + skinStyle: '02', + trainingName: '道岔区段设置限速({7})', + trainingRemark: '道岔区段设置限速功能(限速值:5)', + trainingType: '03', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '02', orderNum: 1, operateCode: '113', tip: '鼠标右键菜单选择【道岔区段设置限速】' }, + { deviceType: '02', orderNum: 2, operateCode: '1136', tip: '鼠标左键选择【限速值5】', val: '5' }, + { deviceType: '02', orderNum: 3, operateCode: '1131', tip: '鼠标左键点击【下达】按钮' }, + { deviceType: '02', orderNum: 4, operateCode: '1132', tip: '鼠标左键点击【确认】按钮' }, + { deviceType: '02', orderNum: 5, operateCode: '1133', tip: '鼠标左键点击【确认1】按钮' }, + { deviceType: '02', orderNum: 6, operateCode: '1134', tip: '鼠标左键点击【确认2】按钮', val: '5' }, + { deviceType: '02', orderNum: 7, operateCode: '001', tip: '鼠标左键点击【关闭】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0311', + skinStyle: '02', + trainingName: '道岔区段取消限速({7})', + trainingRemark: '道岔区段取消限速功能', + trainingType: '03', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '02', orderNum: 1, operateCode: '114', tip: '鼠标右键菜单选择【道岔区段取消限速】' }, + { deviceType: '02', orderNum: 2, operateCode: '1141', tip: '鼠标左键点击【下达】按钮' }, + { deviceType: '02', orderNum: 3, operateCode: '1142', tip: '鼠标左键点击【确认】按钮' }, + { deviceType: '02', orderNum: 4, operateCode: '1143', tip: '鼠标左键点击【确认1】按钮' }, + { deviceType: '02', orderNum: 5, operateCode: '1144', tip: '鼠标左键点击【确认2】按钮', val: '5' }, + { deviceType: '02', orderNum: 6, operateCode: '001', tip: '鼠标左键点击【关闭】按钮' }, + ] + }, + + { + maxDuration: 15, + minDuration: 8, + operateType: '0201', + skinStyle: '02', + trainingName: '进路选排({3})', + trainingRemark: '选择排列进路', + trainingType: '02', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '301', tip: '鼠标右键菜单选择【进路选排】' }, + { deviceType: '04', orderNum: 2, operateCode: '3011', tip: '鼠标左键选择进路名称【{3}】', val: '{4}' }, + { deviceType: '04', orderNum: 3, operateCode: '301', tip: '鼠标左键点击【确定】按钮' }, + { deviceType: '04', orderNum: 4, operateCode: '3012', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0202', + skinStyle: '02', + trainingName: '进路取消({3})', + trainingRemark: '进路取消', + trainingType: '02', + productTypes: ['02'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '303', tip: '鼠标右键菜单选择【进路取消】' }, + { deviceType: '04', orderNum: 2, operateCode: '303', tip: '鼠标左键点击【确定】按钮' }, + { deviceType: '04', orderNum: 3, operateCode: '3031', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0202', + skinStyle: '02', + trainingName: '进路取消({3})', + trainingRemark: '进路取消', + trainingType: '02', + productTypes: ['01'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '303', tip: '鼠标右键菜单选择【进路取消】' }, + { deviceType: '04', orderNum: 2, operateCode: '303', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0203', + skinStyle: '02', + trainingName: '信号封闭({5})', + trainingRemark: '信号封闭', + trainingType: '02', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '306', tip: '鼠标右键菜单选择【信号封闭】' }, + { deviceType: '04', orderNum: 2, operateCode: '306', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0204', + skinStyle: '02', + trainingName: '信号解封({5})', + trainingRemark: '信号解封', + trainingType: '02', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '307', tip: '鼠标右键菜单选择【信号解封】' }, + { deviceType: '04', orderNum: 2, operateCode: '3071', tip: '鼠标左键点击【下达】按钮' }, + { deviceType: '04', orderNum: 3, operateCode: '3072', tip: '鼠标左键点击【确认】按钮' }, + { deviceType: '04', orderNum: 4, operateCode: '3073', tip: '鼠标左键点击【确认1】按钮' }, + { deviceType: '04', orderNum: 5, operateCode: '3074', tip: '鼠标左键点击【确认2】按钮' }, + { deviceType: '04', orderNum: 6, operateCode: '001', tip: '鼠标左键点击【关闭】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0205', + skinStyle: '02', + trainingName: '信号关灯({3})', + trainingRemark: '信号关灯', + trainingType: '02', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '313', tip: '鼠标右键菜单选择【信号关灯】' }, + { deviceType: '04', orderNum: 2, operateCode: '313', tip: '鼠标左键点击【确定】按钮' }, + { deviceType: '04', orderNum: 3, operateCode: '3131', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0206', + skinStyle: '02', + trainingName: '信号重开({3})', + trainingRemark: '信号重开', + trainingType: '02', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '304', tip: '鼠标右键菜单选择【信号重开】' }, + { deviceType: '04', orderNum: 2, operateCode: '304', tip: '鼠标左键点击【确定】按钮' }, + { deviceType: '04', orderNum: 3, operateCode: '3041', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0207', + skinStyle: '02', + trainingName: '引导进路办理({3})', + trainingRemark: '进路办理信号引导', + trainingType: '02', + productTypes: ['01'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '308', tip: '鼠标右键菜单选择【引导进路办理】' }, + { deviceType: '04', orderNum: 2, operateCode: '3081', tip: '鼠标左键点击【下达】按钮' }, + { deviceType: '04', orderNum: 3, operateCode: '3083', tip: '鼠标左键点击【确认1】按钮' }, + { deviceType: '04', orderNum: 4, operateCode: '3084', tip: '鼠标左键点击【确认2】按钮' }, + { deviceType: '04', orderNum: 5, operateCode: '001', tip: '鼠标左键点击【关闭】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0208', + skinStyle: '02', + trainingName: '自排开({5})', + trainingRemark: '自排开', + trainingType: '02', + productTypes: ['02'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '315', tip: '鼠标右键菜单选择【自排开】' }, + { deviceType: '04', orderNum: 2, operateCode: '3151', tip: '鼠标左键选择控制状态为"人工"的进路', val: '{6}' }, + { deviceType: '04', orderNum: 3, operateCode: '315', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0209', + skinStyle: '02', + trainingName: '自排关({5})', + trainingRemark: '自排关', + trainingType: '02', + productTypes: ['02'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '314', tip: '鼠标右键菜单选择【自排关】' }, + { deviceType: '04', orderNum: 2, operateCode: '3141', tip: '鼠标左键选择第一条进路', val: '{6}' }, + { deviceType: '04', orderNum: 3, operateCode: '314', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0210', + skinStyle: '02', + trainingName: '查询进路控制状态({5})', + trainingRemark: '查询进路控制状态', + trainingType: '02', + productTypes: ['02'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '316', tip: '鼠标右键菜单选择【查询进路控制模式】' }, + { deviceType: '04', orderNum: 2, operateCode: '316', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0211', + skinStyle: '02', + trainingName: '设置联锁自动进路({5})', + trainingRemark: '设置联锁自动进路', + trainingType: '02', + productTypes: ['01'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '309', tip: '鼠标右键菜单选择【设置联锁自动进路】' }, + { deviceType: '04', orderNum: 2, operateCode: '309', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0212', + skinStyle: '02', + trainingName: '取消联锁自动进路({5})', + trainingRemark: '取消联锁自动进路', + trainingType: '02', + productTypes: ['01'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '310', tip: '鼠标右键菜单选择【取消联锁自动进路】' }, + { deviceType: '04', orderNum: 2, operateCode: '310', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0213', + skinStyle: '02', + trainingName: '设置联锁自动触发({5})', + trainingRemark: '设置联锁自动触发', + trainingType: '02', + productTypes: ['01'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '311', tip: '鼠标右键菜单选择【设置联锁自动触发】' }, + { deviceType: '04', orderNum: 2, operateCode: '311', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0214', + skinStyle: '02', + trainingName: '取消联锁自动触发({5})', + trainingRemark: '取消联锁自动触发', + trainingType: '02', + productTypes: ['01'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '312', tip: '鼠标右键菜单选择【取消联锁自动触发】' }, + { deviceType: '04', orderNum: 2, operateCode: '312', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + ] + }, + '03': { + list: [ + { + maxDuration: 8, + minDuration: 5, + operateType: '0501', + skinStyle: '03', + trainingName: '设置扣车({10}-{12} 站台)', + trainingRemark: '设置扣车功能', + trainingType: '05', + productTypes: ['01,02'], // 产品类型 01 现地 02 行调 + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '504', tip: '鼠标右键菜单选择【设置扣车】' }, + { deviceType: '06', orderNum: 2, operateCode: '504', tip: '鼠标左键点击【确定】按钮', }, + ] + }, + { + maxDuration: 8, + minDuration: 5, + operateType: '0502', + skinStyle: '03', + trainingName: '取消扣车({10}-{12} 站台)', + trainingRemark: '取消扣车功能', + trainingType: '05', + productTypes: ['01,02'], // 产品类型 01 现地 02 行调 + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '505', tip: '鼠标右键菜单选择【取消扣车】' }, + { deviceType: '06', orderNum: 2, operateCode: '505', tip: '鼠标左键点击【确定】按钮', }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0505', + skinStyle: '03', + trainingName: '设置跳停({10}-{12} 站台)', + trainingRemark: '设置跳停功能', + trainingType: '05', + productTypes: ['01,02'], // 产品类型 01 现地 02 行调 + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '502', tip: '鼠标右键菜单选择【设置跳停】' }, + { deviceType: '06', orderNum: 2, operateCode: '502', tip: '鼠标左键点击【确定】按钮', val: '{11}' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0505', + skinStyle: '03', + trainingName: '设置跳停({10}-{12} 站台)', + trainingRemark: '设置指定001号列车跳停功能', + trainingType: '05', + productTypes: ['01,02'], // 产品类型 01 现地 02 行调 + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '502', tip: '鼠标右键菜单选择【设置跳停】' }, + { deviceType: '06', orderNum: 2, operateCode: '5024', tip: '鼠标左键点击' }, + { deviceType: '06', orderNum: 2, operateCode: '5022', tip: '请选择001号车', val: '001' }, + { deviceType: '06', orderNum: 2, operateCode: '502', tip: '鼠标左键点击【确定】按钮', val: '{11}::001' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0506', + skinStyle: '03', + trainingName: '取消跳停({10}-{12} 站台)', + trainingRemark: '取消跳停功能', + trainingType: '05', + productTypes: ['01,02'], // 产品类型 01 现地 02 行调 + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '503', tip: '鼠标右键菜单选择【取消跳停】' }, + { deviceType: '06', orderNum: 2, operateCode: '503', tip: '鼠标左键点击【确定】按钮', val: '{11}' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0506', + skinStyle: '03', + trainingName: '取消跳停({10}-{12} 站台)', + trainingRemark: '取消指定001号列车跳停功能', + trainingType: '05', + productTypes: ['01,02'], // 产品类型 01 现地 02 行调 + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '503', tip: '鼠标右键菜单选择【取消跳停】' }, + { deviceType: '06', orderNum: 2, operateCode: '5034', tip: '鼠标左键点击' }, + { deviceType: '06', orderNum: 2, operateCode: '5032', tip: '请选择001号车', val: '001' }, + { deviceType: '06', orderNum: 2, operateCode: '503', tip: '鼠标左键点击【确定】按钮', val: '{11}::001' }, + ] + }, + { + maxDuration: 8, + minDuration: 5, + operateType: '0507', + skinStyle: '03', + trainingName: '查询站台状态({10}-{12} 站台)', + trainingRemark: '查询站台状态功能', + trainingType: '05', + productTypes: ['01,02'], // 产品类型 01 现地 02 行调 + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '507', tip: '鼠标右键菜单选择【站台信息】' }, + { deviceType: '06', orderNum: 2, operateCode: '0012', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0508', + skinStyle: '03', + trainingName: '停站时间控制({10}-{12} 站台)', + trainingRemark: '停站时间控制(自动, 一直有效)', + trainingType: '05', + productTypes: ['02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '509', tip: '鼠标右键菜单选择【停站时间控制】' }, + { deviceType: '06', orderNum: 2, operateCode: '5092', tip: '鼠标左键点击,选择【自动】', val: '01' }, + { deviceType: '06', orderNum: 3, operateCode: '509', tip: '鼠标左键点击【确认】按钮', val: '01::20::true' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0508', + skinStyle: '03', + trainingName: '停站时间控制({10}-{12} 站台)', + trainingRemark: '停站时间控制(人工, 20秒, 一直有效)', + trainingType: '05', + productTypes: ['02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '509', tip: '鼠标右键菜单选择【停站时间控制】' }, + { deviceType: '06', orderNum: 2, operateCode: '5092', tip: '鼠标左键点击,选择【人工】', val: '02' }, + { deviceType: '06', orderNum: 3, operateCode: '5094', tip: '输入或鼠标点击,调整为【20】', val: '20' }, + { deviceType: '06', orderNum: 4, operateCode: '509', tip: '鼠标左键点击【确认】按钮', val: '02::20::true' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0508', + skinStyle: '03', + trainingName: '停站时间控制({10}-{12} 站台)', + trainingRemark: '停站时间控制(人工, 20秒, 一次有效)', + trainingType: '05', + productTypes: ['02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '509', tip: '鼠标右键菜单选择【停站时间控制】' }, + { deviceType: '06', orderNum: 2, operateCode: '5092', tip: '鼠标左键点击,选择【人工】', val: '02' }, + { deviceType: '06', orderNum: 3, operateCode: '5094', tip: '输入或鼠标点击,调整为【20】', val: '20' }, + { deviceType: '06', orderNum: 4, operateCode: '5093', tip: '鼠标左键点击,选择【一次有效】', val: 'false' }, + { deviceType: '06', orderNum: 5, operateCode: '509', tip: '鼠标左键点击【确认】按钮', val: '02::20::false' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0509', + skinStyle: '03', + trainingName: '运行时间控制({10}-{12} 站台)', + trainingRemark: '运行时间控制(设置区间 运行等级1,运行时间为115,一直有效)', + trainingType: '05', + productTypes: ['02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '510', tip: '鼠标右键菜单选择【运行时间控制】' }, + { deviceType: '06', orderNum: 2, operateCode: '5104', tip: '鼠标左键点击,选择【1】', val: '1' }, + { deviceType: '06', orderNum: 3, operateCode: '5101', tip: '鼠标左键点击,选择【115】', val: '115' }, + { deviceType: '06', orderNum: 4, operateCode: '510', tip: '鼠标左键点击【确认】按钮', val: '115::true' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0509', + skinStyle: '03', + trainingName: '运行时间控制({10}-{12} 站台)', + trainingRemark: '运行时间控制(设置区间 运行等级1,运行时间为115,一次有效)', + trainingType: '05', + productTypes: ['02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '510', tip: '鼠标右键菜单选择【运行时间控制】' }, + { deviceType: '06', orderNum: 2, operateCode: '5104', tip: '鼠标左键点击,选择【1】', val: '1' }, + { deviceType: '06', orderNum: 3, operateCode: '5101', tip: '鼠标左键点击,选择【115】', val: '115' }, + { deviceType: '06', orderNum: 4, operateCode: '5103', tip: '鼠标左键点击,取消选择【一直有效】', val: 'false' }, + { deviceType: '06', orderNum: 5, operateCode: '510', tip: '鼠标左键点击【确认】按钮', val: '115::false' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0510', + skinStyle: '03', + trainingName: '提前发车({10}-{12} 站台)', + trainingRemark: '提前发车功能', + trainingType: '05', + productTypes: ['02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '501', tip: '鼠标右键菜单选择【提前发车】' }, + { deviceType: '06', orderNum: 2, operateCode: '501', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0511', + skinStyle: '03', + trainingName: '变通策略管理({10}-{12} 站台)', + trainingRemark: '变通策略管理功能', + trainingType: '05', + productTypes: ['02'], + stepVOList: [ + { deviceType: '06', orderNum: 1, operateCode: '511', tip: '鼠标右键菜单选择【变通策略管理】' }, + { deviceType: '06', orderNum: 2, operateCode: '5111', tip: '鼠标左键点击,选择【无折返】', val: '01' }, + { deviceType: '06', orderNum: 3, operateCode: '511', tip: '鼠标左键点击【确定】按钮', val: '01' }, + ] + }, + + { + maxDuration: 15, + minDuration: 8, + operateType: '0402', + skinStyle: '03', + trainingName: '轨道切除({8}{9} 区段)', + trainingRemark: '轨道切除功能', + trainingType: '04', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '03', orderNum: 1, operateCode: '405', tip: '鼠标右键菜单选择【轨道切除】' }, + { deviceType: '03', orderNum: 2, operateCode: '405', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0403', + skinStyle: '03', + trainingName: '轨道激活({8}{9} 区段)', + trainingRemark: '轨道激活功能', + trainingType: '04', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '03', orderNum: 1, operateCode: '406', tip: '鼠标右键菜单选择【轨道激活】' }, + { deviceType: '03', orderNum: 2, operateCode: '406', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0407', + skinStyle: '03', + trainingName: '设置临时限速({8}{9} 区段)', + trainingRemark: '设置临时限速功能(限速值:15)', + trainingType: '04', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '03', orderNum: 1, operateCode: '407', tip: '鼠标右键菜单选择【设置临时限速】' }, + { deviceType: '03', orderNum: 2, operateCode: '4076', tip: '鼠标左键选择【限速值15】', val: '15' }, + { deviceType: '03', orderNum: 3, operateCode: '4071', tip: '鼠标左键点击【下达】按钮' }, + { deviceType: '03', orderNum: 4, operateCode: '4072', tip: '鼠标左键点击【确认】按钮' }, + { deviceType: '03', orderNum: 5, operateCode: '4073', tip: '鼠标左键点击【确认1】按钮' }, + { deviceType: '03', orderNum: 6, operateCode: '4074', tip: '鼠标左键点击【确认2】按钮', val: '15' }, + { deviceType: '03', orderNum: 7, operateCode: '001', tip: '鼠标左键点击【关闭】按钮' }, + ] + }, + + { + maxDuration: 15, + minDuration: 8, + operateType: '0308', + skinStyle: '03', + trainingName: '轨道切除({7} 道岔)', + trainingRemark: '轨道切除功能', + trainingType: '03', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '02', orderNum: 1, operateCode: '111', tip: '鼠标右键菜单选择【轨道切除】' }, + { deviceType: '02', orderNum: 2, operateCode: '111', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0309', + skinStyle: '03', + trainingName: '轨道激活({7} 道岔)', + trainingRemark: '轨道激活功能', + trainingType: '03', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '02', orderNum: 1, operateCode: '112', tip: '鼠标右键菜单选择【轨道激活】' }, + { deviceType: '02', orderNum: 2, operateCode: '112', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0310', + skinStyle: '03', + trainingName: '设置临时限速({7} 道岔)', + trainingRemark: '设置临时限速功能(限速值 35 零点西上行公里标0 - 零点西上行公里标3000)', + trainingType: '03', + productTypes: ['01,02'], + // stepVOList: [ + // { deviceType: '02', orderNum: 1, operateCode: '113', tip: '鼠标右键菜单选择【设置临时限速】' }, + // { deviceType: '02', orderNum: 2, operateCode: '1131', tip: '鼠标左键选择【打开会话】' }, + // { deviceType: '02', orderNum: 3, operateCode: '1132', tip: '选择【35】', val: '35' }, + // { deviceType: '02', orderNum: 4, operateCode: '1134', tip: '选择【零点西上行】', val: '01' }, + // { deviceType: '02', orderNum: 5, operateCode: '1135', tip: '输入0', val: '0' }, + // { deviceType: '02', orderNum: 6, operateCode: '1136', tip: '选择【零点西上行】', val: '01' }, + // { deviceType: '02', orderNum: 7, operateCode: '1137', tip: '输入3000', val: '3000' }, + // { deviceType: '02', orderNum: 8, operateCode: '1138', tip: '鼠标左键点击【确认设备】', val: '35::01::0::01::3000' }, + // { deviceType: '02', orderNum: 9, operateCode: '1139', tip: '鼠标左键点击【首次确认】' }, + // { deviceType: '02', orderNum: 10, operateCode: '11310', tip: '选择【35】', val: '35' }, + // { deviceType: '02', orderNum: 11, operateCode: '11311', tip: '选择【零点西上行】', val: '01' }, + // { deviceType: '02', orderNum: 12, operateCode: '11312', tip: '选择【0】', val: '0' }, + // { deviceType: '02', orderNum: 13, operateCode: '11313', tip: '选择【零点西上行】', val: '01' }, + // { deviceType: '02', orderNum: 14, operateCode: '11314', tip: '选择【3000】', val: '3000' }, + // { deviceType: '02', orderNum: 15, operateCode: '11315', tip: '鼠标左键点击【确认设备】' }, + // { deviceType: '02', orderNum: 16, operateCode: '114', tip: '鼠标左键点击【确认发送】', val: '35::01::0::01::3000' }, + // { deviceType: '02', orderNum: 17, operateCode: '11317', tip: '鼠标左键点击【关闭】' }, + // { deviceType: '02', orderNum: 18, operateCode: '11318', tip: '鼠标左键点击【确认】' }, + // ] + stepVOList: [ + { deviceType: '02', orderNum: 1, operateCode: '113', tip: '鼠标右键菜单选择【设置临时限速】' }, + { deviceType: '02', orderNum: 2, operateCode: '1136', tip: '鼠标左键选择【限速值35】', val: '35' }, + { deviceType: '02', orderNum: 3, operateCode: '1131', tip: '鼠标左键点击【下达】按钮' }, + { deviceType: '02', orderNum: 4, operateCode: '1132', tip: '鼠标左键点击【确认】按钮' }, + { deviceType: '02', orderNum: 5, operateCode: '1133', tip: '鼠标左键点击【确认1】按钮' }, + { deviceType: '02', orderNum: 6, operateCode: '1134', tip: '鼠标左键点击【确认2】按钮', val: '35' }, + { deviceType: '02', orderNum: 7, operateCode: '001', tip: '鼠标左键点击【关闭】按钮' }, + ] + }, + + { + maxDuration: 15, + minDuration: 8, + operateType: '0201', + skinStyle: '03', + trainingName: '排列进路({3} 进路)', + trainingRemark: '排列进路功能', + trainingType: '02', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '301', tip: '鼠标右键菜单选择【排列进路】' }, + { deviceType: '04', orderNum: 2, operateCode: '3011', tip: '鼠标左键选择进路名称【{3}】', val: '{4}' }, + { deviceType: '04', orderNum: 3, operateCode: '301', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 15, + minDuration: 8, + operateType: '0202', + skinStyle: '03', + trainingName: '取消进路({3} 进路)', + trainingRemark: '取消进路功能', + trainingType: '02', + productTypes: ['02'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '303', tip: '鼠标右键菜单选择【取消进路】' }, + { deviceType: '04', orderNum: 2, operateCode: '303', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 8, + minDuration: 5, + operateType: '0210', + skinStyle: '03', + trainingName: '查询进路控制状态({5} 信号机)', + trainingRemark: '查询进路控制状态功能', + trainingType: '02', + productTypes: ['02'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '316', tip: '鼠标右键菜单选择【查询进路控制状态】' }, + { deviceType: '04', orderNum: 2, operateCode: '316', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 8, + minDuration: 5, + operateType: '0208', + skinStyle: '03', + trainingName: '进路交自动控({5} 信号机)', + trainingRemark: '进路交自动控功能', + trainingType: '02', + productTypes: ['02'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '315', tip: '鼠标右键菜单选择【进路交自动控】' }, + { deviceType: '04', orderNum: 2, operateCode: '3151', tip: '鼠标左键选择【{5}】', val: '{6}' }, + { deviceType: '04', orderNum: 3, operateCode: '315', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 8, + minDuration: 5, + operateType: '0209', + skinStyle: '03', + trainingName: '进路交人工控({5} 信号机)', + trainingRemark: '进路交人工控功能', + trainingType: '02', + productTypes: ['02'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '314', tip: '鼠标右键菜单选择【进路交人工控】' }, + { deviceType: '04', orderNum: 2, operateCode: '3141', tip: '鼠标左键选择【{5}】', val: '{6}' }, + { deviceType: '04', orderNum: 3, operateCode: '314', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + { + maxDuration: 8, + minDuration: 5, + operateType: '0206', + skinStyle: '03', + trainingName: '信号重开({3} 进路)', + trainingRemark: '信号重开功能', + trainingType: '02', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '04', orderNum: 1, operateCode: '304', tip: '鼠标右键菜单选择【信号重开】' }, + { deviceType: '04', orderNum: 2, operateCode: '304', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + + { + maxDuration: 15, + minDuration: 8, + operateType: '0801', + skinStyle: '03', + trainingName: '全线取消临时限速', + trainingRemark: '全线取消临时限速功能', + trainingType: '08', + productTypes: ['01,02'], + stepVOList: [ + { deviceType: '10', orderNum: 1, operateCode: '800', tip: '鼠标右键菜单选择【取消全线临时限速】' }, + { deviceType: '10', orderNum: 2, operateCode: '8001', tip: '鼠标左键点击【打开会话】按钮' }, + { deviceType: '10', orderNum: 3, operateCode: '8003', tip: '鼠标左键点击【确认】按钮' }, + { deviceType: '10', orderNum: 4, operateCode: '8004', tip: '鼠标左键点击【确认】按钮' }, + { deviceType: '10', orderNum: 5, operateCode: '8005', tip: '鼠标左键点击【关闭】按钮' }, + { deviceType: '10', orderNum: 6, operateCode: '8006', tip: '鼠标左键点击【确定】按钮' }, + ] + }, + ], + } +}; \ No newline at end of file diff --git a/src/scripts/plugin/converter/CancelLimitOperationConverter.js b/src/scripts/plugin/converter/CancelLimitOperationConverter.js new file mode 100644 index 000000000..eabb64f66 --- /dev/null +++ b/src/scripts/plugin/converter/CancelLimitOperationConverter.js @@ -0,0 +1,45 @@ +import { OperationEvent, getOperateTypeBy, MapDeviceType } from '@/scripts/ConstDic'; + +const CancelLimitOperation = OperationEvent.LimitControl; + +const CancelLimitType = MapDeviceType.LimitControl.type; + +export default { + test: function (operates) { + let operateType = getOperateTypeBy('LimitControl', operates[0].operation); + if (operateType) { + return true; + } else { + return false; + } + }, + convert: function (operates) { + if (operates && operates.length) { + let operation = operates[0].operation; + switch (operation) { + case CancelLimitOperation.CancelAllLimit.menu.operation: return handleMenuCancelAllLimit(operates); + + } + } + } +}; + +/** + * 处理右键菜单 取消故障 操作 + * @param {*} operates + */ +function handleMenuCancelAllLimit(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === CancelLimitType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: CancelLimitOperation.CancelAllLimit.event + }; + } + return { error: true }; + } + return null; +} diff --git a/src/scripts/plugin/converter/MixinOperationConverter.js b/src/scripts/plugin/converter/MixinOperationConverter.js new file mode 100644 index 000000000..752159f19 --- /dev/null +++ b/src/scripts/plugin/converter/MixinOperationConverter.js @@ -0,0 +1,43 @@ +import { OperationEvent, getOperateTypeBy, checkOperationIsCurrentOperate } from '@/scripts/ConstDic'; +import { handleMenuRequestCentralControl, handleMenuRequestStationControl, handleMenuEmergencyStationControl } from './StationControlOperationConverter'; + +const MixinCommandOperation = OperationEvent.MixinCommand +const StationControlOperation = OperationEvent.StationControl; + +export default { + test: function (operates) { + let operateType = getOperateTypeBy('MixinCommand', operates[0].operation); + if (operateType) { + return true; + } else { + return false; + } + }, + convert: function (operates) { + if (operates && operates.length) { + switch (operates[0].operation) { + case MixinCommandOperation.remoteControl.mbar.operation: return handleMenuRemoteControl(operates); + } + } + } +}; + + +/** + * 控制模式操作 + */ +function handleMenuRemoteControl(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (checkOperationIsCurrentOperate(operate.operation, StationControlOperation.requestStationControl)) { + return handleMenuRequestCentralControl(operates); + } else if (checkOperationIsCurrentOperate(operate.operation, StationControlOperation.requestCentralControl)) { + return handleMenuRequestStationControl(operates); + } else if (checkOperationIsCurrentOperate(operate.operation, StationControlOperation.emergencyStationControl)) { + return handleMenuEmergencyStationControl(operates); + } + + return { error: true }; + } + return null; +} \ No newline at end of file diff --git a/src/scripts/plugin/converter/SectionOperationConverter.js b/src/scripts/plugin/converter/SectionOperationConverter.js new file mode 100644 index 000000000..f0f5d4773 --- /dev/null +++ b/src/scripts/plugin/converter/SectionOperationConverter.js @@ -0,0 +1,439 @@ +import { OperationEvent, getOperateTypeBy, MapDeviceType } from '@/scripts/ConstDic'; +import localStore from 'storejs'; +import store from '@/store'; + +const SectionOperation = OperationEvent.Section; + +const SectionType = MapDeviceType.Section.type; + +export default { + test: function (operates) { + let operateType = getOperateTypeBy('Section', operates[0].operation); + if (operateType) { + return true; + } else { + return false; + } + }, + convert: function (operates) { + if (operates && operates.length) { + let operation = operates[0].operation; + switch (operation) { + case SectionOperation.cancelStoppage.menu.operation: return handleMenuCancelStopPage(operates); + + case SectionOperation.stoppage.menu.operation: return handleMenuFailure(operates); + + case SectionOperation.alxeFailure.menu.operation: return handleMenuAlxeFailure(operates); + + case SectionOperation.fault.menu.operation: return handleMenuFault(operates); + case SectionOperation.fault.button.operation: return handleButtonFault(operates); + + case SectionOperation.lock.menu.operation: return handleMenuLock(operates); + + case SectionOperation.unlock.menu.operation: return handleMenuUnlock(operates); + + case SectionOperation.split.menu.operation: return handleMenuSplit(operates); + + case SectionOperation.active.menu.operation: return handleMenuActive(operates); + + case SectionOperation.setSpeed.menu.operation: return handleMenuSetSpeed(operates); + + case SectionOperation.cancelSpeed.menu.operation: return handleMenuCancelSpeed(operates); + + case SectionOperation.axlePreReset.menu.operation: return handleMenuAxlePreReset(operates); + case SectionOperation.axlePreReset.button.operation: return handleButtonAxlePreReset(operates); + + case SectionOperation.detail.menu.operation: return handleMenuDetail(operates); + + case SectionOperation.newtrain.menu.operation: return handleMenuNewTrain(operates); + + case SectionOperation.alxeEffective.menu.operation: return handleMenuAlxeEffective(operates); + + case SectionOperation.setLimitSpeed.menu.operation: return handleMenuSetLimitSpeed(operates); + } + } + } +}; + +/** + * 处理右键菜单 取消故障 操作 + * @param {*} operates + */ +function handleMenuCancelStopPage(operates) { + if (operates.length >= 1) { + let operate = operates[0]; + if (operate.type === SectionType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SectionOperation.cancelStoppage.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 设置计轴失效 操作 + * @param {*} operates + */ +function handleMenuFailure(operates) { + if (operates.length >= 1) { + let operate = operates[0]; + if (operate.type === SectionType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SectionOperation.stoppage.event, + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 设置计轴失效 操作 + * @param {*} operates + */ +function handleMenuAlxeFailure(operates) { + if (operates.length >= 1) { + let operate = operates[0]; + if (operate.type === SectionType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SectionOperation.alxeFailure.event, + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 区故解 操作 + * @param {*} operates + */ +function handleMenuFault(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SectionType && operate.code) { + if (operate.code) { + let val = ''; + let counter = store.getters['map/getCounterBySectionCode'](operate.code, '01'); + if (counter) { + val = localStore.get(counter.code); + } + + return { + type: operate.type, + code: operate.code, + over: true, + operation: SectionOperation.fault.event, + val: val + }; + } + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 封锁 操作 + * @param {*} operates + */ +function handleMenuLock(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SectionType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SectionOperation.lock.event, + val: operates[operates.length - 1].val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 解锁 操作 + * @param {*} operates + */ +function handleMenuUnlock(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SectionType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SectionOperation.unlock.event, + val: operates[operates.length - 1].val + }; + } + return { error: true }; + } + return null; +} + + +/** + * 处理右键菜单 切除 操作 + * @param {*} operates + */ +function handleMenuSplit(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SectionType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SectionOperation.split.event, + val: operates[operates.length - 1].val + }; + } + return { error: true }; + } + return null; +} + + +/** + * 处理右键菜单 激活 操作 + * @param {*} operates + */ +function handleMenuActive(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SectionType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SectionOperation.active.event, + val: operates[operates.length - 1].val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 设置速度 + * @param {*} operates + */ + +function handleMenuSetSpeed(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SectionType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SectionOperation.setSpeed.event, + val: operates[operates.length - 1].val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 取消速度 + * @param {*} operates + */ + +function handleMenuCancelSpeed(operates) { + if (operates.length == 1) { + let operate = operates[0]; + if (operate.type === SectionType && operate.code) { + return { + type: operate.type, + code: operate.code, + operation: SectionOperation.cancelSpeed.event.query + }; + } + return { error: true }; + } else if (operates.length >= 3) { + let operate = operates[operates.length - 1]; + if (operate.type === SectionType && operates[0].code) { + return { + over: true, + type: operate.type, + code: operates[0].code, + operation: SectionOperation.cancelSpeed.event.confirm + }; + } + return { error: true }; + } + + return null; +} + +/** + * 处理右键菜单 区段预复位 + * @param {*} operates + */ + +function handleMenuAxlePreReset(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SectionType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SectionOperation.axlePreReset.event, + val: operates[operates.length - 1].val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 区段详细信息 + * @param {*} operates + */ + +function handleMenuDetail(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SectionType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SectionOperation.detail.event, + val: operates[operates.length - 1].val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 新建列车操作 + * @param {*} operates + */ +function handleMenuNewTrain(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SectionType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SectionOperation.newtrain.event, + val: operates[operates.length - 1].val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 设置计轴有效操作 + * @param {*} operates + */ +function handleMenuAlxeEffective(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SectionType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SectionOperation.alxeEffective.event, + val: operates[operates.length - 1].val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 设置临时限速操作 + * @param {*} operates + */ +function handleMenuSetLimitSpeed(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SectionType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SectionOperation.setLimitSpeed.event, + val: operates[operates.length - 1].val + }; + } else if (operate.type === MapDeviceType.Switch.type && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SectionOperation.setLimitSpeed.event, + val: operates[operates.length - 1].val + }; + } + return { error: true }; + } +} + +/** + * 处理点击按钮 区故解 操作 + * @param {*} operates + */ +function handleButtonFault(operates) { + if (operates.length >= 2) { + let operate = operates[operates.length - 1]; + if (operate.type === SectionType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SectionOperation.fault.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理点击按钮 计轴预复位 操作 + * @param {*} operates + */ +function handleButtonAxlePreReset(operates) { + if (operates.length >= 2) { + let operate = operates[operates.length - 1]; + if (operate.type === SectionType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SectionOperation.axlePreReset.event + }; + } + return { error: true }; + } + return null; +} diff --git a/src/scripts/plugin/converter/SignalRouteOperationConverter.js b/src/scripts/plugin/converter/SignalRouteOperationConverter.js new file mode 100644 index 000000000..a2b6bf3c5 --- /dev/null +++ b/src/scripts/plugin/converter/SignalRouteOperationConverter.js @@ -0,0 +1,644 @@ +import { OperationEvent, getOperateTypeBy, MapDeviceType } from '@/scripts/ConstDic'; +import localStore from 'storejs'; +import store from '@/store'; + +const SignalOperation = OperationEvent.Signal; + +const SignalType = MapDeviceType.Signal.type; + +export default { + test: function (operates) { + let operateType = getOperateTypeBy('Signal', operates[0].operation); + if (operateType) { + return true; + } else { + return false; + } + }, + /** + * 验证前预处理 + */ + preHandle: function (operates) { + if (operates && operates.length) { + let curOperate = operates[operates.length - 1]; + switch (operates[0].operation) { + case SignalOperation.arrangementRoute.button: { + if (curOperate.model) { + curOperate.val = curOperate.model._viewVal; + } + return curOperate; + } + case SignalOperation.cancelTrainRoute.button: { + if (curOperate.model) { + curOperate.val = curOperate.model._viewVal; + } + return curOperate; + } + case SignalOperation.reopenSignal.button: { + return curOperate; + } + case SignalOperation.humanTrainRoute.button: { + if (curOperate.model) { + curOperate.val = curOperate.model._viewVal; + } + return curOperate; + } + default: { + return curOperate; + } + } + } + }, + convert: function (operates) { + if (operates && operates.length) { + switch (operates[0].operation) { + case SignalOperation.cancelStoppage.menu.operation: return handleMenuCancelStopPage(operates); + + case SignalOperation.stoppage.menu.operation: return handleMenuStoppage(operates); + + case SignalOperation.arrangementRoute.button.operation: return handleButtonArrangementRoute(operates); + case SignalOperation.arrangementRoute.menu.operation: return handleMenuArrangementRoute(operates); + + case SignalOperation.cancelTrainRoute.button.operation: return handleButtonCancelTrainRoute(operates); + case SignalOperation.cancelTrainRoute.menu.operation: return handleMenuCancelTrainRoute(operates); + + case SignalOperation.reopenSignal.button.operation: return handleButtonReopenSignal(operates); + case SignalOperation.reopenSignal.menu.operation: return handleMenuReopenSignal(operates); + + case SignalOperation.humanTrainRoute.button.operation: return handleButtonHumanTrainRoute(operates); + case SignalOperation.humanTrainRoute.menu.operation: return handleMenuHumanTrainRoute(operates); + + case SignalOperation.lock.menu.operation: return handleMenuLock(operates); + + case SignalOperation.unlock.menu.operation: return handleMenuUnlock(operates); + + case SignalOperation.guide.menu.operation: return handleMenuGuideRoute(operates); + case SignalOperation.guide.button.operation: return handleButtonGuideRoute(operates); + + case SignalOperation.signalClose.menu.operation: return handleMenuSignalClose(operates); + + case SignalOperation.setAutoInterlock.menu.operation: return handleMenuSetAutoInterlock(operates); + case SignalOperation.setAutoInterlock.button.operation: return handleButtonSetAutoInterlock(operates); + + case SignalOperation.cancelAutoInterlock.menu.operation: return handleMenuCancelAutoInterlock(operates); + + case SignalOperation.setAutoTrigger.menu.operation: return handleMenuSetAutoTrigger(operates); + + case SignalOperation.cancelAutoTrigger.menu.operation: return handleMenuCancelAutoTrigger(operates); + + case SignalOperation.humanControl.menu.operation: return handleMenuHumanControl(operates); + + case SignalOperation.atsAutoControl.menu.operation: return handleMenuAtsAutoControl(operates); + + case SignalOperation.detail.menu.operation: return handleMenuDetail(operates); + } + } + } +}; + +/** + * 处理点击按钮 排列进路 操作 + * @param {*} operates + */ +function handleButtonArrangementRoute(operates) { + if (operates.length === 2) { + let operate = operates[1]; + if (operate.type === SignalType && operate.code && + operate.model && operate.model._viewVal) { + return { + type: operate.type, + code: operate.code, + operation: SignalOperation.arrangementRoute.event.query, + val: operate.model._viewVal + }; + } + return { error: true }; + } else if (operates.length >= 3) { + let operateBeg = operates[1]; + let operateEnd = operates[operates.length - 1]; + if (operateEnd.type === SignalType && operateEnd.code && + operateEnd.model && operateBeg.val === operateEnd.model._viewVal && + operateEnd.tempData && operateEnd.tempData.length) { + for (let i = 0; i < operateEnd.tempData.length; i++) { + let elem = operateEnd.tempData[i]; + if (elem.startSignalCode === operateBeg.code && elem.endSignalCode == operateEnd.code) { + return { + type: operateEnd.type, + code: operateEnd.code, + over: true, + operation: SignalOperation.arrangementRoute.event.confirm, + val: elem.code + }; + } + } + } + return { error: true }; + } + return null; +} + +/** + * 处理点击按钮 取消进路 操作 + * @param {*} operates + */ +function handleButtonCancelTrainRoute(operates) { + if (operates.length >= 2) { + let operate = operates[operates.length - 1]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SignalOperation.cancelTrainRoute.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理点击按钮 信号重开 操作 + * @param {*} operates + */ +function handleButtonReopenSignal(operates) { + if (operates.length >= 2) { + let operate = operates[operates.length - 1]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SignalOperation.reopenSignal.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理点击按钮 总人解 操作 + * @param {*} operates + */ +function handleButtonHumanTrainRoute(operates) { + if (operates.length >= 2) { + let operate = operates[operates.length - 1]; + if (operate.type === SignalType && operate.code) { + if (operate.code) { + let counter = store.getters['map/getCounterBySingalCode'](operate.code, '02'); + if (counter) { + let val = localStore.get(counter.code) || ''; + return { + type: operate.type, + code: operate.code, + over: true, + operation: SignalOperation.humanTrainRoute.event, + val: val + }; + } + } + + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 取消故障 操作 + * @param {*} operates + */ +function handleMenuCancelStopPage(operates) { + if (operates.length >= 1) { + let operate = operates[0]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SignalOperation.cancelStoppage.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 设置故障 操作 + * @param {*} operates + */ +function handleMenuStoppage(operates) { + if (operates.length >= 1) { + let operate = operates[0]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SignalOperation.stoppage.event, + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 排列进路 操作 + * @param {*} operates + */ +function handleMenuArrangementRoute(operates) { + if (operates.length == 1) { + let operate = operates[0]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + operation: SignalOperation.arrangementRoute.event.query, + }; + } + return { error: true }; + } else if (operates.length >= 3) { + let operate = operates[operates.length - 1]; + if (operate.type === SignalType && operates[0].code && (operates[operates.length - 2].val || operates[operates.length - 3].val)) { + return { + over: true, + type: operate.type, + code: operates[0].code, + operation: SignalOperation.arrangementRoute.event.confirm, + val: operates[operates.length - 2].val || operates[operates.length - 3].val + }; + } + return { error: true }; + } + + return null; +} + +/** + * 处理右键菜单 取消进路 操作 + * @param {*} operates + */ +function handleMenuCancelTrainRoute(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SignalOperation.cancelTrainRoute.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 信号重开 操作 + * @param {*} operates + */ +function handleMenuReopenSignal(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SignalOperation.reopenSignal.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 总人解 操作 + * @param {*} operates + */ +function handleMenuHumanTrainRoute(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SignalType && operate.code) { + if (operate.code) { + let counter = store.getters['map/getCounterBySingalCode'](operate.code, '02'); + if (counter) { + let val = localStore.get(counter.code) || ''; + return { + type: operate.type, + code: operate.code, + over: true, + operation: SignalOperation.humanTrainRoute.event, + val: val + }; + } + } + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 封锁 操作 + * @param {*} operates + */ +function handleMenuLock(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SignalOperation.lock.event + }; + } + return { error: true }; + } +} + +/** + * 处理右键菜单 解封 操作 + * @param {*} operates + */ +function handleMenuUnlock(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SignalOperation.unlock.event + }; + } + return { error: true }; + } +} + +/** + * 处理右键菜单 引导 操作 + * @param {*} operates + */ +function handleMenuGuideRoute(operates) { + if (operates.length == 1) { + let operate = operates[0]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + operation: SignalOperation.guide.event.query, + }; + } + return { error: true }; + } else if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SignalOperation.guide.event.confirm + }; + } + return { error: true }; + } +} + +/** + * 处理右键菜单 信号关灯 操作 + * @param {*} operates + */ +function handleMenuSignalClose(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SignalOperation.signalClose.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 设置联锁自动进路 操作 + * @param {*} operates + */ +function handleMenuSetAutoInterlock(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SignalOperation.setAutoInterlock.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 取消联锁自动进路 操作 + * @param {*} operates + */ +function handleMenuCancelAutoInterlock(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SignalOperation.cancelAutoInterlock.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 设置联锁自动触发 操作 + * @param {*} operates + */ +function handleMenuSetAutoTrigger(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SignalOperation.setAutoTrigger.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 取消联锁自动触发 操作 + * @param {*} operates + */ +function handleMenuCancelAutoTrigger(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SignalOperation.cancelAutoTrigger.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 进路交人工控 操作 + * @param {*} operates + */ +function handleMenuHumanControl(operates) { + if (operates.length == 1) { + let operate = operates[0]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + operation: SignalOperation.humanControl.event.query, + }; + } + return { error: true }; + } else if (operates.length >= 2) { + let operate = operates[operates.length - 1]; + let val = null; + val = operates[operates.length - 3] && operates[operates.length - 3].val ? operates[operates.length - 3].val : val; + val = operates[operates.length - 2] && operates[operates.length - 2].val ? operates[operates.length - 2].val : val; + val = operates[operates.length - 1] && operates[operates.length - 1].val ? operates[operates.length - 1].val : val; + + if (operate.type === SignalType && operates[0].code && val) { + return { + over: true, + type: operate.type, + code: operates[0].code, + operation: SignalOperation.humanControl.event.confirm, + val: val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 进路交ATS自动工控 操作 + * @param {*} operates + */ +function handleMenuAtsAutoControl(operates) { + if (operates.length == 1) { + let operate = operates[0]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + operation: SignalOperation.atsAutoControl.event.query, + }; + } + return { error: true }; + } else if (operates.length >= 2) { + let operate = operates[operates.length - 1]; + let val = null; + val = operates[operates.length - 3] && operates[operates.length - 3].val ? operates[operates.length - 3].val : val; + val = operates[operates.length - 2] && operates[operates.length - 2].val ? operates[operates.length - 2].val : val; + val = operates[operates.length - 1] && operates[operates.length - 1].val ? operates[operates.length - 1].val : val; + + if (operate.type === SignalType && operates[0].code && val) { + return { + over: true, + type: operate.type, + code: operates[0].code, + operation: SignalOperation.atsAutoControl.event.confirm, + val: val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 查询进路状态 操作 + * @param {*} operates + */ +function handleMenuDetail(operates) { + if (operates.length == 1) { + let operate = operates[0]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + operation: SignalOperation.detail.event.query, + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理点击按钮 设置联锁自动进路 操作 + * @param {*} operates + */ +function handleButtonSetAutoInterlock(operates) { + if (operates.length >= 2) { + let operate = operates[operates.length - 1]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SignalOperation.setAutoInterlock.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理点击按钮 引导 操作 + * @param {*} operates + */ +function handleButtonGuideRoute(operates) { + if (operates.length >= 2) { + let operate = operates[operates.length - 1]; + if (operate.type === SignalType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SignalOperation.guide.event + }; + } + return { error: true }; + } + return null; +} + diff --git a/src/scripts/plugin/converter/StationControlOperationConverter.js b/src/scripts/plugin/converter/StationControlOperationConverter.js new file mode 100644 index 000000000..b922c4997 --- /dev/null +++ b/src/scripts/plugin/converter/StationControlOperationConverter.js @@ -0,0 +1,257 @@ +import { OperationEvent, getOperateTypeBy, MapDeviceType } from '@/scripts/ConstDic'; + +const StationControlOperation = OperationEvent.StationControl; + +const StationControlType = MapDeviceType.StationControl.type; + +export default { + test: function (operates) { + let operateType = getOperateTypeBy('StationControl', operates[0].operation); + if (operateType) { + return true; + } else { + return false; + } + }, + convert: function (operates) { + if (operates && operates.length) { + let operation = operates[0].operation; + switch (operation) { + case StationControlOperation.cancelStoppage.menu.operation: return handleMenuCancelStopPage(operates); + + case StationControlOperation.stoppage.menu.operation: return handleMenuStoppage(operates); + + case StationControlOperation.emergencyStationControl.menu.operation: return handleMenuEmergencyStationControl(operates); + + case StationControlOperation.requestStationControl.menu.operation: return handleMenuRequestStationControl(operates); + case StationControlOperation.requestStationControl.mbar.operation: return handleBarRequestStationControl(operates); + + case StationControlOperation.forcedStationControl.menu.operation: return handleMenuForcedStationControl(operates); + case StationControlOperation.forcedStationControl.passwordConfirm.operation: return handleBarForcedStationControl(operates); + case StationControlOperation.forcedStationControl.mbar.operation: return handleBarForcedStationControl(operates); + + case StationControlOperation.requestCentralControl.menu.operation: return handleMenuRequestCentralControl(operates); + case StationControlOperation.requestCentralControl.mbar.operation: return handleBarRequestCentralControl(operates); + + case StationControlOperation.controlResponse.menu.operation: return handleMenuControlResponse(operates); + } + } + } +}; + +/** + * 处理右键菜单 取消故障 操作 + * @param {*} operates + */ +export function handleMenuCancelStopPage(operates) { + if (operates.length >= 1) { + let operate = operates[0]; + if (operate.type === StationControlType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: StationControlOperation.cancelStoppage.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 设置故障 操作 + * @param {*} operates + */ +export function handleMenuStoppage(operates) { + if (operates.length >= 1) { + let operate = operates[0]; + if (operate.type === StationControlType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: StationControlOperation.stoppage.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 紧急站控 操作 + * @param {*} operates + */ +export function handleMenuEmergencyStationControl(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationControlType && operate.val) { + return { + type: operate.type, + val: operate.val, + code: operate.code, + over: true, + operation: StationControlOperation.emergencyStationControl.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 请求站控 操作 + * @param {*} operates + */ +export function handleMenuRequestStationControl(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationControlType && operate.val) { + return { + type: operate.type, + val: operate.val, + code: operate.code, + over: true, + operation: StationControlOperation.requestStationControl.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 强行站控 操作 + * @param {*} operates + */ +export function handleMenuForcedStationControl(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationControlType && operate.val) { + return { + type: operate.type, + val: operate.val, + code: operate.code, + over: true, + operation: StationControlOperation.forcedStationControl.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 请求中控 操作 + * @param {*} operates + */ +export function handleMenuRequestCentralControl(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationControlType && operate.val) { + return { + type: operate.type, + val: operate.val, + code: operate.code, + over: true, + operation: StationControlOperation.requestCentralControl.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 控制模式应答 操作 + * @param {*} operates + */ +export function handleMenuControlResponse(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationControlType && operate.val) { + let operation = ''; + if (operate.operation == StationControlOperation.controlResponse.agree.operation) { + if (operate.prdType == '01') { + operation = StationControlOperation.controlResponse.event.stationAgree; + } else { + operation = StationControlOperation.controlResponse.event.centralAgree; + } + } else { + if (operate.prdType == '01') { + operation = StationControlOperation.controlResponse.event.stationRefuse; + } else { + operation = StationControlOperation.controlResponse.event.centralRefuse; + } + } + + return { + type: operate.type, + val: operate.val, + code: operate.code, + over: true, + operation: operation + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理菜单栏 请求站控 操作 + * @param {*} operates + */ +export function handleBarRequestStationControl(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationControlType && operate.val) { + return { + type: operate.type, + operation: StationControlOperation.requestStationControl.event, + code: operate.val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理菜单栏 强制站控 操作 + * @param {*} operates + */ +export function handleBarForcedStationControl(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationControlType && operate.val) { + return { + type: operate.type, + code: operate.val, + operation: StationControlOperation.forcedStationControl.event, + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理菜单栏 请求中控 操作 + * @param {*} operates + */ +export function handleBarRequestCentralControl(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationControlType && operate.val) { + return { + type: operate.type, + code: operate.val, + operation: StationControlOperation.requestCentralControl.event, + }; + } + return { error: true }; + } + return null; +} \ No newline at end of file diff --git a/src/scripts/plugin/converter/StationOperationConverter.js b/src/scripts/plugin/converter/StationOperationConverter.js new file mode 100644 index 000000000..cb28ac193 --- /dev/null +++ b/src/scripts/plugin/converter/StationOperationConverter.js @@ -0,0 +1,201 @@ +import { OperationEvent, getOperateTypeBy, MapDeviceType } from '@/scripts/ConstDic'; + +const StationOperation = OperationEvent.Station; + +const StationType = MapDeviceType.Station.type; + +export default { + test: function (operates) { + let operateType = getOperateTypeBy('Station', operates[0].operation); + if (operateType) { + return true; + } else { + return false; + } + }, + convert: function (operates) { + if (operates && operates.length) { + let operation = operates[0].operation; + switch (operation) { + case StationOperation.cancelStoppage.menu.operation: return handleMenuCancelStopPage(operates); + + case StationOperation.stoppage.menu.operation: return handleMenuStoppage(operates); + + case StationOperation.setAutoTrigger.menu.operation: return handleMenuSetAutoTrigger(operates); + + case StationOperation.cancelAutoTrigger.menu.operation: return handleMenuCancelAutoTrigger(operates); + + case StationOperation.powerUnLock.menu.operation: return handleMenuPowerUnLock(operates); + + case StationOperation.execKeyOperationTest.menu.operation: return handleMenuExecKeyOperationTest(operates); + + case StationOperation.humanControlALL.menu.operation: return handleMenuHumanControlAll(operates); + + case StationOperation.atsAutoControlALL.menu.operation: return handleMenuAtsAutoControlAll(operates); + } + } + } +}; + +/** + * 处理右键菜单 取消故障 操作 + * @param {*} operates + */ +function handleMenuCancelStopPage(operates) { + if (operates.length >= 1) { + let operate = operates[0]; + if (operate.type === StationType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: StationOperation.cancelStoppage.event, + }; + } + return { error: true }; + } + return null; +} + + +/** + * 处理右键菜单 设置故障 操作 + * @param {*} operates + */ +function handleMenuStoppage(operates) { + if (operates.length >= 1) { + let operate = operates[0]; + if (operate.type === StationType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: StationOperation.stoppage.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 全站设置联锁自动触发 操作 + * @param {*} operates + */ +function handleMenuSetAutoTrigger(operates) { + if (operates.length >= 1) { + let operate = operates[0]; + if (operate.type === StationType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: StationOperation.setAutoTrigger.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 全站取消联锁自动触发 操作 + * @param {*} operates + */ +function handleMenuCancelAutoTrigger(operates) { + if (operates.length >= 1) { + let operate = operates[0]; + if (operate.type === StationType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: StationOperation.cancelAutoTrigger.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 上电解锁 操作 + * @param {*} operates + */ +function handleMenuPowerUnLock(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === StationType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: StationOperation.powerUnLock.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 执行关键操作测试 操作 + * @param {*} operates + */ +function handleMenuExecKeyOperationTest(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === StationType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: StationOperation.execKeyOperationTest.event + }; + } + return { error: true }; + } + return null; +} + + +/** + * 处理右键菜单 所有进路自排关 操作 + * @param {*} operates + */ +function handleMenuHumanControlAll(operates) { + if (operates.length >= 1) { + let operate = operates[0]; + if (operate.type === StationType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: StationOperation.humanControlALL.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 所有进路自排开 操作 + * @param {*} operates + */ +function handleMenuAtsAutoControlAll(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationType && operates[0].code) { + return { + type: operate.type, + code: operates[0].code, + over: true, + operation: StationOperation.atsAutoControlALL.event, + val: operate.val + }; + } + return { error: true }; + } + return null; +} \ No newline at end of file diff --git a/src/scripts/plugin/converter/StationStandOperationConverter.js b/src/scripts/plugin/converter/StationStandOperationConverter.js new file mode 100644 index 000000000..39df39ba3 --- /dev/null +++ b/src/scripts/plugin/converter/StationStandOperationConverter.js @@ -0,0 +1,438 @@ +import { OperationEvent, getOperateTypeBy, MapDeviceType } from '@/scripts/ConstDic'; + +const StationStandOperation = OperationEvent.StationStand; + +const StationStandType = MapDeviceType.StationStand.type; + +export default { + test: function (operates) { + let operateType = getOperateTypeBy('StationStand', operates[0].operation); + if (operateType) { + return true; + } else { + return false; + } + }, + convert: function (operates) { + if (operates && operates.length) { + let operation = operates[0].operation; + switch (operation) { + case StationStandOperation.cancelStoppage.menu.operation: return handleMenuCancelStopPage(operates); + + case StationStandOperation.stoppage.menu.operation: return handleMenuStoppage(operates); + + case StationStandOperation.earlyDeparture.menu.operation: return handleMenuEarlyDeparture(operates); + + case StationStandOperation.setJumpStop.menu.operation: return handleMenuSetJumpStop(operates); + + case StationStandOperation.cancelJumpStop.menu.operation: return handleMenuCancelJumpStop(operates); + + case StationStandOperation.setDetainTrain.menu.operation: return handleMenuSetDetainTrain(operates); + + case StationStandOperation.cancelDetainTrain.menu.operation: return handleMenuCancelDetainTrain(operates); + + case StationStandOperation.cancelDetainTrainForce.menu.operation: return handleMenuCancelDetainTrainForce(operates); + + case StationStandOperation.detail.menu.operation: return handleMenuDetail(operates); + + case StationStandOperation.cancelDetainTrainAll.menu.operation: return handleMenuCancelDetainTrainAll(operates); + + case StationStandOperation.setStopTime.menu.operation: return handleMenuSetStopTime(operates); + + case StationStandOperation.setRunLevel.menu.operation: return handleMenuSetRunLevel(operates); + + case StationStandOperation.setBackStrategy.menu.operation: return handleMenuSetBackStrategy(operates); + + case StationStandOperation.setDetainTrainAll.mbar.operation: return handleMbarSetDetainTrainAll(operates); + + case StationStandOperation.cancelDetainTrainAll.mbar.operation: return handleMbarCancelDetainTrainAll(operates); + + case StationStandOperation.cancelUpDetainTrainAll.mbar.operation: return handleMbarCancelUpDetainTrainAll(operates); + + case StationStandOperation.cancelDownDetainTrainAll.mbar.operation: return handleMbarCancelDownDetainTrainAll(operates); + } + } + } +}; + +/** + * 处理右键菜单 取消故障 操作 + * @param {*} operates + */ +function handleMenuCancelStopPage(operates) { + if (operates.length >= 1) { + let operate = operates[0]; + if (operate.type === StationStandType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: StationStandOperation.cancelStoppage.event, + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 设置故障 操作 + * @param {*} operates + */ +function handleMenuStoppage(operates) { + if (operates.length >= 1) { + let operate = operates[0]; + if (operate.type === StationStandType) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: StationStandOperation.stoppage.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 提前发车 操作 + * @param {*} operates + */ +function handleMenuEarlyDeparture(operates) { + if (operates.length > 0) { + let operate = operates[operates.length - 1]; + if (operate.type === StationStandType) { + return { + type: operate.type, + code: operates[0].code, + over: true, + operation: StationStandOperation.earlyDeparture.event, + val: operate.val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 设置跳停 操作 + * @param {*} operates + */ +function handleMenuSetJumpStop(operates) { + if (operates.length > 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationStandType) { + return { + type: operate.type, + code: operates[0].code, + over: true, + operation: StationStandOperation.setJumpStop.event, + val: operate.val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 取消跳停 操作 + * @param {*} operates + */ +function handleMenuCancelJumpStop(operates) { + if (operates.length > 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationStandType) { + return { + type: operate.type, + code: operates[0].code, + over: true, + operation: StationStandOperation.cancelJumpStop.event, + val: operate.val + }; + } + return { error: true }; + } + return null; +} + + +/** + * 处理右键菜单 设置扣车 操作 + * @param {*} operates + */ +function handleMenuSetDetainTrain(operates) { + if (operates.length > 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationStandType) { + return { + type: operate.type, + code: operates[0].code, + over: true, + operation: StationStandOperation.setDetainTrain.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 取消扣车 操作 + * @param {*} operates + */ +function handleMenuCancelDetainTrain(operates) { + if (operates.length > 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationStandType) { + return { + type: operate.type, + code: operates[0].code, + over: true, + // operation: operate.operation, + operation: StationStandOperation.cancelDetainTrain.event, + val: operate.val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 强制取消扣车 操作 + * @param {*} operates + */ +function handleMenuCancelDetainTrainForce(operates) { + if (operates.length > 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationStandType) { + return { + type: operate.type, + code: operates[0].code, + over: true, + operation: StationStandOperation.cancelDetainTrainForce.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 站台详细信息 操作 + * @param {*} operates + */ +function handleMenuDetail(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationStandType) { + return { + type: operate.type, + code: operates[0].code, + over: true, + operation: StationStandOperation.detail.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 取消全站扣车 操作 + * @param {*} operates + */ +function handleMenuCancelDetainTrainAll(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationStandType) { + return { + type: operate.type, + code: operates[0].code, + over: true, + operation: StationStandOperation.cancelDetainTrainAll.event, + val: operate.val, + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 设置停站时间 操作 + * @param {*} operates + */ +function handleMenuSetStopTime(operates) { + if (operates.length == 1) { + let operate = operates[0]; + if (operate.type === StationStandType && operate.code) { + return { + type: operate.type, + code: operate.code, + operation: StationStandOperation.setStopTime.event.query, + }; + } + return { error: true }; + } else if (operates.length >= 2) { + let operate = operates[operates.length - 1]; + if (operate.type === StationStandType && operate.val) { + return { + over: true, + type: operate.type, + code: operates[0].code, + operation: StationStandOperation.setStopTime.event.confirm, + val: operate.val + }; + } + return { error: true }; + } + + return null; +} + +/** + * 处理右键菜单 设置运行等级 操作 + * @param {*} operates + */ +function handleMenuSetRunLevel(operates) { + if (operates.length == 1) { + let operate = operates[0]; + if (operate.type === StationStandType && operate.code) { + return { + type: operate.type, + code: operate.code, + operation: StationStandOperation.setRunLevel.event.query, + }; + } + return { error: true }; + } else if (operates.length >= 2) { + let operate = operates[operates.length - 1]; + if (operate.type === StationStandType && operate.val) { + return { + over: true, + type: operate.type, + code: operates[0].code, + operation: StationStandOperation.setRunLevel.event.confirm, + val: operate.val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 提前发车 操作 + * @param {*} operates + */ +function handleMenuSetBackStrategy(operates) { + if (operates.length == 1) { + let operate = operates[0]; + if (operate.type === StationStandType && operate.code) { + return { + type: operate.type, + code: operate.code, + operation: StationStandOperation.setBackStrategy.event.query, + }; + } + return { error: true }; + } else if (operates.length >= 2) { + let operate = operates[operates.length - 1]; + if (operate.type === StationStandType && operate.val) { + return { + over: true, + type: operate.type, + code: operates[0].code, + operation: StationStandOperation.setBackStrategy.event.confirm, + val: operate.val + }; + } + return { error: true }; + } + return null; +} + + +/** + * 处理菜单栏 设置全线扣车 操作 + * @param {*} operates + */ +function handleMbarSetDetainTrainAll(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationStandType) { + return { + type: operate.type, + code: operate.val, + operation: StationStandOperation.setDetainTrainAll.event, + val: operate.val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理菜单栏 取消全线扣车 操作 + * @param {*} operates + */ +function handleMbarCancelDetainTrainAll(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationStandType) { + return { + type: operate.type, + code: operate.val, + operation: StationStandOperation.cancelDetainTrainAll.event, + val: operate.val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理菜单栏 取消全线上行扣车 操作 + * @param {*} operates + */ +function handleMbarCancelUpDetainTrainAll(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationStandType) { + return { + type: operate.type, + code: operate.val, + operation: StationStandOperation.cancelUpDetainTrainAll.event, + val: operate.val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理菜单栏 取消全线上行扣车 操作 + * @param {*} operates + */ +function handleMbarCancelDownDetainTrainAll(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === StationStandType) { + return { + type: operate.type, + code: operate.val, + operation: StationStandOperation.cancelDownDetainTrainAll.event, + val: operate.val + }; + } + return { error: true }; + } + return null; +} \ No newline at end of file diff --git a/src/scripts/plugin/converter/SwitchOperationConverter.js b/src/scripts/plugin/converter/SwitchOperationConverter.js new file mode 100644 index 000000000..4bdc0d422 --- /dev/null +++ b/src/scripts/plugin/converter/SwitchOperationConverter.js @@ -0,0 +1,494 @@ +import { OperationEvent, getOperateTypeBy, MapDeviceType } from '@/scripts/ConstDic'; + +const SwitchOperation = OperationEvent.Switch; + +const SwitchType = MapDeviceType.Switch.type; + +export default { + test: function (operates) { + let operateType = getOperateTypeBy('Switch', operates[0].operation); + if (operateType) { + return true; + } else { + return false; + } + }, + convert: function (operates) { + if (operates && operates.length) { + let operation = operates[0].operation; + switch (operation) { + case SwitchOperation.cancelStoppage.menu.operation: return handleMenuCancelStopPage(operates); + + case SwitchOperation.stoppage.menu.operation: return handleMenuStoppage(operates); + + case SwitchOperation.locate.button.operation: return handleButtonLocate(operates); + case SwitchOperation.locate.menu.operation: return handleMenuLocate(operates); + + case SwitchOperation.reverse.button.operation: return handleButtonReverse(operates); + case SwitchOperation.reverse.menu.operation: return handleMenuReverse(operates); + + case SwitchOperation.lock.button.operation: return handleButtonLock(operates); + case SwitchOperation.lock.menu.operation: return handleMenuLock(operates); + + case SwitchOperation.unlock.button.operation: return handleButtonUnlock(operates); + case SwitchOperation.unlock.menu.operation: return handleMenuUnlock(operates); + + case SwitchOperation.block.menu.operation: return handleMenuBlock(operates); + + case SwitchOperation.unblock.menu.operation: return handleMenuUnblock(operates); + + case SwitchOperation.turnout.menu.operation: return handleMenuTurnout(operates); + + case SwitchOperation.turnoutForce.menu.operation: return handleMenuTurnoutForce(operates); + + case SwitchOperation.fault.menu.operation: return handleMenuFault(operates); + + case SwitchOperation.axlePreReset.menu.operation: return handleMenuAxlePreReset(operates); + + case SwitchOperation.split.menu.operation: return handleMenuSplit(operates); + + case SwitchOperation.active.menu.operation: return handleMenuActive(operates); + + case SwitchOperation.setSpeed.menu.operation: return handleMenuSetSpeed(operates); + + case SwitchOperation.cancelSpeed.menu.operation: return handleMenuCancelSpeed(operates); + + case SwitchOperation.alxeEffective.menu.operation: return handleMenuAlxeEffective(operates); + } + } + } +}; + +/** + * 处理点击按钮 定操 操作 + * @param {*} operates + */ +function handleButtonLocate(operates) { + if (operates.length >= 2) { + let operate = operates[operates.length - 1]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.locate.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理点击按钮 反操 操作 + * @param {*} operates + */ +function handleButtonReverse(operates) { + if (operates.length >= 2) { + let operate = operates[operates.length - 1]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.reverse.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理点击按钮 单锁 操作 + * @param {*} operates + */ +function handleButtonLock(operates) { + if (operates.length >= 2) { + let operate = operates[operates.length - 1]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.lock.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理点击按钮 解锁 操作 + * @param {*} operates + */ +function handleButtonUnlock(operates) { + if (operates.length >= 2) { + let operate = operates[operates.length - 1]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.unlock.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 取消故障 操作 + * @param {*} operates + */ +function handleMenuCancelStopPage(operates) { + if (operates.length >= 1) { + let operate = operates[0]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.cancelStoppage.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 设置故障 操作 + * @param {*} operates + */ +function handleMenuStoppage(operates) { + if (operates.length >= 1) { + let operate = operates[0]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.stoppage.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 定位 操作 + * @param {*} operates + */ +function handleMenuLocate(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.locate.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 反位 操作 + * @param {*} operates + */ +function handleMenuReverse(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.reverse.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 单锁 操作 + * @param {*} operates + */ +function handleMenuLock(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.lock.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 解锁 操作 + * @param {*} operates + */ +function handleMenuUnlock(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.unlock.event + }; + } + return { error: true }; + } + return null; +} + + +/** + * 处理右键菜单 道岔封锁 操作 + * @param {*} operates + */ +function handleMenuBlock(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.block.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 道岔解封 操作 + * @param {*} operates + */ +function handleMenuUnblock(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.unblock.event + }; + } + return { error: true }; + } + return null; +} + + +/** + * 处理右键菜单 故障解锁 操作 + * @param {*} operates + */ +function handleMenuFault(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.fault.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 转动 操作 + * @param {*} operates + */ +function handleMenuTurnout(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.turnout.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 扳动 操作 + * @param {*} operates + */ +function handleMenuTurnoutForce(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.turnoutForce.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 计轴预复位 操作 + * @param {*} operates + */ +function handleMenuAxlePreReset(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.axlePreReset.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 区段切除 操作 + * @param {*} operates + */ +function handleMenuSplit(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.split.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 区段激活 操作 + * @param {*} operates + */ +function handleMenuActive(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.active.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 设置速度 操作 + * @param {*} operates + */ +function handleMenuSetSpeed(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.setSpeed.event, + val: operates[operates.length - 1].val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 取消速度 操作 + * @param {*} operates + */ +function handleMenuCancelSpeed(operates) { + if (operates.length == 1) { + let operate = operates[0]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + operation: SwitchOperation.cancelSpeed.event.query, + }; + } + return { error: true }; + } else if (operates.length >= 3) { + let operate = operates[operates.length - 1]; + if (operate.type === SwitchType && operates[0].code) { + return { + over: true, + type: operate.type, + code: operates[0].code, + operation: SwitchOperation.cancelSpeed.event.confirm, + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 设置计轴有效操作 + * @param {*} operates + */ +function handleMenuAlxeEffective(operates) { + if (operates.length >= 2) { + let operate = operates[0]; + if (operate.type === SwitchType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: SwitchOperation.alxeEffective.event, + val: operates[operates.length - 1].val + }; + } + return { error: true }; + } + return null; +} \ No newline at end of file diff --git a/src/scripts/plugin/converter/TrainOperationConverter.js b/src/scripts/plugin/converter/TrainOperationConverter.js new file mode 100644 index 000000000..64dceefa8 --- /dev/null +++ b/src/scripts/plugin/converter/TrainOperationConverter.js @@ -0,0 +1,306 @@ +import { OperationEvent, getOperateTypeBy, MapDeviceType } from '@/scripts/ConstDic'; + +const TrainOperation = OperationEvent.Train; + +const TrainType = MapDeviceType.Train.type; + +export default { + test: function (operates) { + let operateType = getOperateTypeBy('Train', operates[0].operation); + if (operateType) { + return true; + } else { + return false; + } + }, + convert: function (operates) { + if (operates && operates.length) { + let operation = operates[0].operation; + switch (operation) { + case TrainOperation.cancelStoppage.menu.operation: return handleMenuCancelStopPage(operates); + + case TrainOperation.stoppage.menu.operation: return handleMenuStoppage(operates); + + case TrainOperation.addTrainId.menu.operation: return handleMenuAddTrainId(operates); + + case TrainOperation.editTrainId.menu.operation: return handleMenuEditTrainId(operates); + + case TrainOperation.delTrainId.menu.operation: return handleMenuDelTrainId(operates); + + case TrainOperation.moveTrainId.menu.operation: return handleMenuMoveTrainId(operates); + + case TrainOperation.switchTrainId.menu.operation: return handleMenuSwitchTrainId(operates); + + case TrainOperation.editTrainNo.menu.operation: return handleMenuEditTrainNo(operates); + + case TrainOperation.limitSpeed.menu.operation: return handleMenuLimitSpeed(operates); + + case TrainOperation.setPlanTrainId.menu.operation: return handleMenuSetPlanTrainId(operates); + + case TrainOperation.addPlanTrainId.menu.operation: return handleMenuAddPlanTrainId(operates); + + case TrainOperation.moveEventlyTrainId.menu.operation: return handleMenuMoveEventlyTrainId(operates); + + case TrainOperation.deletePlanTrainId.menu.operation: return handleMenuDeletePlanTrainId(operates); + + } + } + } +}; + +/** + * 处理右键菜单 取消故障 操作 + * @param {*} operates + */ +function handleMenuCancelStopPage(operates) { + if (operates.length >= 1) { + let operate = operates[0]; + if (operate.type === TrainType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: TrainOperation.cancelStoppage.event + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 设置故障 操作 + * @param {*} operates + */ +function handleMenuStoppage(operates) { + if (operates.length >= 1) { + let operate = operates[0]; + if (operate.type === TrainType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: TrainOperation.stoppage.event + }; + } + return { error: true }; + } + return null; +} + + +/** + * 处理右键菜单 添加列车识别号 操作 + * @param {*} operates + */ +function handleMenuAddTrainId(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === TrainType && operate.val) { + return { + type: operate.type, + code: operates[0].code, + over: true, + operation: TrainOperation.addTrainId.event, + val: operate.val, + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 修改列车识别号 操作 + * @param {*} operates + */ +function handleMenuEditTrainId(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === TrainType && operate.val) { + return { + type: operate.type, + code: operates[0].code, + over: true, + operation: TrainOperation.editTrainId.event, + val: operate.val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 删除列车识别号 操作 + * @param {*} operates + */ +function handleMenuDelTrainId(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === TrainType && operate.val) { + return { + type: operate.type, + code: operates[0].code, + over: true, + operation: TrainOperation.delTrainId.event, + val: operate.val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 移动列车识别号 操作 + * @param {*} operates + */ +function handleMenuMoveTrainId(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === TrainType && operate.val) { + return { + type: operate.type, + code: operates[0].code, + over: true, + operation: TrainOperation.moveTrainId.event, + val: operate.val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 交换列车识别号 操作 + * @param {*} operates + */ +function handleMenuSwitchTrainId(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === TrainType && operate.val) { + return { + type: operate.type, + code: operates[0].code, + over: true, + operation: TrainOperation.switchTrainId.event, + val: operate.val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 修改车组号 操作 + * @param {*} operates + */ +function handleMenuEditTrainNo(operates) { + if (operates.length >= 1) { + let operate = operates[operates.length - 1]; + if (operate.type === TrainType && operate.val) { + return { + type: operate.type, + code: operate[0].code, + over: true, + operation: TrainOperation.editTrainNo.event, + val: operate.val + }; + } + return { error: true }; + } + return null; +} + +/** + * 处理右键菜单 限速行驶 + * @param {*} operates + */ + +function handleMenuLimitSpeed(operates) { + if (operates.length > 0) { + let operate = operates[0]; + if (operate.type === TrainType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: TrainOperation.limitSpeed.event, + }; + } + return { error: true }; + } + return null; +} + + +/** + * 处理右键菜单 设置计划车 + * @param {*} operates + */ + +function handleMenuSetPlanTrainId(operates) { + if (operates.length > 0) { + let operate = operates[0]; + if (operate.type === TrainType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: TrainOperation.setPlanTrainId.event, + }; + } + return { error: true }; + } + return null; +} + +function handleMenuAddPlanTrainId(operates) { + if (operates.length > 0) { + let operate = operates[0]; + if (operate.type === TrainType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: TrainOperation.addPlanTrainId.event, + }; + } + return { error: true }; + } + return null; +} + +function handleMenuMoveEventlyTrainId(operates) { + if (operates.length > 0) { + let operate = operates[0]; + if (operate.type === TrainType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: TrainOperation.moveEventlyTrainId.event, + }; + } + return { error: true }; + } + return null; +} +function handleMenuDeletePlanTrainId(operates) { + if (operates.length > 0) { + let operate = operates[0]; + if (operate.type === TrainType && operate.code) { + return { + type: operate.type, + code: operate.code, + over: true, + operation: TrainOperation.deletePlanTrainId.event, + }; + } + return { error: true }; + } + return null; +} \ No newline at end of file diff --git a/src/scripts/plugin/converter/manager.js b/src/scripts/plugin/converter/manager.js new file mode 100644 index 000000000..1b19e8c9e --- /dev/null +++ b/src/scripts/plugin/converter/manager.js @@ -0,0 +1,39 @@ +import SwitchOperationConverter from './SwitchOperationConverter'; +import StationControlOperationConverter from './StationControlOperationConverter'; +import StationOperationConverter from './StationOperationConverter'; +import SignalOperationConverter from './SignalRouteOperationConverter'; +import SectionOperationConverter from './SectionOperationConverter'; +import TrainOperationConverter from './TrainOperationConverter'; +import StationStandOperationConverter from './StationStandOperationConverter'; +import CancelLimitOperationConverter from './CancelLimitOperationConverter'; +import MixinOperationConverter from './MixinOperationConverter'; + + +export function getConverter(operates) { + if (SwitchOperationConverter.test(operates)) { + return SwitchOperationConverter; + + } else if (SignalOperationConverter.test(operates)) { + return SignalOperationConverter; + + } else if (StationControlOperationConverter.test(operates)) { + return StationControlOperationConverter; + + } else if (StationOperationConverter.test(operates)) { + return StationOperationConverter; + + } else if (SectionOperationConverter.test(operates)) { + return SectionOperationConverter; + + } else if (StationStandOperationConverter.test(operates)) { + return StationStandOperationConverter; + + } else if (TrainOperationConverter.test(operates)) { + return TrainOperationConverter; + + } else if (CancelLimitOperationConverter.test(operates)) { + return CancelLimitOperationConverter; + } else if (MixinOperationConverter.test(operates)) { + return MixinOperationConverter; + } +} \ No newline at end of file diff --git a/src/scripts/plugin/operateConvert2Command.js b/src/scripts/plugin/operateConvert2Command.js new file mode 100644 index 000000000..24b8ac606 --- /dev/null +++ b/src/scripts/plugin/operateConvert2Command.js @@ -0,0 +1,67 @@ +import store from '@/store'; +import router from '@/router'; +import { sendCommand } from '@/api/jmap/training'; +import { OperationEvent } from '@/scripts/ConstDic'; +import OperateHandler from '@/scripts/plugin/trainingOperateHandler'; +import { getConverter } from '@/scripts/plugin/converter/manager'; + +var OperateConverter = function () { }; +OperateConverter.prototype = { + + /** 转换 操作 为 指令 */ + convert: function (operate) { + if (operate.send) { + let command = null; + let converter = getConverter(OperateHandler.operates); + if (converter) { + command = converter.convert(OperateHandler.operates); + } + + if (command && command.error) { + // 命令错误,可以回退或清空(暂用回退) + OperateHandler.operates.pop(); + store.dispatch('training/setTempStep', null); + command = null; + } else if (command && command.over) { + // 清空操作组 + OperateHandler.cleanOperates(); + store.dispatch('menuOperation/setButtonOperation', null); + } + + return command; + } else if (operate.operation === OperationEvent.Command.cancel.menu.operation || operate.over) { + // 取消操作,清空操作组 + OperateHandler.cleanOperates(); + } + return null; + }, + + sendToServer: function (command) { + let group = router.currentRoute.query.group; + return new Promise((resolve, reject) => { + sendCommand(group, command).then((response) => { + resolve(response); + }).catch(error => { + reject(error); + }); + }); + }, + + /** 转换操作 为 指令并发送 */ + convertAndSend: function (operate) { + return new Promise((resolve, reject) => { + let command = this.convert(operate); + if (command) { + this.sendToServer(command).then(response => { + resolve(response); + }).catch(error => { + reject(error); + }); + } else { + resolve(); + } + }); + }, +}; + +export default new OperateConverter(); \ No newline at end of file diff --git a/src/scripts/plugin/trainingOperateHandler.js b/src/scripts/plugin/trainingOperateHandler.js new file mode 100644 index 000000000..1b5007c6e --- /dev/null +++ b/src/scripts/plugin/trainingOperateHandler.js @@ -0,0 +1,142 @@ +import store from '@/store'; +import router from '@/router'; +import OperateConverter from '@/scripts/plugin/operateConvert2Command'; +import { TrainingMode } from '@/scripts/ConstDic'; +import { getConverter } from '@/scripts/plugin/converter/manager'; +import { sendTrainingNextStep } from '@/api/jmap/training'; +import { Message } from 'element-ui'; + +var OperateHandler = function () { +}; + +OperateHandler.prototype = { + /** 操作组 */ + operates: [], + + /** 清空操作组 */ + cleanOperates: function () { + this.operates.splice(0, this.operates.length); + }, + + /** + * 判断操作步骤是否正确 + */ + judge: function (operate) { + let valid = false; + let steps = this.getSteps(); + let order = this.getOrder(); + if (order >= steps) { + return valid; + } + + let standard = steps[order]; + if (operate && standard && operate.code == standard.code && operate.type == standard.type + && operate.operation == standard.operation + && operate.val == standard.val) { + valid = true; + } + return valid; + }, + + /** + * 根据模式验证操作步骤 + */ + validate: function (operate) { + // 按钮操作之后,第二步错误操作菜单的情况,需要直接返回 + if ((this.operates.length && operate.start === true) && + (this.operates[0].type === 'mbm')) { + return false; + } + + //如果是正常的第一步操作,需要清空operates数组 + if (operate.type === 'mbm' || operate.type === 'bar' || operate.start === true) { + this.cleanOperates(); + } + + this.operates.push(operate); + + // 预处理 + let converter = getConverter(this.operates); + if (converter && converter.preHandle instanceof Function) { + operate = converter.preHandle(this.operates); + } + + let valid = true; + let mode = this.getTrainingMode(); + if (TrainingMode.EDIT === mode) { + // 编辑制作模式 + if (this.getOperateBreakStatus()) { + valid = false; + Message.error('请输入提示并点击下一步'); + this.operates.pop(); + return valid; + } + if (this.getTrainingStart()) { + store.dispatch('training/setTempStep', operate); + } + } else if (TrainingMode.TEACH === mode || TrainingMode.PRACTICE === mode) { + // 教学模式/练习模式 + if (this.getTrainingStart()) { + valid = this.judge(operate); + } else { + this.cleanOperates(); + valid = false; + } + } + + // 发送每一步的步骤数据; + let basicInfo = store.getters['training/basicInfo']; + if (basicInfo.id && valid) { + let group = router.currentRoute.query.group; + sendTrainingNextStep({ trainingId: basicInfo.id, operate: operate }, group); + } + + if (!valid) { + // 如果操作校验不正确,回退 + this.operates.pop(); + } else { + if (operate.cancel === true) { + this.cleanOperates(); + } + } + + return valid; + }, + handle: function (operate) { + return new Promise((resolve, reject) => { + let rtn = { valid: false, response: null }; + let valid = this.validate(operate); + + rtn.valid = valid; + + if (valid) { + // 改变状态开始请求 + OperateConverter.convertAndSend(operate).then(response => { + rtn.response = response; + resolve(rtn); + }).catch(error => { + reject(error); + }); + } else { + resolve(rtn); + } + }); + }, + getTrainingMode: function () { + return store.state.training.mode; + }, + getSteps: function () { + return store.state.training.steps; + }, + getOrder: function () { + return store.state.training.order; + }, + getTrainingStart: function () { + return store.state.training.started; + }, + getOperateBreakStatus: function () { + return store.state.menuOperation.break; + } +}; + +export default new OperateHandler(); \ No newline at end of file diff --git a/src/store/index.js b/src/store/index.js index 1c43beec7..cdf9ee6f9 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -6,8 +6,13 @@ import user from './modules/user'; import config from './modules/config'; import permission from './modules/permission'; import map from './modules/map'; -import training from './modules/training'; import menuOperation from './modules/menuoperation'; +import training from './modules/training'; +import trainingList from './modules/trainingList'; +import exam from './modules/exam'; +import runPlan from './modules/runplan'; +import socket from './modules/socket'; + import getters from './getters'; Vue.use(Vuex); @@ -20,8 +25,12 @@ const store = new Vuex.Store({ permission, config, map, + menuOperation, training, - menuOperation + trainingList, + exam, + runPlan, + socket }, getters }); diff --git a/src/store/modules/app.js b/src/store/modules/app.js index 13d84a576..f279b1a5e 100644 --- a/src/store/modules/app.js +++ b/src/store/modules/app.js @@ -5,7 +5,10 @@ const state = { opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true, withoutAnimation: false }, - device: 'desktop' + device: 'desktop', + course: [], + courseDetail: {}, + ruleList: [] }; const mutations = { @@ -25,6 +28,15 @@ const mutations = { }, TOGGLE_DEVICE: (state, device) => { state.device = device; + }, + HANDLECOURSE: (state, course) => { + state.course = course; + }, + HANDLECOURSEDETAIL: (state, courseDetail) => { + state.courseDetail = courseDetail; + }, + HANDLERULELIST: (state, ruleList) => { + state.ruleList = ruleList; } }; @@ -37,6 +49,15 @@ const actions = { }, toggleDevice({ commit }, device) { commit('TOGGLE_DEVICE', device); + }, + handleCourse({ commit }, course) { + commit('HANDLECOURSE', course); + }, + handleCourseDetail({ commit }, courseDetail) { + commit('HANDLECOURSEDETAIL', courseDetail); + }, + handleRuleList({ commit }, ruleList) { + commit('HANDLERULELIST', ruleList); } }; diff --git a/src/store/modules/exam.js b/src/store/modules/exam.js new file mode 100644 index 000000000..8ec9f2c50 --- /dev/null +++ b/src/store/modules/exam.js @@ -0,0 +1,100 @@ +import store from '@/store'; + +const exam = { + namespaced: true, + + state: { + started: false, //考试状态 + usedTime: 0, //考试当前所用时间 + totalTime: 0, //考试总时间 + timeInterval: null, //计时器 + suspend: false, //暂停 + }, + getters: { + started: (state) => { + return state.started; + }, + usedTime: (state) => { + return state.usedTime; + }, + totalTime: (state) => { + return state.totalTime; + }, + suspend: (state) => { + return state.suspend; + } + }, + mutations: { + setStarted: (state, started) => { + state.started = started; + }, + setSuspend: (state, suspend) => { + state.suspend = suspend; + }, + setUsedTime: (state, usedTime) => { + state.usedTime = usedTime; + }, + setTotalTime: (state, totalTime) => { + state.totalTime = totalTime; + }, + countUsedTime: (state) => { + if (state.timeInterval) { + clearInterval(state.timeInterval); + state.timeInterval = null; + } + state.timeInterval = setInterval(() => { + if (!state.suspend) { + state.usedTime++; + } + }, 1000); + }, + stopCountTime: (state) => { + if (state.timeInterval) { + clearInterval(state.timeInterval); + state.timeInterval = null; + } + } + }, + actions: { + start({ commit }) { + commit('setStarted', true); + commit('setSuspend', false); + commit('setUsedTime', 0); + commit('countUsedTime'); + }, + over({ commit }) { + commit('setStarted', false); + commit('setSuspend', true); + commit('setUsedTime', 0); + commit('stopCountTime'); + }, + isOver() { + let trainingList = store.getters['trainingList/trainingList']; + let doList = trainingList.filter(elem => { + if (elem.usedTime !== undefined) return true; + }); + + if (doList.length >= trainingList.length) { + return true; + } + }, + setSuspend({ commit }, suspend) { + commit('setSuspend', suspend); + }, + countUsedTime({ commit }) { + commit('countUsedTime'); + }, + setUsedTime({ state }, usedTime) { + if (usedTime) { + state.usedTime = usedTime; + } + }, + setTotalTime({ commit }, totalTime) { + if (totalTime) { + commit('setTotalTime', totalTime); + } + } + } +}; + +export default exam; \ No newline at end of file diff --git a/src/store/modules/runplan.js b/src/store/modules/runplan.js new file mode 100644 index 000000000..463484778 --- /dev/null +++ b/src/store/modules/runplan.js @@ -0,0 +1,140 @@ +const runPlan = { + namespaced: true, + + state: { + stations: [], //车站列表 + planData: {}, //运行图原始数据 + editData: {}, //运行图编辑数据 + updateData: {}, //运行图更新数据 + planSizeCount: 0, //运行图canvas 大小变更标识 + planLoadedCount: 0, //运行图数据更新 + planUpdateCount: 0, //运行图更新标识 + select: {}, //选择的对象 + width: 800, //运行图canvas 容器 宽度 + height: 600, //运行图canvas 容器 高度 + + }, + getters: { + stations: (state) => { + return state.stations || []; + }, + convertPlanData: (state) => () => { + let data = { serviceNumberDataList: [] }; + + let serviceNumberList = Object.keys(state.editData).sort((a, b) => { + return state.editData[a].oldIndex - state.editData[b].oldIndex; + }); + + serviceNumberList.forEach(serviceNumber => { + let serviceObj = { + serviceNumber: serviceNumber, + tripNumberDataList: [] + }; + + if (state.editData[serviceNumber].backup) { + serviceObj['backup'] = state.editData[serviceNumber].backup; + } + + let trainMap = state.editData[serviceNumber].trainMap; + let tripNumberList = Object.keys(trainMap).sort((a, b) => { + return trainMap[a].oldIndex - trainMap[b].oldIndex; + }); + + tripNumberList.forEach(tripNumber => { + serviceObj.tripNumberDataList.push(trainMap[tripNumber]); + }); + + data.serviceNumberDataList.push(serviceObj); + }); + + return data; + }, + + }, + mutations: { + setWidth: (state, width) => { + state.width = width; + state.planSizeCount += 1; + }, + setHeight: (state, height) => { + state.height = height; + state.planSizeCount += 1; + }, + setStations: (state, stations) => { + state.stations = stations; + }, + setPlanData: (state, data) => { + state.planData = data; + state.editData = {}; + if (data && data.serviceNumberDataList && data.serviceNumberDataList.length) { + let serviceList = data.serviceNumberDataList; + serviceList.forEach((service, i) => { + let trainList = service.tripNumberDataList; + state.editData[service.serviceNumber] = { oldIndex: i, serviceNumber: service.serviceNumber, backup: service.backup, trainMap: {} }; + trainList.forEach((train, j) => { + state.editData[service.serviceNumber].trainMap[`${train.directionCode}${train.tripNumber}`] = { oldIndex: j, tripNumber: train.tripNumber, directionCode: train.directionCode, reentry: train.reentry, stationTimeList: train.stationTimeList }; + }); + }); + } + state.planLoadedCount++; + }, + setSelected: (state, select) => { + state.select = select; + }, + addserviceNumber: (state, serviceNumber) => { + let serviceObj = state.planData[serviceNumber]; + if (serviceObj) { + let oldIndex = serviceObj.oldIndex; + state.planData[serviceNumber] = { oldIndex, serviceNumber, trainMap: {} }; + state.planLoadedCount++; + } + }, + updateRunPlanData: (state, data) => { + state.updateData = data; + state.planUpdateCount++; + }, + clear: (state) => { + state.stations = []; + state.planData = {}; + state.editData = {}; + state.select = {}; + } + }, + actions: { + /** 设置运行图大小*/ + resize({ commit }, opt) { + if (opt.width) { + commit('setWidth', opt.width); + } + if (opt.height) { + commit('setHeight', opt.height); + } + }, + /** 设置stations数据 */ + setStations: ({ commit }, mapModel) => { + commit('setStations', mapModel); + }, + /** 设置运行图数据 */ + setPlanData: ({ commit }, data) => { + commit('setPlanData', data); + }, + /** 增加服务号*/ + addserviceNumber: ({ commit }, serviceNumber) => { + commit('addserviceNumber', serviceNumber); + }, + /** 选择车次*/ + setSelected: ({ commit }, select) => { + commit('setSelected', select); + }, + /** 更新数据*/ + updateRunPlanData: ({ commit }, data) => { + commit('updateRunPlanData', data); + }, + /** 清空数据*/ + clear: ({ commit }) => { + commit('clear'); + } + } +}; + +export default runPlan; diff --git a/src/store/modules/socket.js b/src/store/modules/socket.js new file mode 100644 index 000000000..d0f7281b2 --- /dev/null +++ b/src/store/modules/socket.js @@ -0,0 +1,390 @@ + +import store from '@/store'; + +function handle(state, data) { + // console.log(data, 'socket订阅'); + let msg = data.body; + switch (data.type) { + case 'Order_Pay_Result': { // 订单支付结果消息 + state.payOrder = msg; + } break; + case 'JointTraining_Room': { // 综合演练室-房间消息 + state.jointRoomInfo = msg; // 房间信息 + } break; + case 'JointTraining_User': { // 综合演练室-用户消息 + handleUserinfo(state, msg); // 分配角色权限消息 + } break; + case 'JointTraining_Chat': { // 综合演练室-群聊消息 + handleRecordList(state, msg); + } break; + case 'Simulation_DeviceStatus': { // 仿真-设备状态消息 + state.equipmentStatus = msg; + } break; + case 'Simulation_RunFact': { // 仿真-列车实际到发车站消息 + state.trainStationList = msg; + } break; + case 'Simulation_Error': { // 仿真-异常消息 + state.simulationError = msg; + } break; + case 'Simulation_RunAsPlan_Start': { // 仿真-仿真开始按计划行车消息 + state.simulationStart = msg; + } break; + case 'Simulation_Reset': { // 仿真-仿真重置消息 退出计划行车 + state.simulationReset = msg; + } break; + case 'Simulation_Conversation': { // 仿真-用户交互消息(聊天/命令) + handleSimulationInfo(state, msg); + } break; + case 'Simulation_PlayBack_Conversation': {// 回放-用户交互消息 + handleSimulationInfo(state, msg); + } break; + case 'Simulation_ApplyHandle': { // 请求处理消息 + handlePushMsgQueue(state, msg); + } break; + case 'JointTraining_User_Permit': { // 综合演练室-用户获取权限消息 + state.userPermit = msg; + } break; + case 'JointTraining_User_RoomKickOut': { // 综合演练室-用户被踢出房间消息 + state.userRoomKickOut = msg; // 没有给被踢用户发送 + handleMessageInfo(state, 'userRoomKickOut', msg); + } break; + case 'JointTraining_User_InRoom': { // 综合演练室-用户从外部进入房间消息 + state.userInRoom = msg; + handleMessageInfo(state, 'userInRoom', msg); + } break; + case 'JointTraining_User_OutRoom': { // 综合演练室-用户退出房间消息 + state.userOutRoom = msg; + handleMessageInfo(state, 'userOutRoom', msg); + } break; + case 'JointTraining_User_InSimulation': { // 综合演练室-用户进入仿真消息 + state.userInSimulation = msg; + handleMessageInfoSimulation(state, 'userInSimulation', msg); + } break; + case 'JointTraining_User_BackRoom': { // 综合演练室-用户从仿真返回房间消息 + state.userBackRoom = msg; + handleMessageInfoSimulation(state, 'userBackRoom', msg); + } break; + case 'JointTraining_Room_Invite': { // 综合演练室-房间邀请消息 + state.roomInvite = msg; + } break; + case 'Big_Screen_Simulation_DeviceStatus': { //大屏仿真状态数据 + state.equipmentStatus = msg; + } break; + case 'Big_Screen_Simulation_RunFact': { //大屏仿真运行图状态 + state.trainStationList = msg; + } break; + case 'Plan_Simulation_DeviceStatus': { //大屏仿真状态数据 + state.equipmentStatus = msg; + } break; + case 'Plan_Simulation_RunFact': { //大屏仿真运行图状态 + state.trainStationList = msg; + } break; + case 'Simulation_Permission_Over': { //权限结束 + state.permissionOver = msg; + } break; + case 'Simulation_Quest_Finish': { //任务结束标志 + state.tipOperateCount++; + } + } +} +// 操作聊天信息 +function handleRecordList(state, data) { + let userId = store.state.user.id; + let isSelf = false; + if (data.member.id == userId) { + isSelf = true; + } + let myDate = new Date(); + let myDate1 = myDate.toLocaleDateString().replace(/\//g, "-"); + let param = { + join: false, + value: data.message, + self: isSelf, + voice: data.isAudio, + src: data.isAudio ? `${process.env.VOICE_API}/jlcloud/audio/${data.audioPath}` : '', + other: !isSelf, + userName: data.member.nickName, + chatTime: data.chatTime, + chatInfo: true, + inSimulation: false, + inRoom: true, + date: +new Date(`${myDate1} ${data.chatTime}`), + }; + state.chatContentList = param; +} +// 仿真内部聊天 +function handleSimulationInfo(state, data) { + let userId = store.state.user.id; + let isSelf = false; + if (data.member.userId == userId) { + isSelf = true; + } + let role = ''; + switch (data.member.role) { + case '01': { + role = '管理员'; + } break; + case '02': { + role = '教员'; + } break; + case '03': { + role = `调度员`; + } break; + case '04': { + store.state.map.map.stationList.forEach(nor => { + if (nor.code == data.member.deviceCode) { + role = `${nor.name}`; + } + }); + } break; + } + if (!data.member.userId && data.member.deviceName) { + data.member.nickName = data.member.deviceName; + } + if (!data.targetMember.userId && data.targetMember.deviceName) { + data.targetMember.nickName = data.targetMember.deviceName; + } + let myDate = new Date(); + let myDate1 = myDate.toLocaleDateString().replace(/\//g, "-"); + let chatTime = /\d{2}:\d{2}:\d{2}/.exec(data.chatTime)[0] || data.chatTime; + let param = { + join: false, + value: data.message, + self: isSelf, + voice: data.isAudio, + src: data.isAudio ? `${process.env.VOICE_API}/jlcloud/audio/${data.audioPath}` : '', + other: !isSelf, + userName: '', + id: data.member.id, + chatTime: data.chatTime, + date: +new Date(`${myDate1} ${chatTime}`), + conversationId: data.conversationId, + changeVO: data.changeVO || {}, + targetMember: data.targetMember, + member: data.member, + group: data.group, + }; + if (data.member.role) { + param.userName = `${role}【${data.member.nickName}】`; + } else { + param.userName = `${data.member.nickName}`; + } + state.simulationText = param; +} +function handleUserinfo(state, data) { + if (data.length) { // 分配角色信息 + state.roleInfo = data; + } +} +function handleMessageInfo(state, type, data) { + let message = { + join: true, + id: data.id, + userName: data.nickName, + userRole: data.userRole, + name: data.name, + roomTip: '', + chatInfo: true, + inSimulation: data.inSimulation, + inRoom: data.inRoom, + session: 'session', + type: type, + oneself: data.id === store.state.user.id, + }; + switch (type) { + case 'userInRoom': { + message.roomTip = `${data.nickName}进入房间`; + } break; + case 'userRoomKickOut': { + message.roomTip = `${data.nickName}被踢出房间`; + } break; + case 'userOutRoom': { + message.roomTip = `${data.nickName}退出房间`; + } break; + } + state.chatContentList = message; +} +function handleMessageInfoSimulation(state, type, data) { + let message = { + join: true, + id: data.id, + userName: data.nickName, + userRole: data.userRole, + name: data.name, + simulationTip: '', + chatInfo: true, + inSimulation: data.inSimulation, + inRoom: data.inRoom, + session: 'session', + }; + switch (type) { + case 'userInSimulation': { + message.simulationTip = `${data.nickName}进入仿真`; + } break; + case 'userBackRoom': { + message.simulationTip = `${data.nickName}退出仿真`; + } break; + } + state.chatContentSimuList = message; +} +function handlePushMsgQueue(state, msg) { + if (msg instanceof Array) { + state.msgQueue.concat(msg); + } else { + state.msgQueue.push(msg); + } + + if (!state.msgHead) { + state.msgHead = state.msgQueue[0]; + } +} + +const socket = { + namespaced: true, + + state: { + payOrder: {}, // 支付消息 + jointRoomInfo: {}, // 受邀请房间信息 + chatContentList: {}, // 聊天室聊天内容 + roleInfo: [], // 设置角色信息 + jointRoomPrepare: false, // 演练房间准备状态 + equipmentStatus: [], // 仿真-设备状态消息 + trainStationList: [], // 仿真-列车实际到发车站消息 + simulationError: '', // 仿真-异常消息 + simulationStart: '', // 仿真-开始消息 + simulationReset: '', // 仿真-异常消息 + simulationText: {}, // 仿真-用户交互消息(聊天/命令) + message: {}, // 仿真聊天 + msgQueue: [], //命令请求列表 + msgHead: null, //消息头 + + userPermit: {}, // 用户获取权限消息 + userRoomKickOut: {}, // 用户被踢出房间消息 + userInRoom: {}, // 用户从外部进入房间消息 + userOutRoom: {}, // 用户退出房间消息 + userInSimulation: {}, // 用户进入仿真消息 + userBackRoom: {}, // 用户从仿真返回房间消息 + roomInvite: {}, // 用户扫码信息 + chatContentSimuList: {}, // 进入仿真,离开仿真信息 + + permissionOver: {}, // 权限结束 + + tipOperateCount: 0 // 任务结束提示消息 + }, + + getters: { + }, + mutations: { + setJointRoomInfo: (state, jointRoomInfo) => { + state.jointRoomInfo = jointRoomInfo; + }, + + setChatContentList: (state, chatContentList) => { + state.chatContentList = chatContentList; + }, + + setEquipmentStatus: (state, equipmentStatus) => { + state.equipmentStatus = equipmentStatus; + }, + + setTrainStationList: (state, trainStationList) => { + state.trainStationList = trainStationList; + }, + + setSimulationError: (state, simulationError) => { + state.simulationError = simulationError; + }, + + setSimulationStart: (state, simulationStart) => { + state.simulationStart = simulationStart; + }, + + setSimulationReset: (state, simulationReset) => { + state.simulationReset = simulationReset; + }, + + setSimulationTextList: (state, simulationText) => { + state.simulationText = simulationText; + }, + + setPayOrder: (state, payOrder) => { + state.payOrder = payOrder; + }, + + shiftMsgQueue: (state) => { + state.msgHead = null; + state.msgQueue.shift(); + state.msgHead = state.msgQueue[0]; + }, + pushMsgQueue: (state, msg) => { + handlePushMsgQueue(state, msg); + }, + + setRoomInvite: (state, roomInvite) => { + state.roomInvite = roomInvite; + } + }, + + actions: { + setStomp: ({ state }, res) => { + handle(state, res); + }, + + setChatContentList: ({ commit }, chatContentList) => { + commit('setChatContentList', chatContentList); + }, + + setJointRoomInfo: ({ commit }) => { + commit('setJointRoomInfo', {}); + }, + + setEquipmentStatus: ({ commit }) => { + commit('setEquipmentStatus', []); + }, + + setTrainStationList: ({ commit }) => { + commit('setTrainStationList', []); + }, + + setSimulationError: ({ commit }) => { + commit('setSimulationError', ''); + }, + + setSimulationStart: ({ commit }) => { + commit('setSimulationStart', ''); + }, + + setSimulationReset: ({ commit }) => { + commit('setSimulationReset', ''); + }, + + setSimulationTextList: ({ commit }) => { + commit('setSimulationTextList', {}); + }, + + setRoomInvite: ({ commit }) => { + commit('setRoomInvite', {}); + }, + + setMessage: ({ state }, data) => { + if (!state.message[data.key]) { + state.message[data.key] = []; + } + state.message[data.key].push(data.value); + }, + + setPayOrder: ({ commit }) => { + commit('setPayOrder', {}); + }, + + shiftMsgQueue: ({ commit }) => { + commit('shiftMsgQueue'); + }, + + pushMsgQueue: ({ commit }, msg) => { + commit('pushMsgQueue', msg); + } + }, +}; + +export default socket; diff --git a/src/store/modules/trainingList.js b/src/store/modules/trainingList.js new file mode 100644 index 000000000..743f19d5a --- /dev/null +++ b/src/store/modules/trainingList.js @@ -0,0 +1,66 @@ +const trainingList = { + namespaced: true, + + state: { + index: 0, //当前选择实训索引 + trainingList: [], //实训初始列表 + }, + getters: { + trainingList: (state) => { + return state.trainingList; + }, + prevTraining: (state) => () => { + if (state.trainingList && state.trainingList.length > 0) { + state.index = state.index - 1; + if (state.index >= 0) { + return state.trainingList[state.index]; + } + } + }, + nextTraining: (state) => () => { + if (state.trainingList && state.trainingList.length > 0) { + state.index = state.index + 1; + if (state.index < state.trainingList.length) { + return state.trainingList[state.index]; + } + } + } + }, + mutations: { + setIndex: (state, index) => { + state.index = index; + }, + setTrainingList: (state, trainingList) => { + if (trainingList && trainingList.length) { + state.trainingList = trainingList; + } else { + state.trainingList = []; + } + }, + }, + actions: { + setIndex({ commit }, index) { + commit('setIndex', index); + }, + setIndexByTrainingObj({ commit, state }, trainingId) { + let index = 0; + if (state.trainingList && state.trainingList.length) { + state.trainingList.forEach((elem, i) => { + if (elem.id === trainingId) { + index = i; + } + }); + } + commit('setIndex', index); + }, + setTrainingList({ commit }, trainingList) { + commit('setTrainingList', trainingList); + }, + clearTrainingList({ commit }) { + commit('setTrainingList', []); + commit('setIndex', 0); + } + } +}; + +export default trainingList; \ No newline at end of file diff --git a/src/store/modules/user.js b/src/store/modules/user.js index cef0155a9..9cac7c9be 100644 --- a/src/store/modules/user.js +++ b/src/store/modules/user.js @@ -1,8 +1,7 @@ import localStore from 'storejs'; import { login, logout, getInfo } from '@/api/login'; import { getToken, setToken, removeToken, removeScreenToken, setScreenToken, getScreenToken, setPlanToken, getPlanToken, removePlanToken, handleToken, handleRemoveToken } from '@/utils/auth'; -import { getUserConfigInfo } from '@/api/user'; -// import { creatSubscribe, perpetualTopic } from '@/utils/stomp'; +import { getUserConfigInfo } from '@/api/management/user'; import { LoginParams } from '@/utils/login'; const user = { diff --git a/src/utils/Blob.js b/src/utils/Blob.js new file mode 100644 index 000000000..26382ccd2 --- /dev/null +++ b/src/utils/Blob.js @@ -0,0 +1,179 @@ +/* eslint-disable */ +/* Blob.js + * A Blob implementation. + * 2014-05-27 + * + * By Eli Grey, http://eligrey.com + * By Devin Samarin, https://github.com/eboyjr + * License: X11/MIT + * See LICENSE.md + */ + +/*global self, unescape */ +/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true, + plusplus: true */ + +/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */ + +(function (view) { + "use strict"; + + view.URL = view.URL || view.webkitURL; + + if (view.Blob && view.URL) { + try { + new Blob; + return; + } catch (e) {} + } + + // Internally we use a BlobBuilder implementation to base Blob off of + // in order to support older browsers that only have BlobBuilder + var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) { + var + get_class = function(object) { + return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1]; + } + , FakeBlobBuilder = function BlobBuilder() { + this.data = []; + } + , FakeBlob = function Blob(data, type, encoding) { + this.data = data; + this.size = data.length; + this.type = type; + this.encoding = encoding; + } + , FBB_proto = FakeBlobBuilder.prototype + , FB_proto = FakeBlob.prototype + , FileReaderSync = view.FileReaderSync + , FileException = function(type) { + this.code = this[this.name = type]; + } + , file_ex_codes = ( + "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR " + + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR" + ).split(" ") + , file_ex_code = file_ex_codes.length + , real_URL = view.URL || view.webkitURL || view + , real_create_object_URL = real_URL.createObjectURL + , real_revoke_object_URL = real_URL.revokeObjectURL + , URL = real_URL + , btoa = view.btoa + , atob = view.atob + + , ArrayBuffer = view.ArrayBuffer + , Uint8Array = view.Uint8Array + ; + FakeBlob.fake = FB_proto.fake = true; + while (file_ex_code--) { + FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1; + } + if (!real_URL.createObjectURL) { + URL = view.URL = {}; + } + URL.createObjectURL = function(blob) { + var + type = blob.type + , data_URI_header + ; + if (type === null) { + type = "application/octet-stream"; + } + if (blob instanceof FakeBlob) { + data_URI_header = "data:" + type; + if (blob.encoding === "base64") { + return data_URI_header + ";base64," + blob.data; + } else if (blob.encoding === "URI") { + return data_URI_header + "," + decodeURIComponent(blob.data); + } if (btoa) { + return data_URI_header + ";base64," + btoa(blob.data); + } else { + return data_URI_header + "," + encodeURIComponent(blob.data); + } + } else if (real_create_object_URL) { + return real_create_object_URL.call(real_URL, blob); + } + }; + URL.revokeObjectURL = function(object_URL) { + if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) { + real_revoke_object_URL.call(real_URL, object_URL); + } + }; + FBB_proto.append = function(data/*, endings*/) { + var bb = this.data; + // decode data to a binary string + if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) { + var + str = "" + , buf = new Uint8Array(data) + , i = 0 + , buf_len = buf.length + ; + for (; i < buf_len; i++) { + str += String.fromCharCode(buf[i]); + } + bb.push(str); + } else if (get_class(data) === "Blob" || get_class(data) === "File") { + if (FileReaderSync) { + var fr = new FileReaderSync; + bb.push(fr.readAsBinaryString(data)); + } else { + // async FileReader won't work as BlobBuilder is sync + throw new FileException("NOT_READABLE_ERR"); + } + } else if (data instanceof FakeBlob) { + if (data.encoding === "base64" && atob) { + bb.push(atob(data.data)); + } else if (data.encoding === "URI") { + bb.push(decodeURIComponent(data.data)); + } else if (data.encoding === "raw") { + bb.push(data.data); + } + } else { + if (typeof data !== "string") { + data += ""; // convert unsupported types to strings + } + // decode UTF-16 to binary string + bb.push(unescape(encodeURIComponent(data))); + } + }; + FBB_proto.getBlob = function(type) { + if (!arguments.length) { + type = null; + } + return new FakeBlob(this.data.join(""), type, "raw"); + }; + FBB_proto.toString = function() { + return "[object BlobBuilder]"; + }; + FB_proto.slice = function(start, end, type) { + var args = arguments.length; + if (args < 3) { + type = null; + } + return new FakeBlob( + this.data.slice(start, args > 1 ? end : this.data.length) + , type + , this.encoding + ); + }; + FB_proto.toString = function() { + return "[object Blob]"; + }; + FB_proto.close = function() { + this.size = this.data.length = 0; + }; + return FakeBlobBuilder; + }(view)); + + view.Blob = function Blob(blobParts, options) { + var type = options ? (options.type || "") : ""; + var builder = new BlobBuilder(); + if (blobParts) { + for (var i = 0, len = blobParts.length; i < len; i++) { + builder.append(blobParts[i]); + } + } + return builder.getBlob(type); + }; +}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this)); diff --git a/src/utils/HZRecorder.js b/src/utils/HZRecorder.js new file mode 100644 index 000000000..ff19ac2a4 --- /dev/null +++ b/src/utils/HZRecorder.js @@ -0,0 +1,204 @@ + +//兼容 +window.URL = window.URL || window.webkitURL; +navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; + +export function init(stream, config) { + config = config || {}; + config.sampleBits = config.sampleBits || 16; //采样数位 8, 16 + config.sampleRate = config.sampleRate || (16000); //采样率(1/6 44100) + + var context = new (window.webkitAudioContext || window.AudioContext)(); + var audioInput = context.createMediaStreamSource(stream); + var createScript = context.createScriptProcessor || context.createJavaScriptNode; + var recorder = createScript.apply(context, [4096, 1, 1]); + + var audioData = { + size: 0 //录音文件长度 + , buffer: [] //录音缓存 + , inputSampleRate: context.sampleRate //输入采样率 + , inputSampleBits: 16 //输入采样数位 8, 16 + , outputSampleRate: config.sampleRate //输出采样率 + , oututSampleBits: config.sampleBits //输出采样数位 8, 16 + , input: function (data) { + this.buffer.push(new Float32Array(data)); + this.size += data.length; + } + , compress: function () { //合并压缩 + //合并 + var data = new Float32Array(this.size); + var offset = 0; + for (var i = 0; i < this.buffer.length; i++) { + data.set(this.buffer[i], offset); + offset += this.buffer[i].length; + } + //压缩 + var compression = parseInt(this.inputSampleRate / this.outputSampleRate); + var length = data.length / compression; + var result = new Float32Array(length); + var index = 0, j = 0; + while (index < length) { + result[index] = data[j]; + j += compression; + index++; + } + return result; + } + , encodeWAV: function () { + var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate); + var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits); + var bytes = this.compress(); + var dataLength = bytes.length * (sampleBits / 8); + var buffer = new ArrayBuffer(44 + dataLength); + var data = new DataView(buffer); + + var channelCount = 1;//单声道 + var offset = 0; + + var writeString = function (str) { + for (var i = 0; i < str.length; i++) { + data.setUint8(offset + i, str.charCodeAt(i)); + } + }; + + // 资源交换文件标识符 + writeString('RIFF'); offset += 4; + // 下个地址开始到文件尾总字节数,即文件大小-8 + data.setUint32(offset, 36 + dataLength, true); offset += 4; + // WAV文件标志 + writeString('WAVE'); offset += 4; + // 波形格式标志 + writeString('fmt '); offset += 4; + // 过滤字节,一般为 0x10 = 16 + data.setUint32(offset, 16, true); offset += 4; + // 格式类别 (PCM形式采样数据) + data.setUint16(offset, 1, true); offset += 2; + // 通道数 + data.setUint16(offset, channelCount, true); offset += 2; + // 采样率,每秒样本数,表示每个通道的播放速度 + data.setUint32(offset, sampleRate, true); offset += 4; + // 波形数据传输率 (每秒平均字节数) 单声道×每秒数据位数×每样本数据位/8 + data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset += 4; + // 快数据调整数 采样一次占用字节数 单声道×每样本的数据位数/8 + data.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2; + // 每样本数据位数 + data.setUint16(offset, sampleBits, true); offset += 2; + // 数据标识符 + writeString('data'); offset += 4; + // 采样数据总数,即数据总大小-44 + data.setUint32(offset, dataLength, true); offset += 4; + // 写入采样数据 + if (sampleBits === 8) { + for (let i = 0; i < bytes.length; i++ , offset++) { + let s = Math.max(-1, Math.min(1, bytes[i])); + let val = s < 0 ? s * 0x8000 : s * 0x7FFF; + val = parseInt(255 / (65535 / (val + 32768))); + data.setInt8(offset, val, true); + } + } else { + for (let i = 0; i < bytes.length; i++ , offset += 2) { + let s = Math.max(-1, Math.min(1, bytes[i])); + data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true); + } + } + + return new Blob([data], { type: 'audio/wav' }); + } + }; + + //开始录音 + this.start = function () { + audioInput.connect(recorder); + recorder.connect(context.destination); + }; + + //停止 + this.stop = function () { + recorder.disconnect(); + }; + + //获取音频文件 + this.getBlob = function () { + this.stop(); + return audioData.encodeWAV(); + }; + + //回放 + this.play = function (audio) { + audio.src = window.URL.createObjectURL(this.getBlob()); + }; + + //上传 + this.upload = function (url, callback) { + var fd = new FormData(); + fd.append("audioData", this.getBlob()); + var xhr = new XMLHttpRequest(); + if (callback) { + xhr.upload.addEventListener("progress", function (e) { + callback('uploading', e); + }, false); + xhr.addEventListener("load", function (e) { + callback('ok', e); + }, false); + xhr.addEventListener("error", function (e) { + callback('error', e); + }, false); + xhr.addEventListener("abort", function (e) { + callback('cancel', e); + }, false); + } + xhr.open("POST", url); + xhr.send(fd); + }; + + //音频采集 + recorder.onaudioprocess = function (e) { + audioData.input(e.inputBuffer.getChannelData(0)); + //record(e.inputBuffer.getChannelData(0)); + }; + +} +//抛出异常 +init.throwError = function (message, callback) { + alert(message); + callback('1001'); + // throw new function () { this.toString = function () { return message; }; }; +}; +//是否支持录音 +init.canRecording = (navigator.getUserMedia != null); +//获取录音机 +init.get = function (callback, config) { + if (callback) { + if (navigator.getUserMedia) { + navigator.getUserMedia( + { audio: true } //只启用音频 + , function (stream) { + var rec = new init(stream, config); + callback(rec); + } + , function (error) { + switch (error.code || error.name) { + case 'PERMISSION_DENIED': + case 'PermissionDeniedError': + init.throwError('用户拒绝提供信息。'); + break; + case 'NOT_SUPPORTED_ERROR': + case 'NotSupportedError': + init.throwError('浏览器不支持硬件设备。', callback); + break; + case 'MANDATORY_UNSATISFIED_ERROR': + case 'MandatoryUnsatisfiedError': + init.throwError('无法发现指定的硬件设备。', callback); + break; + default: + init.throwError('无法打开麦克风。', callback); + break; + } + }); + } else { + init.throwErr('当前浏览器不支持录音功能。'); return; + } + } +}; + +export default { init }; \ No newline at end of file diff --git a/src/utils/animation.js b/src/utils/animation.js new file mode 100644 index 000000000..ee2d10b11 --- /dev/null +++ b/src/utils/animation.js @@ -0,0 +1,13 @@ +export const requestAnimationFrame = window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { callback.timter = window.setTimeout(callback, 1000 / 60); }; + +export const cancelRequestAnimFrame = window.cancelAnimationFrame || + window.webkitCancelRequestAnimationFrame || + window.mozCancelRequestAnimationFrame || + window.oCancelRequestAnimationFrame || + window.msCancelRequestAnimationFrame || + function (callback) { window.clearTimeout(callback); }; diff --git a/src/utils/chatFomat.js b/src/utils/chatFomat.js new file mode 100644 index 000000000..8deb5c114 --- /dev/null +++ b/src/utils/chatFomat.js @@ -0,0 +1,33 @@ +var ChatFomat = { + roleConfig: { + 'Admin': '管理员', + 'Instructor': '教员', + 'Dispatcher': '行调', + 'Attendant': '车站', + 'Audience': '观众', + 'Driver': '列车' + }, + formatTime(time) { + return /\d{2}:\d{2}:\d{2}/.exec(time)[0] || time; + }, + formatName(member) { + let name = `${member.nickName}`; + if (member.id) { + name = `【${this.roleConfig[member.role] || ''}】${name}`; + } + return name; + }, + formatSay(nor) { + let who = ''; + if (nor.group) { + if (nor.targetMember) { + who = `@${nor.targetMember.nickName}`; + } else { + who = '@ALL'; + } + } + return `${who} ${nor.value}`; + } +}; + +export default ChatFomat; diff --git a/src/utils/date.js b/src/utils/date.js new file mode 100644 index 000000000..dc52ba841 --- /dev/null +++ b/src/utils/date.js @@ -0,0 +1,40 @@ +export function now() { + var d = new Date(); + var year = d.getFullYear(); + var month = change(d.getMonth() + 1); + var day = change(d.getDate()); + var hour = change(d.getHours()); + var minute = change(d.getMinutes()); + var second = change(d.getSeconds()); + function change(t) { + if (t < 10) { + return "0" + t; + } else { + return t; + } + } + return year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second; +} + +export function timeFormat(usedTime) { + let hour = 0, minute = 0, second = 0, sumTime = usedTime; + if (sumTime) { + if (sumTime >= 3600) { + hour = Math.floor(sumTime / 3600) % 24; + sumTime = (sumTime % 3600); + } + if (sumTime >= 60) { + minute = Math.floor(sumTime / 60); + sumTime = sumTime % 60; + } + second = Math.floor(sumTime); + } + function getTimeStr(val) { + return val < 10 ? '0' + val : val; + } + return getTimeStr(hour) + ':' + getTimeStr(minute) + ':' + getTimeStr(second); +} + +export function prefixIntrger(num, length) { + return (Array(length).join('0') + num).slice(-length); +} \ No newline at end of file diff --git a/src/utils/loaddata.js b/src/utils/loaddata.js new file mode 100644 index 000000000..2de62589a --- /dev/null +++ b/src/utils/loaddata.js @@ -0,0 +1,36 @@ +import store from '@/store'; +import { getPublishMapVersion, getPublishMapDetail } from '@/api/jmap/map'; + +// 获取地图版本数据,和store里面的map版本做比较,如果不同 +// 获取发布的地图数据 +// 先设置地图数据 +// 清除列车数据 +export function loadMapData(skinStyle) { + return new Promise((resolve, reject) => { + getPublishMapVersion(skinStyle).then(resp => { + let version = resp.data; + let mapData = store.state.map.mapData[`mapDate_${skinStyle}`]; + if (mapData && mapData.version == version) { + store.dispatch('map/setMapData', mapData).then(() => { + store.dispatch('map/clearJlmapTrainView').then(() => { + resolve(); + }); + }); + } else { + getPublishMapDetail(skinStyle).then(res => { + mapData = res.data; + store.dispatch('map/setMapDataList', mapData); // 缓存地图 + store.dispatch('map/setMapData', mapData).then(() => { + store.dispatch('map/clearJlmapTrainView').then(() => { + resolve(); + }); + }); + }).catch(error => { + reject(error); + }); + } + }).catch(error => { + reject(error); + }); + }); +} diff --git a/src/utils/screen.js b/src/utils/screen.js new file mode 100644 index 000000000..30254f094 --- /dev/null +++ b/src/utils/screen.js @@ -0,0 +1,52 @@ +//判断全屏模式是否是可用 +export function isFullscreenEnabled() { + return document.fullscreenEnabled || + document.mozFullScreenEnabled || + document.webkitFullscreenEnabled || + document.msFullscreenEnabled || false; +} + +//判断浏览器是否全屏 +export function isFullscreen() { + return document.fullscreenElement || + document.msFullscreenElement || + document.mozFullScreenElement || + document.webkitFullscreenElement || false; +} + +//启动全屏 +export function launchFullscreen() { + if (isFullscreenEnabled()) { + let element = document.documentElement; + if (element.requestFullScreen) { + element.requestFullScreen(); + } else if (element.mozRequestFullScreen) { + element.mozRequestFullScreen(); + } else if (element.msRequestFullscreen) { + element.msRequestFullscreen(); + } else if (element.oRequestFullscreen) { + element.oRequestFullscreen(); + } else if (element.webkitRequestFullScreen) { + element.webkitRequestFullScreen(); + } + } +} + +//退出全屏 +export function exitFullscreen() { + if (isFullscreen()) { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if (document.mozExitFullScreen) { + document.mozExitFullScreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } + } +} + + diff --git a/src/views/components/filterCity/index.vue b/src/views/components/filterCity/index.vue new file mode 100644 index 000000000..0cf1381f7 --- /dev/null +++ b/src/views/components/filterCity/index.vue @@ -0,0 +1,221 @@ + + + + + \ No newline at end of file diff --git a/src/views/components/limits/distribute.vue b/src/views/components/limits/distribute.vue new file mode 100644 index 000000000..5db734b4a --- /dev/null +++ b/src/views/components/limits/distribute.vue @@ -0,0 +1,203 @@ + + + \ No newline at end of file diff --git a/src/views/components/limits/index.vue b/src/views/components/limits/index.vue new file mode 100644 index 000000000..c63486dde --- /dev/null +++ b/src/views/components/limits/index.vue @@ -0,0 +1,140 @@ + + + \ No newline at end of file diff --git a/src/views/components/limits/transfer.vue b/src/views/components/limits/transfer.vue new file mode 100644 index 000000000..ac1448a9e --- /dev/null +++ b/src/views/components/limits/transfer.vue @@ -0,0 +1,209 @@ + + + \ No newline at end of file diff --git a/src/views/components/pay/commit.vue b/src/views/components/pay/commit.vue new file mode 100644 index 000000000..c8056a0cd --- /dev/null +++ b/src/views/components/pay/commit.vue @@ -0,0 +1,192 @@ + + + \ No newline at end of file diff --git a/src/views/components/pay/confirm.vue b/src/views/components/pay/confirm.vue new file mode 100644 index 000000000..10911a614 --- /dev/null +++ b/src/views/components/pay/confirm.vue @@ -0,0 +1,295 @@ + + + + \ No newline at end of file diff --git a/src/views/components/pay/finish.vue b/src/views/components/pay/finish.vue new file mode 100644 index 000000000..4f77df7ef --- /dev/null +++ b/src/views/components/pay/finish.vue @@ -0,0 +1,78 @@ + + + \ No newline at end of file diff --git a/src/views/components/pay/index.vue b/src/views/components/pay/index.vue new file mode 100644 index 000000000..7f0a6d01b --- /dev/null +++ b/src/views/components/pay/index.vue @@ -0,0 +1,125 @@ + + + \ No newline at end of file diff --git a/src/views/components/quickMenu/index.vue b/src/views/components/quickMenu/index.vue new file mode 100644 index 000000000..eeb6a895e --- /dev/null +++ b/src/views/components/quickMenu/index.vue @@ -0,0 +1,448 @@ + + + \ No newline at end of file diff --git a/src/views/components/systemTime/digit.vue b/src/views/components/systemTime/digit.vue new file mode 100644 index 000000000..8442e7c83 --- /dev/null +++ b/src/views/components/systemTime/digit.vue @@ -0,0 +1,112 @@ + + + + + \ No newline at end of file diff --git a/src/views/components/systemTime/index.vue b/src/views/components/systemTime/index.vue new file mode 100644 index 000000000..0a0cef9d9 --- /dev/null +++ b/src/views/components/systemTime/index.vue @@ -0,0 +1,80 @@ + + + + + \ No newline at end of file diff --git a/src/views/components/systemTime/point.vue b/src/views/components/systemTime/point.vue new file mode 100644 index 000000000..888730e2e --- /dev/null +++ b/src/views/components/systemTime/point.vue @@ -0,0 +1,43 @@ + + + + + \ No newline at end of file diff --git a/src/views/components/systemTime/separator.vue b/src/views/components/systemTime/separator.vue new file mode 100644 index 000000000..710c219fd --- /dev/null +++ b/src/views/components/systemTime/separator.vue @@ -0,0 +1,51 @@ + + + + + \ No newline at end of file diff --git a/src/views/demonstration/deomonList/index.vue b/src/views/demonstration/deomonList/index.vue new file mode 100644 index 000000000..4dc89cf18 --- /dev/null +++ b/src/views/demonstration/deomonList/index.vue @@ -0,0 +1,121 @@ + + + + + \ No newline at end of file diff --git a/src/views/demonstration/deomonTopic/index.vue b/src/views/demonstration/deomonTopic/index.vue new file mode 100644 index 000000000..2c3e4080c --- /dev/null +++ b/src/views/demonstration/deomonTopic/index.vue @@ -0,0 +1,77 @@ + + + + + \ No newline at end of file diff --git a/src/views/demonstration/detail/index.vue b/src/views/demonstration/detail/index.vue new file mode 100644 index 000000000..855a4523d --- /dev/null +++ b/src/views/demonstration/detail/index.vue @@ -0,0 +1,358 @@ + + + + \ No newline at end of file diff --git a/src/views/demonstration/home.vue b/src/views/demonstration/home.vue new file mode 100644 index 000000000..59ddee229 --- /dev/null +++ b/src/views/demonstration/home.vue @@ -0,0 +1,104 @@ + + + + \ No newline at end of file diff --git a/src/views/demonstration/index.vue b/src/views/demonstration/index.vue new file mode 100644 index 000000000..984c12f1a --- /dev/null +++ b/src/views/demonstration/index.vue @@ -0,0 +1,78 @@ + + + + diff --git a/src/views/demonstration/list/demonList.vue b/src/views/demonstration/list/demonList.vue new file mode 100644 index 000000000..088c27cbd --- /dev/null +++ b/src/views/demonstration/list/demonList.vue @@ -0,0 +1,138 @@ + + + + \ No newline at end of file diff --git a/src/views/display/demon/addQuest.vue b/src/views/display/demon/addQuest.vue new file mode 100644 index 000000000..6c93cd7e4 --- /dev/null +++ b/src/views/display/demon/addQuest.vue @@ -0,0 +1,137 @@ + + + \ No newline at end of file diff --git a/src/views/display/demon/chartView.vue b/src/views/display/demon/chartView.vue new file mode 100644 index 000000000..2ccc2e89a --- /dev/null +++ b/src/views/display/demon/chartView.vue @@ -0,0 +1,678 @@ + + + + + \ No newline at end of file diff --git a/src/views/display/demon/chatBox.vue b/src/views/display/demon/chatBox.vue new file mode 100644 index 000000000..92e126600 --- /dev/null +++ b/src/views/display/demon/chatBox.vue @@ -0,0 +1,532 @@ + + + + + \ No newline at end of file diff --git a/src/views/display/demon/faultChoose.vue b/src/views/display/demon/faultChoose.vue new file mode 100644 index 000000000..eab52f2c0 --- /dev/null +++ b/src/views/display/demon/faultChoose.vue @@ -0,0 +1,162 @@ + + + + \ No newline at end of file diff --git a/src/views/display/demon/runPlanLoad.vue b/src/views/display/demon/runPlanLoad.vue new file mode 100644 index 000000000..118c3aff4 --- /dev/null +++ b/src/views/display/demon/runPlanLoad.vue @@ -0,0 +1,67 @@ + + + + \ No newline at end of file diff --git a/src/views/display/demon/runPlanView.vue b/src/views/display/demon/runPlanView.vue new file mode 100644 index 000000000..a4f5ce866 --- /dev/null +++ b/src/views/display/demon/runPlanView.vue @@ -0,0 +1,351 @@ + + + + \ No newline at end of file diff --git a/src/views/display/demon/setTime.vue b/src/views/display/demon/setTime.vue new file mode 100644 index 000000000..334a938ac --- /dev/null +++ b/src/views/display/demon/setTime.vue @@ -0,0 +1,113 @@ + + + \ No newline at end of file diff --git a/src/views/display/index.vue b/src/views/display/index.vue new file mode 100644 index 000000000..88e74915c --- /dev/null +++ b/src/views/display/index.vue @@ -0,0 +1,540 @@ + + + + \ No newline at end of file diff --git a/src/views/display/menuDemon.vue b/src/views/display/menuDemon.vue new file mode 100644 index 000000000..e9bd23739 --- /dev/null +++ b/src/views/display/menuDemon.vue @@ -0,0 +1,250 @@ + + + + + \ No newline at end of file diff --git a/src/views/display/menuExam.vue b/src/views/display/menuExam.vue new file mode 100644 index 000000000..2b85312b3 --- /dev/null +++ b/src/views/display/menuExam.vue @@ -0,0 +1,295 @@ + + + + \ No newline at end of file diff --git a/src/views/display/menuLesson.vue b/src/views/display/menuLesson.vue new file mode 100644 index 000000000..2b0705f7d --- /dev/null +++ b/src/views/display/menuLesson.vue @@ -0,0 +1,274 @@ + + + + \ No newline at end of file diff --git a/src/views/display/menuPlan.vue b/src/views/display/menuPlan.vue new file mode 100644 index 000000000..d37342efc --- /dev/null +++ b/src/views/display/menuPlan.vue @@ -0,0 +1,145 @@ + + + + \ No newline at end of file diff --git a/src/views/display/menuReplay.vue b/src/views/display/menuReplay.vue new file mode 100644 index 000000000..8efd7695b --- /dev/null +++ b/src/views/display/menuReplay.vue @@ -0,0 +1,176 @@ + + + + + \ No newline at end of file diff --git a/src/views/display/menuSchema.vue b/src/views/display/menuSchema.vue new file mode 100644 index 000000000..4876d3757 --- /dev/null +++ b/src/views/display/menuSchema.vue @@ -0,0 +1,155 @@ + + + \ No newline at end of file diff --git a/src/views/display/menuScreen.vue b/src/views/display/menuScreen.vue new file mode 100644 index 000000000..5a7e8df7f --- /dev/null +++ b/src/views/display/menuScreen.vue @@ -0,0 +1,156 @@ + + + + \ No newline at end of file diff --git a/src/views/display/menuScript.vue b/src/views/display/menuScript.vue new file mode 100644 index 000000000..79bf8b641 --- /dev/null +++ b/src/views/display/menuScript.vue @@ -0,0 +1,188 @@ + + + + + \ No newline at end of file diff --git a/src/views/display/menuSystemTime.vue b/src/views/display/menuSystemTime.vue new file mode 100644 index 000000000..c7d0f8e1d --- /dev/null +++ b/src/views/display/menuSystemTime.vue @@ -0,0 +1,78 @@ + + + + \ No newline at end of file diff --git a/src/views/display/scriptRecord/addAction.vue b/src/views/display/scriptRecord/addAction.vue new file mode 100644 index 000000000..ead8c36a1 --- /dev/null +++ b/src/views/display/scriptRecord/addAction.vue @@ -0,0 +1,108 @@ + + + diff --git a/src/views/display/scriptRecord/addBehavior.vue b/src/views/display/scriptRecord/addBehavior.vue new file mode 100644 index 000000000..19a405745 --- /dev/null +++ b/src/views/display/scriptRecord/addBehavior.vue @@ -0,0 +1,200 @@ + + + \ No newline at end of file diff --git a/src/views/display/scriptRecord/getAction.vue b/src/views/display/scriptRecord/getAction.vue new file mode 100644 index 000000000..5d27fc21b --- /dev/null +++ b/src/views/display/scriptRecord/getAction.vue @@ -0,0 +1,110 @@ + + + diff --git a/src/views/display/scriptRecord/getBehavior.vue b/src/views/display/scriptRecord/getBehavior.vue new file mode 100644 index 000000000..ef961f198 --- /dev/null +++ b/src/views/display/scriptRecord/getBehavior.vue @@ -0,0 +1,135 @@ + + + \ No newline at end of file diff --git a/src/views/display/scriptRecord/roleSection.vue b/src/views/display/scriptRecord/roleSection.vue new file mode 100644 index 000000000..42df33b39 --- /dev/null +++ b/src/views/display/scriptRecord/roleSection.vue @@ -0,0 +1,194 @@ + + + diff --git a/src/views/display/scriptRecord/targetCondition.vue b/src/views/display/scriptRecord/targetCondition.vue new file mode 100644 index 000000000..8a4bc5030 --- /dev/null +++ b/src/views/display/scriptRecord/targetCondition.vue @@ -0,0 +1,243 @@ + + + + + \ No newline at end of file diff --git a/src/views/display/tipExamList.vue b/src/views/display/tipExamList.vue new file mode 100644 index 000000000..bb3e50a05 --- /dev/null +++ b/src/views/display/tipExamList.vue @@ -0,0 +1,391 @@ + + + + \ No newline at end of file diff --git a/src/views/display/tipQuestDetail.vue b/src/views/display/tipQuestDetail.vue new file mode 100644 index 000000000..e46d91c86 --- /dev/null +++ b/src/views/display/tipQuestDetail.vue @@ -0,0 +1,204 @@ + + + + \ No newline at end of file diff --git a/src/views/display/tipScriptRecord.vue b/src/views/display/tipScriptRecord.vue new file mode 100644 index 000000000..0c5fd9c2b --- /dev/null +++ b/src/views/display/tipScriptRecord.vue @@ -0,0 +1,310 @@ + + + + \ No newline at end of file diff --git a/src/views/display/tipTrainingDetail.vue b/src/views/display/tipTrainingDetail.vue new file mode 100644 index 000000000..501cb22fc --- /dev/null +++ b/src/views/display/tipTrainingDetail.vue @@ -0,0 +1,268 @@ + + + + \ No newline at end of file diff --git a/src/views/exam/detail/courseDetail.vue b/src/views/exam/detail/courseDetail.vue new file mode 100644 index 000000000..06703ea7b --- /dev/null +++ b/src/views/exam/detail/courseDetail.vue @@ -0,0 +1,171 @@ + + + \ No newline at end of file diff --git a/src/views/exam/detail/examDetail.vue b/src/views/exam/detail/examDetail.vue new file mode 100644 index 000000000..78a1503c3 --- /dev/null +++ b/src/views/exam/detail/examDetail.vue @@ -0,0 +1,294 @@ + + + \ No newline at end of file diff --git a/src/views/exam/detail/questionDetail.vue b/src/views/exam/detail/questionDetail.vue new file mode 100644 index 000000000..18182c736 --- /dev/null +++ b/src/views/exam/detail/questionDetail.vue @@ -0,0 +1,163 @@ + + + \ No newline at end of file diff --git a/src/views/exam/home.vue b/src/views/exam/home.vue new file mode 100644 index 000000000..3109d88f5 --- /dev/null +++ b/src/views/exam/home.vue @@ -0,0 +1,106 @@ + + + + \ No newline at end of file diff --git a/src/views/exam/index.vue b/src/views/exam/index.vue new file mode 100644 index 000000000..a7f9a5ced --- /dev/null +++ b/src/views/exam/index.vue @@ -0,0 +1,108 @@ + + + + \ No newline at end of file diff --git a/src/views/exam/list/examList.vue b/src/views/exam/list/examList.vue new file mode 100644 index 000000000..61cc25e8c --- /dev/null +++ b/src/views/exam/list/examList.vue @@ -0,0 +1,145 @@ + + + + \ No newline at end of file diff --git a/src/views/exam/list/questionList.vue b/src/views/exam/list/questionList.vue new file mode 100644 index 000000000..03c1d2ff9 --- /dev/null +++ b/src/views/exam/list/questionList.vue @@ -0,0 +1,124 @@ + + + \ No newline at end of file diff --git a/src/views/exam/result.vue b/src/views/exam/result.vue new file mode 100644 index 000000000..a1c264ed8 --- /dev/null +++ b/src/views/exam/result.vue @@ -0,0 +1,140 @@ + + + \ No newline at end of file diff --git a/src/views/index.js b/src/views/index.js new file mode 100644 index 000000000..55af6d062 --- /dev/null +++ b/src/views/index.js @@ -0,0 +1,974 @@ +/* eslint-disable no-unused-vars */ +import Vue from 'vue'; +import Router from 'vue-router'; + +// in development-env not use lazy-loading, because lazy-loading too many pages will cause webpack hot update too slow. so only in production use lazy-loading; +// detail: https://panjiachen.github.io/vue-element-admin-site/#/lazy-loading + +Vue.use(Router); + +/* Dashboard */ +import Dashboard from '@/views/dashboard/index.vue'; + +/* Layout */ +import Layout from '@/views/layout/Layout'; +import Login from '@/views/login/index'; +import LoginNew from '@/views/login/loginNew'; +import Page404 from '@/views/404'; + +import LoginScreen from '@/views/login/loginScreen'; +import LoginNewScreen from '@/views/login/loginNewScreen'; + +import LoginPlan from '@/views/login/loginPlan'; +import LoginNewPlan from '@/views/login/loginNewPlan'; + + +//地图绘制 +import MapManage from '@/views/map/mapdraft/index'; +import MapEdit from '@/views/map/mapdraft/mapedit/index'; +import SkinStyle from '@/views/map/skinStyle/index'; +import SkinStyleDraft from '@/views/map/skinStyle/draft'; + +//列车运行表 +import ListPlan from '@/views/map/runplan/index'; +//运行图视图 +import ViewPlan from '@/views/map/runplan/chart'; + +//实训管理和编辑 +import TrainingManage from '@/views/lesson/trainingrecord/index'; +import TrainingEdit from '@/views/lesson/trainingrecord/manage/index'; +//产品管理 +import TrainingCategory from '@/views/lesson/trainingcategory/index'; +import ProductList from '@/views/map/product/list'; +import ProductDraft from '@/views/map/product/draft'; + +//课程管理 +import LessonCategory from '@/views/lesson/lessoncategory/index'; +//自动生成实训 +import TrainingGeneration from '@/views/lesson/trainingmanage/index'; +import TrainingRule from '@/views/lesson/trainingRule/list'; +import TrainingRuleDetail from '@/views/lesson/trainingRule/detail/index'; + +//购买页面 +import PayPage from '@/views/components/pay/index'; + +//教学系统 +import Teach from '@/views/teach/index'; +import TeachHome from '@/views/teach/home'; +import TeachDetail from '@/views/teach/detail/index'; +import TeachPracticalDetail from '@/views/teach/practical/index'; +import TaskManageList from '@/views/lesson/taskmanage/list'; +import ScriptManage from '@/views/lesson/scriptmanage/list'; + +//考试系统 +import Exam from '@/views/exam/index'; +import ExamHome from '@/views/exam/home'; +import ExamDetail from '@/views/exam/detail/examDetail'; +import ExamCourseDetail from '@/views/exam/detail/courseDetail'; +import ExamQuestionDetail from '@/views/exam/detail/questionDetail'; +import ExamResult from '@/views/exam/result'; + +//考试规则 +import ExamRuleList from '@/views/publish/examRule/index'; +import ExamRuleDraft from '@/views/publish/examRule/draft/index'; + +//仿真系统 +import Demonstration from '@/views/demonstration/index'; +import DemonstrationHome from '@/views/demonstration/home'; +import DemonstrationDetail from '@/views/demonstration/detail/index'; + +//大屏系统 +import ScreenMonitor from '@/views/screenMonitor/index'; +import ScreenMonitorHome from '@/views/screenMonitor/home'; +import ScreenMonitorDetail from '@/views/screenMonitor/detail/index'; + +//琏计划系统 +import PlanMonitor from '@/views/planMonitor/index'; +import PlanMonitorHome from '@/views/planMonitor/home'; +import PlanMonitorDetail from '@/views/planMonitor/detail'; +import PlanEditTool from '@/views/planMonitor/editTool/index'; + +//回放管理 +import replay from '@/views/replay/index'; + +//权限管理 +import PermissionList from '@/views/package/index'; +import PermissionDraft from '@/views/package/draft/ruleForm'; + +// 产品状态管理 +import ProductStatus from '@/views/publish/productStatus/index'; +// 发布地图管理 +import PublishMap from '@/views/publish/publishMap/index'; +import PublishMapDraft from '@/views/publish/publishMap/draft'; +// 发布课程管理 +import PublishLesson from '@/views/publish/publishLesson/index'; +// 仿真脚本管理 +import SimulationScript from '@/views/publish/simulationScript/index'; +// 运行图模板管理 +import PlanTemplate from '@/views/publish/runPlanTemplate/index'; +import PlanCommon from '@/views/publish/runPlanCommon/index'; +import PlanCommonDraft from '@/views/publish/runPlanCommon/draft'; +// 运行图每日加载管理 +import PlanEveryDay from '@/views/publish/runPlanEveryDay/index'; + +// 系统字典管理 +import Dictionary from '@/views/management/dictionary/index'; +import DictionaryDetail from '@/views/management/dictionaryDetail/index'; +// 用户考试管理 +import UserExam from '@/views/management/userExam/index'; +// 用户仿真管理 +import UserSimulation from '@/views/management/userSimulation/index'; +// 存在仿真管理 +import ExistingSimulation from '@/views/management/existingSimulation/index'; +// 用户实训管理 +import UserTraining from '@/views/management/userTraining/index'; +// 用户管理 +import UserControl from '@/views/management/userControl/index'; + +// 商品管理 +import commodityList from '@/views/orderauthor/commodity/index'; +import CommodityDraft from '@/views/orderauthor/commodity/draft'; + +//课程权限管理 +import LessonPermission from '@/views/orderauthor/author/index'; +import LessonPermissionDraft from '@/views/orderauthor/author/draft'; + +//订单操作 +import OrderList from '@/views/orderauthor/order/list'; +import OrderDraft from '@/views/orderauthor/order/draft'; +import runPlan from '../store/modules/runplan'; + +//打包管理 +import PackageMange from '@/views/orderauthor/permission/index'; +import PackageDetail from '@/views/orderauthor/permission/detail'; +import PackageDraft from '@/views/orderauthor/permission/draft/ruleForm'; + +//综合演练 +import trainRoom from '@/views/trainRoom/index'; +import JointTrainingDraft from '@/views/jointTraining/index'; + +import ThreeTest from '@/views/jlmap3d/index';//test +import ThreeDrawTest from '@/views/jlmap3d/jlmap3dedit';//test +import Jl3dSimulation from '@/views/jlmap3d/simulation/jl3dsimulation'; + +export const UrlConfig = { + display: '/display', + examRuleDraft: '/examRule/draft', + examRuleManage: '/examRule/manage', + map: { + prefix: '/map', + draft: '/map/draw', + skinStyle: '/map/skinStyle', + skinStyleDraft: '/map/skinStyle/draft', + runPlan: '/map/runPlan', + runPlanView: '/map/runPlan/view', + product: '/map/product', + productDraft: '/map/product/draft', + }, + lesson: { + prefix: '/lesson', + record: '/lesson/record/training', + manage: '/lesson/manage/training' + }, + teach: { + prefix: '/teach', + detail: '/teach/detail', + practical: '/teach/practical', + pay: '/teach/pay', + }, + exam: { + prefix: '/exam', + detail: '/exam/detail', + course: '/exam/course', + examRuleManage: '/exam/examRule/manage', + pay: '/exam/pay' + }, + demonstration: { + prefix: '/demonstration', + detail: '/demonstration/detail', + pay: '/demonstration/pay' + }, + dp: { + prefix: '/dp', + detail: '/dp/detail', + pay: '/dp/pay' + + }, + plan: { + prefix: '/plan', + tool: '/plan/tool', + detail: '/plan/detail', + pay: '/plan/pay' + }, + replay: { + prefix: '/replay' + }, + permission: { + prefix: '/permission', + permissionList: '/permission/manage', + permissionDraft: '/permission/draft', + permission: '/permission/restore' + }, + publish: { + prefix: '/publish', + map: '/publish/map', + mapDraft: '/publish/map/draft', + runPlanEveryDay: '/publish/runPlan/everyDay', + runPlanTemplate: '/publish/runPlan/template', + runPlanCommon: '/publish/runPlan/common', + runPlanView: '/publish/runPlan/view', + }, + orderauthor: { + prefix: '/orderauthor', + commodityManage: '/orderauthor/commodity/manage', + commodityDraft: '/orderauthor/commodity/draft', + orderManage: '/orderauthor/order/manage', + orderDraft: '/orderauthor/order/draft', + authorMange: '/orderauthor/author/manage', + authorDraft: '/orderauthor/author/draft', + transferDetail: '/orderauthor/transfer/detail', + trainferPackage: '/orderauthor/transfer/package' + } +}; + +export const constantRouterMap = [ + { path: '/dp/login', component: resolve => require(['@/views/login/loginNewScreen'], resolve), hidden: true }, //扫码登录 + { path: '/dp/login1', component: resolve => require(['@/views/login/loginScreen'], resolve), hidden: true }, //密码登录 + + { path: '/plan/login', component: resolve => require(['@/views/login/loginNewPlan'], resolve), hidden: true }, //扫码登录 + { path: '/plan/login1', component: resolve => require(['@/views/login/loginPlan'], resolve), hidden: true }, //密码登录 + + { path: '/login', component: resolve => require(['@/views/login/loginNew'], resolve), hidden: true }, //扫码登录 + { path: '/login1', component: resolve => require(['@/views/login/index'], resolve), hidden: true }, //密码登录 + { path: '/jlmap3d/edit', component: resolve => require(['@/views/jlmap3d/jlmap3dedit'], resolve), hidden: true }, + { path: '/404', component: resolve => require(['@/views/404'], resolve), hidden: true }, + { path: '*', redirect: '/404', hidden: true } +]; + +export default new Router({ + mode: 'history', //后端支持可开 + scrollBehavior: () => ({ y: 0 }), + routes: constantRouterMap +}); + +export const user = '01'; // 普通用户 +export const mapCreater = '02'; // 地图创建权限 +export const lessonCreater = '03'; // 课程创建权限 +export const admin = '04'; // 管理员 +export const superAdmin = '05'; // 超级管理员 + +export const userExam = '011'; // 考试系统 +export const userLesson = '012'; // 教学系统 +export const userSimulation = '013'; // 仿真系统 +export const userScreen = '014'; // 大屏系统 +export const userPlan = '015'; // 计划系统 + + +export const asyncRouterMap = [ + { + path: '/', + component: resolve => require(['@/views/layout/Layout'], resolve), + redirect: '/dashboard', + name: 'Dashboard', + hidden: true, + children: [ + { + path: 'dashboard', + component: resolve => require(['@/views/dashboard/index.vue'], resolve) + }, + ] + }, + { + path: '/display/:mode', + component: resolve => require(['@/views/display/index'], resolve), + meta: { + title: '实训视图' + }, + hidden: true + }, + { + path: '/trainroom', + component: resolve => require(['@/views/trainRoom/index'], resolve), + meta: { + title: '综合演练房间' + }, + hidden: true, + }, + { + path: '/jointTraining', + name: '联合演练试图', + component: resolve => require(['@/views/jointTraining/index'], resolve), + hidden: true + }, + { + path: '/jlmap3d', + name: '三维test', + component: resolve => require(['@/views/jlmap3d/index'], resolve), + meta: { + title: '三维test' + }, + hidden: true + }, + { + path: '/map', + name: '地图管理', + meta: { + title: '地图管理', + roles: [admin, mapCreater], + }, + component: resolve => require(['@/views/layout/Layout'], resolve), + children: [ + { + path: 'skinStyle', + name: '皮肤管理', + component: resolve => require(['@/views/map/skinStyle/index'], resolve), + meta: { + title: '皮肤管理', + }, + }, + { + path: 'skinStyle/draft/:mode/:skinStyleId', + name: '操作权限', + hidden: true, + component: resolve => require(['@/views/map/skinStyle/draft'], resolve), + meta: { + title: '操作权限' + } + }, + { + path: 'draw', + redirect: '/map/draw/0/draft', + component: resolve => require(['@/views/map/mapdraft/index'], resolve), + meta: { + title: '地图绘制', + }, + children: [ + { + path: ':mapId/:view', + component: resolve => require(['@/views/map/mapdraft/mapedit/index'], resolve), + meta: { + title: '地图绘制', + }, + hidden: true + } + ] + }, + { + path: 'runPlan', + redirect: '/map/runPlan/view/draft', + name: '运行图管理', + component: resolve => require(['@/views/map/runplan/index'], resolve), + meta: { + title: '运行图管理' + }, + children: [{ + path: 'view/:mode', + component: resolve => require(['@/views/map/runplan/chart'], resolve), + hidden: true + }], + }, + { + path: 'product', + name: '产品编辑', + component: resolve => require(['@/views/lesson/trainingcategory/index'], resolve), + meta: { + title: '产品编辑', + } + } + ] + }, + { + path: '/lesson', + component: resolve => require(['@/views/layout/Layout'], resolve), + name: '课程管理', + meta: { + title: '课程管理', + roles: [admin, lessonCreater], + }, + children: [ + { + path: 'record', + redirect: '/lesson/record/training/0/null', + component: resolve => require(['@/views/lesson/trainingrecord/index'], resolve), + meta: { + title: '实训录制', + }, + children: [ + { + path: 'training/:trainingId/:trainingName', + component: resolve => require(['@/views/lesson/trainingrecord/manage/index'], resolve), + meta: { + title: '实训录制', + }, + hidden: true + }, + ] + }, + { + path: 'manage/taskmanage', + name: '任务管理', + component: resolve => require(['@/views/lesson/taskmanage/list'], resolve), + meta: { + title: '任务管理' + } + }, + { + path: 'manage/trainingRule', + name: '操作定义', + component: resolve => require(['@/views/lesson/trainingRule/list'], resolve), + meta: { + title: '操作定义' + }, + }, + { + path: 'manage/trainingRule/detail', + name: '操作定义明细', + hidden: true, + component: resolve => require(['@/views/lesson/trainingRule/detail/index'], resolve), + meta: { + title: '操作定义明细' + } + }, + { + path: 'manage/training', + name: '实训管理', + component: resolve => require(['@/views/lesson/trainingmanage/index'], resolve), + meta: { + title: '实训管理' + } + }, + { + path: 'edit', + name: '课程编辑', + component: resolve => require(['@/views/lesson/lessoncategory/index'], resolve), + meta: { + title: '课程编辑', + } + }, + { + path: 'manage/scriptmanage', + name: '剧本管理', + component: resolve => require(['@/views/lesson/scriptmanage/list'], resolve), + meta: { + title: '剧本管理' + } + } + ] + }, + { + path: '/teach', + redirect: '/teach/home', + name: '教学系统', + component: resolve => require(['@/views/teach/index'], resolve), + meta: { + roles: [admin, userLesson, user], + }, + children: [ + { + path: 'home', + component: resolve => require(['@/views/teach/home'], resolve), + meta: { + title: '教学系统' + }, + }, + { + path: 'detail/:lessonId', + name: '课程详情', + component: resolve => require(['@/views/teach/detail/index'], resolve), + meta: { + title: '课程详情' + }, + hidden: true + }, + { + path: 'practical/:trainingId/:lessonId', + name: '实训详情', + component: resolve => require(['@/views/teach/practical/index'], resolve), + meta: { + title: '实训详情' + }, + hidden: true + }, + { + path: 'pay/:lessonId', + component: resolve => require(['@/views/components/pay/index'], resolve), + meta: { + }, + hidden: true + } + ] + }, + { + path: '/exam', + redirect: '/exam/home', + component: resolve => require(['@/views/exam/index'], resolve), + name: '考试系统', + meta: { + roles: [admin, userExam, user], + }, + children: [ + { + path: 'home', + component: resolve => require(['@/views/exam/home'], resolve), + meta: { + title: '考试系统' + }, + }, + { + path: 'detail/:examId', + name: '试卷详情', + component: resolve => require(['@/views/exam/detail/examDetail'], resolve), + meta: { + title: '试卷详情' + }, + hidden: true, + }, + { + path: 'course/:lessonId', + component: resolve => require(['@/views/exam/detail/courseDetail'], resolve), + meta: { + }, + hidden: true, + }, + { + path: 'examRule/manage', + name: '试题设置', + component: resolve => require(['@/views/publish/examRule/index'], resolve), + meta: { + title: '试题设置' + }, + hidden: true, + }, + { + path: 'examRule/draft/:mode/:ruleId/:lessonId', + hidden: true, + component: resolve => require(['@/views/publish/examRule/draft/index'], resolve), + meta: { + title: '添加订单' + } + }, + { + path: 'pay/:lessonId', + component: resolve => require(['@/views/components/pay/index'], resolve), + meta: { + }, + hidden: true + }, + { + path: 'questionDetail/:examQuestionId', + name: '开始考试', + component: resolve => require(['@/views/exam/detail/questionDetail'], resolve), + meta: { + title: '开始考试', + }, + hidden: true, + }, + { + path: 'result/:userExamId', + name: '考试结果', + component: resolve => require(['@/views/exam/result'], resolve), + meta: { + title: '考试结果', + }, + hidden: true + }, + ] + }, + { + path: '/demonstration', + redirect: '/demonstration/home', + component: resolve => require(['@/views/demonstration/index'], resolve), + name: '仿真系统', + meta: { + roles: [admin, userSimulation, user], + }, + children: [ + { + path: 'home', + component: resolve => require(['@/views/demonstration/home'], resolve), + meta: { + title: '仿真系统' + }, + }, + { + path: 'detail/:mapId', + name: '地图详情', + component: resolve => require(['@/views/demonstration/detail/index'], resolve), + meta: { + title: '地图详情' + }, + hidden: true + }, + { + path: 'pay/:lessonId', + component: resolve => require(['@/views/components/pay/index'], resolve), + meta: { + }, + hidden: true + } + ] + }, + { + path: '/dp', + redirect: '/dp/home', + component: resolve => require(['@/views/screenMonitor/index'], resolve), + name: '大屏系统', + meta: { + roles: [admin, userScreen], + }, + children: [ + { + path: 'home', + component: resolve => require(['@/views/screenMonitor/home'], resolve), + meta: { + title: '大屏系统' + }, + target: true + }, + { + path: 'detail/:lessonId', + component: resolve => require(['@/views/screenMonitor/detail/index'], resolve), + meta: { + }, + hidden: true + }, + { + path: 'pay/:lessonId', + component: resolve => require(['@/views/components/pay/index'], resolve), + meta: { + }, + hidden: true + } + ] + }, + { + path: '/plan/tool', + component: resolve => require(['@/views/planMonitor/editTool/index'], resolve), + meta: { + }, + hidden: true + }, + { + path: '/plan', + redirect: '/plan/home', + component: resolve => require(['@/views/planMonitor/index'], resolve), + name: '琏计划', + meta: { + roles: [admin, user], + }, + children: [ + { + path: 'home', + component: resolve => require(['@/views/planMonitor/home'], resolve), + meta: { + title: '琏计划' + }, + target: true + }, + { + path: 'detail/:lessonId', + component: resolve => require(['@/views/planMonitor/detail'], resolve), + meta: { + }, + hidden: true + }, + { + path: 'pay/:lessonId', + component: resolve => require(['@/views/components/pay/index'], resolve), + meta: { + }, + hidden: true + } + ] + }, + { + path: '/replay', + component: resolve => require(['@/views/layout/Layout'], resolve), + meta: { + roles: [admin], + }, + children: [ + { + path: 'manage', + component: resolve => require(['@/views/replay/index'], resolve), + meta: { + title: '回放管理' + }, + } + ] + }, + { + path: '/permission', + component: resolve => require(['@/views/layout/Layout'], resolve), + meta: { + roles: [admin, user], + }, + children: [ + { + path: 'manage', + component: resolve => require(['@/views/package/index'], resolve), + meta: { + title: '权限管理' + }, + }, + { + path: 'draft', + component: resolve => require(['@/views/package/draft/ruleForm'], resolve), + meta: { + }, + hidden: true + } + ] + }, + { + path: '/publish', + name: '发布内容管理', + component: resolve => require(['@/views/layout/Layout'], resolve), + meta: { + title: '发布内容管理', + roles: [admin], + }, + children: [ + { + path: 'map', + name: '发布地图管理', + component: resolve => require(['@/views/publish/publishMap/index'], resolve), + meta: { + title: '发布地图管理', + } + }, + { + path: 'product', + name: '产品状态管理', + component: resolve => require(['@/views/publish/productStatus/index'], resolve), + meta: { + title: '产品状态管理', + } + }, + { + path: 'lesson', + name: '发布课程管理', + component: resolve => require(['@/views/publish/publishLesson/index'], resolve), + meta: { + title: '发布课程管理', + } + }, + // { + // path: 'script', + // name: '仿真脚本管理', + // component: resolve => require(['@/views/publish/simulationScript/index'], resolve), + // meta: { + // title: '仿真脚本管理', + // } + // }, + { + path: 'runPlan/template', + component: resolve => require(['@/views/publish/runPlanTemplate/index'], resolve), + meta: { + title: '运行图模板管理' + } + }, + { + path: 'runPlan/common', + component: resolve => require(['@/views/publish/runPlanCommon/index'], resolve), + meta: { + title: '运行图通用管理' + } + }, + { + path: 'runPlan/everyDay', + component: resolve => require(['@/views/publish/runPlanEveryDay/index'], resolve), + meta: { + title: '运行图每日管理' + } + }, + { + path: 'examRule/manage', + name: '试题管理', + component: resolve => require(['@/views/publish/examRule/index'], resolve), + meta: { + title: '试题管理' + } + }, + { + path: 'map/draft/:mode/:mapId', + component: resolve => require(['@/views/publish/publishMap/draft'], resolve), + meta: { + }, + hidden: true + }, + { + path: 'examRule/draft/:mode/:ruleId/:lessonId', + hidden: true, + component: resolve => require(['@/views/publish/examRule/draft/index'], resolve), + meta: { + } + }, + { + path: 'runPlan/common/:mode', + component: resolve => require(['@/views/publish/runPlanCommon/draft'], resolve), + meta: { + }, + hidden: true + }, + { + path: 'runPlan/view/:mode', + component: resolve => require(['@/views/map/runplan/chart'], resolve), + meta: { + }, + hidden: true + } + ] + }, + { + path: '/orderauthor', + component: resolve => require(['@/views/layout/Layout'], resolve), + name: '订单权限管理', + meta: { + title: '订单权限管理', + roles: [admin] + }, + children: [ + { + path: 'commodity/manage', + name: '商品管理', + component: resolve => require(['@/views/orderauthor/commodity/index'], resolve), + meta: { + title: '商品管理' + } + }, + { + path: 'order/manage', + name: '订单管理', + component: resolve => require(['@/views/orderauthor/order/list'], resolve), + meta: { + title: '订单管理' + } + }, + { + path: 'author/manage', + name: '权限管理', + component: resolve => require(['@/views/orderauthor/author/index'], resolve), + meta: { + title: '权限管理' + } + }, + { + path: 'transfer/manage', + name: '权限分发管理', + component: resolve => require(['@/views/orderauthor/permission/index'], resolve), + meta: { + title: '权限分发管理' + } + }, + { + path: 'commodity/draft/:mode/:commodityId', + name: '添加商品', + hidden: true, + component: resolve => require(['@/views/orderauthor/commodity/draft'], resolve), + meta: { + title: '添加商品' + } + }, + { + path: 'order/draft/:mode/:orderId', + name: '添加订单', + hidden: true, + component: resolve => require(['@/views/orderauthor/order/draft'], resolve), + meta: { + title: '添加订单' + } + }, + { + path: 'author/draft/:mode/:userId', + name: '添加课程权限', + hidden: true, + component: resolve => require(['@/views/orderauthor/author/draft'], resolve), + meta: { + title: '添加课程权限' + } + }, + { + path: 'transfer/detail/:permissionId', + component: resolve => require(['@/views/orderauthor/permission/detail'], resolve), + meta: { + }, + hidden: true + }, + { + path: 'transfer/package', + component: resolve => require(['@/views/orderauthor/permission/draft/ruleForm'], resolve), + meta: { + }, + hidden: true + } + ] + }, + { + path: '/system', + component: resolve => require(['@/views/layout/Layout'], resolve), + meta: { + title: '系统管理', + roles: [admin], + }, + children: [ + { + path: 'dictionary', + name: 'Dictionary', + component: resolve => require(['@/views/management/dictionary/index'], resolve), + meta: { + title: '数据字典' + } + }, + { + path: 'dictionary/detail', + name: '数据字典明细', + hidden: true, + component: resolve => require(['@/views/management/dictionaryDetail/index'], resolve), + meta: { + title: '数据字典明细' + } + }, + { + path: 'userManage', + name: '用户管理', + component: resolve => require(['@/views/management/userControl/index'], resolve), + meta: { + title: '用户管理', + } + }, + { + path: 'userTrainingManage', + name: '用户实训管理', + component: resolve => require(['@/views/management/userTraining/index'], resolve), + meta: { + title: '用户实训管理', + } + }, + { + path: 'userExamManage', + name: '用户考试管理', + component: resolve => require(['@/views/management/userExam/index'], resolve), + meta: { + title: '用户考试管理', + } + }, + { + path: 'userSimulationManage', + name: '用户仿真管理', + component: resolve => require(['@/views/management/userSimulation/index'], resolve), + meta: { + title: '用户仿真管理', + } + }, + { + path: 'existingSimulation', + name: '存在仿真管理', + component: resolve => require(['@/views/management/existingSimulation/index'], resolve), + meta: { + title: '存在仿真管理', + } + } + ] + }, +]; diff --git a/src/views/jlmap3d/edit/action.vue b/src/views/jlmap3d/edit/action.vue new file mode 100644 index 000000000..a4a431c64 --- /dev/null +++ b/src/views/jlmap3d/edit/action.vue @@ -0,0 +1,168 @@ + + + + + + diff --git a/src/views/jlmap3d/edit/assets.vue b/src/views/jlmap3d/edit/assets.vue new file mode 100644 index 000000000..322a4e5d2 --- /dev/null +++ b/src/views/jlmap3d/edit/assets.vue @@ -0,0 +1,208 @@ + + + + + + diff --git a/src/views/jlmap3d/edit/index.vue b/src/views/jlmap3d/edit/index.vue new file mode 100644 index 000000000..a75f96293 --- /dev/null +++ b/src/views/jlmap3d/edit/index.vue @@ -0,0 +1,36 @@ + + + diff --git a/src/views/jlmap3d/edit/map3dlist.vue b/src/views/jlmap3d/edit/map3dlist.vue new file mode 100644 index 000000000..8621705fb --- /dev/null +++ b/src/views/jlmap3d/edit/map3dlist.vue @@ -0,0 +1,306 @@ + + + + + diff --git a/src/views/jlmap3d/edit/menu.vue b/src/views/jlmap3d/edit/menu.vue new file mode 100644 index 000000000..544cacbd9 --- /dev/null +++ b/src/views/jlmap3d/edit/menu.vue @@ -0,0 +1,84 @@ + + + + + + diff --git a/src/views/jlmap3d/edit/property.vue b/src/views/jlmap3d/edit/property.vue new file mode 100644 index 000000000..50b86f8e9 --- /dev/null +++ b/src/views/jlmap3d/edit/property.vue @@ -0,0 +1,200 @@ + + + + + + diff --git a/src/views/jlmap3d/index.vue b/src/views/jlmap3d/index.vue new file mode 100644 index 000000000..dd791e92b --- /dev/null +++ b/src/views/jlmap3d/index.vue @@ -0,0 +1,256 @@ + + + + + + diff --git a/src/views/jlmap3d/jlmap3dedit.vue b/src/views/jlmap3d/jlmap3dedit.vue new file mode 100644 index 000000000..2d643fac9 --- /dev/null +++ b/src/views/jlmap3d/jlmap3dedit.vue @@ -0,0 +1,216 @@ + + + + + diff --git a/src/views/jlmap3d/show/configmenu.vue b/src/views/jlmap3d/show/configmenu.vue new file mode 100644 index 000000000..2efdb62f7 --- /dev/null +++ b/src/views/jlmap3d/show/configmenu.vue @@ -0,0 +1,83 @@ + + + + + + diff --git a/src/views/jlmap3d/show/menu.vue b/src/views/jlmap3d/show/menu.vue new file mode 100644 index 000000000..13ceed726 --- /dev/null +++ b/src/views/jlmap3d/show/menu.vue @@ -0,0 +1,165 @@ + + + + + + diff --git a/src/views/jlmap3d/show/msg.vue b/src/views/jlmap3d/show/msg.vue new file mode 100644 index 000000000..0eaf1b37f --- /dev/null +++ b/src/views/jlmap3d/show/msg.vue @@ -0,0 +1,180 @@ + + + + + + diff --git a/src/views/jlmap3d/show/property.vue b/src/views/jlmap3d/show/property.vue new file mode 100644 index 000000000..39ade69a8 --- /dev/null +++ b/src/views/jlmap3d/show/property.vue @@ -0,0 +1,124 @@ + + + + + + diff --git a/src/views/jlmap3d/simulation/jl3dsimulation.vue b/src/views/jlmap3d/simulation/jl3dsimulation.vue new file mode 100644 index 000000000..d8d6d4c6c --- /dev/null +++ b/src/views/jlmap3d/simulation/jl3dsimulation.vue @@ -0,0 +1,303 @@ + + + + + + diff --git a/src/views/jlmap3d/simulation/sceneview/mmi.css b/src/views/jlmap3d/simulation/sceneview/mmi.css new file mode 100644 index 000000000..f0532b361 --- /dev/null +++ b/src/views/jlmap3d/simulation/sceneview/mmi.css @@ -0,0 +1,78 @@ +/* N区 跳停扣车显示 */ +.narea{ + left:0px;top:0; +} + +/* D区 信息设置按钮 */ +.darea{ + right:0px;top:0; +} + +/* M1区 当前驾驶模式显示 */ +.m1area{ + left:0px;top:12%; +} + +/* M2区 当前运行等级显示 */ +.m2area{ + right:0px;top:12%; +} + +/* M3区 折返状态显示 */ +.m3area{ + left:0px;top:24%; +} + +/* M4区 列车进入停车窗显示 */ +.m4area{ + right:0px;top:24%; +} + +/* M5区 车门状态及门允许侧显示 */ +.m5area{ + left:0px;top:36%; +} + +/* M6区 发车信息显示 */ +.m6area{ + right:0px;top:36%; +} + +/* M7区 客室门控制模式显示 */ +.m7area{ + left:0px;top:48%; +} + +/* M8区 屏蔽门状态显示 */ +.m8area{ + right:0px;top:48%; +} + +/* M9区 设备故障显示 */ +.m9area{ + left:0px;top:60%; +} + +/* M10区 停车场/车辆段的转换区显示 */ +.m10area{ + right:0px;top:60%; +} + +/* C1区 ATO牵引制动状态显示 */ +.c1area{ +} + +/* C2区 最高预设驾驶模式显示 */ +.c2area{ + left:70px; +} + +/* C3区 列车完整性状态显示 */ +.c3area{ + left:140px; +} + +/* C5区 列车头尾设备状态显示 */ +.c5area{ + right:0; +} diff --git a/src/views/jlmap3d/simulation/sceneview/mmi.js b/src/views/jlmap3d/simulation/sceneview/mmi.js new file mode 100644 index 000000000..5da0d2fd0 --- /dev/null +++ b/src/views/jlmap3d/simulation/sceneview/mmi.js @@ -0,0 +1,127 @@ +import Vue from 'vue'; + +import {Three} from '@/jlmap3d/main/three.min.js'; + + +export function mmirender(dom) { + let scope = this; + + let scene = new THREE.Scene(); + scene.background = new THREE.Color( 0xcce0ff ); + scene.fog = new THREE.Fog( 0xcce0ff, 500, 10000 ); + scene.add( new THREE.AmbientLight( 0xffffff ) ); + // camera + + let camera = new THREE.PerspectiveCamera( 30, 360 / 200, 1, 10000 ); + camera.position.set( 0, 0, 80 ); + + let renderer = new THREE.WebGLRenderer( { antialias: true } ); + renderer.setPixelRatio( window.devicePixelRatio ); + renderer.setSize( 360, 200 ); + + var geometry = new THREE.PlaneBufferGeometry( 80, 80, 2 ); + var material = new THREE.MeshBasicMaterial( {color: 0x000000, side: THREE.DoubleSide} ); + var plane = new THREE.Mesh( geometry, material ); + scene.add( plane ); + + + var texture = new THREE.TextureLoader().load( '/static/jl3d/biao.png' ); + var geometry = new THREE.PlaneBufferGeometry( 60, 60, 2 ); + var material = new THREE.MeshBasicMaterial( {map: texture, side: THREE.DoubleSide} ); + var kd = new THREE.Mesh( geometry, material ); + kd.position.x = -5; + scene.add( kd ); + + var geometry = new THREE.PlaneBufferGeometry( 2, 2, 2 ); + var material = new THREE.MeshBasicMaterial( {color: 0x000000, side: THREE.DoubleSide} ); + var zc = new THREE.Mesh( geometry, material ); + zc.position.x = 2.48; + zc.position.y = -0.34; + zc.position.z = 1; + //zc.rotation.y = Math.PI; + //0刻度-Math.PI*65.8/100 + //110刻度-Math.PI*234.8/100 + zc.rotation.z = -Math.PI*65.8/100; + scene.add( zc ); + + var geometry = new THREE.PlaneBufferGeometry( 14, 0.8, 2 ); + var material = new THREE.MeshBasicMaterial( {color: 0xffffff, side: THREE.DoubleSide} ); + var zz = new THREE.Mesh( geometry, material ); + zz.position.z = 1; + zz.position.x = 11; + zc.add( zz ); + + var geometry = new THREE.PlaneBufferGeometry( 2, 2, 2 ); + var material = new THREE.MeshBasicMaterial( {color: 0x000000, side: THREE.DoubleSide} ); + var atp = new THREE.Mesh( geometry, material ); + atp.position.x = 2.48; + atp.position.y = -0.34; + atp.position.z = 1; + //zc.rotation.y = Math.PI; + //0刻度-Math.PI*65.8/100 + //110刻度-Math.PI*234.8/100 + atp.rotation.z = -Math.PI*65.8/100; + scene.add( atp ); + + var geometry = new THREE.CircleBufferGeometry(1.5, 1 ); + var material = new THREE.MeshBasicMaterial( {color: 0xea0000, side: THREE.DoubleSide} ); + var atpmark = new THREE.Mesh( geometry, material ); + atpmark.position.z = 1; + atpmark.position.x = 21; + atpmark.rotation.y = Math.PI; + atp.add( atpmark ); + + var geometry = new THREE.PlaneBufferGeometry( 2, 2, 2 ); + var material = new THREE.MeshBasicMaterial( {color: 0x000000, side: THREE.DoubleSide} ); + var ato = new THREE.Mesh( geometry, material ); + ato.position.x = 2.48; + ato.position.y = -0.34; + ato.position.z = 1; + //zc.rotation.y = Math.PI; + //0刻度-Math.PI*65.8/100 + //110刻度-Math.PI*234.8/100 + ato.rotation.z = -Math.PI*65.8/100; + scene.add( ato ); + + var geometry = new THREE.CircleBufferGeometry(1.5, 1 ); + var material = new THREE.MeshBasicMaterial( {color: 0xEEEE00, side: THREE.DoubleSide} ); + var atomark = new THREE.Mesh( geometry, material ); + atomark.position.z = 1; + atomark.position.x = 21; + atomark.rotation.y = Math.PI; + ato.add( atomark ); + + + + dom.appendChild( renderer.domElement); + animate(); + + function animate() { + //zc.rotation.z -= 0.01; + renderer.render( scene, camera ); + requestAnimationFrame(animate); + } + + this.updatezz = function(speed){ + zc.rotation.z = -Math.PI*(65.8+169*speed/110)/100; + } + this.updateatp = function(atpspeed){ + if(atpspeed<0){ + atp.position.x = 10000; + }else{ + atp.position.x = 2.48; + atp.rotation.z = -Math.PI*(65.8+169*atpspeed/110)/100; + } + + } + + this.updateato = function(atospeed){ + if(atospeed<0){ + ato.position.x = 10000; + }else{ + ato.position.x = 2.48; + ato.rotation.z = -Math.PI*(65.8+169*atospeed/110)/100; + } + + } +} diff --git a/src/views/jlmap3d/simulation/sceneview/mmiimage.js b/src/views/jlmap3d/simulation/sceneview/mmiimage.js new file mode 100644 index 000000000..eec1756eb --- /dev/null +++ b/src/views/jlmap3d/simulation/sceneview/mmiimage.js @@ -0,0 +1,94 @@ +import Vue from 'vue'; + +export function mmiimage() { + let scope = this; + + //n + this.n = []; + this.n['tiaoting'] = '/static/jl3d/mmi/n-tiaoting.png'; + this.n['kouche'] = '/static/jl3d/mmi/n-kouche.png'; + this.n['none'] = '/static/jl3d/mmi/none.png'; + + //d + this.d = [] + this.d['info'] = '/static/jl3d/mmi/d-info.png'; + + this.m1 = []; + this.m1['start'] = '/static/jl3d/mmi/none.png'; + this.m1['am'] = '/static/jl3d/mmi/m1-am.png'; + this.m1['cm'] = '/static/jl3d/mmi/m1-cm.png'; + this.m1['rm'] = '/static/jl3d/mmi/m1-rm.png'; + + this.m2 = []; + this.m2['start'] = '/static/jl3d/mmi/none.png'; + this.m2['il'] = '/static/jl3d/mmi/m2-il.png'; + this.m2['itc'] = '/static/jl3d/mmi/m2-itc.png'; + this.m2['cbtc'] = '/static/jl3d/mmi/m2-cbtc.png'; + + this.m3 = []; + this.m3['zhefan'] = '/static/jl3d/mmi/m3-zhefan.png'; + this.m3['zhefanzhong'] = '/static/jl3d/mmi/m3-zhefanzhong.png'; + this.m3['none'] = '/static/jl3d/mmi/none.png'; + + this.m4 = []; + this.m4['none'] = '/static/jl3d/mmi/none.png'; + this.m4['out'] = '/static/jl3d/mmi/m4-out.png'; + this.m4['in'] = '/static/jl3d/mmi/m4-in.png'; + + this.m5 = []; + this.m5['state15'] = '/static/jl3d/mmi/m5-15.png'; + + this.m6 = []; + this.m6['none'] = '/static/jl3d/mmi/none.png'; + this.m6['fache'] = '/static/jl3d/mmi/m6-fache.png'; + this.m6['closedoor'] = '/static/jl3d/mmi/m6-closedoor.png'; + + this.m7 = []; + this.m7['momc'] = '/static/jl3d/mmi/m7-momc.png'; + this.m7['aomc'] = '/static/jl3d/mmi/m7-aomc.png'; + this.m7['aoac'] = '/static/jl3d/mmi/m7-aoac.png'; + this.m7['none'] = '/static/jl3d/mmi/none.png'; + + this.m8 = []; + this.m8['dahua'] = '/static/jl3d/mmi/m8-dahua.png'; + this.m8['jinjizhidong'] = '/static/jl3d/mmi/m8-jinjizhidong.png'; + this.m8['doornotclose'] = '/static/jl3d/mmi/m8-doornotclose.png'; + this.m8['none'] = '/static/jl3d/mmi/none.png'; + + this.m9 = []; + this.m9['rad'] = '/static/jl3d/mmi/m9-rad.png'; + this.m9['atp'] = '/static/jl3d/mmi/m9-atp.png'; + this.m9['ato'] = '/static/jl3d/mmi/m9-ato.png'; + this.m9['none'] = '/static/jl3d/mmi/none.png'; + + this.m10 = []; + this.m10['jinduan'] = '/static/jl3d/mmi/m10-jinduan.png'; + this.m10['shiwei'] = '/static/jl3d/mmi/m10-shiwei.png'; + this.m10['querenxinhao'] = '/static/jl3d/mmi/m10-querenxinhao.png'; + this.m10['none'] = '/static/jl3d/mmi/none.png'; + console.log(scope); + + this.c1 = []; + this.c1['qianyin'] = '/static/jl3d/mmi/c1-qianyin.png'; + this.c1['duoxing'] = '/static/jl3d/mmi/c1-duoxing.png'; + this.c1['zhidong'] = '/static/jl3d/mmi/c1-zhidong.png'; + this.c1['none'] = '/static/jl3d/mmi/none.png'; + + + this.c2 = []; + this.c2['rm'] = '/static/jl3d/mmi/m1-rm.png'; + this.c2['cmi'] = '/static/jl3d/mmi/c2-cm-i.png'; + this.c2['cmc'] = '/static/jl3d/mmi/c2-cm-c.png'; + this.c2['ami'] = '/static/jl3d/mmi/c2-am-i.png'; + this.c2['amc'] = '/static/jl3d/mmi/c2-am-c.png'; + this.c2['none'] = '/static/jl3d/mmi/none.png'; + + this.c3 = []; + this.c3['normal'] = '/static/jl3d/mmi/c3-normal.png'; + this.c3['lose'] = '/static/jl3d/mmi/c3-lose.png'; + + this.c5 = []; + this.c5['normal'] = '/static/jl3d/mmi/c5-normal.png'; + this.c5['lose'] = '/static/jl3d/mmi/c5-lose.png'; + +} diff --git a/src/views/jlmap3d/simulation/sceneview/mmiview.vue b/src/views/jlmap3d/simulation/sceneview/mmiview.vue new file mode 100644 index 000000000..13aca37f1 --- /dev/null +++ b/src/views/jlmap3d/simulation/sceneview/mmiview.vue @@ -0,0 +1,374 @@ + + + + + + diff --git a/src/views/jlmap3d/simulation/sceneview/tmsview.vue b/src/views/jlmap3d/simulation/sceneview/tmsview.vue new file mode 100644 index 000000000..24c305d47 --- /dev/null +++ b/src/views/jlmap3d/simulation/sceneview/tmsview.vue @@ -0,0 +1,54 @@ + + + + + + diff --git a/src/views/jointTraining/chartView.vue b/src/views/jointTraining/chartView.vue new file mode 100644 index 000000000..7349d79ea --- /dev/null +++ b/src/views/jointTraining/chartView.vue @@ -0,0 +1,493 @@ + + + + + diff --git a/src/views/jointTraining/chatWindow.vue b/src/views/jointTraining/chatWindow.vue new file mode 100644 index 000000000..177327c41 --- /dev/null +++ b/src/views/jointTraining/chatWindow.vue @@ -0,0 +1,673 @@ + + + + + \ No newline at end of file diff --git a/src/views/jointTraining/index.vue b/src/views/jointTraining/index.vue new file mode 100644 index 000000000..3c0b61ce7 --- /dev/null +++ b/src/views/jointTraining/index.vue @@ -0,0 +1,336 @@ + + + + diff --git a/src/views/jointTraining/menuDemon.vue b/src/views/jointTraining/menuDemon.vue new file mode 100644 index 000000000..825c037cc --- /dev/null +++ b/src/views/jointTraining/menuDemon.vue @@ -0,0 +1,468 @@ + + + + diff --git a/src/views/jointTraining/menuDraft/chooseRole.vue b/src/views/jointTraining/menuDraft/chooseRole.vue new file mode 100644 index 000000000..426cf67e6 --- /dev/null +++ b/src/views/jointTraining/menuDraft/chooseRole.vue @@ -0,0 +1,88 @@ + + + + + \ No newline at end of file diff --git a/src/views/jointTraining/menuDraft/operateMenu.vue b/src/views/jointTraining/menuDraft/operateMenu.vue new file mode 100644 index 000000000..3f5a81600 --- /dev/null +++ b/src/views/jointTraining/menuDraft/operateMenu.vue @@ -0,0 +1,135 @@ + + + diff --git a/src/views/jointTraining/menuSchema.vue b/src/views/jointTraining/menuSchema.vue new file mode 100644 index 000000000..9a6c24c12 --- /dev/null +++ b/src/views/jointTraining/menuSchema.vue @@ -0,0 +1,157 @@ + + + \ No newline at end of file diff --git a/src/views/lesson/lessoncategory/category/operateMenu.vue b/src/views/lesson/lessoncategory/category/operateMenu.vue new file mode 100644 index 000000000..c93cb1d45 --- /dev/null +++ b/src/views/lesson/lessoncategory/category/operateMenu.vue @@ -0,0 +1,115 @@ + + + \ No newline at end of file diff --git a/src/views/lesson/lessoncategory/category/tree.vue b/src/views/lesson/lessoncategory/category/tree.vue new file mode 100644 index 000000000..fc5b626ec --- /dev/null +++ b/src/views/lesson/lessoncategory/category/tree.vue @@ -0,0 +1,170 @@ + + + \ No newline at end of file diff --git a/src/views/lesson/lessoncategory/edit/chapter/index.vue b/src/views/lesson/lessoncategory/edit/chapter/index.vue new file mode 100644 index 000000000..32fcd1ecc --- /dev/null +++ b/src/views/lesson/lessoncategory/edit/chapter/index.vue @@ -0,0 +1,286 @@ + + + + + \ No newline at end of file diff --git a/src/views/lesson/lessoncategory/edit/chapter/list.vue b/src/views/lesson/lessoncategory/edit/chapter/list.vue new file mode 100644 index 000000000..1e6ad527a --- /dev/null +++ b/src/views/lesson/lessoncategory/edit/chapter/list.vue @@ -0,0 +1,239 @@ + + + \ No newline at end of file diff --git a/src/views/lesson/lessoncategory/edit/create/index.vue b/src/views/lesson/lessoncategory/edit/create/index.vue new file mode 100644 index 000000000..5b8526675 --- /dev/null +++ b/src/views/lesson/lessoncategory/edit/create/index.vue @@ -0,0 +1,91 @@ + + + + \ No newline at end of file diff --git a/src/views/lesson/lessoncategory/edit/lesson/index.vue b/src/views/lesson/lessoncategory/edit/lesson/index.vue new file mode 100644 index 000000000..b7c42379c --- /dev/null +++ b/src/views/lesson/lessoncategory/edit/lesson/index.vue @@ -0,0 +1,266 @@ + + + + + \ No newline at end of file diff --git a/src/views/lesson/lessoncategory/edit/lesson/publish.vue b/src/views/lesson/lessoncategory/edit/lesson/publish.vue new file mode 100644 index 000000000..8d4ded1b0 --- /dev/null +++ b/src/views/lesson/lessoncategory/edit/lesson/publish.vue @@ -0,0 +1,136 @@ + + + \ No newline at end of file diff --git a/src/views/lesson/lessoncategory/edit/sorttree/index.vue b/src/views/lesson/lessoncategory/edit/sorttree/index.vue new file mode 100644 index 000000000..fa3cb467b --- /dev/null +++ b/src/views/lesson/lessoncategory/edit/sorttree/index.vue @@ -0,0 +1,138 @@ + + + + \ No newline at end of file diff --git a/src/views/lesson/lessoncategory/index.vue b/src/views/lesson/lessoncategory/index.vue new file mode 100644 index 000000000..86248333f --- /dev/null +++ b/src/views/lesson/lessoncategory/index.vue @@ -0,0 +1,129 @@ + + + + \ No newline at end of file diff --git a/src/views/lesson/scriptmanage/create.vue b/src/views/lesson/scriptmanage/create.vue new file mode 100644 index 000000000..20c3a48af --- /dev/null +++ b/src/views/lesson/scriptmanage/create.vue @@ -0,0 +1,107 @@ + + + \ No newline at end of file diff --git a/src/views/lesson/scriptmanage/list.vue b/src/views/lesson/scriptmanage/list.vue new file mode 100644 index 000000000..4d0d387da --- /dev/null +++ b/src/views/lesson/scriptmanage/list.vue @@ -0,0 +1,188 @@ + + + \ No newline at end of file diff --git a/src/views/lesson/taskmanage/createTask.vue b/src/views/lesson/taskmanage/createTask.vue new file mode 100644 index 000000000..2b656a489 --- /dev/null +++ b/src/views/lesson/taskmanage/createTask.vue @@ -0,0 +1,100 @@ + + + \ No newline at end of file diff --git a/src/views/lesson/taskmanage/list.vue b/src/views/lesson/taskmanage/list.vue new file mode 100644 index 000000000..c2d6e8209 --- /dev/null +++ b/src/views/lesson/taskmanage/list.vue @@ -0,0 +1,181 @@ + + + \ No newline at end of file diff --git a/src/views/lesson/trainingRule/addBatch.vue b/src/views/lesson/trainingRule/addBatch.vue new file mode 100644 index 000000000..37f775391 --- /dev/null +++ b/src/views/lesson/trainingRule/addBatch.vue @@ -0,0 +1,115 @@ + + + \ No newline at end of file diff --git a/src/views/lesson/trainingRule/addEdit.vue b/src/views/lesson/trainingRule/addEdit.vue new file mode 100644 index 000000000..3e78a82d7 --- /dev/null +++ b/src/views/lesson/trainingRule/addEdit.vue @@ -0,0 +1,300 @@ + + + \ No newline at end of file diff --git a/src/views/lesson/trainingRule/detail/edit.vue b/src/views/lesson/trainingRule/detail/edit.vue new file mode 100644 index 000000000..3f5a738ae --- /dev/null +++ b/src/views/lesson/trainingRule/detail/edit.vue @@ -0,0 +1,178 @@ + + + \ No newline at end of file diff --git a/src/views/lesson/trainingRule/detail/index.vue b/src/views/lesson/trainingRule/detail/index.vue new file mode 100644 index 000000000..c3cb09788 --- /dev/null +++ b/src/views/lesson/trainingRule/detail/index.vue @@ -0,0 +1,184 @@ + + + \ No newline at end of file diff --git a/src/views/lesson/trainingRule/list.vue b/src/views/lesson/trainingRule/list.vue new file mode 100644 index 000000000..bbe3fbb7f --- /dev/null +++ b/src/views/lesson/trainingRule/list.vue @@ -0,0 +1,271 @@ + + + \ No newline at end of file diff --git a/src/views/lesson/trainingcategory/edit.vue b/src/views/lesson/trainingcategory/edit.vue new file mode 100644 index 000000000..10d8b0b64 --- /dev/null +++ b/src/views/lesson/trainingcategory/edit.vue @@ -0,0 +1,303 @@ + + + + \ No newline at end of file diff --git a/src/views/lesson/trainingcategory/index.vue b/src/views/lesson/trainingcategory/index.vue new file mode 100644 index 000000000..d1e11c30e --- /dev/null +++ b/src/views/lesson/trainingcategory/index.vue @@ -0,0 +1,61 @@ + + + + \ No newline at end of file diff --git a/src/views/lesson/trainingcategory/tree.vue b/src/views/lesson/trainingcategory/tree.vue new file mode 100644 index 000000000..7ce52d968 --- /dev/null +++ b/src/views/lesson/trainingcategory/tree.vue @@ -0,0 +1,110 @@ + + + + \ No newline at end of file diff --git a/src/views/lesson/trainingmanage/draft.vue b/src/views/lesson/trainingmanage/draft.vue new file mode 100644 index 000000000..ff840868b --- /dev/null +++ b/src/views/lesson/trainingmanage/draft.vue @@ -0,0 +1,317 @@ + + + + \ No newline at end of file diff --git a/src/views/lesson/trainingmanage/index.vue b/src/views/lesson/trainingmanage/index.vue new file mode 100644 index 000000000..af0806625 --- /dev/null +++ b/src/views/lesson/trainingmanage/index.vue @@ -0,0 +1,286 @@ + + + \ No newline at end of file diff --git a/src/views/lesson/trainingrecord/category/draft.vue b/src/views/lesson/trainingrecord/category/draft.vue new file mode 100644 index 000000000..d64934059 --- /dev/null +++ b/src/views/lesson/trainingrecord/category/draft.vue @@ -0,0 +1,302 @@ + + + \ No newline at end of file diff --git a/src/views/lesson/trainingrecord/category/operateMenu.vue b/src/views/lesson/trainingrecord/category/operateMenu.vue new file mode 100644 index 000000000..9ca8bfa3e --- /dev/null +++ b/src/views/lesson/trainingrecord/category/operateMenu.vue @@ -0,0 +1,126 @@ + + + \ No newline at end of file diff --git a/src/views/lesson/trainingrecord/category/tree.vue b/src/views/lesson/trainingrecord/category/tree.vue new file mode 100644 index 000000000..c90f1f11a --- /dev/null +++ b/src/views/lesson/trainingrecord/category/tree.vue @@ -0,0 +1,132 @@ + + + + \ No newline at end of file diff --git a/src/views/lesson/trainingrecord/index.vue b/src/views/lesson/trainingrecord/index.vue new file mode 100644 index 000000000..8f22bb690 --- /dev/null +++ b/src/views/lesson/trainingrecord/index.vue @@ -0,0 +1,111 @@ + + + + \ No newline at end of file diff --git a/src/views/lesson/trainingrecord/manage/index.vue b/src/views/lesson/trainingrecord/manage/index.vue new file mode 100644 index 000000000..7161f7969 --- /dev/null +++ b/src/views/lesson/trainingrecord/manage/index.vue @@ -0,0 +1,114 @@ + + + + \ No newline at end of file diff --git a/src/views/lesson/trainingrecord/manage/stepmanage/index.vue b/src/views/lesson/trainingrecord/manage/stepmanage/index.vue new file mode 100644 index 000000000..cdbaadf6f --- /dev/null +++ b/src/views/lesson/trainingrecord/manage/stepmanage/index.vue @@ -0,0 +1,85 @@ + + + \ No newline at end of file diff --git a/src/views/lesson/trainingrecord/manage/stepmanage/stationlist/index.vue b/src/views/lesson/trainingrecord/manage/stepmanage/stationlist/index.vue new file mode 100644 index 000000000..d1fb835b2 --- /dev/null +++ b/src/views/lesson/trainingrecord/manage/stepmanage/stationlist/index.vue @@ -0,0 +1,76 @@ + + + \ No newline at end of file diff --git a/src/views/lesson/trainingrecord/manage/stepmanage/stepinfo/index.vue b/src/views/lesson/trainingrecord/manage/stepmanage/stepinfo/index.vue new file mode 100644 index 000000000..789fdfc38 --- /dev/null +++ b/src/views/lesson/trainingrecord/manage/stepmanage/stepinfo/index.vue @@ -0,0 +1,49 @@ + + \ No newline at end of file diff --git a/src/views/lesson/trainingrecord/manage/stepmanage/stepinfo/list.vue b/src/views/lesson/trainingrecord/manage/stepmanage/stepinfo/list.vue new file mode 100644 index 000000000..4359b56e5 --- /dev/null +++ b/src/views/lesson/trainingrecord/manage/stepmanage/stepinfo/list.vue @@ -0,0 +1,40 @@ + + \ No newline at end of file diff --git a/src/views/lesson/trainingrecord/manage/stepmanage/stepinfo/step.vue b/src/views/lesson/trainingrecord/manage/stepmanage/stepinfo/step.vue new file mode 100644 index 000000000..da5713376 --- /dev/null +++ b/src/views/lesson/trainingrecord/manage/stepmanage/stepinfo/step.vue @@ -0,0 +1,222 @@ + + \ No newline at end of file diff --git a/src/views/management/dictionary/edit.vue b/src/views/management/dictionary/edit.vue new file mode 100644 index 000000000..6269d09b9 --- /dev/null +++ b/src/views/management/dictionary/edit.vue @@ -0,0 +1,155 @@ + + + \ No newline at end of file diff --git a/src/views/management/dictionary/index.vue b/src/views/management/dictionary/index.vue new file mode 100644 index 000000000..76dbbf8f2 --- /dev/null +++ b/src/views/management/dictionary/index.vue @@ -0,0 +1,138 @@ + + + \ No newline at end of file diff --git a/src/views/management/dictionaryDetail/edit.vue b/src/views/management/dictionaryDetail/edit.vue new file mode 100644 index 000000000..71b8d69ae --- /dev/null +++ b/src/views/management/dictionaryDetail/edit.vue @@ -0,0 +1,159 @@ + + + \ No newline at end of file diff --git a/src/views/management/dictionaryDetail/index.vue b/src/views/management/dictionaryDetail/index.vue new file mode 100644 index 000000000..1f8a7da4b --- /dev/null +++ b/src/views/management/dictionaryDetail/index.vue @@ -0,0 +1,166 @@ + + + + \ No newline at end of file diff --git a/src/views/management/existingSimulation/index.vue b/src/views/management/existingSimulation/index.vue new file mode 100644 index 000000000..c8aeb3bb5 --- /dev/null +++ b/src/views/management/existingSimulation/index.vue @@ -0,0 +1,206 @@ + + + diff --git a/src/views/management/userControl/correlationMap.vue b/src/views/management/userControl/correlationMap.vue new file mode 100644 index 000000000..5cb7f5cb0 --- /dev/null +++ b/src/views/management/userControl/correlationMap.vue @@ -0,0 +1,85 @@ + + + \ No newline at end of file diff --git a/src/views/management/userControl/edit.vue b/src/views/management/userControl/edit.vue new file mode 100644 index 000000000..5e2b4fd47 --- /dev/null +++ b/src/views/management/userControl/edit.vue @@ -0,0 +1,101 @@ + + + \ No newline at end of file diff --git a/src/views/management/userControl/index.vue b/src/views/management/userControl/index.vue new file mode 100644 index 000000000..52a885670 --- /dev/null +++ b/src/views/management/userControl/index.vue @@ -0,0 +1,146 @@ + + + \ No newline at end of file diff --git a/src/views/management/userExam/edit.vue b/src/views/management/userExam/edit.vue new file mode 100644 index 000000000..17ce53c9e --- /dev/null +++ b/src/views/management/userExam/edit.vue @@ -0,0 +1,107 @@ + + + \ No newline at end of file diff --git a/src/views/management/userExam/index.vue b/src/views/management/userExam/index.vue new file mode 100644 index 000000000..df01a323c --- /dev/null +++ b/src/views/management/userExam/index.vue @@ -0,0 +1,150 @@ + + + \ No newline at end of file diff --git a/src/views/management/userSimulation/add.vue b/src/views/management/userSimulation/add.vue new file mode 100644 index 000000000..afc76dd0e --- /dev/null +++ b/src/views/management/userSimulation/add.vue @@ -0,0 +1,201 @@ + + + \ No newline at end of file diff --git a/src/views/management/userSimulation/edit.vue b/src/views/management/userSimulation/edit.vue new file mode 100644 index 000000000..62b6ad1a8 --- /dev/null +++ b/src/views/management/userSimulation/edit.vue @@ -0,0 +1,109 @@ + + + \ No newline at end of file diff --git a/src/views/management/userSimulation/index.vue b/src/views/management/userSimulation/index.vue new file mode 100644 index 000000000..fdfe4c0cc --- /dev/null +++ b/src/views/management/userSimulation/index.vue @@ -0,0 +1,171 @@ + + + \ No newline at end of file diff --git a/src/views/management/userTraining/add.vue b/src/views/management/userTraining/add.vue new file mode 100644 index 000000000..502c58470 --- /dev/null +++ b/src/views/management/userTraining/add.vue @@ -0,0 +1,186 @@ + + + \ No newline at end of file diff --git a/src/views/management/userTraining/addTraining.vue b/src/views/management/userTraining/addTraining.vue new file mode 100644 index 000000000..4d41fa0e3 --- /dev/null +++ b/src/views/management/userTraining/addTraining.vue @@ -0,0 +1,92 @@ + + + \ No newline at end of file diff --git a/src/views/management/userTraining/edit.vue b/src/views/management/userTraining/edit.vue new file mode 100644 index 000000000..cc72d67bb --- /dev/null +++ b/src/views/management/userTraining/edit.vue @@ -0,0 +1,108 @@ + + + \ No newline at end of file diff --git a/src/views/management/userTraining/index.vue b/src/views/management/userTraining/index.vue new file mode 100644 index 000000000..0f49e384b --- /dev/null +++ b/src/views/management/userTraining/index.vue @@ -0,0 +1,146 @@ + + + \ No newline at end of file diff --git a/src/views/map/mapdraft/mapmanage/edit.vue b/src/views/map/mapdraft/mapmanage/edit.vue index 96ed6a2f5..dcf564032 100644 --- a/src/views/map/mapdraft/mapmanage/edit.vue +++ b/src/views/map/mapdraft/mapmanage/edit.vue @@ -213,7 +213,7 @@ export default { }, initLoadData() { this.cityList = []; - // this.$Dictionary.CityType().then(list => { + // this.$Dictionary.cityType().then(list => { // this.cityList = list; // }).catch(() => { // this.$messageBox('加载城市列表失败'); diff --git a/src/views/map/mapdraft/mapmanage/publish.vue b/src/views/map/mapdraft/mapmanage/publish.vue index f55062ae1..e3b40c439 100644 --- a/src/views/map/mapdraft/mapmanage/publish.vue +++ b/src/views/map/mapdraft/mapmanage/publish.vue @@ -56,7 +56,7 @@ export default { } }, mounted() { - // this.$Dictionary.CityType().then(list => { + // this.$Dictionary.cityType().then(list => { // this.cityList = list; // }); }, diff --git a/src/views/map/product/draft.vue b/src/views/map/product/draft.vue new file mode 100644 index 000000000..f786e5e0e --- /dev/null +++ b/src/views/map/product/draft.vue @@ -0,0 +1,304 @@ + + + + \ No newline at end of file diff --git a/src/views/map/product/list.vue b/src/views/map/product/list.vue new file mode 100644 index 000000000..9511579a8 --- /dev/null +++ b/src/views/map/product/list.vue @@ -0,0 +1,168 @@ + + + \ No newline at end of file diff --git a/src/views/map/runplan/chart.vue b/src/views/map/runplan/chart.vue new file mode 100644 index 000000000..7678071c7 --- /dev/null +++ b/src/views/map/runplan/chart.vue @@ -0,0 +1,364 @@ + + + \ No newline at end of file diff --git a/src/views/map/runplan/draft/setting.vue b/src/views/map/runplan/draft/setting.vue new file mode 100644 index 000000000..ee6ce592b --- /dev/null +++ b/src/views/map/runplan/draft/setting.vue @@ -0,0 +1,80 @@ + + + + \ No newline at end of file diff --git a/src/views/map/runplan/index.vue b/src/views/map/runplan/index.vue new file mode 100644 index 000000000..d08ba0e06 --- /dev/null +++ b/src/views/map/runplan/index.vue @@ -0,0 +1,67 @@ + + + + \ No newline at end of file diff --git a/src/views/map/runplan/manage/check.vue b/src/views/map/runplan/manage/check.vue new file mode 100644 index 000000000..b358b83f1 --- /dev/null +++ b/src/views/map/runplan/manage/check.vue @@ -0,0 +1,59 @@ + + + + \ No newline at end of file diff --git a/src/views/map/runplan/manage/create.vue b/src/views/map/runplan/manage/create.vue new file mode 100644 index 000000000..52379b2e6 --- /dev/null +++ b/src/views/map/runplan/manage/create.vue @@ -0,0 +1,319 @@ + + + + \ No newline at end of file diff --git a/src/views/map/runplan/manage/import.vue b/src/views/map/runplan/manage/import.vue new file mode 100644 index 000000000..9ce036d42 --- /dev/null +++ b/src/views/map/runplan/manage/import.vue @@ -0,0 +1,110 @@ + + + + + \ No newline at end of file diff --git a/src/views/map/runplan/manage/list.vue b/src/views/map/runplan/manage/list.vue new file mode 100644 index 000000000..e2d4cc458 --- /dev/null +++ b/src/views/map/runplan/manage/list.vue @@ -0,0 +1,141 @@ + + + + \ No newline at end of file diff --git a/src/views/map/runplan/manage/operateMenu.vue b/src/views/map/runplan/manage/operateMenu.vue new file mode 100644 index 000000000..47febec79 --- /dev/null +++ b/src/views/map/runplan/manage/operateMenu.vue @@ -0,0 +1,197 @@ + + + \ No newline at end of file diff --git a/src/views/map/runplan/manage/publish.vue b/src/views/map/runplan/manage/publish.vue new file mode 100644 index 000000000..cff68cbcc --- /dev/null +++ b/src/views/map/runplan/manage/publish.vue @@ -0,0 +1,78 @@ + + + \ No newline at end of file diff --git a/src/views/map/skinStyle/draft.vue b/src/views/map/skinStyle/draft.vue new file mode 100644 index 000000000..531294e01 --- /dev/null +++ b/src/views/map/skinStyle/draft.vue @@ -0,0 +1,266 @@ + + + + + \ No newline at end of file diff --git a/src/views/map/skinStyle/index.vue b/src/views/map/skinStyle/index.vue new file mode 100644 index 000000000..8107c76b0 --- /dev/null +++ b/src/views/map/skinStyle/index.vue @@ -0,0 +1,154 @@ + + + \ No newline at end of file diff --git a/src/views/orderauthor/author/detail.vue b/src/views/orderauthor/author/detail.vue new file mode 100644 index 000000000..f52ff9ab9 --- /dev/null +++ b/src/views/orderauthor/author/detail.vue @@ -0,0 +1,97 @@ + + + \ No newline at end of file diff --git a/src/views/orderauthor/author/draft.vue b/src/views/orderauthor/author/draft.vue new file mode 100644 index 000000000..d4097abbf --- /dev/null +++ b/src/views/orderauthor/author/draft.vue @@ -0,0 +1,236 @@ + + + + + \ No newline at end of file diff --git a/src/views/orderauthor/author/index.vue b/src/views/orderauthor/author/index.vue new file mode 100644 index 000000000..8f79cbb1f --- /dev/null +++ b/src/views/orderauthor/author/index.vue @@ -0,0 +1,265 @@ + + + \ No newline at end of file diff --git a/src/views/orderauthor/commodity/draft.vue b/src/views/orderauthor/commodity/draft.vue new file mode 100644 index 000000000..bdb15904a --- /dev/null +++ b/src/views/orderauthor/commodity/draft.vue @@ -0,0 +1,407 @@ + + + + + \ No newline at end of file diff --git a/src/views/orderauthor/commodity/index.vue b/src/views/orderauthor/commodity/index.vue new file mode 100644 index 000000000..a3fd4f582 --- /dev/null +++ b/src/views/orderauthor/commodity/index.vue @@ -0,0 +1,242 @@ + + + \ No newline at end of file diff --git a/src/views/orderauthor/order/addGoods.vue b/src/views/orderauthor/order/addGoods.vue new file mode 100644 index 000000000..e6aeb70f9 --- /dev/null +++ b/src/views/orderauthor/order/addGoods.vue @@ -0,0 +1,212 @@ + + + \ No newline at end of file diff --git a/src/views/orderauthor/order/canDistribute.vue b/src/views/orderauthor/order/canDistribute.vue new file mode 100644 index 000000000..dc46d97fc --- /dev/null +++ b/src/views/orderauthor/order/canDistribute.vue @@ -0,0 +1,54 @@ + + \ No newline at end of file diff --git a/src/views/orderauthor/order/draft.vue b/src/views/orderauthor/order/draft.vue new file mode 100644 index 000000000..46463eecd --- /dev/null +++ b/src/views/orderauthor/order/draft.vue @@ -0,0 +1,569 @@ + + + + + \ No newline at end of file diff --git a/src/views/orderauthor/order/list.vue b/src/views/orderauthor/order/list.vue new file mode 100644 index 000000000..bffb1fefc --- /dev/null +++ b/src/views/orderauthor/order/list.vue @@ -0,0 +1,333 @@ + + + \ No newline at end of file diff --git a/src/views/orderauthor/permission/detail.vue b/src/views/orderauthor/permission/detail.vue new file mode 100644 index 000000000..4e700b695 --- /dev/null +++ b/src/views/orderauthor/permission/detail.vue @@ -0,0 +1,200 @@ + + + + \ No newline at end of file diff --git a/src/views/orderauthor/permission/draft/choosePermission.vue b/src/views/orderauthor/permission/draft/choosePermission.vue new file mode 100644 index 000000000..f21da73c8 --- /dev/null +++ b/src/views/orderauthor/permission/draft/choosePermission.vue @@ -0,0 +1,219 @@ + + + \ No newline at end of file diff --git a/src/views/orderauthor/permission/draft/ruleForm.vue b/src/views/orderauthor/permission/draft/ruleForm.vue new file mode 100644 index 000000000..fba3e19e2 --- /dev/null +++ b/src/views/orderauthor/permission/draft/ruleForm.vue @@ -0,0 +1,245 @@ + + + + \ No newline at end of file diff --git a/src/views/orderauthor/permission/index.vue b/src/views/orderauthor/permission/index.vue new file mode 100644 index 000000000..5fddddc02 --- /dev/null +++ b/src/views/orderauthor/permission/index.vue @@ -0,0 +1,285 @@ + + + \ No newline at end of file diff --git a/src/views/package/draft/canDistribute.vue b/src/views/package/draft/canDistribute.vue new file mode 100644 index 000000000..f0d003267 --- /dev/null +++ b/src/views/package/draft/canDistribute.vue @@ -0,0 +1,51 @@ + + \ No newline at end of file diff --git a/src/views/package/draft/editRule.vue b/src/views/package/draft/editRule.vue new file mode 100644 index 000000000..cc7c54504 --- /dev/null +++ b/src/views/package/draft/editRule.vue @@ -0,0 +1,328 @@ + + + + + + \ No newline at end of file diff --git a/src/views/package/draft/ruleForm.vue b/src/views/package/draft/ruleForm.vue new file mode 100644 index 000000000..c0a9286af --- /dev/null +++ b/src/views/package/draft/ruleForm.vue @@ -0,0 +1,310 @@ + + + + \ No newline at end of file diff --git a/src/views/package/index.vue b/src/views/package/index.vue new file mode 100644 index 000000000..58aaaa229 --- /dev/null +++ b/src/views/package/index.vue @@ -0,0 +1,232 @@ + + + \ No newline at end of file diff --git a/src/views/package/selectRole/list.vue b/src/views/package/selectRole/list.vue new file mode 100644 index 000000000..1f5dd4da3 --- /dev/null +++ b/src/views/package/selectRole/list.vue @@ -0,0 +1,122 @@ + + + \ No newline at end of file diff --git a/src/views/planMonitor/detail.vue b/src/views/planMonitor/detail.vue new file mode 100644 index 000000000..9a57a9ab8 --- /dev/null +++ b/src/views/planMonitor/detail.vue @@ -0,0 +1,205 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/index.vue b/src/views/planMonitor/editTool/index.vue new file mode 100644 index 000000000..35e112416 --- /dev/null +++ b/src/views/planMonitor/editTool/index.vue @@ -0,0 +1,487 @@ + + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menuBar.vue b/src/views/planMonitor/editTool/menuBar.vue new file mode 100644 index 000000000..34a082792 --- /dev/null +++ b/src/views/planMonitor/editTool/menuBar.vue @@ -0,0 +1,728 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menuTool.vue b/src/views/planMonitor/editTool/menuTool.vue new file mode 100644 index 000000000..d3ecde016 --- /dev/null +++ b/src/views/planMonitor/editTool/menuTool.vue @@ -0,0 +1,172 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/addPlanningTrain.vue b/src/views/planMonitor/editTool/menus/addPlanningTrain.vue new file mode 100644 index 000000000..27efe2da9 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/addPlanningTrain.vue @@ -0,0 +1,92 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/addSmoothRunTime.vue b/src/views/planMonitor/editTool/menus/addSmoothRunTime.vue new file mode 100644 index 000000000..6d042481b --- /dev/null +++ b/src/views/planMonitor/editTool/menus/addSmoothRunTime.vue @@ -0,0 +1,140 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/addTask.vue b/src/views/planMonitor/editTool/menus/addTask.vue new file mode 100644 index 000000000..7f0487536 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/addTask.vue @@ -0,0 +1,317 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/components/dataTable.vue b/src/views/planMonitor/editTool/menus/components/dataTable.vue new file mode 100644 index 000000000..6d0ad3146 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/components/dataTable.vue @@ -0,0 +1,203 @@ + + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/createEmptyPlan.vue b/src/views/planMonitor/editTool/menus/createEmptyPlan.vue new file mode 100644 index 000000000..64ad3a8c5 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/createEmptyPlan.vue @@ -0,0 +1,90 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/deleteTask.vue b/src/views/planMonitor/editTool/menus/deleteTask.vue new file mode 100644 index 000000000..84c149433 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/deleteTask.vue @@ -0,0 +1,91 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/duplicateTrain.vue b/src/views/planMonitor/editTool/menus/duplicateTrain.vue new file mode 100644 index 000000000..4f22bb19b --- /dev/null +++ b/src/views/planMonitor/editTool/menus/duplicateTrain.vue @@ -0,0 +1,90 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/editPlanningTrain.vue b/src/views/planMonitor/editTool/menus/editPlanningTrain.vue new file mode 100644 index 000000000..eead789fd --- /dev/null +++ b/src/views/planMonitor/editTool/menus/editPlanningTrain.vue @@ -0,0 +1,232 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/editSmoothRunTime.vue b/src/views/planMonitor/editTool/menus/editSmoothRunTime.vue new file mode 100644 index 000000000..87ae64950 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/editSmoothRunTime.vue @@ -0,0 +1,113 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/editStationBetweenTime.vue b/src/views/planMonitor/editTool/menus/editStationBetweenTime.vue new file mode 100644 index 000000000..d622cde02 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/editStationBetweenTime.vue @@ -0,0 +1,45 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/lookup.vue b/src/views/planMonitor/editTool/menus/lookup.vue new file mode 100644 index 000000000..3e94d21d0 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/lookup.vue @@ -0,0 +1,45 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/modifyingBeginTime.vue b/src/views/planMonitor/editTool/menus/modifyingBeginTime.vue new file mode 100644 index 000000000..42c4e50e3 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/modifyingBeginTime.vue @@ -0,0 +1,64 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/modifyingRouting.vue b/src/views/planMonitor/editTool/menus/modifyingRouting.vue new file mode 100644 index 000000000..4c757e95b --- /dev/null +++ b/src/views/planMonitor/editTool/menus/modifyingRouting.vue @@ -0,0 +1,85 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/modifyingStationIntervalTime.vue b/src/views/planMonitor/editTool/menus/modifyingStationIntervalTime.vue new file mode 100644 index 000000000..fc05ed699 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/modifyingStationIntervalTime.vue @@ -0,0 +1,183 @@ + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/modifyingTask.vue b/src/views/planMonitor/editTool/menus/modifyingTask.vue new file mode 100644 index 000000000..60d5a481f --- /dev/null +++ b/src/views/planMonitor/editTool/menus/modifyingTask.vue @@ -0,0 +1,423 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/offLine.vue b/src/views/planMonitor/editTool/menus/offLine.vue new file mode 100644 index 000000000..1111996b5 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/offLine.vue @@ -0,0 +1,68 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/openRunPlan.vue b/src/views/planMonitor/editTool/menus/openRunPlan.vue new file mode 100644 index 000000000..aced4f971 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/openRunPlan.vue @@ -0,0 +1,105 @@ + + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/parameter/baseLoop.vue b/src/views/planMonitor/editTool/menus/parameter/baseLoop.vue new file mode 100644 index 000000000..8f8c3259f --- /dev/null +++ b/src/views/planMonitor/editTool/menus/parameter/baseLoop.vue @@ -0,0 +1,65 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/parameter/baseParameter.vue b/src/views/planMonitor/editTool/menus/parameter/baseParameter.vue new file mode 100644 index 000000000..22747ceda --- /dev/null +++ b/src/views/planMonitor/editTool/menus/parameter/baseParameter.vue @@ -0,0 +1,72 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/parameter/baseParkingDepot.vue b/src/views/planMonitor/editTool/menus/parameter/baseParkingDepot.vue new file mode 100644 index 000000000..4c33b3c1a --- /dev/null +++ b/src/views/planMonitor/editTool/menus/parameter/baseParkingDepot.vue @@ -0,0 +1,44 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/parameter/baseRouting.vue b/src/views/planMonitor/editTool/menus/parameter/baseRouting.vue new file mode 100644 index 000000000..ea9e85b70 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/parameter/baseRouting.vue @@ -0,0 +1,53 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/parameter/baseStation.vue b/src/views/planMonitor/editTool/menus/parameter/baseStation.vue new file mode 100644 index 000000000..f57802bd8 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/parameter/baseStation.vue @@ -0,0 +1,44 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/parameter/index.vue b/src/views/planMonitor/editTool/menus/parameter/index.vue new file mode 100644 index 000000000..13bbed1f5 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/parameter/index.vue @@ -0,0 +1,95 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/parameter/modifyingDepotProperty.vue b/src/views/planMonitor/editTool/menus/parameter/modifyingDepotProperty.vue new file mode 100644 index 000000000..6eec7445f --- /dev/null +++ b/src/views/planMonitor/editTool/menus/parameter/modifyingDepotProperty.vue @@ -0,0 +1,73 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/parameter/modifyingStationProperty.vue b/src/views/planMonitor/editTool/menus/parameter/modifyingStationProperty.vue new file mode 100644 index 000000000..b6ffd4132 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/parameter/modifyingStationProperty.vue @@ -0,0 +1,113 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/parameter/modifyingStationStandProperty.vue b/src/views/planMonitor/editTool/menus/parameter/modifyingStationStandProperty.vue new file mode 100644 index 000000000..d06ce5fe6 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/parameter/modifyingStationStandProperty.vue @@ -0,0 +1,73 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/quickLoopsIncrease.vue b/src/views/planMonitor/editTool/menus/quickLoopsIncrease.vue new file mode 100644 index 000000000..6e963ac92 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/quickLoopsIncrease.vue @@ -0,0 +1,45 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/quickTaskIncrease.vue b/src/views/planMonitor/editTool/menus/quickTaskIncrease.vue new file mode 100644 index 000000000..2ac99000f --- /dev/null +++ b/src/views/planMonitor/editTool/menus/quickTaskIncrease.vue @@ -0,0 +1,45 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/routingSelection.vue b/src/views/planMonitor/editTool/menus/routingSelection.vue new file mode 100644 index 000000000..631b5f45d --- /dev/null +++ b/src/views/planMonitor/editTool/menus/routingSelection.vue @@ -0,0 +1,45 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/selectDeleteRoute.vue b/src/views/planMonitor/editTool/menus/selectDeleteRoute.vue new file mode 100644 index 000000000..f557ee6a1 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/selectDeleteRoute.vue @@ -0,0 +1,45 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/selectPrintArea.vue b/src/views/planMonitor/editTool/menus/selectPrintArea.vue new file mode 100644 index 000000000..49d538139 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/selectPrintArea.vue @@ -0,0 +1,45 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/systermOut.vue b/src/views/planMonitor/editTool/menus/systermOut.vue new file mode 100644 index 000000000..0f18a09fe --- /dev/null +++ b/src/views/planMonitor/editTool/menus/systermOut.vue @@ -0,0 +1,62 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/menus/updateStationIntervalTime.vue b/src/views/planMonitor/editTool/menus/updateStationIntervalTime.vue new file mode 100644 index 000000000..959077da7 --- /dev/null +++ b/src/views/planMonitor/editTool/menus/updateStationIntervalTime.vue @@ -0,0 +1,116 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/schedule.vue b/src/views/planMonitor/editTool/schedule.vue new file mode 100644 index 000000000..c3258a9f7 --- /dev/null +++ b/src/views/planMonitor/editTool/schedule.vue @@ -0,0 +1,519 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/statusBar.vue b/src/views/planMonitor/editTool/statusBar.vue new file mode 100644 index 000000000..22c676719 --- /dev/null +++ b/src/views/planMonitor/editTool/statusBar.vue @@ -0,0 +1,100 @@ + + + + + \ No newline at end of file diff --git a/src/views/planMonitor/editTool/titleBar.vue b/src/views/planMonitor/editTool/titleBar.vue new file mode 100644 index 000000000..02c78a8c6 --- /dev/null +++ b/src/views/planMonitor/editTool/titleBar.vue @@ -0,0 +1,74 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/home.vue b/src/views/planMonitor/home.vue new file mode 100644 index 000000000..2d722cc4c --- /dev/null +++ b/src/views/planMonitor/home.vue @@ -0,0 +1,98 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/index.vue b/src/views/planMonitor/index.vue new file mode 100644 index 000000000..5d845668a --- /dev/null +++ b/src/views/planMonitor/index.vue @@ -0,0 +1,77 @@ + + + + \ No newline at end of file diff --git a/src/views/planMonitor/planmanage/planList.vue b/src/views/planMonitor/planmanage/planList.vue new file mode 100644 index 000000000..6a293e19a --- /dev/null +++ b/src/views/planMonitor/planmanage/planList.vue @@ -0,0 +1,132 @@ + + + \ No newline at end of file diff --git a/src/views/publish/examRule/draft/editRule.vue b/src/views/publish/examRule/draft/editRule.vue new file mode 100644 index 000000000..aa80c7fff --- /dev/null +++ b/src/views/publish/examRule/draft/editRule.vue @@ -0,0 +1,301 @@ + + + + \ No newline at end of file diff --git a/src/views/publish/examRule/draft/examFrom.vue b/src/views/publish/examRule/draft/examFrom.vue new file mode 100644 index 000000000..410a9160c --- /dev/null +++ b/src/views/publish/examRule/draft/examFrom.vue @@ -0,0 +1,272 @@ + + + + \ No newline at end of file diff --git a/src/views/publish/examRule/draft/index.vue b/src/views/publish/examRule/draft/index.vue new file mode 100644 index 000000000..0ac28bb6f --- /dev/null +++ b/src/views/publish/examRule/draft/index.vue @@ -0,0 +1,165 @@ + + + + \ No newline at end of file diff --git a/src/views/publish/examRule/draft/rule.vue b/src/views/publish/examRule/draft/rule.vue new file mode 100644 index 000000000..79ee48d61 --- /dev/null +++ b/src/views/publish/examRule/draft/rule.vue @@ -0,0 +1,291 @@ + + + + \ No newline at end of file diff --git a/src/views/publish/examRule/index.vue b/src/views/publish/examRule/index.vue new file mode 100644 index 000000000..d6402beec --- /dev/null +++ b/src/views/publish/examRule/index.vue @@ -0,0 +1,321 @@ + + + + \ No newline at end of file diff --git a/src/views/publish/productStatus/index.vue b/src/views/publish/productStatus/index.vue new file mode 100644 index 000000000..9329321fd --- /dev/null +++ b/src/views/publish/productStatus/index.vue @@ -0,0 +1,180 @@ + + + \ No newline at end of file diff --git a/src/views/publish/publishLesson/index.vue b/src/views/publish/publishLesson/index.vue new file mode 100644 index 000000000..ba099f9d4 --- /dev/null +++ b/src/views/publish/publishLesson/index.vue @@ -0,0 +1,187 @@ + + + \ No newline at end of file diff --git a/src/views/publish/publishMap/draft.vue b/src/views/publish/publishMap/draft.vue new file mode 100644 index 000000000..50db883d1 --- /dev/null +++ b/src/views/publish/publishMap/draft.vue @@ -0,0 +1,160 @@ + + + + + \ No newline at end of file diff --git a/src/views/publish/publishMap/index.vue b/src/views/publish/publishMap/index.vue new file mode 100644 index 000000000..e447daf42 --- /dev/null +++ b/src/views/publish/publishMap/index.vue @@ -0,0 +1,258 @@ + + + \ No newline at end of file diff --git a/src/views/publish/runPlanCommon/chooseTemplatePlan.vue b/src/views/publish/runPlanCommon/chooseTemplatePlan.vue new file mode 100644 index 000000000..877ffbf2e --- /dev/null +++ b/src/views/publish/runPlanCommon/chooseTemplatePlan.vue @@ -0,0 +1,123 @@ + + + + \ No newline at end of file diff --git a/src/views/publish/runPlanCommon/draft.vue b/src/views/publish/runPlanCommon/draft.vue new file mode 100644 index 000000000..e6d961c86 --- /dev/null +++ b/src/views/publish/runPlanCommon/draft.vue @@ -0,0 +1,182 @@ + + + + + \ No newline at end of file diff --git a/src/views/publish/runPlanCommon/index.vue b/src/views/publish/runPlanCommon/index.vue new file mode 100644 index 000000000..69c6f541d --- /dev/null +++ b/src/views/publish/runPlanCommon/index.vue @@ -0,0 +1,121 @@ + + + \ No newline at end of file diff --git a/src/views/publish/runPlanEveryDay/index.vue b/src/views/publish/runPlanEveryDay/index.vue new file mode 100644 index 000000000..6a384eb2c --- /dev/null +++ b/src/views/publish/runPlanEveryDay/index.vue @@ -0,0 +1,139 @@ + + + \ No newline at end of file diff --git a/src/views/publish/runPlanTemplate/index.vue b/src/views/publish/runPlanTemplate/index.vue new file mode 100644 index 000000000..5ccbe7b74 --- /dev/null +++ b/src/views/publish/runPlanTemplate/index.vue @@ -0,0 +1,161 @@ + + + \ No newline at end of file diff --git a/src/views/publish/simulationScript/index.vue b/src/views/publish/simulationScript/index.vue new file mode 100644 index 000000000..eb471e363 --- /dev/null +++ b/src/views/publish/simulationScript/index.vue @@ -0,0 +1,141 @@ + + + \ No newline at end of file diff --git a/src/views/replay/index.vue b/src/views/replay/index.vue new file mode 100644 index 000000000..a3c198692 --- /dev/null +++ b/src/views/replay/index.vue @@ -0,0 +1,146 @@ + + + \ No newline at end of file diff --git a/src/views/screenMonitor/detail/index.vue b/src/views/screenMonitor/detail/index.vue new file mode 100644 index 000000000..c620b7730 --- /dev/null +++ b/src/views/screenMonitor/detail/index.vue @@ -0,0 +1,256 @@ + + + + \ No newline at end of file diff --git a/src/views/screenMonitor/home.vue b/src/views/screenMonitor/home.vue new file mode 100644 index 000000000..19a0fdef2 --- /dev/null +++ b/src/views/screenMonitor/home.vue @@ -0,0 +1,101 @@ + + + + \ No newline at end of file diff --git a/src/views/screenMonitor/index.vue b/src/views/screenMonitor/index.vue new file mode 100644 index 000000000..ea0c921fa --- /dev/null +++ b/src/views/screenMonitor/index.vue @@ -0,0 +1,87 @@ + + + + \ No newline at end of file diff --git a/src/views/screenMonitor/list/demonList.vue b/src/views/screenMonitor/list/demonList.vue new file mode 100644 index 000000000..5547cead4 --- /dev/null +++ b/src/views/screenMonitor/list/demonList.vue @@ -0,0 +1,131 @@ + + + \ No newline at end of file diff --git a/src/views/teach/category/tree.vue b/src/views/teach/category/tree.vue new file mode 100644 index 000000000..3abe96876 --- /dev/null +++ b/src/views/teach/category/tree.vue @@ -0,0 +1,207 @@ + + + + \ No newline at end of file diff --git a/src/views/teach/detail/index.vue b/src/views/teach/detail/index.vue new file mode 100644 index 000000000..0e97d657f --- /dev/null +++ b/src/views/teach/detail/index.vue @@ -0,0 +1,163 @@ + + + + \ No newline at end of file diff --git a/src/views/teach/home.vue b/src/views/teach/home.vue new file mode 100644 index 000000000..429fc8a2e --- /dev/null +++ b/src/views/teach/home.vue @@ -0,0 +1,105 @@ + + + + \ No newline at end of file diff --git a/src/views/teach/index.vue b/src/views/teach/index.vue new file mode 100644 index 000000000..88b1eed6d --- /dev/null +++ b/src/views/teach/index.vue @@ -0,0 +1,83 @@ + + + + \ No newline at end of file diff --git a/src/views/teach/practical/index.vue b/src/views/teach/practical/index.vue new file mode 100644 index 000000000..cb05ca038 --- /dev/null +++ b/src/views/teach/practical/index.vue @@ -0,0 +1,129 @@ + + + + \ No newline at end of file diff --git a/src/views/trainRoom/addPerson.vue b/src/views/trainRoom/addPerson.vue new file mode 100644 index 000000000..f11ca14a8 --- /dev/null +++ b/src/views/trainRoom/addPerson.vue @@ -0,0 +1,158 @@ + + + + + \ No newline at end of file diff --git a/src/views/trainRoom/index.vue b/src/views/trainRoom/index.vue new file mode 100644 index 000000000..0e44aeb1d --- /dev/null +++ b/src/views/trainRoom/index.vue @@ -0,0 +1,846 @@ + + + + + diff --git a/src/views/trainRoom/operateMenu.vue b/src/views/trainRoom/operateMenu.vue new file mode 100644 index 000000000..8ffbd3a13 --- /dev/null +++ b/src/views/trainRoom/operateMenu.vue @@ -0,0 +1,83 @@ + + + \ No newline at end of file diff --git a/src/views/trainRoom/trainChat.vue b/src/views/trainRoom/trainChat.vue new file mode 100644 index 000000000..2ad3cc161 --- /dev/null +++ b/src/views/trainRoom/trainChat.vue @@ -0,0 +1,351 @@ + + + + + \ No newline at end of file