@woosh/meep-engine 2.43.39 → 2.43.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/core/binary/BitSet.js +4 -18
  2. package/core/color/Color.js +1 -1
  3. package/core/geom/ConicRay.js +1 -1
  4. package/core/geom/Quaternion.js +1 -1
  5. package/core/geom/Vector1.js +1 -1
  6. package/core/geom/Vector3.js +1 -1
  7. package/core/geom/Vector4.js +1 -1
  8. package/core/math/hash/computeHashFloatArray.js +1 -1
  9. package/core/math/hash/computeObjectHash.js +1 -1
  10. package/core/math/interval/NumericInterval.js +1 -1
  11. package/core/model/BoundedValue.js +1 -1
  12. package/core/model/node-graph/Connection.js +47 -6
  13. package/core/model/node-graph/Connection.spec.js +21 -0
  14. package/core/model/node-graph/DataType.js +36 -13
  15. package/core/model/node-graph/DataType.spec.js +28 -0
  16. package/core/model/node-graph/NodeGraph.js +46 -12
  17. package/core/model/node-graph/node/NodeDescription.js +26 -16
  18. package/core/model/node-graph/node/NodeDescription.spec.js +14 -0
  19. package/core/model/node-graph/node/NodeInstance.js +10 -4
  20. package/core/model/node-graph/node/NodeInstancePortReference.js +18 -0
  21. package/core/model/node-graph/node/NodeRegistry.js +36 -10
  22. package/core/model/node-graph/node/NodeRegistry.spec.js +25 -0
  23. package/core/model/node-graph/node/Port.js +12 -1
  24. package/core/model/node-graph/node/PortDirection.js +1 -1
  25. package/core/model/node-graph/node/parameter/NodeParameterDescription.js +1 -1
  26. package/core/model/reactive/model/terminal/ReactiveLiteralNumber.js +1 -1
  27. package/core/model/stat/LinearModifier.js +1 -1
  28. package/core/{math/hash → primitives/numbers}/computeHashFloat.js +0 -0
  29. package/core/{math/hash → primitives/numbers}/computeHashFloat.spec.js +0 -0
  30. package/engine/asset/loaders/material/computeMaterialHash.js +1 -1
  31. package/engine/asset/loaders/material/computeTextureHash.js +1 -1
  32. package/engine/ecs/animation/Animation.js +1 -1
  33. package/engine/ecs/animation/InverseKinematics.js +1 -1
  34. package/engine/ecs/dynamic_actions/actions/definition/WeightedRandomActionDescription.js +1 -1
  35. package/engine/ecs/gui/position/ViewportPosition.js +1 -1
  36. package/engine/graphics/ecs/animation/animator/AnimationClip.js +1 -1
  37. package/engine/graphics/ecs/animation/animator/AnimationNotification.js +1 -1
  38. package/engine/graphics/ecs/animation/animator/graph/AnimationGraph.js +1 -1
  39. package/engine/graphics/ecs/animation/animator/graph/definition/AnimationTransitionDefinition.js +1 -1
  40. package/engine/graphics/ecs/highlight/HighlightDefinition.js +1 -1
  41. package/engine/graphics/ecs/mesh/Mesh.js +1 -1
  42. package/engine/graphics/geometry/MikkT/GenerateSharedVerticesIndexList.js +1 -1
  43. package/engine/graphics/micron/render/PatchCacheKey.js +1 -1
  44. package/engine/graphics/particles/particular/engine/emitter/ParticleLayer.js +1 -1
  45. package/engine/graphics/render/buffer/slot/parameter/ProgramValueSlotParameter.js +1 -1
  46. package/engine/graphics/render/frame_graph/RenderGraph.js +5 -1
  47. package/engine/intelligence/behavior/SelectorBehavior.js +3 -1
  48. package/engine/intelligence/behavior/behavior_to_dot.js +251 -0
  49. package/engine/intelligence/behavior/behavior_to_dot.prototype.js +55 -0
  50. package/engine/intelligence/behavior/composite/CompositeBehavior.js +6 -0
  51. package/engine/intelligence/behavior/composite/ParallelBehavior.js +10 -0
  52. package/engine/intelligence/behavior/composite/SequenceBehavior.js +6 -50
  53. package/engine/intelligence/behavior/composite/SequenceBehaviorSerializationAdapter.js +52 -0
  54. package/engine/intelligence/behavior/selector/WeightedElement.js +1 -1
  55. package/engine/intelligence/blackboard/Blackboard.js +26 -14
  56. package/engine/intelligence/blackboard/Blackboard.spec.js +62 -0
  57. package/engine/intelligence/blackboard/{Blacboard.spec.js → BlackboardSerializationAdapter.spec.js} +1 -1
  58. package/engine/intelligence/mcts/README.md +7 -0
  59. package/engine/intelligence/optimization/RandomOptimizer.js +2 -1
  60. package/engine/intelligence/resource/ResourceAllocationBid.js +3 -3
  61. package/engine/intelligence/resource/ResourceAllocationSolver.js +1 -1
  62. package/engine/intelligence/resource/StrategicResourceAllocator.js +6 -4
  63. package/engine/intelligence/resource/TacticalModule.js +7 -0
  64. package/engine/knowledge/database/StaticKnowledgeDataTableDescriptor.js +5 -5
  65. package/engine/navigation/grid/GridField.js +1 -0
  66. package/engine/notify/NotificationLog.js +2 -2
  67. package/engine/sound/ecs/emitter/SoundEmitter.js +1 -1
  68. package/engine/sound/ecs/emitter/SoundTrack.js +1 -1
  69. package/package.json +1 -1
