Skip to content

Commit

Permalink
Drawing action sequence SVG.
Browse files Browse the repository at this point in the history
  • Loading branch information
calvertdw committed Oct 15, 2024
1 parent fd6d572 commit d111965
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ else if (!isRootNode())

if (ImGui.menuItem(labels.get("Draw to SVG")))
{
definition.drawToSVG();
state.drawToSVG();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,6 @@ public void saveToFile(ObjectNode jsonNode)
}
}

public void drawToSVG()
{
new BehaviorTreeSVGWriter(this, saveFileDirectory);
}

/**
* Loads just this node's definition data. Not recursive
* because higher level node builders are required.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ public void fromMessage(BehaviorTreeNodeStateMessage message)
logger.fromMessage(message.getRecentLogMessages());
}

public void drawToSVG()
{
new BehaviorTreeSVGWriter(this);
}

@Override
public void destroy()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package us.ihmc.behaviors.behaviorTree;

import org.apache.commons.lang.WordUtils;
import org.jfree.svg.SVGGraphics2D;
import us.ihmc.behaviors.door.DoorTraversalDefinition;
import us.ihmc.behaviors.sequence.ActionNodeState;
import us.ihmc.behaviors.sequence.ActionSequenceDefinition;
import us.ihmc.behaviors.sequence.actions.ChestOrientationActionDefinition;
import us.ihmc.behaviors.sequence.actions.FootstepPlanActionDefinition;
import us.ihmc.behaviors.sequence.actions.HandPoseActionDefinition;
import us.ihmc.behaviors.sequence.actions.SakeHandCommandActionDefinition;
import us.ihmc.behaviors.sequence.actions.ScrewPrimitiveActionDefinition;
import us.ihmc.behaviors.sequence.actions.WaitDurationActionDefinition;

import java.awt.*;
import java.util.ArrayList;

public class BehaviorTreeSVGNode
{
private final SVGGraphics2D svgGraphics2D;
private final BehaviorTreeNodeState<?> node;
private final ArrayList<BehaviorTreeSVGNode> allNodes;
private int originX;
private int originY;
private int x;
private int y;

public BehaviorTreeSVGNode(SVGGraphics2D svgGraphics2D,
BehaviorTreeNodeState<?> node,
ArrayList<BehaviorTreeSVGNode> allNodes,
int index,
int originX,
int originY)
{
this.svgGraphics2D = svgGraphics2D;
this.node = node;
this.allNodes = allNodes;
this.originX = originX;
this.originY = originY;

x = originX;
y = originY;

if (node instanceof ActionNodeState actionNode)
{
// index = actionNode.getActionIndex();
// node.
}

// svgGraphics2D.setStroke();

svgGraphics2D.setColor(new Color((int) (Math.random() * 256), (int) (Math.random() * 256), (int) (Math.random() * 256), 100));
svgGraphics2D.setStroke(new BasicStroke(0.5f));
svgGraphics2D.fillRect(x, y, 200, 30);
svgGraphics2D.setColor(Color.GRAY);
svgGraphics2D.setStroke(new BasicStroke(0.25f));
drawRect(x, y, 12, 10);
x += 2;
y += 8;
svgGraphics2D.setFont(new Font("Arial", Font.PLAIN, 8));
String indexString = "%d".formatted(index);
svgGraphics2D.drawString(indexString, x + 4 * (2 - indexString.length()), y);
x += 12;
y += 4;
svgGraphics2D.setColor(Color.BLACK);
svgGraphics2D.setFont(new Font("Arial", Font.PLAIN, 12));
svgGraphics2D.drawString("%s".formatted(filterName(node)), x, y);
y += 13;
svgGraphics2D.setColor(Color.GRAY);
svgGraphics2D.setFont(new Font("Arial", Font.PLAIN, 10));
svgGraphics2D.drawString("%s".formatted(getTypeName(node.getDefinition())), x, y);

}

public int getHeight()
{
return y - originY;
}

public int getWidth()
{
return x - originX;
}

private void drawRect(int x, int y, int width, int height)
{
// The supplied drawRect is broken for strokes < 1.0f
svgGraphics2D.drawLine(x, y, x + width, y);
svgGraphics2D.drawLine(x, y, x, y + height);
svgGraphics2D.drawLine(x, y + height, x + width, y + height);
svgGraphics2D.drawLine(x + width, y, x + width, y + height);
}

private String filterName(BehaviorTreeNodeState<?> node)
{
String name = node.getDefinition().getName();

if (name.startsWith("CHECK POINT "))
name = name.substring("CHECK POINT ".length());
if (name.startsWith("RASVideo_"))
name = name.substring("RASVideo_".length());
if (name.endsWith(".json"))
{
name = name.substring(0, name.length() - 5);
String afterUnderscore = name.substring(name.lastIndexOf("_") + 1);
String titleCaseString = WordUtils.capitalizeFully(afterUnderscore.replaceAll("([a-z])([A-Z])", "$1 $2"));
name = titleCaseString;
}

return name;
}

private String getTypeName(BehaviorTreeNodeDefinition node)
{
if (node.getName().contains("CHECK POINT"))
return "Checkpoint Node";

if (node instanceof BehaviorTreeRootNodeDefinition)
return "Root Node";
if (node instanceof DoorTraversalDefinition)
return "Door Traversal Coordinator";
if (node instanceof ActionSequenceDefinition)
return "Action Sequence";
if (node instanceof WaitDurationActionDefinition)
return "Wait Action";
if (node instanceof HandPoseActionDefinition)
return "Hand Pose Action";
if (node instanceof FootstepPlanActionDefinition)
return "Walk Action";
if (node instanceof ChestOrientationActionDefinition)
return "Chest Trajectory Action";
if (node instanceof SakeHandCommandActionDefinition)
return "Finger Trajectory Action";
if (node instanceof ScrewPrimitiveActionDefinition)
return "Screw Trajectory Action";
return "";
}
}
Original file line number Diff line number Diff line change
@@ -1,50 +1,38 @@
package us.ihmc.behaviors.behaviorTree;

import org.apache.commons.lang.WordUtils;
import org.jfree.svg.SVGGraphics2D;
import us.ihmc.behaviors.door.DoorTraversalDefinition;
import us.ihmc.behaviors.sequence.ActionSequenceDefinition;
import us.ihmc.behaviors.sequence.actions.ChestOrientationActionDefinition;
import us.ihmc.behaviors.sequence.actions.FootstepPlanActionDefinition;
import us.ihmc.behaviors.sequence.actions.HandPoseActionDefinition;
import us.ihmc.behaviors.sequence.actions.SakeHandCommandActionDefinition;
import us.ihmc.behaviors.sequence.actions.ScrewPrimitiveActionDefinition;
import us.ihmc.behaviors.sequence.actions.WaitDurationActionDefinition;
import us.ihmc.log.LogTools;
import us.ihmc.tools.io.WorkspaceResourceDirectory;

import java.awt.*;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;

public class BehaviorTreeSVGWriter
{
private int i = 0;
private int x = 100;
private int y = 100;
private final ArrayList<BehaviorTreeSVGNode> svgNodes = new ArrayList<>();

public BehaviorTreeSVGWriter(BehaviorTreeNodeDefinition node, WorkspaceResourceDirectory saveFileDirectory)
public BehaviorTreeSVGWriter(BehaviorTreeNodeState node)
{
double documentSize = 1000.0;
double documentSize = 1500.0;
SVGGraphics2D svgGraphics2D = new SVGGraphics2D(documentSize, documentSize);

// svgGraphics2D.drawString(node.getName(), x, y);

BehaviorTreeTools.runForSubtreeNodes(node, child ->
{
y += 15;
svgGraphics2D.setColor(Color.BLACK);
svgGraphics2D.setFont(new Font("Arial", Font.PLAIN, 12));
svgGraphics2D.drawString("%s".formatted(filterName(child)), x, y);
y += 13;
svgGraphics2D.setColor(Color.GRAY);
svgGraphics2D.setFont(new Font("Arial", Font.PLAIN, 10));
svgGraphics2D.drawString("%s".formatted(getTypeName(child)), x, y);
y += 3;
BehaviorTreeSVGNode svgNode = new BehaviorTreeSVGNode(svgGraphics2D, child, svgNodes, i, x, y);
svgNodes.add(svgNode);

int verticalSpacing = 10;
y += svgNode.getHeight() + verticalSpacing;

++i;
});

Path svgPath = Paths.get("%s.svg".formatted(node.getName()));
Path svgPath = Paths.get("%s.svg".formatted(node.getDefinition().getName()));
LogTools.info("Saving SVG to {}", svgPath);

try (FileWriter writer = new FileWriter(svgPath.toFile()))
Expand All @@ -59,49 +47,4 @@ public BehaviorTreeSVGWriter(BehaviorTreeNodeDefinition node, WorkspaceResourceD
throw new RuntimeException(e);
}
}

private String filterName(BehaviorTreeNodeDefinition node)
{
String name = node.getName();

if (name.startsWith("CHECK POINT "))
name = name.substring("CHECK POINT ".length());
if (name.startsWith("RASVideo_"))
name = name.substring("RASVideo_".length());
if (name.endsWith(".json"))
{
name = name.substring(0, name.length() - 5);
String afterUnderscore = name.substring(name.lastIndexOf("_") + 1);
String titleCaseString = WordUtils.capitalizeFully(afterUnderscore.replaceAll("([a-z])([A-Z])", "$1 $2"));
name = titleCaseString;
}

return name;
}

private String getTypeName(BehaviorTreeNodeDefinition node)
{
if (node.getName().contains("CHECK POINT"))
return "Checkpoint Node";

if (node instanceof BehaviorTreeRootNodeDefinition)
return "Root Node";
if (node instanceof DoorTraversalDefinition)
return "Door Traversal Coordinator";
if (node instanceof ActionSequenceDefinition)
return "Action Sequence";
if (node instanceof WaitDurationActionDefinition)
return "Wait Action";
if (node instanceof HandPoseActionDefinition)
return "Hand Pose Action";
if (node instanceof FootstepPlanActionDefinition)
return "Walk Action";
if (node instanceof ChestOrientationActionDefinition)
return "Chest Trajectory Action";
if (node instanceof SakeHandCommandActionDefinition)
return "Finger Trajectory Action";
if (node instanceof ScrewPrimitiveActionDefinition)
return "Screw Trajectory Action";
return "";
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package us.ihmc.behaviors.behaviorTree;

import us.ihmc.behaviors.sequence.ActionNodeDefinition;
import us.ihmc.behaviors.sequence.ActionSequenceDefinition;
import us.ihmc.behaviors.sequence.ActionSequenceExecutor;
import us.ihmc.behaviors.sequence.ActionSequenceState;

import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -44,6 +41,16 @@ public static void runForSubtreeNodes(BehaviorTreeNodeDefinition node, Consumer<
}
}

public static void runForSubtreeNodes(BehaviorTreeNodeState<?> node, Consumer<BehaviorTreeNodeState<?>> operation)
{
operation.accept(node);

for (BehaviorTreeNodeState<?> child : node.getChildren())
{
runForSubtreeNodes(child, operation);
}
}

public static void runForEntireTree(BehaviorTreeNodeDefinition anyNode, Consumer<BehaviorTreeNodeDefinition> operation)
{
runForSubtreeNodes(findRootNode(anyNode), operation);
Expand Down

0 comments on commit d111965

Please sign in to comment.