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:
Blake Bourque
2015-06-18 22:15:24 -04:00
commit 0bb2dff347
382 changed files with 273704 additions and 0 deletions
+201
View File
@@ -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; }
}
}
}
+101
View File
@@ -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);
}
}
}
+136
View File
@@ -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;
}
}
}
}
+525
View File
@@ -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);
}
}
+308
View File
@@ -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;
}
}
}
+254
View File
@@ -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
};
}
+560
View File
@@ -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
}
}
+443
View File
@@ -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
}
}
+100
View File
@@ -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;
}
}
}
}
+149
View File
@@ -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>
+48
View File
@@ -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>
+758
View File
@@ -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);
}
}
+42
View File
@@ -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;
}
}
}
+618
View File
@@ -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
}
}
+158
View File
@@ -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]; }
}
}
}
+203
View File
@@ -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;
}
}
}
}
+382
View File
@@ -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();
}
}
+440
View File
@@ -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
+462
View File
@@ -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);
}
}
+596
View File
@@ -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);
}
}
+523
View File
@@ -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);
}
}
+554
View File
@@ -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);
}
}