Add parameter component
This commit is contained in:
		
							parent
							
								
									d7e15793b9
								
							
						
					
					
						commit
						7556ea983e
					
				| @ -5,7 +5,7 @@ import { Brownian } from "./Brownian"; | |||||||
| import { BoundaryConstraint } from "./BoundaryConstraint";  | import { BoundaryConstraint } from "./BoundaryConstraint";  | ||||||
| 
 | 
 | ||||||
| const AllBehaviors: IAnyBehaviorRecorder[] = [ | const AllBehaviors: IAnyBehaviorRecorder[] = [ | ||||||
|     // new BehaviorRecorder(Template),
 |     new BehaviorRecorder(Template), | ||||||
|     new BehaviorRecorder(Dynamics), |     new BehaviorRecorder(Dynamics), | ||||||
|     new BehaviorRecorder(Brownian), |     new BehaviorRecorder(Brownian), | ||||||
|     new BehaviorRecorder(BoundaryConstraint), |     new BehaviorRecorder(BoundaryConstraint), | ||||||
|  | |||||||
| @ -78,4 +78,4 @@ class Localization extends Component<ILocalizationProps & IMixinSettingProps & | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export { Localization, I18N, LanguageDataBase, AllI18nKeys }; | export { Localization, I18N, LanguageDataBase, AllI18nKeys, ILocalizationProps }; | ||||||
							
								
								
									
										0
									
								
								source/Component/Parameter/Parameter.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								source/Component/Parameter/Parameter.scss
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										202
									
								
								source/Component/Parameter/Parameter.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								source/Component/Parameter/Parameter.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,202 @@ | |||||||
|  | import { AttrInput } from "@Component/AttrInput/AttrInput"; | ||||||
|  | import { | ||||||
|  |     IParameter, IParameterOption, IParameterOptionItem, | ||||||
|  |     IParameterValue, IParamValue, isObjectType, isVectorType | ||||||
|  | } from "@Model/Parameter"; | ||||||
|  | import { ObjectID } from "@Model/Renderer"; | ||||||
|  | import { Component, Fragment, ReactNode } from "react"; | ||||||
|  | import { TogglesInput } from "@Component/TogglesInput/TogglesInput"; | ||||||
|  | import { ObjectPicker } from "@Component/ObjectPicker/ObjectPicker"; | ||||||
|  | import { useSettingWithEvent, IMixinSettingProps, Language } from "@Context/Setting"; | ||||||
|  | import { AllI18nKeys } from "@Component/Localization/Localization"; | ||||||
|  | import { Message } from "@Component/Message/Message"; | ||||||
|  | import "./Parameter.scss"; | ||||||
|  | 
 | ||||||
|  | interface IParameterProps<P extends IParameter = {}> { | ||||||
|  |     option: IParameterOption<P>; | ||||||
|  |     value: IParameterValue<P>; | ||||||
|  |     key: ObjectID; | ||||||
|  |     change: <K extends keyof P>(key: K, val: IParamValue<P[K]>) => any; | ||||||
|  |     i18n: <K extends keyof P>(option: IParameterOptionItem<P[K]>, language: Language) => string; | ||||||
|  |     title?: AllI18nKeys; | ||||||
|  |     titleOption?: Record<string, string>; | ||||||
|  |     isFirst?: boolean; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @useSettingWithEvent("language") | ||||||
|  | class Parameter<P extends IParameter> extends Component<IParameterProps<P> & IMixinSettingProps> { | ||||||
|  | 
 | ||||||
|  |     private renderParameter<K extends keyof P> | ||||||
|  |     (key: K, option: IParameterOptionItem<P[K]>, value: IParamValue<P[K]>): 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 <AttrInput | ||||||
|  |                 key={indexKey} | ||||||
|  |                 id={indexKey} | ||||||
|  |                 keyI18n="Panel.Info.Behavior.Details.Parameter.Key" | ||||||
|  |                 keyI18nOption={{ key: i18nString }} | ||||||
|  |                 isNumber={true} | ||||||
|  |                 step={option.numberStep} | ||||||
|  |                 maxLength={option.maxLength} | ||||||
|  |                 max={option.numberMax} | ||||||
|  |                 min={option.numberMin} | ||||||
|  |                 value={value as IParamValue<"number"> ?? 0} | ||||||
|  |                 valueChange={(val) => { | ||||||
|  |                     this.props.change(key, parseFloat(val) as IParamValue<P[K]>); | ||||||
|  |                 }} | ||||||
|  |             />; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         else if (type === "string") { | ||||||
|  |             return <AttrInput | ||||||
|  |                 key={indexKey} | ||||||
|  |                 id={indexKey} | ||||||
|  |                 keyI18n="Panel.Info.Behavior.Details.Parameter.Key" | ||||||
|  |                 keyI18nOption={{ key: i18nString }} | ||||||
|  |                 maxLength={option.maxLength} | ||||||
|  |                 value={value as IParamValue<"string"> ?? ""} | ||||||
|  |                 valueChange={(val) => { | ||||||
|  |                     this.props.change(key, val as IParamValue<P[K]>); | ||||||
|  |                 }} | ||||||
|  |             />; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         else if (type === "boolean") { | ||||||
|  |             return <TogglesInput | ||||||
|  |                 key={indexKey} | ||||||
|  |                 keyI18n="Panel.Info.Behavior.Details.Parameter.Key" | ||||||
|  |                 keyI18nOption={{ key: i18nString }} | ||||||
|  | 				onIconName={option.iconName} | ||||||
|  |                 value={value as IParamValue<"boolean"> ?? false} | ||||||
|  | 				valueChange={(val) => { | ||||||
|  | 					this.props.change(key, val as IParamValue<P[K]>); | ||||||
|  | 				}} | ||||||
|  | 			/> | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         else if (isObjectType(type)) { | ||||||
|  | 
 | ||||||
|  |             type IObjectParamValue = IParamValue<"G" | "R" | "LG" | "LR">; | ||||||
|  |             const typedValue = value as IObjectParamValue; | ||||||
|  | 
 | ||||||
|  |             return <ObjectPicker | ||||||
|  |                 key={indexKey} | ||||||
|  |                 keyI18n="Panel.Info.Behavior.Details.Parameter.Key" | ||||||
|  |                 keyI18nOption={{ key: i18nString }} | ||||||
|  |                 type={type} | ||||||
|  |                 value={typedValue.picker} | ||||||
|  |                 valueChange={(obj) => { | ||||||
|  |                     typedValue.picker = obj as IObjectParamValue["picker"]; | ||||||
|  |                     this.props.change(key, typedValue as IParamValue<P[K]>); | ||||||
|  |                 }} | ||||||
|  |                 cleanValue={() => { | ||||||
|  |                     typedValue.picker = undefined as IObjectParamValue["picker"]; | ||||||
|  |                     this.props.change(key, typedValue as IParamValue<P[K]>); | ||||||
|  |                 }} | ||||||
|  |             /> | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         else if (isVectorType(type)) { | ||||||
|  | 
 | ||||||
|  |             type IObjectParamValue = IParamValue<"vec">; | ||||||
|  |             const typedValue = value as IObjectParamValue; | ||||||
|  |              | ||||||
|  |             return <Fragment key={indexKey}> | ||||||
|  |                  | ||||||
|  |                 <AttrInput | ||||||
|  |                     key={`${indexKey}-X`} | ||||||
|  |                     id={indexKey} | ||||||
|  |                     keyI18n="Panel.Info.Behavior.Details.Parameter.Key.Vec.X" | ||||||
|  |                     keyI18nOption={{ key: i18nString }} | ||||||
|  |                     isNumber={true} | ||||||
|  |                     step={option.numberStep} | ||||||
|  |                     maxLength={option.maxLength} | ||||||
|  |                     max={option.numberMax} | ||||||
|  |                     min={option.numberMin} | ||||||
|  |                     value={typedValue[0] ?? 0} | ||||||
|  |                     valueChange={(val) => { | ||||||
|  |                         typedValue[0] = parseFloat(val); | ||||||
|  |                         this.props.change(key, typedValue as IParamValue<P[K]>); | ||||||
|  |                     }} | ||||||
|  |                 /> | ||||||
|  | 
 | ||||||
|  |                 <AttrInput | ||||||
|  |                     key={`${indexKey}-Y`} | ||||||
|  |                     id={indexKey} | ||||||
|  |                     keyI18n="Panel.Info.Behavior.Details.Parameter.Key.Vec.Y" | ||||||
|  |                     keyI18nOption={{ key: i18nString }} | ||||||
|  |                     isNumber={true} | ||||||
|  |                     step={option.numberStep} | ||||||
|  |                     maxLength={option.maxLength} | ||||||
|  |                     max={option.numberMax} | ||||||
|  |                     min={option.numberMin} | ||||||
|  |                     value={typedValue[1] ?? 0} | ||||||
|  |                     valueChange={(val) => { | ||||||
|  |                         typedValue[1] = parseFloat(val); | ||||||
|  |                         this.props.change(key, typedValue as IParamValue<P[K]>); | ||||||
|  |                     }} | ||||||
|  |                 /> | ||||||
|  | 
 | ||||||
|  |                 <AttrInput | ||||||
|  |                     key={`${indexKey}-Z`} | ||||||
|  |                     id={indexKey} | ||||||
|  |                     keyI18n="Panel.Info.Behavior.Details.Parameter.Key.Vec.Z" | ||||||
|  |                     keyI18nOption={{ key: i18nString }} | ||||||
|  |                     isNumber={true} | ||||||
|  |                     step={option.numberStep} | ||||||
|  |                     maxLength={option.maxLength} | ||||||
|  |                     max={option.numberMax} | ||||||
|  |                     min={option.numberMin} | ||||||
|  |                     value={typedValue[2] ?? 0} | ||||||
|  |                     valueChange={(val) => { | ||||||
|  |                         typedValue[2] = parseFloat(val); | ||||||
|  |                         this.props.change(key, typedValue as IParamValue<P[K]>); | ||||||
|  |                     }} | ||||||
|  |                 /> | ||||||
|  | 
 | ||||||
|  |             </Fragment> | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         else { | ||||||
|  |             return <Fragment key={indexKey}/> | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private renderAllParameter(key: Array<keyof P>) { | ||||||
|  |         return key.map((key) => { | ||||||
|  |             return this.renderParameter( | ||||||
|  |                 key, | ||||||
|  |                 this.props.option[key], | ||||||
|  |                 this.props.value[key], | ||||||
|  |             ); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     public render(): ReactNode { | ||||||
|  |         const allOptionKeys: Array<keyof P> = Object.getOwnPropertyNames(this.props.option); | ||||||
|  |          | ||||||
|  |         return <> | ||||||
|  |          | ||||||
|  |             { | ||||||
|  |                 allOptionKeys.length <= 0 && this.props.title ? | ||||||
|  |                     <Message | ||||||
|  |                         isTitle | ||||||
|  |                         first={this.props.isFirst} | ||||||
|  |                         i18nKey={this.props.title} | ||||||
|  |                         options={this.props.titleOption} | ||||||
|  |                     /> : null | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             { | ||||||
|  |                 this.renderAllParameter(allOptionKeys) | ||||||
|  |             } | ||||||
|  |          | ||||||
|  |         </> | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export { Parameter } | ||||||
| @ -11,7 +11,8 @@ import { Setting } from "./Setting"; | |||||||
| import { I18N } from "@Component/Localization/Localization"; | import { I18N } from "@Component/Localization/Localization"; | ||||||
| import { superConnectWithEvent, superConnect } from "./Context"; | import { superConnectWithEvent, superConnect } from "./Context"; | ||||||
| import { PopupController } from "./Popups"; | 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"; | import { Actuator } from "@Model/Actuator"; | ||||||
| 
 | 
 | ||||||
| function randomColor(unNormal: boolean = false) { | function randomColor(unNormal: boolean = false) { | ||||||
| @ -208,7 +209,7 @@ class Status extends Emitter<IStatusEvent> { | |||||||
|     /** |     /** | ||||||
|      * 修改群属性 |      * 修改群属性 | ||||||
|      */ |      */ | ||||||
|     public changeBehaviorAttrib<K extends IBehaviorParameter, P extends keyof K | keyof Behavior<K>> |     public changeBehaviorAttrib<K extends IParameter, P extends keyof K | keyof Behavior<K>> | ||||||
|     (id: ObjectID, key: P, val: IParamValue<K[P]>, noParameter?: boolean) { |     (id: ObjectID, key: P, val: IParamValue<K[P]>, noParameter?: boolean) { | ||||||
|         const behavior = this.model.getBehaviorById(id); |         const behavior = this.model.getBehaviorById(id); | ||||||
|         if (behavior) { |         if (behavior) { | ||||||
|  | |||||||
| @ -3,17 +3,17 @@ import type { Individual } from "./Individual"; | |||||||
| import type { Group } from "./Group"; | import type { Group } from "./Group"; | ||||||
| import type { Model } from "./Model"; | import type { Model } from "./Model"; | ||||||
| import { | import { | ||||||
|     IParamValue, isObjectType, isVectorType, |     IParamValue, isObjectType, isVectorType, getDefaultValue, | ||||||
|     IBehaviorParameterOptionItem, IBehaviorParameter, IBehaviorParameterOption, IBehaviorParameterValue |     IParameterOptionItem, IParameter, IParameterOption, IParameterValue | ||||||
| } from "./Parameter"; | } from "./Parameter"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 行为构造函数类型 |  * 行为构造函数类型 | ||||||
|  */ |  */ | ||||||
| type IBehaviorConstructor< | type IBehaviorConstructor< | ||||||
|     P extends IBehaviorParameter = {}, |     P extends IParameter = {}, | ||||||
|     E extends Record<EventType, any> = {} |     E extends Record<EventType, any> = {} | ||||||
| > = new (id: string, parameter: IBehaviorParameterValue<P>) => Behavior<P, E>; | > = new (id: string, parameter: IParameterValue<P>) => Behavior<P, E>; | ||||||
| 
 | 
 | ||||||
| type IAnyBehavior = Behavior<any, any>; | type IAnyBehavior = Behavior<any, any>; | ||||||
| type IAnyBehaviorRecorder = BehaviorRecorder<any, any>; | type IAnyBehaviorRecorder = BehaviorRecorder<any, any>; | ||||||
| @ -75,7 +75,7 @@ class BehaviorInfo<E extends Record<EventType, any> = {}> extends Emitter<E> { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class BehaviorRecorder< | class BehaviorRecorder< | ||||||
|     P extends IBehaviorParameter = {}, |     P extends IParameter = {}, | ||||||
|     E extends Record<EventType, any> = {} |     E extends Record<EventType, any> = {} | ||||||
| > extends BehaviorInfo<{}> { | > extends BehaviorInfo<{}> { | ||||||
| 
 | 
 | ||||||
| @ -104,62 +104,13 @@ class BehaviorRecorder< | |||||||
|     /** |     /** | ||||||
|      * 对象参数列表 |      * 对象参数列表 | ||||||
|      */ |      */ | ||||||
|     public parameterOption: IBehaviorParameterOption<P>; |     public parameterOption: IParameterOption<P>; | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 获取参数列表的默认值 |  | ||||||
|      */ |  | ||||||
|     public getDefaultValue(): IBehaviorParameterValue<P> { |  | ||||||
|         let defaultObj = {} as IBehaviorParameterValue<P>; |  | ||||||
|         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 new(): Behavior<P, E> { |     public new(): Behavior<P, E> { | ||||||
|         return new this.behavior(this.getNextId(), this.getDefaultValue()); |         return new this.behavior(this.getNextId(), getDefaultValue(this.parameterOption)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public constructor(behavior: IBehaviorConstructor<P, E>) { |     public constructor(behavior: IBehaviorConstructor<P, E>) { | ||||||
| @ -180,7 +131,7 @@ class BehaviorRecorder< | |||||||
|  * 群体的某种行为 |  * 群体的某种行为 | ||||||
|  */ |  */ | ||||||
| class Behavior< | class Behavior< | ||||||
|     P extends IBehaviorParameter = {}, |     P extends IParameter = {}, | ||||||
|     E extends Record<EventType, any> = {} |     E extends Record<EventType, any> = {} | ||||||
| > extends BehaviorInfo<E> { | > extends BehaviorInfo<E> { | ||||||
| 
 | 
 | ||||||
| @ -208,14 +159,14 @@ class Behavior< | |||||||
|     /** |     /** | ||||||
|      * 行为参数 |      * 行为参数 | ||||||
|      */ |      */ | ||||||
|     public parameter: IBehaviorParameterValue<P>; |     public parameter: IParameterValue<P>; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 对象参数列表 |      * 对象参数列表 | ||||||
|      */ |      */ | ||||||
|     public parameterOption: IBehaviorParameterOption<P> = {} as any; |     public parameterOption: IParameterOption<P> = {} as any; | ||||||
| 
 | 
 | ||||||
|     public constructor(id: string, parameter: IBehaviorParameterValue<P>) { |     public constructor(id: string, parameter: IParameterValue<P>) { | ||||||
|         super(); |         super(); | ||||||
|         this.id = id; |         this.id = id; | ||||||
|         this.parameter = parameter; |         this.parameter = parameter; | ||||||
| @ -299,8 +250,6 @@ class Behavior< | |||||||
| type IRenderBehavior = BehaviorInfo | Behavior; | type IRenderBehavior = BehaviorInfo | Behavior; | ||||||
| 
 | 
 | ||||||
| export { | export { | ||||||
|     Behavior, BehaviorRecorder, IBehaviorParameterOption, IBehaviorParameterOptionItem, IParamValue, |     Behavior, BehaviorRecorder, IAnyBehavior, IAnyBehaviorRecorder, BehaviorInfo, IRenderBehavior | ||||||
|     IAnyBehavior, IAnyBehaviorRecorder, BehaviorInfo, IRenderBehavior, IBehaviorParameter, |  | ||||||
|     isObjectType, isVectorType |  | ||||||
| }; | }; | ||||||
| export default { Behavior }; | export default { Behavior }; | ||||||
| @ -5,7 +5,8 @@ import { Emitter, EventType, EventMixin } from "./Emitter"; | |||||||
| import { CtrlObject } from "./CtrlObject"; | import { CtrlObject } from "./CtrlObject"; | ||||||
| import { ObjectID, AbstractRenderer } from "./Renderer"; | import { ObjectID, AbstractRenderer } from "./Renderer"; | ||||||
| import { Label } from "./Label"; | import { Label } from "./Label"; | ||||||
| import { Behavior, IAnyBehavior, IAnyBehaviorRecorder, IParamValue } from "./Behavior"; | import { Behavior, IAnyBehavior, IAnyBehaviorRecorder } from "./Behavior"; | ||||||
|  | import { IParamValue } from "@Model/Parameter";  | ||||||
| 
 | 
 | ||||||
| type ModelEvent = { | type ModelEvent = { | ||||||
|     labelChange: Label[]; |     labelChange: Label[]; | ||||||
|  | |||||||
| @ -39,26 +39,26 @@ type IParamValue<K extends IParamType> = AllMapType[K]; | |||||||
| /** | /** | ||||||
|  * 特殊对象类型判定 |  * 特殊对象类型判定 | ||||||
|  */ |  */ | ||||||
| const objectTypeListEnumSet = new Set<IParamType>(["R", "G", "LR", "LG"]); | const objectTypeListEnumSet = new Set<string>(["R", "G", "LR", "LG"]); | ||||||
| 
 | 
 | ||||||
|  /** |  /** | ||||||
|   * 对象断言表达式 |   * 对象断言表达式 | ||||||
|   */ |   */ | ||||||
| function isObjectType(key: IParamType): key is IVectorType { | function isObjectType(key: string): key is IVectorType { | ||||||
|     return objectTypeListEnumSet.has(key); |     return objectTypeListEnumSet.has(key); | ||||||
| } | } | ||||||
|   |   | ||||||
|  /** |  /** | ||||||
|   * 向量断言表达式 |   * 向量断言表达式 | ||||||
|   */ |   */ | ||||||
| function isVectorType(key: IParamType): key is IObjectType { | function isVectorType(key: string): key is IObjectType { | ||||||
|     return key === "vec"; |     return key === "vec"; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 模型参数类型 |  * 模型参数类型 | ||||||
|  */ |  */ | ||||||
| interface IBehaviorParameterOptionItem<T extends IParamType = IParamType> { | interface IParameterOptionItem<T extends IParamType = IParamType> { | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 参数类型 |      * 参数类型 | ||||||
| @ -102,25 +102,72 @@ interface IBehaviorParameterOptionItem<T extends IParamType = IParamType> { | |||||||
|     iconName?: string; |     iconName?: string; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface IBehaviorParameter { | interface IParameter { | ||||||
|     [x: string]: IParamType; |     [x: string]: IParamType; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 参数类型列表 |  * 参数类型列表 | ||||||
|  */ |  */ | ||||||
| type IBehaviorParameterOption<P extends IBehaviorParameter> = { | type IParameterOption<P extends IParameter> = { | ||||||
|     [X in keyof P]: IBehaviorParameterOptionItem<P[X]>; |     [X in keyof P]: IParameterOptionItem<P[X]>; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 参数类型列表映射到参数对象 |  * 参数类型列表映射到参数对象 | ||||||
|  */ |  */ | ||||||
| type IBehaviorParameterValue<P extends IBehaviorParameter> = { | type IParameterValue<P extends IParameter> = { | ||||||
|     [X in keyof P]: IParamValue<P[X]> |     [X in keyof P]: IParamValue<P[X]> | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export { | function getDefaultValue<P extends IParameter> (option: IParameterOption<P>): IParameterValue<P> { | ||||||
|     IParamType, IParamValue, isObjectType, isVectorType, |     let defaultObj = {} as IParameterValue<P>; | ||||||
|     IBehaviorParameterOptionItem, IBehaviorParameter, IBehaviorParameterOption, IBehaviorParameterValue |     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, getDefaultValue, | ||||||
|  |     IParameterOptionItem, IParameter, IParameterOption, IParameterValue | ||||||
| } | } | ||||||
| @ -55,16 +55,16 @@ class SimulatorWeb extends Component { | |||||||
|             this.status.model.update(0); |             this.status.model.update(0); | ||||||
|             this.status.newLabel().name = "New Label"; |             this.status.newLabel().name = "New Label"; | ||||||
|             this.status.newLabel().name = "Test Label 01"; |             this.status.newLabel().name = "Test Label 01"; | ||||||
|             // let template = this.status.model.addBehavior(AllBehaviors[0]);
 |             let template = this.status.model.addBehavior(AllBehaviors[0]); | ||||||
|             // template.name = "Template"; template.color = [150, 20, 220];
 |             template.name = "Template"; template.color = [150, 20, 220]; | ||||||
|             let dynamic = this.status.model.addBehavior(AllBehaviors[0]); |             let dynamic = this.status.model.addBehavior(AllBehaviors[1]); | ||||||
|             dynamic.name = "Dynamic"; dynamic.color = [250, 200, 80]; |             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]; |             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.name = "Boundary"; boundary.color = [80, 200, 250]; | ||||||
|             // boundary.parameter.range.picker = this.status.model.allRangeLabel;
 |             boundary.parameter.range.picker = this.status.model.allRangeLabel; | ||||||
|             // group.addBehavior(template);
 |             group.addBehavior(template); | ||||||
|             group.addBehavior(dynamic); |             group.addBehavior(dynamic); | ||||||
|             group.addBehavior(brownian); |             group.addBehavior(brownian); | ||||||
|             group.addBehavior(boundary); |             group.addBehavior(boundary); | ||||||
|  | |||||||
| @ -1,13 +1,13 @@ | |||||||
| import { Component, Fragment, ReactNode} from "react"; | import { Component, ReactNode} from "react"; | ||||||
| import { useSettingWithEvent, IMixinSettingProps } from "@Context/Setting"; | import { useSettingWithEvent, IMixinSettingProps } from "@Context/Setting"; | ||||||
| import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status"; | 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 { Message } from "@Component/Message/Message"; | ||||||
| import { AttrInput } from "@Component/AttrInput/AttrInput"; | import { AttrInput } from "@Component/AttrInput/AttrInput"; | ||||||
| import { ColorInput } from "@Component/ColorInput/ColorInput"; | import { ColorInput } from "@Component/ColorInput/ColorInput"; | ||||||
| import { TogglesInput } from "@Component/TogglesInput/TogglesInput"; | import { TogglesInput } from "@Component/TogglesInput/TogglesInput"; | ||||||
| import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup"; | import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup"; | ||||||
| import { ObjectPicker } from "@Component/ObjectPicker/ObjectPicker"; | import { Parameter } from "@Component/Parameter/Parameter"; | ||||||
| import "./BehaviorDetails.scss"; | import "./BehaviorDetails.scss"; | ||||||
| 
 | 
 | ||||||
| interface IBehaviorDetailsProps {} | interface IBehaviorDetailsProps {} | ||||||
| @ -16,10 +16,23 @@ interface IBehaviorDetailsProps {} | |||||||
| @useStatusWithEvent("focusBehaviorChange", "behaviorAttrChange") | @useStatusWithEvent("focusBehaviorChange", "behaviorAttrChange") | ||||||
| class BehaviorDetails extends Component<IBehaviorDetailsProps & IMixinStatusProps & IMixinSettingProps> { | class BehaviorDetails extends Component<IBehaviorDetailsProps & IMixinStatusProps & IMixinSettingProps> { | ||||||
| 
 | 
 | ||||||
| 	private renderFrom<T extends IBehaviorParameter> |     private handelDeleteBehavior = (behavior: IAnyBehavior) => { | ||||||
|     (behavior: Behavior<T, Record<string, any>>): ReactNode { |         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(); | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|         const allParameterKeys = Object.getOwnPropertyNames(behavior.parameterOption); | 	private renderFrom(behavior: IAnyBehavior): ReactNode { | ||||||
|          |          | ||||||
| 		return <> | 		return <> | ||||||
|          |          | ||||||
| @ -44,145 +57,29 @@ class BehaviorDetails extends Component<IBehaviorDetailsProps & IMixinStatusProp | |||||||
| 				keyI18n="Common.Attr.Key.Delete" red | 				keyI18n="Common.Attr.Key.Delete" red | ||||||
| 				onIconName="delete" offIconName="delete" | 				onIconName="delete" offIconName="delete" | ||||||
| 				valueChange={() => { | 				valueChange={() => { | ||||||
| 					if (this.props.status) { | 					this.handelDeleteBehavior(behavior) | ||||||
|                         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(); |  | ||||||
|                             } |  | ||||||
|                         }) |  | ||||||
|                     } |  | ||||||
| 				}} | 				}} | ||||||
| 			/> | 			/> | ||||||
|              |              | ||||||
|             { |             <Parameter | ||||||
|                 allParameterKeys.length > 0 ? |                 key={behavior.id} | ||||||
|                     <Message |                 option={behavior.parameterOption} | ||||||
|                         isTitle |                 value={behavior.parameter} | ||||||
|                         i18nKey="Panel.Info.Behavior.Details.Behavior.Props" |                 i18n={(option, language) => behavior.getTerms(option.name, language)} | ||||||
|                         options={{ |                 title={"Panel.Info.Behavior.Details.Behavior.Props"} | ||||||
|  |                 titleOption={{ | ||||||
|                     behavior: behavior.getTerms(behavior.behaviorName, this.props.setting?.language) |                     behavior: behavior.getTerms(behavior.behaviorName, this.props.setting?.language) | ||||||
|                 }} |                 }} | ||||||
|                     /> : null |                 change={(key, value) => { | ||||||
|             } |                     this.props.status?.changeBehaviorAttrib( | ||||||
|              |                         behavior.id, key as string, value | ||||||
|             { |                     ); | ||||||
|                 allParameterKeys.map((key) => { |                 }} | ||||||
|                     return this.renderParameter(behavior, key); |             /> | ||||||
|                 }) |  | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|         </>; |         </>; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|     private renderParameter<T extends IBehaviorParameter> |  | ||||||
|     (behavior: Behavior<T, Record<string, any>>, key: keyof T): ReactNode { |  | ||||||
|         const type = behavior.parameterOption[key]; |  | ||||||
|         const value = behavior.parameter[key]; |  | ||||||
|         const indexKey = `${behavior.id}-${key}`; |  | ||||||
| 
 |  | ||||||
|         if (type.type === "number") { |  | ||||||
|             return <AttrInput |  | ||||||
|                 keyI18n="Panel.Info.Behavior.Details.Parameter.Key" |  | ||||||
|                 keyI18nOption={{ key: behavior.getTerms(type.name, this.props.setting?.language) }} |  | ||||||
|                 key={indexKey} id={indexKey} isNumber={true} step={type.numberStep} maxLength={type.maxLength} |  | ||||||
|                 max={type.numberMax} min={type.numberMin} |  | ||||||
|                 value={(value as number) ?? 0} |  | ||||||
|                 valueChange={(val) => { |  | ||||||
|                     this.props.status?.changeBehaviorAttrib(behavior.id, key as string, (val as any) / 1); |  | ||||||
|                 }} |  | ||||||
|             /> |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (type.type === "string") { |  | ||||||
|             return <AttrInput |  | ||||||
|                 keyI18n="Panel.Info.Behavior.Details.Parameter.Key" |  | ||||||
|                 keyI18nOption={{ key: behavior.getTerms(type.name, this.props.setting?.language) }} |  | ||||||
|                 key={indexKey} id={indexKey} maxLength={type.maxLength} |  | ||||||
|                 value={(value as string) ?? ""} |  | ||||||
|                 valueChange={(val) => { |  | ||||||
|                     this.props.status?.changeBehaviorAttrib(behavior.id, key as string, val); |  | ||||||
|                 }} |  | ||||||
|             /> |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (type.type === "boolean") { |  | ||||||
|             return <TogglesInput |  | ||||||
|                 keyI18n="Panel.Info.Behavior.Details.Parameter.Key" |  | ||||||
|                 keyI18nOption={{ key: behavior.getTerms(type.name, this.props.setting?.language) }} |  | ||||||
| 				onIconName={type.iconName} key={indexKey} value={(value as boolean) ?? false} |  | ||||||
| 				valueChange={(val) => { |  | ||||||
| 					this.props.status?.changeBehaviorAttrib(behavior.id, key as string, val); |  | ||||||
| 				}} |  | ||||||
| 			/> |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (isObjectType(type.type as any)) { |  | ||||||
|             return <ObjectPicker |  | ||||||
|                 keyI18n="Panel.Info.Behavior.Details.Parameter.Key" |  | ||||||
|                 keyI18nOption={{ key: behavior.getTerms(type.name, this.props.setting?.language) }} |  | ||||||
|                 type={type.type} value={(value as any).picker} key={indexKey} |  | ||||||
|                 valueChange={(obj) => { |  | ||||||
|                     (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 <Fragment key={indexKey}> |  | ||||||
|                  |  | ||||||
|                 <AttrInput |  | ||||||
|                     keyI18n="Panel.Info.Behavior.Details.Parameter.Key.Vec.X" |  | ||||||
|                     keyI18nOption={{ key: behavior.getTerms(type.name, this.props.setting?.language) }} |  | ||||||
|                     key={`${indexKey}-X`} id={indexKey} isNumber={true} step={type.numberStep} maxLength={type.maxLength} |  | ||||||
|                     max={type.numberMax} min={type.numberMin} |  | ||||||
|                     value={(value as number[])[0] ?? 0} |  | ||||||
|                     valueChange={(val) => { |  | ||||||
|                         (value as number[])[0] = (val as any) / 1; |  | ||||||
|                         this.props.status?.changeBehaviorAttrib(behavior.id, key as string, value); |  | ||||||
|                     }} |  | ||||||
|                 /> |  | ||||||
| 
 |  | ||||||
|                 <AttrInput |  | ||||||
|                     keyI18n="Panel.Info.Behavior.Details.Parameter.Key.Vec.Y" |  | ||||||
|                     keyI18nOption={{ key: behavior.getTerms(type.name, this.props.setting?.language) }} |  | ||||||
|                     key={`${indexKey}-Y`} id={indexKey} isNumber={true} step={type.numberStep} maxLength={type.maxLength} |  | ||||||
|                     max={type.numberMax} min={type.numberMin} |  | ||||||
|                     value={(value as number[])[1] ?? 0} |  | ||||||
|                     valueChange={(val) => { |  | ||||||
|                         (value as number[])[1] = (val as any) / 1; |  | ||||||
|                         this.props.status?.changeBehaviorAttrib(behavior.id, key as string, value); |  | ||||||
|                     }} |  | ||||||
|                 /> |  | ||||||
| 
 |  | ||||||
|                 <AttrInput |  | ||||||
|                     keyI18n="Panel.Info.Behavior.Details.Parameter.Key.Vec.Z" |  | ||||||
|                     keyI18nOption={{ key: behavior.getTerms(type.name, this.props.setting?.language) }} |  | ||||||
|                     key={`${indexKey}-Z`} id={indexKey} isNumber={true} step={type.numberStep} maxLength={type.maxLength} |  | ||||||
|                     max={type.numberMax} min={type.numberMin} |  | ||||||
|                     value={(value as number[])[2] ?? 0} |  | ||||||
|                     valueChange={(val) => { |  | ||||||
|                         (value as number[])[2] = (val as any) / 1; |  | ||||||
|                         this.props.status?.changeBehaviorAttrib(behavior.id, key as string, value); |  | ||||||
|                     }} |  | ||||||
|                 /> |  | ||||||
| 
 |  | ||||||
|             </Fragment> |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return <Fragment key={indexKey}/> |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 	public render(): ReactNode { | 	public render(): ReactNode { | ||||||
| 		if (this.props.status && this.props.status.focusBehavior) { | 		if (this.props.status && this.props.status.focusBehavior) { | ||||||
|             return this.renderFrom(this.props.status.focusBehavior); |             return this.renderFrom(this.props.status.focusBehavior); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user