From 3b3c5f1d31803a74c3a59a1735412c9e5a0295f4 Mon Sep 17 00:00:00 2001
From: Damon Kohler <damonkohler@google.com>
Date: Thu, 19 Jul 2012 13:36:33 +0200
Subject: [PATCH] Improves performance of FrameTransformTree.

---
 .../internal/transport/queue/LazyMessage.java |  1 -
 .../ros/rosjava_geometry/FrameTransform.java  |  8 +++
 .../rosjava_geometry/FrameTransformTree.java  | 68 +++++++++----------
 .../rosjava_geometry/LazyFrameTransform.java  | 54 +++++++++++++++
 4 files changed, 93 insertions(+), 38 deletions(-)
 create mode 100644 rosjava_geometry/src/main/java/org/ros/rosjava_geometry/LazyFrameTransform.java

diff --git a/rosjava/src/main/java/org/ros/internal/transport/queue/LazyMessage.java b/rosjava/src/main/java/org/ros/internal/transport/queue/LazyMessage.java
index 789e45c3..1be527b9 100644
--- a/rosjava/src/main/java/org/ros/internal/transport/queue/LazyMessage.java
+++ b/rosjava/src/main/java/org/ros/internal/transport/queue/LazyMessage.java
@@ -50,7 +50,6 @@ public class LazyMessage<T> {
     this.buffer = buffer;
     this.deserializer = deserializer;
     mutex = new Object();
-    message = null;
   }
 
   @VisibleForTesting
