Add option to set endpoints for stretch and toggle
This commit is contained in:
parent
9ab3d75e7b
commit
5f8b5f8112
@ -1,11 +1,21 @@
|
||||
namespace VRC_Console;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace VRC_Console;
|
||||
|
||||
internal class Config
|
||||
{
|
||||
internal static readonly string ConfigFilePath = Path.Combine(Directory.GetCurrentDirectory(), "config.json");
|
||||
public string Ip = "127.0.0.1";
|
||||
public int PortSend = 9000;
|
||||
public double Radius = 100;
|
||||
public double WalkStretchDeadzone = 0.1;
|
||||
public double RunStretch = 0.4;
|
||||
public int CalibrationValues = 128;
|
||||
public string LeashStretchParameter = "Leash/Leash_Stretch";
|
||||
public string LeashToggleParameter = "Leash/Toggle";
|
||||
|
||||
public Config()
|
||||
{
|
||||
File.WriteAllText(ConfigFilePath, JsonConvert.SerializeObject(this, Formatting.Indented));
|
||||
}
|
||||
}
|
@ -1,28 +1,44 @@
|
||||
using Spectre.Console;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace VRC_Console;
|
||||
|
||||
public partial class OSCCollar
|
||||
{
|
||||
private const byte CoordinateSystemSize = 16;
|
||||
private const int MinWindowHeight = 10;
|
||||
private const int MinWindowWidth = 60;
|
||||
|
||||
private void CheckConsoleMinSize()
|
||||
{
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
return;
|
||||
if (Console.WindowHeight < MinWindowHeight)
|
||||
Console.WindowHeight = MinWindowHeight;
|
||||
if (Console.WindowWidth < MinWindowWidth)
|
||||
Console.WindowWidth = MinWindowWidth;
|
||||
}
|
||||
|
||||
private void PrintConsole()
|
||||
{
|
||||
Console.Clear();
|
||||
CheckConsoleMinSize();
|
||||
ValueTuple<string, string>[] configValues = new[]
|
||||
{
|
||||
new ValueTuple<string, string>("IP", this._config.Ip),
|
||||
new ValueTuple<string, string>("Send-Osc-Port", this._config.PortSend.ToString()),
|
||||
new ValueTuple<string, string>("IP", Config.Ip),
|
||||
new ValueTuple<string, string>("Send-Osc-Port", Config.PortSend.ToString()),
|
||||
new ValueTuple<string, string>("Receive-Osc-Port", this._osc.OSCPort.ToString()),
|
||||
new ValueTuple<string, string>("Calibration Values", $"{this._calibrationAverage.Count}/{this._config.CalibrationValues}"),
|
||||
new ValueTuple<string, string>("Calibration Values", $"{this._calibrationAverage.Count}/{Config.CalibrationValues}"),
|
||||
new ValueTuple<string, string>("Stretch Parameter", Config.LeashStretchParameter),
|
||||
new ValueTuple<string, string>("Toggle Parameter", Config.LeashToggleParameter),
|
||||
new ValueTuple<string, string>("AvatarId", _avatarId.Length < 1 ? "Reset Avatar to show." : _avatarId),
|
||||
};
|
||||
|
||||
ValueTuple<string, string>[] variableValues = new[]
|
||||
{
|
||||
new ValueTuple<string, string>("Status", $"{(_allowMoving ? Resources.OSCCollar_ConsoleOutput_StatusEnabled : Resources.OSCCollar_ConsoleOutput_StatusDisabled)}"),
|
||||
new ValueTuple<string, string>("GPS 1", $"{_gps1.Distance: 0.00000}"),
|
||||
new ValueTuple<string, string>("GPS 2", $"{_gps2.Distance: 0.00000}"),
|
||||
new ValueTuple<string, string>("GPS 3", $"{_gps3.Distance: 0.00000}"),
|
||||
new ValueTuple<string, string>("GPS 1", $"{refPos1.Distance: 0.00000}"),
|
||||
new ValueTuple<string, string>("GPS 2", $"{refPos2.Distance: 0.00000}"),
|
||||
new ValueTuple<string, string>("GPS 3", $"{refPos3.Distance: 0.00000}"),
|
||||
new ValueTuple<string, string>(Resources.OSCCollar_ConsoleOutput_PositionVector, _unitVectorLeash.ToString()),
|
||||
new ValueTuple<string, string>(Resources.OSCCollar_ConsoleOutput_LeashStretch, _leashStretch.ToString(" 0.00000")),
|
||||
new ValueTuple<string, string>(Resources.OSCCollar_ConsoleOutput_MovementVector, _movementVector.ToString()),
|
||||
@ -57,7 +73,7 @@ public partial class OSCCollar
|
||||
}
|
||||
|
||||
Console.SetCursorPosition(0, Console.WindowHeight - 2);
|
||||
Console.Write($"Last Hand Reset: {DateTime.Now.Subtract(_lastNilMessageSent):ss\\.fff}\t\tAvatarId: {(_avatarId.Length < 1 ? "Reset Avatar to show." : _avatarId)}\t\tRuntime: {DateTime.Now.Subtract(_programStarted):hh\\:mm\\:ss\\.fff}");
|
||||
Console.Write($"Last Hand Reset: {DateTime.Now.Subtract(_lastNilMessageSent):ss\\.fff}\tLast Message Received: {(_lastMessageReceived == DateTime.UnixEpoch ? "never" : DateTime.Now.Subtract(_lastMessageReceived)):ss\\.fff} {_lastMessageReceivedParameter}\tRuntime: {DateTime.Now.Subtract(_programStarted):hh\\:mm\\:ss\\.fff}");
|
||||
}
|
||||
|
||||
private String GetCoordinateSystem()
|
||||
|
@ -5,29 +5,13 @@ namespace VRC_Console;
|
||||
|
||||
public partial class OSCCollar
|
||||
{
|
||||
private static readonly string ConfigFilePath = Path.Combine(Directory.GetCurrentDirectory(), "config.json");
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
OSCCollar _;
|
||||
if (File.Exists(ConfigFilePath))
|
||||
{
|
||||
Config? config = JsonConvert.DeserializeObject<Config>(File.ReadAllText(ConfigFilePath));
|
||||
if(config is { })
|
||||
{
|
||||
_ = new OSCCollar(config);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = new OSCCollar();
|
||||
}
|
||||
}else
|
||||
{
|
||||
_ = new OSCCollar();
|
||||
}
|
||||
_ = new OSCCollar();
|
||||
}
|
||||
|
||||
private readonly Config _config;
|
||||
private static readonly Config Config = File.Exists(Config.ConfigFilePath) ? JsonConvert.DeserializeObject<Config>(File.ReadAllText(Config.ConfigFilePath))! : new Config();
|
||||
private readonly OSC _osc;
|
||||
|
||||
private Task _consoleOutputTask;
|
||||
@ -38,25 +22,24 @@ public partial class OSCCollar
|
||||
private Vector _movementVector = new(0, 0);
|
||||
private bool _allowMoving = true;
|
||||
private DateTime _lastNilMessageSent = DateTime.UnixEpoch;
|
||||
private DateTime _lastMessageReceived = DateTime.UnixEpoch;
|
||||
private string _lastMessageReceivedParameter = "null";
|
||||
private readonly DateTime _programStarted = DateTime.Now;
|
||||
private static readonly TimeSpan UpdateInterval = TimeSpan.FromMilliseconds(10);
|
||||
private static readonly TimeSpan MessageMinInterval = TimeSpan.FromMilliseconds(400);
|
||||
private GPS _gps1 = null!, _gps2 = null!, _gps3 = null!;
|
||||
private static readonly TimeSpan UpdateInterval = TimeSpan.FromMilliseconds(10); //Minimum is 10ms for VRC to register
|
||||
private static readonly TimeSpan MessageMinInterval = TimeSpan.FromMilliseconds(400); //Reset OSC movement at least once per second, to prevent hands from resetting to desktop position
|
||||
private ReferencePosition refPos1 = null!, refPos2 = null!, refPos3 = null!;
|
||||
private readonly Queue<ValueTuple<double, double>> _calibrationAverage = new();
|
||||
private double _constantA, _constantB, _constantC, _constantD, _constantE;
|
||||
private double _constantA, _constantB, _constantC, _constantD, _constantE;
|
||||
|
||||
private OSCCollar(Config config, bool skipSetup = true)
|
||||
private OSCCollar(bool skipSetup = true)
|
||||
{
|
||||
this._config = config;
|
||||
File.WriteAllText(ConfigFilePath, JsonConvert.SerializeObject(this._config, Formatting.Indented));
|
||||
|
||||
this._osc = new OSC("OSCCollar", new Dictionary<string, Type>()
|
||||
this._osc = new OSC("OSCCollar", new List<OSCEndpoint>()
|
||||
{
|
||||
{ "/avatar/parameters/GPS1", typeof(float) },
|
||||
{ "/avatar/parameters/GPS2", typeof(float) },
|
||||
{ "/avatar/parameters/GPS3", typeof(float) },
|
||||
{ "/avatar/parameters/Leash_Stretch", typeof(float) },
|
||||
{ "/avatar/parameters/Leash_Toggle", typeof(bool) },
|
||||
new("/avatar/parameters/GPS1", typeof(float), OnGPSParameterChanged),
|
||||
new("/avatar/parameters/GPS2", typeof(float), OnGPSParameterChanged),
|
||||
new("/avatar/parameters/GPS3", typeof(float), OnGPSParameterChanged),
|
||||
new($"/avatar/parameters/{Config.LeashStretchParameter}", typeof(float), OnLeashStretchChanged),
|
||||
new($"/avatar/parameters/{Config.LeashToggleParameter}", typeof(bool), OnLeashToggled),
|
||||
});
|
||||
this._osc.OnParameterChangeEvent += OnOscParameterChangeEvent;
|
||||
|
||||
@ -64,10 +47,10 @@ public partial class OSCCollar
|
||||
if (!skipSetup)
|
||||
{
|
||||
Console.WriteLine(Resources.OSCCollar_OSCCollar_Position_your_GPS_receivers);
|
||||
Console.WriteLine(Resources.OSCCollar_OSCCollar_GPS_1_position, _gps1.X, _gps1.Y);
|
||||
Console.WriteLine(Resources.OSCCollar_OSCCollar_GPS_2_position, _gps2.X, _gps2.Y);
|
||||
Console.WriteLine(Resources.OSCCollar_OSCCollar_GPS_3_position, _gps3.X, _gps3.Y);
|
||||
Console.WriteLine(Resources.OSCCollar_OSCCollar_Radius_of_each_receiver, this._config.Radius * 2);
|
||||
Console.WriteLine(Resources.OSCCollar_OSCCollar_GPS_1_position, refPos1.X, refPos1.Y);
|
||||
Console.WriteLine(Resources.OSCCollar_OSCCollar_GPS_2_position, refPos2.X, refPos2.Y);
|
||||
Console.WriteLine(Resources.OSCCollar_OSCCollar_GPS_3_position, refPos3.X, refPos3.Y);
|
||||
Console.WriteLine(Resources.OSCCollar_OSCCollar_Radius_of_each_receiver, Config.Radius * 2);
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
@ -75,85 +58,86 @@ public partial class OSCCollar
|
||||
runningThread.Start();
|
||||
}
|
||||
|
||||
private void OnOscParameterChangeEvent(string endpoint, object? oldvalue, object? newvalue)
|
||||
private void OnLeashToggled(string endpoint, object? oldValue, object? newValue)
|
||||
{
|
||||
this._allowMoving = (bool)(newValue ?? false);
|
||||
}
|
||||
|
||||
|
||||
private void OnLeashStretchChanged(string endpoint, object? oldValue, object? newValue)
|
||||
{
|
||||
this._leashStretch = (float)(newValue ?? 0);
|
||||
}
|
||||
|
||||
private void OnGPSParameterChanged(string endpoint, object? oldValue, object? newValue)
|
||||
{
|
||||
switch (endpoint)
|
||||
{
|
||||
case "/avatar/parameters/GPS1":
|
||||
this._gps1.Distance = 1 - (float)(newvalue ?? 1);
|
||||
this.refPos1.Distance = 1 - (float)(newValue ?? 1);
|
||||
break;
|
||||
case "/avatar/parameters/GPS2":
|
||||
this._gps2.Distance = 1 - (float)(newvalue ?? 1);
|
||||
this.refPos2.Distance = 1 - (float)(newValue ?? 1);
|
||||
break;
|
||||
case "/avatar/parameters/GPS3":
|
||||
this._gps3.Distance = 1 - (float)(newvalue ?? 1);
|
||||
break;
|
||||
case "/avatar/parameters/Leash_Stretch":
|
||||
this._leashStretch = (float)(newvalue ?? 0);
|
||||
break;
|
||||
case "/avatar/parameters/Leash_Toggle":
|
||||
this._allowMoving = (bool)(newvalue ?? false);
|
||||
break;
|
||||
case "/avatar/change":
|
||||
this._avatarId = (string)(newvalue ?? "");
|
||||
this.refPos3.Distance = 1 - (float)(newValue ?? 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private OSCCollar(string ip = "127.0.0.1", int portSend = 9000, double radius = 100, double walkStretchDeadzone = 0.1, double runStretch = 0.4, int calibrationValues = 128,
|
||||
bool skipSetup = true) : this(new Config()
|
||||
{
|
||||
Ip = ip,
|
||||
PortSend = portSend,
|
||||
WalkStretchDeadzone = walkStretchDeadzone,
|
||||
RunStretch = runStretch,
|
||||
Radius = radius,
|
||||
CalibrationValues = calibrationValues
|
||||
}, skipSetup)
|
||||
|
||||
private void OnOscParameterChangeEvent(string endpoint, object? oldValue, object? newValue)
|
||||
{
|
||||
|
||||
_lastMessageReceived = DateTime.Now;
|
||||
_lastMessageReceivedParameter = endpoint;
|
||||
switch (endpoint)
|
||||
{
|
||||
case "/avatar/change":
|
||||
this._avatarId = (string)(newValue ?? "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#region Setup
|
||||
private void SetupGPSVars()
|
||||
{
|
||||
this._gps1 = new(0, -this._config.Radius);
|
||||
this._gps2 = new(-(this._config.Radius * 0.866), this._config.Radius / 2);
|
||||
this._gps3 = new(this._config.Radius * 0.866, this._config.Radius / 2);
|
||||
this._constantA = 2 * _gps2.X - 2 * _gps1.X;
|
||||
this._constantB = 2 * _gps2.Y - 2 * _gps1.Y;
|
||||
this._constantC = Math.Pow(_gps1.X, 2) + Math.Pow(_gps2.X, 2) - Math.Pow(_gps1.Y, 2) + Math.Pow(_gps2.Y, 2);
|
||||
this._constantD = 2 * _gps3.X - 2 * _gps2.X;
|
||||
this._constantE = Math.Pow(_gps2.X, 2) + Math.Pow(_gps3.X, 2) - Math.Pow(_gps2.Y, 2) + Math.Pow(_gps3.Y, 2);
|
||||
this.refPos1 = new(0, -Config.Radius);
|
||||
this.refPos2 = new(-(Config.Radius * 0.866), Config.Radius / 2);
|
||||
this.refPos3 = new(Config.Radius * 0.866, Config.Radius / 2);
|
||||
this._constantA = 2 * refPos2.X - 2 * refPos1.X;
|
||||
this._constantB = 2 * refPos2.Y - 2 * refPos1.Y;
|
||||
this._constantC = Math.Pow(refPos1.X, 2) + Math.Pow(refPos2.X, 2) - Math.Pow(refPos1.Y, 2) + Math.Pow(refPos2.Y, 2);
|
||||
this._constantD = 2 * refPos3.X - 2 * refPos2.X;
|
||||
this._constantE = Math.Pow(refPos2.X, 2) + Math.Pow(refPos3.X, 2) - Math.Pow(refPos2.Y, 2) + Math.Pow(refPos3.Y, 2);
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void CalculatePositionFromGPS()
|
||||
{
|
||||
if (this._gps1.Distance == 0 || this._gps2.Distance == 0 || this._gps3.Distance == 0)
|
||||
if (this.refPos1.Distance == 0 || this.refPos2.Distance == 0 || this.refPos3.Distance == 0)
|
||||
return;
|
||||
double gpsFactorA = Math.Pow(_gps1.Distance, 2) - Math.Pow(_gps2.Distance, 2) - _constantC;
|
||||
double gpsFactorB = Math.Pow(_gps2.Distance, 2) - Math.Pow(_gps3.Distance, 2) - _constantE;
|
||||
double factorA = Math.Pow(refPos1.Distance, 2) - Math.Pow(refPos2.Distance, 2) - _constantC;
|
||||
double factorB = Math.Pow(refPos2.Distance, 2) - Math.Pow(refPos3.Distance, 2) - _constantE;
|
||||
|
||||
double gpsPosX = (gpsFactorB / _constantD);
|
||||
double gpsPosY = -1 * (gpsFactorA * _constantD - _constantA * gpsFactorB) / (_constantB * _constantD);
|
||||
double calculatedPosX = (factorB / _constantD);
|
||||
double calculatedPosY = -1 * (factorA * _constantD - _constantA * factorB) / (_constantB * _constantD);
|
||||
|
||||
if (this._leashStretch == 0)
|
||||
AddCalibrationValues(gpsPosX, gpsPosY);
|
||||
AddCalibrationValues(calculatedPosX, calculatedPosY);
|
||||
|
||||
ValueTuple<double, double> calibrationValues = GetCalibrationValues();
|
||||
|
||||
double posX = gpsPosX + calibrationValues.Item1;
|
||||
double posY = gpsPosY + calibrationValues.Item2;
|
||||
double calibratedPosX = calculatedPosX + calibrationValues.Item1;
|
||||
double calibratedPosY = calculatedPosY + calibrationValues.Item2;
|
||||
|
||||
double inverseLength = 1 / Math.Sqrt(Math.Pow(posX, 2) + Math.Pow(posY, 2));
|
||||
double inverseLength = 1 / Math.Sqrt(Math.Pow(calibratedPosX, 2) + Math.Pow(calibratedPosY, 2));
|
||||
|
||||
_unitVectorLeash.X = inverseLength is not Double.PositiveInfinity ? posX * inverseLength : 0;
|
||||
_unitVectorLeash.Y = inverseLength is not Double.PositiveInfinity ? posY * inverseLength : 0;
|
||||
_unitVectorLeash.X = inverseLength is not Double.PositiveInfinity ? calibratedPosX * inverseLength : 0;
|
||||
_unitVectorLeash.Y = inverseLength is not Double.PositiveInfinity ? calibratedPosY * inverseLength : 0;
|
||||
|
||||
if (_leashStretch < this._config.WalkStretchDeadzone) //Below Deadzone
|
||||
if (_leashStretch < Config.WalkStretchDeadzone) //Below Deadzone
|
||||
_movementVector = new();
|
||||
else if (_leashStretch < this._config.RunStretch) //Walk
|
||||
else if (_leashStretch < Config.RunStretch) //Walk
|
||||
_movementVector = _unitVectorLeash.MultipliedWith(0.5);
|
||||
else //Run
|
||||
_movementVector = _unitVectorLeash.MultipliedWith(1);
|
||||
@ -183,7 +167,7 @@ public partial class OSCCollar
|
||||
private void AddCalibrationValues(double x, double y)
|
||||
{
|
||||
this._calibrationAverage.Enqueue(new ValueTuple<double, double>(x, y));
|
||||
while (this._calibrationAverage.Count > this._config.CalibrationValues)
|
||||
while (this._calibrationAverage.Count > Config.CalibrationValues)
|
||||
this._calibrationAverage.Dequeue();
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>VRC_Console</RootNamespace>
|
||||
|
@ -1,12 +1,12 @@
|
||||
namespace VRC_Console;
|
||||
|
||||
public class GPS
|
||||
public class ReferencePosition
|
||||
{
|
||||
public double X { get; init; }
|
||||
public double Y { get; init; }
|
||||
public double Distance { get; set; }
|
||||
|
||||
public GPS(double x, double y)
|
||||
public ReferencePosition(double x, double y)
|
||||
{
|
||||
this.X = x;
|
||||
this.Y = y;
|
@ -19,7 +19,7 @@ public class Vector
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"({this.X:' '0.00000;'-'0.00000} {this.Y:' '0.00000;'-'0.00000})";
|
||||
return $"(Vector X={this.X:' '0.00000;'-'0.00000} Y={this.Y:' '0.00000;'-'0.00000})";
|
||||
}
|
||||
|
||||
public Vector MultipliedWith(double factor)
|
||||
|
Loading…
Reference in New Issue
Block a user