@@ -4,16 +4,25 @@ import { DataType } from "../DataType.js";
4
4
  import { NodeDescription } from "./NodeDescription.js";
5
5
  import { Port } from "./Port.js";
6
6
 
7
+ /**
8
+ *
9
+ * @param {Port} port
10
+ * @return {DataType}
11
+ */
12
+ function getPortType(port) {
13
+ return port.dataType;
14
+ }
15
+
7
16
  export class NodeRegistry {
8
17
  constructor() {
9
18
  /**
10
- *
19
+ * ID -> Node mapping
11
20
  * @type {Map<number, NodeDescription>}
12
21
  */
13
22
  this.nodes = new Map();
14
23
 
15
24
  /**
16
- *
25
+ * ID -> DataType mapping
17
26
  * @type {Map<number, DataType>}
18
27
  */
19
28
  this.types = new Map();
@@ -31,19 +40,17 @@ export class NodeRegistry {
31
40
  * @param {NodeDescription} node
32
41
  */
33
42
  addNode(node) {
34
- assert.notEqual(node, undefined, "node is undefined");
35
- assert.notEqual(node, null, "node is null");
43
+ assert.defined(node, "node");
44
+ assert.notNull(node, "node");
36
45
 
37
46
  if (this.nodes.has(node.id)) {
38
- console.warn(`Node with id ${node.id} already exists`);
47
+ // console.warn(`Node with id ${node.id} already exists`);
39
48
 
40
49
  return false;
41
50
  }
42
51
 
43
52
  //extract types
44
- node.getPorts().forEach(port => {
45
- this.addType(port.dataType);
46
- });
53
+ node.getPorts().map(getPortType).forEach(this.addType, this);
47
54
 
48
55
  this.nodes.set(node.id, node);
49
56
 
@@ -55,14 +62,27 @@ export class NodeRegistry {
55
62
  * @param {DataType} type
56
63
  */
57
64
  addType(type) {
58
- if (this.types.has(type.id)) {
65
+ if (this.hasType(type)) {
59
66
  return false;
60
67
  }
61
68
 
62
69
  this.types.set(type.id, type);
70
+
63
71
  return true;
64
72
  }
65
73
 
74
+ /**
75
+ *
76
+ * @param {DataType} type
77
+ * @return {boolean}
78
+ */
79
+ hasType(type) {
80
+ assert.defined(type, 'type');
81
+ assert.notNull(type, 'type');
82
+
83
+ return this.types.has(type.id);
84
+ }
85
+
66
86
  /**
67
87
  *
68
88
  * @returns {number}
@@ -123,7 +143,7 @@ export class NodeRegistry {
123
143
  }
124
144
 
125
145
  /**
126
- *
146
+ * NOTE: this only works with un-extended node descriptions
127
147
  * @param json
128
148
  * @returns {NodeDescription}
129
149
  */
@@ -215,3 +235,9 @@ export class NodeRegistry {
215
235
  };
216
236
  }
217
237
  }
238
+
239
+ /**
240
+ * @readonly
241
+ * @type {boolean}
242
+ */
243
+ NodeRegistry.prototype.isNodeRegistry = true;
@@ -50,3 +50,28 @@ test("serialize/deserialize 1 node with 1 port", () => {
50
50
  expect(port.dataType.name).toBe("kitty");
51
51
  expect(port.dataType.id).toBe(42);
52
52
  });
53
+
54
+ test("port types are added automatically when node is added", () => {
55
+ const registry = new NodeRegistry();
56
+
57
+ const type = DataType.from(0, 'a');
58
+
59
+ const node = new NodeDescription();
60
+ node.createPort(type, "port name", PortDirection.In);
61
+
62
+ registry.addNode(node);
63
+
64
+ expect(registry.hasType(type));
65
+ });
66
+
67
+ test("hasType", () => {
68
+ const registry = new NodeRegistry();
69
+
70
+ const type = DataType.from(0, 'a');
71
+
72
+ expect(registry.hasType(type)).toBe(false);
73
+
74
+ registry.addType(type);
75
+
76
+ expect(registry.hasType(type)).toBe(true);
77
+ });
@@ -2,6 +2,11 @@ import { computeStringHash } from "../../../primitives/strings/computeStringHash
2
2
  import { PortDirection } from "./PortDirection.js";
3
3
  import { computeHashIntegerArray } from "../../../collection/array/computeHashIntegerArray.js";
4
4
 
5
+ /**
6
+ * @type {number}
7
+ */
8
+ let id_counter = 0;
9
+
5
10
  export class Port {
6
11
  constructor() {
7
12
  /**
@@ -14,7 +19,7 @@ export class Port {
14
19
  * ID uniquely identifies object within some context. Ids are assumed to be immutable
15
20
  * @type {number}
16
21
  */
17
- this.id = 0;
22
+ this.id = id_counter++;
18
23
 
19
24
  /**
20
25
  *
@@ -53,3 +58,9 @@ export class Port {
53
58
 
54
59
  }
55
60
  }
61
+
62
+ /**
63
+ * @readonly
64
+ * @type {boolean}
65
+ */
66
+ Port.prototype.isPort = true;
@@ -1,5 +1,5 @@
1
1
  /**
2
- *
2
+ * @readonly
3
3
  * @enum
4
4
  */
5
5
  export const PortDirection = {
@@ -5,7 +5,7 @@ export class NodeParameterDescription {
5
5
  constructor() {
6
6
  this.name = "";
7
7
  this.id = 0;
8
- this.type = NodeParameterDataType.Float;
8
+ this.type = NodeParameterDataType.Number;
9
9
 
10
10
  /**
11
11
  *
@@ -2,7 +2,7 @@ import { assert } from "../../../../assert.js";
2
2
  import Signal from "../../../../events/signal/Signal.js";
3
3
  import DataType from "../../../../parser/simple/DataType.js";
4
4
  import { ReactiveExpression } from "../ReactiveExpression.js";
5
- import { computeHashFloat } from "../../../../math/hash/computeHashFloat.js";
5
+ import { computeHashFloat } from "../../../../primitives/numbers/computeHashFloat.js";
6
6
 
7
7
  const dummySignal = new Signal();
8
8
 
@@ -1,5 +1,5 @@
1
1
  import { assert } from "../../assert.js";
2
- import { computeHashFloat } from "../../math/hash/computeHashFloat.js";
2
+ import { computeHashFloat } from "../../primitives/numbers/computeHashFloat.js";
3
3
 
4
4
 
5
5
  /**
@@ -2,7 +2,7 @@ import { planeHash } from "./planeHash.js";
2
2
  import { computeStringHash } from "../../../../core/primitives/strings/computeStringHash.js";
3
3
  import { computeHashArray } from "../../../../core/collection/array/computeHashArray.js";
4
4
  import { computeHashIntegerArray } from "../../../../core/collection/array/computeHashIntegerArray.js";
5
- import { computeHashFloat } from "../../../../core/math/hash/computeHashFloat.js";
5
+ import { computeHashFloat } from "../../../../core/primitives/numbers/computeHashFloat.js";
6
6
  import { textureHashById } from "./textureHashById.js";
7
7
  import { computeHashColor } from "./computeHashColor.js";
8
8
 
@@ -1,6 +1,6 @@
1
1
  import { computeImageBitmapHash } from "./computeImageBitmapHash.js";
2
2
  import { computeHashIntegerArray } from "../../../../core/collection/array/computeHashIntegerArray.js";
3
- import { computeHashFloat } from "../../../../core/math/hash/computeHashFloat.js";
3
+ import { computeHashFloat } from "../../../../core/primitives/numbers/computeHashFloat.js";
4
4
  import { murmur3_32 } from "../../../../core/math/hash/murmur3_32.js";
5
5
  import { computeStringHash } from "../../../../core/primitives/strings/computeStringHash.js";
6
6
 
@@ -9,7 +9,7 @@ import ObservedInteger from "../../../core/model/ObservedInteger.js";
9
9
  import Vector1 from "../../../core/geom/Vector1.js";
10
10
  import { BinaryClassSerializationAdapter } from "../storage/binary/BinaryClassSerializationAdapter.js";
11
11
  import { computeHashIntegerArray } from "../../../core/collection/array/computeHashIntegerArray.js";
12
- import { computeHashFloat } from "../../../core/math/hash/computeHashFloat.js";
12
+ import { computeHashFloat } from "../../../core/primitives/numbers/computeHashFloat.js";
13
13
 
14
14
  /**
15
15
  *
@@ -2,7 +2,7 @@ import { BinaryClassSerializationAdapter } from "../storage/binary/BinaryClassSe
2
2
  import { NumericInterval } from "../../../core/math/interval/NumericInterval.js";
3
3
  import { computeStringHash } from "../../../core/primitives/strings/computeStringHash.js";
4
4
  import { computeHashIntegerArray } from "../../../core/collection/array/computeHashIntegerArray.js";
5
- import { computeHashFloat } from "../../../core/math/hash/computeHashFloat.js";
5
+ import { computeHashFloat } from "../../../core/primitives/numbers/computeHashFloat.js";
6
6
 
7
7
  class IKConstraint {
8
8
  constructor() {
@@ -1,7 +1,7 @@
1
1
  import { AbstractActionDescription } from "./AbstractActionDescription.js";
2
2
  import { WeightedRandomBehavior } from "../../../../intelligence/behavior/selector/WeightedRandomBehavior.js";
3
3
  import { WeightedElement } from "../../../../intelligence/behavior/selector/WeightedElement.js";
4
- import { computeHashFloat } from "../../../../../core/math/hash/computeHashFloat.js";
4
+ import { computeHashFloat } from "../../../../../core/primitives/numbers/computeHashFloat.js";
5
5
 
6
6
  export class WeightedRandomActionDescription extends AbstractActionDescription {
7
7
  constructor() {
@@ -6,7 +6,7 @@ import Vector2 from "../../../../core/geom/Vector2.js";
6
6
  import ObservedBoolean from "../../../../core/model/ObservedBoolean.js";
7
7
  import { BinaryClassSerializationAdapter } from "../../storage/binary/BinaryClassSerializationAdapter.js";
8
8
  import { computeHashIntegerArray } from "../../../../core/collection/array/computeHashIntegerArray.js";
9
- import { computeHashFloat } from "../../../../core/math/hash/computeHashFloat.js";
9
+ import { computeHashFloat } from "../../../../core/primitives/numbers/computeHashFloat.js";
10
10
 
11
11
  /**
12
12
  * @readonly
@@ -3,7 +3,7 @@ import { AnimationClipFlag } from "./AnimationClipFlag.js";
3
3
  import { LoopOnce, LoopRepeat } from "three";
4
4
  import { AnimationEventTypes } from "./AnimationEventTypes.js";
5
5
  import { computeHashIntegerArray } from "../../../../../core/collection/array/computeHashIntegerArray.js";
6
- import { computeHashFloat } from "../../../../../core/math/hash/computeHashFloat.js";
6
+ import { computeHashFloat } from "../../../../../core/primitives/numbers/computeHashFloat.js";
7
7
 
8
8
  export class AnimationClip {
9
9
  constructor() {
@@ -1,5 +1,5 @@
1
1
  import { computeHashIntegerArray } from "../../../../../core/collection/array/computeHashIntegerArray.js";
2
- import { computeHashFloat } from "../../../../../core/math/hash/computeHashFloat.js";
2
+ import { computeHashFloat } from "../../../../../core/primitives/numbers/computeHashFloat.js";
3
3
 
4
4
  export class AnimationNotification {
5
5
  constructor() {
@@ -10,7 +10,7 @@ import {
10
10
  import { assert } from "../../../../../../core/assert.js";
11
11
  import { threeUpdateTransform } from "../../../../util/threeUpdateTransform.js";
12
12
  import { computeHashIntegerArray } from "../../../../../../core/collection/array/computeHashIntegerArray.js";
13
- import { computeHashFloat } from "../../../../../../core/math/hash/computeHashFloat.js";
13
+ import { computeHashFloat } from "../../../../../../core/primitives/numbers/computeHashFloat.js";
14
14
 
15
15
  export class AnimationGraph {
16
16
  constructor() {
@@ -1,6 +1,6 @@
1
1
  import { computeStringHash } from "../../../../../../../core/primitives/strings/computeStringHash.js";
2
2
  import { computeHashIntegerArray } from "../../../../../../../core/collection/array/computeHashIntegerArray.js";
3
- import { computeHashFloat } from "../../../../../../../core/math/hash/computeHashFloat.js";
3
+ import { computeHashFloat } from "../../../../../../../core/primitives/numbers/computeHashFloat.js";
4
4
 
5
5
  export class AnimationTransitionDefinition {
6
6
  constructor() {
@@ -1,7 +1,7 @@
1
1
  import { Color } from "../../../../core/color/Color.js";
2
2
  import { assert } from "../../../../core/assert.js";
3
3
  import { computeHashIntegerArray } from "../../../../core/collection/array/computeHashIntegerArray.js";
4
- import { computeHashFloat } from "../../../../core/math/hash/computeHashFloat.js";
4
+ import { computeHashFloat } from "../../../../core/primitives/numbers/computeHashFloat.js";
5
5
 
6
6
  export class HighlightDefinition {
7
7
  constructor() {
@@ -10,7 +10,7 @@ import { Transform } from "../../../ecs/transform/Transform.js";
10
10
  import { updateNodeByTransformAndBBB } from "./updateNodeByTransformAndBBB.js";
11
11
  import { applyTransformToThreeObject } from "./applyTransformToThreeObject.js";
12
12
  import { computeHashIntegerArray } from "../../../../core/collection/array/computeHashIntegerArray.js";
13
- import { computeHashFloat } from "../../../../core/math/hash/computeHashFloat.js";
13
+ import { computeHashFloat } from "../../../../core/primitives/numbers/computeHashFloat.js";
14
14
 
15
15
  /**
16
16
  *
@@ -1,6 +1,6 @@
1
1
  import { vec3 } from "gl-matrix";
2
2
  import { GetPosition } from "./GetPosition.js";
3
- import { computeHashFloat } from "../../../../core/math/hash/computeHashFloat.js";
3
+ import { computeHashFloat } from "../../../../core/primitives/numbers/computeHashFloat.js";
4
4
  import { GetNormal } from "./GetNormal.js";
5
5
  import { GetTexCoord } from "./GetTexCoord.js";
6
6
 
@@ -1,7 +1,7 @@
1
1
  import { mortonEncode_magicbits } from "../../../../core/geom/3d/morton/mortonEncode_magicbits.js";
2
2
  import { isArrayEqualStrict } from "../../../../core/collection/array/isArrayEqualStrict.js";
3
3
  import { SimpleObjectPoolFactory } from "../../../../core/model/object/SimpleObjectPoolFactory.js";
4
- import { computeHashFloat } from "../../../../core/math/hash/computeHashFloat.js";
4
+ import { computeHashFloat } from "../../../../core/primitives/numbers/computeHashFloat.js";
5
5
 
6
6
  export class PatchCacheKey {
7
7
  constructor() {
@@ -12,7 +12,7 @@ import { computeConeBoundingBox } from "../../../../../../core/geom/3d/cone/comp
12
12
  import { EmissionFromType } from "./EmissionFromType.js";
13
13
  import { EmissionShapeType } from "./EmissionShapeType.js";
14
14
  import { computeHashIntegerArray } from "../../../../../../core/collection/array/computeHashIntegerArray.js";
15
- import { computeHashFloat } from "../../../../../../core/math/hash/computeHashFloat.js";
15
+ import { computeHashFloat } from "../../../../../../core/primitives/numbers/computeHashFloat.js";
16
16
 
17
17
  const aabb3 = new AABB3(0, 0, 0, 0, 0, 0);
18
18
 
@@ -1,7 +1,7 @@
1
1
  import { ProgramValueSlotParameterType } from "./ProgramValueSlotParameterType.js";
2
2
  import { computeStringHash } from "../../../../../../core/primitives/strings/computeStringHash.js";
3
3
  import { computeHashIntegerArray } from "../../../../../../core/collection/array/computeHashIntegerArray.js";
4
- import { computeHashFloat } from "../../../../../../core/math/hash/computeHashFloat.js";
4
+ import { computeHashFloat } from "../../../../../../core/primitives/numbers/computeHashFloat.js";
5
5
 
6
6
  /**
7
7
  *
@@ -338,6 +338,10 @@ export class RenderGraph {
338
338
  }
339
339
  }
340
340
 
341
+ /**
342
+ * @see https://en.wikipedia.org/wiki/DOT_(graph_description_language)
343
+ * @return {string}
344
+ */
341
345
  exportToDot() {
342
346
  const out = new LineBuilder();
343
347
 
@@ -397,9 +401,9 @@ export class RenderGraph {
397
401
  out.dedent();
398
402
  out.add("} [color=olivedrab3]");
399
403
  }
404
+ // -- Clusters:
400
405
  out.dedent();
401
406
  out.add("}");
402
- // -- Clusters:
403
407
  return out.build();
404
408
  }
405
409
  }
@@ -46,7 +46,9 @@ export class SelectorBehavior extends CompositeBehavior {
46
46
  }
47
47
  }
48
48
 
49
- initialize() {
49
+ initialize(context) {
50
+ super.initialize(context);
51
+
50
52
  this.__currentBehaviourIndex = 0;
51
53
  this.__currentBehaviour = this.__children[0];
52
54
 
@@ -0,0 +1,251 @@
1
+ import LineBuilder from "../../../core/codegen/LineBuilder.js";
2
+ import { Behavior } from "./Behavior.js";
3
+
4
+ /**
5
+ *
6
+ * @param {Behavior} behavior
7
+ * @return {string}
8
+ */
9
+ function compute_behavior_name(behavior) {
10
+
11
+ // try to get name from the constructor field
12
+ const typeName = behavior.constructor.typeName;
13
+ if (typeof typeName === "string") {
14
+ return typeName;
15
+ }
16
+
17
+ const constructor_name = behavior.constructor.name;
18
+ if (typeof constructor_name === "string") {
19
+ return constructor_name;
20
+ }
21
+
22
+ return '$Behavior';
23
+ }
24
+
25
+
26
+ /**
27
+ *
28
+ * @param {Behavior|SequenceBehavior|ParallelBehavior} behavior
29
+ * @return {Object}
30
+ */
31
+ function behavior_node_attributes(behavior) {
32
+ const result = {};
33
+
34
+ if (behavior.isCompositeBehavior === true) {
35
+
36
+ result.fillcolor = "orange";
37
+
38
+ if (behavior.isSequenceBehavior === true) {
39
+ result.fillcolor = "paleturquoise";
40
+ } else if (behavior.isParallelBehavior === true) {
41
+ result.fillcolor = "plum";
42
+ }
43
+ } else {
44
+
45
+ }
46
+
47
+ return result;
48
+ }
49
+
50
+ /**
51
+ *
52
+ * @param {Object} attributes
53
+ * @return {string}
54
+ */
55
+ function attributes_to_dot(attributes) {
56
+ const key_names = Object.keys(attributes);
57
+
58
+ return key_names.map(key => {
59
+
60
+ return `${key}=${attributes[key]}`;
61
+
62
+ }).join(', ');
63
+ }
64
+
65
+ /**
66
+ *
67
+ * @param {string} prop
68
+ * @param {Object} object
69
+ */
70
+ function should_property_be_included(prop, object) {
71
+ if (prop.startsWith('_')) {
72
+ // private field
73
+ return false;
74
+ }
75
+
76
+ const value = object[prop];
77
+
78
+ if (object.constructor.prototype[prop] === value) {
79
+ return false;
80
+ }
81
+
82
+ const typeof_value = typeof value;
83
+
84
+ if (value === null || typeof_value === "undefined" || typeof_value === "symbol") {
85
+ return false;
86
+ }
87
+
88
+ if (typeof_value === "object" && String(value) === '[object Object]') {
89
+ return false;
90
+ }
91
+
92
+ return true;
93
+ }
94
+
95
+ function property_to_string(value, max_length = 200) {
96
+
97
+
98
+ let string_value;
99
+
100
+ if (typeof value !== "string") {
101
+ string_value = String(value);
102
+ } else {
103
+ string_value = `\"${value}\"`;
104
+ }
105
+
106
+ const sanitized = string_value.replace(/\n/, "\n");
107
+
108
+ if (sanitized.length > max_length) {
109
+ return '[...]';
110
+ }
111
+
112
+ return sanitized;
113
+ }
114
+
115
+ /**
116
+ *
117
+ * @param {Behavior} behavior
118
+ * @param {number} max_props
119
+ */
120
+ function build_node_properties_string(behavior, max_props = 10) {
121
+
122
+ const properties = {};
123
+
124
+ let qualified_property_count = 0;
125
+
126
+ for (const behaviorKey in behavior) {
127
+
128
+ if (should_property_be_included(behaviorKey, behavior)) {
129
+ if (qualified_property_count < max_props) {
130
+ properties[behaviorKey] = property_to_string(behavior[behaviorKey]);
131
+ }
132
+
133
+ qualified_property_count++;
134
+ }
135
+
136
+ }
137
+
138
+ // stringify and concatenate
139
+ return Object.keys(properties).map(key => `${key} = ${properties[key]}`).join('<BR/>');
140
+
141
+ }
142
+
143
+ /**
144
+ *
145
+ * @param {Behavior} behavior
146
+ * @return {boolean}
147
+ */
148
+ function should_behavior_have_properties_string(behavior) {
149
+ if (behavior.isCompositeBehavior === true) {
150
+ return false;
151
+ }
152
+
153
+ return true;
154
+ }
155
+
156
+ function build_node_label(behavior) {
157
+ const node_name = compute_behavior_name(behavior);
158
+
159
+ const label_pieces = [];
160
+
161
+ label_pieces.push(`<B>${node_name}</B>`);
162
+
163
+ if (should_behavior_have_properties_string(behavior)) {
164
+ const properties = build_node_properties_string(behavior);
165
+
166
+ if (properties.trim().length > 0) {
167
+
168
+ label_pieces.push(properties);
169
+
170
+ }
171
+ }
172
+
173
+ return `<{ {${label_pieces.join('<BR/>')} } }>`;
174
+ }
175
+
176
+ /**
177
+ *
178
+ * @param {LineBuilder} out
179
+ * @param {Behavior|CompositeBehavior} behavior
180
+ * @param {{id_counter:number, node_ids:Map<Behavior,string>}} context
181
+ * @returns {string}
182
+ */
183
+ function parse_behavior(out, behavior, context) {
184
+ const id = context.id_counter++;
185
+ const node_id = `Node_${id}`;
186
+
187
+ context.node_ids.set(behavior, node_id);
188
+
189
+ const attributes = {
190
+ label: build_node_label(behavior),
191
+ style: "\"rounded,filled\""
192
+ };
193
+
194
+ Object.assign(attributes, behavior_node_attributes(behavior));
195
+
196
+ out.add(`${node_id} [${attributes_to_dot(attributes)}]`);
197
+
198
+ if (behavior.isCompositeBehavior === true) {
199
+ // is a composite behavior, which means it includes other behaviors
200
+
201
+ const children = behavior.getChildren();
202
+
203
+ const child_count = children.length;
204
+
205
+ for (let i = 0; i < child_count; i++) {
206
+ const child = children[i];
207
+
208
+ const child_id = parse_behavior(out, child, context);
209
+
210
+ // create edge
211
+ out.add(`${node_id} -> ${child_id} [label="№${i + 1}"];`);
212
+ }
213
+
214
+ }
215
+
216
+ return node_id;
217
+ }
218
+
219
+ /**
220
+ * Produces a diagram of behavior tree in DOT format, useful for debugging and exploration of complex trees
221
+ *
222
+ * @see https://en.wikipedia.org/wiki/DOT_(graph_description_language)
223
+ * @param {Behavior} behavior
224
+ * @returns {string}
225
+ */
226
+ export function behavior_to_dot(behavior) {
227
+ const out = new LineBuilder();
228
+
229
+ const context = {
230
+ id_counter: 0,
231
+ node_ids: new Map()
232
+ };
233
+
234
+ const font = {
235
+ name: "helvetica",
236
+ size: 10
237
+ };
238
+
239
+ out.add("digraph BehaviorTree {");
240
+ out.indent();
241
+ out.add("graph [style=invis, rankdir=\"TB\" ordering=out, splines=spline]");
242
+ out.add(`node [shape=record, fontname=\"${font.name}\", fontsize=${font.size}, margin=\"0.2,0.03\", fillcolor=ivory]`);
243
+ out.add(`edge [labelfontname=\"${font.name}\", labelfontsize=${font.size}, fontname=\"${font.name}\", fontsize=${font.size}, fontcolor=grey]`);
244
+
245
+ parse_behavior(out, behavior, context);
246
+
247
+ out.dedent();
248
+ out.add("}");
249
+
250
+ return out.build();
251
+ }