Skip to content

Commit

Permalink
feat(Action): determine if surface position has had specified change
Browse files Browse the repository at this point in the history
The SurfaceChangeAction digests SurfaceData and compares the current
surface position and the previous surface position and checks to see if
the distance between the two positions is within the specified
threshold.

This can be useful to capture SurfaceLocator events and only carry out
actions if the surface difference is within the threshold. A good use
case is only blink the camera if the surface difference is over a
certain step size.
  • Loading branch information
thestonefox committed May 13, 2018
1 parent 1e17af6 commit 5974bca
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 0 deletions.
67 changes: 67 additions & 0 deletions Scripts/Action/SurfaceChangeAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
namespace VRTK.Core.Action
{
using UnityEngine;
using VRTK.Core.Data.Type;
using VRTK.Core.Extension;

/// <summary>
/// The SurfaceChangeAction emits an event when all given actions are in their active state.
/// </summary>
public class SurfaceChangeAction : BooleanAction
{
[Tooltip("The distance between the current surface and previous surface to consider a valid change.")]
public float changeDistance = 0.5f;
[Tooltip("The axes to check for distance differences on.")]
public Vector3State checkAxis = new Vector3State(true, true, true);

/// <summary>
/// The Receive method digests SurfaceData and compares the current surface to the previous surface to determine if a change has occured.
/// </summary>
/// <param name="surfaceData">The SurfaceData to check on.</param>
/// <param name="sender">The sender of the action.</param>
public virtual void Receive(SurfaceData surfaceData, object sender = null)
{
if (ValidSurfaceData(surfaceData))
{
Vector3 generatedOrigin = GetCollisionPoint(surfaceData.PreviousCollisionData);
Vector3 generatedTarget = GeneratePoint(surfaceData.Position);

bool result = !generatedOrigin.Compare(generatedTarget, changeDistance);
Receive(result, sender);
}
}

/// <summary>
/// The ValidSurfaceData method checks to see if the given SurfaceData is valid.
/// </summary>
/// <param name="surfaceData">The SurfaceData to check on.</param>
/// <returns>Returns `true` if the SurfaceData given is valid.</returns>
protected virtual bool ValidSurfaceData(SurfaceData surfaceData)
{
return (surfaceData != null && surfaceData.Valid);
}

/// <summary>
/// The GetCollisionPoint method attempts to get the collision point for the given RaycastHit data.
/// </summary>
/// <param name="collisionData">The RaycastHit data to get the collision point from.</param>
/// <returns>Returns a Vector3 of the collision point.</returns>
protected virtual Vector3 GetCollisionPoint(RaycastHit collisionData)
{
return (collisionData.transform != null ? GeneratePoint(collisionData.point) : Vector3.zero);
}

/// <summary>
/// The GeneratePoint method creates a Vector3 based on the given point for the valid axes.
/// </summary>
/// <param name="point">The Point to generate the Vector3 from.</param>
/// <returns>A Vector3 of the point only within the valid axes.</returns>
protected virtual Vector3 GeneratePoint(Vector3 point)
{
float resultX = (checkAxis.xState ? point.x : 0f);
float resultY = (checkAxis.yState ? point.y : 0f);
float resultZ = (checkAxis.zState ? point.z : 0f);
return new Vector3(resultX, resultY, resultZ);
}
}
}
11 changes: 11 additions & 0 deletions Scripts/Action/SurfaceChangeAction.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

101 changes: 101 additions & 0 deletions Tests/Editor/Action/SurfaceChangeActionTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
namespace VRTK.Core.Action
{
using UnityEngine;
using NUnit.Framework;
using VRTK.Core.Utility.Mock;
using VRTK.Core.Data.Type;

public class SurfaceChangeActionTest
{
private GameObject containingObject;
private SurfaceChangeActionMock subject;

[SetUp]
public void SetUp()
{
containingObject = new GameObject();
subject = containingObject.AddComponent<SurfaceChangeActionMock>();
}

[TearDown]
public void TearDown()
{
Object.DestroyImmediate(subject);
Object.DestroyImmediate(containingObject);
}

[Test]
public void SurfaceChanged()
{
UnityEventListenerMock activatedListenerMock = new UnityEventListenerMock();
subject.Activated.AddListener(activatedListenerMock.Listen);
subject.changeDistance = 1f;
subject.checkAxis = Vector3State.True;

SurfaceData surfaceData = new SurfaceData(Vector3.one, Vector3.down);

//set current surface to zero
RaycastHit ray = new RaycastHit
{
point = Vector3.zero
};
surfaceData.CollisionData = ray;
surfaceData.positionOverride = ray.point;
//set surface to one so there is a change between surface positions
ray = new RaycastHit
{
point = Vector3.one
};
surfaceData.CollisionData = ray;
surfaceData.positionOverride = ray.point;

subject.Receive(surfaceData, null);

Assert.IsTrue(activatedListenerMock.Received);
}

[Test]
public void SurfaceUnchanged()
{
UnityEventListenerMock activatedListenerMock = new UnityEventListenerMock();
subject.Activated.AddListener(activatedListenerMock.Listen);
subject.changeDistance = 1f;
subject.checkAxis = new Vector3State(false, true, false);

SurfaceData surfaceData = new SurfaceData(Vector3.one, Vector3.down);

//set current surface to zero
RaycastHit ray = new RaycastHit
{
point = Vector3.zero
};
surfaceData.CollisionData = ray;
surfaceData.positionOverride = ray.point;
//set surface to one so there is a change between surface positions
ray = new RaycastHit
{
point = Vector3.one
};
surfaceData.CollisionData = ray;
surfaceData.positionOverride = ray.point;

subject.Receive(surfaceData, null);

Assert.IsFalse(activatedListenerMock.Received);
}
}

public class SurfaceChangeActionMock : SurfaceChangeAction
{
//As The transform in the RaycastHit cannot be set without doing an actual raycast, just ignore that check for the test.
protected override bool ValidSurfaceData(SurfaceData surfaceData)
{
return true;
}

protected override Vector3 GetCollisionPoint(RaycastHit collisionData)
{
return GeneratePoint(collisionData.point);
}
}
}
11 changes: 11 additions & 0 deletions Tests/Editor/Action/SurfaceChangeActionTest.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 5974bca

Please sign in to comment.