base bt
This commit is contained in:
parent
c43c192a3e
commit
dffc73c30f
18 changed files with 525 additions and 134 deletions
|
@ -32,6 +32,3 @@ export const StringExtensions = () => {
|
|||
};
|
||||
}
|
||||
};
|
||||
// python3 /Users/idontsudo/framework/path.py --path /Users/idontsudo/webservice/server/build/public/0a3422cc-f2e3-4abc-87d8-ae13b8b6d26d/ --env /Users/idontsudo/framework/cad_generation/env.json
|
||||
// python3 /Users/idontsudo/framework/path.py --path /Users/idontsudo/webservice/server/build/public/0a3422cc-f2e3-4abc-87d8-ae13b8b6d26d/ --env /Users/idontsudo/framework/cad_generation/env.json
|
||||
// /Users/idontsudo/Desktop/FreeCAD.app/Contents/MacOS/FreeCAD /Users/idontsudo/framework/cad_generation/main.py
|
||||
|
|
6
ui/package-lock.json
generated
6
ui/package-lock.json
generated
|
@ -6882,9 +6882,9 @@
|
|||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
|
||||
},
|
||||
"node_modules/diff-sequences": {
|
||||
"node_modules/diff-Sequence": {
|
||||
"version": "27.5.1",
|
||||
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/diff-Sequence/-/diff-Sequence-27.5.1.tgz",
|
||||
"integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==",
|
||||
"engines": {
|
||||
"node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
|
||||
|
@ -10154,7 +10154,7 @@
|
|||
"integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==",
|
||||
"dependencies": {
|
||||
"chalk": "^4.0.0",
|
||||
"diff-sequences": "^27.5.1",
|
||||
"diff-Sequence": "^27.5.1",
|
||||
"jest-get-type": "^27.5.1",
|
||||
"pretty-format": "^27.5.1"
|
||||
},
|
||||
|
|
|
@ -4,8 +4,12 @@ import { NumberExtensions } from "./number";
|
|||
import { StringExtensions } from "./string";
|
||||
|
||||
export type CallBackVoidFunction = <T>(value: T) => void;
|
||||
|
||||
export type CallBackStringVoidFunction = (value: string) => void;
|
||||
export type CallBackEventTarget = (value: EventTarget) => void;
|
||||
export type OptionalProperties<T> = {
|
||||
[P in keyof T]?: T[P];
|
||||
};
|
||||
|
||||
declare global {
|
||||
interface Array<T> {
|
||||
|
@ -25,8 +29,12 @@ declare global {
|
|||
}
|
||||
interface Map<K, V> {
|
||||
addValueOrMakeCallback(key: K, value: V, callBack: CallBackVoidFunction): void;
|
||||
getKeyFromValueIsExists(value: V): K | undefined;
|
||||
overrideValue(key: K, value: OptionalProperties<V>): void;
|
||||
keysToJson(): string;
|
||||
toArray(): V[];
|
||||
getPredicateValue(callBack: (value: V) => boolean): K[];
|
||||
}
|
||||
interface Vector3 {}
|
||||
}
|
||||
export const extensions = () => {
|
||||
ArrayExtensions();
|
||||
|
|
|
@ -11,5 +11,51 @@ export const MapExtensions = () => {
|
|||
}
|
||||
};
|
||||
}
|
||||
if (Map.prototype.getKeyFromValueIsExists === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Map.prototype.getKeyFromValueIsExists = function (value) {
|
||||
let result;
|
||||
this.forEach((el, key) => {
|
||||
if (el === value) {
|
||||
result = key;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
}
|
||||
if (Map.prototype.overrideValue === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Map.prototype.overrideValue = function (key, value) {
|
||||
const result = this.get(key);
|
||||
|
||||
this.set(key, Object.assign(result, value));
|
||||
};
|
||||
}
|
||||
if (Map.prototype.keysToJson === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Map.prototype.keysToJson = function () {
|
||||
const result: any[] = [];
|
||||
this.forEach((el) => result.push(el));
|
||||
return JSON.stringify(result);
|
||||
};
|
||||
}
|
||||
if (Map.prototype.toArray === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Map.prototype.toArray = function () {
|
||||
return Array.from(this.values());
|
||||
};
|
||||
}
|
||||
if (Map.prototype.getPredicateValue === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Map.prototype.getPredicateValue = function (callBack) {
|
||||
const result: any[] = [];
|
||||
this.forEach((el, key) => {
|
||||
const callBackExecute = callBack(el);
|
||||
if (callBackExecute) {
|
||||
result.push(key);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
}
|
||||
};
|
||||
Object();
|
||||
|
|
84
ui/src/features/behavior_tree_builder/model/editor_view.ts
Normal file
84
ui/src/features/behavior_tree_builder/model/editor_view.ts
Normal file
|
@ -0,0 +1,84 @@
|
|||
import { TypedEvent } from "../../../core/helper/typed_event";
|
||||
import { NodeEditor } from "rete";
|
||||
import { AreaExtra, Schemes } from "../presentation/ui/editor/editor";
|
||||
import { Result } from "../../../core/helper/result";
|
||||
import { AreaPlugin } from "rete-area-plugin";
|
||||
|
||||
export interface BtDrawDragAndDropView {
|
||||
x: number;
|
||||
y: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export class BtNodeView extends TypedEvent<BtDrawDragAndDropView> {}
|
||||
|
||||
export class ReteObserver extends TypedEvent<any> {}
|
||||
export class BtBuilderModel {
|
||||
public static result = "";
|
||||
static fromReteScene(editor: NodeEditor<Schemes>, area: AreaPlugin<Schemes, AreaExtra>) {
|
||||
this.getFirstSequence(editor).map((sortedSequence) => {
|
||||
const firstNodeId = sortedSequence.getKeyFromValueIsExists(1) as string;
|
||||
this.findSequence(firstNodeId, editor, sortedSequence, 2);
|
||||
this.result += `<${this.getNodeLabelAtId(editor, firstNodeId)} id="${firstNodeId}">`;
|
||||
this.toXML(sortedSequence as Map<string, number>, editor, area, firstNodeId);
|
||||
this.result += `</${this.getNodeLabelAtId(editor, firstNodeId)}>`;
|
||||
});
|
||||
}
|
||||
public static getNodeLabelAtId(editor: NodeEditor<Schemes>, id: string) {
|
||||
return editor.getNode(id).label;
|
||||
}
|
||||
|
||||
static toXML(
|
||||
sortedSequence: Map<string, number>,
|
||||
editor: NodeEditor<Schemes>,
|
||||
area: AreaPlugin<Schemes, AreaExtra>,
|
||||
activeElementId = ""
|
||||
) {
|
||||
editor.getConnections().forEach((el) => {
|
||||
if (el.source === activeElementId) {
|
||||
this.result += `<${this.getNodeLabelAtId(editor, el.target)} id="${el.target}">`;
|
||||
this.toXML(sortedSequence, editor, area, el.target);
|
||||
this.result += `</${this.getNodeLabelAtId(editor, el.target)}>`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static getBtPriorities(ids: string[]) {}
|
||||
public static findSequence(
|
||||
nodeId: string,
|
||||
editor: NodeEditor<Schemes>,
|
||||
result: Map<string, boolean | number>,
|
||||
lvl: number
|
||||
): Map<string, number> | undefined {
|
||||
const connections: string[] = [];
|
||||
|
||||
editor.getConnections().forEach((el) => {
|
||||
if (el.source === nodeId) {
|
||||
connections.push(el.target);
|
||||
}
|
||||
});
|
||||
|
||||
if (connections.isEmpty()) return result as Map<string, number>;
|
||||
let lv = lvl;
|
||||
connections.forEach((el) => {
|
||||
result.set(el, lvl);
|
||||
this.findSequence(el, editor, result, (lv += 1));
|
||||
});
|
||||
}
|
||||
public static getFirstSequence(editor: NodeEditor<Schemes>): Result<undefined, Map<string, boolean | number>> {
|
||||
const node = new Map<string, boolean | number>();
|
||||
editor.getNodes().forEach((el) => {
|
||||
node.set(el.id, 1);
|
||||
});
|
||||
|
||||
editor.getConnections().forEach((el) => {
|
||||
node.set(el.target, false);
|
||||
});
|
||||
|
||||
const key = node.getKeyFromValueIsExists(1);
|
||||
if (key === undefined) {
|
||||
return Result.error(undefined);
|
||||
}
|
||||
return Result.ok(node);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import { AreaExtra, Schemes } from "../presentation/ui/editor/editor";
|
||||
import { NodeEditor } from "rete";
|
||||
import { AreaPlugin } from "rete-area-plugin";
|
||||
|
||||
export interface Position2D {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export class NodeBehaviorTree {
|
||||
label: string;
|
||||
id: string;
|
||||
outputs: string[];
|
||||
inputs: string[];
|
||||
connectTo?: string;
|
||||
position: Position2D;
|
||||
|
||||
constructor(label: string, id: string, outputs: string[], inputs: string[], position: Position2D) {
|
||||
this.label = label;
|
||||
this.id = id;
|
||||
this.outputs = outputs;
|
||||
this.inputs = inputs;
|
||||
this.connectTo = undefined;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
static fromReteScene(editor: NodeEditor<Schemes>, area: AreaPlugin<Schemes, AreaExtra>): NodeBehaviorTree[] {
|
||||
const nodes = new Map<string, NodeBehaviorTree>();
|
||||
editor
|
||||
.getNodes()
|
||||
.forEach((el) =>
|
||||
nodes.set(
|
||||
el.id,
|
||||
new NodeBehaviorTree(
|
||||
el.label,
|
||||
el.id,
|
||||
Object.keys(el.outputs),
|
||||
Object.keys(el.inputs),
|
||||
area.nodeViews.get(el.id)!.position
|
||||
)
|
||||
)
|
||||
);
|
||||
editor.getConnections().forEach((el) => console.log(el));
|
||||
editor.getConnections().forEach((el) => nodes.overrideValue(el.target, { connectTo: el.source }));
|
||||
|
||||
return nodes.toArray();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import * as React from "react";
|
||||
import { CoreText, CoreTextType } from "../../../core/ui/text/text";
|
||||
import { Button, Input } from "antd";
|
||||
|
||||
import { useRete } from "rete-react-plugin";
|
||||
import { createEditor } from "./ui/editor/editor";
|
||||
|
@ -12,18 +13,14 @@ const skills = {
|
|||
name: "",
|
||||
children: [
|
||||
{
|
||||
name: "arm",
|
||||
children: [{ name: "move to", interface: "Vector3", out: "vo" }],
|
||||
name: "Actions",
|
||||
children: [{ name: "MoveToPose", interface: "Vector3", out: "vo" }],
|
||||
},
|
||||
{
|
||||
name: "image",
|
||||
name: "Control",
|
||||
children: [
|
||||
{
|
||||
name: "object detection",
|
||||
interface: "image",
|
||||
out: "BoundBox",
|
||||
},
|
||||
{ name: "people detection", interface: "image", out: "BoundBox" },
|
||||
{ name: "Fallback", interface: "Vector3", out: "vo" },
|
||||
{ name: "Sequence", interface: "Vector3", out: "vo" },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
@ -31,8 +28,9 @@ const skills = {
|
|||
export const behaviorTreeBuilderStore = new BehaviorTreeBuilderStore();
|
||||
|
||||
export function BehaviorTreeBuilderScreen() {
|
||||
const [store] = React.useState(behaviorTreeBuilderStore);
|
||||
const store = behaviorTreeBuilderStore;
|
||||
const [ref] = useRete(createEditor);
|
||||
|
||||
React.useEffect(() => {
|
||||
store.init();
|
||||
|
||||
|
@ -62,6 +60,28 @@ export function BehaviorTreeBuilderScreen() {
|
|||
}}
|
||||
>
|
||||
<CoreText text="Robossembler studio" type={CoreTextType.header} />
|
||||
<Button
|
||||
onClick={() => {
|
||||
store.copyBt();
|
||||
}}
|
||||
>
|
||||
copy
|
||||
</Button>
|
||||
<Input onChange={(e) => store.setBt(e.target.value)} placeholder="bt JSON" />
|
||||
<Button
|
||||
onClick={() => {
|
||||
store.copyBt();
|
||||
}}
|
||||
>
|
||||
update value
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
store.validateBt();
|
||||
}}
|
||||
>
|
||||
validate
|
||||
</Button>
|
||||
</div>
|
||||
<div style={{ display: "flex" }}>
|
||||
<div
|
||||
|
|
|
@ -2,23 +2,107 @@ import makeAutoObservable from "mobx-store-inheritance";
|
|||
|
||||
import { HttpError } from "../../../core/repository/http_repository";
|
||||
import { UiErrorState } from "../../../core/store/base_store";
|
||||
import { BtNodeView } from "./ui/editor/editor_view";
|
||||
import { BtBuilderModel, BtNodeView, ReteObserver } from "../model/editor_view";
|
||||
import { NodeEditor } from "rete";
|
||||
import { AreaExtra, Schemes } from "./ui/editor/editor";
|
||||
import { AreaPlugin } from "rete-area-plugin";
|
||||
import { NodeBehaviorTree } from "../model/node_behavior_tree";
|
||||
|
||||
interface I2DArea {
|
||||
x: number;
|
||||
y: number;
|
||||
w: number;
|
||||
h: number;
|
||||
}
|
||||
|
||||
export class BehaviorTreeBuilderStore extends UiErrorState<HttpError> {
|
||||
validateBt() {}
|
||||
area?: I2DArea;
|
||||
errorHandingStrategy: (error: HttpError) => void;
|
||||
btNodeView: BtNodeView = new BtNodeView();
|
||||
reteNode?: ReteObserver;
|
||||
canRun = true;
|
||||
nodes: NodeBehaviorTree[] = [
|
||||
{
|
||||
label: "Sequence",
|
||||
id: "1",
|
||||
outputs: ["a"],
|
||||
inputs: ["a"],
|
||||
position: { x: -488.1303402823156, y: -227.90861570911895 },
|
||||
},
|
||||
{
|
||||
label: "Sequence",
|
||||
id: "2",
|
||||
outputs: ["a"],
|
||||
inputs: ["a"],
|
||||
connectTo: "1",
|
||||
position: { x: 119.95735514023244, y: -182.24902817216878 },
|
||||
},
|
||||
{
|
||||
label: "Sequence",
|
||||
id: "6",
|
||||
outputs: ["a"],
|
||||
inputs: ["a"],
|
||||
connectTo: "2",
|
||||
position: { x: 402.06111561958505, y: -100.97814086372247 },
|
||||
},
|
||||
{
|
||||
label: "MoveToPose",
|
||||
id: "7",
|
||||
outputs: ["a"],
|
||||
inputs: ["a"],
|
||||
connectTo: "6",
|
||||
position: { x: 932.0688448287292, y: 90.10780461923443 },
|
||||
},
|
||||
{
|
||||
label: "MoveToPose",
|
||||
id: "8",
|
||||
outputs: ["a"],
|
||||
inputs: ["a"],
|
||||
connectTo: "6",
|
||||
position: { x: 898.1684213039546, y: -119.80545806921208 },
|
||||
},
|
||||
{
|
||||
label: "MoveToPose",
|
||||
id: "3",
|
||||
outputs: [],
|
||||
inputs: ["a"],
|
||||
connectTo: "1",
|
||||
position: { x: 208.65749743442188, y: 16.256489050033785 },
|
||||
},
|
||||
{
|
||||
label: "MoveToPose",
|
||||
id: "4",
|
||||
outputs: [],
|
||||
inputs: ["a"],
|
||||
connectTo: "1",
|
||||
position: { x: -50.46426323140673, y: 99.22642447650014 },
|
||||
},
|
||||
{
|
||||
label: "Sequence",
|
||||
id: "5",
|
||||
outputs: ["a"],
|
||||
inputs: ["a"],
|
||||
connectTo: "1",
|
||||
position: { x: -234.54554662642457, y: 245.4012710608967 },
|
||||
},
|
||||
];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
canRun = true;
|
||||
draw() {}
|
||||
|
||||
setBt(value: string): void {
|
||||
JSON.parse(value) as BtNodeView[];
|
||||
}
|
||||
bt(editor: NodeEditor<Schemes>, area: AreaPlugin<Schemes, AreaExtra>) {
|
||||
console.log(BtBuilderModel.fromReteScene(editor, area));
|
||||
}
|
||||
copyBt = () => {
|
||||
this.reteNode?.emit("200");
|
||||
};
|
||||
errorHandingStrategy: (error: HttpError) => void;
|
||||
|
||||
dragEnd = (e: EventTarget) => {
|
||||
if (this.canRun) {
|
||||
//@ts-expect-error
|
||||
|
@ -38,16 +122,14 @@ export class BehaviorTreeBuilderStore extends UiErrorState<HttpError> {
|
|||
drawPoint.y < this.area!.y + this.area!.h &&
|
||||
drawPoint.y + drawPoint.h > this.area!.y
|
||||
) {
|
||||
this.drawSkill(x, y - (this.area!.y + this.area!.h / 2), name);
|
||||
this.btNodeView.emit({
|
||||
x: x,
|
||||
y: y - (this.area!.y + this.area!.h / 2),
|
||||
name: name,
|
||||
});
|
||||
}
|
||||
}
|
||||
drawSkill(x: number, y: number, name: string) {
|
||||
this.btNodeView.emit({
|
||||
x: x,
|
||||
y: y,
|
||||
name: name,
|
||||
});
|
||||
}
|
||||
|
||||
async init(): Promise<any> {}
|
||||
dragZoneSetOffset(offsetTop: number, offsetWidth: number, width: number, height: number) {
|
||||
this.area = {
|
||||
|
|
|
@ -14,7 +14,7 @@ const Svg = styled.svg`
|
|||
|
||||
const Path = styled.path<{ styles?: (props: any) => any }>`
|
||||
fill: none;
|
||||
stroke-width: 5px;
|
||||
stroke-width: 3px;
|
||||
stroke: black;
|
||||
pointer-events: auto;
|
||||
${(props) => props.styles && props.styles(props)}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import * as React from "react";
|
||||
import { ClassicPreset } from "rete";
|
||||
import styled from "styled-components";
|
||||
import { $socketsize } from "./vars";
|
||||
|
||||
const Styles = styled.div`
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
border: 1px solid grey;
|
||||
width: ${$socketsize}px;
|
||||
height: ${$socketsize * 2}px;
|
||||
border: 1px solid black;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
background: #fff;
|
||||
background: black;
|
||||
z-index: 2;
|
||||
box-sizing: border-box;
|
||||
&:hover {
|
||||
background: #ddd;
|
||||
}
|
||||
border-radius: 50%;
|
||||
`;
|
||||
|
||||
export function CustomSocket<T extends ClassicPreset.Socket>(props: { data: T }) {
|
||||
|
|
|
@ -3,28 +3,39 @@ import { NodeEditor, GetSchemes, ClassicPreset } from "rete";
|
|||
import { AreaPlugin, AreaExtensions } from "rete-area-plugin";
|
||||
import { ConnectionPlugin, Presets as ConnectionPresets } from "rete-connection-plugin";
|
||||
import { ReactPlugin, Presets, ReactArea2D } from "rete-react-plugin";
|
||||
import { CustomNode } from "./custom_node";
|
||||
import { CustomSocket } from "./custom_socket";
|
||||
import { CustomConnection } from "./custom_connection";
|
||||
import { addCustomBackground } from "./custom_background";
|
||||
import { StyledNode } from "./style_node";
|
||||
import { behaviorTreeBuilderStore } from "../../behavior_tree_builder_screen";
|
||||
import { SequenceNode } from "./nodes/controls_node";
|
||||
import { ReteObserver } from "../../../model/editor_view";
|
||||
|
||||
type Schemes = GetSchemes<ClassicPreset.Node, ClassicPreset.Connection<ClassicPreset.Node, ClassicPreset.Node>>;
|
||||
type AreaExtra = ReactArea2D<Schemes>;
|
||||
export type Schemes = GetSchemes<ClassicPreset.Node, ClassicPreset.Connection<ClassicPreset.Node, ClassicPreset.Node>>;
|
||||
export type AreaExtra = ReactArea2D<Schemes>;
|
||||
|
||||
export async function createEditor(container: HTMLElement) {
|
||||
const socket = new ClassicPreset.Socket("socket");
|
||||
const observer = new ReteObserver();
|
||||
|
||||
behaviorTreeBuilderStore.reteNode = observer;
|
||||
|
||||
behaviorTreeBuilderStore.btNodeView.on(async (event) => {
|
||||
setTimeout(async () => {
|
||||
const node = new ClassicPreset.Node(event.name);
|
||||
|
||||
const { x, y } = areaContainer.area.pointer;
|
||||
console.log(x, y);
|
||||
|
||||
node.addOutput("a", new ClassicPreset.Output(socket));
|
||||
node.addInput("a", new ClassicPreset.Input(socket));
|
||||
await editor.addNode(node);
|
||||
await areaContainer.translate(node.id, { x, y });
|
||||
}, 50);
|
||||
});
|
||||
|
||||
observer.on(() => {
|
||||
behaviorTreeBuilderStore.bt(editor, areaContainer);
|
||||
});
|
||||
|
||||
const editor = new NodeEditor<Schemes>();
|
||||
const areaContainer = new AreaPlugin<Schemes, AreaExtra>(container);
|
||||
const connection = new ConnectionPlugin<Schemes, AreaExtra>();
|
||||
|
@ -38,13 +49,7 @@ export async function createEditor(container: HTMLElement) {
|
|||
Presets.classic.setup({
|
||||
customize: {
|
||||
node(context) {
|
||||
if (context.payload.label === "Fully customized") {
|
||||
return CustomNode;
|
||||
}
|
||||
if (context.payload.label === "Override styles") {
|
||||
return StyledNode;
|
||||
}
|
||||
return Presets.classic.Node;
|
||||
return SequenceNode;
|
||||
},
|
||||
socket(_context) {
|
||||
return CustomSocket;
|
||||
|
@ -66,18 +71,27 @@ export async function createEditor(container: HTMLElement) {
|
|||
|
||||
AreaExtensions.simpleNodesOrder(areaContainer);
|
||||
|
||||
const a = new ClassicPreset.Node("Override styles");
|
||||
for await (const el of behaviorTreeBuilderStore.nodes) {
|
||||
const node = new ClassicPreset.Node(el.label);
|
||||
node.id = el.id;
|
||||
el.outputs.forEach((outputName) => {
|
||||
node.addOutput(outputName, new ClassicPreset.Output(socket));
|
||||
});
|
||||
el.inputs.forEach((inputName) => {
|
||||
node.addInput(inputName, new ClassicPreset.Input(socket));
|
||||
});
|
||||
|
||||
a.addOutput("a", new ClassicPreset.Output(socket));
|
||||
a.addInput("a", new ClassicPreset.Input(socket));
|
||||
await editor.addNode(a);
|
||||
await editor.addNode(node);
|
||||
await areaContainer.translate(node.id, el.position);
|
||||
}
|
||||
|
||||
const b = new ClassicPreset.Node("Fully customized");
|
||||
b.addOutput("a", new ClassicPreset.Output(socket));
|
||||
b.addInput("a", new ClassicPreset.Input(socket));
|
||||
await editor.addNode(b);
|
||||
|
||||
await editor.addConnection(new ClassicPreset.Connection(a, "a", b, "a"));
|
||||
behaviorTreeBuilderStore.nodes.forEach(async (el) => {
|
||||
if (el.connectTo) {
|
||||
const nodeConnectTo = editor.getNode(el.connectTo!);
|
||||
const nodeConnect = editor.getNode(el.id);
|
||||
await editor.addConnection(new ClassicPreset.Connection(nodeConnectTo, "a", nodeConnect, "a"));
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
AreaExtensions.zoomAt(areaContainer, editor.getNodes());
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
import { TypedEvent } from "../../../../../core/helper/typed_event";
|
||||
export interface BtDrawView {
|
||||
x: number;
|
||||
y: number;
|
||||
name: string;
|
||||
}
|
||||
export class BtNodeView extends TypedEvent<BtDrawView> {}
|
|
@ -0,0 +1,167 @@
|
|||
import * as React from "react";
|
||||
import { ClassicScheme, RenderEmit, Presets } from "rete-react-plugin";
|
||||
import styled, { css } from "styled-components";
|
||||
import { $nodewidth, $socketmargin, $socketsize } from "./vars";
|
||||
|
||||
const { RefSocket, RefControl } = Presets.classic;
|
||||
|
||||
type NodeExtraData = { width?: number; height?: number };
|
||||
|
||||
export const NodeStyles = styled.div<NodeExtraData & { selected: boolean; styles?: (props: any) => any }>`
|
||||
background: blue;
|
||||
border: 2px solid grey;
|
||||
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
width: ${(props) => (Number.isFinite(props.width) ? `${props.width}px` : `${$nodewidth}px`)};
|
||||
height: ${(props) => (Number.isFinite(props.height) ? `${props.height}px` : "auto")};
|
||||
padding-bottom: 6px;
|
||||
|
||||
user-select: none;
|
||||
&:hover {
|
||||
background: #333;
|
||||
}
|
||||
${(props) =>
|
||||
props.selected &&
|
||||
css`
|
||||
border-color: red;
|
||||
`}
|
||||
.title {
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
font-size: 18px;
|
||||
padding: 8px;
|
||||
position: relative;
|
||||
top: 12px;
|
||||
text-align-last: center;
|
||||
}
|
||||
.output {
|
||||
text-align: right;
|
||||
}
|
||||
.input {
|
||||
text-align: left;
|
||||
}
|
||||
.output-socket {
|
||||
text-align: right;
|
||||
margin-right: -1px;
|
||||
display: inline-block;
|
||||
}
|
||||
.input-socket {
|
||||
text-align: left;
|
||||
margin-left: -1px;
|
||||
display: inline-block;
|
||||
}
|
||||
.input-title,
|
||||
.output-title {
|
||||
vertical-align: middle;
|
||||
color: white;
|
||||
display: inline-block;
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
margin: ${$socketmargin}px;
|
||||
line-height: ${$socketsize}px;
|
||||
}
|
||||
.input-control {
|
||||
z-index: 1;
|
||||
width: calc(100% - ${$socketsize + 2 * $socketmargin}px);
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
.control {
|
||||
display: block;
|
||||
padding: ${$socketmargin}px ${$socketsize / 2 + $socketmargin}px;
|
||||
}
|
||||
${(props) => props.styles && props.styles(props)}
|
||||
`;
|
||||
|
||||
function sortByIndex<T extends [string, undefined | { index?: number }][]>(entries: T) {
|
||||
entries.sort((a, b) => {
|
||||
const ai = a[1]?.index || 0;
|
||||
const bi = b[1]?.index || 0;
|
||||
|
||||
return ai - bi;
|
||||
});
|
||||
}
|
||||
|
||||
type Props<S extends ClassicScheme> = {
|
||||
data: S["Node"] & NodeExtraData;
|
||||
styles?: () => any;
|
||||
emit: RenderEmit<S>;
|
||||
};
|
||||
export type NodeComponent<Scheme extends ClassicScheme> = (props: Props<Scheme>) => JSX.Element;
|
||||
|
||||
export function SequenceNode<Scheme extends ClassicScheme>(props: Props<Scheme>) {
|
||||
const inputs = Object.entries(props.data.inputs);
|
||||
const outputs = Object.entries(props.data.outputs);
|
||||
const controls = Object.entries(props.data.controls);
|
||||
const selected = props.data.selected || false;
|
||||
|
||||
const { id, label, width, height } = props.data;
|
||||
|
||||
sortByIndex(inputs);
|
||||
sortByIndex(outputs);
|
||||
sortByIndex(controls);
|
||||
|
||||
return (
|
||||
<NodeStyles selected={selected} width={width} height={height} styles={props.styles} data-testid="node">
|
||||
<div
|
||||
onPointerDown={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
className="title"
|
||||
data-testid="title"
|
||||
>
|
||||
{label}
|
||||
<div>{id}</div>
|
||||
</div>
|
||||
|
||||
{outputs.map(
|
||||
([key, output]) =>
|
||||
output && (
|
||||
<div className="output" key={key} data-testid={`output-${key}`}>
|
||||
<div className="output-title" data-testid="output-title">
|
||||
{output?.label}
|
||||
</div>
|
||||
<RefSocket
|
||||
name="output-socket"
|
||||
side="output"
|
||||
emit={props.emit}
|
||||
socketKey={key}
|
||||
nodeId={id}
|
||||
payload={output.socket}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
|
||||
{controls.map(([key, control]) => {
|
||||
return control ? <RefControl key={key} name="control" emit={props.emit} payload={control} /> : null;
|
||||
})}
|
||||
{inputs.map(
|
||||
([key, input]) =>
|
||||
input && (
|
||||
<div className="input" key={key} data-testid={`input-${key}`}>
|
||||
<RefSocket
|
||||
name="input-socket"
|
||||
emit={props.emit}
|
||||
side="input"
|
||||
socketKey={key}
|
||||
nodeId={id}
|
||||
payload={input.socket}
|
||||
/>
|
||||
{input && (!input.control || !input.showControl) && (
|
||||
<div className="input-title" data-testid="input-title">
|
||||
{input?.label}
|
||||
</div>
|
||||
)}
|
||||
{input?.control && input?.showControl && (
|
||||
<span className="input-control">
|
||||
<RefControl key={key} name="input-control" emit={props.emit} payload={input.control} />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</NodeStyles>
|
||||
);
|
||||
}
|
|
@ -31,6 +31,7 @@ export const NodeStyles = styled.div<NodeExtraData & { selected: boolean; styles
|
|||
font-family: sans-serif;
|
||||
font-size: 18px;
|
||||
padding: 8px;
|
||||
height: 100%;
|
||||
}
|
||||
.output {
|
||||
text-align: right;
|
||||
|
@ -103,7 +104,6 @@ export function CustomNode<Scheme extends ClassicScheme>(props: Props<Scheme>) {
|
|||
<div
|
||||
onPointerDown={(e) => {
|
||||
e.stopPropagation();
|
||||
console.log(">>>");
|
||||
}}
|
||||
className="title"
|
||||
data-testid="title"
|
|
@ -1,68 +0,0 @@
|
|||
export {};
|
||||
// import React from "react";
|
||||
// import { CoreError, UiErrorState } from "../core/store/base_store";
|
||||
// import { SelectProjectStore } from "./select_project/presentation/select_project_store";
|
||||
|
||||
// export declare type ClassConstructor<T> = {
|
||||
// new (...args: any[]): T;
|
||||
// };
|
||||
// interface MobxReactComponentProps<T extends UiErrorState<CoreError>, ClassConstructor> {
|
||||
// store: ClassConstructor;
|
||||
// children: (element: T) => React.ReactElement;
|
||||
// }
|
||||
|
||||
// class UiStateErrorComponent<T extends UiErrorState<CoreError>, K> extends React.Component<
|
||||
// MobxReactComponentProps<T, K>,
|
||||
// { store: T | undefined }
|
||||
// > {
|
||||
// async componentDidMount(): Promise<void> {
|
||||
// const store = this.props.store as ClassConstructor<T>;
|
||||
// console.log(store);
|
||||
// const s = new store();
|
||||
// this.setState({ store: s });
|
||||
// if (this.state !== null) {
|
||||
// await this.state.store?.init();
|
||||
// }
|
||||
// }
|
||||
// componentWillUnmount(): void {
|
||||
// if (this.state.store !== undefined) {
|
||||
// this.state.store.dispose();
|
||||
// }
|
||||
// }
|
||||
|
||||
// render() {
|
||||
// if (this.state !== null) {
|
||||
// if (this.state.store?.isLoading) {
|
||||
// return <>Loading</>;
|
||||
// }
|
||||
// if (this.state.store !== undefined) {
|
||||
// return this.props.children(this.state.store);
|
||||
// }
|
||||
// }
|
||||
|
||||
// return (
|
||||
// <div>
|
||||
// <>{this.props.children}</>
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
// export const ExampleScreen: React.FC = () => {
|
||||
// return (
|
||||
// <div>
|
||||
// <UiStateErrorComponent<SelectProjectStore, {}> store={SelectProjectStore}>
|
||||
// {(store) => {
|
||||
// console.log(store);
|
||||
// return (
|
||||
// <div>
|
||||
// {store.projects.map((el) => {
|
||||
// return <>{el}</>;
|
||||
// })}
|
||||
// </div>
|
||||
// );
|
||||
// }}
|
||||
// </UiStateErrorComponent>
|
||||
// </div>
|
||||
// );
|
||||
// };
|
Loading…
Add table
Add a link
Reference in a new issue