import { createContext } from "react"; import { Emitter } from "@Model/Emitter"; import { Model, ObjectID } from "@Model/Model"; import { Label } from "@Model/Label"; import { Range } from "@Model/Range"; import { Group } from "@Model/Group"; import { Archive } from "@Model/Archive"; import { AbstractRenderer } from "@Model/Renderer"; import { ClassicRenderer, MouseMod } from "@GLRender/ClassicRenderer"; import { Setting } from "@Context/Setting"; import { I18N } from "@Component/Localization/Localization"; import { superConnectWithEvent, superConnect } from "@Context/Context"; import { PopupController } from "@Context/Popups"; import { Behavior } from "@Model/Behavior"; import { IParameter, IParamValue } from "@Model/Parameter"; import { Actuator } from "@Model/Actuator"; import { Clip } from "@Model/Clip"; function randomColor(unNormal: boolean = false) { const color = [ Math.random() * .8 + .2, Math.random() * .8 + .2, Math.random() * .8 + .2, 1 ] if (unNormal) { color[0] = Math.round(color[0] * 255), color[1] = Math.round(color[1] * 255), color[2] = Math.round(color[2] * 255) } return color; } interface IStatusEvent { fileSave: void; fileLoad: void; fileChange: void; renderLoop: number; physicsLoop: number; recordLoop: number; mouseModChange: void; focusObjectChange: void; focusLabelChange: void; focusBehaviorChange: void; objectChange: void; focusClipChange: void; rangeLabelChange: void; groupLabelChange: void; groupBehaviorChange: void; clipChange: void; labelChange: void; rangeAttrChange: void; labelAttrChange: void; groupAttrChange: void; behaviorAttrChange: void; individualChange: void; behaviorChange: void; popupChange: void; actuatorStartChange: void; } class Status extends Emitter { public setting: Setting = undefined as any; /** * 渲染器 */ public renderer: AbstractRenderer = undefined as any; /** * 文件状态 */ public archive: Archive = new Archive(); /** * 模型状态 */ public model: Model = new Model(); /** * 执行器 */ public actuator: Actuator; /** * 弹窗 */ public popup: PopupController = new PopupController(); /** * 焦点对象 */ public focusObject: Set = new Set(); /** * 焦点标签 */ public focusLabel?: Label; /** * 焦点行为 */ public focusBehavior?: Behavior; /** * 焦点行为 */ public focusClip?: Clip; private drawTimer?: NodeJS.Timeout; private delayDraw = () => { this.drawTimer ? clearTimeout(this.drawTimer) : null; this.drawTimer = setTimeout(() => { this.model.draw(); this.drawTimer = undefined; }); } public constructor() { super(); // 初始化执行器 this.actuator = new Actuator(this.model); // 执行器开启事件 this.actuator.on("startChange", () => { this.emit("actuatorStartChange") }); // 循环事件 this.actuator.on("loop", (t) => { this.emit("physicsLoop", t) }); this.actuator.on("record", (t) => { this.emit("recordLoop", t) }); // 对象变化事件 this.model.on("objectChange", () => this.emit("objectChange")); this.model.on("labelChange", () => this.emit("labelChange")); this.model.on("behaviorChange", () => this.emit("behaviorChange")); this.model.on("clipChange", () => this.emit("clipChange")); // 弹窗事件 this.popup.on("popupChange", () => this.emit("popupChange")); // 对象变换时执行渲染,更新渲染器数据 this.model.on("individualChange", () => { this.emit("individualChange"); }); // 渲染器重绘 this.on("objectChange", this.delayDraw); this.on("individualChange", this.delayDraw); this.on("groupAttrChange", this.delayDraw); this.on("rangeAttrChange", this.delayDraw); // 当模型中的标签和对象改变时,更新全部行为参数中的受控对象 const updateBehaviorParameter = () => { this.model.updateBehaviorParameter(); } this.on("objectChange", updateBehaviorParameter); this.on("behaviorChange", updateBehaviorParameter); this.on("labelChange", updateBehaviorParameter); this.on("groupLabelChange", updateBehaviorParameter); this.on("rangeLabelChange", updateBehaviorParameter); this.on("behaviorAttrChange", updateBehaviorParameter); // 映射文件状态改变事件 this.archive.on("fileSave", () => this.emit("fileSave")); // 处理存档加载事件 this.archive.on("fileLoad", () => { // 触发对象修改 this.emit("objectChange"); this.emit("labelChange"); this.emit("behaviorChange"); // 清除焦点对象 this.setBehaviorObject(); this.setFocusObject(new Set()); this.setLabelObject(); // 映射 this.emit("fileLoad"); }); // 处理存档事件 const handelFileChange = () => { if (this.archive.isSaved) { this.emit("fileChange"); } } // 设置文件修改状态 this.on("objectChange", handelFileChange); this.on("behaviorChange", handelFileChange); this.on("labelChange", handelFileChange); this.on("individualChange", handelFileChange); this.on("groupAttrChange", handelFileChange); this.on("rangeAttrChange", handelFileChange); this.on("labelAttrChange", handelFileChange); this.on("behaviorAttrChange", handelFileChange); this.on("fileChange", () => this.archive.emit("fileChange")); } public bindRenderer(renderer: AbstractRenderer) { this.renderer = renderer; this.renderer.on("loop", (t) => { this.emit("renderLoop", t) }); this.model.bindRenderer(this.renderer); } /** * 更新焦点对象 */ public setFocusObject(focusObject: Set) { this.focusObject = focusObject; this.emit("focusObjectChange"); } /** * 更新焦点标签 */ public setLabelObject(focusLabel?: Label) { this.focusLabel = focusLabel; this.emit("focusLabelChange"); } /** * 更新焦点行为 */ public setBehaviorObject(focusBehavior?: Behavior) { this.focusBehavior = focusBehavior; this.emit("focusBehaviorChange"); } /** * 更新焦点行为 */ public setClipObject(clip?: Clip) { if (this.focusClip !== clip) { this.focusClip = clip; } this.emit("focusClipChange"); } /** * 修改范围属性 */ public changeRangeAttrib (id: ObjectID, key: K, val: Range[K]) { const range = this.model.getObjectById(id); if (range && range instanceof Range) { range[key] = val; this.emit("rangeAttrChange"); } } /** * 修改群属性 */ public changeGroupAttrib (id: ObjectID, key: K, val: Group[K]) { const group = this.model.getObjectById(id); if (group && group instanceof Group) { group[key] = val; this.emit("groupAttrChange"); } } /** * 修改群属性 */ public changeBehaviorAttrib> (id: ObjectID, key: P, val: IParamValue, noParameter?: boolean) { const behavior = this.model.getBehaviorById(id); if (behavior) { if (noParameter) { behavior[key as keyof Behavior] = val as never; } else { behavior.parameter[key] = val; behavior.parameterOption[key]?.onChange ? behavior.parameterOption[key]?.onChange!(val) : undefined; } this.emit("behaviorAttrChange"); } } public addGroupBehavior(id: ObjectID, val: Behavior) { const group = this.model.getObjectById(id); if (group && group instanceof Group) { group.addBehavior(val); this.emit("groupBehaviorChange"); } } public deleteGroupBehavior(id: ObjectID, val: Behavior) { const group = this.model.getObjectById(id); if (group && group instanceof Group) { group.deleteBehavior(val); this.emit("groupBehaviorChange"); } } public addGroupLabel(id: ObjectID, val: Label) { const group = this.model.getObjectById(id); if (group && group instanceof Group) { group.addLabel(val); this.emit("groupLabelChange"); } } public deleteGroupLabel(id: ObjectID, val: Label) { const group = this.model.getObjectById(id); if (group && group instanceof Group) { group.removeLabel(val); this.emit("groupLabelChange"); } } public addRangeLabel(id: ObjectID, val: Label) { const range = this.model.getObjectById(id); if (range && range instanceof Range) { range.addLabel(val); this.emit("rangeLabelChange"); } } public deleteRangeLabel(id: ObjectID, val: Label) { const range = this.model.getObjectById(id); if (range && range instanceof Range) { range.removeLabel(val); this.emit("rangeLabelChange"); } } /** * 修改范围属性 */ public changeLabelAttrib (label: Label, key: K, val: Label[K]) { let findLabel: Label | undefined; for (let i = 0; i < this.model.labelPool.length; i++) { if (this.model.labelPool[i].equal(label)) { findLabel = this.model.labelPool[i]; break; } } if (findLabel) { findLabel[key] = val; this.emit("labelAttrChange"); } } /** * 鼠标工具状态 */ public mouseMod: MouseMod = MouseMod.Drag; private readonly SEARCH_NAME_KEY_REG = /(\d+)$/; private getNextNumber(name: string, searchKey: string): number { if (name.indexOf(searchKey) < 0) return 1; let searchRes = name.match(this.SEARCH_NAME_KEY_REG); if (searchRes) { let nameNumber = parseInt(searchRes[1]); if (isNaN(nameNumber)) { return 1; } else { return nameNumber + 1; } } else { return 1; } } public newGroup() { const group = this.model.addGroup(); group.color = randomColor(); let searchKey = I18N(this.setting.language, "Object.List.New.Group", { id: "" }); let nextIndex = 1; this.model.objectPool.forEach((obj) => { if (obj instanceof Group) { nextIndex = Math.max(nextIndex, this.getNextNumber( obj.displayName, searchKey )); } }); group.displayName = I18N(this.setting.language, "Object.List.New.Group", { id: nextIndex.toString() }); return group; } public newRange() { const range = this.model.addRange(); range.color = randomColor(); let searchKey = I18N(this.setting.language, "Object.List.New.Range", { id: "" }); let nextIndex = 1; this.model.objectPool.forEach((obj) => { if (obj instanceof Range) { nextIndex = Math.max(nextIndex, this.getNextNumber( obj.displayName, searchKey )); } }); range.displayName = I18N(this.setting.language, "Object.List.New.Range", { id: nextIndex.toString() }); return range; } public newLabel() { let searchKey = I18N(this.setting.language, "Object.List.New.Label", { id: "" }); let nextIndex = 1; this.model.labelPool.forEach((obj) => { nextIndex = Math.max(nextIndex, this.getNextNumber( obj.name, searchKey )); }); const label = this.model.addLabel( I18N(this.setting.language, "Object.List.New.Label", { id: nextIndex.toString() }) ); label.color = randomColor(true); return label; } public setMouseMod(mod: MouseMod) { this.mouseMod = mod; if (this.renderer instanceof ClassicRenderer) { this.renderer.mouseMod = mod; this.renderer.setMouseIcon(); } this.emit("mouseModChange"); } } interface IMixinStatusProps { status?: Status; } const StatusContext = createContext(new Status()); StatusContext.displayName = "Status"; const StatusProvider = StatusContext.Provider; const StatusConsumer = StatusContext.Consumer; /** * 修饰器 */ const useStatus = superConnect(StatusConsumer, "status"); const useStatusWithEvent = superConnectWithEvent(StatusConsumer, "status"); export { Status, StatusContext, useStatus, useStatusWithEvent, randomColor, IMixinStatusProps, StatusProvider, StatusConsumer };