public void HighlightEntirePath() {
   CameraPathTinter t = WidgetManager.m_Instance.PathTinter;
   for (int i = 0; i < Path.PositionKnots.Count; ++i) {
     CameraPathPositionKnot pk = Path.PositionKnots[i];
     pk.RegisterHighlight(0, true);
     t.TintKnot(pk);
   }
   for (int i = 0; i < Path.RotationKnots.Count; ++i) {
     CameraPathRotationKnot rk = Path.RotationKnots[i];
     rk.RegisterHighlight(0, true);
     t.TintKnot(rk);
   }
   for (int i = 0; i < Path.SpeedKnots.Count; ++i) {
     CameraPathSpeedKnot sk = Path.SpeedKnots[i];
     sk.RegisterHighlight(0, true);
     t.TintKnot(sk);
   }
   for (int i = 0; i < Path.FovKnots.Count; ++i) {
     CameraPathFovKnot fk = Path.FovKnots[i];
     fk.RegisterHighlight(0, true);
     t.TintKnot(fk);
   }
   for (int i = 0; i < Path.Segments.Count; ++i) {
     t.TintSegment(m_Path.Segments[i]);
   }
 }
  static public void CreateFromSaveData(CameraPathMetadata cameraPath) {
    // Create a new widget.
    CameraPathWidget widget = Instantiate<CameraPathWidget>(
        WidgetManager.m_Instance.CameraPathWidgetPrefab);
    widget.transform.parent = App.Scene.MainCanvas.transform;

    // The scale of path widgets is arbitrary.  However, the scale should be one at creation
    // time so the knots added below have appropriate mesh scales.
    widget.transform.localScale = Vector3.one;
    widget.transform.localPosition = Vector3.zero;
    widget.transform.localRotation = Quaternion.identity;

    // Add the path knots and set their tangent speed.
    for (int i = 0; i < cameraPath.PathKnots.Length; ++i) {
      GameObject go = Instantiate<GameObject>(
          WidgetManager.m_Instance.CameraPathPositionKnotPrefab);
      go.transform.position = cameraPath.PathKnots[i].Xf.translation;
      go.transform.rotation = cameraPath.PathKnots[i].Xf.rotation;
      go.transform.parent = widget.transform;

      CameraPathPositionKnot knot = go.GetComponent<CameraPathPositionKnot>();
      knot.TangentMagnitude = cameraPath.PathKnots[i].TangentMagnitude;

      widget.m_Path.PositionKnots.Add(knot);
      widget.m_Path.AllKnots.Add(knot);

      if (i > 0) {
        widget.m_Path.Segments.Add(CameraPath.CreateSegment(widget.transform));
      }
    }

    // Refresh the path so the segment curves are correct.
    for (int i = 0; i < cameraPath.PathKnots.Length - 1; ++i) {
      widget.m_Path.RefreshSegment(i);
    }

    // Add the rotation knots.  Note this list is ordered, and they're serialized in order,
    // so we need to make sure they're created in order.
    for (int i = 0; i < cameraPath.RotationKnots.Length; ++i) {
      GameObject go = Instantiate<GameObject>(
          WidgetManager.m_Instance.CameraPathRotationKnotPrefab);
      go.transform.position = cameraPath.RotationKnots[i].Xf.translation;
      go.transform.rotation = cameraPath.RotationKnots[i].Xf.rotation;
      go.transform.parent = widget.transform;

      CameraPathRotationKnot knot = go.GetComponent<CameraPathRotationKnot>();
      knot.PathT = new PathT(cameraPath.RotationKnots[i].PathTValue);
      knot.DistanceAlongSegment = widget.m_Path.GetSegmentDistanceToT(knot.PathT);

      widget.m_Path.RotationKnots.Add(knot);
      widget.m_Path.AllKnots.Add(knot);
    }
    // Align quaternions on all rotation knots so we don't have unexpected camera flips
    // when calculating rotation as we walk the path.
    widget.m_Path.RefreshRotationKnotPolarities();

    // Add the speed knots.  Note this list is ordered, and they're serialized in order,
    // so we need to make sure they're created in order.
    for (int i = 0; i < cameraPath.SpeedKnots.Length; ++i) {
      GameObject go = Instantiate<GameObject>(
          WidgetManager.m_Instance.CameraPathSpeedKnotPrefab);
      go.transform.position = cameraPath.SpeedKnots[i].Xf.translation;
      go.transform.rotation = cameraPath.SpeedKnots[i].Xf.rotation;
      go.transform.parent = widget.transform;

      CameraPathSpeedKnot knot = go.GetComponent<CameraPathSpeedKnot>();

      knot.PathT = new PathT(cameraPath.SpeedKnots[i].PathTValue);
      knot.DistanceAlongSegment = widget.m_Path.GetSegmentDistanceToT(knot.PathT);
      knot.SpeedValue = cameraPath.SpeedKnots[i].Speed;

      widget.m_Path.SpeedKnots.Add(knot);
      widget.m_Path.AllKnots.Add(knot);
    }

    // Add the fov knots.  Note this list is ordered, and they're serialized in order,
    // so we need to make sure they're created in order.
    for (int i = 0; i < cameraPath.FovKnots.Length; ++i) {
      GameObject go = Instantiate<GameObject>(
          WidgetManager.m_Instance.CameraPathFovKnotPrefab);
      go.transform.position = cameraPath.FovKnots[i].Xf.translation;
      go.transform.rotation = cameraPath.FovKnots[i].Xf.rotation;
      go.transform.parent = widget.transform;

      CameraPathFovKnot knot = go.GetComponent<CameraPathFovKnot>();

      knot.PathT = new PathT(cameraPath.FovKnots[i].PathTValue);
      knot.DistanceAlongSegment = widget.m_Path.GetSegmentDistanceToT(knot.PathT);
      knot.FovValue = cameraPath.FovKnots[i].Fov;

      widget.m_Path.FovKnots.Add(knot);
      widget.m_Path.AllKnots.Add(knot);
    }

    // Refresh visuals on the whole path.
    for (int i = 0; i < widget.m_Path.AllKnots.Count; ++i) {
      widget.m_Path.AllKnots[i].RefreshVisuals();
      widget.m_Path.AllKnots[i].ActivateTint(false);
      widget.m_Path.AllKnots[i].SetActivePathVisuals(false);
    }

    // And turn them off.
    widget.m_Path.ValidatePathLooping();
    widget.m_Path.SetKnotsActive(false);
    App.Switchboard.TriggerCameraPathCreated();
  }
  override public float GetActivationScore(Vector3 point, InputManager.ControllerName name) {
    float nearestKnotScore = -1.0f;
    KnotDescriptor nearestResult = new KnotDescriptor();

    if (VideoRecorderUtils.ActiveVideoRecording == null &&
        !WidgetManager.m_Instance.WidgetsDormant &&
        !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate)) {
      // Check against all knots and put our results in storage bins.
      // Note that we're walking along the sorted lists instead of using m_Path.m_AllKnots.
      // This ensures the knotIndex stored in KnotCollisionResult is correct.
      for (int i = 0; i < m_Path.PositionKnots.Count; ++i) {
        int control = -1;
        CameraPathPositionKnot pk = m_Path.PositionKnots[i];
        float knotScore = pk.gameObject.activeSelf ?
            pk.CollisionWithPoint(point, out control) : -1.0f;
        if (knotScore > nearestKnotScore) {
          nearestResult.Set(pk, control, i, null);
          nearestKnotScore = knotScore;
        }
      }
      for (int i = 0; i < m_Path.RotationKnots.Count; ++i) {
        int control = -1;
        CameraPathRotationKnot rk = m_Path.RotationKnots[i];
        float knotScore = rk.CollisionWithPoint(point, out control);
        if (knotScore > nearestKnotScore) {
          nearestResult.Set(rk, control, null, rk.PathT);
          nearestKnotScore = knotScore;
        }
      }
      for (int i = 0; i < m_Path.SpeedKnots.Count; ++i) {
        int control = -1;
        CameraPathSpeedKnot sk = m_Path.SpeedKnots[i];
        float knotScore = sk.CollisionWithPoint(point, out control);
        if (knotScore > nearestKnotScore) {
          nearestResult.Set(sk, control, null, sk.PathT);
          nearestKnotScore = knotScore;
        }
      }
      for (int i = 0; i < m_Path.FovKnots.Count; ++i) {
        int control = -1;
        CameraPathFovKnot fk = m_Path.FovKnots[i];
        float knotScore = fk.CollisionWithPoint(point, out control);
        if (knotScore > nearestKnotScore) {
          nearestResult.Set(fk, control, null, fk.PathT);
          nearestKnotScore = knotScore;
        }
      }
    }

    bool brushOrWand = (name == InputManager.ControllerName.Brush) ||
        (name == InputManager.ControllerName.Wand);
    if (!m_UserInteracting) {
      if (brushOrWand) {
        m_LastCollisionResults[(int)name].Set(nearestResult);
        if ((nearestResult.knot != null) && (nearestResult.control != -1)) {
          m_LastValidCollisionResults[(int)name].Set(nearestResult);
        }
      }
    } else if (m_InteractingController != name && brushOrWand) {
      m_LastCollisionResults[(int)name].Set(null, CameraPathKnot.kDefaultControl, null, null);
    }
    return nearestKnotScore;
  }