Compare commits
No commits in common. "8ede30466425470bc967e91f283416ee3e3c81f8" and "7adadb6d46b7220f716aed1b6ff24ebc086ce719" have entirely different histories.
8ede304664
...
7adadb6d46
27
package-lock.json
generated
27
package-lock.json
generated
@ -11,13 +11,11 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fluentui/react": "^8.56.0",
|
"@fluentui/react": "^8.56.0",
|
||||||
"@juggle/resize-observer": "^3.3.1",
|
"@juggle/resize-observer": "^3.3.1",
|
||||||
"chart.js": "^3.7.1",
|
|
||||||
"detect-port": "^1.3.0",
|
"detect-port": "^1.3.0",
|
||||||
"downloadjs": "^1.4.7",
|
"downloadjs": "^1.4.7",
|
||||||
"express": "^4.17.3",
|
"express": "^4.17.3",
|
||||||
"gl-matrix": "^3.4.3",
|
"gl-matrix": "^3.4.3",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-chartjs-2": "^4.1.0",
|
|
||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
"react-dnd-html5-backend": "^16.0.1",
|
"react-dnd-html5-backend": "^16.0.1",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
@ -2202,11 +2200,6 @@
|
|||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/chart.js": {
|
|
||||||
"version": "3.7.1",
|
|
||||||
"resolved": "https://registry.npmmirror.com/chart.js/-/chart.js-3.7.1.tgz",
|
|
||||||
"integrity": "sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA=="
|
|
||||||
},
|
|
||||||
"node_modules/chokidar": {
|
"node_modules/chokidar": {
|
||||||
"version": "3.5.3",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||||
@ -6217,15 +6210,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-chartjs-2": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmmirror.com/react-chartjs-2/-/react-chartjs-2-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-AsUihxEp8Jm1oBhbEovE+w50m9PVNhz1sfwEIT4hZduRC0m14gHWHd0cUaxkFDb8HNkdMIGzsNlmVqKiOpU74g==",
|
|
||||||
"peerDependencies": {
|
|
||||||
"chart.js": "^3.5.0",
|
|
||||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-dnd": {
|
"node_modules/react-dnd": {
|
||||||
"version": "16.0.1",
|
"version": "16.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/react-dnd/-/react-dnd-16.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/react-dnd/-/react-dnd-16.0.1.tgz",
|
||||||
@ -10129,11 +10113,6 @@
|
|||||||
"supports-color": "^7.1.0"
|
"supports-color": "^7.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"chart.js": {
|
|
||||||
"version": "3.7.1",
|
|
||||||
"resolved": "https://registry.npmmirror.com/chart.js/-/chart.js-3.7.1.tgz",
|
|
||||||
"integrity": "sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA=="
|
|
||||||
},
|
|
||||||
"chokidar": {
|
"chokidar": {
|
||||||
"version": "3.5.3",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||||
@ -13221,12 +13200,6 @@
|
|||||||
"object-assign": "^4.1.1"
|
"object-assign": "^4.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-chartjs-2": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmmirror.com/react-chartjs-2/-/react-chartjs-2-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-AsUihxEp8Jm1oBhbEovE+w50m9PVNhz1sfwEIT4hZduRC0m14gHWHd0cUaxkFDb8HNkdMIGzsNlmVqKiOpU74g==",
|
|
||||||
"requires": {}
|
|
||||||
},
|
|
||||||
"react-dnd": {
|
"react-dnd": {
|
||||||
"version": "16.0.1",
|
"version": "16.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/react-dnd/-/react-dnd-16.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/react-dnd/-/react-dnd-16.0.1.tgz",
|
||||||
|
@ -71,13 +71,11 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fluentui/react": "^8.56.0",
|
"@fluentui/react": "^8.56.0",
|
||||||
"@juggle/resize-observer": "^3.3.1",
|
"@juggle/resize-observer": "^3.3.1",
|
||||||
"chart.js": "^3.7.1",
|
|
||||||
"detect-port": "^1.3.0",
|
"detect-port": "^1.3.0",
|
||||||
"downloadjs": "^1.4.7",
|
"downloadjs": "^1.4.7",
|
||||||
"express": "^4.17.3",
|
"express": "^4.17.3",
|
||||||
"gl-matrix": "^3.4.3",
|
"gl-matrix": "^3.4.3",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-chartjs-2": "^4.1.0",
|
|
||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
"react-dnd-html5-backend": "^16.0.1",
|
"react-dnd-html5-backend": "^16.0.1",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
@ -44,11 +44,6 @@ class Setting extends Emitter<ISettingEvents> {
|
|||||||
*/
|
*/
|
||||||
public layout: Layout = new Layout();
|
public layout: Layout = new Layout();
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否显示线性图表
|
|
||||||
*/
|
|
||||||
public lineChartType: boolean = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置参数
|
* 设置参数
|
||||||
*/
|
*/
|
||||||
|
@ -38,7 +38,6 @@ interface IStatusEvent {
|
|||||||
physicsLoop: number;
|
physicsLoop: number;
|
||||||
recordLoop: number;
|
recordLoop: number;
|
||||||
offlineLoop: number;
|
offlineLoop: number;
|
||||||
modelUpdate: void;
|
|
||||||
mouseModChange: void;
|
mouseModChange: void;
|
||||||
focusObjectChange: void;
|
focusObjectChange: void;
|
||||||
focusLabelChange: void;
|
focusLabelChange: void;
|
||||||
@ -133,7 +132,6 @@ class Status extends Emitter<IStatusEvent> {
|
|||||||
this.actuator.on("loop", (t) => { this.emit("physicsLoop", t) });
|
this.actuator.on("loop", (t) => { this.emit("physicsLoop", t) });
|
||||||
this.actuator.on("record", (t) => { this.emit("recordLoop", t) });
|
this.actuator.on("record", (t) => { this.emit("recordLoop", t) });
|
||||||
this.actuator.on("offline", (t) => { this.emit("offlineLoop", t) });
|
this.actuator.on("offline", (t) => { this.emit("offlineLoop", t) });
|
||||||
this.actuator.on("modelUpdate", () => { this.emit("modelUpdate") });
|
|
||||||
|
|
||||||
// 对象变化事件
|
// 对象变化事件
|
||||||
this.model.on("objectChange", () => this.emit("objectChange"));
|
this.model.on("objectChange", () => this.emit("objectChange"));
|
||||||
|
@ -62,8 +62,6 @@ const EN_US = {
|
|||||||
"Panel.Info.Behavior.Clip.Player": "Pre render recorded data",
|
"Panel.Info.Behavior.Clip.Player": "Pre render recorded data",
|
||||||
"Panel.Title.Behavior.Clip.Details": "Clip",
|
"Panel.Title.Behavior.Clip.Details": "Clip",
|
||||||
"Panel.Info.Behavior.Clip.Details": "Edit view clip attributes",
|
"Panel.Info.Behavior.Clip.Details": "Edit view clip attributes",
|
||||||
"Panel.Info.Statistics": "View statistics",
|
|
||||||
"Panel.Title.Statistics": "Statistics",
|
|
||||||
"Panel.Info.Behavior.Clip.Time.Formate": "{current} / {all} / {fps}fps",
|
"Panel.Info.Behavior.Clip.Time.Formate": "{current} / {all} / {fps}fps",
|
||||||
"Panel.Info.Behavior.Clip.Record.Formate": "Record: {time}",
|
"Panel.Info.Behavior.Clip.Record.Formate": "Record: {time}",
|
||||||
"Panel.Info.Behavior.Clip.Uname.Clip": "Waiting for recording...",
|
"Panel.Info.Behavior.Clip.Uname.Clip": "Waiting for recording...",
|
||||||
@ -170,7 +168,6 @@ const EN_US = {
|
|||||||
"Panel.Info.Behavior.Details.Parameter.Key.Vec.Z": "{key} Z",
|
"Panel.Info.Behavior.Details.Parameter.Key.Vec.Z": "{key} Z",
|
||||||
"Panel.Info.Clip.List.Error.Nodata": "There is no clip, please click the record button to record, or click the plus sign to create",
|
"Panel.Info.Clip.List.Error.Nodata": "There is no clip, please click the record button to record, or click the plus sign to create",
|
||||||
"Panel.Info.Clip.Details.Error.Nodata": "Specify a clip to view an attribute",
|
"Panel.Info.Clip.Details.Error.Nodata": "Specify a clip to view an attribute",
|
||||||
"Panel.Info.Statistics.Nodata": "There are no groups in the model or clip",
|
|
||||||
"Info.Hint.Save.After.Close": "Any unsaved progress will be lost. Are you sure you want to continue?",
|
"Info.Hint.Save.After.Close": "Any unsaved progress will be lost. Are you sure you want to continue?",
|
||||||
"Info.Hint.Load.File.Title": "Load save",
|
"Info.Hint.Load.File.Title": "Load save",
|
||||||
"Info.Hint.Load.File.Intro": "Release to load the dragged save file",
|
"Info.Hint.Load.File.Intro": "Release to load the dragged save file",
|
||||||
|
@ -26,10 +26,10 @@ const ZH_CN = {
|
|||||||
"Command.Bar.Camera.Info": "渲染器设置",
|
"Command.Bar.Camera.Info": "渲染器设置",
|
||||||
"Command.Bar.Setting.Info": "全局设置",
|
"Command.Bar.Setting.Info": "全局设置",
|
||||||
"Input.Error.Not.Number": "请输入数字",
|
"Input.Error.Not.Number": "请输入数字",
|
||||||
"Input.Error.Max": "输入数值须小于 {num}",
|
"Input.Error.Max": "输入数值须小于 {number}",
|
||||||
"Input.Error.Min": "输入数值须大于 {num}",
|
"Input.Error.Min": "输入数值须大于 {number}",
|
||||||
"Input.Error.Length": "输入内容长度须小于 {num}",
|
"Input.Error.Length": "输入内容长度须小于 {number}",
|
||||||
"Input.Error.Length.Less": "输入内容长度须大于 {num}",
|
"Input.Error.Length.Less": "输入内容长度须大于 {number}",
|
||||||
"Input.Error.Select": "选择对象 ...",
|
"Input.Error.Select": "选择对象 ...",
|
||||||
"Input.Error.Combo": "选择选项 ...",
|
"Input.Error.Combo": "选择选项 ...",
|
||||||
"Object.List.New.Group": "群对象 {id}",
|
"Object.List.New.Group": "群对象 {id}",
|
||||||
@ -62,8 +62,6 @@ const ZH_CN = {
|
|||||||
"Panel.Info.Behavior.Clip.Player": "预渲染录制数据",
|
"Panel.Info.Behavior.Clip.Player": "预渲染录制数据",
|
||||||
"Panel.Title.Behavior.Clip.Details": "剪辑",
|
"Panel.Title.Behavior.Clip.Details": "剪辑",
|
||||||
"Panel.Info.Behavior.Clip.Details": "编辑查看剪辑片段属性",
|
"Panel.Info.Behavior.Clip.Details": "编辑查看剪辑片段属性",
|
||||||
"Panel.Info.Statistics": "查看统计信息",
|
|
||||||
"Panel.Title.Statistics": "统计",
|
|
||||||
"Panel.Info.Behavior.Clip.Time.Formate": "{current} / {all} / {fps} fps",
|
"Panel.Info.Behavior.Clip.Time.Formate": "{current} / {all} / {fps} fps",
|
||||||
"Panel.Info.Behavior.Clip.Record.Formate": "录制: {time}",
|
"Panel.Info.Behavior.Clip.Record.Formate": "录制: {time}",
|
||||||
"Panel.Info.Behavior.Clip.Uname.Clip": "等待录制...",
|
"Panel.Info.Behavior.Clip.Uname.Clip": "等待录制...",
|
||||||
@ -170,7 +168,6 @@ const ZH_CN = {
|
|||||||
"Panel.Info.Behavior.Details.Parameter.Key.Vec.Z": "{key} Z 坐标",
|
"Panel.Info.Behavior.Details.Parameter.Key.Vec.Z": "{key} Z 坐标",
|
||||||
"Panel.Info.Clip.List.Error.Nodata": "没有剪辑片段,请点击录制按钮录制,或者点击加号创建",
|
"Panel.Info.Clip.List.Error.Nodata": "没有剪辑片段,请点击录制按钮录制,或者点击加号创建",
|
||||||
"Panel.Info.Clip.Details.Error.Nodata": "请指定一个剪辑片段以查看属性",
|
"Panel.Info.Clip.Details.Error.Nodata": "请指定一个剪辑片段以查看属性",
|
||||||
"Panel.Info.Statistics.Nodata": "模型或剪辑中不存在任何群",
|
|
||||||
"Info.Hint.Save.After.Close": "任何未保存的进度都会丢失, 确定要继续吗?",
|
"Info.Hint.Save.After.Close": "任何未保存的进度都会丢失, 确定要继续吗?",
|
||||||
"Info.Hint.Load.File.Title": "加载存档",
|
"Info.Hint.Load.File.Title": "加载存档",
|
||||||
"Info.Hint.Load.File.Intro": "释放以加载拽入的存档",
|
"Info.Hint.Load.File.Intro": "释放以加载拽入的存档",
|
||||||
|
@ -14,7 +14,6 @@ interface IActuatorEvent {
|
|||||||
record: number;
|
record: number;
|
||||||
loop: number;
|
loop: number;
|
||||||
offline: number;
|
offline: number;
|
||||||
modelUpdate: void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -402,7 +401,6 @@ class Actuator extends Emitter<IActuatorEvent> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.emit("loop", this.alignTimer);
|
this.emit("loop", this.alignTimer);
|
||||||
this.emit("modelUpdate");
|
|
||||||
this.alignTimer = 0;
|
this.alignTimer = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ class SimulatorDesktop extends Component {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
items: [{
|
items: [{
|
||||||
panels: ["ObjectList", "Statistics"]
|
panels: ["ObjectList"]
|
||||||
}, {
|
}, {
|
||||||
panels: ["GroupDetails", "RangeDetails", "LabelDetails", "BehaviorDetails", "ClipDetails"]
|
panels: ["GroupDetails", "RangeDetails", "LabelDetails", "BehaviorDetails", "ClipDetails"]
|
||||||
}],
|
}],
|
||||||
|
@ -65,7 +65,7 @@ class SimulatorWeb extends Component {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
items: [{
|
items: [{
|
||||||
panels: ["ObjectList", "Statistics"]
|
panels: ["ObjectList"]
|
||||||
}, {
|
}, {
|
||||||
panels: ["GroupDetails", "RangeDetails", "LabelDetails", "BehaviorDetails", "ClipDetails"]
|
panels: ["GroupDetails", "RangeDetails", "LabelDetails", "BehaviorDetails", "ClipDetails"]
|
||||||
}],
|
}],
|
||||||
|
@ -13,7 +13,6 @@ import { BehaviorDetails } from "@Panel/BehaviorDetails/BehaviorDetails";
|
|||||||
import { ClipPlayer } from "@Panel/ClipPlayer/ClipPlayer";
|
import { ClipPlayer } from "@Panel/ClipPlayer/ClipPlayer";
|
||||||
import { ClipRecorder } from "@Panel/ClipPlayer/ClipRecorder";
|
import { ClipRecorder } from "@Panel/ClipPlayer/ClipRecorder";
|
||||||
import { ClipDetails } from "@Panel/ClipDetails/ClipDetails";
|
import { ClipDetails } from "@Panel/ClipDetails/ClipDetails";
|
||||||
import { Statistics } from "@Panel/Statistics/Statistics";
|
|
||||||
|
|
||||||
interface IPanelInfo {
|
interface IPanelInfo {
|
||||||
nameKey: string;
|
nameKey: string;
|
||||||
@ -37,7 +36,6 @@ type PanelId = ""
|
|||||||
| "BehaviorDetails" // 行为属性
|
| "BehaviorDetails" // 行为属性
|
||||||
| "ClipPlayer" // 剪辑影片
|
| "ClipPlayer" // 剪辑影片
|
||||||
| "ClipDetails" // 剪辑详情
|
| "ClipDetails" // 剪辑详情
|
||||||
| "Statistics" // 统计信息
|
|
||||||
;
|
;
|
||||||
|
|
||||||
const PanelInfoMap = new Map<PanelId, IPanelInfo>();
|
const PanelInfoMap = new Map<PanelId, IPanelInfo>();
|
||||||
@ -81,10 +79,6 @@ PanelInfoMap.set("ClipDetails", {
|
|||||||
nameKey: "Panel.Title.Behavior.Clip.Details", introKay: "Panel.Info.Behavior.Clip.Details",
|
nameKey: "Panel.Title.Behavior.Clip.Details", introKay: "Panel.Info.Behavior.Clip.Details",
|
||||||
class: ClipDetails
|
class: ClipDetails
|
||||||
});
|
});
|
||||||
PanelInfoMap.set("Statistics", {
|
|
||||||
nameKey: "Panel.Title.Statistics", introKay: "Panel.Info.Statistics",
|
|
||||||
class: Statistics
|
|
||||||
});
|
|
||||||
|
|
||||||
function getPanelById(panelId: PanelId): ReactNode {
|
function getPanelById(panelId: PanelId): ReactNode {
|
||||||
switch (panelId) {
|
switch (panelId) {
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
@import "../../Component/Theme/Theme.scss";
|
|
||||||
|
|
||||||
div.statistics-panel {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
min-height: 100%;
|
|
||||||
|
|
||||||
div.statistics-chart {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding-top: 10px;
|
|
||||||
max-width: 400px;
|
|
||||||
min-height: 100%;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.statistics-switch {
|
|
||||||
width: 100%;
|
|
||||||
height: 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
|
|
||||||
div.switch-button {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
position: relative;
|
|
||||||
user-select: none;
|
|
||||||
right: -10px;
|
|
||||||
top: -2px;
|
|
||||||
border-radius: 3px;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
div.statistics-panel.dark {
|
|
||||||
|
|
||||||
div.switch-button {
|
|
||||||
background-color: $lt-bg-color-lvl2-dark;
|
|
||||||
color: $lt-font-color-lvl2-dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.switch-button:hover {
|
|
||||||
background-color: $lt-bg-color-lvl1-dark;
|
|
||||||
color: $lt-font-color-lvl1-dark;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
div.statistics-panel.light {
|
|
||||||
|
|
||||||
div.switch-button {
|
|
||||||
background-color: $lt-bg-color-lvl2-light;
|
|
||||||
color: $lt-font-color-lvl2-light;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.switch-button:hover {
|
|
||||||
background-color: $lt-bg-color-lvl1-light;
|
|
||||||
color: $lt-font-color-lvl1-light;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,303 +0,0 @@
|
|||||||
import { Component, ReactNode } from "react";
|
|
||||||
import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status";
|
|
||||||
import { useSettingWithEvent, IMixinSettingProps, Themes } from "@Context/Setting";
|
|
||||||
import {
|
|
||||||
Chart as ChartJS, CategoryScale, LinearScale,
|
|
||||||
BarElement, Tooltip, Legend, Decimation,
|
|
||||||
PointElement, LineElement, Title
|
|
||||||
} from 'chart.js';
|
|
||||||
import { Bar, Line } from 'react-chartjs-2';
|
|
||||||
import { Theme } from "@Component/Theme/Theme";
|
|
||||||
import { Icon } from "@fluentui/react";
|
|
||||||
import { IAnyObject, Model } from "@Model/Model";
|
|
||||||
import { Group } from "@Model/Group";
|
|
||||||
import { ActuatorModel } from "@Model/Actuator";
|
|
||||||
import { Message } from "@Input/Message/Message";
|
|
||||||
import { Clip, IFrame } from "@Model/Clip";
|
|
||||||
import "./Statistics.scss";
|
|
||||||
|
|
||||||
ChartJS.register(
|
|
||||||
CategoryScale,
|
|
||||||
LinearScale,
|
|
||||||
BarElement,
|
|
||||||
Tooltip,
|
|
||||||
Legend,
|
|
||||||
PointElement,
|
|
||||||
LineElement,
|
|
||||||
Title,
|
|
||||||
Decimation
|
|
||||||
);
|
|
||||||
|
|
||||||
interface IStatisticsProps {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@useSettingWithEvent("themes", "language", "lineChartType")
|
|
||||||
@useStatusWithEvent("focusClipChange", "actuatorStartChange", "fileLoad", "modelUpdate", "recordLoop", "individualChange")
|
|
||||||
class Statistics extends Component<IStatisticsProps & IMixinStatusProps & IMixinSettingProps> {
|
|
||||||
|
|
||||||
public barDarkOption = {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
plugins: { legend: {
|
|
||||||
position: 'bottom' as const,
|
|
||||||
labels: { boxWidth: 10, boxHeight: 10, color: 'rgba(255, 255, 255, .5)' }
|
|
||||||
}},
|
|
||||||
scales: {
|
|
||||||
x: { grid: { color: 'rgba(255, 255, 255, .2)' }, title: { color: 'rgba(255, 255, 255, .5)'} },
|
|
||||||
y: { grid: { color: 'rgba(255, 255, 255, .2)', borderDash: [3, 3] }, title: { color: 'rgba(255, 255, 255, .5)'} }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public barLightOption = {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
plugins: { legend: {
|
|
||||||
position: 'bottom' as const,
|
|
||||||
labels: { boxWidth: 10, boxHeight: 10, color: 'rgba(0, 0, 0, .5)' }
|
|
||||||
}},
|
|
||||||
scales: {
|
|
||||||
x: { grid: { color: 'rgba(0, 0, 0, .2)' }, title: { color: 'rgba(0, 0, 0, .5)'} },
|
|
||||||
y: { grid: { color: 'rgba(0, 0, 0, .2)', borderDash: [3, 3] }, title: { color: 'rgba(0, 0, 0, .5)'} }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private modelBarChart(model: Model, theme: boolean) {
|
|
||||||
|
|
||||||
const datasets: any[] = [];
|
|
||||||
const labels: any[] = ["Group"];
|
|
||||||
|
|
||||||
// 收集数据
|
|
||||||
model.objectPool.forEach((obj) => {
|
|
||||||
let label = obj.displayName;
|
|
||||||
let color = `rgb(${obj.color.map((v) => Math.floor(v * 255)).join(",")})`;
|
|
||||||
|
|
||||||
if (obj instanceof Group) {
|
|
||||||
datasets.push({label, data: [obj.individuals.size], backgroundColor: color});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (datasets.length <= 0) {
|
|
||||||
return <Message i18nKey="Panel.Info.Statistics.Nodata"/>
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Bar
|
|
||||||
data={{datasets, labels}}
|
|
||||||
options={theme ? this.barLightOption : this.barDarkOption }
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
private clipBarChart(frame: IFrame, theme: boolean) {
|
|
||||||
|
|
||||||
const datasets: any[] = [];
|
|
||||||
const labels: any[] = ["Group"];
|
|
||||||
|
|
||||||
// 收集数据
|
|
||||||
frame.commands.forEach((command) => {
|
|
||||||
let label = command.name;
|
|
||||||
let color = `rgb(${command.parameter?.color.map((v: number) => Math.floor(v * 255)).join(",")})`;
|
|
||||||
|
|
||||||
if (command.type === "points") {
|
|
||||||
datasets.push({label, data: [(command.data?.length ?? 0) / 3], backgroundColor: color});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (datasets.length <= 0) {
|
|
||||||
return <Message i18nKey="Panel.Info.Statistics.Nodata"/>
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Bar
|
|
||||||
data={{datasets, labels}}
|
|
||||||
options={ theme ? this.barLightOption : this.barDarkOption }
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
public lineDarkOption = {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
position: 'bottom' as const,
|
|
||||||
labels: { boxWidth: 10, boxHeight: 10, color: 'rgba(255, 255, 255, .5)' },
|
|
||||||
decimation: { enabled: true }
|
|
||||||
},
|
|
||||||
decimation: { enabled: true, algorithm: "lttb" as const, samples: 100 }
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
x: { grid: { color: 'rgba(255, 255, 255, .2)' }, type: "linear", title: { color: 'rgba(255, 255, 255, .5)'} },
|
|
||||||
y: { grid: { color: 'rgba(255, 255, 255, .2)', borderDash: [3, 3] }, title: { color: 'rgba(255, 255, 255, .5)'} }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public lineLightOption = {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
position: 'bottom' as const,
|
|
||||||
labels: { boxWidth: 10, boxHeight: 10, color: 'rgba(0, 0, 0, .5)' },
|
|
||||||
},
|
|
||||||
decimation: { enabled: true, algorithm: "lttb" as const, samples: 100 }
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
x: { grid: { color: 'rgba(0, 0, 0, .2)' }, title: { color: 'rgba(0, 0, 0, .5)'} },
|
|
||||||
y: { grid: { color: 'rgba(0, 0, 0, .2)', borderDash: [3, 3] }, title: { color: 'rgba(0, 0, 0, .5)'} }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private clipLineChart(clip: Clip, theme: boolean) {
|
|
||||||
|
|
||||||
type IDataSet = {label: string, data: number[], id: string} & IAnyObject;
|
|
||||||
const datasets: IDataSet[] = [];
|
|
||||||
const labels: number[] = [];
|
|
||||||
let frameLen: number = 0;
|
|
||||||
let lastDataSet: Map<string, number> | undefined;
|
|
||||||
let lastProcess: number | undefined;
|
|
||||||
|
|
||||||
// 收集数据
|
|
||||||
clip.frames.forEach((frame) => {
|
|
||||||
|
|
||||||
const frameData = new Map<string, number>();
|
|
||||||
|
|
||||||
frame.commands.forEach((command) => {
|
|
||||||
|
|
||||||
if (command.type !== "points") return;
|
|
||||||
|
|
||||||
let findKey: IDataSet | undefined;
|
|
||||||
for (let i = 0; i < datasets.length; i++) {
|
|
||||||
if (datasets[i].id === command.id) {
|
|
||||||
findKey = datasets[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记录当前数据
|
|
||||||
frameData.set(command.id, (command.data?.length ?? 0) / 3);
|
|
||||||
|
|
||||||
// 新建数据
|
|
||||||
if (!findKey) {
|
|
||||||
|
|
||||||
const color = `rgb(${command.parameter?.color.map((v: number) => Math.floor(v * 255)).join(",")})`;
|
|
||||||
|
|
||||||
findKey = {} as any;
|
|
||||||
findKey!.label = command.name ?? "";
|
|
||||||
findKey!.backgroundColor = color;
|
|
||||||
findKey!.borderColor = color;
|
|
||||||
findKey!.id = command.id;
|
|
||||||
findKey!.pointRadius = 0;
|
|
||||||
findKey!.borderWidth = 1.5;
|
|
||||||
findKey!.borderCapStyle = "round";
|
|
||||||
findKey!.borderJoinStyle = "round";
|
|
||||||
findKey!.pointHitRadius = 8;
|
|
||||||
|
|
||||||
// 补充数据
|
|
||||||
findKey!.data = new Array(frameLen).fill(0);
|
|
||||||
|
|
||||||
datasets.push(findKey!);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 与上一帧数据进行对比
|
|
||||||
const isSameData = datasets.every((value: IDataSet) => {
|
|
||||||
if (value.data[frameLen - 1] === frameData.get(value.id)) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
lastDataSet = frameData;
|
|
||||||
lastProcess = frame.process;
|
|
||||||
|
|
||||||
// 如果是不同数据 纪录
|
|
||||||
if (!isSameData) {
|
|
||||||
datasets.forEach((value: IDataSet) => {
|
|
||||||
value.data.push(frameData.get(value.id) ?? 0);
|
|
||||||
});
|
|
||||||
frameLen ++;
|
|
||||||
labels.push(frame.process);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 记录最后一帧数据
|
|
||||||
if (lastDataSet && lastProcess !== labels[labels.length - 1]) {
|
|
||||||
datasets.forEach((value: IDataSet) => {
|
|
||||||
value.data.push(lastDataSet!.get(value.id) ?? 0);
|
|
||||||
});
|
|
||||||
frameLen ++;
|
|
||||||
labels.push(lastProcess!);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (datasets.length <= 0) {
|
|
||||||
return <Message i18nKey="Panel.Info.Statistics.Nodata"/>
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Line
|
|
||||||
options={ theme ? this.lineLightOption : this.lineDarkOption }
|
|
||||||
data={{labels, datasets }}
|
|
||||||
/>;
|
|
||||||
}
|
|
||||||
|
|
||||||
private renderChart() {
|
|
||||||
|
|
||||||
let themes = this.props.setting?.themes === Themes.light;
|
|
||||||
let lineChartType = this.props.setting?.lineChartType;
|
|
||||||
|
|
||||||
// 播放模式
|
|
||||||
if (this.props.status?.focusClip) {
|
|
||||||
if (this.props.status.actuator.playClip && lineChartType) {
|
|
||||||
return this.clipLineChart(this.props.status.actuator.playClip, themes);
|
|
||||||
}
|
|
||||||
if (this.props.status.actuator.playFrame) {
|
|
||||||
return this.clipBarChart(this.props.status.actuator.playFrame, themes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 正在录制中
|
|
||||||
else if (
|
|
||||||
this.props.status?.actuator.mod === ActuatorModel.Record ||
|
|
||||||
this.props.status?.actuator.mod === ActuatorModel.Offline
|
|
||||||
) {
|
|
||||||
if (this.props.status.actuator.recordClip && lineChartType) {
|
|
||||||
return this.clipLineChart(this.props.status.actuator.recordClip, themes);
|
|
||||||
}
|
|
||||||
return this.modelBarChart(this.props.status.model, themes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 主时钟运行
|
|
||||||
else if (this.props.status) {
|
|
||||||
return this.modelBarChart(this.props.status.model, themes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(): ReactNode {
|
|
||||||
return <Theme className="statistics-panel">
|
|
||||||
|
|
||||||
{
|
|
||||||
(
|
|
||||||
this.props.status?.focusClip ||
|
|
||||||
this.props.status?.actuator.mod === ActuatorModel.Record ||
|
|
||||||
this.props.status?.actuator.mod === ActuatorModel.Offline
|
|
||||||
) ?
|
|
||||||
|
|
||||||
<div className="statistics-switch">
|
|
||||||
<div className="switch-button" onClick={() => {
|
|
||||||
this.props.setting?.setProps("lineChartType", !this.props.setting?.lineChartType);
|
|
||||||
}}>
|
|
||||||
<Icon iconName={
|
|
||||||
this.props.setting?.lineChartType ? "BarChartVertical" : "LineChart"
|
|
||||||
}/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
|
|
||||||
<div className="statistics-chart">
|
|
||||||
{ this.renderChart() }
|
|
||||||
</div>
|
|
||||||
</Theme>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { Statistics };
|
|
Loading…
Reference in New Issue
Block a user