mirror of
https://github.com/TechplexEngineer/plexview.git
synced 2026-07-28 06:22:14 +00:00
Initial commit
I wish I could figure out how to have PlexVie be out of the LibOMV tree but its just not working die to a DLL not found exception about libopenjpeg. (Yes the dll is in the bin folder) This will do for now
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
/// <summary>
|
||||
/// Same as Queue except Dequeue function blocks until there is an object to return.
|
||||
/// Note: This class does not need to be synchronized
|
||||
/// </summary>
|
||||
public class BlockingQueue<T> : Queue<T>
|
||||
{
|
||||
private object SyncRoot;
|
||||
private bool open;
|
||||
|
||||
/// <summary>
|
||||
/// Create new BlockingQueue.
|
||||
/// </summary>
|
||||
/// <param name="col">The System.Collections.ICollection to copy elements from</param>
|
||||
public BlockingQueue(IEnumerable<T> col)
|
||||
: base(col)
|
||||
{
|
||||
SyncRoot = new object();
|
||||
open = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create new BlockingQueue.
|
||||
/// </summary>
|
||||
/// <param name="capacity">The initial number of elements that the queue can contain</param>
|
||||
public BlockingQueue(int capacity)
|
||||
: base(capacity)
|
||||
{
|
||||
SyncRoot = new object();
|
||||
open = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create new BlockingQueue.
|
||||
/// </summary>
|
||||
public BlockingQueue()
|
||||
: base()
|
||||
{
|
||||
SyncRoot = new object();
|
||||
open = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// BlockingQueue Destructor (Close queue, resume any waiting thread).
|
||||
/// </summary>
|
||||
~BlockingQueue()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove all objects from the Queue.
|
||||
/// </summary>
|
||||
public new void Clear()
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
base.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove all objects from the Queue, resume all dequeue threads.
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
open = false;
|
||||
base.Clear();
|
||||
Monitor.PulseAll(SyncRoot); // resume any waiting threads
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes and returns the object at the beginning of the Queue.
|
||||
/// </summary>
|
||||
/// <returns>Object in queue.</returns>
|
||||
public new T Dequeue()
|
||||
{
|
||||
return Dequeue(Timeout.Infinite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes and returns the object at the beginning of the Queue.
|
||||
/// </summary>
|
||||
/// <param name="timeout">time to wait before returning</param>
|
||||
/// <returns>Object in queue.</returns>
|
||||
public T Dequeue(TimeSpan timeout)
|
||||
{
|
||||
return Dequeue(timeout.Milliseconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes and returns the object at the beginning of the Queue.
|
||||
/// </summary>
|
||||
/// <param name="timeout">time to wait before returning (in milliseconds)</param>
|
||||
/// <returns>Object in queue.</returns>
|
||||
public T Dequeue(int timeout)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
while (open && (base.Count == 0))
|
||||
{
|
||||
if (!Monitor.Wait(SyncRoot, timeout))
|
||||
throw new InvalidOperationException("Timeout");
|
||||
}
|
||||
if (open)
|
||||
return base.Dequeue();
|
||||
else
|
||||
throw new InvalidOperationException("Queue Closed");
|
||||
}
|
||||
}
|
||||
|
||||
public bool Dequeue(int timeout, ref T obj)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
while (open && (base.Count == 0))
|
||||
{
|
||||
if (!Monitor.Wait(SyncRoot, timeout))
|
||||
return false;
|
||||
}
|
||||
if (open)
|
||||
{
|
||||
obj = base.Dequeue();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
obj = default(T);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an object to the end of the Queue
|
||||
/// </summary>
|
||||
/// <param name="obj">Object to put in queue</param>
|
||||
public new void Enqueue(T obj)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
base.Enqueue(obj);
|
||||
Monitor.Pulse(SyncRoot);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open Queue.
|
||||
/// </summary>
|
||||
public void Open()
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
open = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets flag indicating if queue has been closed.
|
||||
/// </summary>
|
||||
public bool Closed
|
||||
{
|
||||
get { return !open; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
public class CRC32
|
||||
{
|
||||
#region CRC table for polynomial 0xEDB88320
|
||||
|
||||
static readonly uint[] crcTable = new uint[] {
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
||||
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
||||
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
||||
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
||||
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
||||
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
|
||||
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
|
||||
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
||||
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
|
||||
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
|
||||
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
||||
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
|
||||
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
||||
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
||||
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
|
||||
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
|
||||
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
||||
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||||
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
|
||||
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
||||
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
|
||||
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||
};
|
||||
|
||||
#endregion CRC table for polynomial 0xEDB88320
|
||||
|
||||
public uint CRC;
|
||||
|
||||
public CRC32()
|
||||
{
|
||||
CRC = 0xffffffff;
|
||||
}
|
||||
|
||||
public void Update(byte value)
|
||||
{
|
||||
CRC = crcTable[(CRC ^ value) & 0xff] ^ (CRC >> 8);
|
||||
}
|
||||
|
||||
public void Update(byte[] value, int pos, int length)
|
||||
{
|
||||
for (int i = pos; i < length; i++)
|
||||
CRC = crcTable[(CRC ^ value[i]) & 0xff] ^ (CRC >> 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
public class CircularQueue<T>
|
||||
{
|
||||
public readonly T[] Items;
|
||||
|
||||
int first;
|
||||
int next;
|
||||
int capacity;
|
||||
object syncRoot;
|
||||
|
||||
public int First { get { return first; } }
|
||||
public int Next { get { return next; } }
|
||||
|
||||
public CircularQueue(int capacity)
|
||||
{
|
||||
this.capacity = capacity;
|
||||
Items = new T[capacity];
|
||||
syncRoot = new object();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy constructor
|
||||
/// </summary>
|
||||
/// <param name="queue">Circular queue to copy</param>
|
||||
public CircularQueue(CircularQueue<T> queue)
|
||||
{
|
||||
lock (queue.syncRoot)
|
||||
{
|
||||
capacity = queue.capacity;
|
||||
Items = new T[capacity];
|
||||
syncRoot = new object();
|
||||
|
||||
for (int i = 0; i < capacity; i++)
|
||||
Items[i] = queue.Items[i];
|
||||
|
||||
first = queue.first;
|
||||
next = queue.next;
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (syncRoot)
|
||||
{
|
||||
// Explicitly remove references to help garbage collection
|
||||
for (int i = 0; i < capacity; i++)
|
||||
Items[i] = default(T);
|
||||
|
||||
first = next;
|
||||
}
|
||||
}
|
||||
|
||||
public void Enqueue(T value)
|
||||
{
|
||||
lock (syncRoot)
|
||||
{
|
||||
Items[next] = value;
|
||||
next = (next + 1) % capacity;
|
||||
if (next == first) first = (first + 1) % capacity;
|
||||
}
|
||||
}
|
||||
|
||||
public T Dequeue()
|
||||
{
|
||||
lock (syncRoot)
|
||||
{
|
||||
T value = Items[first];
|
||||
Items[first] = default(T);
|
||||
|
||||
if (first != next)
|
||||
first = (first + 1) % capacity;
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public T DequeueLast()
|
||||
{
|
||||
lock (syncRoot)
|
||||
{
|
||||
// If the next element is right behind the first element (queue is full),
|
||||
// back up the first element by one
|
||||
int firstTest = first - 1;
|
||||
if (firstTest < 0) firstTest = capacity - 1;
|
||||
|
||||
if (firstTest == next)
|
||||
{
|
||||
--next;
|
||||
if (next < 0) next = capacity - 1;
|
||||
|
||||
--first;
|
||||
if (first < 0) first = capacity - 1;
|
||||
}
|
||||
else if (first != next)
|
||||
{
|
||||
--next;
|
||||
if (next < 0) next = capacity - 1;
|
||||
}
|
||||
|
||||
T value = Items[next];
|
||||
Items[next] = default(T);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,525 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
/// <summary>
|
||||
/// An 8-bit color structure including an alpha channel
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Color4 : IComparable<Color4>, IEquatable<Color4>
|
||||
{
|
||||
/// <summary>Red</summary>
|
||||
public float R;
|
||||
/// <summary>Green</summary>
|
||||
public float G;
|
||||
/// <summary>Blue</summary>
|
||||
public float B;
|
||||
/// <summary>Alpha</summary>
|
||||
public float A;
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="r"></param>
|
||||
/// <param name="g"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <param name="a"></param>
|
||||
public Color4(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
const float quanta = 1.0f / 255.0f;
|
||||
|
||||
R = (float)r * quanta;
|
||||
G = (float)g * quanta;
|
||||
B = (float)b * quanta;
|
||||
A = (float)a * quanta;
|
||||
}
|
||||
|
||||
public Color4(float r, float g, float b, float a)
|
||||
{
|
||||
// Quick check to see if someone is doing something obviously wrong
|
||||
// like using float values from 0.0 - 255.0
|
||||
if (r > 1f || g > 1f || b > 1f || a > 1f)
|
||||
throw new ArgumentException(
|
||||
String.Format("Attempting to initialize Color4 with out of range values <{0},{1},{2},{3}>",
|
||||
r, g, b, a));
|
||||
|
||||
// Valid range is from 0.0 to 1.0
|
||||
R = Utils.Clamp(r, 0f, 1f);
|
||||
G = Utils.Clamp(g, 0f, 1f);
|
||||
B = Utils.Clamp(b, 0f, 1f);
|
||||
A = Utils.Clamp(a, 0f, 1f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a color from a byte array
|
||||
/// </summary>
|
||||
/// <param name="byteArray">Byte array containing a 16 byte color</param>
|
||||
/// <param name="pos">Beginning position in the byte array</param>
|
||||
/// <param name="inverted">True if the byte array stores inverted values,
|
||||
/// otherwise false. For example the color black (fully opaque) inverted
|
||||
/// would be 0xFF 0xFF 0xFF 0x00</param>
|
||||
public Color4(byte[] byteArray, int pos, bool inverted)
|
||||
{
|
||||
R = G = B = A = 0f;
|
||||
FromBytes(byteArray, pos, inverted);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the raw bytes for this vector
|
||||
/// </summary>
|
||||
/// <param name="byteArray">Byte array containing a 16 byte color</param>
|
||||
/// <param name="pos">Beginning position in the byte array</param>
|
||||
/// <param name="inverted">True if the byte array stores inverted values,
|
||||
/// otherwise false. For example the color black (fully opaque) inverted
|
||||
/// would be 0xFF 0xFF 0xFF 0x00</param>
|
||||
/// <param name="alphaInverted">True if the alpha value is inverted in
|
||||
/// addition to whatever the inverted parameter is. Setting inverted true
|
||||
/// and alphaInverted true will flip the alpha value back to non-inverted,
|
||||
/// but keep the other color bytes inverted</param>
|
||||
/// <returns>A 16 byte array containing R, G, B, and A</returns>
|
||||
public Color4(byte[] byteArray, int pos, bool inverted, bool alphaInverted)
|
||||
{
|
||||
R = G = B = A = 0f;
|
||||
FromBytes(byteArray, pos, inverted, alphaInverted);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy constructor
|
||||
/// </summary>
|
||||
/// <param name="color">Color to copy</param>
|
||||
public Color4(Color4 color)
|
||||
{
|
||||
R = color.R;
|
||||
G = color.G;
|
||||
B = color.B;
|
||||
A = color.A;
|
||||
}
|
||||
|
||||
#endregion Constructors
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// IComparable.CompareTo implementation
|
||||
/// </summary>
|
||||
/// <remarks>Sorting ends up like this: |--Grayscale--||--Color--|.
|
||||
/// Alpha is only used when the colors are otherwise equivalent</remarks>
|
||||
public int CompareTo(Color4 color)
|
||||
{
|
||||
float thisHue = GetHue();
|
||||
float thatHue = color.GetHue();
|
||||
|
||||
if (thisHue < 0f && thatHue < 0f)
|
||||
{
|
||||
// Both monochromatic
|
||||
if (R == color.R)
|
||||
{
|
||||
// Monochromatic and equal, compare alpha
|
||||
return A.CompareTo(color.A);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compare lightness
|
||||
return R.CompareTo(R);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (thisHue == thatHue)
|
||||
{
|
||||
// RGB is equal, compare alpha
|
||||
return A.CompareTo(color.A);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compare hues
|
||||
return thisHue.CompareTo(thatHue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void FromBytes(byte[] byteArray, int pos, bool inverted)
|
||||
{
|
||||
const float quanta = 1.0f / 255.0f;
|
||||
|
||||
if (inverted)
|
||||
{
|
||||
R = (float)(255 - byteArray[pos]) * quanta;
|
||||
G = (float)(255 - byteArray[pos + 1]) * quanta;
|
||||
B = (float)(255 - byteArray[pos + 2]) * quanta;
|
||||
A = (float)(255 - byteArray[pos + 3]) * quanta;
|
||||
}
|
||||
else
|
||||
{
|
||||
R = (float)byteArray[pos] * quanta;
|
||||
G = (float)byteArray[pos + 1] * quanta;
|
||||
B = (float)byteArray[pos + 2] * quanta;
|
||||
A = (float)byteArray[pos + 3] * quanta;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a color from a byte array
|
||||
/// </summary>
|
||||
/// <param name="byteArray">Byte array containing a 16 byte color</param>
|
||||
/// <param name="pos">Beginning position in the byte array</param>
|
||||
/// <param name="inverted">True if the byte array stores inverted values,
|
||||
/// otherwise false. For example the color black (fully opaque) inverted
|
||||
/// would be 0xFF 0xFF 0xFF 0x00</param>
|
||||
/// <param name="alphaInverted">True if the alpha value is inverted in
|
||||
/// addition to whatever the inverted parameter is. Setting inverted true
|
||||
/// and alphaInverted true will flip the alpha value back to non-inverted,
|
||||
/// but keep the other color bytes inverted</param>
|
||||
public void FromBytes(byte[] byteArray, int pos, bool inverted, bool alphaInverted)
|
||||
{
|
||||
FromBytes(byteArray, pos, inverted);
|
||||
|
||||
if (alphaInverted)
|
||||
A = 1.0f - A;
|
||||
}
|
||||
|
||||
public byte[] GetBytes()
|
||||
{
|
||||
return GetBytes(false);
|
||||
}
|
||||
|
||||
public byte[] GetBytes(bool inverted)
|
||||
{
|
||||
byte[] byteArray = new byte[4];
|
||||
ToBytes(byteArray, 0, inverted);
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
public byte[] GetFloatBytes()
|
||||
{
|
||||
byte[] bytes = new byte[16];
|
||||
ToFloatBytes(bytes, 0);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the raw bytes for this color to a byte array
|
||||
/// </summary>
|
||||
/// <param name="dest">Destination byte array</param>
|
||||
/// <param name="pos">Position in the destination array to start
|
||||
/// writing. Must be at least 16 bytes before the end of the array</param>
|
||||
public void ToBytes(byte[] dest, int pos)
|
||||
{
|
||||
ToBytes(dest, pos, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes this color into four bytes in a byte array
|
||||
/// </summary>
|
||||
/// <param name="dest">Destination byte array</param>
|
||||
/// <param name="pos">Position in the destination array to start
|
||||
/// writing. Must be at least 4 bytes before the end of the array</param>
|
||||
/// <param name="inverted">True to invert the output (1.0 becomes 0
|
||||
/// instead of 255)</param>
|
||||
public void ToBytes(byte[] dest, int pos, bool inverted)
|
||||
{
|
||||
dest[pos + 0] = Utils.FloatToByte(R, 0f, 1f);
|
||||
dest[pos + 1] = Utils.FloatToByte(G, 0f, 1f);
|
||||
dest[pos + 2] = Utils.FloatToByte(B, 0f, 1f);
|
||||
dest[pos + 3] = Utils.FloatToByte(A, 0f, 1f);
|
||||
|
||||
if (inverted)
|
||||
{
|
||||
dest[pos + 0] = (byte)(255 - dest[pos + 0]);
|
||||
dest[pos + 1] = (byte)(255 - dest[pos + 1]);
|
||||
dest[pos + 2] = (byte)(255 - dest[pos + 2]);
|
||||
dest[pos + 3] = (byte)(255 - dest[pos + 3]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the raw bytes for this color to a byte array
|
||||
/// </summary>
|
||||
/// <param name="dest">Destination byte array</param>
|
||||
/// <param name="pos">Position in the destination array to start
|
||||
/// writing. Must be at least 16 bytes before the end of the array</param>
|
||||
public void ToFloatBytes(byte[] dest, int pos)
|
||||
{
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(R), 0, dest, pos + 0, 4);
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(G), 0, dest, pos + 4, 4);
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(B), 0, dest, pos + 8, 4);
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(A), 0, dest, pos + 12, 4);
|
||||
}
|
||||
|
||||
public float GetHue()
|
||||
{
|
||||
const float HUE_MAX = 360f;
|
||||
|
||||
float max = Math.Max(Math.Max(R, G), B);
|
||||
float min = Math.Min(Math.Min(R, B), B);
|
||||
|
||||
if (max == min)
|
||||
{
|
||||
// Achromatic, hue is undefined
|
||||
return -1f;
|
||||
}
|
||||
else if (R == max)
|
||||
{
|
||||
float bDelta = (((max - B) * (HUE_MAX / 6f)) + ((max - min) / 2f)) / (max - min);
|
||||
float gDelta = (((max - G) * (HUE_MAX / 6f)) + ((max - min) / 2f)) / (max - min);
|
||||
return bDelta - gDelta;
|
||||
}
|
||||
else if (G == max)
|
||||
{
|
||||
float rDelta = (((max - R) * (HUE_MAX / 6f)) + ((max - min) / 2f)) / (max - min);
|
||||
float bDelta = (((max - B) * (HUE_MAX / 6f)) + ((max - min) / 2f)) / (max - min);
|
||||
return (HUE_MAX / 3f) + rDelta - bDelta;
|
||||
}
|
||||
else // B == max
|
||||
{
|
||||
float gDelta = (((max - G) * (HUE_MAX / 6f)) + ((max - min) / 2f)) / (max - min);
|
||||
float rDelta = (((max - R) * (HUE_MAX / 6f)) + ((max - min) / 2f)) / (max - min);
|
||||
return ((2f * HUE_MAX) / 3f) + gDelta - rDelta;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that values are in range 0-1
|
||||
/// </summary>
|
||||
public void ClampValues()
|
||||
{
|
||||
if (R < 0f)
|
||||
R = 0f;
|
||||
if (G < 0f)
|
||||
G = 0f;
|
||||
if (B < 0f)
|
||||
B = 0f;
|
||||
if (A < 0f)
|
||||
A = 0f;
|
||||
if (R > 1f)
|
||||
R = 1f;
|
||||
if (G > 1f)
|
||||
G = 1f;
|
||||
if (B > 1f)
|
||||
B = 1f;
|
||||
if (A > 1f)
|
||||
A = 1f;
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Static Methods
|
||||
|
||||
/// <summary>
|
||||
/// Create an RGB color from a hue, saturation, value combination
|
||||
/// </summary>
|
||||
/// <param name="hue">Hue</param>
|
||||
/// <param name="saturation">Saturation</param>
|
||||
/// <param name="value">Value</param>
|
||||
/// <returns>An fully opaque RGB color (alpha is 1.0)</returns>
|
||||
public static Color4 FromHSV(double hue, double saturation, double value)
|
||||
{
|
||||
double r = 0d;
|
||||
double g = 0d;
|
||||
double b = 0d;
|
||||
|
||||
if (saturation == 0d)
|
||||
{
|
||||
// If s is 0, all colors are the same.
|
||||
// This is some flavor of gray.
|
||||
r = value;
|
||||
g = value;
|
||||
b = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
double p;
|
||||
double q;
|
||||
double t;
|
||||
|
||||
double fractionalSector;
|
||||
int sectorNumber;
|
||||
double sectorPos;
|
||||
|
||||
// The color wheel consists of 6 sectors.
|
||||
// Figure out which sector you//re in.
|
||||
sectorPos = hue / 60d;
|
||||
sectorNumber = (int)(Math.Floor(sectorPos));
|
||||
|
||||
// get the fractional part of the sector.
|
||||
// That is, how many degrees into the sector
|
||||
// are you?
|
||||
fractionalSector = sectorPos - sectorNumber;
|
||||
|
||||
// Calculate values for the three axes
|
||||
// of the color.
|
||||
p = value * (1d - saturation);
|
||||
q = value * (1d - (saturation * fractionalSector));
|
||||
t = value * (1d - (saturation * (1d - fractionalSector)));
|
||||
|
||||
// Assign the fractional colors to r, g, and b
|
||||
// based on the sector the angle is in.
|
||||
switch (sectorNumber)
|
||||
{
|
||||
case 0:
|
||||
r = value;
|
||||
g = t;
|
||||
b = p;
|
||||
break;
|
||||
case 1:
|
||||
r = q;
|
||||
g = value;
|
||||
b = p;
|
||||
break;
|
||||
case 2:
|
||||
r = p;
|
||||
g = value;
|
||||
b = t;
|
||||
break;
|
||||
case 3:
|
||||
r = p;
|
||||
g = q;
|
||||
b = value;
|
||||
break;
|
||||
case 4:
|
||||
r = t;
|
||||
g = p;
|
||||
b = value;
|
||||
break;
|
||||
case 5:
|
||||
r = value;
|
||||
g = p;
|
||||
b = q;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Color4((float)r, (float)g, (float)b, 1f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs linear interpolation between two colors
|
||||
/// </summary>
|
||||
/// <param name="value1">Color to start at</param>
|
||||
/// <param name="value2">Color to end at</param>
|
||||
/// <param name="amount">Amount to interpolate</param>
|
||||
/// <returns>The interpolated color</returns>
|
||||
public static Color4 Lerp(Color4 value1, Color4 value2, float amount)
|
||||
{
|
||||
return new Color4(
|
||||
Utils.Lerp(value1.R, value2.R, amount),
|
||||
Utils.Lerp(value1.G, value2.G, amount),
|
||||
Utils.Lerp(value1.B, value2.B, amount),
|
||||
Utils.Lerp(value1.A, value2.A, amount));
|
||||
}
|
||||
|
||||
#endregion Static Methods
|
||||
|
||||
#region Overrides
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format(Utils.EnUsCulture, "<{0}, {1}, {2}, {3}>", R, G, B, A);
|
||||
}
|
||||
|
||||
public string ToRGBString()
|
||||
{
|
||||
return String.Format(Utils.EnUsCulture, "<{0}, {1}, {2}>", R, G, B);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return (obj is Color4) ? this == (Color4)obj : false;
|
||||
}
|
||||
|
||||
public bool Equals(Color4 other)
|
||||
{
|
||||
return this == other;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return R.GetHashCode() ^ G.GetHashCode() ^ B.GetHashCode() ^ A.GetHashCode();
|
||||
}
|
||||
|
||||
#endregion Overrides
|
||||
|
||||
#region Operators
|
||||
|
||||
public static bool operator ==(Color4 lhs, Color4 rhs)
|
||||
{
|
||||
return (lhs.R == rhs.R) && (lhs.G == rhs.G) && (lhs.B == rhs.B) && (lhs.A == rhs.A);
|
||||
}
|
||||
|
||||
public static bool operator !=(Color4 lhs, Color4 rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
public static Color4 operator +(Color4 lhs, Color4 rhs)
|
||||
{
|
||||
lhs.R += rhs.R;
|
||||
lhs.G += rhs.G;
|
||||
lhs.B += rhs.B;
|
||||
lhs.A += rhs.A;
|
||||
lhs.ClampValues();
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
public static Color4 operator -(Color4 lhs, Color4 rhs)
|
||||
{
|
||||
lhs.R -= rhs.R;
|
||||
lhs.G -= rhs.G;
|
||||
lhs.B -= rhs.B;
|
||||
lhs.A -= rhs.A;
|
||||
lhs.ClampValues();
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
public static Color4 operator *(Color4 lhs, Color4 rhs)
|
||||
{
|
||||
lhs.R *= rhs.R;
|
||||
lhs.G *= rhs.G;
|
||||
lhs.B *= rhs.B;
|
||||
lhs.A *= rhs.A;
|
||||
lhs.ClampValues();
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
#endregion Operators
|
||||
|
||||
/// <summary>A Color4 with zero RGB values and fully opaque (alpha 1.0)</summary>
|
||||
public readonly static Color4 Black = new Color4(0f, 0f, 0f, 1f);
|
||||
|
||||
/// <summary>A Color4 with full RGB values (1.0) and fully opaque (alpha 1.0)</summary>
|
||||
public readonly static Color4 White = new Color4(1f, 1f, 1f, 1f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if VISUAL_STUDIO
|
||||
using ReaderWriterLockImpl = System.Threading.ReaderWriterLockSlim;
|
||||
#else
|
||||
using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim;
|
||||
#endif
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
public class DoubleDictionary<TKey1, TKey2, TValue>
|
||||
{
|
||||
Dictionary<TKey1, TValue> Dictionary1;
|
||||
Dictionary<TKey2, TValue> Dictionary2;
|
||||
ReaderWriterLockImpl rwLock = new ReaderWriterLockImpl();
|
||||
|
||||
public DoubleDictionary()
|
||||
{
|
||||
Dictionary1 = new Dictionary<TKey1,TValue>();
|
||||
Dictionary2 = new Dictionary<TKey2,TValue>();
|
||||
}
|
||||
|
||||
public DoubleDictionary(int capacity)
|
||||
{
|
||||
Dictionary1 = new Dictionary<TKey1, TValue>(capacity);
|
||||
Dictionary2 = new Dictionary<TKey2, TValue>(capacity);
|
||||
}
|
||||
|
||||
public void Add(TKey1 key1, TKey2 key2, TValue value)
|
||||
{
|
||||
rwLock.EnterWriteLock();
|
||||
|
||||
try
|
||||
{
|
||||
if (Dictionary1.ContainsKey(key1))
|
||||
{
|
||||
if (!Dictionary2.ContainsKey(key2))
|
||||
throw new ArgumentException("key1 exists in the dictionary but not key2");
|
||||
}
|
||||
else if (Dictionary2.ContainsKey(key2))
|
||||
{
|
||||
if (!Dictionary1.ContainsKey(key1))
|
||||
throw new ArgumentException("key2 exists in the dictionary but not key1");
|
||||
}
|
||||
|
||||
Dictionary1[key1] = value;
|
||||
Dictionary2[key2] = value;
|
||||
}
|
||||
finally { rwLock.ExitWriteLock(); }
|
||||
}
|
||||
|
||||
public bool Remove(TKey1 key1, TKey2 key2)
|
||||
{
|
||||
bool success;
|
||||
rwLock.EnterWriteLock();
|
||||
|
||||
try
|
||||
{
|
||||
Dictionary1.Remove(key1);
|
||||
success = Dictionary2.Remove(key2);
|
||||
}
|
||||
finally { rwLock.ExitWriteLock(); }
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public bool Remove(TKey1 key1)
|
||||
{
|
||||
bool found = false;
|
||||
rwLock.EnterWriteLock();
|
||||
|
||||
try
|
||||
{
|
||||
// This is an O(n) operation!
|
||||
TValue value;
|
||||
if (Dictionary1.TryGetValue(key1, out value))
|
||||
{
|
||||
foreach (KeyValuePair<TKey2, TValue> kvp in Dictionary2)
|
||||
{
|
||||
if (kvp.Value.Equals(value))
|
||||
{
|
||||
Dictionary1.Remove(key1);
|
||||
Dictionary2.Remove(kvp.Key);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally { rwLock.ExitWriteLock(); }
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
public bool Remove(TKey2 key2)
|
||||
{
|
||||
bool found = false;
|
||||
rwLock.EnterWriteLock();
|
||||
|
||||
try
|
||||
{
|
||||
// This is an O(n) operation!
|
||||
TValue value;
|
||||
if (Dictionary2.TryGetValue(key2, out value))
|
||||
{
|
||||
foreach (KeyValuePair<TKey1, TValue> kvp in Dictionary1)
|
||||
{
|
||||
if (kvp.Value.Equals(value))
|
||||
{
|
||||
Dictionary2.Remove(key2);
|
||||
Dictionary1.Remove(kvp.Key);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally { rwLock.ExitWriteLock(); }
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
rwLock.EnterWriteLock();
|
||||
|
||||
try
|
||||
{
|
||||
Dictionary1.Clear();
|
||||
Dictionary2.Clear();
|
||||
}
|
||||
finally { rwLock.ExitWriteLock(); }
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return Dictionary1.Count; }
|
||||
}
|
||||
|
||||
public bool ContainsKey(TKey1 key)
|
||||
{
|
||||
return Dictionary1.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool ContainsKey(TKey2 key)
|
||||
{
|
||||
return Dictionary2.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey1 key, out TValue value)
|
||||
{
|
||||
bool success;
|
||||
rwLock.EnterReadLock();
|
||||
|
||||
try { success = Dictionary1.TryGetValue(key, out value); }
|
||||
finally { rwLock.ExitReadLock(); }
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey2 key, out TValue value)
|
||||
{
|
||||
bool success;
|
||||
rwLock.EnterReadLock();
|
||||
|
||||
try { success = Dictionary2.TryGetValue(key, out value); }
|
||||
finally { rwLock.ExitReadLock(); }
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void ForEach(Action<TValue> action)
|
||||
{
|
||||
rwLock.EnterReadLock();
|
||||
|
||||
try
|
||||
{
|
||||
foreach (TValue value in Dictionary1.Values)
|
||||
action(value);
|
||||
}
|
||||
finally { rwLock.ExitReadLock(); }
|
||||
}
|
||||
|
||||
public void ForEach(Action<KeyValuePair<TKey1, TValue>> action)
|
||||
{
|
||||
rwLock.EnterReadLock();
|
||||
|
||||
try
|
||||
{
|
||||
foreach (KeyValuePair<TKey1, TValue> entry in Dictionary1)
|
||||
action(entry);
|
||||
}
|
||||
finally { rwLock.ExitReadLock(); }
|
||||
}
|
||||
|
||||
public void ForEach(Action<KeyValuePair<TKey2, TValue>> action)
|
||||
{
|
||||
rwLock.EnterReadLock();
|
||||
|
||||
try
|
||||
{
|
||||
foreach (KeyValuePair<TKey2, TValue> entry in Dictionary2)
|
||||
action(entry);
|
||||
}
|
||||
finally { rwLock.ExitReadLock(); }
|
||||
}
|
||||
|
||||
public TValue FindValue(Predicate<TValue> predicate)
|
||||
{
|
||||
rwLock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
foreach (TValue value in Dictionary1.Values)
|
||||
{
|
||||
if (predicate(value))
|
||||
return value;
|
||||
}
|
||||
}
|
||||
finally { rwLock.ExitReadLock(); }
|
||||
|
||||
return default(TValue);
|
||||
}
|
||||
|
||||
public IList<TValue> FindAll(Predicate<TValue> predicate)
|
||||
{
|
||||
IList<TValue> list = new List<TValue>();
|
||||
rwLock.EnterReadLock();
|
||||
|
||||
try
|
||||
{
|
||||
foreach (TValue value in Dictionary1.Values)
|
||||
{
|
||||
if (predicate(value))
|
||||
list.Add(value);
|
||||
}
|
||||
}
|
||||
finally { rwLock.ExitReadLock(); }
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public int RemoveAll(Predicate<TValue> predicate)
|
||||
{
|
||||
IList<TKey1> list = new List<TKey1>();
|
||||
|
||||
rwLock.EnterUpgradeableReadLock();
|
||||
|
||||
try
|
||||
{
|
||||
foreach (KeyValuePair<TKey1, TValue> kvp in Dictionary1)
|
||||
{
|
||||
if (predicate(kvp.Value))
|
||||
list.Add(kvp.Key);
|
||||
}
|
||||
|
||||
IList<TKey2> list2 = new List<TKey2>(list.Count);
|
||||
foreach (KeyValuePair<TKey2, TValue> kvp in Dictionary2)
|
||||
{
|
||||
if (predicate(kvp.Value))
|
||||
list2.Add(kvp.Key);
|
||||
}
|
||||
|
||||
rwLock.EnterWriteLock();
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
Dictionary1.Remove(list[i]);
|
||||
|
||||
for (int i = 0; i < list2.Count; i++)
|
||||
Dictionary2.Remove(list2[i]);
|
||||
}
|
||||
finally { rwLock.ExitWriteLock(); }
|
||||
}
|
||||
finally { rwLock.ExitUpgradeableReadLock(); }
|
||||
|
||||
return list.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
/// <summary>
|
||||
/// Attribute class that allows extra attributes to be attached to ENUMs
|
||||
/// </summary>
|
||||
public class EnumInfoAttribute : Attribute
|
||||
{
|
||||
/// <summary>Text used when presenting ENUM to user</summary>
|
||||
public string Text = string.Empty;
|
||||
|
||||
/// <summary>Default initializer</summary>
|
||||
public EnumInfoAttribute() { }
|
||||
|
||||
/// <summary>Text used when presenting ENUM to user</summary>
|
||||
public EnumInfoAttribute(string text)
|
||||
{
|
||||
this.Text = text;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The different types of grid assets
|
||||
/// </summary>
|
||||
public enum AssetType : sbyte
|
||||
{
|
||||
/// <summary>Unknown asset type</summary>
|
||||
Unknown = -1,
|
||||
/// <summary>Texture asset, stores in JPEG2000 J2C stream format</summary>
|
||||
Texture = 0,
|
||||
/// <summary>Sound asset</summary>
|
||||
Sound = 1,
|
||||
/// <summary>Calling card for another avatar</summary>
|
||||
CallingCard = 2,
|
||||
/// <summary>Link to a location in world</summary>
|
||||
Landmark = 3,
|
||||
// <summary>Legacy script asset, you should never see one of these</summary>
|
||||
//[Obsolete]
|
||||
//Script = 4,
|
||||
/// <summary>Collection of textures and parameters that can be
|
||||
/// worn by an avatar</summary>
|
||||
Clothing = 5,
|
||||
/// <summary>Primitive that can contain textures, sounds,
|
||||
/// scripts and more</summary>
|
||||
Object = 6,
|
||||
/// <summary>Notecard asset</summary>
|
||||
Notecard = 7,
|
||||
/// <summary>Holds a collection of inventory items</summary>
|
||||
Folder = 8,
|
||||
/// <summary>Root inventory folder</summary>
|
||||
RootFolder = 9,
|
||||
/// <summary>Linden scripting language script</summary>
|
||||
LSLText = 10,
|
||||
/// <summary>LSO bytecode for a script</summary>
|
||||
LSLBytecode = 11,
|
||||
/// <summary>Uncompressed TGA texture</summary>
|
||||
TextureTGA = 12,
|
||||
/// <summary>Collection of textures and shape parameters that can
|
||||
/// be worn</summary>
|
||||
Bodypart = 13,
|
||||
/// <summary>Trash folder</summary>
|
||||
TrashFolder = 14,
|
||||
/// <summary>Snapshot folder</summary>
|
||||
SnapshotFolder = 15,
|
||||
/// <summary>Lost and found folder</summary>
|
||||
LostAndFoundFolder = 16,
|
||||
/// <summary>Uncompressed sound</summary>
|
||||
SoundWAV = 17,
|
||||
/// <summary>Uncompressed TGA non-square image, not to be used as a
|
||||
/// texture</summary>
|
||||
ImageTGA = 18,
|
||||
/// <summary>Compressed JPEG non-square image, not to be used as a
|
||||
/// texture</summary>
|
||||
ImageJPEG = 19,
|
||||
/// <summary>Animation</summary>
|
||||
Animation = 20,
|
||||
/// <summary>Sequence of animations, sounds, chat, and pauses</summary>
|
||||
Gesture = 21,
|
||||
/// <summary>Simstate file</summary>
|
||||
Simstate = 22,
|
||||
/// <summary>Contains landmarks for favorites</summary>
|
||||
FavoriteFolder = 23,
|
||||
/// <summary>Asset is a link to another inventory item</summary>
|
||||
Link = 24,
|
||||
/// <summary>Asset is a link to another inventory folder</summary>
|
||||
LinkFolder = 25,
|
||||
/// <summary>Beginning of the range reserved for ensembles</summary>
|
||||
EnsembleStart = 26,
|
||||
/// <summary>End of the range reserved for ensembles</summary>
|
||||
EnsembleEnd = 45,
|
||||
/// <summary>Folder containing inventory links to wearables and attachments
|
||||
/// that are part of the current outfit</summary>
|
||||
CurrentOutfitFolder = 46,
|
||||
/// <summary>Folder containing inventory items or links to
|
||||
/// inventory items of wearables and attachments
|
||||
/// together make a full outfit</summary>
|
||||
OutfitFolder = 47,
|
||||
/// <summary>Root folder for the folders of type OutfitFolder</summary>
|
||||
MyOutfitsFolder = 48,
|
||||
/// <summary>Linden mesh format</summary>
|
||||
Mesh = 49,
|
||||
/// <summary>Marketplace direct delivery inbox ("Received Items")</summary>
|
||||
Inbox = 50,
|
||||
/// <summary>Marketplace direct delivery outbox</summary>
|
||||
Outbox = 51,
|
||||
/// <summary></summary>
|
||||
BasicRoot = 51,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inventory Item Types, eg Script, Notecard, Folder, etc
|
||||
/// </summary>
|
||||
public enum InventoryType : sbyte
|
||||
{
|
||||
/// <summary>Unknown</summary>
|
||||
Unknown = -1,
|
||||
/// <summary>Texture</summary>
|
||||
Texture = 0,
|
||||
/// <summary>Sound</summary>
|
||||
Sound = 1,
|
||||
/// <summary>Calling Card</summary>
|
||||
CallingCard = 2,
|
||||
/// <summary>Landmark</summary>
|
||||
Landmark = 3,
|
||||
/*
|
||||
/// <summary>Script</summary>
|
||||
//[Obsolete("See LSL")] Script = 4,
|
||||
/// <summary>Clothing</summary>
|
||||
//[Obsolete("See Wearable")] Clothing = 5,
|
||||
/// <summary>Object, both single and coalesced</summary>
|
||||
*/
|
||||
Object = 6,
|
||||
/// <summary>Notecard</summary>
|
||||
Notecard = 7,
|
||||
/// <summary></summary>
|
||||
Category = 8,
|
||||
/// <summary>Folder</summary>
|
||||
Folder = 8,
|
||||
/// <summary></summary>
|
||||
RootCategory = 9,
|
||||
/// <summary>an LSL Script</summary>
|
||||
LSL = 10,
|
||||
/*
|
||||
/// <summary></summary>
|
||||
//[Obsolete("See LSL")] LSLBytecode = 11,
|
||||
/// <summary></summary>
|
||||
//[Obsolete("See Texture")] TextureTGA = 12,
|
||||
/// <summary></summary>
|
||||
//[Obsolete] Bodypart = 13,
|
||||
/// <summary></summary>
|
||||
//[Obsolete] Trash = 14,
|
||||
*/
|
||||
/// <summary></summary>
|
||||
Snapshot = 15,
|
||||
/*
|
||||
/// <summary></summary>
|
||||
//[Obsolete] LostAndFound = 16,
|
||||
*/
|
||||
/// <summary></summary>
|
||||
Attachment = 17,
|
||||
/// <summary></summary>
|
||||
Wearable = 18,
|
||||
/// <summary></summary>
|
||||
Animation = 19,
|
||||
/// <summary></summary>
|
||||
Gesture = 20,
|
||||
|
||||
/// <summary></summary>
|
||||
Mesh = 22,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Item Sale Status
|
||||
/// </summary>
|
||||
public enum SaleType : byte
|
||||
{
|
||||
/// <summary>Not for sale</summary>
|
||||
Not = 0,
|
||||
/// <summary>The original is for sale</summary>
|
||||
Original = 1,
|
||||
/// <summary>Copies are for sale</summary>
|
||||
Copy = 2,
|
||||
/// <summary>The contents of the object are for sale</summary>
|
||||
Contents = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Types of wearable assets
|
||||
/// </summary>
|
||||
public enum WearableType : byte
|
||||
{
|
||||
/// <summary>Body shape</summary>
|
||||
Shape = 0,
|
||||
/// <summary>Skin textures and attributes</summary>
|
||||
Skin,
|
||||
/// <summary>Hair</summary>
|
||||
Hair,
|
||||
/// <summary>Eyes</summary>
|
||||
Eyes,
|
||||
/// <summary>Shirt</summary>
|
||||
Shirt,
|
||||
/// <summary>Pants</summary>
|
||||
Pants,
|
||||
/// <summary>Shoes</summary>
|
||||
Shoes,
|
||||
/// <summary>Socks</summary>
|
||||
Socks,
|
||||
/// <summary>Jacket</summary>
|
||||
Jacket,
|
||||
/// <summary>Gloves</summary>
|
||||
Gloves,
|
||||
/// <summary>Undershirt</summary>
|
||||
Undershirt,
|
||||
/// <summary>Underpants</summary>
|
||||
Underpants,
|
||||
/// <summary>Skirt</summary>
|
||||
Skirt,
|
||||
/// <summary>Alpha mask to hide parts of the avatar</summary>
|
||||
Alpha,
|
||||
/// <summary>Tattoo</summary>
|
||||
Tattoo,
|
||||
/// <summary>Physics</summary>
|
||||
Physics,
|
||||
/// <summary>Invalid wearable asset</summary>
|
||||
Invalid = 255
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,560 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifier code for primitive types
|
||||
/// </summary>
|
||||
public enum PCode : byte
|
||||
{
|
||||
/// <summary>None</summary>
|
||||
None = 0,
|
||||
/// <summary>A Primitive</summary>
|
||||
Prim = 9,
|
||||
/// <summary>A Avatar</summary>
|
||||
Avatar = 47,
|
||||
/// <summary>Linden grass</summary>
|
||||
Grass = 95,
|
||||
/// <summary>Linden tree</summary>
|
||||
NewTree = 111,
|
||||
/// <summary>A primitive that acts as the source for a particle stream</summary>
|
||||
ParticleSystem = 143,
|
||||
/// <summary>A Linden tree</summary>
|
||||
Tree = 255
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Primary parameters for primitives such as Physics Enabled or Phantom
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum PrimFlags : uint
|
||||
{
|
||||
/// <summary>Deprecated</summary>
|
||||
None = 0,
|
||||
/// <summary>Whether physics are enabled for this object</summary>
|
||||
Physics = 0x00000001,
|
||||
/// <summary></summary>
|
||||
CreateSelected = 0x00000002,
|
||||
/// <summary></summary>
|
||||
ObjectModify = 0x00000004,
|
||||
/// <summary></summary>
|
||||
ObjectCopy = 0x00000008,
|
||||
/// <summary></summary>
|
||||
ObjectAnyOwner = 0x00000010,
|
||||
/// <summary></summary>
|
||||
ObjectYouOwner = 0x00000020,
|
||||
/// <summary></summary>
|
||||
Scripted = 0x00000040,
|
||||
/// <summary>Whether this object contains an active touch script</summary>
|
||||
Touch = 0x00000080,
|
||||
/// <summary></summary>
|
||||
ObjectMove = 0x00000100,
|
||||
/// <summary>Whether this object can receive payments</summary>
|
||||
Money = 0x00000200,
|
||||
/// <summary>Whether this object is phantom (no collisions)</summary>
|
||||
Phantom = 0x00000400,
|
||||
/// <summary></summary>
|
||||
InventoryEmpty = 0x00000800,
|
||||
/// <summary></summary>
|
||||
JointHinge = 0x00001000,
|
||||
/// <summary></summary>
|
||||
JointP2P = 0x00002000,
|
||||
/// <summary></summary>
|
||||
JointLP2P = 0x00004000,
|
||||
/// <summary>Deprecated</summary>
|
||||
JointWheel = 0x00008000,
|
||||
/// <summary></summary>
|
||||
AllowInventoryDrop = 0x00010000,
|
||||
/// <summary></summary>
|
||||
ObjectTransfer = 0x00020000,
|
||||
/// <summary></summary>
|
||||
ObjectGroupOwned = 0x00040000,
|
||||
/// <summary>Deprecated</summary>
|
||||
ObjectYouOfficer = 0x00080000,
|
||||
/// <summary></summary>
|
||||
CameraDecoupled = 0x00100000,
|
||||
/// <summary></summary>
|
||||
AnimSource = 0x00200000,
|
||||
/// <summary></summary>
|
||||
CameraSource = 0x00400000,
|
||||
/// <summary></summary>
|
||||
CastShadows = 0x00800000,
|
||||
/// <summary>Server flag, will not be sent to clients. Specifies that
|
||||
/// the object is destroyed when it touches a simulator edge</summary>
|
||||
DieAtEdge = 0x01000000,
|
||||
/// <summary>Server flag, will not be sent to clients. Specifies that
|
||||
/// the object will be returned to the owner's inventory when it
|
||||
/// touches a simulator edge</summary>
|
||||
ReturnAtEdge = 0x02000000,
|
||||
/// <summary>Server flag, will not be sent to clients.</summary>
|
||||
Sandbox = 0x04000000,
|
||||
/// <summary>Server flag, will not be sent to client. Specifies that
|
||||
/// the object is hovering/flying</summary>
|
||||
Flying = 0x08000000,
|
||||
/// <summary></summary>
|
||||
ObjectOwnerModify = 0x10000000,
|
||||
/// <summary></summary>
|
||||
TemporaryOnRez = 0x20000000,
|
||||
/// <summary></summary>
|
||||
Temporary = 0x40000000,
|
||||
/// <summary></summary>
|
||||
ZlibCompressed = 0x80000000
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sound flags for sounds attached to primitives
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum SoundFlags : byte
|
||||
{
|
||||
/// <summary></summary>
|
||||
None = 0,
|
||||
/// <summary></summary>
|
||||
Loop = 0x01,
|
||||
/// <summary></summary>
|
||||
SyncMaster = 0x02,
|
||||
/// <summary></summary>
|
||||
SyncSlave = 0x04,
|
||||
/// <summary></summary>
|
||||
SyncPending = 0x08,
|
||||
/// <summary></summary>
|
||||
Queue = 0x10,
|
||||
/// <summary></summary>
|
||||
Stop = 0x20
|
||||
}
|
||||
|
||||
public enum ProfileCurve : byte
|
||||
{
|
||||
Circle = 0x00,
|
||||
Square = 0x01,
|
||||
IsoTriangle = 0x02,
|
||||
EqualTriangle = 0x03,
|
||||
RightTriangle = 0x04,
|
||||
HalfCircle = 0x05
|
||||
}
|
||||
|
||||
public enum HoleType : byte
|
||||
{
|
||||
Same = 0x00,
|
||||
Circle = 0x10,
|
||||
Square = 0x20,
|
||||
Triangle = 0x30
|
||||
}
|
||||
|
||||
public enum PathCurve : byte
|
||||
{
|
||||
Line = 0x10,
|
||||
Circle = 0x20,
|
||||
Circle2 = 0x30,
|
||||
Test = 0x40,
|
||||
Flexible = 0x80
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Material type for a primitive
|
||||
/// </summary>
|
||||
public enum Material : byte
|
||||
{
|
||||
/// <summary></summary>
|
||||
Stone = 0,
|
||||
/// <summary></summary>
|
||||
Metal,
|
||||
/// <summary></summary>
|
||||
Glass,
|
||||
/// <summary></summary>
|
||||
Wood,
|
||||
/// <summary></summary>
|
||||
Flesh,
|
||||
/// <summary></summary>
|
||||
Plastic,
|
||||
/// <summary></summary>
|
||||
Rubber,
|
||||
/// <summary></summary>
|
||||
Light
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used in a helper function to roughly determine prim shape
|
||||
/// </summary>
|
||||
public enum PrimType
|
||||
{
|
||||
Unknown,
|
||||
Box,
|
||||
Cylinder,
|
||||
Prism,
|
||||
Sphere,
|
||||
Torus,
|
||||
Tube,
|
||||
Ring,
|
||||
Sculpt,
|
||||
Mesh
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extra parameters for primitives, these flags are for features that have
|
||||
/// been added after the original ObjectFlags that has all eight bits
|
||||
/// reserved already
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ExtraParamType : ushort
|
||||
{
|
||||
/// <summary>Whether this object has flexible parameters</summary>
|
||||
Flexible = 0x10,
|
||||
/// <summary>Whether this object has light parameters</summary>
|
||||
Light = 0x20,
|
||||
/// <summary>Whether this object is a sculpted prim</summary>
|
||||
Sculpt = 0x30,
|
||||
/// <summary>Whether this object is a light image map</summary>
|
||||
LightImage = 0x40,
|
||||
/// <summary>Whether this object is a mesh</summary>
|
||||
Mesh = 0x60,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public enum JointType : byte
|
||||
{
|
||||
/// <summary></summary>
|
||||
Invalid = 0,
|
||||
/// <summary></summary>
|
||||
Hinge = 1,
|
||||
/// <summary></summary>
|
||||
Point = 2,
|
||||
// <summary></summary>
|
||||
//[Obsolete]
|
||||
//LPoint = 3,
|
||||
//[Obsolete]
|
||||
//Wheel = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public enum SculptType : byte
|
||||
{
|
||||
/// <summary></summary>
|
||||
None = 0,
|
||||
/// <summary></summary>
|
||||
Sphere = 1,
|
||||
/// <summary></summary>
|
||||
Torus = 2,
|
||||
/// <summary></summary>
|
||||
Plane = 3,
|
||||
/// <summary></summary>
|
||||
Cylinder = 4,
|
||||
/// <summary></summary>
|
||||
Mesh = 5,
|
||||
/// <summary></summary>
|
||||
Invert = 64,
|
||||
/// <summary></summary>
|
||||
Mirror = 128
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public enum FaceType : ushort
|
||||
{
|
||||
/// <summary></summary>
|
||||
PathBegin = 0x1 << 0,
|
||||
/// <summary></summary>
|
||||
PathEnd = 0x1 << 1,
|
||||
/// <summary></summary>
|
||||
InnerSide = 0x1 << 2,
|
||||
/// <summary></summary>
|
||||
ProfileBegin = 0x1 << 3,
|
||||
/// <summary></summary>
|
||||
ProfileEnd = 0x1 << 4,
|
||||
/// <summary></summary>
|
||||
OuterSide0 = 0x1 << 5,
|
||||
/// <summary></summary>
|
||||
OuterSide1 = 0x1 << 6,
|
||||
/// <summary></summary>
|
||||
OuterSide2 = 0x1 << 7,
|
||||
/// <summary></summary>
|
||||
OuterSide3 = 0x1 << 8
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public enum ObjectCategory
|
||||
{
|
||||
/// <summary></summary>
|
||||
Invalid = -1,
|
||||
/// <summary></summary>
|
||||
None = 0,
|
||||
/// <summary></summary>
|
||||
Owner,
|
||||
/// <summary></summary>
|
||||
Group,
|
||||
/// <summary></summary>
|
||||
Other,
|
||||
/// <summary></summary>
|
||||
Selected,
|
||||
/// <summary></summary>
|
||||
Temporary
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attachment points for objects on avatar bodies
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Both InventoryObject and InventoryAttachment types can be attached
|
||||
///</remarks>
|
||||
public enum AttachmentPoint : byte
|
||||
{
|
||||
/// <summary>Right hand if object was not previously attached</summary>
|
||||
[EnumInfo(Text = "Default")]
|
||||
Default = 0,
|
||||
/// <summary>Chest</summary>
|
||||
[EnumInfo(Text = "Chest")]
|
||||
Chest = 1,
|
||||
/// <summary>Skull</summary>
|
||||
[EnumInfo(Text = "Head")]
|
||||
Skull,
|
||||
/// <summary>Left shoulder</summary>
|
||||
[EnumInfo(Text = "Left Shoulder")]
|
||||
LeftShoulder,
|
||||
/// <summary>Right shoulder</summary>
|
||||
[EnumInfo(Text = "Right Shoulder")]
|
||||
RightShoulder,
|
||||
/// <summary>Left hand</summary>
|
||||
[EnumInfo(Text = "Left Hand")]
|
||||
LeftHand,
|
||||
/// <summary>Right hand</summary>
|
||||
[EnumInfo(Text = "Right Hand")]
|
||||
RightHand,
|
||||
/// <summary>Left foot</summary>
|
||||
[EnumInfo(Text = "Left Foot")]
|
||||
LeftFoot,
|
||||
/// <summary>Right foot</summary>
|
||||
[EnumInfo(Text = "Right Foot")]
|
||||
RightFoot,
|
||||
/// <summary>Spine</summary>
|
||||
[EnumInfo(Text = "Back")]
|
||||
Spine,
|
||||
/// <summary>Pelvis</summary>
|
||||
[EnumInfo(Text = "Pelvis")]
|
||||
Pelvis,
|
||||
/// <summary>Mouth</summary>
|
||||
[EnumInfo(Text = "Mouth")]
|
||||
Mouth,
|
||||
/// <summary>Chin</summary>
|
||||
[EnumInfo(Text = "Chin")]
|
||||
Chin,
|
||||
/// <summary>Left ear</summary>
|
||||
[EnumInfo(Text = "Left Ear")]
|
||||
LeftEar,
|
||||
/// <summary>Right ear</summary>
|
||||
[EnumInfo(Text = "Right Ear")]
|
||||
RightEar,
|
||||
/// <summary>Left eyeball</summary>
|
||||
[EnumInfo(Text = "Left Eye")]
|
||||
LeftEyeball,
|
||||
/// <summary>Right eyeball</summary>
|
||||
[EnumInfo(Text = "Right Eye")]
|
||||
RightEyeball,
|
||||
/// <summary>Nose</summary>
|
||||
[EnumInfo(Text = "Nose")]
|
||||
Nose,
|
||||
/// <summary>Right upper arm</summary>
|
||||
[EnumInfo(Text = "Right Upper Arm")]
|
||||
RightUpperArm,
|
||||
/// <summary>Right forearm</summary>
|
||||
[EnumInfo(Text = "Right Lower Arm")]
|
||||
RightForearm,
|
||||
/// <summary>Left upper arm</summary>
|
||||
[EnumInfo(Text = "Left Upper Arm")]
|
||||
LeftUpperArm,
|
||||
/// <summary>Left forearm</summary>
|
||||
[EnumInfo(Text = "Left Lower Arm")]
|
||||
LeftForearm,
|
||||
/// <summary>Right hip</summary>
|
||||
[EnumInfo(Text = "Right Hip")]
|
||||
RightHip,
|
||||
/// <summary>Right upper leg</summary>
|
||||
[EnumInfo(Text = "Right Upper Leg")]
|
||||
RightUpperLeg,
|
||||
/// <summary>Right lower leg</summary>
|
||||
[EnumInfo(Text = "Right Lower Leg")]
|
||||
RightLowerLeg,
|
||||
/// <summary>Left hip</summary>
|
||||
[EnumInfo(Text = "Left Hip")]
|
||||
LeftHip,
|
||||
/// <summary>Left upper leg</summary>
|
||||
[EnumInfo(Text = "Left Upper Leg")]
|
||||
LeftUpperLeg,
|
||||
/// <summary>Left lower leg</summary>
|
||||
[EnumInfo(Text = "Left Lower Leg")]
|
||||
LeftLowerLeg,
|
||||
/// <summary>Stomach</summary>
|
||||
[EnumInfo(Text = "Belly")]
|
||||
Stomach,
|
||||
/// <summary>Left pectoral</summary>
|
||||
[EnumInfo(Text = "Left Pec")]
|
||||
LeftPec,
|
||||
/// <summary>Right pectoral</summary>
|
||||
[EnumInfo(Text = "Right Pec")]
|
||||
RightPec,
|
||||
/// <summary>HUD Center position 2</summary>
|
||||
[EnumInfo(Text = "HUD Center 2")]
|
||||
HUDCenter2,
|
||||
/// <summary>HUD Top-right</summary>
|
||||
[EnumInfo(Text = "HUD Top Right")]
|
||||
HUDTopRight,
|
||||
/// <summary>HUD Top</summary>
|
||||
[EnumInfo(Text = "HUD Top Center")]
|
||||
HUDTop,
|
||||
/// <summary>HUD Top-left</summary>
|
||||
[EnumInfo(Text = "HUD Top Left")]
|
||||
HUDTopLeft,
|
||||
/// <summary>HUD Center</summary>
|
||||
[EnumInfo(Text = "HUD Center 1")]
|
||||
HUDCenter,
|
||||
/// <summary>HUD Bottom-left</summary>
|
||||
[EnumInfo(Text = "HUD Bottom Left")]
|
||||
HUDBottomLeft,
|
||||
/// <summary>HUD Bottom</summary>
|
||||
[EnumInfo(Text = "HUD Bottom")]
|
||||
HUDBottom,
|
||||
/// <summary>HUD Bottom-right</summary>
|
||||
[EnumInfo(Text = "HUD Bottom Right")]
|
||||
HUDBottomRight,
|
||||
/// <summary>Neck</summary>
|
||||
[EnumInfo(Text = "Neck")]
|
||||
Neck,
|
||||
/// <summary>Avatar Center</summary>
|
||||
[EnumInfo(Text = "Avatar Center")]
|
||||
Root,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tree foliage types
|
||||
/// </summary>
|
||||
public enum Tree : byte
|
||||
{
|
||||
/// <summary>Pine1 tree</summary>
|
||||
Pine1 = 0,
|
||||
/// <summary>Oak tree</summary>
|
||||
Oak,
|
||||
/// <summary>Tropical Bush1</summary>
|
||||
TropicalBush1,
|
||||
/// <summary>Palm1 tree</summary>
|
||||
Palm1,
|
||||
/// <summary>Dogwood tree</summary>
|
||||
Dogwood,
|
||||
/// <summary>Tropical Bush2</summary>
|
||||
TropicalBush2,
|
||||
/// <summary>Palm2 tree</summary>
|
||||
Palm2,
|
||||
/// <summary>Cypress1 tree</summary>
|
||||
Cypress1,
|
||||
/// <summary>Cypress2 tree</summary>
|
||||
Cypress2,
|
||||
/// <summary>Pine2 tree</summary>
|
||||
Pine2,
|
||||
/// <summary>Plumeria</summary>
|
||||
Plumeria,
|
||||
/// <summary>Winter pinetree1</summary>
|
||||
WinterPine1,
|
||||
/// <summary>Winter Aspen tree</summary>
|
||||
WinterAspen,
|
||||
/// <summary>Winter pinetree2</summary>
|
||||
WinterPine2,
|
||||
/// <summary>Eucalyptus tree</summary>
|
||||
Eucalyptus,
|
||||
/// <summary>Fern</summary>
|
||||
Fern,
|
||||
/// <summary>Eelgrass</summary>
|
||||
Eelgrass,
|
||||
/// <summary>Sea Sword</summary>
|
||||
SeaSword,
|
||||
/// <summary>Kelp1 plant</summary>
|
||||
Kelp1,
|
||||
/// <summary>Beach grass</summary>
|
||||
BeachGrass1,
|
||||
/// <summary>Kelp2 plant</summary>
|
||||
Kelp2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Grass foliage types
|
||||
/// </summary>
|
||||
public enum Grass : byte
|
||||
{
|
||||
/// <summary></summary>
|
||||
Grass0 = 0,
|
||||
/// <summary></summary>
|
||||
Grass1,
|
||||
/// <summary></summary>
|
||||
Grass2,
|
||||
/// <summary></summary>
|
||||
Grass3,
|
||||
/// <summary></summary>
|
||||
Grass4,
|
||||
/// <summary></summary>
|
||||
Undergrowth1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Action associated with clicking on an object
|
||||
/// </summary>
|
||||
public enum ClickAction : byte
|
||||
{
|
||||
/// <summary>Touch object</summary>
|
||||
Touch = 0,
|
||||
/// <summary>Sit on object</summary>
|
||||
Sit = 1,
|
||||
/// <summary>Purchase object or contents</summary>
|
||||
Buy = 2,
|
||||
/// <summary>Pay the object</summary>
|
||||
Pay = 3,
|
||||
/// <summary>Open task inventory</summary>
|
||||
OpenTask = 4,
|
||||
/// <summary>Play parcel media</summary>
|
||||
PlayMedia = 5,
|
||||
/// <summary>Open parcel media</summary>
|
||||
OpenMedia = 6
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type of physics representation used for this prim in the simulator
|
||||
/// </summary>
|
||||
public enum PhysicsShapeType : byte
|
||||
{
|
||||
/// <summary>Use prim physics form this object</summary>
|
||||
Prim = 0,
|
||||
/// <summary>No physics, prim doesn't collide</summary>
|
||||
None,
|
||||
/// <summary>Use convex hull represantion of this prim</summary>
|
||||
ConvexHull
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
#region TimedCacheKey Class
|
||||
|
||||
class TimedCacheKey<TKey> : IComparable<TKey>
|
||||
{
|
||||
private DateTime expirationDate;
|
||||
private bool slidingExpiration;
|
||||
private TimeSpan slidingExpirationWindowSize;
|
||||
private TKey key;
|
||||
|
||||
public DateTime ExpirationDate { get { return expirationDate; } }
|
||||
public TKey Key { get { return key; } }
|
||||
public bool SlidingExpiration { get { return slidingExpiration; } }
|
||||
public TimeSpan SlidingExpirationWindowSize { get { return slidingExpirationWindowSize; } }
|
||||
|
||||
public TimedCacheKey(TKey key, DateTime expirationDate)
|
||||
{
|
||||
this.key = key;
|
||||
this.slidingExpiration = false;
|
||||
this.expirationDate = expirationDate;
|
||||
}
|
||||
|
||||
public TimedCacheKey(TKey key, TimeSpan slidingExpirationWindowSize)
|
||||
{
|
||||
this.key = key;
|
||||
this.slidingExpiration = true;
|
||||
this.slidingExpirationWindowSize = slidingExpirationWindowSize;
|
||||
Accessed();
|
||||
}
|
||||
|
||||
public void Accessed()
|
||||
{
|
||||
if (slidingExpiration)
|
||||
expirationDate = DateTime.Now.Add(slidingExpirationWindowSize);
|
||||
}
|
||||
|
||||
public int CompareTo(TKey other)
|
||||
{
|
||||
return key.GetHashCode().CompareTo(other.GetHashCode());
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public sealed class ExpiringCache<TKey, TValue>
|
||||
{
|
||||
const double CACHE_PURGE_HZ = 1.0;
|
||||
const int MAX_LOCK_WAIT = 5000; // milliseconds
|
||||
|
||||
#region Private fields
|
||||
|
||||
/// <summary>For thread safety</summary>
|
||||
object syncRoot = new object();
|
||||
/// <summary>For thread safety</summary>
|
||||
object isPurging = new object();
|
||||
|
||||
Dictionary<TimedCacheKey<TKey>, TValue> timedStorage = new Dictionary<TimedCacheKey<TKey>, TValue>();
|
||||
Dictionary<TKey, TimedCacheKey<TKey>> timedStorageIndex = new Dictionary<TKey, TimedCacheKey<TKey>>();
|
||||
private System.Timers.Timer timer = new System.Timers.Timer(TimeSpan.FromSeconds(CACHE_PURGE_HZ).TotalMilliseconds);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
public ExpiringCache()
|
||||
{
|
||||
timer.Elapsed += PurgeCache;
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public methods
|
||||
|
||||
public bool Add(TKey key, TValue value, double expirationSeconds)
|
||||
{
|
||||
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
|
||||
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
|
||||
try
|
||||
{
|
||||
// This is the actual adding of the key
|
||||
if (timedStorageIndex.ContainsKey(key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
TimedCacheKey<TKey> internalKey = new TimedCacheKey<TKey>(key, DateTime.UtcNow + TimeSpan.FromSeconds(expirationSeconds));
|
||||
timedStorage.Add(internalKey, value);
|
||||
timedStorageIndex.Add(key, internalKey);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
finally { Monitor.Exit(syncRoot); }
|
||||
}
|
||||
|
||||
public bool Add(TKey key, TValue value, TimeSpan slidingExpiration)
|
||||
{
|
||||
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
|
||||
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
|
||||
try
|
||||
{
|
||||
// This is the actual adding of the key
|
||||
if (timedStorageIndex.ContainsKey(key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
TimedCacheKey<TKey> internalKey = new TimedCacheKey<TKey>(key, slidingExpiration);
|
||||
timedStorage.Add(internalKey, value);
|
||||
timedStorageIndex.Add(key, internalKey);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
finally { Monitor.Exit(syncRoot); }
|
||||
}
|
||||
|
||||
public bool AddOrUpdate(TKey key, TValue value, double expirationSeconds)
|
||||
{
|
||||
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
|
||||
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
|
||||
try
|
||||
{
|
||||
if (Contains(key))
|
||||
{
|
||||
Update(key, value, expirationSeconds);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Add(key, value, expirationSeconds);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
finally { Monitor.Exit(syncRoot); }
|
||||
}
|
||||
|
||||
public bool AddOrUpdate(TKey key, TValue value, TimeSpan slidingExpiration)
|
||||
{
|
||||
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
|
||||
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
|
||||
try
|
||||
{
|
||||
if (Contains(key))
|
||||
{
|
||||
Update(key, value, slidingExpiration);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Add(key, value, slidingExpiration);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
finally { Monitor.Exit(syncRoot); }
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
|
||||
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
|
||||
try
|
||||
{
|
||||
timedStorage.Clear();
|
||||
timedStorageIndex.Clear();
|
||||
}
|
||||
finally { Monitor.Exit(syncRoot); }
|
||||
}
|
||||
|
||||
public bool Contains(TKey key)
|
||||
{
|
||||
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
|
||||
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
|
||||
try
|
||||
{
|
||||
return timedStorageIndex.ContainsKey(key);
|
||||
}
|
||||
finally { Monitor.Exit(syncRoot); }
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return timedStorage.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public object this[TKey key]
|
||||
{
|
||||
get
|
||||
{
|
||||
TValue o;
|
||||
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
|
||||
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
|
||||
try
|
||||
{
|
||||
if (timedStorageIndex.ContainsKey(key))
|
||||
{
|
||||
TimedCacheKey<TKey> tkey = timedStorageIndex[key];
|
||||
o = timedStorage[tkey];
|
||||
timedStorage.Remove(tkey);
|
||||
tkey.Accessed();
|
||||
timedStorage.Add(tkey, o);
|
||||
return o;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Key not found in the cache");
|
||||
}
|
||||
}
|
||||
finally { Monitor.Exit(syncRoot); }
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
|
||||
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
|
||||
try
|
||||
{
|
||||
if (timedStorageIndex.ContainsKey(key))
|
||||
{
|
||||
timedStorage.Remove(timedStorageIndex[key]);
|
||||
timedStorageIndex.Remove(key);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
finally { Monitor.Exit(syncRoot); }
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
TValue o;
|
||||
|
||||
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
|
||||
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
|
||||
try
|
||||
{
|
||||
if (timedStorageIndex.ContainsKey(key))
|
||||
{
|
||||
TimedCacheKey<TKey> tkey = timedStorageIndex[key];
|
||||
o = timedStorage[tkey];
|
||||
timedStorage.Remove(tkey);
|
||||
tkey.Accessed();
|
||||
timedStorage.Add(tkey, o);
|
||||
value = o;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
finally { Monitor.Exit(syncRoot); }
|
||||
|
||||
value = default(TValue);
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Update(TKey key, TValue value)
|
||||
{
|
||||
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
|
||||
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
|
||||
try
|
||||
{
|
||||
if (timedStorageIndex.ContainsKey(key))
|
||||
{
|
||||
timedStorage.Remove(timedStorageIndex[key]);
|
||||
timedStorageIndex[key].Accessed();
|
||||
timedStorage.Add(timedStorageIndex[key], value);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
finally { Monitor.Exit(syncRoot); }
|
||||
}
|
||||
|
||||
public bool Update(TKey key, TValue value, double expirationSeconds)
|
||||
{
|
||||
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
|
||||
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
|
||||
try
|
||||
{
|
||||
if (timedStorageIndex.ContainsKey(key))
|
||||
{
|
||||
timedStorage.Remove(timedStorageIndex[key]);
|
||||
timedStorageIndex.Remove(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TimedCacheKey<TKey> internalKey = new TimedCacheKey<TKey>(key, DateTime.UtcNow + TimeSpan.FromSeconds(expirationSeconds));
|
||||
timedStorage.Add(internalKey, value);
|
||||
timedStorageIndex.Add(key, internalKey);
|
||||
return true;
|
||||
}
|
||||
finally { Monitor.Exit(syncRoot); }
|
||||
}
|
||||
|
||||
public bool Update(TKey key, TValue value, TimeSpan slidingExpiration)
|
||||
{
|
||||
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
|
||||
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
|
||||
try
|
||||
{
|
||||
if (timedStorageIndex.ContainsKey(key))
|
||||
{
|
||||
timedStorage.Remove(timedStorageIndex[key]);
|
||||
timedStorageIndex.Remove(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TimedCacheKey<TKey> internalKey = new TimedCacheKey<TKey>(key, slidingExpiration);
|
||||
timedStorage.Add(internalKey, value);
|
||||
timedStorageIndex.Add(key, internalKey);
|
||||
return true;
|
||||
}
|
||||
finally { Monitor.Exit(syncRoot); }
|
||||
}
|
||||
|
||||
public void CopyTo(Array array, int startIndex)
|
||||
{
|
||||
// Error checking
|
||||
if (array == null) { throw new ArgumentNullException("array"); }
|
||||
|
||||
if (startIndex < 0) { throw new ArgumentOutOfRangeException("startIndex", "startIndex must be >= 0."); }
|
||||
|
||||
if (array.Rank > 1) { throw new ArgumentException("array must be of Rank 1 (one-dimensional)", "array"); }
|
||||
if (startIndex >= array.Length) { throw new ArgumentException("startIndex must be less than the length of the array.", "startIndex"); }
|
||||
if (Count > array.Length - startIndex) { throw new ArgumentException("There is not enough space from startIndex to the end of the array to accomodate all items in the cache."); }
|
||||
|
||||
// Copy the data to the array (in a thread-safe manner)
|
||||
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
|
||||
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
|
||||
try
|
||||
{
|
||||
foreach (object o in timedStorage)
|
||||
{
|
||||
array.SetValue(o, startIndex);
|
||||
startIndex++;
|
||||
}
|
||||
}
|
||||
finally { Monitor.Exit(syncRoot); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private methods
|
||||
|
||||
/// <summary>
|
||||
/// Purges expired objects from the cache. Called automatically by the purge timer.
|
||||
/// </summary>
|
||||
private void PurgeCache(object sender, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
// Only let one thread purge at once - a buildup could cause a crash
|
||||
// This could cause the purge to be delayed while there are lots of read/write ops
|
||||
// happening on the cache
|
||||
if (!Monitor.TryEnter(isPurging))
|
||||
return;
|
||||
|
||||
DateTime signalTime = DateTime.UtcNow;
|
||||
|
||||
try
|
||||
{
|
||||
// If we fail to acquire a lock on the synchronization root after MAX_LOCK_WAIT, skip this purge cycle
|
||||
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
|
||||
return;
|
||||
try
|
||||
{
|
||||
Lazy<List<object>> expiredItems = new Lazy<List<object>>();
|
||||
|
||||
foreach (TimedCacheKey<TKey> timedKey in timedStorage.Keys)
|
||||
{
|
||||
if (timedKey.ExpirationDate < signalTime)
|
||||
{
|
||||
// Mark the object for purge
|
||||
expiredItems.Value.Add(timedKey.Key);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (expiredItems.IsValueCreated)
|
||||
{
|
||||
foreach (TKey key in expiredItems.Value)
|
||||
{
|
||||
TimedCacheKey<TKey> timedKey = timedStorageIndex[key];
|
||||
timedStorageIndex.Remove(timedKey.Key);
|
||||
timedStorage.Remove(timedKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally { Monitor.Exit(syncRoot); }
|
||||
}
|
||||
finally { Monitor.Exit(isPurging); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
public class Lazy<T>
|
||||
{
|
||||
private T _value = default(T);
|
||||
private volatile bool _isValueCreated = false;
|
||||
private Func<T> _valueFactory = null;
|
||||
private object _lock;
|
||||
|
||||
public bool IsValueCreated { get { return _isValueCreated; } }
|
||||
|
||||
public Lazy()
|
||||
: this(() => Activator.CreateInstance<T>())
|
||||
{
|
||||
}
|
||||
|
||||
public Lazy(bool isThreadSafe)
|
||||
: this(() => Activator.CreateInstance<T>(), isThreadSafe)
|
||||
{
|
||||
}
|
||||
|
||||
public Lazy(Func<T> valueFactory) :
|
||||
this(valueFactory, true)
|
||||
{
|
||||
}
|
||||
|
||||
public Lazy(Func<T> valueFactory, bool isThreadSafe)
|
||||
{
|
||||
if (isThreadSafe)
|
||||
{
|
||||
this._lock = new object();
|
||||
}
|
||||
|
||||
this._valueFactory = valueFactory;
|
||||
}
|
||||
|
||||
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!this._isValueCreated)
|
||||
{
|
||||
if (this._lock != null)
|
||||
{
|
||||
Monitor.Enter(this._lock);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
T value = this._valueFactory.Invoke();
|
||||
this._valueFactory = null;
|
||||
Thread.MemoryBarrier();
|
||||
this._value = value;
|
||||
this._isValueCreated = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (this._lock != null)
|
||||
{
|
||||
Monitor.Exit(this._lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this._value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
/// <summary>
|
||||
/// A thread-safe lockless queue that supports multiple readers and
|
||||
/// multiple writers
|
||||
/// </summary>
|
||||
public sealed class LocklessQueue<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a node container for data in a singly linked list
|
||||
/// </summary>
|
||||
private sealed class SingleLinkNode
|
||||
{
|
||||
/// <summary>Pointer to the next node in list</summary>
|
||||
public SingleLinkNode Next;
|
||||
/// <summary>The data contained by the node</summary>
|
||||
public T Item;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public SingleLinkNode() { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public SingleLinkNode(T item)
|
||||
{
|
||||
this.Item = item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Queue head</summary>
|
||||
SingleLinkNode head;
|
||||
/// <summary>Queue tail</summary>
|
||||
SingleLinkNode tail;
|
||||
/// <summary>Queue item count</summary>
|
||||
int count;
|
||||
|
||||
/// <summary>Gets the current number of items in the queue. Since this
|
||||
/// is a lockless collection this value should be treated as a close
|
||||
/// estimate</summary>
|
||||
public int Count { get { return count; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public LocklessQueue()
|
||||
{
|
||||
count = 0;
|
||||
head = tail = new SingleLinkNode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue an item
|
||||
/// </summary>
|
||||
/// <param name="item">Item to enqeue</param>
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
SingleLinkNode newNode = new SingleLinkNode { Item = item };
|
||||
|
||||
while (true)
|
||||
{
|
||||
SingleLinkNode oldTail = tail;
|
||||
SingleLinkNode oldTailNext = oldTail.Next;
|
||||
|
||||
if (tail == oldTail)
|
||||
{
|
||||
if (oldTailNext != null)
|
||||
{
|
||||
CAS(ref tail, oldTail, oldTailNext);
|
||||
}
|
||||
else if (CAS(ref tail.Next, null, newNode))
|
||||
{
|
||||
CAS(ref tail, oldTail, newNode);
|
||||
Interlocked.Increment(ref count);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to dequeue an item
|
||||
/// </summary>
|
||||
/// <param name="item">Dequeued item if the dequeue was successful</param>
|
||||
/// <returns>True if an item was successfully deqeued, otherwise false</returns>
|
||||
public bool TryDequeue(out T item)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
SingleLinkNode oldHead = head;
|
||||
SingleLinkNode oldHeadNext = oldHead.Next;
|
||||
|
||||
if (oldHead == head)
|
||||
{
|
||||
if (oldHeadNext == null)
|
||||
{
|
||||
item = default(T);
|
||||
count = 0;
|
||||
return false;
|
||||
}
|
||||
if (CAS(ref head, oldHead, oldHeadNext))
|
||||
{
|
||||
item = oldHeadNext.Item;
|
||||
Interlocked.Decrement(ref count);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CAS(ref SingleLinkNode location, SingleLinkNode comparand, SingleLinkNode newValue)
|
||||
{
|
||||
return
|
||||
(object)comparand ==
|
||||
(object)Interlocked.CompareExchange<SingleLinkNode>(ref location, newValue, comparand);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,94 @@
|
||||
<?xml version="1.0" ?>
|
||||
<project name="OpenMetaverseTypes" default="build">
|
||||
<target name="build">
|
||||
<echo message="Build Directory is ${build.dir}" />
|
||||
<mkdir dir="${build.dir}" />
|
||||
<csc target="library" debug="${build.debug}" unsafe="True" warnaserror="False" define="TRACE" nostdlib="False" main="" doc="${build.dir}/OpenMetaverseTypes.XML" output="${build.dir}/${project::get-name()}.dll">
|
||||
<resources prefix="OpenMetaverse" dynamicprefix="true" >
|
||||
</resources>
|
||||
<sources failonempty="true">
|
||||
<include name="BlockingQueue.cs" />
|
||||
<include name="CRC32.cs" />
|
||||
<include name="CircularQueue.cs" />
|
||||
<include name="Color4.cs" />
|
||||
<include name="DoubleDictionary.cs" />
|
||||
<include name="Enums.cs" />
|
||||
<include name="EnumsPrimitive.cs" />
|
||||
<include name="ExpiringCache.cs" />
|
||||
<include name="Lazy.cs" />
|
||||
<include name="LocklessQueue.cs" />
|
||||
<include name="Matrix4.cs" />
|
||||
<include name="Quaternion.cs" />
|
||||
<include name="Ray.cs" />
|
||||
<include name="ReaderWriterLockSlim.cs" />
|
||||
<include name="ThreadSafeDictionary.cs" />
|
||||
<include name="TokenBucket.cs" />
|
||||
<include name="UUID.cs" />
|
||||
<include name="Utils.cs" />
|
||||
<include name="UtilsConversions.cs" />
|
||||
<include name="Vector2.cs" />
|
||||
<include name="Vector3.cs" />
|
||||
<include name="Vector3d.cs" />
|
||||
<include name="Vector4.cs" />
|
||||
</sources>
|
||||
<references basedir="${project::get-base-directory()}">
|
||||
<lib>
|
||||
<include name="${project::get-base-directory()}" />
|
||||
<include name="${build.dir}" />
|
||||
</lib>
|
||||
<include name="System.dll" />
|
||||
<include name="System.Core.dll" />
|
||||
</references>
|
||||
<nowarn>
|
||||
<warning number="1591" />
|
||||
<warning number="1574" />
|
||||
<warning number="0419" />
|
||||
<warning number="0618" />
|
||||
</nowarn>
|
||||
</csc>
|
||||
</target>
|
||||
<target name="clean">
|
||||
<delete dir="${bin.dir}" failonerror="false" />
|
||||
<delete dir="${obj.dir}" failonerror="false" />
|
||||
</target>
|
||||
<target name="doc" description="Creates documentation.">
|
||||
<property name="doc.target" value="" />
|
||||
<if test="${platform::is-unix()}">
|
||||
<property name="doc.target" value="Web" />
|
||||
</if>
|
||||
<ndoc failonerror="false" verbose="true">
|
||||
<assemblies basedir="${project::get-base-directory()}">
|
||||
<include name="${build.dir}/${project::get-name()}.dll" />
|
||||
</assemblies>
|
||||
<summaries basedir="${project::get-base-directory()}">
|
||||
<include name="${build.dir}/${project::get-name()}.xml"/>
|
||||
</summaries>
|
||||
<referencepaths basedir="${project::get-base-directory()}">
|
||||
<include name="${build.dir}" />
|
||||
</referencepaths>
|
||||
<documenters>
|
||||
<documenter name="MSDN">
|
||||
<property name="OutputDirectory" value="${build.dir}/doc/${project::get-name()}" />
|
||||
<property name="OutputTarget" value="${doc.target}" />
|
||||
<property name="HtmlHelpName" value="${project::get-name()}" />
|
||||
<property name="IncludeFavorites" value="False" />
|
||||
<property name="Title" value="${project::get-name()} SDK Documentation" />
|
||||
<property name="SplitTOCs" value="False" />
|
||||
<property name="DefaulTOC" value="" />
|
||||
<property name="ShowVisualBasic" value="True" />
|
||||
<property name="AutoDocumentConstructors" value="True" />
|
||||
<property name="ShowMissingSummaries" value="${build.debug}" />
|
||||
<property name="ShowMissingRemarks" value="${build.debug}" />
|
||||
<property name="ShowMissingParams" value="${build.debug}" />
|
||||
<property name="ShowMissingReturns" value="${build.debug}" />
|
||||
<property name="ShowMissingValues" value="${build.debug}" />
|
||||
<property name="DocumentInternals" value="False" />
|
||||
<property name="DocumentPrivates" value="False" />
|
||||
<property name="DocumentProtected" value="True" />
|
||||
<property name="DocumentEmptyNamespaces" value="${build.debug}" />
|
||||
<property name="IncludeAssemblyVersion" value="True" />
|
||||
</documenter>
|
||||
</documenters>
|
||||
</ndoc>
|
||||
</target>
|
||||
</project>
|
||||
@@ -0,0 +1,48 @@
|
||||
<Project name="OpenMetaverseTypes" description="" standardNamespace="OpenMetaverse" newfilesearch="None" enableviewstate="True" fileversion="2.0" language="C#" clr-version="Net_2_0" ctype="DotNetProject">
|
||||
<Configurations active="Debug">
|
||||
<Configuration name="Release" ctype="DotNetProjectConfiguration">
|
||||
<Output directory="./../bin/" assembly="OpenMetaverseTypes" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
<Build debugmode="True" target="Library" />
|
||||
<Execution runwithwarnings="True" consolepause="True" runtime="MsNet" clr-version="Net_2_0" />
|
||||
<CodeGeneration compiler="Csc" warninglevel="4" nowarn="1591,1574,0419,0618" includedebuginformation="False" optimize="True" unsafecodeallowed="True" generateoverflowchecks="False" mainclass="" target="Library" definesymbols="TRACE" generatexmldocumentation="True" win32Icon="" ctype="CSharpCompilerParameters" />
|
||||
</Configuration>
|
||||
<Configuration name="Debug" ctype="DotNetProjectConfiguration">
|
||||
<Output directory="./../bin/" assembly="OpenMetaverseTypes" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
|
||||
<Build debugmode="True" target="Library" />
|
||||
<Execution runwithwarnings="True" consolepause="True" runtime="MsNet" clr-version="Net_2_0" />
|
||||
<CodeGeneration compiler="Csc" warninglevel="4" nowarn="1591,1574,0419,0618" includedebuginformation="True" optimize="False" unsafecodeallowed="True" generateoverflowchecks="False" mainclass="" target="Library" definesymbols="TRACE;DEBUG" generatexmldocumentation="False" win32Icon="" ctype="CSharpCompilerParameters" />
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<DeploymentInformation target="" script="" strategy="File">
|
||||
<excludeFiles />
|
||||
</DeploymentInformation>
|
||||
<Contents>
|
||||
<File name="./BlockingQueue.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./CRC32.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./CircularQueue.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./Color4.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./DoubleDictionary.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./Enums.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./EnumsPrimitive.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./ExpiringCache.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./Lazy.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./LocklessQueue.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./Matrix4.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./Quaternion.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./Ray.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./ReaderWriterLockSlim.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./ThreadSafeDictionary.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./TokenBucket.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./UUID.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./Utils.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./UtilsConversions.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./Vector2.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./Vector3.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./Vector3d.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
<File name="./Vector4.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
|
||||
</Contents>
|
||||
<References>
|
||||
<ProjectReference type="Gac" localcopy="False" refto="System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<ProjectReference type="Gac" localcopy="False" refto="System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
</References>
|
||||
</Project>
|
||||
@@ -0,0 +1,758 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Globalization;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Quaternion : IEquatable<Quaternion>
|
||||
{
|
||||
/// <summary>X value</summary>
|
||||
public float X;
|
||||
/// <summary>Y value</summary>
|
||||
public float Y;
|
||||
/// <summary>Z value</summary>
|
||||
public float Z;
|
||||
/// <summary>W value</summary>
|
||||
public float W;
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Quaternion(float x, float y, float z, float w)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
W = w;
|
||||
}
|
||||
|
||||
public Quaternion(Vector3 vectorPart, float scalarPart)
|
||||
{
|
||||
X = vectorPart.X;
|
||||
Y = vectorPart.Y;
|
||||
Z = vectorPart.Z;
|
||||
W = scalarPart;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build a quaternion from normalized float values
|
||||
/// </summary>
|
||||
/// <param name="x">X value from -1.0 to 1.0</param>
|
||||
/// <param name="y">Y value from -1.0 to 1.0</param>
|
||||
/// <param name="z">Z value from -1.0 to 1.0</param>
|
||||
public Quaternion(float x, float y, float z)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
|
||||
float xyzsum = 1 - X * X - Y * Y - Z * Z;
|
||||
W = (xyzsum > 0) ? (float)Math.Sqrt(xyzsum) : 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor, builds a quaternion object from a byte array
|
||||
/// </summary>
|
||||
/// <param name="byteArray">Byte array containing four four-byte floats</param>
|
||||
/// <param name="pos">Offset in the byte array to start reading at</param>
|
||||
/// <param name="normalized">Whether the source data is normalized or
|
||||
/// not. If this is true 12 bytes will be read, otherwise 16 bytes will
|
||||
/// be read.</param>
|
||||
public Quaternion(byte[] byteArray, int pos, bool normalized)
|
||||
{
|
||||
X = Y = Z = W = 0;
|
||||
FromBytes(byteArray, pos, normalized);
|
||||
}
|
||||
|
||||
public Quaternion(Quaternion q)
|
||||
{
|
||||
X = q.X;
|
||||
Y = q.Y;
|
||||
Z = q.Z;
|
||||
W = q.W;
|
||||
}
|
||||
|
||||
#endregion Constructors
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public bool ApproxEquals(Quaternion quat, float tolerance)
|
||||
{
|
||||
Quaternion diff = this - quat;
|
||||
return (diff.LengthSquared() <= tolerance * tolerance);
|
||||
}
|
||||
|
||||
public float Length()
|
||||
{
|
||||
return (float)Math.Sqrt(X * X + Y * Y + Z * Z + W * W);
|
||||
}
|
||||
|
||||
public float LengthSquared()
|
||||
{
|
||||
return (X * X + Y * Y + Z * Z + W * W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the quaternion
|
||||
/// </summary>
|
||||
public void Normalize()
|
||||
{
|
||||
this = Normalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a quaternion object from a byte array
|
||||
/// </summary>
|
||||
/// <param name="byteArray">The source byte array</param>
|
||||
/// <param name="pos">Offset in the byte array to start reading at</param>
|
||||
/// <param name="normalized">Whether the source data is normalized or
|
||||
/// not. If this is true 12 bytes will be read, otherwise 16 bytes will
|
||||
/// be read.</param>
|
||||
public void FromBytes(byte[] byteArray, int pos, bool normalized)
|
||||
{
|
||||
if (!normalized)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
{
|
||||
// Big endian architecture
|
||||
byte[] conversionBuffer = new byte[16];
|
||||
|
||||
Buffer.BlockCopy(byteArray, pos, conversionBuffer, 0, 16);
|
||||
|
||||
Array.Reverse(conversionBuffer, 0, 4);
|
||||
Array.Reverse(conversionBuffer, 4, 4);
|
||||
Array.Reverse(conversionBuffer, 8, 4);
|
||||
Array.Reverse(conversionBuffer, 12, 4);
|
||||
|
||||
X = BitConverter.ToSingle(conversionBuffer, 0);
|
||||
Y = BitConverter.ToSingle(conversionBuffer, 4);
|
||||
Z = BitConverter.ToSingle(conversionBuffer, 8);
|
||||
W = BitConverter.ToSingle(conversionBuffer, 12);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Little endian architecture
|
||||
X = BitConverter.ToSingle(byteArray, pos);
|
||||
Y = BitConverter.ToSingle(byteArray, pos + 4);
|
||||
Z = BitConverter.ToSingle(byteArray, pos + 8);
|
||||
W = BitConverter.ToSingle(byteArray, pos + 12);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
{
|
||||
// Big endian architecture
|
||||
byte[] conversionBuffer = new byte[16];
|
||||
|
||||
Buffer.BlockCopy(byteArray, pos, conversionBuffer, 0, 12);
|
||||
|
||||
Array.Reverse(conversionBuffer, 0, 4);
|
||||
Array.Reverse(conversionBuffer, 4, 4);
|
||||
Array.Reverse(conversionBuffer, 8, 4);
|
||||
|
||||
X = BitConverter.ToSingle(conversionBuffer, 0);
|
||||
Y = BitConverter.ToSingle(conversionBuffer, 4);
|
||||
Z = BitConverter.ToSingle(conversionBuffer, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Little endian architecture
|
||||
X = BitConverter.ToSingle(byteArray, pos);
|
||||
Y = BitConverter.ToSingle(byteArray, pos + 4);
|
||||
Z = BitConverter.ToSingle(byteArray, pos + 8);
|
||||
}
|
||||
|
||||
float xyzsum = 1f - X * X - Y * Y - Z * Z;
|
||||
W = (xyzsum > 0f) ? (float)Math.Sqrt(xyzsum) : 0f;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalize this quaternion and serialize it to a byte array
|
||||
/// </summary>
|
||||
/// <returns>A 12 byte array containing normalized X, Y, and Z floating
|
||||
/// point values in order using little endian byte ordering</returns>
|
||||
public byte[] GetBytes()
|
||||
{
|
||||
byte[] bytes = new byte[12];
|
||||
ToBytes(bytes, 0);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the raw bytes for this quaternion to a byte array
|
||||
/// </summary>
|
||||
/// <param name="dest">Destination byte array</param>
|
||||
/// <param name="pos">Position in the destination array to start
|
||||
/// writing. Must be at least 12 bytes before the end of the array</param>
|
||||
public void ToBytes(byte[] dest, int pos)
|
||||
{
|
||||
float norm = (float)Math.Sqrt(X * X + Y * Y + Z * Z + W * W);
|
||||
|
||||
if (norm != 0f)
|
||||
{
|
||||
norm = 1f / norm;
|
||||
|
||||
float x, y, z;
|
||||
if (W >= 0f)
|
||||
{
|
||||
x = X; y = Y; z = Z;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = -X; y = -Y; z = -Z;
|
||||
}
|
||||
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(norm * x), 0, dest, pos + 0, 4);
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(norm * y), 0, dest, pos + 4, 4);
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(norm * z), 0, dest, pos + 8, 4);
|
||||
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
{
|
||||
Array.Reverse(dest, pos + 0, 4);
|
||||
Array.Reverse(dest, pos + 4, 4);
|
||||
Array.Reverse(dest, pos + 8, 4);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(String.Format(
|
||||
"Quaternion {0} normalized to zero", ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert this quaternion to euler angles
|
||||
/// </summary>
|
||||
/// <param name="roll">X euler angle</param>
|
||||
/// <param name="pitch">Y euler angle</param>
|
||||
/// <param name="yaw">Z euler angle</param>
|
||||
public void GetEulerAngles(out float roll, out float pitch, out float yaw)
|
||||
{
|
||||
roll = 0f;
|
||||
pitch = 0f;
|
||||
yaw = 0f;
|
||||
|
||||
Quaternion t = new Quaternion(this.X * this.X, this.Y * this.Y, this.Z * this.Z, this.W * this.W);
|
||||
|
||||
float m = (t.X + t.Y + t.Z + t.W);
|
||||
if (Math.Abs(m) < 0.001d) return;
|
||||
float n = 2 * (this.Y * this.W + this.X * this.Z);
|
||||
float p = m * m - n * n;
|
||||
|
||||
if (p > 0f)
|
||||
{
|
||||
roll = (float)Math.Atan2(2.0f * (this.X * this.W - this.Y * this.Z), (-t.X - t.Y + t.Z + t.W));
|
||||
pitch = (float)Math.Atan2(n, Math.Sqrt(p));
|
||||
yaw = (float)Math.Atan2(2.0f * (this.Z * this.W - this.X * this.Y), t.X - t.Y - t.Z + t.W);
|
||||
}
|
||||
else if (n > 0f)
|
||||
{
|
||||
roll = 0f;
|
||||
pitch = (float)(Math.PI / 2d);
|
||||
yaw = (float)Math.Atan2((this.Z * this.W + this.X * this.Y), 0.5f - t.X - t.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
roll = 0f;
|
||||
pitch = -(float)(Math.PI / 2d);
|
||||
yaw = (float)Math.Atan2((this.Z * this.W + this.X * this.Y), 0.5f - t.X - t.Z);
|
||||
}
|
||||
|
||||
//float sqx = X * X;
|
||||
//float sqy = Y * Y;
|
||||
//float sqz = Z * Z;
|
||||
//float sqw = W * W;
|
||||
|
||||
//// Unit will be a correction factor if the quaternion is not normalized
|
||||
//float unit = sqx + sqy + sqz + sqw;
|
||||
//double test = X * Y + Z * W;
|
||||
|
||||
//if (test > 0.499f * unit)
|
||||
//{
|
||||
// // Singularity at north pole
|
||||
// yaw = 2f * (float)Math.Atan2(X, W);
|
||||
// pitch = (float)Math.PI / 2f;
|
||||
// roll = 0f;
|
||||
//}
|
||||
//else if (test < -0.499f * unit)
|
||||
//{
|
||||
// // Singularity at south pole
|
||||
// yaw = -2f * (float)Math.Atan2(X, W);
|
||||
// pitch = -(float)Math.PI / 2f;
|
||||
// roll = 0f;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// yaw = (float)Math.Atan2(2f * Y * W - 2f * X * Z, sqx - sqy - sqz + sqw);
|
||||
// pitch = (float)Math.Asin(2f * test / unit);
|
||||
// roll = (float)Math.Atan2(2f * X * W - 2f * Y * Z, -sqx + sqy - sqz + sqw);
|
||||
//}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert this quaternion to an angle around an axis
|
||||
/// </summary>
|
||||
/// <param name="axis">Unit vector describing the axis</param>
|
||||
/// <param name="angle">Angle around the axis, in radians</param>
|
||||
public void GetAxisAngle(out Vector3 axis, out float angle)
|
||||
{
|
||||
Quaternion q = Normalize(this);
|
||||
|
||||
float sin = (float)Math.Sqrt(1.0f - q.W * q.W);
|
||||
if (sin >= 0.001)
|
||||
{
|
||||
float invSin = 1.0f / sin;
|
||||
if (q.W < 0) invSin = -invSin;
|
||||
axis = new Vector3(q.X, q.Y, q.Z) * invSin;
|
||||
|
||||
angle = 2.0f * (float)Math.Acos(q.W);
|
||||
if (angle > Math.PI)
|
||||
angle = 2.0f * (float)Math.PI - angle;
|
||||
}
|
||||
else
|
||||
{
|
||||
axis = Vector3.UnitX;
|
||||
angle = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Static Methods
|
||||
|
||||
public static Quaternion Add(Quaternion quaternion1, Quaternion quaternion2)
|
||||
{
|
||||
quaternion1.X += quaternion2.X;
|
||||
quaternion1.Y += quaternion2.Y;
|
||||
quaternion1.Z += quaternion2.Z;
|
||||
quaternion1.W += quaternion2.W;
|
||||
return quaternion1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the conjugate (spatial inverse) of a quaternion
|
||||
/// </summary>
|
||||
public static Quaternion Conjugate(Quaternion quaternion)
|
||||
{
|
||||
quaternion.X = -quaternion.X;
|
||||
quaternion.Y = -quaternion.Y;
|
||||
quaternion.Z = -quaternion.Z;
|
||||
return quaternion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build a quaternion from an axis and an angle of rotation around
|
||||
/// that axis
|
||||
/// </summary>
|
||||
public static Quaternion CreateFromAxisAngle(float axisX, float axisY, float axisZ, float angle)
|
||||
{
|
||||
Vector3 axis = new Vector3(axisX, axisY, axisZ);
|
||||
return CreateFromAxisAngle(axis, angle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build a quaternion from an axis and an angle of rotation around
|
||||
/// that axis
|
||||
/// </summary>
|
||||
/// <param name="axis">Axis of rotation</param>
|
||||
/// <param name="angle">Angle of rotation</param>
|
||||
public static Quaternion CreateFromAxisAngle(Vector3 axis, float angle)
|
||||
{
|
||||
Quaternion q;
|
||||
axis = Vector3.Normalize(axis);
|
||||
|
||||
angle *= 0.5f;
|
||||
float c = (float)Math.Cos(angle);
|
||||
float s = (float)Math.Sin(angle);
|
||||
|
||||
q.X = axis.X * s;
|
||||
q.Y = axis.Y * s;
|
||||
q.Z = axis.Z * s;
|
||||
q.W = c;
|
||||
|
||||
return Quaternion.Normalize(q);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a quaternion from a vector containing roll, pitch, and yaw
|
||||
/// in radians
|
||||
/// </summary>
|
||||
/// <param name="eulers">Vector representation of the euler angles in
|
||||
/// radians</param>
|
||||
/// <returns>Quaternion representation of the euler angles</returns>
|
||||
public static Quaternion CreateFromEulers(Vector3 eulers)
|
||||
{
|
||||
return CreateFromEulers(eulers.X, eulers.Y, eulers.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a quaternion from roll, pitch, and yaw euler angles in
|
||||
/// radians
|
||||
/// </summary>
|
||||
/// <param name="roll">X angle in radians</param>
|
||||
/// <param name="pitch">Y angle in radians</param>
|
||||
/// <param name="yaw">Z angle in radians</param>
|
||||
/// <returns>Quaternion representation of the euler angles</returns>
|
||||
public static Quaternion CreateFromEulers(float roll, float pitch, float yaw)
|
||||
{
|
||||
if (roll > Utils.TWO_PI || pitch > Utils.TWO_PI || yaw > Utils.TWO_PI)
|
||||
throw new ArgumentException("Euler angles must be in radians");
|
||||
|
||||
double atCos = Math.Cos(roll / 2f);
|
||||
double atSin = Math.Sin(roll / 2f);
|
||||
double leftCos = Math.Cos(pitch / 2f);
|
||||
double leftSin = Math.Sin(pitch / 2f);
|
||||
double upCos = Math.Cos(yaw / 2f);
|
||||
double upSin = Math.Sin(yaw / 2f);
|
||||
double atLeftCos = atCos * leftCos;
|
||||
double atLeftSin = atSin * leftSin;
|
||||
return new Quaternion(
|
||||
(float)(atSin * leftCos * upCos + atCos * leftSin * upSin),
|
||||
(float)(atCos * leftSin * upCos - atSin * leftCos * upSin),
|
||||
(float)(atLeftCos * upSin + atLeftSin * upCos),
|
||||
(float)(atLeftCos * upCos - atLeftSin * upSin)
|
||||
);
|
||||
}
|
||||
|
||||
public static Quaternion CreateFromRotationMatrix(Matrix4 matrix)
|
||||
{
|
||||
float num8 = (matrix.M11 + matrix.M22) + matrix.M33;
|
||||
Quaternion quaternion = new Quaternion();
|
||||
if (num8 > 0f)
|
||||
{
|
||||
float num = (float)Math.Sqrt((double)(num8 + 1f));
|
||||
quaternion.W = num * 0.5f;
|
||||
num = 0.5f / num;
|
||||
quaternion.X = (matrix.M23 - matrix.M32) * num;
|
||||
quaternion.Y = (matrix.M31 - matrix.M13) * num;
|
||||
quaternion.Z = (matrix.M12 - matrix.M21) * num;
|
||||
return quaternion;
|
||||
}
|
||||
if ((matrix.M11 >= matrix.M22) && (matrix.M11 >= matrix.M33))
|
||||
{
|
||||
float num7 = (float)Math.Sqrt((double)(((1f + matrix.M11) - matrix.M22) - matrix.M33));
|
||||
float num4 = 0.5f / num7;
|
||||
quaternion.X = 0.5f * num7;
|
||||
quaternion.Y = (matrix.M12 + matrix.M21) * num4;
|
||||
quaternion.Z = (matrix.M13 + matrix.M31) * num4;
|
||||
quaternion.W = (matrix.M23 - matrix.M32) * num4;
|
||||
return quaternion;
|
||||
}
|
||||
if (matrix.M22 > matrix.M33)
|
||||
{
|
||||
float num6 = (float)Math.Sqrt((double)(((1f + matrix.M22) - matrix.M11) - matrix.M33));
|
||||
float num3 = 0.5f / num6;
|
||||
quaternion.X = (matrix.M21 + matrix.M12) * num3;
|
||||
quaternion.Y = 0.5f * num6;
|
||||
quaternion.Z = (matrix.M32 + matrix.M23) * num3;
|
||||
quaternion.W = (matrix.M31 - matrix.M13) * num3;
|
||||
return quaternion;
|
||||
}
|
||||
float num5 = (float)Math.Sqrt((double)(((1f + matrix.M33) - matrix.M11) - matrix.M22));
|
||||
float num2 = 0.5f / num5;
|
||||
quaternion.X = (matrix.M31 + matrix.M13) * num2;
|
||||
quaternion.Y = (matrix.M32 + matrix.M23) * num2;
|
||||
quaternion.Z = 0.5f * num5;
|
||||
quaternion.W = (matrix.M12 - matrix.M21) * num2;
|
||||
|
||||
return quaternion;
|
||||
}
|
||||
|
||||
public static Quaternion Divide(Quaternion q1, Quaternion q2)
|
||||
{
|
||||
return Quaternion.Inverse(q1) * q2;
|
||||
}
|
||||
|
||||
public static float Dot(Quaternion q1, Quaternion q2)
|
||||
{
|
||||
return (q1.X * q2.X) + (q1.Y * q2.Y) + (q1.Z * q2.Z) + (q1.W * q2.W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Conjugates and renormalizes a vector
|
||||
/// </summary>
|
||||
public static Quaternion Inverse(Quaternion quaternion)
|
||||
{
|
||||
float norm = quaternion.LengthSquared();
|
||||
|
||||
if (norm == 0f)
|
||||
{
|
||||
quaternion.X = quaternion.Y = quaternion.Z = quaternion.W = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
float oonorm = 1f / norm;
|
||||
quaternion = Conjugate(quaternion);
|
||||
|
||||
quaternion.X *= oonorm;
|
||||
quaternion.Y *= oonorm;
|
||||
quaternion.Z *= oonorm;
|
||||
quaternion.W *= oonorm;
|
||||
}
|
||||
|
||||
return quaternion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spherical linear interpolation between two quaternions
|
||||
/// </summary>
|
||||
public static Quaternion Slerp(Quaternion q1, Quaternion q2, float amount)
|
||||
{
|
||||
float angle = Dot(q1, q2);
|
||||
|
||||
if (angle < 0f)
|
||||
{
|
||||
q1 *= -1f;
|
||||
angle *= -1f;
|
||||
}
|
||||
|
||||
float scale;
|
||||
float invscale;
|
||||
|
||||
if ((angle + 1f) > 0.05f)
|
||||
{
|
||||
if ((1f - angle) >= 0.05f)
|
||||
{
|
||||
// slerp
|
||||
float theta = (float)Math.Acos(angle);
|
||||
float invsintheta = 1f / (float)Math.Sin(theta);
|
||||
scale = (float)Math.Sin(theta * (1f - amount)) * invsintheta;
|
||||
invscale = (float)Math.Sin(theta * amount) * invsintheta;
|
||||
}
|
||||
else
|
||||
{
|
||||
// lerp
|
||||
scale = 1f - amount;
|
||||
invscale = amount;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
q2.X = -q1.Y;
|
||||
q2.Y = q1.X;
|
||||
q2.Z = -q1.W;
|
||||
q2.W = q1.Z;
|
||||
|
||||
scale = (float)Math.Sin(Utils.PI * (0.5f - amount));
|
||||
invscale = (float)Math.Sin(Utils.PI * amount);
|
||||
}
|
||||
|
||||
return (q1 * scale) + (q2 * invscale);
|
||||
}
|
||||
|
||||
public static Quaternion Subtract(Quaternion quaternion1, Quaternion quaternion2)
|
||||
{
|
||||
quaternion1.X -= quaternion2.X;
|
||||
quaternion1.Y -= quaternion2.Y;
|
||||
quaternion1.Z -= quaternion2.Z;
|
||||
quaternion1.W -= quaternion2.W;
|
||||
return quaternion1;
|
||||
}
|
||||
|
||||
public static Quaternion Multiply(Quaternion a, Quaternion b)
|
||||
{
|
||||
return new Quaternion(
|
||||
a.W * b.X + a.X * b.W + a.Y * b.Z - a.Z * b.Y,
|
||||
a.W * b.Y + a.Y * b.W + a.Z * b.X - a.X * b.Z,
|
||||
a.W * b.Z + a.Z * b.W + a.X * b.Y - a.Y * b.X,
|
||||
a.W * b.W - a.X * b.X - a.Y * b.Y - a.Z * b.Z
|
||||
);
|
||||
}
|
||||
|
||||
public static Quaternion Multiply(Quaternion quaternion, float scaleFactor)
|
||||
{
|
||||
quaternion.X *= scaleFactor;
|
||||
quaternion.Y *= scaleFactor;
|
||||
quaternion.Z *= scaleFactor;
|
||||
quaternion.W *= scaleFactor;
|
||||
return quaternion;
|
||||
}
|
||||
|
||||
public static Quaternion Negate(Quaternion quaternion)
|
||||
{
|
||||
quaternion.X = -quaternion.X;
|
||||
quaternion.Y = -quaternion.Y;
|
||||
quaternion.Z = -quaternion.Z;
|
||||
quaternion.W = -quaternion.W;
|
||||
return quaternion;
|
||||
}
|
||||
|
||||
public static Quaternion Normalize(Quaternion q)
|
||||
{
|
||||
const float MAG_THRESHOLD = 0.0000001f;
|
||||
float mag = q.Length();
|
||||
|
||||
// Catch very small rounding errors when normalizing
|
||||
if (mag > MAG_THRESHOLD)
|
||||
{
|
||||
float oomag = 1f / mag;
|
||||
q.X *= oomag;
|
||||
q.Y *= oomag;
|
||||
q.Z *= oomag;
|
||||
q.W *= oomag;
|
||||
}
|
||||
else
|
||||
{
|
||||
q.X = 0f;
|
||||
q.Y = 0f;
|
||||
q.Z = 0f;
|
||||
q.W = 1f;
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
public static Quaternion Parse(string val)
|
||||
{
|
||||
char[] splitChar = { ',' };
|
||||
string[] split = val.Replace("<", String.Empty).Replace(">", String.Empty).Split(splitChar);
|
||||
if (split.Length == 3)
|
||||
{
|
||||
return new Quaternion(
|
||||
float.Parse(split[0].Trim(), Utils.EnUsCulture),
|
||||
float.Parse(split[1].Trim(), Utils.EnUsCulture),
|
||||
float.Parse(split[2].Trim(), Utils.EnUsCulture));
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Quaternion(
|
||||
float.Parse(split[0].Trim(), Utils.EnUsCulture),
|
||||
float.Parse(split[1].Trim(), Utils.EnUsCulture),
|
||||
float.Parse(split[2].Trim(), Utils.EnUsCulture),
|
||||
float.Parse(split[3].Trim(), Utils.EnUsCulture));
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryParse(string val, out Quaternion result)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = Parse(val);
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
result = new Quaternion();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Static Methods
|
||||
|
||||
#region Overrides
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return (obj is Quaternion) ? this == (Quaternion)obj : false;
|
||||
}
|
||||
|
||||
public bool Equals(Quaternion other)
|
||||
{
|
||||
return W == other.W
|
||||
&& X == other.X
|
||||
&& Y == other.Y
|
||||
&& Z == other.Z;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode() ^ W.GetHashCode());
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format(Utils.EnUsCulture, "<{0}, {1}, {2}, {3}>", X, Y, Z, W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a string representation of the quaternion elements with up to three
|
||||
/// decimal digits and separated by spaces only
|
||||
/// </summary>
|
||||
/// <returns>Raw string representation of the quaternion</returns>
|
||||
public string ToRawString()
|
||||
{
|
||||
CultureInfo enUs = new CultureInfo("en-us");
|
||||
enUs.NumberFormat.NumberDecimalDigits = 3;
|
||||
|
||||
return String.Format(enUs, "{0} {1} {2} {3}", X, Y, Z, W);
|
||||
}
|
||||
|
||||
#endregion Overrides
|
||||
|
||||
#region Operators
|
||||
|
||||
public static bool operator ==(Quaternion quaternion1, Quaternion quaternion2)
|
||||
{
|
||||
return quaternion1.Equals(quaternion2);
|
||||
}
|
||||
|
||||
public static bool operator !=(Quaternion quaternion1, Quaternion quaternion2)
|
||||
{
|
||||
return !(quaternion1 == quaternion2);
|
||||
}
|
||||
|
||||
public static Quaternion operator +(Quaternion quaternion1, Quaternion quaternion2)
|
||||
{
|
||||
return Add(quaternion1, quaternion2);
|
||||
}
|
||||
|
||||
public static Quaternion operator -(Quaternion quaternion)
|
||||
{
|
||||
return Negate(quaternion);
|
||||
}
|
||||
|
||||
public static Quaternion operator -(Quaternion quaternion1, Quaternion quaternion2)
|
||||
{
|
||||
return Subtract(quaternion1, quaternion2);
|
||||
}
|
||||
|
||||
public static Quaternion operator *(Quaternion a, Quaternion b)
|
||||
{
|
||||
return Multiply(a, b);
|
||||
}
|
||||
|
||||
public static Quaternion operator *(Quaternion quaternion, float scaleFactor)
|
||||
{
|
||||
return Multiply(quaternion, scaleFactor);
|
||||
}
|
||||
|
||||
public static Quaternion operator /(Quaternion quaternion1, Quaternion quaternion2)
|
||||
{
|
||||
return Divide(quaternion1, quaternion2);
|
||||
}
|
||||
|
||||
#endregion Operators
|
||||
|
||||
/// <summary>A quaternion with a value of 0,0,0,1</summary>
|
||||
public readonly static Quaternion Identity = new Quaternion(0f, 0f, 0f, 1f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
public struct Ray
|
||||
{
|
||||
public Vector3 Origin;
|
||||
public Vector3 Direction;
|
||||
|
||||
public Ray(Vector3 origin, Vector3 direction)
|
||||
{
|
||||
Origin = origin;
|
||||
Direction = direction;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,618 @@
|
||||
//
|
||||
// System.Threading.ReaderWriterLockSlim.cs
|
||||
//
|
||||
// Authors:
|
||||
// Miguel de Icaza (miguel@novell.com)
|
||||
// Dick Porter (dick@ximian.com)
|
||||
// Jackson Harper (jackson@ximian.com)
|
||||
// Lluis Sanchez Gual (lluis@ximian.com)
|
||||
// Marek Safar (marek.safar@gmail.com)
|
||||
//
|
||||
// Copyright 2004-2008 Novell, Inc (http://www.novell.com)
|
||||
// Copyright 2003, Ximian, Inc.
|
||||
//
|
||||
// NoRecursion code based on the blog post from Vance Morrison:
|
||||
// http://blogs.msdn.com/vancem/archive/2006/03/28/563180.aspx
|
||||
//
|
||||
// Recursion code based on Mono's implementation of ReaderWriterLock.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Permissions;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Threading;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
[Serializable]
|
||||
public class LockRecursionException : Exception
|
||||
{
|
||||
public LockRecursionException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public LockRecursionException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public LockRecursionException(string message, Exception e)
|
||||
: base(message, e)
|
||||
{
|
||||
}
|
||||
|
||||
protected LockRecursionException(SerializationInfo info, StreamingContext sc)
|
||||
: base(info, sc)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// This implementation is based on the light-weight
|
||||
// Reader/Writer lock sample from Vance Morrison's blog:
|
||||
//
|
||||
// http://blogs.msdn.com/vancem/archive/2006/03/28/563180.aspx
|
||||
//
|
||||
// And in Mono's ReaderWriterLock
|
||||
//
|
||||
[HostProtectionAttribute(SecurityAction.LinkDemand, MayLeakOnAbort = true)]
|
||||
[HostProtectionAttribute(SecurityAction.LinkDemand, Synchronization = true, ExternalThreading = true)]
|
||||
public class ReaderWriterLockSlim : IDisposable
|
||||
{
|
||||
sealed class LockDetails
|
||||
{
|
||||
public int ThreadId;
|
||||
public int ReadLocks;
|
||||
}
|
||||
|
||||
// Are we on a multiprocessor?
|
||||
static readonly bool smp;
|
||||
|
||||
// Lock specifiation for myLock: This lock protects exactly the local fields associted
|
||||
// instance of MyReaderWriterLock. It does NOT protect the memory associted with the
|
||||
// the events that hang off this lock (eg writeEvent, readEvent upgradeEvent).
|
||||
int myLock;
|
||||
|
||||
// Who owns the lock owners > 0 => readers
|
||||
// owners = -1 means there is one writer, Owners must be >= -1.
|
||||
int owners;
|
||||
Thread upgradable_thread;
|
||||
Thread write_thread;
|
||||
|
||||
// These variables allow use to avoid Setting events (which is expensive) if we don't have to.
|
||||
uint numWriteWaiters; // maximum number of threads that can be doing a WaitOne on the writeEvent
|
||||
uint numReadWaiters; // maximum number of threads that can be doing a WaitOne on the readEvent
|
||||
uint numUpgradeWaiters; // maximum number of threads that can be doing a WaitOne on the upgradeEvent (at most 1).
|
||||
|
||||
// conditions we wait on.
|
||||
EventWaitHandle writeEvent; // threads waiting to aquire a write lock go here.
|
||||
EventWaitHandle readEvent; // threads waiting to aquire a read lock go here (will be released in bulk)
|
||||
EventWaitHandle upgradeEvent; // thread waiting to upgrade a read lock to a write lock go here (at most one)
|
||||
|
||||
//int lock_owner;
|
||||
|
||||
// Only set if we are a recursive lock
|
||||
//Dictionary<int,int> reader_locks;
|
||||
|
||||
LockDetails[] read_locks = new LockDetails[8];
|
||||
|
||||
static ReaderWriterLockSlim()
|
||||
{
|
||||
smp = Environment.ProcessorCount > 1;
|
||||
}
|
||||
|
||||
public ReaderWriterLockSlim()
|
||||
{
|
||||
// NoRecursion (0) is the default value
|
||||
}
|
||||
|
||||
public void EnterReadLock()
|
||||
{
|
||||
TryEnterReadLock(-1);
|
||||
}
|
||||
|
||||
public bool TryEnterReadLock(int millisecondsTimeout)
|
||||
{
|
||||
if (millisecondsTimeout < Timeout.Infinite)
|
||||
throw new ArgumentOutOfRangeException("millisecondsTimeout");
|
||||
|
||||
if (read_locks == null)
|
||||
throw new ObjectDisposedException(null);
|
||||
|
||||
if (Thread.CurrentThread == write_thread)
|
||||
throw new LockRecursionException("Read lock cannot be acquired while write lock is held");
|
||||
|
||||
EnterMyLock();
|
||||
|
||||
LockDetails ld = GetReadLockDetails(Thread.CurrentThread.ManagedThreadId, true);
|
||||
if (ld.ReadLocks != 0)
|
||||
{
|
||||
ExitMyLock();
|
||||
throw new LockRecursionException("Recursive read lock can only be aquired in SupportsRecursion mode");
|
||||
}
|
||||
++ld.ReadLocks;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Easy case, no contention
|
||||
// owners >= 0 means there might be readers (but no writer)
|
||||
if (owners >= 0 && numWriteWaiters == 0)
|
||||
{
|
||||
owners++;
|
||||
break;
|
||||
}
|
||||
|
||||
// If the request is to probe.
|
||||
if (millisecondsTimeout == 0)
|
||||
{
|
||||
ExitMyLock();
|
||||
return false;
|
||||
}
|
||||
|
||||
// We need to wait. Mark that we have waiters and wait.
|
||||
if (readEvent == null)
|
||||
{
|
||||
LazyCreateEvent(ref readEvent, false);
|
||||
// since we left the lock, start over.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!WaitOnEvent(readEvent, ref numReadWaiters, millisecondsTimeout))
|
||||
return false;
|
||||
}
|
||||
ExitMyLock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryEnterReadLock(TimeSpan timeout)
|
||||
{
|
||||
return TryEnterReadLock(CheckTimeout(timeout));
|
||||
}
|
||||
|
||||
//
|
||||
// TODO: What to do if we are releasing a ReadLock and we do not own it?
|
||||
//
|
||||
public void ExitReadLock()
|
||||
{
|
||||
EnterMyLock();
|
||||
|
||||
if (owners < 1)
|
||||
{
|
||||
ExitMyLock();
|
||||
throw new SynchronizationLockException("Releasing lock and no read lock taken");
|
||||
}
|
||||
|
||||
--owners;
|
||||
--GetReadLockDetails(Thread.CurrentThread.ManagedThreadId, false).ReadLocks;
|
||||
|
||||
ExitAndWakeUpAppropriateWaiters();
|
||||
}
|
||||
|
||||
public void EnterWriteLock()
|
||||
{
|
||||
TryEnterWriteLock(-1);
|
||||
}
|
||||
|
||||
public bool TryEnterWriteLock(int millisecondsTimeout)
|
||||
{
|
||||
if (millisecondsTimeout < Timeout.Infinite)
|
||||
throw new ArgumentOutOfRangeException("millisecondsTimeout");
|
||||
|
||||
if (read_locks == null)
|
||||
throw new ObjectDisposedException(null);
|
||||
|
||||
if (IsWriteLockHeld)
|
||||
throw new LockRecursionException();
|
||||
|
||||
EnterMyLock();
|
||||
|
||||
LockDetails ld = GetReadLockDetails(Thread.CurrentThread.ManagedThreadId, false);
|
||||
if (ld != null && ld.ReadLocks > 0)
|
||||
{
|
||||
ExitMyLock();
|
||||
throw new LockRecursionException("Write lock cannot be acquired while read lock is held");
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
// There is no contention, we are done
|
||||
if (owners == 0)
|
||||
{
|
||||
// Indicate that we have a writer
|
||||
owners = -1;
|
||||
write_thread = Thread.CurrentThread;
|
||||
break;
|
||||
}
|
||||
|
||||
// If we are the thread that took the Upgradable read lock
|
||||
if (owners == 1 && upgradable_thread == Thread.CurrentThread)
|
||||
{
|
||||
owners = -1;
|
||||
write_thread = Thread.CurrentThread;
|
||||
break;
|
||||
}
|
||||
|
||||
// If the request is to probe.
|
||||
if (millisecondsTimeout == 0)
|
||||
{
|
||||
ExitMyLock();
|
||||
return false;
|
||||
}
|
||||
|
||||
// We need to wait, figure out what kind of waiting.
|
||||
|
||||
if (upgradable_thread == Thread.CurrentThread)
|
||||
{
|
||||
// We are the upgradable thread, register our interest.
|
||||
|
||||
if (upgradeEvent == null)
|
||||
{
|
||||
LazyCreateEvent(ref upgradeEvent, false);
|
||||
|
||||
// since we left the lock, start over.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (numUpgradeWaiters > 0)
|
||||
{
|
||||
ExitMyLock();
|
||||
throw new ApplicationException("Upgrading lock to writer lock already in process, deadlock");
|
||||
}
|
||||
|
||||
if (!WaitOnEvent(upgradeEvent, ref numUpgradeWaiters, millisecondsTimeout))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (writeEvent == null)
|
||||
{
|
||||
LazyCreateEvent(ref writeEvent, true);
|
||||
|
||||
// since we left the lock, retry
|
||||
continue;
|
||||
}
|
||||
if (!WaitOnEvent(writeEvent, ref numWriteWaiters, millisecondsTimeout))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(owners == -1, "Owners is not -1");
|
||||
ExitMyLock();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryEnterWriteLock(TimeSpan timeout)
|
||||
{
|
||||
return TryEnterWriteLock(CheckTimeout(timeout));
|
||||
}
|
||||
|
||||
public void ExitWriteLock()
|
||||
{
|
||||
EnterMyLock();
|
||||
|
||||
if (owners != -1)
|
||||
{
|
||||
ExitMyLock();
|
||||
throw new SynchronizationLockException("Calling ExitWriterLock when no write lock is held");
|
||||
}
|
||||
|
||||
//Debug.Assert (numUpgradeWaiters > 0);
|
||||
write_thread = upgradable_thread = null;
|
||||
owners = 0;
|
||||
ExitAndWakeUpAppropriateWaiters();
|
||||
}
|
||||
|
||||
public void EnterUpgradeableReadLock()
|
||||
{
|
||||
TryEnterUpgradeableReadLock(-1);
|
||||
}
|
||||
|
||||
//
|
||||
// Taking the Upgradable read lock is like taking a read lock
|
||||
// but we limit it to a single upgradable at a time.
|
||||
//
|
||||
public bool TryEnterUpgradeableReadLock(int millisecondsTimeout)
|
||||
{
|
||||
if (millisecondsTimeout < Timeout.Infinite)
|
||||
throw new ArgumentOutOfRangeException("millisecondsTimeout");
|
||||
|
||||
if (read_locks == null)
|
||||
throw new ObjectDisposedException(null);
|
||||
|
||||
if (IsUpgradeableReadLockHeld)
|
||||
throw new LockRecursionException();
|
||||
|
||||
if (IsWriteLockHeld)
|
||||
throw new LockRecursionException();
|
||||
|
||||
EnterMyLock();
|
||||
while (true)
|
||||
{
|
||||
if (owners == 0 && numWriteWaiters == 0 && upgradable_thread == null)
|
||||
{
|
||||
owners++;
|
||||
upgradable_thread = Thread.CurrentThread;
|
||||
break;
|
||||
}
|
||||
|
||||
// If the request is to probe
|
||||
if (millisecondsTimeout == 0)
|
||||
{
|
||||
ExitMyLock();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (readEvent == null)
|
||||
{
|
||||
LazyCreateEvent(ref readEvent, false);
|
||||
// since we left the lock, start over.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!WaitOnEvent(readEvent, ref numReadWaiters, millisecondsTimeout))
|
||||
return false;
|
||||
}
|
||||
|
||||
ExitMyLock();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryEnterUpgradeableReadLock(TimeSpan timeout)
|
||||
{
|
||||
return TryEnterUpgradeableReadLock(CheckTimeout(timeout));
|
||||
}
|
||||
|
||||
public void ExitUpgradeableReadLock()
|
||||
{
|
||||
EnterMyLock();
|
||||
Debug.Assert(owners > 0, "Releasing an upgradable lock, but there was no reader!");
|
||||
--owners;
|
||||
upgradable_thread = null;
|
||||
ExitAndWakeUpAppropriateWaiters();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
read_locks = null;
|
||||
}
|
||||
|
||||
public bool IsReadLockHeld
|
||||
{
|
||||
get { return RecursiveReadCount != 0; }
|
||||
}
|
||||
|
||||
public bool IsWriteLockHeld
|
||||
{
|
||||
get { return RecursiveWriteCount != 0; }
|
||||
}
|
||||
|
||||
public bool IsUpgradeableReadLockHeld
|
||||
{
|
||||
get { return RecursiveUpgradeCount != 0; }
|
||||
}
|
||||
|
||||
public int CurrentReadCount
|
||||
{
|
||||
get { return owners & 0xFFFFFFF; }
|
||||
}
|
||||
|
||||
public int RecursiveReadCount
|
||||
{
|
||||
get
|
||||
{
|
||||
EnterMyLock();
|
||||
LockDetails ld = GetReadLockDetails(Thread.CurrentThread.ManagedThreadId, false);
|
||||
int count = ld == null ? 0 : ld.ReadLocks;
|
||||
ExitMyLock();
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
public int RecursiveUpgradeCount
|
||||
{
|
||||
get { return upgradable_thread == Thread.CurrentThread ? 1 : 0; }
|
||||
}
|
||||
|
||||
public int RecursiveWriteCount
|
||||
{
|
||||
get { return write_thread == Thread.CurrentThread ? 1 : 0; }
|
||||
}
|
||||
|
||||
public int WaitingReadCount
|
||||
{
|
||||
get { return (int)numReadWaiters; }
|
||||
}
|
||||
|
||||
public int WaitingUpgradeCount
|
||||
{
|
||||
get { return (int)numUpgradeWaiters; }
|
||||
}
|
||||
|
||||
public int WaitingWriteCount
|
||||
{
|
||||
get { return (int)numWriteWaiters; }
|
||||
}
|
||||
|
||||
#region Private methods
|
||||
void EnterMyLock()
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref myLock, 1, 0) != 0)
|
||||
EnterMyLockSpin();
|
||||
}
|
||||
|
||||
void EnterMyLockSpin()
|
||||
{
|
||||
|
||||
for (int i = 0; ; i++)
|
||||
{
|
||||
if (i < 3 && smp)
|
||||
Thread.SpinWait(20); // Wait a few dozen instructions to let another processor release lock.
|
||||
else
|
||||
Thread.Sleep(0); // Give up my quantum.
|
||||
|
||||
if (Interlocked.CompareExchange(ref myLock, 1, 0) == 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ExitMyLock()
|
||||
{
|
||||
Debug.Assert(myLock != 0, "Exiting spin lock that is not held");
|
||||
myLock = 0;
|
||||
}
|
||||
|
||||
bool MyLockHeld { get { return myLock != 0; } }
|
||||
|
||||
/// <summary>
|
||||
/// Determines the appropriate events to set, leaves the locks, and sets the events.
|
||||
/// </summary>
|
||||
private void ExitAndWakeUpAppropriateWaiters()
|
||||
{
|
||||
Debug.Assert(MyLockHeld);
|
||||
|
||||
// First a writing thread waiting on being upgraded
|
||||
if (owners == 1 && numUpgradeWaiters != 0)
|
||||
{
|
||||
// Exit before signaling to improve efficiency (wakee will need the lock)
|
||||
ExitMyLock();
|
||||
// release all upgraders (however there can be at most one).
|
||||
upgradeEvent.Set();
|
||||
//
|
||||
// TODO: What does the following comment mean?
|
||||
// two threads upgrading is a guarenteed deadlock, so we throw in that case.
|
||||
}
|
||||
else if (owners == 0 && numWriteWaiters > 0)
|
||||
{
|
||||
// Exit before signaling to improve efficiency (wakee will need the lock)
|
||||
ExitMyLock();
|
||||
// release one writer.
|
||||
writeEvent.Set();
|
||||
}
|
||||
else if (owners >= 0 && numReadWaiters != 0)
|
||||
{
|
||||
// Exit before signaling to improve efficiency (wakee will need the lock)
|
||||
ExitMyLock();
|
||||
// release all readers.
|
||||
readEvent.Set();
|
||||
}
|
||||
else
|
||||
ExitMyLock();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A routine for lazily creating a event outside the lock (so if errors
|
||||
/// happen they are outside the lock and that we don't do much work
|
||||
/// while holding a spin lock). If all goes well, reenter the lock and
|
||||
/// set 'waitEvent'
|
||||
/// </summary>
|
||||
void LazyCreateEvent(ref EventWaitHandle waitEvent, bool makeAutoResetEvent)
|
||||
{
|
||||
Debug.Assert(MyLockHeld);
|
||||
Debug.Assert(waitEvent == null);
|
||||
|
||||
ExitMyLock();
|
||||
EventWaitHandle newEvent;
|
||||
if (makeAutoResetEvent)
|
||||
newEvent = new AutoResetEvent(false);
|
||||
else
|
||||
newEvent = new ManualResetEvent(false);
|
||||
|
||||
EnterMyLock();
|
||||
|
||||
// maybe someone snuck in.
|
||||
if (waitEvent == null)
|
||||
waitEvent = newEvent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits on 'waitEvent' with a timeout of 'millisceondsTimeout.
|
||||
/// Before the wait 'numWaiters' is incremented and is restored before leaving this routine.
|
||||
/// </summary>
|
||||
bool WaitOnEvent(EventWaitHandle waitEvent, ref uint numWaiters, int millisecondsTimeout)
|
||||
{
|
||||
Debug.Assert(MyLockHeld);
|
||||
|
||||
waitEvent.Reset();
|
||||
numWaiters++;
|
||||
|
||||
bool waitSuccessful = false;
|
||||
|
||||
// Do the wait outside of any lock
|
||||
ExitMyLock();
|
||||
try
|
||||
{
|
||||
waitSuccessful = waitEvent.WaitOne(millisecondsTimeout, false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EnterMyLock();
|
||||
--numWaiters;
|
||||
if (!waitSuccessful)
|
||||
ExitMyLock();
|
||||
}
|
||||
return waitSuccessful;
|
||||
}
|
||||
|
||||
static int CheckTimeout(TimeSpan timeout)
|
||||
{
|
||||
try
|
||||
{
|
||||
return checked((int)timeout.TotalMilliseconds);
|
||||
}
|
||||
catch (System.OverflowException)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("timeout");
|
||||
}
|
||||
}
|
||||
|
||||
LockDetails GetReadLockDetails(int threadId, bool create)
|
||||
{
|
||||
int i;
|
||||
LockDetails ld;
|
||||
for (i = 0; i < read_locks.Length; ++i)
|
||||
{
|
||||
ld = read_locks[i];
|
||||
if (ld == null)
|
||||
break;
|
||||
|
||||
if (ld.ThreadId == threadId)
|
||||
return ld;
|
||||
}
|
||||
|
||||
if (!create)
|
||||
return null;
|
||||
|
||||
if (i == read_locks.Length)
|
||||
Array.Resize(ref read_locks, read_locks.Length * 2);
|
||||
|
||||
ld = read_locks[i] = new LockDetails();
|
||||
ld.ThreadId = threadId;
|
||||
return ld;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
public class ThreadSafeDictionary<TKey, TValue>
|
||||
{
|
||||
Dictionary<TKey, TValue> Dictionary;
|
||||
object syncObject = new object();
|
||||
|
||||
public ThreadSafeDictionary()
|
||||
{
|
||||
Dictionary = new Dictionary<TKey, TValue>();
|
||||
}
|
||||
|
||||
public ThreadSafeDictionary(int capacity)
|
||||
{
|
||||
Dictionary = new Dictionary<TKey, TValue>(capacity);
|
||||
}
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
lock (syncObject)
|
||||
{
|
||||
Dictionary[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
lock (syncObject)
|
||||
{
|
||||
return Dictionary.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (syncObject)
|
||||
{
|
||||
Dictionary.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return Dictionary.Count; }
|
||||
}
|
||||
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
return Dictionary.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
return Dictionary.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
public void ForEach(Action<TValue> action)
|
||||
{
|
||||
lock (syncObject)
|
||||
{
|
||||
foreach (TValue value in Dictionary.Values)
|
||||
action(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void ForEach(Action<KeyValuePair<TKey, TValue>> action)
|
||||
{
|
||||
lock (syncObject)
|
||||
{
|
||||
foreach (KeyValuePair<TKey, TValue> entry in Dictionary)
|
||||
action(entry);
|
||||
}
|
||||
}
|
||||
|
||||
public TValue FindValue(Predicate<TValue> predicate)
|
||||
{
|
||||
lock (syncObject)
|
||||
{
|
||||
foreach (TValue value in Dictionary.Values)
|
||||
{
|
||||
if (predicate(value))
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return default(TValue);
|
||||
}
|
||||
|
||||
public IList<TValue> FindAll(Predicate<TValue> predicate)
|
||||
{
|
||||
IList<TValue> list = new List<TValue>();
|
||||
|
||||
lock (syncObject)
|
||||
{
|
||||
foreach (TValue value in Dictionary.Values)
|
||||
{
|
||||
if (predicate(value))
|
||||
list.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public int RemoveAll(Predicate<TValue> predicate)
|
||||
{
|
||||
IList<TKey> list = new List<TKey>();
|
||||
|
||||
lock (syncObject)
|
||||
{
|
||||
foreach (KeyValuePair<TKey, TValue> kvp in Dictionary)
|
||||
{
|
||||
if (predicate(kvp.Value))
|
||||
list.Add(kvp.Key);
|
||||
}
|
||||
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
Dictionary.Remove(list[i]);
|
||||
}
|
||||
|
||||
return list.Count;
|
||||
}
|
||||
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get { return Dictionary[key]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
/// <summary>
|
||||
/// A hierarchical token bucket for bandwidth throttling. See
|
||||
/// http://en.wikipedia.org/wiki/Token_bucket for more information
|
||||
/// </summary>
|
||||
public class TokenBucket
|
||||
{
|
||||
/// <summary>Parent bucket to this bucket, or null if this is a root
|
||||
/// bucket</summary>
|
||||
TokenBucket parent;
|
||||
/// <summary>Size of the bucket in bytes. If zero, the bucket has
|
||||
/// infinite capacity</summary>
|
||||
int maxBurst;
|
||||
/// <summary>Rate that the bucket fills, in bytes per millisecond. If
|
||||
/// zero, the bucket always remains full</summary>
|
||||
int tokensPerMS;
|
||||
/// <summary>Number of tokens currently in the bucket</summary>
|
||||
int content;
|
||||
/// <summary>Time of the last drip, in system ticks</summary>
|
||||
int lastDrip;
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// The parent bucket of this bucket, or null if this bucket has no
|
||||
/// parent. The parent bucket will limit the aggregate bandwidth of all
|
||||
/// of its children buckets
|
||||
/// </summary>
|
||||
public TokenBucket Parent
|
||||
{
|
||||
get { return parent; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximum burst rate in bytes per second. This is the maximum number
|
||||
/// of tokens that can accumulate in the bucket at any one time
|
||||
/// </summary>
|
||||
public int MaxBurst
|
||||
{
|
||||
get { return maxBurst; }
|
||||
set { maxBurst = (value >= 0 ? value : 0); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The speed limit of this bucket in bytes per second. This is the
|
||||
/// number of tokens that are added to the bucket per second
|
||||
/// </summary>
|
||||
/// <remarks>Tokens are added to the bucket any time
|
||||
/// <seealso cref="RemoveTokens"/> is called, at the granularity of
|
||||
/// the system tick interval (typically around 15-22ms)</remarks>
|
||||
public int DripRate
|
||||
{
|
||||
get { return tokensPerMS * 1000; }
|
||||
set
|
||||
{
|
||||
if (value == 0)
|
||||
tokensPerMS = 0;
|
||||
else if (value / 1000 <= 0)
|
||||
tokensPerMS = 1; // 1 byte/ms is the minimum granularity
|
||||
else
|
||||
tokensPerMS = value / 1000;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The number of bytes that can be sent at this moment. This is the
|
||||
/// current number of tokens in the bucket
|
||||
/// <remarks>If this bucket has a parent bucket that does not have
|
||||
/// enough tokens for a request, <seealso cref="RemoveTokens"/> will
|
||||
/// return false regardless of the content of this bucket</remarks>
|
||||
/// </summary>
|
||||
public int Content
|
||||
{
|
||||
get { return content; }
|
||||
}
|
||||
|
||||
#endregion Properties
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent bucket if this is a child bucket, or
|
||||
/// null if this is a root bucket</param>
|
||||
/// <param name="maxBurst">Maximum size of the bucket in bytes, or
|
||||
/// zero if this bucket has no maximum capacity</param>
|
||||
/// <param name="dripRate">Rate that the bucket fills, in bytes per
|
||||
/// second. If zero, the bucket always remains full</param>
|
||||
public TokenBucket(TokenBucket parent, int maxBurst, int dripRate)
|
||||
{
|
||||
this.parent = parent;
|
||||
MaxBurst = maxBurst;
|
||||
DripRate = dripRate;
|
||||
lastDrip = Environment.TickCount & Int32.MaxValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a given number of tokens from the bucket
|
||||
/// </summary>
|
||||
/// <param name="amount">Number of tokens to remove from the bucket</param>
|
||||
/// <returns>True if the requested number of tokens were removed from
|
||||
/// the bucket, otherwise false</returns>
|
||||
public bool RemoveTokens(int amount)
|
||||
{
|
||||
bool dummy;
|
||||
return RemoveTokens(amount, out dummy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a given number of tokens from the bucket
|
||||
/// </summary>
|
||||
/// <param name="amount">Number of tokens to remove from the bucket</param>
|
||||
/// <param name="dripSucceeded">True if tokens were added to the bucket
|
||||
/// during this call, otherwise false</param>
|
||||
/// <returns>True if the requested number of tokens were removed from
|
||||
/// the bucket, otherwise false</returns>
|
||||
public bool RemoveTokens(int amount, out bool dripSucceeded)
|
||||
{
|
||||
if (maxBurst == 0)
|
||||
{
|
||||
dripSucceeded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
dripSucceeded = Drip();
|
||||
|
||||
if (content - amount >= 0)
|
||||
{
|
||||
if (parent != null && !parent.RemoveTokens(amount))
|
||||
return false;
|
||||
|
||||
content -= amount;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add tokens to the bucket over time. The number of tokens added each
|
||||
/// call depends on the length of time that has passed since the last
|
||||
/// call to Drip
|
||||
/// </summary>
|
||||
/// <returns>True if tokens were added to the bucket, otherwise false</returns>
|
||||
private bool Drip()
|
||||
{
|
||||
if (tokensPerMS == 0)
|
||||
{
|
||||
content = maxBurst;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int now = Environment.TickCount & Int32.MaxValue;
|
||||
int deltaMS = now - lastDrip;
|
||||
|
||||
if (deltaMS <= 0)
|
||||
{
|
||||
if (deltaMS < 0)
|
||||
lastDrip = now;
|
||||
return false;
|
||||
}
|
||||
|
||||
int dripAmount = deltaMS * tokensPerMS;
|
||||
|
||||
content = Math.Min(content + dripAmount, maxBurst);
|
||||
lastDrip = now;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
/// <summary>
|
||||
/// A 128-bit Universally Unique Identifier, used throughout the Second
|
||||
/// Life networking protocol
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct UUID : IComparable<UUID>, IEquatable<UUID>
|
||||
{
|
||||
/// <summary>The System.Guid object this struct wraps around</summary>
|
||||
public Guid Guid;
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that takes a string UUID representation
|
||||
/// </summary>
|
||||
/// <param name="val">A string representation of a UUID, case
|
||||
/// insensitive and can either be hyphenated or non-hyphenated</param>
|
||||
/// <example>UUID("11f8aa9c-b071-4242-836b-13b7abe0d489")</example>
|
||||
public UUID(string val)
|
||||
{
|
||||
if (String.IsNullOrEmpty(val))
|
||||
Guid = new Guid();
|
||||
else
|
||||
Guid = new Guid(val);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that takes a System.Guid object
|
||||
/// </summary>
|
||||
/// <param name="val">A Guid object that contains the unique identifier
|
||||
/// to be represented by this UUID</param>
|
||||
public UUID(Guid val)
|
||||
{
|
||||
Guid = val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that takes a byte array containing a UUID
|
||||
/// </summary>
|
||||
/// <param name="source">Byte array containing a 16 byte UUID</param>
|
||||
/// <param name="pos">Beginning offset in the array</param>
|
||||
public UUID(byte[] source, int pos)
|
||||
{
|
||||
Guid = UUID.Zero.Guid;
|
||||
FromBytes(source, pos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that takes an unsigned 64-bit unsigned integer to
|
||||
/// convert to a UUID
|
||||
/// </summary>
|
||||
/// <param name="val">64-bit unsigned integer to convert to a UUID</param>
|
||||
public UUID(ulong val)
|
||||
{
|
||||
byte[] end = BitConverter.GetBytes(val);
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Array.Reverse(end);
|
||||
|
||||
Guid = new Guid(0, 0, 0, end);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy constructor
|
||||
/// </summary>
|
||||
/// <param name="val">UUID to copy</param>
|
||||
public UUID(UUID val)
|
||||
{
|
||||
Guid = val.Guid;
|
||||
}
|
||||
|
||||
#endregion Constructors
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// IComparable.CompareTo implementation
|
||||
/// </summary>
|
||||
public int CompareTo(UUID id)
|
||||
{
|
||||
return Guid.CompareTo(id.Guid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns this UUID from 16 bytes out of a byte array
|
||||
/// </summary>
|
||||
/// <param name="source">Byte array containing the UUID to assign this UUID to</param>
|
||||
/// <param name="pos">Starting position of the UUID in the byte array</param>
|
||||
public void FromBytes(byte[] source, int pos)
|
||||
{
|
||||
int a = (source[pos + 0] << 24) | (source[pos + 1] << 16) | (source[pos + 2] << 8) | source[pos + 3];
|
||||
short b = (short)((source[pos + 4] << 8) | source[pos + 5]);
|
||||
short c = (short)((source[pos + 6] << 8) | source[pos + 7]);
|
||||
|
||||
Guid = new Guid(a, b, c, source[pos + 8], source[pos + 9], source[pos + 10], source[pos + 11],
|
||||
source[pos + 12], source[pos + 13], source[pos + 14], source[pos + 15]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the raw bytes for this UUID
|
||||
/// </summary>
|
||||
/// <returns>A 16 byte array containing this UUID</returns>
|
||||
public byte[] GetBytes()
|
||||
{
|
||||
byte[] output = new byte[16];
|
||||
ToBytes(output, 0);
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the raw bytes for this UUID to a byte array
|
||||
/// </summary>
|
||||
/// <param name="dest">Destination byte array</param>
|
||||
/// <param name="pos">Position in the destination array to start
|
||||
/// writing. Must be at least 16 bytes before the end of the array</param>
|
||||
public void ToBytes(byte[] dest, int pos)
|
||||
{
|
||||
byte[] bytes = Guid.ToByteArray();
|
||||
dest[pos + 0] = bytes[3];
|
||||
dest[pos + 1] = bytes[2];
|
||||
dest[pos + 2] = bytes[1];
|
||||
dest[pos + 3] = bytes[0];
|
||||
dest[pos + 4] = bytes[5];
|
||||
dest[pos + 5] = bytes[4];
|
||||
dest[pos + 6] = bytes[7];
|
||||
dest[pos + 7] = bytes[6];
|
||||
Buffer.BlockCopy(bytes, 8, dest, pos + 8, 8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate an LLCRC (cyclic redundancy check) for this UUID
|
||||
/// </summary>
|
||||
/// <returns>The CRC checksum for this UUID</returns>
|
||||
public uint CRC()
|
||||
{
|
||||
uint retval = 0;
|
||||
byte[] bytes = GetBytes();
|
||||
|
||||
retval += (uint)((bytes[3] << 24) + (bytes[2] << 16) + (bytes[1] << 8) + bytes[0]);
|
||||
retval += (uint)((bytes[7] << 24) + (bytes[6] << 16) + (bytes[5] << 8) + bytes[4]);
|
||||
retval += (uint)((bytes[11] << 24) + (bytes[10] << 16) + (bytes[9] << 8) + bytes[8]);
|
||||
retval += (uint)((bytes[15] << 24) + (bytes[14] << 16) + (bytes[13] << 8) + bytes[12]);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a 64-bit integer representation from the second half of this UUID
|
||||
/// </summary>
|
||||
/// <returns>An integer created from the last eight bytes of this UUID</returns>
|
||||
public ulong GetULong()
|
||||
{
|
||||
byte[] bytes = Guid.ToByteArray();
|
||||
|
||||
return (ulong)
|
||||
((ulong)bytes[8] +
|
||||
((ulong)bytes[9] << 8) +
|
||||
((ulong)bytes[10] << 16) +
|
||||
((ulong)bytes[12] << 24) +
|
||||
((ulong)bytes[13] << 32) +
|
||||
((ulong)bytes[13] << 40) +
|
||||
((ulong)bytes[14] << 48) +
|
||||
((ulong)bytes[15] << 56));
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Static Methods
|
||||
|
||||
/// <summary>
|
||||
/// Generate a UUID from a string
|
||||
/// </summary>
|
||||
/// <param name="val">A string representation of a UUID, case
|
||||
/// insensitive and can either be hyphenated or non-hyphenated</param>
|
||||
/// <example>UUID.Parse("11f8aa9c-b071-4242-836b-13b7abe0d489")</example>
|
||||
public static UUID Parse(string val)
|
||||
{
|
||||
return new UUID(val);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a UUID from a string
|
||||
/// </summary>
|
||||
/// <param name="val">A string representation of a UUID, case
|
||||
/// insensitive and can either be hyphenated or non-hyphenated</param>
|
||||
/// <param name="result">Will contain the parsed UUID if successful,
|
||||
/// otherwise null</param>
|
||||
/// <returns>True if the string was successfully parse, otherwise false</returns>
|
||||
/// <example>UUID.TryParse("11f8aa9c-b071-4242-836b-13b7abe0d489", result)</example>
|
||||
public static bool TryParse(string val, out UUID result)
|
||||
{
|
||||
if (String.IsNullOrEmpty(val) ||
|
||||
(val[0] == '{' && val.Length != 38) ||
|
||||
(val.Length != 36 && val.Length != 32))
|
||||
{
|
||||
result = UUID.Zero;
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
result = Parse(val);
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
result = UUID.Zero;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combine two UUIDs together by taking the MD5 hash of a byte array
|
||||
/// containing both UUIDs
|
||||
/// </summary>
|
||||
/// <param name="first">First UUID to combine</param>
|
||||
/// <param name="second">Second UUID to combine</param>
|
||||
/// <returns>The UUID product of the combination</returns>
|
||||
public static UUID Combine(UUID first, UUID second)
|
||||
{
|
||||
// Construct the buffer that MD5ed
|
||||
byte[] input = new byte[32];
|
||||
Buffer.BlockCopy(first.GetBytes(), 0, input, 0, 16);
|
||||
Buffer.BlockCopy(second.GetBytes(), 0, input, 16, 16);
|
||||
|
||||
return new UUID(Utils.MD5(input), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static UUID Random()
|
||||
{
|
||||
return new UUID(Guid.NewGuid());
|
||||
}
|
||||
|
||||
#endregion Static Methods
|
||||
|
||||
#region Overrides
|
||||
|
||||
/// <summary>
|
||||
/// Return a hash code for this UUID, used by .NET for hash tables
|
||||
/// </summary>
|
||||
/// <returns>An integer composed of all the UUID bytes XORed together</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Guid.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Comparison function
|
||||
/// </summary>
|
||||
/// <param name="o">An object to compare to this UUID</param>
|
||||
/// <returns>True if the object is a UUID and both UUIDs are equal</returns>
|
||||
public override bool Equals(object o)
|
||||
{
|
||||
if (!(o is UUID)) return false;
|
||||
|
||||
UUID uuid = (UUID)o;
|
||||
return Guid == uuid.Guid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Comparison function
|
||||
/// </summary>
|
||||
/// <param name="uuid">UUID to compare to</param>
|
||||
/// <returns>True if the UUIDs are equal, otherwise false</returns>
|
||||
public bool Equals(UUID uuid)
|
||||
{
|
||||
return Guid == uuid.Guid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a hyphenated string representation of this UUID
|
||||
/// </summary>
|
||||
/// <returns>A string representation of this UUID, lowercase and
|
||||
/// with hyphens</returns>
|
||||
/// <example>11f8aa9c-b071-4242-836b-13b7abe0d489</example>
|
||||
public override string ToString()
|
||||
{
|
||||
if (Guid == Guid.Empty)
|
||||
return ZeroString;
|
||||
else
|
||||
return Guid.ToString();
|
||||
}
|
||||
|
||||
#endregion Overrides
|
||||
|
||||
#region Operators
|
||||
|
||||
/// <summary>
|
||||
/// Equals operator
|
||||
/// </summary>
|
||||
/// <param name="lhs">First UUID for comparison</param>
|
||||
/// <param name="rhs">Second UUID for comparison</param>
|
||||
/// <returns>True if the UUIDs are byte for byte equal, otherwise false</returns>
|
||||
public static bool operator ==(UUID lhs, UUID rhs)
|
||||
{
|
||||
return lhs.Guid == rhs.Guid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not equals operator
|
||||
/// </summary>
|
||||
/// <param name="lhs">First UUID for comparison</param>
|
||||
/// <param name="rhs">Second UUID for comparison</param>
|
||||
/// <returns>True if the UUIDs are not equal, otherwise true</returns>
|
||||
public static bool operator !=(UUID lhs, UUID rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// XOR operator
|
||||
/// </summary>
|
||||
/// <param name="lhs">First UUID</param>
|
||||
/// <param name="rhs">Second UUID</param>
|
||||
/// <returns>A UUID that is a XOR combination of the two input UUIDs</returns>
|
||||
public static UUID operator ^(UUID lhs, UUID rhs)
|
||||
{
|
||||
byte[] lhsbytes = lhs.GetBytes();
|
||||
byte[] rhsbytes = rhs.GetBytes();
|
||||
byte[] output = new byte[16];
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
output[i] = (byte)(lhsbytes[i] ^ rhsbytes[i]);
|
||||
}
|
||||
|
||||
return new UUID(output, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// String typecasting operator
|
||||
/// </summary>
|
||||
/// <param name="val">A UUID in string form. Case insensitive,
|
||||
/// hyphenated or non-hyphenated</param>
|
||||
/// <returns>A UUID built from the string representation</returns>
|
||||
public static explicit operator UUID(string val)
|
||||
{
|
||||
return new UUID(val);
|
||||
}
|
||||
|
||||
#endregion Operators
|
||||
|
||||
/// <summary>An UUID with a value of all zeroes</summary>
|
||||
public static readonly UUID Zero = new UUID();
|
||||
|
||||
/// <summary>A cache of UUID.Zero as a string to optimize a common path</summary>
|
||||
private static readonly string ZeroString = Guid.Empty.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,440 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
public static partial class Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Operating system
|
||||
/// </summary>
|
||||
public enum Platform
|
||||
{
|
||||
/// <summary>Unknown</summary>
|
||||
Unknown,
|
||||
/// <summary>Microsoft Windows</summary>
|
||||
Windows,
|
||||
/// <summary>Microsoft Windows CE</summary>
|
||||
WindowsCE,
|
||||
/// <summary>Linux</summary>
|
||||
Linux,
|
||||
/// <summary>Apple OSX</summary>
|
||||
OSX
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runtime platform
|
||||
/// </summary>
|
||||
public enum Runtime
|
||||
{
|
||||
/// <summary>.NET runtime</summary>
|
||||
Windows,
|
||||
/// <summary>Mono runtime: http://www.mono-project.com/</summary>
|
||||
Mono
|
||||
}
|
||||
|
||||
public const float E = (float)Math.E;
|
||||
public const float LOG10E = 0.4342945f;
|
||||
public const float LOG2E = 1.442695f;
|
||||
public const float PI = (float)Math.PI;
|
||||
public const float TWO_PI = (float)(Math.PI * 2.0d);
|
||||
public const float PI_OVER_TWO = (float)(Math.PI / 2.0d);
|
||||
public const float PI_OVER_FOUR = (float)(Math.PI / 4.0d);
|
||||
/// <summary>Used for converting degrees to radians</summary>
|
||||
public const float DEG_TO_RAD = (float)(Math.PI / 180.0d);
|
||||
/// <summary>Used for converting radians to degrees</summary>
|
||||
public const float RAD_TO_DEG = (float)(180.0d / Math.PI);
|
||||
|
||||
/// <summary>Provide a single instance of the CultureInfo class to
|
||||
/// help parsing in situations where the grid assumes an en-us
|
||||
/// culture</summary>
|
||||
public static readonly System.Globalization.CultureInfo EnUsCulture =
|
||||
new System.Globalization.CultureInfo("en-us");
|
||||
|
||||
/// <summary>UNIX epoch in DateTime format</summary>
|
||||
public static readonly DateTime Epoch = new DateTime(1970, 1, 1);
|
||||
|
||||
public static readonly byte[] EmptyBytes = new byte[0];
|
||||
|
||||
/// <summary>Provide a single instance of the MD5 class to avoid making
|
||||
/// duplicate copies and handle thread safety</summary>
|
||||
private static readonly System.Security.Cryptography.MD5 MD5Builder =
|
||||
new System.Security.Cryptography.MD5CryptoServiceProvider();
|
||||
|
||||
/// <summary>Provide a single instance of the SHA-1 class to avoid
|
||||
/// making duplicate copies and handle thread safety</summary>
|
||||
private static readonly System.Security.Cryptography.SHA1 SHA1Builder =
|
||||
new System.Security.Cryptography.SHA1CryptoServiceProvider();
|
||||
|
||||
private static readonly System.Security.Cryptography.SHA256 SHA256Builder =
|
||||
new System.Security.Cryptography.SHA256Managed();
|
||||
|
||||
/// <summary>Provide a single instance of a random number generator
|
||||
/// to avoid making duplicate copies and handle thread safety</summary>
|
||||
private static readonly Random RNG = new Random();
|
||||
|
||||
#region Math
|
||||
|
||||
/// <summary>
|
||||
/// Clamp a given value between a range
|
||||
/// </summary>
|
||||
/// <param name="value">Value to clamp</param>
|
||||
/// <param name="min">Minimum allowable value</param>
|
||||
/// <param name="max">Maximum allowable value</param>
|
||||
/// <returns>A value inclusively between lower and upper</returns>
|
||||
public static float Clamp(float value, float min, float max)
|
||||
{
|
||||
// First we check to see if we're greater than the max
|
||||
value = (value > max) ? max : value;
|
||||
|
||||
// Then we check to see if we're less than the min.
|
||||
value = (value < min) ? min : value;
|
||||
|
||||
// There's no check to see if min > max.
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamp a given value between a range
|
||||
/// </summary>
|
||||
/// <param name="value">Value to clamp</param>
|
||||
/// <param name="min">Minimum allowable value</param>
|
||||
/// <param name="max">Maximum allowable value</param>
|
||||
/// <returns>A value inclusively between lower and upper</returns>
|
||||
public static double Clamp(double value, double min, double max)
|
||||
{
|
||||
// First we check to see if we're greater than the max
|
||||
value = (value > max) ? max : value;
|
||||
|
||||
// Then we check to see if we're less than the min.
|
||||
value = (value < min) ? min : value;
|
||||
|
||||
// There's no check to see if min > max.
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamp a given value between a range
|
||||
/// </summary>
|
||||
/// <param name="value">Value to clamp</param>
|
||||
/// <param name="min">Minimum allowable value</param>
|
||||
/// <param name="max">Maximum allowable value</param>
|
||||
/// <returns>A value inclusively between lower and upper</returns>
|
||||
public static int Clamp(int value, int min, int max)
|
||||
{
|
||||
// First we check to see if we're greater than the max
|
||||
value = (value > max) ? max : value;
|
||||
|
||||
// Then we check to see if we're less than the min.
|
||||
value = (value < min) ? min : value;
|
||||
|
||||
// There's no check to see if min > max.
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Round a floating-point value to the nearest integer
|
||||
/// </summary>
|
||||
/// <param name="val">Floating point number to round</param>
|
||||
/// <returns>Integer</returns>
|
||||
public static int Round(float val)
|
||||
{
|
||||
return (int)Math.Floor(val + 0.5f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test if a single precision float is a finite number
|
||||
/// </summary>
|
||||
public static bool IsFinite(float value)
|
||||
{
|
||||
return !(Single.IsNaN(value) || Single.IsInfinity(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test if a double precision float is a finite number
|
||||
/// </summary>
|
||||
public static bool IsFinite(double value)
|
||||
{
|
||||
return !(Double.IsNaN(value) || Double.IsInfinity(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the distance between two floating-point values
|
||||
/// </summary>
|
||||
/// <param name="value1">First value</param>
|
||||
/// <param name="value2">Second value</param>
|
||||
/// <returns>The distance between the two values</returns>
|
||||
public static float Distance(float value1, float value2)
|
||||
{
|
||||
return Math.Abs(value1 - value2);
|
||||
}
|
||||
|
||||
public static float Hermite(float value1, float tangent1, float value2, float tangent2, float amount)
|
||||
{
|
||||
// All transformed to double not to lose precission
|
||||
// Otherwise, for high numbers of param:amount the result is NaN instead of Infinity
|
||||
double v1 = value1, v2 = value2, t1 = tangent1, t2 = tangent2, s = amount, result;
|
||||
double sCubed = s * s * s;
|
||||
double sSquared = s * s;
|
||||
|
||||
if (amount == 0f)
|
||||
result = value1;
|
||||
else if (amount == 1f)
|
||||
result = value2;
|
||||
else
|
||||
result = (2d * v1 - 2d * v2 + t2 + t1) * sCubed +
|
||||
(3d * v2 - 3d * v1 - 2d * t1 - t2) * sSquared +
|
||||
t1 * s + v1;
|
||||
return (float)result;
|
||||
}
|
||||
|
||||
public static double Hermite(double value1, double tangent1, double value2, double tangent2, double amount)
|
||||
{
|
||||
// All transformed to double not to lose precission
|
||||
// Otherwise, for high numbers of param:amount the result is NaN instead of Infinity
|
||||
double v1 = value1, v2 = value2, t1 = tangent1, t2 = tangent2, s = amount, result;
|
||||
double sCubed = s * s * s;
|
||||
double sSquared = s * s;
|
||||
|
||||
if (amount == 0d)
|
||||
result = value1;
|
||||
else if (amount == 1f)
|
||||
result = value2;
|
||||
else
|
||||
result = (2d * v1 - 2d * v2 + t2 + t1) * sCubed +
|
||||
(3d * v2 - 3d * v1 - 2d * t1 - t2) * sSquared +
|
||||
t1 * s + v1;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static float Lerp(float value1, float value2, float amount)
|
||||
{
|
||||
return value1 + (value2 - value1) * amount;
|
||||
}
|
||||
|
||||
public static double Lerp(double value1, double value2, double amount)
|
||||
{
|
||||
return value1 + (value2 - value1) * amount;
|
||||
}
|
||||
|
||||
public static float SmoothStep(float value1, float value2, float amount)
|
||||
{
|
||||
// It is expected that 0 < amount < 1
|
||||
// If amount < 0, return value1
|
||||
// If amount > 1, return value2
|
||||
float result = Utils.Clamp(amount, 0f, 1f);
|
||||
return Utils.Hermite(value1, 0f, value2, 0f, result);
|
||||
}
|
||||
|
||||
public static double SmoothStep(double value1, double value2, double amount)
|
||||
{
|
||||
// It is expected that 0 < amount < 1
|
||||
// If amount < 0, return value1
|
||||
// If amount > 1, return value2
|
||||
double result = Utils.Clamp(amount, 0f, 1f);
|
||||
return Utils.Hermite(value1, 0f, value2, 0f, result);
|
||||
}
|
||||
|
||||
public static float ToDegrees(float radians)
|
||||
{
|
||||
// This method uses double precission internally,
|
||||
// though it returns single float
|
||||
// Factor = 180 / pi
|
||||
return (float)(radians * 57.295779513082320876798154814105);
|
||||
}
|
||||
|
||||
public static float ToRadians(float degrees)
|
||||
{
|
||||
// This method uses double precission internally,
|
||||
// though it returns single float
|
||||
// Factor = pi / 180
|
||||
return (float)(degrees * 0.017453292519943295769236907684886);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the MD5 hash for a byte array
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to compute the hash for</param>
|
||||
/// <returns>MD5 hash of the input data</returns>
|
||||
public static byte[] MD5(byte[] data)
|
||||
{
|
||||
lock (MD5Builder)
|
||||
return MD5Builder.ComputeHash(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the SHA1 hash for a byte array
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to compute the hash for</param>
|
||||
/// <returns>SHA1 hash of the input data</returns>
|
||||
public static byte[] SHA1(byte[] data)
|
||||
{
|
||||
lock (SHA1Builder)
|
||||
return SHA1Builder.ComputeHash(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the SHA1 hash of a given string
|
||||
/// </summary>
|
||||
/// <param name="value">The string to hash</param>
|
||||
/// <returns>The SHA1 hash as a string</returns>
|
||||
public static string SHA1String(string value)
|
||||
{
|
||||
StringBuilder digest = new StringBuilder(40);
|
||||
byte[] hash = SHA1(Encoding.UTF8.GetBytes(value));
|
||||
|
||||
// Convert the hash to a hex string
|
||||
foreach (byte b in hash)
|
||||
digest.AppendFormat(Utils.EnUsCulture, "{0:x2}", b);
|
||||
|
||||
return digest.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the SHA256 hash for a byte array
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to compute the hash for</param>
|
||||
/// <returns>SHA256 hash of the input data</returns>
|
||||
public static byte[] SHA256(byte[] data)
|
||||
{
|
||||
lock (SHA256Builder)
|
||||
return SHA256Builder.ComputeHash(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the SHA256 hash of a given string
|
||||
/// </summary>
|
||||
/// <param name="value">The string to hash</param>
|
||||
/// <returns>The SHA256 hash as a string</returns>
|
||||
public static string SHA256String(string value)
|
||||
{
|
||||
StringBuilder digest = new StringBuilder(64);
|
||||
byte[] hash = SHA256(Encoding.UTF8.GetBytes(value));
|
||||
|
||||
// Convert the hash to a hex string
|
||||
foreach (byte b in hash)
|
||||
digest.AppendFormat(Utils.EnUsCulture, "{0:x2}", b);
|
||||
|
||||
return digest.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the MD5 hash of a given string
|
||||
/// </summary>
|
||||
/// <param name="password">The password to hash</param>
|
||||
/// <returns>An MD5 hash in string format, with $1$ prepended</returns>
|
||||
public static string MD5(string password)
|
||||
{
|
||||
StringBuilder digest = new StringBuilder(32);
|
||||
byte[] hash = MD5(ASCIIEncoding.Default.GetBytes(password));
|
||||
|
||||
// Convert the hash to a hex string
|
||||
foreach (byte b in hash)
|
||||
digest.AppendFormat(Utils.EnUsCulture, "{0:x2}", b);
|
||||
|
||||
return "$1$" + digest.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the MD5 hash of a given string
|
||||
/// </summary>
|
||||
/// <param name="value">The string to hash</param>
|
||||
/// <returns>The MD5 hash as a string</returns>
|
||||
public static string MD5String(string value)
|
||||
{
|
||||
StringBuilder digest = new StringBuilder(32);
|
||||
byte[] hash = MD5(Encoding.UTF8.GetBytes(value));
|
||||
|
||||
// Convert the hash to a hex string
|
||||
foreach (byte b in hash)
|
||||
digest.AppendFormat(Utils.EnUsCulture, "{0:x2}", b);
|
||||
|
||||
return digest.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a random double precision floating point value
|
||||
/// </summary>
|
||||
/// <returns>Random value of type double</returns>
|
||||
public static double RandomDouble()
|
||||
{
|
||||
lock (RNG)
|
||||
return RNG.NextDouble();
|
||||
}
|
||||
|
||||
#endregion Math
|
||||
|
||||
#region Platform
|
||||
|
||||
/// <summary>
|
||||
/// Get the current running platform
|
||||
/// </summary>
|
||||
/// <returns>Enumeration of the current platform we are running on</returns>
|
||||
public static Platform GetRunningPlatform()
|
||||
{
|
||||
const string OSX_CHECK_FILE = "/Library/Extensions.kextcache";
|
||||
|
||||
if (Environment.OSVersion.Platform == PlatformID.WinCE)
|
||||
{
|
||||
return Platform.WindowsCE;
|
||||
}
|
||||
else
|
||||
{
|
||||
int plat = (int)Environment.OSVersion.Platform;
|
||||
|
||||
if ((plat != 4) && (plat != 128))
|
||||
{
|
||||
return Platform.Windows;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (System.IO.File.Exists(OSX_CHECK_FILE))
|
||||
return Platform.OSX;
|
||||
else
|
||||
return Platform.Linux;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current running runtime
|
||||
/// </summary>
|
||||
/// <returns>Enumeration of the current runtime we are running on</returns>
|
||||
public static Runtime GetRunningRuntime()
|
||||
{
|
||||
Type t = Type.GetType("Mono.Runtime");
|
||||
if (t != null)
|
||||
return Runtime.Mono;
|
||||
else
|
||||
return Runtime.Windows;
|
||||
}
|
||||
|
||||
#endregion Platform
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Globalization;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
/// <summary>
|
||||
/// A two-dimensional vector with floating-point values
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Vector2 : IComparable<Vector2>, IEquatable<Vector2>
|
||||
{
|
||||
/// <summary>X value</summary>
|
||||
public float X;
|
||||
/// <summary>Y value</summary>
|
||||
public float Y;
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Vector2(float x, float y)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
public Vector2(float value)
|
||||
{
|
||||
X = value;
|
||||
Y = value;
|
||||
}
|
||||
|
||||
public Vector2(Vector2 vector)
|
||||
{
|
||||
X = vector.X;
|
||||
Y = vector.Y;
|
||||
}
|
||||
|
||||
#endregion Constructors
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Test if this vector is equal to another vector, within a given
|
||||
/// tolerance range
|
||||
/// </summary>
|
||||
/// <param name="vec">Vector to test against</param>
|
||||
/// <param name="tolerance">The acceptable magnitude of difference
|
||||
/// between the two vectors</param>
|
||||
/// <returns>True if the magnitude of difference between the two vectors
|
||||
/// is less than the given tolerance, otherwise false</returns>
|
||||
public bool ApproxEquals(Vector2 vec, float tolerance)
|
||||
{
|
||||
Vector2 diff = this - vec;
|
||||
return (diff.LengthSquared() <= tolerance * tolerance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test if this vector is composed of all finite numbers
|
||||
/// </summary>
|
||||
public bool IsFinite()
|
||||
{
|
||||
return Utils.IsFinite(X) && Utils.IsFinite(Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IComparable.CompareTo implementation
|
||||
/// </summary>
|
||||
public int CompareTo(Vector2 vector)
|
||||
{
|
||||
return Length().CompareTo(vector.Length());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a vector from a byte array
|
||||
/// </summary>
|
||||
/// <param name="byteArray">Byte array containing two four-byte floats</param>
|
||||
/// <param name="pos">Beginning position in the byte array</param>
|
||||
public void FromBytes(byte[] byteArray, int pos)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
{
|
||||
// Big endian architecture
|
||||
byte[] conversionBuffer = new byte[8];
|
||||
|
||||
Buffer.BlockCopy(byteArray, pos, conversionBuffer, 0, 8);
|
||||
|
||||
Array.Reverse(conversionBuffer, 0, 4);
|
||||
Array.Reverse(conversionBuffer, 4, 4);
|
||||
|
||||
X = BitConverter.ToSingle(conversionBuffer, 0);
|
||||
Y = BitConverter.ToSingle(conversionBuffer, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Little endian architecture
|
||||
X = BitConverter.ToSingle(byteArray, pos);
|
||||
Y = BitConverter.ToSingle(byteArray, pos + 4);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the raw bytes for this vector
|
||||
/// </summary>
|
||||
/// <returns>An eight-byte array containing X and Y</returns>
|
||||
public byte[] GetBytes()
|
||||
{
|
||||
byte[] byteArray = new byte[8];
|
||||
ToBytes(byteArray, 0);
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the raw bytes for this vector to a byte array
|
||||
/// </summary>
|
||||
/// <param name="dest">Destination byte array</param>
|
||||
/// <param name="pos">Position in the destination array to start
|
||||
/// writing. Must be at least 8 bytes before the end of the array</param>
|
||||
public void ToBytes(byte[] dest, int pos)
|
||||
{
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(X), 0, dest, pos + 0, 4);
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(Y), 0, dest, pos + 4, 4);
|
||||
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
{
|
||||
Array.Reverse(dest, pos + 0, 4);
|
||||
Array.Reverse(dest, pos + 4, 4);
|
||||
}
|
||||
}
|
||||
|
||||
public float Length()
|
||||
{
|
||||
return (float)Math.Sqrt(DistanceSquared(this, Zero));
|
||||
}
|
||||
|
||||
public float LengthSquared()
|
||||
{
|
||||
return DistanceSquared(this, Zero);
|
||||
}
|
||||
|
||||
public void Normalize()
|
||||
{
|
||||
this = Normalize(this);
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Static Methods
|
||||
|
||||
public static Vector2 Add(Vector2 value1, Vector2 value2)
|
||||
{
|
||||
value1.X += value2.X;
|
||||
value1.Y += value2.Y;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector2 Clamp(Vector2 value1, Vector2 min, Vector2 max)
|
||||
{
|
||||
return new Vector2(
|
||||
Utils.Clamp(value1.X, min.X, max.X),
|
||||
Utils.Clamp(value1.Y, min.Y, max.Y));
|
||||
}
|
||||
|
||||
public static float Distance(Vector2 value1, Vector2 value2)
|
||||
{
|
||||
return (float)Math.Sqrt(DistanceSquared(value1, value2));
|
||||
}
|
||||
|
||||
public static float DistanceSquared(Vector2 value1, Vector2 value2)
|
||||
{
|
||||
return
|
||||
(value1.X - value2.X) * (value1.X - value2.X) +
|
||||
(value1.Y - value2.Y) * (value1.Y - value2.Y);
|
||||
}
|
||||
|
||||
public static Vector2 Divide(Vector2 value1, Vector2 value2)
|
||||
{
|
||||
value1.X /= value2.X;
|
||||
value1.Y /= value2.Y;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector2 Divide(Vector2 value1, float divider)
|
||||
{
|
||||
float factor = 1 / divider;
|
||||
value1.X *= factor;
|
||||
value1.Y *= factor;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static float Dot(Vector2 value1, Vector2 value2)
|
||||
{
|
||||
return value1.X * value2.X + value1.Y * value2.Y;
|
||||
}
|
||||
|
||||
public static Vector2 Lerp(Vector2 value1, Vector2 value2, float amount)
|
||||
{
|
||||
return new Vector2(
|
||||
Utils.Lerp(value1.X, value2.X, amount),
|
||||
Utils.Lerp(value1.Y, value2.Y, amount));
|
||||
}
|
||||
|
||||
public static Vector2 Max(Vector2 value1, Vector2 value2)
|
||||
{
|
||||
return new Vector2(
|
||||
Math.Max(value1.X, value2.X),
|
||||
Math.Max(value1.Y, value2.Y));
|
||||
}
|
||||
|
||||
public static Vector2 Min(Vector2 value1, Vector2 value2)
|
||||
{
|
||||
return new Vector2(
|
||||
Math.Min(value1.X, value2.X),
|
||||
Math.Min(value1.Y, value2.Y));
|
||||
}
|
||||
|
||||
public static Vector2 Multiply(Vector2 value1, Vector2 value2)
|
||||
{
|
||||
value1.X *= value2.X;
|
||||
value1.Y *= value2.Y;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector2 Multiply(Vector2 value1, float scaleFactor)
|
||||
{
|
||||
value1.X *= scaleFactor;
|
||||
value1.Y *= scaleFactor;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector2 Negate(Vector2 value)
|
||||
{
|
||||
value.X = -value.X;
|
||||
value.Y = -value.Y;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Vector2 Normalize(Vector2 value)
|
||||
{
|
||||
const float MAG_THRESHOLD = 0.0000001f;
|
||||
float factor = DistanceSquared(value, Zero);
|
||||
if (factor > MAG_THRESHOLD)
|
||||
{
|
||||
factor = 1f / (float)Math.Sqrt(factor);
|
||||
value.X *= factor;
|
||||
value.Y *= factor;
|
||||
}
|
||||
else
|
||||
{
|
||||
value.X = 0f;
|
||||
value.Y = 0f;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a vector from a string
|
||||
/// </summary>
|
||||
/// <param name="val">A string representation of a 2D vector, enclosed
|
||||
/// in arrow brackets and separated by commas</param>
|
||||
public static Vector3 Parse(string val)
|
||||
{
|
||||
char[] splitChar = { ',' };
|
||||
string[] split = val.Replace("<", String.Empty).Replace(">", String.Empty).Split(splitChar);
|
||||
return new Vector3(
|
||||
float.Parse(split[0].Trim(), Utils.EnUsCulture),
|
||||
float.Parse(split[1].Trim(), Utils.EnUsCulture),
|
||||
float.Parse(split[2].Trim(), Utils.EnUsCulture));
|
||||
}
|
||||
|
||||
public static bool TryParse(string val, out Vector3 result)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = Parse(val);
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
result = Vector3.Zero;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates between two vectors using a cubic equation
|
||||
/// </summary>
|
||||
public static Vector2 SmoothStep(Vector2 value1, Vector2 value2, float amount)
|
||||
{
|
||||
return new Vector2(
|
||||
Utils.SmoothStep(value1.X, value2.X, amount),
|
||||
Utils.SmoothStep(value1.Y, value2.Y, amount));
|
||||
}
|
||||
|
||||
public static Vector2 Subtract(Vector2 value1, Vector2 value2)
|
||||
{
|
||||
value1.X -= value2.X;
|
||||
value1.Y -= value2.Y;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector2 Transform(Vector2 position, Matrix4 matrix)
|
||||
{
|
||||
position.X = (position.X * matrix.M11) + (position.Y * matrix.M21) + matrix.M41;
|
||||
position.Y = (position.X * matrix.M12) + (position.Y * matrix.M22) + matrix.M42;
|
||||
return position;
|
||||
}
|
||||
|
||||
public static Vector2 TransformNormal(Vector2 position, Matrix4 matrix)
|
||||
{
|
||||
position.X = (position.X * matrix.M11) + (position.Y * matrix.M21);
|
||||
position.Y = (position.X * matrix.M12) + (position.Y * matrix.M22);
|
||||
return position;
|
||||
}
|
||||
|
||||
#endregion Static Methods
|
||||
|
||||
#region Overrides
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return (obj is Vector2) ? this == ((Vector2)obj) : false;
|
||||
}
|
||||
|
||||
public bool Equals(Vector2 other)
|
||||
{
|
||||
return this == other;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int hash = X.GetHashCode();
|
||||
hash = hash * 31 + Y.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a formatted string representation of the vector
|
||||
/// </summary>
|
||||
/// <returns>A string representation of the vector</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format(Utils.EnUsCulture, "<{0}, {1}>", X, Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a string representation of the vector elements with up to three
|
||||
/// decimal digits and separated by spaces only
|
||||
/// </summary>
|
||||
/// <returns>Raw string representation of the vector</returns>
|
||||
public string ToRawString()
|
||||
{
|
||||
CultureInfo enUs = new CultureInfo("en-us");
|
||||
enUs.NumberFormat.NumberDecimalDigits = 3;
|
||||
|
||||
return String.Format(enUs, "{0} {1}", X, Y);
|
||||
}
|
||||
|
||||
#endregion Overrides
|
||||
|
||||
#region Operators
|
||||
|
||||
public static bool operator ==(Vector2 value1, Vector2 value2)
|
||||
{
|
||||
return value1.X == value2.X && value1.Y == value2.Y;
|
||||
}
|
||||
|
||||
public static bool operator !=(Vector2 value1, Vector2 value2)
|
||||
{
|
||||
return value1.X != value2.X || value1.Y != value2.Y;
|
||||
}
|
||||
|
||||
public static Vector2 operator +(Vector2 value1, Vector2 value2)
|
||||
{
|
||||
value1.X += value2.X;
|
||||
value1.Y += value2.Y;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector2 operator -(Vector2 value)
|
||||
{
|
||||
value.X = -value.X;
|
||||
value.Y = -value.Y;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Vector2 operator -(Vector2 value1, Vector2 value2)
|
||||
{
|
||||
value1.X -= value2.X;
|
||||
value1.Y -= value2.Y;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector2 operator *(Vector2 value1, Vector2 value2)
|
||||
{
|
||||
value1.X *= value2.X;
|
||||
value1.Y *= value2.Y;
|
||||
return value1;
|
||||
}
|
||||
|
||||
|
||||
public static Vector2 operator *(Vector2 value, float scaleFactor)
|
||||
{
|
||||
value.X *= scaleFactor;
|
||||
value.Y *= scaleFactor;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Vector2 operator /(Vector2 value1, Vector2 value2)
|
||||
{
|
||||
value1.X /= value2.X;
|
||||
value1.Y /= value2.Y;
|
||||
return value1;
|
||||
}
|
||||
|
||||
|
||||
public static Vector2 operator /(Vector2 value1, float divider)
|
||||
{
|
||||
float factor = 1 / divider;
|
||||
value1.X *= factor;
|
||||
value1.Y *= factor;
|
||||
return value1;
|
||||
}
|
||||
|
||||
#endregion Operators
|
||||
|
||||
/// <summary>A vector with a value of 0,0</summary>
|
||||
public readonly static Vector2 Zero = new Vector2();
|
||||
/// <summary>A vector with a value of 1,1</summary>
|
||||
public readonly static Vector2 One = new Vector2(1f, 1f);
|
||||
/// <summary>A vector with a value of 1,0</summary>
|
||||
public readonly static Vector2 UnitX = new Vector2(1f, 0f);
|
||||
/// <summary>A vector with a value of 0,1</summary>
|
||||
public readonly static Vector2 UnitY = new Vector2(0f, 1f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,596 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Globalization;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
/// <summary>
|
||||
/// A three-dimensional vector with floating-point values
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Vector3 : IComparable<Vector3>, IEquatable<Vector3>
|
||||
{
|
||||
/// <summary>X value</summary>
|
||||
public float X;
|
||||
/// <summary>Y value</summary>
|
||||
public float Y;
|
||||
/// <summary>Z value</summary>
|
||||
public float Z;
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Vector3(float x, float y, float z)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
}
|
||||
|
||||
public Vector3(float value)
|
||||
{
|
||||
X = value;
|
||||
Y = value;
|
||||
Z = value;
|
||||
}
|
||||
|
||||
public Vector3(Vector2 value, float z)
|
||||
{
|
||||
X = value.X;
|
||||
Y = value.Y;
|
||||
Z = z;
|
||||
}
|
||||
|
||||
public Vector3(Vector3d vector)
|
||||
{
|
||||
X = (float)vector.X;
|
||||
Y = (float)vector.Y;
|
||||
Z = (float)vector.Z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor, builds a vector from a byte array
|
||||
/// </summary>
|
||||
/// <param name="byteArray">Byte array containing three four-byte floats</param>
|
||||
/// <param name="pos">Beginning position in the byte array</param>
|
||||
public Vector3(byte[] byteArray, int pos)
|
||||
{
|
||||
X = Y = Z = 0f;
|
||||
FromBytes(byteArray, pos);
|
||||
}
|
||||
|
||||
public Vector3(Vector3 vector)
|
||||
{
|
||||
X = vector.X;
|
||||
Y = vector.Y;
|
||||
Z = vector.Z;
|
||||
}
|
||||
|
||||
#endregion Constructors
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public float Length()
|
||||
{
|
||||
return (float)Math.Sqrt(DistanceSquared(this, Zero));
|
||||
}
|
||||
|
||||
public float LengthSquared()
|
||||
{
|
||||
return DistanceSquared(this, Zero);
|
||||
}
|
||||
|
||||
public void Normalize()
|
||||
{
|
||||
this = Normalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test if this vector is equal to another vector, within a given
|
||||
/// tolerance range
|
||||
/// </summary>
|
||||
/// <param name="vec">Vector to test against</param>
|
||||
/// <param name="tolerance">The acceptable magnitude of difference
|
||||
/// between the two vectors</param>
|
||||
/// <returns>True if the magnitude of difference between the two vectors
|
||||
/// is less than the given tolerance, otherwise false</returns>
|
||||
public bool ApproxEquals(Vector3 vec, float tolerance)
|
||||
{
|
||||
Vector3 diff = this - vec;
|
||||
return (diff.LengthSquared() <= tolerance * tolerance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IComparable.CompareTo implementation
|
||||
/// </summary>
|
||||
public int CompareTo(Vector3 vector)
|
||||
{
|
||||
return Length().CompareTo(vector.Length());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test if this vector is composed of all finite numbers
|
||||
/// </summary>
|
||||
public bool IsFinite()
|
||||
{
|
||||
return (Utils.IsFinite(X) && Utils.IsFinite(Y) && Utils.IsFinite(Z));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a vector from a byte array
|
||||
/// </summary>
|
||||
/// <param name="byteArray">Byte array containing a 12 byte vector</param>
|
||||
/// <param name="pos">Beginning position in the byte array</param>
|
||||
public void FromBytes(byte[] byteArray, int pos)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
{
|
||||
// Big endian architecture
|
||||
byte[] conversionBuffer = new byte[12];
|
||||
|
||||
Buffer.BlockCopy(byteArray, pos, conversionBuffer, 0, 12);
|
||||
|
||||
Array.Reverse(conversionBuffer, 0, 4);
|
||||
Array.Reverse(conversionBuffer, 4, 4);
|
||||
Array.Reverse(conversionBuffer, 8, 4);
|
||||
|
||||
X = BitConverter.ToSingle(conversionBuffer, 0);
|
||||
Y = BitConverter.ToSingle(conversionBuffer, 4);
|
||||
Z = BitConverter.ToSingle(conversionBuffer, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Little endian architecture
|
||||
X = BitConverter.ToSingle(byteArray, pos);
|
||||
Y = BitConverter.ToSingle(byteArray, pos + 4);
|
||||
Z = BitConverter.ToSingle(byteArray, pos + 8);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the raw bytes for this vector
|
||||
/// </summary>
|
||||
/// <returns>A 12 byte array containing X, Y, and Z</returns>
|
||||
public byte[] GetBytes()
|
||||
{
|
||||
byte[] byteArray = new byte[12];
|
||||
ToBytes(byteArray, 0);
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the raw bytes for this vector to a byte array
|
||||
/// </summary>
|
||||
/// <param name="dest">Destination byte array</param>
|
||||
/// <param name="pos">Position in the destination array to start
|
||||
/// writing. Must be at least 12 bytes before the end of the array</param>
|
||||
public void ToBytes(byte[] dest, int pos)
|
||||
{
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(X), 0, dest, pos + 0, 4);
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(Y), 0, dest, pos + 4, 4);
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(Z), 0, dest, pos + 8, 4);
|
||||
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
{
|
||||
Array.Reverse(dest, pos + 0, 4);
|
||||
Array.Reverse(dest, pos + 4, 4);
|
||||
Array.Reverse(dest, pos + 8, 4);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Static Methods
|
||||
|
||||
public static Vector3 Add(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
value1.X += value2.X;
|
||||
value1.Y += value2.Y;
|
||||
value1.Z += value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector3 Clamp(Vector3 value1, Vector3 min, Vector3 max)
|
||||
{
|
||||
return new Vector3(
|
||||
Utils.Clamp(value1.X, min.X, max.X),
|
||||
Utils.Clamp(value1.Y, min.Y, max.Y),
|
||||
Utils.Clamp(value1.Z, min.Z, max.Z));
|
||||
}
|
||||
|
||||
public static Vector3 Cross(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
return new Vector3(
|
||||
value1.Y * value2.Z - value2.Y * value1.Z,
|
||||
value1.Z * value2.X - value2.Z * value1.X,
|
||||
value1.X * value2.Y - value2.X * value1.Y);
|
||||
}
|
||||
|
||||
public static float Distance(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
return (float)Math.Sqrt(DistanceSquared(value1, value2));
|
||||
}
|
||||
|
||||
public static float DistanceSquared(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
return
|
||||
(value1.X - value2.X) * (value1.X - value2.X) +
|
||||
(value1.Y - value2.Y) * (value1.Y - value2.Y) +
|
||||
(value1.Z - value2.Z) * (value1.Z - value2.Z);
|
||||
}
|
||||
|
||||
public static Vector3 Divide(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
value1.X /= value2.X;
|
||||
value1.Y /= value2.Y;
|
||||
value1.Z /= value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector3 Divide(Vector3 value1, float value2)
|
||||
{
|
||||
float factor = 1f / value2;
|
||||
value1.X *= factor;
|
||||
value1.Y *= factor;
|
||||
value1.Z *= factor;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static float Dot(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
return value1.X * value2.X + value1.Y * value2.Y + value1.Z * value2.Z;
|
||||
}
|
||||
|
||||
public static Vector3 Lerp(Vector3 value1, Vector3 value2, float amount)
|
||||
{
|
||||
return new Vector3(
|
||||
Utils.Lerp(value1.X, value2.X, amount),
|
||||
Utils.Lerp(value1.Y, value2.Y, amount),
|
||||
Utils.Lerp(value1.Z, value2.Z, amount));
|
||||
}
|
||||
|
||||
public static float Mag(Vector3 value)
|
||||
{
|
||||
return (float)Math.Sqrt((value.X * value.X) + (value.Y * value.Y) + (value.Z * value.Z));
|
||||
}
|
||||
|
||||
public static Vector3 Max(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
return new Vector3(
|
||||
Math.Max(value1.X, value2.X),
|
||||
Math.Max(value1.Y, value2.Y),
|
||||
Math.Max(value1.Z, value2.Z));
|
||||
}
|
||||
|
||||
public static Vector3 Min(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
return new Vector3(
|
||||
Math.Min(value1.X, value2.X),
|
||||
Math.Min(value1.Y, value2.Y),
|
||||
Math.Min(value1.Z, value2.Z));
|
||||
}
|
||||
|
||||
public static Vector3 Multiply(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
value1.X *= value2.X;
|
||||
value1.Y *= value2.Y;
|
||||
value1.Z *= value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector3 Multiply(Vector3 value1, float scaleFactor)
|
||||
{
|
||||
value1.X *= scaleFactor;
|
||||
value1.Y *= scaleFactor;
|
||||
value1.Z *= scaleFactor;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector3 Negate(Vector3 value)
|
||||
{
|
||||
value.X = -value.X;
|
||||
value.Y = -value.Y;
|
||||
value.Z = -value.Z;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Vector3 Normalize(Vector3 value)
|
||||
{
|
||||
const float MAG_THRESHOLD = 0.0000001f;
|
||||
float factor = Distance(value, Zero);
|
||||
if (factor > MAG_THRESHOLD)
|
||||
{
|
||||
factor = 1f / factor;
|
||||
value.X *= factor;
|
||||
value.Y *= factor;
|
||||
value.Z *= factor;
|
||||
}
|
||||
else
|
||||
{
|
||||
value.X = 0f;
|
||||
value.Y = 0f;
|
||||
value.Z = 0f;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a vector from a string
|
||||
/// </summary>
|
||||
/// <param name="val">A string representation of a 3D vector, enclosed
|
||||
/// in arrow brackets and separated by commas</param>
|
||||
public static Vector3 Parse(string val)
|
||||
{
|
||||
char[] splitChar = { ',' };
|
||||
string[] split = val.Replace("<", String.Empty).Replace(">", String.Empty).Split(splitChar);
|
||||
return new Vector3(
|
||||
Single.Parse(split[0].Trim(), Utils.EnUsCulture),
|
||||
Single.Parse(split[1].Trim(), Utils.EnUsCulture),
|
||||
Single.Parse(split[2].Trim(), Utils.EnUsCulture));
|
||||
}
|
||||
|
||||
public static bool TryParse(string val, out Vector3 result)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = Parse(val);
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
result = Vector3.Zero;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the rotation between two vectors
|
||||
/// </summary>
|
||||
/// <param name="a">Normalized directional vector (such as 1,0,0 for forward facing)</param>
|
||||
/// <param name="b">Normalized target vector</param>
|
||||
public static Quaternion RotationBetween(Vector3 a, Vector3 b)
|
||||
{
|
||||
float dotProduct = Dot(a, b);
|
||||
Vector3 crossProduct = Cross(a, b);
|
||||
float magProduct = a.Length() * b.Length();
|
||||
double angle = Math.Acos(dotProduct / magProduct);
|
||||
Vector3 axis = Normalize(crossProduct);
|
||||
float s = (float)Math.Sin(angle / 2d);
|
||||
|
||||
return new Quaternion(
|
||||
axis.X * s,
|
||||
axis.Y * s,
|
||||
axis.Z * s,
|
||||
(float)Math.Cos(angle / 2d));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates between two vectors using a cubic equation
|
||||
/// </summary>
|
||||
public static Vector3 SmoothStep(Vector3 value1, Vector3 value2, float amount)
|
||||
{
|
||||
return new Vector3(
|
||||
Utils.SmoothStep(value1.X, value2.X, amount),
|
||||
Utils.SmoothStep(value1.Y, value2.Y, amount),
|
||||
Utils.SmoothStep(value1.Z, value2.Z, amount));
|
||||
}
|
||||
|
||||
public static Vector3 Subtract(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
value1.X -= value2.X;
|
||||
value1.Y -= value2.Y;
|
||||
value1.Z -= value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector3 Transform(Vector3 position, Matrix4 matrix)
|
||||
{
|
||||
return new Vector3(
|
||||
(position.X * matrix.M11) + (position.Y * matrix.M21) + (position.Z * matrix.M31) + matrix.M41,
|
||||
(position.X * matrix.M12) + (position.Y * matrix.M22) + (position.Z * matrix.M32) + matrix.M42,
|
||||
(position.X * matrix.M13) + (position.Y * matrix.M23) + (position.Z * matrix.M33) + matrix.M43);
|
||||
}
|
||||
|
||||
public static Vector3 TransformNormal(Vector3 position, Matrix4 matrix)
|
||||
{
|
||||
return new Vector3(
|
||||
(position.X * matrix.M11) + (position.Y * matrix.M21) + (position.Z * matrix.M31),
|
||||
(position.X * matrix.M12) + (position.Y * matrix.M22) + (position.Z * matrix.M32),
|
||||
(position.X * matrix.M13) + (position.Y * matrix.M23) + (position.Z * matrix.M33));
|
||||
}
|
||||
|
||||
#endregion Static Methods
|
||||
|
||||
#region Overrides
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return (obj is Vector3) ? this == (Vector3)obj : false;
|
||||
}
|
||||
|
||||
public bool Equals(Vector3 other)
|
||||
{
|
||||
return this == other;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int hash = X.GetHashCode();
|
||||
hash = hash * 31 + Y.GetHashCode();
|
||||
hash = hash * 31 + Z.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a formatted string representation of the vector
|
||||
/// </summary>
|
||||
/// <returns>A string representation of the vector</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format(Utils.EnUsCulture, "<{0}, {1}, {2}>", X, Y, Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a string representation of the vector elements with up to three
|
||||
/// decimal digits and separated by spaces only
|
||||
/// </summary>
|
||||
/// <returns>Raw string representation of the vector</returns>
|
||||
public string ToRawString()
|
||||
{
|
||||
CultureInfo enUs = new CultureInfo("en-us");
|
||||
enUs.NumberFormat.NumberDecimalDigits = 3;
|
||||
|
||||
return String.Format(enUs, "{0} {1} {2}", X, Y, Z);
|
||||
}
|
||||
|
||||
#endregion Overrides
|
||||
|
||||
#region Operators
|
||||
|
||||
public static bool operator ==(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
return value1.X == value2.X
|
||||
&& value1.Y == value2.Y
|
||||
&& value1.Z == value2.Z;
|
||||
}
|
||||
|
||||
public static bool operator !=(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
return !(value1 == value2);
|
||||
}
|
||||
|
||||
public static Vector3 operator +(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
value1.X += value2.X;
|
||||
value1.Y += value2.Y;
|
||||
value1.Z += value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector3 operator -(Vector3 value)
|
||||
{
|
||||
value.X = -value.X;
|
||||
value.Y = -value.Y;
|
||||
value.Z = -value.Z;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Vector3 operator -(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
value1.X -= value2.X;
|
||||
value1.Y -= value2.Y;
|
||||
value1.Z -= value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector3 operator *(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
value1.X *= value2.X;
|
||||
value1.Y *= value2.Y;
|
||||
value1.Z *= value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector3 operator *(Vector3 value, float scaleFactor)
|
||||
{
|
||||
value.X *= scaleFactor;
|
||||
value.Y *= scaleFactor;
|
||||
value.Z *= scaleFactor;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Vector3 operator *(Vector3 vec, Quaternion rot)
|
||||
{
|
||||
float rw = -rot.X * vec.X - rot.Y * vec.Y - rot.Z * vec.Z;
|
||||
float rx = rot.W * vec.X + rot.Y * vec.Z - rot.Z * vec.Y;
|
||||
float ry = rot.W * vec.Y + rot.Z * vec.X - rot.X * vec.Z;
|
||||
float rz = rot.W * vec.Z + rot.X * vec.Y - rot.Y * vec.X;
|
||||
|
||||
vec.X = -rw * rot.X + rx * rot.W - ry * rot.Z + rz * rot.Y;
|
||||
vec.Y = -rw * rot.Y + ry * rot.W - rz * rot.X + rx * rot.Z;
|
||||
vec.Z = -rw * rot.Z + rz * rot.W - rx * rot.Y + ry * rot.X;
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
public static Vector3 operator *(Vector3 vector, Matrix4 matrix)
|
||||
{
|
||||
return Transform(vector, matrix);
|
||||
}
|
||||
|
||||
public static Vector3 operator /(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
value1.X /= value2.X;
|
||||
value1.Y /= value2.Y;
|
||||
value1.Z /= value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector3 operator /(Vector3 value, float divider)
|
||||
{
|
||||
float factor = 1f / divider;
|
||||
value.X *= factor;
|
||||
value.Y *= factor;
|
||||
value.Z *= factor;
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cross product between two vectors
|
||||
/// </summary>
|
||||
public static Vector3 operator %(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
return Cross(value1, value2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicit casting for Vector3d > Vector3
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static explicit operator Vector3(Vector3d value)
|
||||
{
|
||||
Vector3d foo = (Vector3d)Vector3.Zero;
|
||||
return new Vector3(value);
|
||||
}
|
||||
|
||||
#endregion Operators
|
||||
|
||||
/// <summary>A vector with a value of 0,0,0</summary>
|
||||
public readonly static Vector3 Zero = new Vector3();
|
||||
/// <summary>A vector with a value of 1,1,1</summary>
|
||||
public readonly static Vector3 One = new Vector3(1f, 1f, 1f);
|
||||
/// <summary>A unit vector facing forward (X axis), value 1,0,0</summary>
|
||||
public readonly static Vector3 UnitX = new Vector3(1f, 0f, 0f);
|
||||
/// <summary>A unit vector facing left (Y axis), value 0,1,0</summary>
|
||||
public readonly static Vector3 UnitY = new Vector3(0f, 1f, 0f);
|
||||
/// <summary>A unit vector facing up (Z axis), value 0,0,1</summary>
|
||||
public readonly static Vector3 UnitZ = new Vector3(0f, 0f, 1f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,523 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Globalization;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
/// <summary>
|
||||
/// A three-dimensional vector with doubleing-point values
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Vector3d : IComparable<Vector3d>, IEquatable<Vector3d>
|
||||
{
|
||||
/// <summary>X value</summary>
|
||||
public double X;
|
||||
/// <summary>Y value</summary>
|
||||
public double Y;
|
||||
/// <summary>Z value</summary>
|
||||
public double Z;
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Vector3d(double x, double y, double z)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
}
|
||||
|
||||
public Vector3d(double value)
|
||||
{
|
||||
X = value;
|
||||
Y = value;
|
||||
Z = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor, builds a vector from a byte array
|
||||
/// </summary>
|
||||
/// <param name="byteArray">Byte array containing three eight-byte doubles</param>
|
||||
/// <param name="pos">Beginning position in the byte array</param>
|
||||
public Vector3d(byte[] byteArray, int pos)
|
||||
{
|
||||
X = Y = Z = 0d;
|
||||
FromBytes(byteArray, pos);
|
||||
}
|
||||
|
||||
public Vector3d(Vector3 vector)
|
||||
{
|
||||
X = vector.X;
|
||||
Y = vector.Y;
|
||||
Z = vector.Z;
|
||||
}
|
||||
|
||||
public Vector3d(Vector3d vector)
|
||||
{
|
||||
X = vector.X;
|
||||
Y = vector.Y;
|
||||
Z = vector.Z;
|
||||
}
|
||||
|
||||
#endregion Constructors
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public double Length()
|
||||
{
|
||||
return Math.Sqrt(DistanceSquared(this, Zero));
|
||||
}
|
||||
|
||||
public double LengthSquared()
|
||||
{
|
||||
return DistanceSquared(this, Zero);
|
||||
}
|
||||
|
||||
public void Normalize()
|
||||
{
|
||||
this = Normalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test if this vector is equal to another vector, within a given
|
||||
/// tolerance range
|
||||
/// </summary>
|
||||
/// <param name="vec">Vector to test against</param>
|
||||
/// <param name="tolerance">The acceptable magnitude of difference
|
||||
/// between the two vectors</param>
|
||||
/// <returns>True if the magnitude of difference between the two vectors
|
||||
/// is less than the given tolerance, otherwise false</returns>
|
||||
public bool ApproxEquals(Vector3d vec, double tolerance)
|
||||
{
|
||||
Vector3d diff = this - vec;
|
||||
return (diff.LengthSquared() <= tolerance * tolerance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IComparable.CompareTo implementation
|
||||
/// </summary>
|
||||
public int CompareTo(Vector3d vector)
|
||||
{
|
||||
return this.Length().CompareTo(vector.Length());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test if this vector is composed of all finite numbers
|
||||
/// </summary>
|
||||
public bool IsFinite()
|
||||
{
|
||||
return (Utils.IsFinite(X) && Utils.IsFinite(Y) && Utils.IsFinite(Z));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a vector from a byte array
|
||||
/// </summary>
|
||||
/// <param name="byteArray">Byte array containing a 24 byte vector</param>
|
||||
/// <param name="pos">Beginning position in the byte array</param>
|
||||
public void FromBytes(byte[] byteArray, int pos)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
{
|
||||
// Big endian architecture
|
||||
byte[] conversionBuffer = new byte[24];
|
||||
|
||||
Buffer.BlockCopy(byteArray, pos, conversionBuffer, 0, 24);
|
||||
|
||||
Array.Reverse(conversionBuffer, 0, 8);
|
||||
Array.Reverse(conversionBuffer, 8, 8);
|
||||
Array.Reverse(conversionBuffer, 16, 8);
|
||||
|
||||
X = BitConverter.ToDouble(conversionBuffer, 0);
|
||||
Y = BitConverter.ToDouble(conversionBuffer, 8);
|
||||
Z = BitConverter.ToDouble(conversionBuffer, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Little endian architecture
|
||||
X = BitConverter.ToDouble(byteArray, pos);
|
||||
Y = BitConverter.ToDouble(byteArray, pos + 8);
|
||||
Z = BitConverter.ToDouble(byteArray, pos + 16);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the raw bytes for this vector
|
||||
/// </summary>
|
||||
/// <returns>A 24 byte array containing X, Y, and Z</returns>
|
||||
public byte[] GetBytes()
|
||||
{
|
||||
byte[] byteArray = new byte[24];
|
||||
ToBytes(byteArray, 0);
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the raw bytes for this vector to a byte array
|
||||
/// </summary>
|
||||
/// <param name="dest">Destination byte array</param>
|
||||
/// <param name="pos">Position in the destination array to start
|
||||
/// writing. Must be at least 24 bytes before the end of the array</param>
|
||||
public void ToBytes(byte[] dest, int pos)
|
||||
{
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(X), 0, dest, pos + 0, 8);
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(Y), 0, dest, pos + 8, 8);
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(Z), 0, dest, pos + 16, 8);
|
||||
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
{
|
||||
Array.Reverse(dest, pos + 0, 8);
|
||||
Array.Reverse(dest, pos + 8, 8);
|
||||
Array.Reverse(dest, pos + 16, 8);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Static Methods
|
||||
|
||||
public static Vector3d Add(Vector3d value1, Vector3d value2)
|
||||
{
|
||||
value1.X += value2.X;
|
||||
value1.Y += value2.Y;
|
||||
value1.Z += value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector3d Clamp(Vector3d value1, Vector3d min, Vector3d max)
|
||||
{
|
||||
return new Vector3d(
|
||||
Utils.Clamp(value1.X, min.X, max.X),
|
||||
Utils.Clamp(value1.Y, min.Y, max.Y),
|
||||
Utils.Clamp(value1.Z, min.Z, max.Z));
|
||||
}
|
||||
|
||||
public static Vector3d Cross(Vector3d value1, Vector3d value2)
|
||||
{
|
||||
return new Vector3d(
|
||||
value1.Y * value2.Z - value2.Y * value1.Z,
|
||||
value1.Z * value2.X - value2.Z * value1.X,
|
||||
value1.X * value2.Y - value2.X * value1.Y);
|
||||
}
|
||||
|
||||
public static double Distance(Vector3d value1, Vector3d value2)
|
||||
{
|
||||
return Math.Sqrt(DistanceSquared(value1, value2));
|
||||
}
|
||||
|
||||
public static double DistanceSquared(Vector3d value1, Vector3d value2)
|
||||
{
|
||||
return
|
||||
(value1.X - value2.X) * (value1.X - value2.X) +
|
||||
(value1.Y - value2.Y) * (value1.Y - value2.Y) +
|
||||
(value1.Z - value2.Z) * (value1.Z - value2.Z);
|
||||
}
|
||||
|
||||
public static Vector3d Divide(Vector3d value1, Vector3d value2)
|
||||
{
|
||||
value1.X /= value2.X;
|
||||
value1.Y /= value2.Y;
|
||||
value1.Z /= value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector3d Divide(Vector3d value1, double value2)
|
||||
{
|
||||
double factor = 1d / value2;
|
||||
value1.X *= factor;
|
||||
value1.Y *= factor;
|
||||
value1.Z *= factor;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static double Dot(Vector3d value1, Vector3d value2)
|
||||
{
|
||||
return value1.X * value2.X + value1.Y * value2.Y + value1.Z * value2.Z;
|
||||
}
|
||||
|
||||
public static Vector3d Lerp(Vector3d value1, Vector3d value2, double amount)
|
||||
{
|
||||
return new Vector3d(
|
||||
Utils.Lerp(value1.X, value2.X, amount),
|
||||
Utils.Lerp(value1.Y, value2.Y, amount),
|
||||
Utils.Lerp(value1.Z, value2.Z, amount));
|
||||
}
|
||||
|
||||
public static Vector3d Max(Vector3d value1, Vector3d value2)
|
||||
{
|
||||
return new Vector3d(
|
||||
Math.Max(value1.X, value2.X),
|
||||
Math.Max(value1.Y, value2.Y),
|
||||
Math.Max(value1.Z, value2.Z));
|
||||
}
|
||||
|
||||
public static Vector3d Min(Vector3d value1, Vector3d value2)
|
||||
{
|
||||
return new Vector3d(
|
||||
Math.Min(value1.X, value2.X),
|
||||
Math.Min(value1.Y, value2.Y),
|
||||
Math.Min(value1.Z, value2.Z));
|
||||
}
|
||||
|
||||
public static Vector3d Multiply(Vector3d value1, Vector3d value2)
|
||||
{
|
||||
value1.X *= value2.X;
|
||||
value1.Y *= value2.Y;
|
||||
value1.Z *= value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector3d Multiply(Vector3d value1, double scaleFactor)
|
||||
{
|
||||
value1.X *= scaleFactor;
|
||||
value1.Y *= scaleFactor;
|
||||
value1.Z *= scaleFactor;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector3d Negate(Vector3d value)
|
||||
{
|
||||
value.X = -value.X;
|
||||
value.Y = -value.Y;
|
||||
value.Z = -value.Z;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Vector3d Normalize(Vector3d value)
|
||||
{
|
||||
double factor = Distance(value, Zero);
|
||||
if (factor > Double.Epsilon)
|
||||
{
|
||||
factor = 1d / factor;
|
||||
value.X *= factor;
|
||||
value.Y *= factor;
|
||||
value.Z *= factor;
|
||||
}
|
||||
else
|
||||
{
|
||||
value.X = 0d;
|
||||
value.Y = 0d;
|
||||
value.Z = 0d;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a vector from a string
|
||||
/// </summary>
|
||||
/// <param name="val">A string representation of a 3D vector, enclosed
|
||||
/// in arrow brackets and separated by commas</param>
|
||||
public static Vector3d Parse(string val)
|
||||
{
|
||||
char[] splitChar = { ',' };
|
||||
string[] split = val.Replace("<", String.Empty).Replace(">", String.Empty).Split(splitChar);
|
||||
return new Vector3d(
|
||||
Double.Parse(split[0].Trim(), Utils.EnUsCulture),
|
||||
Double.Parse(split[1].Trim(), Utils.EnUsCulture),
|
||||
Double.Parse(split[2].Trim(), Utils.EnUsCulture));
|
||||
}
|
||||
|
||||
public static bool TryParse(string val, out Vector3d result)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = Parse(val);
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
result = Vector3d.Zero;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpolates between two vectors using a cubic equation
|
||||
/// </summary>
|
||||
public static Vector3d SmoothStep(Vector3d value1, Vector3d value2, double amount)
|
||||
{
|
||||
return new Vector3d(
|
||||
Utils.SmoothStep(value1.X, value2.X, amount),
|
||||
Utils.SmoothStep(value1.Y, value2.Y, amount),
|
||||
Utils.SmoothStep(value1.Z, value2.Z, amount));
|
||||
}
|
||||
|
||||
public static Vector3d Subtract(Vector3d value1, Vector3d value2)
|
||||
{
|
||||
value1.X -= value2.X;
|
||||
value1.Y -= value2.Y;
|
||||
value1.Z -= value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
#endregion Static Methods
|
||||
|
||||
#region Overrides
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return (obj is Vector3d) ? this == (Vector3d)obj : false;
|
||||
}
|
||||
|
||||
public bool Equals(Vector3d other)
|
||||
{
|
||||
return this == other;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a formatted string representation of the vector
|
||||
/// </summary>
|
||||
/// <returns>A string representation of the vector</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format(Utils.EnUsCulture, "<{0}, {1}, {2}>", X, Y, Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a string representation of the vector elements with up to three
|
||||
/// decimal digits and separated by spaces only
|
||||
/// </summary>
|
||||
/// <returns>Raw string representation of the vector</returns>
|
||||
public string ToRawString()
|
||||
{
|
||||
CultureInfo enUs = new CultureInfo("en-us");
|
||||
enUs.NumberFormat.NumberDecimalDigits = 3;
|
||||
|
||||
return String.Format(enUs, "{0} {1} {2}", X, Y, Z);
|
||||
}
|
||||
|
||||
#endregion Overrides
|
||||
|
||||
#region Operators
|
||||
|
||||
public static bool operator ==(Vector3d value1, Vector3d value2)
|
||||
{
|
||||
return value1.X == value2.X
|
||||
&& value1.Y == value2.Y
|
||||
&& value1.Z == value2.Z;
|
||||
}
|
||||
|
||||
public static bool operator !=(Vector3d value1, Vector3d value2)
|
||||
{
|
||||
return !(value1 == value2);
|
||||
}
|
||||
|
||||
public static Vector3d operator +(Vector3d value1, Vector3d value2)
|
||||
{
|
||||
value1.X += value2.X;
|
||||
value1.Y += value2.Y;
|
||||
value1.Z += value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector3d operator -(Vector3d value)
|
||||
{
|
||||
value.X = -value.X;
|
||||
value.Y = -value.Y;
|
||||
value.Z = -value.Z;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Vector3d operator -(Vector3d value1, Vector3d value2)
|
||||
{
|
||||
value1.X -= value2.X;
|
||||
value1.Y -= value2.Y;
|
||||
value1.Z -= value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector3d operator *(Vector3d value1, Vector3d value2)
|
||||
{
|
||||
value1.X *= value2.X;
|
||||
value1.Y *= value2.Y;
|
||||
value1.Z *= value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector3d operator *(Vector3d value, double scaleFactor)
|
||||
{
|
||||
value.X *= scaleFactor;
|
||||
value.Y *= scaleFactor;
|
||||
value.Z *= scaleFactor;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Vector3d operator /(Vector3d value1, Vector3d value2)
|
||||
{
|
||||
value1.X /= value2.X;
|
||||
value1.Y /= value2.Y;
|
||||
value1.Z /= value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector3d operator /(Vector3d value, double divider)
|
||||
{
|
||||
double factor = 1d / divider;
|
||||
value.X *= factor;
|
||||
value.Y *= factor;
|
||||
value.Z *= factor;
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cross product between two vectors
|
||||
/// </summary>
|
||||
public static Vector3d operator %(Vector3d value1, Vector3d value2)
|
||||
{
|
||||
return Cross(value1, value2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit casting for Vector3 > Vector3d
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static implicit operator Vector3d(Vector3 value)
|
||||
{
|
||||
return new Vector3d(value);
|
||||
}
|
||||
|
||||
#endregion Operators
|
||||
|
||||
/// <summary>A vector with a value of 0,0,0</summary>
|
||||
public readonly static Vector3d Zero = new Vector3d();
|
||||
/// <summary>A vector with a value of 1,1,1</summary>
|
||||
public readonly static Vector3d One = new Vector3d();
|
||||
/// <summary>A unit vector facing forward (X axis), value of 1,0,0</summary>
|
||||
public readonly static Vector3d UnitX = new Vector3d(1d, 0d, 0d);
|
||||
/// <summary>A unit vector facing left (Y axis), value of 0,1,0</summary>
|
||||
public readonly static Vector3d UnitY = new Vector3d(0d, 1d, 0d);
|
||||
/// <summary>A unit vector facing up (Z axis), value of 0,0,1</summary>
|
||||
public readonly static Vector3d UnitZ = new Vector3d(0d, 0d, 1d);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,554 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2014, openmetaverse.org
|
||||
* All rights reserved.
|
||||
*
|
||||
* - Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Neither the name of the openmetaverse.org nor the names
|
||||
* of its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Globalization;
|
||||
|
||||
namespace OpenMetaverse
|
||||
{
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Vector4 : IComparable<Vector4>, IEquatable<Vector4>
|
||||
{
|
||||
/// <summary>X value</summary>
|
||||
public float X;
|
||||
/// <summary>Y value</summary>
|
||||
public float Y;
|
||||
/// <summary>Z value</summary>
|
||||
public float Z;
|
||||
/// <summary>W value</summary>
|
||||
public float W;
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Vector4(float x, float y, float z, float w)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
W = w;
|
||||
}
|
||||
|
||||
public Vector4(Vector2 value, float z, float w)
|
||||
{
|
||||
X = value.X;
|
||||
Y = value.Y;
|
||||
Z = z;
|
||||
W = w;
|
||||
}
|
||||
|
||||
public Vector4(Vector3 value, float w)
|
||||
{
|
||||
X = value.X;
|
||||
Y = value.Y;
|
||||
Z = value.Z;
|
||||
W = w;
|
||||
}
|
||||
|
||||
public Vector4(float value)
|
||||
{
|
||||
X = value;
|
||||
Y = value;
|
||||
Z = value;
|
||||
W = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor, builds a vector from a byte array
|
||||
/// </summary>
|
||||
/// <param name="byteArray">Byte array containing four four-byte floats</param>
|
||||
/// <param name="pos">Beginning position in the byte array</param>
|
||||
public Vector4(byte[] byteArray, int pos)
|
||||
{
|
||||
X = Y = Z = W = 0f;
|
||||
FromBytes(byteArray, pos);
|
||||
}
|
||||
|
||||
public Vector4(Vector4 value)
|
||||
{
|
||||
X = value.X;
|
||||
Y = value.Y;
|
||||
Z = value.Z;
|
||||
W = value.W;
|
||||
}
|
||||
|
||||
#endregion Constructors
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public float Length()
|
||||
{
|
||||
return (float)Math.Sqrt(DistanceSquared(this, Zero));
|
||||
}
|
||||
|
||||
public float LengthSquared()
|
||||
{
|
||||
return DistanceSquared(this, Zero);
|
||||
}
|
||||
|
||||
public void Normalize()
|
||||
{
|
||||
this = Normalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test if this vector is equal to another vector, within a given
|
||||
/// tolerance range
|
||||
/// </summary>
|
||||
/// <param name="vec">Vector to test against</param>
|
||||
/// <param name="tolerance">The acceptable magnitude of difference
|
||||
/// between the two vectors</param>
|
||||
/// <returns>True if the magnitude of difference between the two vectors
|
||||
/// is less than the given tolerance, otherwise false</returns>
|
||||
public bool ApproxEquals(Vector4 vec, float tolerance)
|
||||
{
|
||||
Vector4 diff = this - vec;
|
||||
return (diff.LengthSquared() <= tolerance * tolerance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IComparable.CompareTo implementation
|
||||
/// </summary>
|
||||
public int CompareTo(Vector4 vector)
|
||||
{
|
||||
return Length().CompareTo(vector.Length());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test if this vector is composed of all finite numbers
|
||||
/// </summary>
|
||||
public bool IsFinite()
|
||||
{
|
||||
return (Utils.IsFinite(X) && Utils.IsFinite(Y) && Utils.IsFinite(Z) && Utils.IsFinite(W));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a vector from a byte array
|
||||
/// </summary>
|
||||
/// <param name="byteArray">Byte array containing a 16 byte vector</param>
|
||||
/// <param name="pos">Beginning position in the byte array</param>
|
||||
public void FromBytes(byte[] byteArray, int pos)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
{
|
||||
// Big endian architecture
|
||||
byte[] conversionBuffer = new byte[16];
|
||||
|
||||
Buffer.BlockCopy(byteArray, pos, conversionBuffer, 0, 16);
|
||||
|
||||
Array.Reverse(conversionBuffer, 0, 4);
|
||||
Array.Reverse(conversionBuffer, 4, 4);
|
||||
Array.Reverse(conversionBuffer, 8, 4);
|
||||
Array.Reverse(conversionBuffer, 12, 4);
|
||||
|
||||
X = BitConverter.ToSingle(conversionBuffer, 0);
|
||||
Y = BitConverter.ToSingle(conversionBuffer, 4);
|
||||
Z = BitConverter.ToSingle(conversionBuffer, 8);
|
||||
W = BitConverter.ToSingle(conversionBuffer, 12);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Little endian architecture
|
||||
X = BitConverter.ToSingle(byteArray, pos);
|
||||
Y = BitConverter.ToSingle(byteArray, pos + 4);
|
||||
Z = BitConverter.ToSingle(byteArray, pos + 8);
|
||||
W = BitConverter.ToSingle(byteArray, pos + 12);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the raw bytes for this vector
|
||||
/// </summary>
|
||||
/// <returns>A 16 byte array containing X, Y, Z, and W</returns>
|
||||
public byte[] GetBytes()
|
||||
{
|
||||
byte[] byteArray = new byte[16];
|
||||
ToBytes(byteArray, 0);
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the raw bytes for this vector to a byte array
|
||||
/// </summary>
|
||||
/// <param name="dest">Destination byte array</param>
|
||||
/// <param name="pos">Position in the destination array to start
|
||||
/// writing. Must be at least 16 bytes before the end of the array</param>
|
||||
public void ToBytes(byte[] dest, int pos)
|
||||
{
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(X), 0, dest, pos + 0, 4);
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(Y), 0, dest, pos + 4, 4);
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(Z), 0, dest, pos + 8, 4);
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(W), 0, dest, pos + 12, 4);
|
||||
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
{
|
||||
Array.Reverse(dest, pos + 0, 4);
|
||||
Array.Reverse(dest, pos + 4, 4);
|
||||
Array.Reverse(dest, pos + 8, 4);
|
||||
Array.Reverse(dest, pos + 12, 4);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Static Methods
|
||||
|
||||
public static Vector4 Add(Vector4 value1, Vector4 value2)
|
||||
{
|
||||
value1.W += value2.W;
|
||||
value1.X += value2.X;
|
||||
value1.Y += value2.Y;
|
||||
value1.Z += value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector4 Clamp(Vector4 value1, Vector4 min, Vector4 max)
|
||||
{
|
||||
return new Vector4(
|
||||
Utils.Clamp(value1.X, min.X, max.X),
|
||||
Utils.Clamp(value1.Y, min.Y, max.Y),
|
||||
Utils.Clamp(value1.Z, min.Z, max.Z),
|
||||
Utils.Clamp(value1.W, min.W, max.W));
|
||||
}
|
||||
|
||||
public static float Distance(Vector4 value1, Vector4 value2)
|
||||
{
|
||||
return (float)Math.Sqrt(DistanceSquared(value1, value2));
|
||||
}
|
||||
|
||||
public static float DistanceSquared(Vector4 value1, Vector4 value2)
|
||||
{
|
||||
return
|
||||
(value1.W - value2.W) * (value1.W - value2.W) +
|
||||
(value1.X - value2.X) * (value1.X - value2.X) +
|
||||
(value1.Y - value2.Y) * (value1.Y - value2.Y) +
|
||||
(value1.Z - value2.Z) * (value1.Z - value2.Z);
|
||||
}
|
||||
|
||||
public static Vector4 Divide(Vector4 value1, Vector4 value2)
|
||||
{
|
||||
value1.W /= value2.W;
|
||||
value1.X /= value2.X;
|
||||
value1.Y /= value2.Y;
|
||||
value1.Z /= value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector4 Divide(Vector4 value1, float divider)
|
||||
{
|
||||
float factor = 1f / divider;
|
||||
value1.W *= factor;
|
||||
value1.X *= factor;
|
||||
value1.Y *= factor;
|
||||
value1.Z *= factor;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static float Dot(Vector4 vector1, Vector4 vector2)
|
||||
{
|
||||
return vector1.X * vector2.X + vector1.Y * vector2.Y + vector1.Z * vector2.Z + vector1.W * vector2.W;
|
||||
}
|
||||
|
||||
public static Vector4 Lerp(Vector4 value1, Vector4 value2, float amount)
|
||||
{
|
||||
return new Vector4(
|
||||
Utils.Lerp(value1.X, value2.X, amount),
|
||||
Utils.Lerp(value1.Y, value2.Y, amount),
|
||||
Utils.Lerp(value1.Z, value2.Z, amount),
|
||||
Utils.Lerp(value1.W, value2.W, amount));
|
||||
}
|
||||
|
||||
public static Vector4 Max(Vector4 value1, Vector4 value2)
|
||||
{
|
||||
return new Vector4(
|
||||
Math.Max(value1.X, value2.X),
|
||||
Math.Max(value1.Y, value2.Y),
|
||||
Math.Max(value1.Z, value2.Z),
|
||||
Math.Max(value1.W, value2.W));
|
||||
}
|
||||
|
||||
public static Vector4 Min(Vector4 value1, Vector4 value2)
|
||||
{
|
||||
return new Vector4(
|
||||
Math.Min(value1.X, value2.X),
|
||||
Math.Min(value1.Y, value2.Y),
|
||||
Math.Min(value1.Z, value2.Z),
|
||||
Math.Min(value1.W, value2.W));
|
||||
}
|
||||
|
||||
public static Vector4 Multiply(Vector4 value1, Vector4 value2)
|
||||
{
|
||||
value1.W *= value2.W;
|
||||
value1.X *= value2.X;
|
||||
value1.Y *= value2.Y;
|
||||
value1.Z *= value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector4 Multiply(Vector4 value1, float scaleFactor)
|
||||
{
|
||||
value1.W *= scaleFactor;
|
||||
value1.X *= scaleFactor;
|
||||
value1.Y *= scaleFactor;
|
||||
value1.Z *= scaleFactor;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector4 Negate(Vector4 value)
|
||||
{
|
||||
value.X = -value.X;
|
||||
value.Y = -value.Y;
|
||||
value.Z = -value.Z;
|
||||
value.W = -value.W;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Vector4 Normalize(Vector4 vector)
|
||||
{
|
||||
const float MAG_THRESHOLD = 0.0000001f;
|
||||
float factor = DistanceSquared(vector, Zero);
|
||||
if (factor > MAG_THRESHOLD)
|
||||
{
|
||||
factor = 1f / (float)Math.Sqrt(factor);
|
||||
vector.X *= factor;
|
||||
vector.Y *= factor;
|
||||
vector.Z *= factor;
|
||||
vector.W *= factor;
|
||||
}
|
||||
else
|
||||
{
|
||||
vector.X = 0f;
|
||||
vector.Y = 0f;
|
||||
vector.Z = 0f;
|
||||
vector.W = 0f;
|
||||
}
|
||||
return vector;
|
||||
}
|
||||
|
||||
public static Vector4 SmoothStep(Vector4 value1, Vector4 value2, float amount)
|
||||
{
|
||||
return new Vector4(
|
||||
Utils.SmoothStep(value1.X, value2.X, amount),
|
||||
Utils.SmoothStep(value1.Y, value2.Y, amount),
|
||||
Utils.SmoothStep(value1.Z, value2.Z, amount),
|
||||
Utils.SmoothStep(value1.W, value2.W, amount));
|
||||
}
|
||||
|
||||
public static Vector4 Subtract(Vector4 value1, Vector4 value2)
|
||||
{
|
||||
value1.W -= value2.W;
|
||||
value1.X -= value2.X;
|
||||
value1.Y -= value2.Y;
|
||||
value1.Z -= value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector4 Transform(Vector2 position, Matrix4 matrix)
|
||||
{
|
||||
return new Vector4(
|
||||
(position.X * matrix.M11) + (position.Y * matrix.M21) + matrix.M41,
|
||||
(position.X * matrix.M12) + (position.Y * matrix.M22) + matrix.M42,
|
||||
(position.X * matrix.M13) + (position.Y * matrix.M23) + matrix.M43,
|
||||
(position.X * matrix.M14) + (position.Y * matrix.M24) + matrix.M44);
|
||||
}
|
||||
|
||||
public static Vector4 Transform(Vector3 position, Matrix4 matrix)
|
||||
{
|
||||
return new Vector4(
|
||||
(position.X * matrix.M11) + (position.Y * matrix.M21) + (position.Z * matrix.M31) + matrix.M41,
|
||||
(position.X * matrix.M12) + (position.Y * matrix.M22) + (position.Z * matrix.M32) + matrix.M42,
|
||||
(position.X * matrix.M13) + (position.Y * matrix.M23) + (position.Z * matrix.M33) + matrix.M43,
|
||||
(position.X * matrix.M14) + (position.Y * matrix.M24) + (position.Z * matrix.M34) + matrix.M44);
|
||||
}
|
||||
|
||||
public static Vector4 Transform(Vector4 vector, Matrix4 matrix)
|
||||
{
|
||||
return new Vector4(
|
||||
(vector.X * matrix.M11) + (vector.Y * matrix.M21) + (vector.Z * matrix.M31) + (vector.W * matrix.M41),
|
||||
(vector.X * matrix.M12) + (vector.Y * matrix.M22) + (vector.Z * matrix.M32) + (vector.W * matrix.M42),
|
||||
(vector.X * matrix.M13) + (vector.Y * matrix.M23) + (vector.Z * matrix.M33) + (vector.W * matrix.M43),
|
||||
(vector.X * matrix.M14) + (vector.Y * matrix.M24) + (vector.Z * matrix.M34) + (vector.W * matrix.M44));
|
||||
}
|
||||
|
||||
public static Vector4 Parse(string val)
|
||||
{
|
||||
char[] splitChar = { ',' };
|
||||
string[] split = val.Replace("<", String.Empty).Replace(">", String.Empty).Split(splitChar);
|
||||
return new Vector4(
|
||||
float.Parse(split[0].Trim(), Utils.EnUsCulture),
|
||||
float.Parse(split[1].Trim(), Utils.EnUsCulture),
|
||||
float.Parse(split[2].Trim(), Utils.EnUsCulture),
|
||||
float.Parse(split[3].Trim(), Utils.EnUsCulture));
|
||||
}
|
||||
|
||||
public static bool TryParse(string val, out Vector4 result)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = Parse(val);
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
result = new Vector4();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Static Methods
|
||||
|
||||
#region Overrides
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return (obj is Vector4) ? this == (Vector4)obj : false;
|
||||
}
|
||||
|
||||
public bool Equals(Vector4 other)
|
||||
{
|
||||
return W == other.W
|
||||
&& X == other.X
|
||||
&& Y == other.Y
|
||||
&& Z == other.Z;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode() ^ W.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format(Utils.EnUsCulture, "<{0}, {1}, {2}, {3}>", X, Y, Z, W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a string representation of the vector elements with up to three
|
||||
/// decimal digits and separated by spaces only
|
||||
/// </summary>
|
||||
/// <returns>Raw string representation of the vector</returns>
|
||||
public string ToRawString()
|
||||
{
|
||||
CultureInfo enUs = new CultureInfo("en-us");
|
||||
enUs.NumberFormat.NumberDecimalDigits = 3;
|
||||
|
||||
return String.Format(enUs, "{0} {1} {2} {3}", X, Y, Z, W);
|
||||
}
|
||||
|
||||
#endregion Overrides
|
||||
|
||||
#region Operators
|
||||
|
||||
public static bool operator ==(Vector4 value1, Vector4 value2)
|
||||
{
|
||||
return value1.W == value2.W
|
||||
&& value1.X == value2.X
|
||||
&& value1.Y == value2.Y
|
||||
&& value1.Z == value2.Z;
|
||||
}
|
||||
|
||||
public static bool operator !=(Vector4 value1, Vector4 value2)
|
||||
{
|
||||
return !(value1 == value2);
|
||||
}
|
||||
|
||||
public static Vector4 operator +(Vector4 value1, Vector4 value2)
|
||||
{
|
||||
value1.W += value2.W;
|
||||
value1.X += value2.X;
|
||||
value1.Y += value2.Y;
|
||||
value1.Z += value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector4 operator -(Vector4 value)
|
||||
{
|
||||
return new Vector4(-value.X, -value.Y, -value.Z, -value.W);
|
||||
}
|
||||
|
||||
public static Vector4 operator -(Vector4 value1, Vector4 value2)
|
||||
{
|
||||
value1.W -= value2.W;
|
||||
value1.X -= value2.X;
|
||||
value1.Y -= value2.Y;
|
||||
value1.Z -= value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector4 operator *(Vector4 value1, Vector4 value2)
|
||||
{
|
||||
value1.W *= value2.W;
|
||||
value1.X *= value2.X;
|
||||
value1.Y *= value2.Y;
|
||||
value1.Z *= value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector4 operator *(Vector4 value1, float scaleFactor)
|
||||
{
|
||||
value1.W *= scaleFactor;
|
||||
value1.X *= scaleFactor;
|
||||
value1.Y *= scaleFactor;
|
||||
value1.Z *= scaleFactor;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector4 operator /(Vector4 value1, Vector4 value2)
|
||||
{
|
||||
value1.W /= value2.W;
|
||||
value1.X /= value2.X;
|
||||
value1.Y /= value2.Y;
|
||||
value1.Z /= value2.Z;
|
||||
return value1;
|
||||
}
|
||||
|
||||
public static Vector4 operator /(Vector4 value1, float divider)
|
||||
{
|
||||
float factor = 1f / divider;
|
||||
value1.W *= factor;
|
||||
value1.X *= factor;
|
||||
value1.Y *= factor;
|
||||
value1.Z *= factor;
|
||||
return value1;
|
||||
}
|
||||
|
||||
#endregion Operators
|
||||
|
||||
/// <summary>A vector with a value of 0,0,0,0</summary>
|
||||
public readonly static Vector4 Zero = new Vector4();
|
||||
/// <summary>A vector with a value of 1,1,1,1</summary>
|
||||
public readonly static Vector4 One = new Vector4(1f, 1f, 1f, 1f);
|
||||
/// <summary>A vector with a value of 1,0,0,0</summary>
|
||||
public readonly static Vector4 UnitX = new Vector4(1f, 0f, 0f, 0f);
|
||||
/// <summary>A vector with a value of 0,1,0,0</summary>
|
||||
public readonly static Vector4 UnitY = new Vector4(0f, 1f, 0f, 0f);
|
||||
/// <summary>A vector with a value of 0,0,1,0</summary>
|
||||
public readonly static Vector4 UnitZ = new Vector4(0f, 0f, 1f, 0f);
|
||||
/// <summary>A vector with a value of 0,0,0,1</summary>
|
||||
public readonly static Vector4 UnitW = new Vector4(0f, 0f, 0f, 1f);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user