Compare commits
	
		
			No commits in common. "6c23ce62ffcee193c8b4eab58d8a7f268278374f" and "c86ff9ef1a2b8cf7e89c892962e8b487aef9f35b" have entirely different histories.
		
	
	
		
			6c23ce62ff
			...
			c86ff9ef1a
		
	
		
| @ -53,7 +53,6 @@ interface IStatusEvent { | ||||
|     labelAttrChange: void; | ||||
|     groupAttrChange: void; | ||||
|     behaviorAttrChange: void; | ||||
|     clipAttrChange: void; | ||||
|     individualChange: void; | ||||
|     behaviorChange: void; | ||||
|     popupChange: void; | ||||
| @ -287,18 +286,6 @@ class Status extends Emitter<IStatusEvent> { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 修改剪辑属性 | ||||
|      */ | ||||
|     public changeClipAttrib<K extends keyof Clip> | ||||
|     (id: ObjectID, key: K, val: Clip[K]) { | ||||
|         const clip = this.model.getClipById(id); | ||||
|         if (clip && clip instanceof Clip) { | ||||
|             clip[key] = val; | ||||
|             this.emit("clipAttrChange"); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public addGroupBehavior(id: ObjectID, val: Behavior) { | ||||
|         const group = this.model.getObjectById(id); | ||||
|         if (group && group instanceof Group) { | ||||
|  | ||||
| @ -56,8 +56,6 @@ const EN_US = { | ||||
|     "Panel.Info.Behavior.Details.View": "Edit view Behavior attributes", | ||||
|     "Panel.Title.Behavior.Clip.Player": "Recording", | ||||
|     "Panel.Info.Behavior.Clip.Player": "Pre render recorded data", | ||||
|     "Panel.Title.Behavior.Clip.Details": "Clip", | ||||
|     "Panel.Info.Behavior.Clip.Details": "Edit view clip attributes", | ||||
|     "Panel.Info.Behavior.Clip.Time.Formate": "{current} / {all} / {fps}fps", | ||||
|     "Panel.Info.Behavior.Clip.Record.Formate": "Record: {time}", | ||||
|     "Panel.Info.Behavior.Clip.Uname.Clip": "Waiting for recording...", | ||||
| @ -163,7 +161,6 @@ const EN_US = { | ||||
|     "Panel.Info.Behavior.Details.Parameter.Key.Vec.Y": "{key} Y", | ||||
|     "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.Details.Error.Nodata": "Specify a clip to view an attribute", | ||||
|     "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.Intro": "Release to load the dragged save file", | ||||
|  | ||||
| @ -56,8 +56,6 @@ const ZH_CN = { | ||||
|     "Panel.Info.Behavior.Details.View": "编辑查看行为属性", | ||||
|     "Panel.Title.Behavior.Clip.Player": "录制", | ||||
|     "Panel.Info.Behavior.Clip.Player": "预渲染录制数据", | ||||
|     "Panel.Title.Behavior.Clip.Details": "剪辑", | ||||
|     "Panel.Info.Behavior.Clip.Details": "编辑查看剪辑片段属性", | ||||
|     "Panel.Info.Behavior.Clip.Time.Formate": "{current} / {all} / {fps} fps", | ||||
|     "Panel.Info.Behavior.Clip.Record.Formate": "录制: {time}", | ||||
|     "Panel.Info.Behavior.Clip.Uname.Clip": "等待录制...", | ||||
| @ -163,7 +161,6 @@ const ZH_CN = { | ||||
|     "Panel.Info.Behavior.Details.Parameter.Key.Vec.Y": "{key} Y 坐标", | ||||
|     "Panel.Info.Behavior.Details.Parameter.Key.Vec.Z": "{key} Z 坐标", | ||||
|     "Panel.Info.Clip.List.Error.Nodata": "没有剪辑片段,请点击录制按钮录制,或者点击加号创建", | ||||
|     "Panel.Info.Clip.Details.Error.Nodata": "请指定一个剪辑片段以查看属性", | ||||
|     "Info.Hint.Save.After.Close": "任何未保存的进度都会丢失, 确定要继续吗?", | ||||
|     "Info.Hint.Load.File.Title": "加载存档", | ||||
|     "Info.Hint.Load.File.Intro": "释放以加载拽入的存档", | ||||
|  | ||||
| @ -8,7 +8,6 @@ import { IArchiveIndividual, Individual } from "@Model/Individual"; | ||||
| import { Behavior, IArchiveBehavior } from "@Model/Behavior"; | ||||
| import { getBehaviorById } from "@Behavior/Behavior"; | ||||
| import { IArchiveParseFn, IObjectParamArchiveType, IRealObjectType } from "@Model/Parameter"; | ||||
| import { Clip, IArchiveClip } from "@Model/Clip"; | ||||
| 
 | ||||
| interface IArchiveEvent { | ||||
|     fileSave: Archive; | ||||
| @ -21,7 +20,6 @@ interface IArchiveObject { | ||||
|     objectPool: IArchiveCtrlObject[]; | ||||
|     labelPool: IArchiveLabel[]; | ||||
|     behaviorPool: IArchiveBehavior[]; | ||||
|     clipPool: IArchiveClip[]; | ||||
| } | ||||
| 
 | ||||
| class Archive extends Emitter<IArchiveEvent> { | ||||
| @ -53,7 +51,7 @@ class Archive extends Emitter<IArchiveEvent> { | ||||
| 
 | ||||
|         // 存贮 CtrlObject
 | ||||
|         const objectPool: IArchiveCtrlObject[] = []; | ||||
|         model.objectPool?.forEach(obj => { | ||||
|         model.objectPool.forEach(obj => { | ||||
|             let archiveObject = obj.toArchive(); | ||||
| 
 | ||||
|             // 处理每个群的个体
 | ||||
| @ -62,7 +60,7 @@ class Archive extends Emitter<IArchiveEvent> { | ||||
|                 const group: Group = obj as Group; | ||||
| 
 | ||||
|                 const individuals: IArchiveIndividual[] = []; | ||||
|                 group.individuals?.forEach((item) => { | ||||
|                 group.individuals.forEach((item) => { | ||||
|                     individuals.push(item.toArchive()); | ||||
|                 }); | ||||
| 
 | ||||
| @ -74,29 +72,22 @@ class Archive extends Emitter<IArchiveEvent> { | ||||
| 
 | ||||
|         // 存储 Label
 | ||||
|         const labelPool: IArchiveLabel[] = []; | ||||
|         model.labelPool?.forEach(obj => { | ||||
|         model.labelPool.forEach(obj => { | ||||
|             labelPool.push(obj.toArchive()); | ||||
|         }); | ||||
| 
 | ||||
|         // 存储全部行为
 | ||||
|         const behaviorPool: IArchiveBehavior[] = []; | ||||
|         model.behaviorPool?.forEach(obj => { | ||||
|         model.behaviorPool.forEach(obj => { | ||||
|             behaviorPool.push(obj.toArchive()); | ||||
|         }); | ||||
| 
 | ||||
|         // 存储全部剪辑片段
 | ||||
|         const clipPool: IArchiveClip[] = []; | ||||
|         model.clipPool?.forEach(obj => { | ||||
|             clipPool.push(obj.toArchive()); | ||||
|         }); | ||||
| 
 | ||||
|         // 生成存档对象
 | ||||
|         const fileData: IArchiveObject = { | ||||
|             nextIndividualId: model.nextIndividualId, | ||||
|             objectPool: objectPool, | ||||
|             labelPool: labelPool, | ||||
|             behaviorPool: behaviorPool, | ||||
|             clipPool: clipPool | ||||
|             behaviorPool: behaviorPool | ||||
|         }; | ||||
| 
 | ||||
|         return JSON.stringify(fileData); | ||||
| @ -114,7 +105,7 @@ class Archive extends Emitter<IArchiveEvent> { | ||||
|         // 实例化全部对象
 | ||||
|         const objectPool: CtrlObject[] = []; | ||||
|         const individualPool: Individual[] = []; | ||||
|         archive.objectPool?.forEach((obj) => { | ||||
|         archive.objectPool.forEach((obj) => { | ||||
| 
 | ||||
|             let ctrlObject: CtrlObject | undefined = undefined; | ||||
|              | ||||
| @ -125,7 +116,7 @@ class Archive extends Emitter<IArchiveEvent> { | ||||
| 
 | ||||
|                 // 实例化全部个体
 | ||||
|                 const individuals: Array<Individual> = []; | ||||
|                 archiveGroup.individuals?.forEach((item) => { | ||||
|                 archiveGroup.individuals.forEach((item) => { | ||||
|                     const newIndividual = new Individual(newGroup); | ||||
|                     newIndividual.id = item.id; | ||||
|                     individuals.push(newIndividual); | ||||
| @ -149,7 +140,7 @@ class Archive extends Emitter<IArchiveEvent> { | ||||
| 
 | ||||
|         // 实例化全部标签
 | ||||
|         const labelPool: Label[] = []; | ||||
|         archive.labelPool?.forEach((item) => { | ||||
|         archive.labelPool.forEach((item) => { | ||||
|             const newLabel = new Label(model); | ||||
|             newLabel.id = item.id; | ||||
|             labelPool.push(newLabel); | ||||
| @ -157,21 +148,13 @@ class Archive extends Emitter<IArchiveEvent> { | ||||
| 
 | ||||
|         // 实例化全部行为
 | ||||
|         const behaviorPool: Behavior[] = []; | ||||
|         archive.behaviorPool?.forEach((item) => { | ||||
|         archive.behaviorPool.forEach((item) => { | ||||
|             const recorder = getBehaviorById(item.behaviorId); | ||||
|             const newBehavior = recorder.new(); | ||||
|             newBehavior.id = item.id; | ||||
|             behaviorPool.push(newBehavior); | ||||
|         }); | ||||
| 
 | ||||
|         // 实例化全部剪辑
 | ||||
|         const clipPool: Clip[] = []; | ||||
|         archive.clipPool?.forEach((item) => { | ||||
|             const newClip = new Clip(model); | ||||
|             newClip.id = item.id; | ||||
|             clipPool.push(newClip); | ||||
|         }); | ||||
| 
 | ||||
|         // 内置标签集合
 | ||||
|         const buildInLabel = [model.allGroupLabel, model.allRangeLabel, model.currentGroupLabel] | ||||
| 
 | ||||
| @ -255,12 +238,6 @@ class Archive extends Emitter<IArchiveEvent> { | ||||
|             item.fromArchive(archive.behaviorPool[index], parseFunction); | ||||
|             return item; | ||||
|         }); | ||||
| 
 | ||||
|         // 加载剪辑
 | ||||
|         model.clipPool = clipPool.map((item, index) => { | ||||
|             item.fromArchive(archive.clipPool[index], parseFunction); | ||||
|             return item; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -2,7 +2,6 @@ import { IAnyObject, Model } from "@Model/Model"; | ||||
| import { v4 as uuid } from "uuid"; | ||||
| import { Group } from "@Model/Group"; | ||||
| import { Range } from "@Model/Range"; | ||||
| import { archiveObject2Parameter, IArchiveParseFn, parameter2ArchiveObject } from "@Model/Parameter"; | ||||
| 
 | ||||
| interface IDrawCommand { | ||||
| 	type: "points" | "cube"; | ||||
| @ -19,13 +18,6 @@ interface IFrame { | ||||
| 	process: number; | ||||
| } | ||||
| 
 | ||||
| interface IArchiveClip { | ||||
| 	id: string; | ||||
| 	time: number; | ||||
| 	name: string; | ||||
| 	frames: IFrame[]; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 剪辑片段 | ||||
|  */ | ||||
| @ -58,127 +50,6 @@ class Clip { | ||||
| 	 */ | ||||
| 	public isRecording: boolean = false; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 判断两个 RenderParameter 是否相同 | ||||
| 	 */ | ||||
| 	public isRenderParameterEqual(p1?: IAnyObject, p2?: IAnyObject, r: boolean = true): boolean { | ||||
| 
 | ||||
| 		if ((p1 && !p2) || (!p1 && p2)) { | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		if (!p1 && !p2) { | ||||
| 			return true; | ||||
| 		} | ||||
| 
 | ||||
| 		for (let key in p1!) { | ||||
| 
 | ||||
| 			// 对象递归校验
 | ||||
| 			if (typeof p1[key] === "object" && !Array.isArray(p1[key])) { | ||||
| 
 | ||||
| 				if (!(typeof (p2 as any)[key] === "object" && !Array.isArray((p2 as any)[key]))) { | ||||
| 					return false; | ||||
| 				} | ||||
| 
 | ||||
| 				// 递归校验
 | ||||
| 				if (r) { | ||||
| 					if (!this.isRenderParameterEqual(p1[key], (p2 as any)[key], false)) { | ||||
| 						return false; | ||||
| 					} | ||||
| 				} | ||||
| 				 | ||||
| 				// 浅校验
 | ||||
| 				else { | ||||
| 					if (p1[key] !== (p2 as any)[key]) { | ||||
| 						return false; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			// 数组遍历校验
 | ||||
| 			else if (Array.isArray(p1[key])) { | ||||
| 
 | ||||
| 				if (!Array.isArray((p2 as any)[key])) { | ||||
| 					return false; | ||||
| 				} | ||||
| 
 | ||||
| 				for (let j = 0; j < p1[key].length; j++) { | ||||
| 					if (p1[key][j] !== (p2 as any)[key][j]) { | ||||
| 						return false; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			// 数值直接校验
 | ||||
| 			else if (p1[key] !== (p2 as any)[key]) { | ||||
| 				return false; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	public cloneRenderParameter(p?: IAnyObject, res: IAnyObject = {}, r: boolean = true): IAnyObject | undefined { | ||||
| 
 | ||||
| 		if (!p) return undefined; | ||||
| 
 | ||||
| 		for (let key in p) { | ||||
| 
 | ||||
| 			// 对象递归克隆
 | ||||
| 			if (typeof p[key] === "object" && !Array.isArray(p[key]) && r) { | ||||
| 				this.cloneRenderParameter(p[key], res, false); | ||||
| 			} | ||||
| 
 | ||||
| 			// 数组克隆
 | ||||
| 			else if (Array.isArray(p[key])) { | ||||
| 				(res as any)[key] = p[key].concat([]); | ||||
| 			} | ||||
| 
 | ||||
| 			// 数值克隆
 | ||||
| 			else { | ||||
| 				(res as any)[key] = p[key]; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		return res; | ||||
| 	} | ||||
| 
 | ||||
| 	public isArrayEqual(a1?: number[], a2?: number[]): boolean { | ||||
| 
 | ||||
| 		if ((a1 && !a2) || (!a1 && a2)) { | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		if (!a1 && !a2) { | ||||
| 			return true; | ||||
| 		} | ||||
| 
 | ||||
| 		for (let i = 0; i < a1!.length; i++) { | ||||
| 			if (a1![i] !== a2![i]) { | ||||
| 				return false; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 从上一帧获取指令数据 | ||||
| 	 */ | ||||
| 	public getCommandFromLastFrame(type: IDrawCommand["type"], id: string, frame?: IFrame): IDrawCommand | undefined { | ||||
| 		let lastCommand: IDrawCommand[] = (frame ?? this.frames[this.frames.length - 1])?.commands; | ||||
| 
 | ||||
| 		if (lastCommand) { | ||||
| 			for (let i = 0; i < lastCommand.length; i++) { | ||||
| 				if (type === lastCommand[i].type && id === lastCommand[i].id) { | ||||
| 					return lastCommand[i]; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		return undefined; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 录制一帧 | ||||
| 	 */ | ||||
| @ -191,59 +62,23 @@ class Clip { | ||||
|             object.renderParameter.color = object.color; | ||||
| 
 | ||||
|             if (object.display && object instanceof Group) { | ||||
| 
 | ||||
| 				// 获取上一帧指令
 | ||||
| 				const lastCommand = this.getCommandFromLastFrame("points", object.id); | ||||
| 
 | ||||
| 				// 记录
 | ||||
| 				const recodeData: IDrawCommand = { | ||||
| 				commands.push({ | ||||
| 					type: "points", | ||||
| 					id: object.id, | ||||
| 					data: object.exportPositionData() | ||||
| 				} | ||||
| 
 | ||||
| 				// 对比校验
 | ||||
| 				if (this.isRenderParameterEqual(object.renderParameter, lastCommand?.parameter)) { | ||||
| 					recodeData.parameter = lastCommand?.parameter; | ||||
| 				} else { | ||||
| 					recodeData.parameter = this.cloneRenderParameter(object.renderParameter); | ||||
| 				} | ||||
| 
 | ||||
| 				commands.push(recodeData); | ||||
| 					data: object.exportPositionData(), | ||||
| 					parameter: object.renderParameter | ||||
| 				}); | ||||
|             } | ||||
| 
 | ||||
| 
 | ||||
|             if (object.display && object instanceof Range) { | ||||
| 
 | ||||
| 				// 获取上一帧指令
 | ||||
| 				const lastCommand = this.getCommandFromLastFrame("cube", object.id); | ||||
| 
 | ||||
| 				// 记录
 | ||||
| 				const recodeData: IDrawCommand = { | ||||
| 				commands.push({ | ||||
| 					type: "cube", | ||||
| 					id: object.id | ||||
| 				} | ||||
| 
 | ||||
| 				// 释放上一帧的内存
 | ||||
| 				if (this.isArrayEqual(object.position, lastCommand?.position)) { | ||||
| 					recodeData.position = lastCommand?.position; | ||||
| 				} else { | ||||
| 					recodeData.position = object.position.concat([]); | ||||
| 				} | ||||
| 
 | ||||
| 				if (this.isArrayEqual(object.radius, lastCommand?.radius)) { | ||||
| 					recodeData.radius = lastCommand?.radius; | ||||
| 				} else { | ||||
| 					recodeData.radius = object.radius.concat([]); | ||||
| 				} | ||||
| 
 | ||||
| 				if (this.isRenderParameterEqual(object.renderParameter, lastCommand?.parameter)) { | ||||
| 					recodeData.parameter = lastCommand?.parameter; | ||||
| 				} else { | ||||
| 					recodeData.parameter = this.cloneRenderParameter(object.renderParameter); | ||||
| 				} | ||||
| 
 | ||||
| 				commands.push(recodeData); | ||||
| 					id: object.id, | ||||
| 					position: object.position, | ||||
| 					radius: object.radius, | ||||
| 					parameter: object.renderParameter | ||||
| 				}); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| @ -260,110 +95,6 @@ class Clip { | ||||
| 		return frame; | ||||
| 	} | ||||
| 
 | ||||
| 	public readonly LastFrameData: "@L" = "@L"; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 压缩帧数据 | ||||
| 	 */ | ||||
| 	public compressed(): IFrame[] { | ||||
| 		const resFrame: IFrame[] = []; | ||||
| 
 | ||||
| 		for (let i = 0; i < this.frames.length; i++) { | ||||
| 			const commands = this.frames[i].commands; | ||||
| 			const res: IDrawCommand[] = []; | ||||
| 
 | ||||
| 			// 处理指令
 | ||||
| 			for (let j = 0; j < commands.length; j++) { | ||||
| 				 | ||||
| 				// 压缩指令
 | ||||
| 				const command: IDrawCommand = { | ||||
| 					id: commands[j].id, | ||||
| 					type: commands[j].type | ||||
| 				}; | ||||
| 
 | ||||
| 				// 搜索上一帧相同指令
 | ||||
| 				const lastCommand = this.frames[i - 1] ? | ||||
| 					this.getCommandFromLastFrame(command.type, command.id, this.frames[i - 1]) : | ||||
| 					undefined; | ||||
| 
 | ||||
| 				// 记录
 | ||||
| 				command.data = (lastCommand?.data === commands[j].data) ? | ||||
| 					this.LastFrameData as any : Array.from(commands[j].data ?? []); | ||||
| 
 | ||||
| 				command.position = (lastCommand?.position === commands[j].position) ? | ||||
| 					this.LastFrameData as any : commands[j].position?.concat([]); | ||||
| 
 | ||||
| 				command.radius = (lastCommand?.radius === commands[j].radius) ? | ||||
| 					this.LastFrameData as any : commands[j].radius?.concat([]); | ||||
| 
 | ||||
| 				command.parameter = (lastCommand?.parameter === commands[j].parameter) ? | ||||
| 					this.LastFrameData as any : parameter2ArchiveObject(commands[j].parameter as any); | ||||
| 
 | ||||
| 				res.push(command); | ||||
| 			} | ||||
| 
 | ||||
| 			resFrame.push({ | ||||
| 				duration: this.frames[i].duration, | ||||
| 				process: this.frames[i].process, | ||||
| 				commands: res | ||||
| 			}) | ||||
| 		} | ||||
| 
 | ||||
| 		return resFrame; | ||||
| 	}; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 加载压缩帧数据 | ||||
| 	 */ | ||||
| 	public uncompressed(frames: IFrame[], paster: IArchiveParseFn): IFrame[] { | ||||
| 		const resFrame: IFrame[] = []; | ||||
| 
 | ||||
| 		for (let i = 0; i < frames.length; i++) { | ||||
| 			const commands = frames[i].commands; | ||||
| 			const res: IDrawCommand[] = []; | ||||
| 
 | ||||
| 			// 处理指令
 | ||||
| 			for (let j = 0; j < commands.length; j++) { | ||||
| 				 | ||||
| 				// 压缩指令
 | ||||
| 				const command: IDrawCommand = { | ||||
| 					id: commands[j].id, | ||||
| 					type: commands[j].type | ||||
| 				}; | ||||
| 
 | ||||
| 				// 搜索上一帧相同指令
 | ||||
| 				const lastCommand = resFrame[resFrame.length - 1] ? | ||||
| 					this.getCommandFromLastFrame(command.type, command.id, resFrame[resFrame.length - 1]) : | ||||
| 					undefined; | ||||
| 
 | ||||
| 				console.log(lastCommand); | ||||
| 
 | ||||
| 				// 记录
 | ||||
| 				command.data = (this.LastFrameData as any === commands[j].data) ? | ||||
| 					lastCommand?.data : new Float32Array(commands[j].data ?? []); | ||||
| 
 | ||||
| 				command.position = (this.LastFrameData as any === commands[j].position) ? | ||||
| 					lastCommand?.position : commands[j].position; | ||||
| 
 | ||||
| 				command.radius = (this.LastFrameData as any === commands[j].radius) ? | ||||
| 					lastCommand?.radius : commands[j].radius; | ||||
| 
 | ||||
| 				command.parameter = (this.LastFrameData as any === commands[j].parameter) ? | ||||
| 					lastCommand?.parameter : archiveObject2Parameter(commands[j].parameter as any, paster); | ||||
| 
 | ||||
| 				res.push(command); | ||||
| 			} | ||||
| 
 | ||||
| 			resFrame.push({ | ||||
| 				duration: frames[i].duration, | ||||
| 				process: frames[i].process, | ||||
| 				commands: res | ||||
| 			}) | ||||
| 		} | ||||
| 
 | ||||
| 		return resFrame; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 播放一帧 | ||||
| 	 */ | ||||
| @ -394,22 +125,6 @@ class Clip { | ||||
| 		this.model = model; | ||||
| 		this.id = uuid(); | ||||
| 	} | ||||
| 
 | ||||
| 	public toArchive(): IArchiveClip { | ||||
|         return { | ||||
|             id: this.id, | ||||
| 			time: this.time, | ||||
| 			name: this.name, | ||||
| 			frames: this.compressed() | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     public fromArchive(archive: IArchiveClip, paster: IArchiveParseFn): void { | ||||
|         this.id = archive.id, | ||||
| 		this.time = archive.time, | ||||
| 		this.name = archive.name, | ||||
| 		this.frames = this.uncompressed(archive.frames, paster); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export { Clip, IFrame, IArchiveClip }; | ||||
| export { Clip, IFrame }; | ||||
| @ -349,14 +349,6 @@ class Model extends Emitter<ModelEvent> { | ||||
|         return newClip; | ||||
|     } | ||||
| 
 | ||||
|     public getClipById(id: ObjectID): Clip | undefined { | ||||
|         for (let i = 0; i < this.clipPool.length; i++) { | ||||
|             if (this.clipPool[i].id.toString() === id.toString()) { | ||||
|                 return this.clipPool[i]; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 删除一个剪辑片段 | ||||
|      */ | ||||
|  | ||||
| @ -64,7 +64,7 @@ class SimulatorDesktop extends Component { | ||||
|                     items: [ | ||||
|                         {panels: ["RenderView"]}, | ||||
|                         { | ||||
|                             items: [{panels: ["BehaviorList", "ClipPlayer"]}, {panels: ["LabelList"]}], | ||||
|                             items: [{panels: ["BehaviorList"]}, {panels: ["LabelList"]}], | ||||
|                             scale: 80, | ||||
|                             layout: LayoutDirection.X | ||||
|                         } | ||||
| @ -76,7 +76,7 @@ class SimulatorDesktop extends Component { | ||||
|                     items: [{ | ||||
|                         panels: ["ObjectList"] | ||||
|                     }, { | ||||
|                         panels: ["GroupDetails", "RangeDetails", "LabelDetails", "BehaviorDetails", "ClipDetails"] | ||||
|                         panels: ["GroupDetails", "RangeDetails", "LabelDetails", "BehaviorDetails"] | ||||
|                     }], | ||||
|                     scale: 30, | ||||
|                     layout: LayoutDirection.Y | ||||
|  | ||||
| @ -55,7 +55,7 @@ class SimulatorWeb extends Component { | ||||
|                     items: [ | ||||
|                         {panels: ["RenderView"]}, | ||||
|                         { | ||||
|                             items: [{panels: ["BehaviorList", "ClipPlayer"]}, {panels: ["LabelList"]}], | ||||
|                             items: [{panels: ["ClipPlayer", "BehaviorList"]}, {panels: ["LabelList"]}], | ||||
|                             scale: 80, | ||||
|                             layout: LayoutDirection.X | ||||
|                         } | ||||
| @ -67,7 +67,7 @@ class SimulatorWeb extends Component { | ||||
|                     items: [{ | ||||
|                         panels: ["ObjectList"] | ||||
|                     }, { | ||||
|                         panels: ["GroupDetails", "RangeDetails", "LabelDetails", "BehaviorDetails", "ClipDetails"] | ||||
|                         panels: ["GroupDetails", "RangeDetails", "LabelDetails", "BehaviorDetails"] | ||||
|                     }], | ||||
|                     scale: 30, | ||||
|                     layout: LayoutDirection.Y | ||||
|  | ||||
| @ -1,60 +0,0 @@ | ||||
| import { Component, ReactNode } from "react"; | ||||
| import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status"; | ||||
| import { TogglesInput } from "@Input/TogglesInput/TogglesInput"; | ||||
| import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup"; | ||||
| import { AttrInput } from "@Input/AttrInput/AttrInput"; | ||||
| import { Message } from "@Input/Message/Message"; | ||||
| import { Clip } from "@Model/Clip"; | ||||
| import "./ClipDetails.scss"; | ||||
| 
 | ||||
| @useStatusWithEvent("focusClipChange", "clipAttrChange") | ||||
| class ClipDetails extends Component<IMixinStatusProps> { | ||||
| 
 | ||||
|     private renderFrom(clip: Clip) { | ||||
|         return <> | ||||
| 
 | ||||
|             <Message i18nKey="Common.Attr.Title.Basic" isTitle first/> | ||||
|              | ||||
|             <AttrInput | ||||
|                 keyI18n="Common.Attr.Key.Display.Name" | ||||
|                 maxLength={15} | ||||
|                 value={clip.name} | ||||
|                 valueChange={(value) => { | ||||
|                     if (this.props.status) { | ||||
|                         this.props.status.changeClipAttrib(clip.id, "name", value); | ||||
|                     } | ||||
|                 }} | ||||
|             /> | ||||
| 
 | ||||
|             <TogglesInput | ||||
|                 keyI18n="Common.Attr.Key.Delete" onIconName="delete" red | ||||
|                 offIconName="delete" valueChange={() => { | ||||
|                     const status = this.props.status; | ||||
|                     if (status) { | ||||
|                         status.popup.showPopup(ConfirmPopup, { | ||||
|                             infoI18n: "Popup.Delete.Clip.Confirm", | ||||
|                             titleI18N: "Popup.Action.Objects.Confirm.Title", | ||||
|                             yesI18n: "Popup.Action.Objects.Confirm.Delete", | ||||
|                             red: "yes", | ||||
|                             yes: () => { | ||||
|                                 status.setClipObject(); | ||||
|                                 this.props.status?.actuator.endPlay(); | ||||
|                                 status.model.deleteClip(clip.id); | ||||
|                             } | ||||
|                         }); | ||||
|                     } | ||||
|                 }} | ||||
|             /> | ||||
| 
 | ||||
| 		</>; | ||||
|     } | ||||
| 
 | ||||
| 	public render(): ReactNode { | ||||
|         if (this.props.status && this.props.status.focusClip) { | ||||
|             return this.renderFrom(this.props.status.focusClip); | ||||
|         } | ||||
| 		return <Message i18nKey="Panel.Info.Clip.Details.Error.Nodata"/>; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| export { ClipDetails }; | ||||
| @ -1,7 +1,6 @@ | ||||
| import { Component, ReactNode } from "react"; | ||||
| import { ClipList } from "@Component/ClipList/ClipList"; | ||||
| import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status"; | ||||
| import { useSetting, IMixinSettingProps } from "@Context/Setting"; | ||||
| import { BackgroundLevel, FontLevel, Theme } from "@Component/Theme/Theme"; | ||||
| import { Message } from "@Input/Message/Message"; | ||||
| import { Clip } from "@Model/Clip"; | ||||
| @ -10,9 +9,8 @@ import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup"; | ||||
| import { OfflineRender } from "@Component/OfflineRender/OfflineRender" | ||||
| import "./ClipPlayer.scss"; | ||||
| 
 | ||||
| @useSetting | ||||
| @useStatusWithEvent("clipChange", "focusClipChange", "actuatorStartChange", "clipAttrChange") | ||||
| class ClipPlayer extends Component<IMixinStatusProps & IMixinSettingProps> { | ||||
| @useStatusWithEvent("clipChange", "focusClipChange", "actuatorStartChange") | ||||
| class ClipPlayer extends Component<IMixinStatusProps> { | ||||
| 
 | ||||
| 	private isInnerClick: boolean = false; | ||||
| 
 | ||||
| @ -59,7 +57,6 @@ class ClipPlayer extends Component<IMixinStatusProps & IMixinSettingProps> { | ||||
| 				this.isInnerClick = true; | ||||
| 				this.props.status?.setClipObject(clip); | ||||
| 				this.props.status?.actuator.startPlay(clip); | ||||
| 				this.props.setting?.layout.focus("ClipDetails"); | ||||
| 			}} | ||||
| 		/>; | ||||
| 	} | ||||
|  | ||||
| @ -3,7 +3,7 @@ import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status"; | ||||
| import { Recorder } from "@Component/Recorder/Recorder"; | ||||
| import { ActuatorModel } from "@Model/Actuator"; | ||||
| 
 | ||||
| @useStatusWithEvent("actuatorStartChange", "focusClipChange", "recordLoop", "clipAttrChange") | ||||
| @useStatusWithEvent("actuatorStartChange", "focusClipChange", "recordLoop") | ||||
| class ClipRecorder extends Component<IMixinStatusProps> { | ||||
| 	public render(): ReactNode { | ||||
| 
 | ||||
|  | ||||
| @ -12,7 +12,6 @@ import { BehaviorList } from "@Panel/BehaviorList/BehaviorList"; | ||||
| import { BehaviorDetails } from "@Panel/BehaviorDetails/BehaviorDetails"; | ||||
| import { ClipPlayer } from "@Panel/ClipPlayer/ClipPlayer"; | ||||
| import { ClipRecorder } from "@Panel/ClipPlayer/ClipRecorder"; | ||||
| import { ClipDetails } from "@Panel/ClipDetails/ClipDetails"; | ||||
| 
 | ||||
| interface IPanelInfo { | ||||
| 	nameKey: string; | ||||
| @ -35,7 +34,6 @@ type PanelId = "" | ||||
| | "BehaviorList" // 行为列表
 | ||||
| | "BehaviorDetails" // 行为属性
 | ||||
| | "ClipPlayer" // 剪辑影片
 | ||||
| | "ClipDetails" // 剪辑详情
 | ||||
| ; | ||||
| 
 | ||||
| const PanelInfoMap = new Map<PanelId, IPanelInfo>(); | ||||
| @ -75,10 +73,6 @@ PanelInfoMap.set("ClipPlayer", { | ||||
|     nameKey: "Panel.Title.Behavior.Clip.Player", introKay: "Panel.Info.Behavior.Clip.Player", | ||||
|     class: ClipPlayer, header: ClipRecorder, hidePadding: true | ||||
| }); | ||||
| PanelInfoMap.set("ClipDetails", { | ||||
|     nameKey: "Panel.Title.Behavior.Clip.Details", introKay: "Panel.Info.Behavior.Clip.Details", | ||||
|     class: ClipDetails | ||||
| }); | ||||
| 
 | ||||
| function getPanelById(panelId: PanelId): ReactNode { | ||||
| 	switch (panelId) { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user