From 7556ea983e8c3db4fcac485c52bac26dd634d5e7 Mon Sep 17 00:00:00 2001 From: MrKBear Date: Fri, 8 Apr 2022 14:00:32 +0800 Subject: [PATCH] Add parameter component --- source/Behavior/Behavior.ts | 2 +- .../Component/Localization/Localization.tsx | 2 +- source/Component/Parameter/Parameter.scss | 0 source/Component/Parameter/Parameter.tsx | 202 ++++++++++++++++++ source/Context/Status.tsx | 5 +- source/Model/Behavior.ts | 75 ++----- source/Model/Model.ts | 3 +- source/Model/Parameter.ts | 67 +++++- source/Page/SimulatorWeb/SimulatorWeb.tsx | 14 +- .../Panel/BehaviorDetails/BehaviorDetails.tsx | 175 ++++----------- 10 files changed, 321 insertions(+), 224 deletions(-) create mode 100644 source/Component/Parameter/Parameter.scss create mode 100644 source/Component/Parameter/Parameter.tsx diff --git a/source/Behavior/Behavior.ts b/source/Behavior/Behavior.ts index 6fc5c89..15c2dad 100644 --- a/source/Behavior/Behavior.ts +++ b/source/Behavior/Behavior.ts @@ -5,7 +5,7 @@ import { Brownian } from "./Brownian"; import { BoundaryConstraint } from "./BoundaryConstraint"; const AllBehaviors: IAnyBehaviorRecorder[] = [ - // new BehaviorRecorder(Template), + new BehaviorRecorder(Template), new BehaviorRecorder(Dynamics), new BehaviorRecorder(Brownian), new BehaviorRecorder(BoundaryConstraint), diff --git a/source/Component/Localization/Localization.tsx b/source/Component/Localization/Localization.tsx index bc3c619..e627ae5 100644 --- a/source/Component/Localization/Localization.tsx +++ b/source/Component/Localization/Localization.tsx @@ -78,4 +78,4 @@ class Localization extends Component { + option: IParameterOption

; + value: IParameterValue

; + key: ObjectID; + change: (key: K, val: IParamValue) => any; + i18n: (option: IParameterOptionItem, language: Language) => string; + title?: AllI18nKeys; + titleOption?: Record; + isFirst?: boolean; +} + +@useSettingWithEvent("language") +class Parameter