diff --git a/rosjava_geometry/src/main/java/org/ros/rosjava_geometry/FrameTransform.java b/rosjava_geometry/src/main/java/org/ros/rosjava_geometry/FrameTransform.java
index eac23953..d258da88 100644
--- a/rosjava_geometry/src/main/java/org/ros/rosjava_geometry/FrameTransform.java
+++ b/rosjava_geometry/src/main/java/org/ros/rosjava_geometry/FrameTransform.java
@@ -29,6 +29,14 @@ public class FrameTransform {
   private final GraphName source;
   private final GraphName target;
 
+  public static FrameTransform
+      fromTransformStamped(geometry_msgs.TransformStamped transformStamped) {
+    Transform transform = Transform.newFromTransformMessage(transformStamped.getTransform());
+    String source = transformStamped.getHeader().getFrameId();
+    String target = transformStamped.getChildFrameId();
+    return new FrameTransform(transform, GraphName.of(source), GraphName.of(target));
+  }
+
   public FrameTransform(Transform transform, GraphName source, GraphName target) {
     this.transform = transform;
     this.source = source;
diff --git a/rosjava_geometry/src/main/java/org/ros/rosjava_geometry/FrameTransformTree.java b/rosjava_geometry/src/main/java/org/ros/rosjava_geometry/FrameTransformTree.java
index f4e33d61..fdbe0c81 100644
--- a/rosjava_geometry/src/main/java/org/ros/rosjava_geometry/FrameTransformTree.java
+++ b/rosjava_geometry/src/main/java/org/ros/rosjava_geometry/FrameTransformTree.java
@@ -40,10 +40,10 @@ public class FrameTransformTree {
   private final NameResolver nameResolver;
 
   /**
-   * A {@link Map} from child frame ID to the child frame's most recent
-   * transform.
+   * A {@link Map} of the most recent {@link LazyFrameTransform} by source
+   * frame.
    */
-  private final Map<GraphName, geometry_msgs.TransformStamped> transforms;
+  private final Map<GraphName, LazyFrameTransform> transforms;
 
   public FrameTransformTree(NameResolver nameResolver) {
     this.nameResolver = nameResolver;
@@ -53,26 +53,20 @@ public class FrameTransformTree {
   /**
    * Updates the transform tree with the provided transform.
    * 
-   * @param transform
+   * @param transformStamped
    *          the transform to update
    */
-  public void updateTransform(geometry_msgs.TransformStamped transform) {
-    transforms.put(nameResolver.resolve(transform.getChildFrameId()), transform);
+  public void updateTransform(geometry_msgs.TransformStamped transformStamped) {
+    GraphName source = nameResolver.resolve(transformStamped.getChildFrameId());
+    transforms.put(source, new LazyFrameTransform(transformStamped));
   }
 
-  public GraphName findRootFrame(GraphName sourceFrame) {
-    GraphName targetFrame = sourceFrame;
-    while (true) {
-      TransformStamped transformStamped = getLatestTransform(targetFrame);
-      if (transformStamped == null) {
-        return targetFrame;
-      }
-      targetFrame = nameResolver.resolve(transformStamped.getHeader().getFrameId());
+  private FrameTransform getLatestTransform(GraphName frame) {
+    LazyFrameTransform lazyFrameTransform = transforms.get(nameResolver.resolve(frame));
+    if (lazyFrameTransform != null) {
+      return lazyFrameTransform.get();
     }
-  }
-
-  private geometry_msgs.TransformStamped getLatestTransform(GraphName frame) {
-    return transforms.get(nameResolver.resolve(frame));
+    return null;
   }
 
   /**
@@ -86,26 +80,25 @@ public class FrameTransformTree {
   public boolean canTransform(GraphName sourceFrame, GraphName targetFrame) {
     Preconditions.checkNotNull(sourceFrame);
     Preconditions.checkNotNull(targetFrame);
-    FrameTransform sourceFrameTransform = newFrameTransformToRoot(sourceFrame);
-    FrameTransform targetFrameTransform = newFrameTransformToRoot(targetFrame);
-    return sourceFrameTransform.getTargetFrame().equals(targetFrameTransform.getTargetFrame());
+    FrameTransform source = newFrameTransformToRoot(sourceFrame);
+    FrameTransform target = newFrameTransformToRoot(targetFrame);
+    return source.getTargetFrame().equals(target.getTargetFrame());
   }
 
   /**
    * @return the {@link FrameTransform} from source the frame to the target
-   *         frame
+   *         frame, or {@code null} if no {@link FrameTransform} could be found
    */
   public FrameTransform newFrameTransform(GraphName sourceFrame, GraphName targetFrame) {
     Preconditions.checkNotNull(sourceFrame);
     Preconditions.checkNotNull(targetFrame);
-    Preconditions.checkArgument(canTransform(sourceFrame, targetFrame),
-        String.format("Cannot transform between %s and %s.", sourceFrame, targetFrame));
-    FrameTransform sourceFrameTransform = newFrameTransformToRoot(sourceFrame);
-    FrameTransform targetFrameTransform = newFrameTransformToRoot(targetFrame);
-    Transform transform =
-        targetFrameTransform.getTransform().invert().multiply(sourceFrameTransform.getTransform());
-    return new FrameTransform(transform, sourceFrameTransform.getSourceFrame(),
-        targetFrameTransform.getSourceFrame());
+    FrameTransform source = newFrameTransformToRoot(sourceFrame);
+    FrameTransform target = newFrameTransformToRoot(targetFrame);
+    if (source.getTargetFrame().equals(target.getTargetFrame())) {
+      Transform transform = target.getTransform().invert().multiply(source.getTransform());
+      return new FrameTransform(transform, source.getSourceFrame(), target.getSourceFrame());
+    }
+    return null;
   }
 
   /**
@@ -115,15 +108,16 @@ public class FrameTransformTree {
    */
   private FrameTransform newFrameTransformToRoot(GraphName frame) {
     GraphName sourceFrame = nameResolver.resolve(frame);
-    Transform result = Transform.newIdentityTransform();
-    GraphName targetFrame = sourceFrame;
+    FrameTransform result =
+        new FrameTransform(Transform.newIdentityTransform(), sourceFrame, sourceFrame);
     while (true) {
-      TransformStamped transformStamped = getLatestTransform(targetFrame);
-      if (transformStamped == null) {
-        return new FrameTransform(result, sourceFrame, targetFrame);
+      FrameTransform parent = getLatestTransform(result.getTargetFrame());
+      if (parent == null) {
+        return result;
       }
-      result = Transform.newFromTransformMessage(transformStamped.getTransform()).multiply(result);
-      targetFrame = nameResolver.resolve(transformStamped.getHeader().getFrameId());
+      Transform transform = result.getTransform().multiply(parent.getTransform());
+      GraphName targetFrame = nameResolver.resolve(parent.getSourceFrame());
+      result = new FrameTransform(transform, sourceFrame, targetFrame);
     }
   }
 }
diff --git a/rosjava_geometry/src/main/java/org/ros/rosjava_geometry/LazyFrameTransform.java b/rosjava_geometry/src/main/java/org/ros/rosjava_geometry/LazyFrameTransform.java
new file mode 100644
index 00000000..a41b2dc7
--- /dev/null
+++ b/rosjava_geometry/src/main/java/org/ros/rosjava_geometry/LazyFrameTransform.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.ros.rosjava_geometry;
+
+/**
+ * Lazily converts a {@link geometry_msgs.Transform} message to a
+ * {@link Transform} on the first call to {@link #get()} and caches the result.
+ * <p>
+ * This class is thread-safe.
+ * 
+ * @author damonkohler@google.com (Damon Kohler)
+ */
+public class LazyFrameTransform {
+
+  private final geometry_msgs.TransformStamped message;
+  private final Object mutex;
+
+  private FrameTransform frameTransform;
+
+  public LazyFrameTransform(geometry_msgs.TransformStamped message) {
+    this.message = message;
+    mutex = new Object();
+  }
+
+  /**
+   * @return the {@link FrameTransform} for the wrapped
+   *         {@link geometry_msgs.TransformStamped} message
+   */
+  public FrameTransform get() {
+    if (frameTransform != null) {
+      return frameTransform;
+    }
+    synchronized (mutex) {
+      if (frameTransform == null) {
+        frameTransform = FrameTransform.fromTransformStamped(message);
+      }
+    }
+    return frameTransform;
+  }
+}
\ No newline at end of file
-- 
GitLab