Skip to content

Commit

Permalink
Merge pull request #64 from bjrtx/main
Browse files Browse the repository at this point in the history
Improve APF and Guardian handling
  • Loading branch information
bjrtx authored May 22, 2024
2 parents 6b9d60c + 8efa0ad commit ab0335a
Show file tree
Hide file tree
Showing 17 changed files with 251 additions and 179 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public record JsonRedirectionData {
/// target selection strategy, if any, or the empty string.
/// </value>
public string Strategy => script switch {
WorldRedirection => (script as WorldRedirection).strategy.ToString(),
WorldRedirection wr => wr.strategy.ToString(),
BodyRedirection => String.Empty,
_ => String.Empty
};
Expand All @@ -55,9 +55,9 @@ public record JsonRedirectionData {
/// technique, if any, or the empty string.
/// </value>
public string Technique => script switch {
WorldRedirection => (script as WorldRedirection).Technique.ToString(),
BodyRedirection => (script as BodyRedirection).Technique.ToString(),
_ => ""
WorldRedirection wr => wr.Technique.ToString(),
BodyRedirection br => br.Technique.ToString(),
_ => String.Empty
};

public bool Redirecting => script.redirect;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ namespace VHToolkit.Logging {
public record RedirectionData {
public DateTime timeStamp = DateTime.Now;
public string Technique => script switch {
WorldRedirection => (script as WorldRedirection).Technique.ToString(),
BodyRedirection => (script as BodyRedirection).Technique.ToString(),
_ => ""
WorldRedirection wr => wr.Technique.ToString(),
BodyRedirection br => br.Technique.ToString(),
_ => String.Empty
};

public Interaction script;
Expand Down Expand Up @@ -119,7 +119,6 @@ private sealed class FileObserver : AbstractFileObserver<RedirectionData> {
}

public override void OnNext(RedirectionData value) {
csvWriter.Context.RegisterClassMap<RedirectionDataMap>();
csvWriter.WriteRecord(value);
csvWriter.NextRecord();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class BodyRedirection : Interaction {
/// Updates the techniqueInstance according to the enumeration technique chosen.
/// </summary>
private void UpdateTechnique() {
techniqueInstance = _technique switch {
techniqueInstance = Technique switch {
BRTechnique.None => new NoBodyRedirection(),
BRTechnique.Reset => new ResetBodyRedirection(),
BRTechnique.Azmandian2016Body => new Azmandian2016Body(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,28 @@
using UnityEngine;

namespace VHToolkit.Redirection {

[System.AttributeUsage(System.AttributeTargets.All, Inherited = false, AllowMultiple = true)]
public abstract class EditorRedirectionAttribute : System.Attribute { }
public sealed class ShowHeadAttribute : EditorRedirectionAttribute { }
public sealed class HasBufferAttribute : EditorRedirectionAttribute { }
public sealed class HasThresholdAttribute : EditorRedirectionAttribute { }
public sealed class HasStrategyAttribute : EditorRedirectionAttribute { }
public sealed class HasTargetsAttribute : EditorRedirectionAttribute { }
public enum BRTechnique {
None,
Reset,
[InspectorName("")] SEPARATOR1, // Adds a visual separator in the drop-down inspector
// Hand Redirection techniques
Han2018TranslationalShift,
Han2018InterpolatedReach,
Azmandian2016Body,
Azmandian2016Hybrid,
Cheng2017Sparse,
Geslain2022Polynom,
Poupyrev1996GoGo,
[HasThreshold] Han2018TranslationalShift,
[HasBuffer, HasThreshold] Han2018InterpolatedReach,
[HasBuffer, HasThreshold] Azmandian2016Body,
[ShowHead, HasThreshold] Azmandian2016Hybrid,
[HasBuffer, HasThreshold] Cheng2017Sparse,
[HasBuffer, HasThreshold] Geslain2022Polynom,
[ShowHead] Poupyrev1996GoGo,
[InspectorName(" ")] SEPARATOR2,
Kohli2010RedirectedTouching,
[HasThreshold] Kohli2010RedirectedTouching,
[InspectorName(" ")] SEPARATOR3,
// Pseudo-haptic techiques
Lecuyer2000Swamp,
Expand All @@ -30,20 +38,20 @@ public enum WRTechnique {
None,
Reset,
[InspectorName("")] SEPARATOR1,
Razzaque2001OverTimeRotation,
Razzaque2001Rotational,
Razzaque2001Curvature,
Razzaque2001Hybrid,
[HasStrategy] Razzaque2001OverTimeRotation,
[HasStrategy] Razzaque2001Rotational,
[HasStrategy] Razzaque2001Curvature,
[HasStrategy] Razzaque2001Hybrid,
Azmandian2016World,
Steinicke2008Translational
}

public enum WRStrategy {
NoSteering,
[InspectorName("")] SEPARATOR1,
SteerToCenter,
[HasTargets] SteerToCenter,
SteerToOrbit,
SteerToMultipleTargets,
[HasTargets] SteerToMultipleTargets,
SteerInDirection,
[InspectorName(" ")] SEPARATOR2,
APF_PushPull
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
namespace VHToolkit.Redirection {
abstract public class RedirectionTechnique {
/// <summary>
/// This abstract method redirects the Scene transforms according to the other parameters and the equations
/// defined in the corresponding techniques. It needs to be overriden by a child class, and it is called on Update() in *Redirection classes.
/// <c>Redirect</c> redirects the Scene transforms according to the other parameters and the equations
/// defined in the corresponding techniques. It needs to be overriden in children classes, and is called on Update() in *Redirection classes.
/// </summary>
public abstract void Redirect(Scene scene);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace VHToolkit {
/// Provide language features not present in this version of .Net.
/// </summary>
static class Future {
public static IEnumerable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(
public static IEnumerable<(TFirst, TSecond)> Zip<TFirst, TSecond>(
this IEnumerable<TFirst> first,
IEnumerable<TSecond> second
) => first.Zip(second, resultSelector: (x, y) => (x, y));
Expand Down Expand Up @@ -44,6 +44,9 @@ public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source, Fun
}
return result;
}

public static IEnumerable<(T, T)> CyclicPairs<T>(this List<T> list) => list.Zip(list.Skip(1).Append(list.First()));

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

using VHToolkit.Redirection.PseudoHaptics;
using VHToolkit.Redirection.Interpolation3D;
using System;
using VHToolkit.Redirection;
using System.Linq;

namespace VHToolkit.Redirection.BodyRedirection {
/// <summary>
Expand All @@ -26,27 +29,24 @@ public class BodyRedirectionEditor : Editor {
SerializedProperty redirect;
SerializedProperty parameters;
SerializedObject parametersObject;

readonly HashSet<string> bufferTechniques = new() { nameof(Han2018InterpolatedReach), nameof(Azmandian2016Body), nameof(Geslain2022Polynom), nameof(Cheng2017Sparse) };
readonly HashSet<string> noThresholdTechniques = new() { nameof(Poupyrev1996GoGo), nameof(Lecuyer2000Swamp), nameof(Samad2019Weight) };

private SerializedProperty Find(string name) => serializedObject.FindProperty(name);

private void OnEnable() {

technique = serializedObject.FindProperty("_technique");
techniqueInstance = serializedObject.FindProperty("techniqueInstance");
technique = Find("_technique");
techniqueInstance = Find("techniqueInstance");

limbs = serializedObject.FindProperty("scene.limbs");
physicalHead = serializedObject.FindProperty("scene.physicalHead");
virtualHead = serializedObject.FindProperty("scene.virtualHead");
physicalTarget = serializedObject.FindProperty("scene.physicalTarget");
virtualTarget = serializedObject.FindProperty("scene.virtualTarget");
origin = serializedObject.FindProperty("scene.origin");
referenceSurface = serializedObject.FindProperty("scene.referenceParent");
interpolatedSurface = serializedObject.FindProperty("scene.interpolatedParent");
limbs = Find("scene.limbs");
physicalHead = Find("scene.physicalHead");
virtualHead = Find("scene.virtualHead");
physicalTarget = Find("scene.physicalTarget");
virtualTarget = Find("scene.virtualTarget");
origin = Find("scene.origin");
referenceSurface = Find("scene.referenceParent");
interpolatedSurface = Find("scene.interpolatedParent");

redirect = serializedObject.FindProperty("redirect");
parameters = serializedObject.FindProperty("scene.parameters");
redirect = Find("redirect");
parameters = Find("scene.parameters");
}

private void MakePropertyField(SerializedProperty property, string text, string tooltip = null) {
Expand All @@ -66,13 +66,16 @@ public override void OnInspectorGUI() {
// Scene
MakePropertyField(limbs, "User Limbs", "A list of tracked user limbs.");

string techniqueName = technique.enumNames[technique.enumValueIndex];
Enum.TryParse(technique.enumNames[technique.enumValueIndex], out BRTechnique actualTechnique);

if (techniqueName == nameof(Azmandian2016Hybrid)) {
var enumType = typeof(BRTechnique);
var memberInfos = enumType.GetMember(actualTechnique.ToString());
var enumValueMemberInfo = memberInfos.FirstOrDefault(m => m.DeclaringType == enumType);
if (enumValueMemberInfo.IsDefined(typeof(ShowHeadAttribute), false)) {
MakePropertyField(physicalHead, "Physical Head", "The Transform of the VR headset worn by the user.");
}
if (actualTechnique == BRTechnique.Azmandian2016Hybrid) {
MakePropertyField(virtualHead, "Virtual Head", "");
} else if (techniqueName == nameof(Poupyrev1996GoGo)) {
MakePropertyField(physicalHead, "Physical Head", "");
}

EditorGUILayout.Space(5);
Expand All @@ -92,37 +95,39 @@ public override void OnInspectorGUI() {
parametersObject = new SerializedObject(parameters.objectReferenceValue);
parametersObject.Update();

if (techniqueName == nameof(Kohli2010RedirectedTouching)) {
if (actualTechnique == BRTechnique.Kohli2010RedirectedTouching) {
MakePropertyField(referenceSurface, "Reference Surface", "Points on the physical surface that the physical hand of the user explores. This Transform should be the parent of the points. The order of points is matched with the order of interpolated surface.");
MakePropertyField(interpolatedSurface, "Interpolated Surface", "Points on the virtual surface that the virtual hand of the user will explore when touching the physical surface.This Transform should be the parent of the points. The order of points is matched with the order of reference surface.");
} else {
}
else {
MakePropertyField(physicalTarget, "Physical Target", "");
MakePropertyField(virtualTarget, "Virtual Target", "");
MakePropertyField(origin, "Origin", "");
}


if (techniqueName == nameof(Kohli2010RedirectedTouching)) {
if (actualTechnique == BRTechnique.Kohli2010RedirectedTouching) {
MakePropertyField(parametersObject.FindProperty("SmoothingParameter"), "Smoothing", "");
MakePropertyField(parametersObject.FindProperty("Rescale"), "Rescale", "");
} else if (techniqueName == nameof(Geslain2022Polynom)) {
MakePropertyField(parametersObject.FindProperty("redirectionLateness"), "Redirection Lateness (a2)");
MakePropertyField(parametersObject.FindProperty("controlPoint"), "ControlPoint");
} else if (techniqueName == nameof(Poupyrev1996GoGo)) {
}
else if (actualTechnique == BRTechnique.Geslain2022Polynom) {
// TODO broken
//MakePropertyField(parametersObject.FindProperty("redirectionLateness"), "Redirection Lateness (a2)");
//MakePropertyField(parametersObject.FindProperty("controlPoint"), "ControlPoint");
}
else if (actualTechnique == BRTechnique.Poupyrev1996GoGo) {
MakePropertyField(parametersObject.FindProperty("GoGoCoefficient"), "Coefficient");
MakePropertyField(parametersObject.FindProperty("GoGoActivationDistance"), "Activation Distance");
}

if (bufferTechniques.Contains(techniqueName)) {
if (enumValueMemberInfo.IsDefined(typeof(HasBufferAttribute), false)) {
MakePropertyField(parametersObject.FindProperty("RedirectionBuffer"), "Redirection Buffer");
}

if (noThresholdTechniques.Contains(techniqueName)) {
if (techniqueName == nameof(Lecuyer2000Swamp)) {
MakePropertyField(parametersObject.FindProperty("SwampSquareLength"), "Square Side Length");
MakePropertyField(parametersObject.FindProperty("SwampCDRatio"), "C/D Ratio");
}
} else {
if (actualTechnique == BRTechnique.Lecuyer2000Swamp) {
MakePropertyField(parametersObject.FindProperty("SwampSquareLength"), "Square Side Length");
MakePropertyField(parametersObject.FindProperty("SwampCDRatio"), "C/D Ratio");
}
if (enumValueMemberInfo.IsDefined(typeof(HasThresholdAttribute), false)) {
EditorGUILayout.Space(5);
EditorGUILayout.LabelField("Threshold Parameters", EditorStyles.largeLabel);
MakePropertyField(parametersObject.FindProperty(nameof(ParametersToolkit.HorizontalAngles)), "Max Horizontal Angles");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using UnityEditor;

using System.Collections.Generic;
using System;
using System.Linq;

namespace VHToolkit.Redirection.WorldRedirection {
/// <summary>
Expand All @@ -20,8 +22,6 @@ public class WorldRedirectionEditor : Editor {
enableOverTime, enableRotational, enableCurvature,
parameters;
SerializedObject parametersObject;
readonly HashSet<string> strategyTechniques = new() { "Razzaque2001OverTimeRotation", "Razzaque2001Rotational", "Razzaque2001Curvature", "Razzaque2001Hybrid" };
readonly HashSet<string> targetsStrategies = new() { "SteerToCenter", "SteerToMultipleTargets" };

private void OnEnable() {
technique = serializedObject.FindProperty("_technique");
Expand Down Expand Up @@ -61,13 +61,16 @@ public override void OnInspectorGUI() {
EditorGUILayout.Space(5);
EditorGUILayout.LabelField("User Parameters", EditorStyles.largeLabel);

string techniqueName = technique.enumNames[technique.enumValueIndex];
Enum.TryParse(technique.enumNames[technique.enumValueIndex], out WRTechnique actualTechnique);
var enumType = typeof(WRTechnique);
var memberInfos = enumType.GetMember(actualTechnique.ToString());
var enumValueMemberInfo = memberInfos.FirstOrDefault(m => m.DeclaringType == enumType);

MakePropertyField(limbs, "User Limbs", "A list of tracked user limbs.");
MakePropertyField(physicalHead, "Physical Head", "Transform tracking the user's real head");
MakePropertyField(virtualHead, "Virtual Head", "Transform tracking the user's virtual head");

if (techniqueName == nameof(Azmandian2016World)) {
if (actualTechnique == WRTechnique.Azmandian2016World) {
MakePropertyField(physicalTarget, "Physical Target");
MakePropertyField(virtualTarget, "Virtual Target");
MakePropertyField(origin, "Origin");
Expand All @@ -92,18 +95,21 @@ public override void OnInspectorGUI() {
parametersObject.Update();

EditorGUILayout.Space(2);
if (techniqueName == nameof(Razzaque2001OverTimeRotation)) {
if (actualTechnique == WRTechnique.Razzaque2001OverTimeRotation) {
MakePropertyField(parametersObject.FindProperty("RotationalError"), "Rotational Error");
MakePropertyField(parametersObject.FindProperty("OverTimeRotation"), "Over Time Rotation Rate");
} else if (techniqueName == nameof(Razzaque2001Rotational)) {
}
else if (actualTechnique == WRTechnique.Razzaque2001Rotational) {
MakePropertyField(parametersObject.FindProperty("RotationalError"), "Rotational Error");
MakePropertyField(parametersObject.FindProperty("GainsRotational"), "Rotational Gains");
MakePropertyField(parametersObject.FindProperty("RotationalThreshold"), "Rotational Threshold");
} else if (techniqueName == nameof(Razzaque2001Curvature)) {
}
else if (actualTechnique == WRTechnique.Razzaque2001Curvature) {
MakePropertyField(parametersObject.FindProperty("RotationalError"), "Rotational Error");
MakePropertyField(parametersObject.FindProperty("CurvatureRadius"), "Curvature Radius");
MakePropertyField(parametersObject.FindProperty("WalkingThreshold"), "Walking Threshold");
} else if (techniqueName == nameof(Razzaque2001Hybrid)) {
}
else if (actualTechnique == WRTechnique.Razzaque2001Hybrid) {
MakePropertyField(parametersObject.FindProperty("RotationalError"), "Rotational Error");
MakePropertyField(enableOverTime, "Enable Over Time Rotation");
if (enableOverTime.boolValue)
Expand Down Expand Up @@ -143,37 +149,46 @@ public override void OnInspectorGUI() {
script.techniqueInstance = Razzaque2001Hybrid.Max();
break;
}
} else if (techniqueName == nameof(Azmandian2016World)) {
}
else if (actualTechnique == WRTechnique.Azmandian2016World) {
MakePropertyField(parametersObject.FindProperty("GainsRotational"), "Gains Rotational");
} else if (techniqueName == nameof(Steinicke2008Translational)) {
}
else if (actualTechnique == WRTechnique.Steinicke2008Translational) {
MakePropertyField(parametersObject.FindProperty("GainsTranslational"), "Translational Gains");
}


// Hides targets, dampening and smoothing if
if (strategyTechniques.Contains(techniqueName)) {
if (enumValueMemberInfo.IsDefined(typeof(HasStrategyAttribute), false)) {
EditorGUILayout.Space(5);
EditorGUILayout.LabelField("Strategy Parameters", EditorStyles.largeLabel);
MakePropertyField(strategy, "Target selection strategy");

if (targetsStrategies.Contains(strategy.enumNames[strategy.enumValueIndex])) {
Enum.TryParse(strategy.enumNames[strategy.enumValueIndex], out WRStrategy actualStrategy);
var strategyEnumType = typeof(WRStrategy);
var strategyMemberInfos = strategyEnumType.GetMember(actualStrategy.ToString());
var strategyEnumValueMemberInfo = strategyMemberInfos.FirstOrDefault(m => m.DeclaringType == strategyEnumType);

if (strategyEnumValueMemberInfo.IsDefined(typeof(HasTargetsAttribute), false)) {
MakePropertyField(targetsScene, "Targets");
MakePropertyField(applyDampening, "Apply Dampening");
MakePropertyField(parametersObject.FindProperty("DampeningDistanceThreshold"), "Dampening Distance Threshold");
MakePropertyField(parametersObject.FindProperty("DampeningRange"), "Dampening Range");
MakePropertyField(applySmoothing, "Apply Smoothing");
MakePropertyField(parametersObject.FindProperty("SmoothingFactor"), "Smoothing Factor");
} else if (strategy.enumNames[strategy.enumValueIndex] == nameof(SteerToOrbit)) {
}
else if (actualStrategy == WRStrategy.SteerToOrbit) {
MakePropertyField(targetsScene, "Targets");

var steerToOrbitRadius = parametersObject.FindProperty("SteerToOrbitRadius");
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(steerToOrbitRadius, new GUIContent("Steer To Orbit Radius"));
EditorGUILayout.Space(20);
EditorGUILayout.LabelField("Rotation Rate: " + (360f / (2 * Mathf.PI * steerToOrbitRadius.floatValue)).ToString("N2") + " °/m/s");
EditorGUILayout.PropertyField(steerToOrbitRadius, new GUIContent("Steer To Orbit Radius"));
EditorGUILayout.Space(20);
EditorGUILayout.LabelField($"Rotation Rate: {(360f / (2 * Mathf.PI * steerToOrbitRadius.floatValue)).ToString("N2")} °/m/s");
GUILayout.EndHorizontal();

} else if (strategy.enumNames[strategy.enumValueIndex] == nameof(SteerInDirection)) {
}
else if (actualStrategy == WRStrategy.SteerInDirection) {
MakePropertyField(direction, "Direction");
}
}
Expand Down
Loading

0 comments on commit ab0335a

Please sign in to comment.