extends Component & IMixinSettingProps> { + + private renderParameter + (key: K, option: IParameterOptionItem, value: IParamValue): ReactNode { + + const indexKey = `${this.props.key}-${key}`; + const type = option.type; + const i18nString = this.props.i18n(option, this.props.setting?.language ?? "EN_US"); + + if (type === "number") { + return ?? 0} + valueChange={(val) => { + this.props.change(key, parseFloat(val) as IParamValue); + }} + />; + } + + else if (type === "string") { + return ?? ""} + valueChange={(val) => { + this.props.change(key, val as IParamValue); + }} + />; + } + + else if (type === "boolean") { + return ?? false} + valueChange={(val) => { + this.props.change(key, val as IParamValue); + }} + /> + } + + else if (isObjectType(type)) { + + type IObjectParamValue = IParamValue<"G" | "R" | "LG" | "LR">; + const typedValue = value as IObjectParamValue; + + return { + typedValue.picker = obj as IObjectParamValue["picker"]; + this.props.change(key, typedValue as IParamValue); + }} + cleanValue={() => { + typedValue.picker = undefined as IObjectParamValue["picker"]; + this.props.change(key, typedValue as IParamValue); + }} + /> + } + + else if (isVectorType(type)) { + + type IObjectParamValue = IParamValue<"vec">; + const typedValue = value as IObjectParamValue; + + return + + { + typedValue[0] = parseFloat(val); + this.props.change(key, typedValue as IParamValue); + }} + /> + + { + typedValue[1] = parseFloat(val); + this.props.change(key, typedValue as IParamValue); + }} + /> + + { + typedValue[2] = parseFloat(val); + this.props.change(key, typedValue as IParamValue); + }} + /> + + + } + + else { + return + } + } + + private renderAllParameter(key: Array) { + return key.map((key) => { + return this.renderParameter( + key, + this.props.option[key], + this.props.value[key], + ); + }); + } + + public render(): ReactNode { + const allOptionKeys: Array = Object.getOwnPropertyNames(this.props.option); + + return <> + + { + allOptionKeys.length <= 0 && this.props.title ? + : null + } + + { + this.renderAllParameter(allOptionKeys) + } + + + } +} + +export { Parameter } \ No newline at end of file diff --git a/source/Context/Status.tsx b/source/Context/Status.tsx index 42b5ba0..b671d38 100644 --- a/source/Context/Status.tsx +++ b/source/Context/Status.tsx @@ -11,7 +11,8 @@ import { Setting } from "./Setting"; import { I18N } from "@Component/Localization/Localization"; import { superConnectWithEvent, superConnect } from "./Context"; import { PopupController } from "./Popups"; -import { Behavior, IBehaviorParameter, IParamValue } from "@Model/Behavior"; +import { Behavior } from "@Model/Behavior"; +import { IParameter, IParamValue } from "@Model/Parameter"; import { Actuator } from "@Model/Actuator"; function randomColor(unNormal: boolean = false) { @@ -208,7 +209,7 @@ class Status extends Emitter { /** * 修改群属性 */ - public changeBehaviorAttrib> + public changeBehaviorAttrib> (id: ObjectID, key: P, val: IParamValue, noParameter?: boolean) { const behavior = this.model.getBehaviorById(id); if (behavior) { diff --git a/source/Model/Behavior.ts b/source/Model/Behavior.ts index 75dea14..76d04ec 100644 --- a/source/Model/Behavior.ts +++ b/source/Model/Behavior.ts @@ -3,17 +3,17 @@ import type { Individual } from "./Individual"; import type { Group } from "./Group"; import type { Model } from "./Model"; import { - IParamValue, isObjectType, isVectorType, - IBehaviorParameterOptionItem, IBehaviorParameter, IBehaviorParameterOption, IBehaviorParameterValue + IParamValue, isObjectType, isVectorType, getDefaultValue, + IParameterOptionItem, IParameter, IParameterOption, IParameterValue } from "./Parameter"; /** * 行为构造函数类型 */ type IBehaviorConstructor< - P extends IBehaviorParameter = {}, + P extends IParameter = {}, E extends Record = {} -> = new (id: string, parameter: IBehaviorParameterValue

) => Behavior; +> = new (id: string, parameter: IParameterValue

) => Behavior; type IAnyBehavior = Behavior; type IAnyBehaviorRecorder = BehaviorRecorder; @@ -75,7 +75,7 @@ class BehaviorInfo = {}> extends Emitter { } class BehaviorRecorder< - P extends IBehaviorParameter = {}, + P extends IParameter = {}, E extends Record = {} > extends BehaviorInfo<{}> { @@ -104,62 +104,13 @@ class BehaviorRecorder< /** * 对象参数列表 */ - public parameterOption: IBehaviorParameterOption

; - - /** - * 获取参数列表的默认值 - */ - public getDefaultValue(): IBehaviorParameterValue

{ - let defaultObj = {} as IBehaviorParameterValue

; - for (let key in this.parameterOption) { - let defaultVal = this.parameterOption[key].defaultValue; - - defaultObj[key] = defaultVal as any; - if (defaultObj[key] === undefined) { - - switch (this.parameterOption[key].type) { - case "string": - defaultObj[key] = "" as any; - break; - - case "number": - defaultObj[key] = 0 as any; - break; - - case "boolean": - defaultObj[key] = false as any; - break; - - case "vec": - defaultObj[key] = [0, 0, 0] as any; - break; - - case "G": - case "R": - defaultObj[key] = { - picker: undefined, - objects: undefined - } as any; - break; - - case "LR": - case "LG": - defaultObj[key] = { - picker: undefined, - objects: [] - } as any; - break; - } - } - } - return defaultObj; - } + public parameterOption: IParameterOption

; /** * 创建一个新的行为实例 */ public new(): Behavior { - return new this.behavior(this.getNextId(), this.getDefaultValue()); + return new this.behavior(this.getNextId(), getDefaultValue(this.parameterOption)); } public constructor(behavior: IBehaviorConstructor) { @@ -180,7 +131,7 @@ class BehaviorRecorder< * 群体的某种行为 */ class Behavior< - P extends IBehaviorParameter = {}, + P extends IParameter = {}, E extends Record = {} > extends BehaviorInfo { @@ -208,14 +159,14 @@ class Behavior< /** * 行为参数 */ - public parameter: IBehaviorParameterValue

; + public parameter: IParameterValue

; /** * 对象参数列表 */ - public parameterOption: IBehaviorParameterOption

= {} as any; + public parameterOption: IParameterOption

= {} as any; - public constructor(id: string, parameter: IBehaviorParameterValue

) { + public constructor(id: string, parameter: IParameterValue

) { super(); this.id = id; this.parameter = parameter; @@ -299,8 +250,6 @@ class Behavior< type IRenderBehavior = BehaviorInfo | Behavior; export { - Behavior, BehaviorRecorder, IBehaviorParameterOption, IBehaviorParameterOptionItem, IParamValue, - IAnyBehavior, IAnyBehaviorRecorder, BehaviorInfo, IRenderBehavior, IBehaviorParameter, - isObjectType, isVectorType + Behavior, BehaviorRecorder, IAnyBehavior, IAnyBehaviorRecorder, BehaviorInfo, IRenderBehavior }; export default { Behavior }; \ No newline at end of file diff --git a/source/Model/Model.ts b/source/Model/Model.ts index b7dbe1f..5ff5f4d 100644 --- a/source/Model/Model.ts +++ b/source/Model/Model.ts @@ -5,7 +5,8 @@ import { Emitter, EventType, EventMixin } from "./Emitter"; import { CtrlObject } from "./CtrlObject"; import { ObjectID, AbstractRenderer } from "./Renderer"; import { Label } from "./Label"; -import { Behavior, IAnyBehavior, IAnyBehaviorRecorder, IParamValue } from "./Behavior"; +import { Behavior, IAnyBehavior, IAnyBehaviorRecorder } from "./Behavior"; +import { IParamValue } from "@Model/Parameter"; type ModelEvent = { labelChange: Label[]; diff --git a/source/Model/Parameter.ts b/source/Model/Parameter.ts index 5027117..0c790ed 100644 --- a/source/Model/Parameter.ts +++ b/source/Model/Parameter.ts @@ -39,26 +39,26 @@ type IParamValue = AllMapType[K]; /** * 特殊对象类型判定 */ -const objectTypeListEnumSet = new Set(["R", "G", "LR", "LG"]); +const objectTypeListEnumSet = new Set(["R", "G", "LR", "LG"]); /** * 对象断言表达式 */ -function isObjectType(key: IParamType): key is IVectorType { +function isObjectType(key: string): key is IVectorType { return objectTypeListEnumSet.has(key); } /** * 向量断言表达式 */ -function isVectorType(key: IParamType): key is IObjectType { +function isVectorType(key: string): key is IObjectType { return key === "vec"; } /** * 模型参数类型 */ -interface IBehaviorParameterOptionItem { +interface IParameterOptionItem { /** * 参数类型 @@ -102,25 +102,72 @@ interface IBehaviorParameterOptionItem { iconName?: string; } -interface IBehaviorParameter { +interface IParameter { [x: string]: IParamType; } /** * 参数类型列表 */ -type IBehaviorParameterOption

= { - [X in keyof P]: IBehaviorParameterOptionItem; +type IParameterOption

= { + [X in keyof P]: IParameterOptionItem; } /** * 参数类型列表映射到参数对象 */ -type IBehaviorParameterValue

= { +type IParameterValue

= { [X in keyof P]: IParamValue } +function getDefaultValue

(option: IParameterOption

): IParameterValue

{ + let defaultObj = {} as IParameterValue

; + for (let key in option) { + let defaultVal = option[key].defaultValue; + + if (defaultVal !== undefined) { + defaultObj[key] = defaultVal; + } else { + + switch (option[key].type) { + case "string": + defaultObj[key] = "" as any; + break; + + case "number": + defaultObj[key] = 0 as any; + break; + + case "boolean": + defaultObj[key] = false as any; + break; + + case "vec": + defaultObj[key] = [0, 0, 0] as any; + break; + + case "G": + case "R": + defaultObj[key] = { + picker: undefined, + objects: undefined + } as any; + break; + + case "LR": + case "LG": + defaultObj[key] = { + picker: undefined, + objects: [] + } as any; + break; + } + } + } + return defaultObj; +} + export { - IParamType, IParamValue, isObjectType, isVectorType, - IBehaviorParameterOptionItem, IBehaviorParameter, IBehaviorParameterOption, IBehaviorParameterValue + IParamType, IParamValue, isObjectType, isVectorType, getDefaultValue, + IParameterOptionItem, IParameter, IParameterOption, IParameterValue } \ No newline at end of file diff --git a/source/Page/SimulatorWeb/SimulatorWeb.tsx b/source/Page/SimulatorWeb/SimulatorWeb.tsx index b74f8a9..0e594de 100644 --- a/source/Page/SimulatorWeb/SimulatorWeb.tsx +++ b/source/Page/SimulatorWeb/SimulatorWeb.tsx @@ -55,16 +55,16 @@ class SimulatorWeb extends Component { this.status.model.update(0); this.status.newLabel().name = "New Label"; this.status.newLabel().name = "Test Label 01"; - // let template = this.status.model.addBehavior(AllBehaviors[0]); - // template.name = "Template"; template.color = [150, 20, 220]; - let dynamic = this.status.model.addBehavior(AllBehaviors[0]); + let template = this.status.model.addBehavior(AllBehaviors[0]); + template.name = "Template"; template.color = [150, 20, 220]; + let dynamic = this.status.model.addBehavior(AllBehaviors[1]); dynamic.name = "Dynamic"; dynamic.color = [250, 200, 80]; - let brownian = this.status.model.addBehavior(AllBehaviors[1]); + let brownian = this.status.model.addBehavior(AllBehaviors[2]); brownian.name = "Brownian"; brownian.color = [200, 80, 250]; - let boundary = this.status.model.addBehavior(AllBehaviors[2]); + let boundary = this.status.model.addBehavior(AllBehaviors[3]); boundary.name = "Boundary"; boundary.color = [80, 200, 250]; - // boundary.parameter.range.picker = this.status.model.allRangeLabel; - // group.addBehavior(template); + boundary.parameter.range.picker = this.status.model.allRangeLabel; + group.addBehavior(template); group.addBehavior(dynamic); group.addBehavior(brownian); group.addBehavior(boundary); diff --git a/source/Panel/BehaviorDetails/BehaviorDetails.tsx b/source/Panel/BehaviorDetails/BehaviorDetails.tsx index cc09762..40cd0dd 100644 --- a/source/Panel/BehaviorDetails/BehaviorDetails.tsx +++ b/source/Panel/BehaviorDetails/BehaviorDetails.tsx @@ -1,13 +1,13 @@ -import { Component, Fragment, ReactNode} from "react"; +import { Component, ReactNode} from "react"; import { useSettingWithEvent, IMixinSettingProps } from "@Context/Setting"; import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status"; -import { Behavior, IBehaviorParameter, isObjectType, isVectorType } from "@Model/Behavior"; +import { IAnyBehavior } from "@Model/Behavior"; import { Message } from "@Component/Message/Message"; import { AttrInput } from "@Component/AttrInput/AttrInput"; import { ColorInput } from "@Component/ColorInput/ColorInput"; import { TogglesInput } from "@Component/TogglesInput/TogglesInput"; import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup"; -import { ObjectPicker } from "@Component/ObjectPicker/ObjectPicker"; +import { Parameter } from "@Component/Parameter/Parameter"; import "./BehaviorDetails.scss"; interface IBehaviorDetailsProps {} @@ -16,11 +16,24 @@ interface IBehaviorDetailsProps {} @useStatusWithEvent("focusBehaviorChange", "behaviorAttrChange") class BehaviorDetails extends Component { - private renderFrom - (behavior: Behavior>): ReactNode { - - const allParameterKeys = Object.getOwnPropertyNames(behavior.parameterOption); + private handelDeleteBehavior = (behavior: IAnyBehavior) => { + if (this.props.status) { + const status = this.props.status; + status.popup.showPopup(ConfirmPopup, { + infoI18n: "Popup.Delete.Behavior.Confirm", + titleI18N: "Popup.Action.Objects.Confirm.Title", + yesI18n: "Popup.Action.Objects.Confirm.Delete", + red: "yes", + yes: () => { + status.model.deleteBehavior(behavior); + status.setBehaviorObject(); + } + }) + } + } + private renderFrom(behavior: IAnyBehavior): ReactNode { + return <> @@ -44,145 +57,29 @@ class BehaviorDetails extends Component { - if (this.props.status) { - const status = this.props.status; - status.popup.showPopup(ConfirmPopup, { - infoI18n: "Popup.Delete.Behavior.Confirm", - titleI18N: "Popup.Action.Objects.Confirm.Title", - yesI18n: "Popup.Action.Objects.Confirm.Delete", - red: "yes", - yes: () => { - status.model.deleteBehavior(behavior); - status.setBehaviorObject(); - } - }) - } + this.handelDeleteBehavior(behavior) }} /> - - { - allParameterKeys.length > 0 ? - : null - } - { - allParameterKeys.map((key) => { - return this.renderParameter(behavior, key); - }) - } + behavior.getTerms(option.name, language)} + title={"Panel.Info.Behavior.Details.Behavior.Props"} + titleOption={{ + behavior: behavior.getTerms(behavior.behaviorName, this.props.setting?.language) + }} + change={(key, value) => { + this.props.status?.changeBehaviorAttrib( + behavior.id, key as string, value + ); + }} + /> ; } - private renderParameter - (behavior: Behavior>, key: keyof T): ReactNode { - const type = behavior.parameterOption[key]; - const value = behavior.parameter[key]; - const indexKey = `${behavior.id}-${key}`; - - if (type.type === "number") { - return { - this.props.status?.changeBehaviorAttrib(behavior.id, key as string, (val as any) / 1); - }} - /> - } - - if (type.type === "string") { - return { - this.props.status?.changeBehaviorAttrib(behavior.id, key as string, val); - }} - /> - } - - if (type.type === "boolean") { - return { - this.props.status?.changeBehaviorAttrib(behavior.id, key as string, val); - }} - /> - } - - if (isObjectType(type.type as any)) { - return { - (value as any).picker = obj; - this.props.status?.changeBehaviorAttrib(behavior.id, key as string, value); - }} - cleanValue={() => { - (value as any).picker = undefined; - this.props.status?.changeBehaviorAttrib(behavior.id, key as string, value); - }} - /> - } - - if (isVectorType(type.type as any)) { - return - - { - (value as number[])[0] = (val as any) / 1; - this.props.status?.changeBehaviorAttrib(behavior.id, key as string, value); - }} - /> - - { - (value as number[])[1] = (val as any) / 1; - this.props.status?.changeBehaviorAttrib(behavior.id, key as string, value); - }} - /> - - { - (value as number[])[2] = (val as any) / 1; - this.props.status?.changeBehaviorAttrib(behavior.id, key as string, value); - }} - /> - - - } - - return - } - public render(): ReactNode { if (this.props.status && this.props.status.focusBehavior) { return this.renderFrom(this.props.status.focusBehavior);