**The Euler class allows you to directly set/get absolute pitch, yaw or roll values without affecting each other**, for example setting the orientation to face east while preserving the pitch and roll.

Further information you find on the Euler Angle Class page.

It contains an elaborate description, an API reference and examples.

### Table of contents

# .NET Port of Euler Angle Class

The following code is a C# port of the Euler Angle Class for usage with Mogre. Also a VB code is available (created by a converter).

If you have problems, report it in this forum topic.

Thanks to user Kojack for the C++ class and the C# port. (published in 2012)

## Differences to the C++ version

There are several changes to make it follow the style of Mogre instead of Ogre.

- Methods use pascal case (eg. LimitYaw) instead of camel case (eg. limitYaw).
- The methods
**getYaw()**,**setYaw()**,**getPitch()**,**setPitch()**,**getRoll()**and**setRoll()**are replaced by the properties**Yaw**,**Pitch**and**Roll**. - The methods
**yaw**,**pitch**and**roll**are replaced by**AddYaw()**,**AddPitch()**and**AddRoll()**. - The methods
**GetForward()**,**GetRight()**and**GetUp()**are replaced with the properties**Forward**,**Right**and**Up**(read only). - Chaining is currently removed, because methods don't return a self reference.
- Created XML documentation for all members and parameters (including modified and additional text)
- Renamed some parameter names (y, p, r,v, lhs, ...) for a better overview
- new constructor: public
**Euler(Quaternion oriantation)** - new: public String
**ToAxisString()** - new: public String
**ToYawPitchRollString()** - new: public static Euler
**ParseStringYawPitchRoll(**String valueString**)**... NOT TESTED - new: public static Euler
**ParseStringAxisXYZ(**String valueString**)**... NOT TESTED - new helper: private static Single[]
**Parse3Params(**String valueString**)**... NOT TESTED - new: Overloaded versions of
**AddYaw()**,**AddPitch()**,**AddRoll()**for parameter type Degree - new: Embedded helper method
**Matrix3ToEulerAnglesYXZ()**as workaround for the buggy method*Mogre.Matrix3.ToEulerAnglesYXZ()*. (The Mogre source code is fixed now, but older binaries still contain the "NaN bug". The helper method is used by the Euler constuctor with a Quaternion as parameter.)

## Further notes

The **output** value range (co-domain) of the Euler class is:

Yaw -180 .. 180 Pitch -89.99 .. 89.99 Roll -180 .. 180

**Be distrustfully** when **Pitch = +/- 90**. (Gimbal Lock case)

In this case the Pitch value is correct, but the returned Yaw and Roll values can be wrong.

**The best is to keep Pitch in the range -89.99 up to +89.99 degree. ** For lower/higher values it could work fine, but also could cause a "180 degree flip" of axes.

## Testing

A **code snippet for tests** you find in this forum post. You can modify it for your custom test cases. Perhaps it's useful for somebody.

Description:

To check the result of the Euler class this method uses these conversion steps: EulerAngles --> Quaternion --> EulerClass --> EulerAngles Then the input angles and output angles will be compared.

# Source code C#

Source: http://www.ogre3d.org/addonforums/viewtopic.php?p=98163#p98163 (23th January 2012)

using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; using Mogre; using Math = Mogre.Math; namespace MogreEulerAngles { // ABOUT THIS CODE: // // Developed by user Kojack in January 2012 // Extended by Beauty and Tubulii // // Detailed information about usage: // http://www.ogre3d.org/tikiwiki/Euler+Angle+Class // // "official place" of C# version: // http://www.ogre3d.org/tikiwiki/Euler+Angle+Class+Mogre // // For questions and bug reports use this forum topic: // viewtopic.php?f=8&t=29262 // /// <summary> /// This struct manages rotations by use of Euler Angles. /// The rotation will be applied in the order Yaw-Pitch-Roll, which are related to the axis order Y-X-Z. /// It's fully compatible with Ogre::Matrix3::FromEulerAnglesYXZ and Ogre::Matrix3::ToEulerAnglesYXZ. /// For the Yaw angle the standard anticlockwise right handed rotation is used (as common in Ogre). /// The Yaw-Pitch-Roll ordering is most convenient for upright character controllers and cameras. /// </summary> public struct Euler { /// <summary>Get or set the Yaw angle.</summary> public Radian Yaw { get { return mYaw; } set { mYaw = value; mChanged = true; } } /// <summary>Get or set the Pitch angle.</summary> public Radian Pitch { get { return mPitch; } set { mPitch = value; mChanged = true; } } /// <summary>Get or set the Roll angle.</summary> public Radian Roll { get { return mRoll; } set { mRoll = value; mChanged = true; } } /// <summary> /// Constructor to create a new Euler Angle struct from angle values. /// The rotation will be applied in the order Yaw-Pitch- Roll, which are related to the axis order Y-X-Z. /// </summary> public Euler(Radian yaw, Radian pitch, Radian roll) { mYaw = yaw; mPitch = pitch; mRoll = roll; mChanged = true; mCachedQuaternion = Quaternion.IDENTITY; } /// <summary> /// Constructor which calculates the Euler Angles from a quaternion. /// </summary> public Euler(Quaternion oriantation) { Matrix3 rotMat; rotMat = oriantation.ToRotationMatrix(); // BUGGY METHOD (NaN return in some cases) // rotMat.ToEulerAnglesYXZ(out mYaw, out mPitch, out mRoll); // WORKAROUND Boolean isUnique; Matrix3ToEulerAnglesYXZ(rotMat, out mYaw, out mPitch, out mRoll, out isUnique); mChanged = true; mCachedQuaternion = Quaternion.IDENTITY; } /// <summary> /// Apply a relative yaw. (Adds the angle to the current yaw value) /// </summary> /// <param name="yaw">Yaw value as radian</param> public void AddYaw(Radian yaw) { mYaw += yaw; mChanged = true; } /// <summary> /// Apply a relative pitch. (Adds the angle to the current pitch value) /// </summary> /// <param name="pitch">Pitch value as radian</param> public void AddPitch(Radian pitch) { mPitch += pitch; mChanged = true; } /// <summary> /// Apply a relative roll. (Adds the angle to the current roll value) /// </summary> /// <param name="roll">Roll value as radian</param> public void AddRoll(Radian roll) { mRoll += roll; mChanged = true; } /// <summary> /// Apply a relative yaw. (Adds the angle to the current yaw value) /// </summary> /// <param name="yaw">Yaw value as degree</param> public void AddYaw(Degree yaw) { mYaw += yaw; mChanged = true; } /// <summary> /// Apply a relative pitch. (Adds the angle to the current pitch value) /// </summary> /// <param name="pitch">Pitch value as degree</param> public void AddPitch(Degree pitch) { mPitch += pitch; mChanged = true; } /// <summary> /// Apply a relative roll. (Adds the angle to the current roll value) /// </summary> /// <param name="roll">Roll value as degree</param> public void AddRoll(Degree roll) { mRoll += roll; mChanged = true; } /// <summary>Get a vector pointing forwards. </summary> public Vector3 Forward { get { return ToQuaternion() * Vector3.NEGATIVE_UNIT_Z; } } /// <summary>Get a vector pointing to the right.</summary> public Vector3 Right { get { return ToQuaternion() * Vector3.UNIT_X; } } /// <summary> Get a vector pointing up.</summary> public Vector3 Up { get { return ToQuaternion() * Vector3.UNIT_Y; } } /// <summary> /// Calculate the quaternion of the euler object. /// The result is cached. It will only be recalculated when the component euler angles are changed. /// </summary> public Quaternion ToQuaternion() { if(mChanged) { mCachedQuaternion = new Quaternion(mYaw, Vector3.UNIT_Y) * new Quaternion(mPitch, Vector3.UNIT_X) * new Quaternion(mRoll, Vector3.UNIT_Z); mChanged = false; } return mCachedQuaternion; } /// <summary> /// Return a String with degree values of the axis rotations (human readable style). /// For example "X-axis: 0° Y-axis: 36° Z-axis: 90°" /// </summary> public String ToAxisString() { return String.Format("X: {0:00}° Y: {1:00}° Z: {2:00}°", Pitch.ValueDegrees, Yaw.ValueDegrees, Roll.ValueDegrees); } /// <summary> /// Return a String with degree values in the applied rotation order (human readable style). /// For example "Yaw: 0° Pitch: 36° Roll: 90°" /// </summary> public String ToYawPitchRollString() { return String.Format("Yaw: {0:00}° Pitch: {1:00}° Roll: {2:00}°", Yaw.ValueDegrees, Pitch.ValueDegrees, Roll.ValueDegrees); } /// <summary> /// Try to parse 3 floating point values from a string, which are seperated by spaces or tabulators. /// Input order for rotation values:: Yaw, Pitch, Roll (all in degree) /// If success, an Euler struct will be returned. /// If parsing failed, a FormatException will be thrown. /// Example: "111 .99 -66" /// </summary> /// <param name="valueString">String which contains 3 floating point values</param> /// <remarks> /// Multiple and mixed usage of space/tabulator/commas are possible. /// As decimal seperator a dot "." is expected. /// </remarks> /// <returns>Returns an Euler struct or a FormatException</returns> public static Euler ParseStringYawPitchRoll(String valueString) { Single[] values = Parse3Params(valueString); if (values == null) throw new FormatException(String.Format("Can't parse floating point values of string '{0}'", valueString)); return new Euler(new Degree(values[0]), new Degree(values[1]), new Degree(values[2])); } /// <summary> /// Try to parse 3 floating point values from a string, which are seperated by spaces or tabulators or comma. /// Input order for rotation values: X-axis, Y-axis, Z-axis (all in degree) /// If success, an Euler struct will be returned. /// If parsing failed, a FormatException will be thrown. /// Example: "111 .99 -66" /// </summary> /// <param name="valueString">String which contains 3 floating point values</param> /// <remarks> /// Multiple and mixed usage of space/tabulator/commas are possible. /// As decimal seperator a dot "." is expected. /// </remarks> /// <returns>Returns an Euler struct or a FormatException</returns> public static Euler ParseStringAxisXYZ(String valueString) { Single[] values = Parse3Params(valueString); if (values == null) throw new FormatException(String.Format("Can't parse floating point values of string '{0}'", valueString)); return new Euler(new Degree(values[1]), new Degree(values[0]), new Degree(values[2])); } /// <summary> /// Try to parse 3 floating point values from a string, which are seperated by spaces or tabulators or comma. /// If parsing failed, null will be returned. /// Example: "111 .99 -66" /// </summary> /// <param name="valueString">String which contains 3 floating point values</param> /// <remarks> /// Multiple and mixed usage of space/tabulator/commas are possible. /// As decimal seperator a dot "." is expected. /// </remarks> /// <returns>Returns 3 Single values or null</returns> private static Single[] Parse3Params(String valueString) { // Some Regex explanation: // // The "@" prefix in front of the String means: // Backslash are processed as Text instead of special symbols. // Advantage: Just write "\" instead of "\\" for each backslash // // "^" at first position means: No text is allowed before // "$" at the end means: No text is allowed after that // // Floating point values are matched // Expression: "-?\d*\.?\d+" // Examples: "111", "0.111", ".99", "-66" // // Seperator can be tabs or spaces or commas (at least one symbol; mixing is possible) // Expression: "[, \t]+" String val = @"[-\d\.]+"; // simplified (faster) floating point pattern (exact pattern would be @"-?\d*\.?\d+" ) String sep = @"[, \t]+"; // seperator pattern // build target pattern String searchPattern = "^(" + val + ")" + sep + "(" + val + ")" + sep + "(" + val + ")$"; Match match = Regex.Match(valueString, searchPattern); try { if (match.Success) { // Force to parse "." as decimal char. (Can be different with other culture settings. E.g. German culture expect "," instad of ".") System.Globalization.CultureInfo englishCulture = new System.Globalization.CultureInfo("en-US"); Single[] result = new Single[3]; result[0] = Convert.ToSingle(match.Groups[1].Value, englishCulture); result[1] = Convert.ToSingle(match.Groups[2].Value, englishCulture); result[2] = Convert.ToSingle(match.Groups[3].Value, englishCulture); return result; } else return null; } catch (FormatException) { return null; } catch (OverflowException) { return null; } } // Parse3Params() /// <summary> /// Return the Euler rotation state as quaternion. /// </summary> /// <param name="e">Euler Angle state</param> /// <returns>Rotation state as Quaternion</returns> public static implicit operator Quaternion(Euler e) { return e.ToQuaternion(); } /// <summary> /// Set the yaw and pitch to face in the given direction. /// The direction doesn't need to be normalised. /// Roll is always unaffected. /// </summary> /// <param name="directionVector">Vector which points to the wanted direction</param> /// <param name="setYaw">if false, the yaw isn't changed.</param> /// <param name="setPitch">if false, the pitch isn't changed.</param> public void SetDirection(Vector3 directionVector, Boolean setYaw, Boolean setPitch) { Vector3 d = directionVector.NormalisedCopy; if(setPitch) mPitch = Math.ASin(d.y); if(setYaw) mYaw = Math.ATan2(-d.x, -d.z);//+Math.PI/2.0; mChanged = setYaw || setPitch; } /// <summary> /// Normalise the selected rotations to be within the +/-180 degree range. /// The normalise uses a wrap around, so for example a yaw of 360 degrees becomes 0 degrees, /// and -190 degrees becomes 170. /// By the parameters it's possible to choose which angles should be normalised. /// </summary> /// <param name="normYaw">If true, the angle will be normalised.</param> /// <param name="normPitch">If true, the angle will be normalised.</param> /// <param name="normRoll">If true, the angle will be normalised.</param> /// <remarks></remarks> public void Normalise(Boolean normYaw, Boolean normPitch, Boolean normRoll) { if(normYaw) { Single yaw = mYaw.ValueRadians; if(yaw < -Math.PI) { yaw = (Single)System.Math.IEEERemainder(yaw, Math.PI * 2.0); if(yaw < -Math.PI) { yaw += Math.PI * 2.0f; } mYaw = yaw; mChanged = true; } else if(yaw > Math.PI) { yaw = (Single)System.Math.IEEERemainder(yaw, Math.PI * 2.0f); if(yaw > Math.PI) { yaw -= Math.PI * 2.0f; } mYaw = yaw; mChanged = true; } } if(normPitch) { Single pitch = mPitch.ValueRadians; if(pitch < -Math.PI) { pitch = (Single)System.Math.IEEERemainder(pitch, Math.PI * 2.0f); if(pitch < -Math.PI) { pitch += Math.PI * 2.0f; } mPitch = pitch; mChanged = true; if (Single.IsNaN(mPitch.ValueDegrees)) // DEBUGGING { } // add breakpoint here } else if(pitch > Math.PI) { pitch = (Single)System.Math.IEEERemainder(pitch, Math.PI * 2.0f); if(pitch > Math.PI) { pitch -= Math.PI * 2.0f; } mPitch = pitch; mChanged = true; if (Single.IsNaN(mPitch.ValueDegrees)) // DEBUGGING { } // add breakpoint here } } if(normRoll) { Single roll= mRoll.ValueRadians; if(roll < -Math.PI) { roll = (Single)System.Math.IEEERemainder(roll, Math.PI * 2.0f); if(roll < -Math.PI) { roll += Math.PI * 2.0f; } mRoll = roll; mChanged = true; } else if(roll > Math.PI) { roll = (Single)System.Math.IEEERemainder(roll, Math.PI * 2.0f); if(roll > Math.PI) { roll -= Math.PI * 2.0f; } mRoll = roll; mChanged = true; } } } // Normalise() /// <summary> /// Return the relative euler angles required to rotate from the current forward direction to the specified direction vector. /// The result euler can then be added to the current euler to immediately face dir. /// Rotation is found to face the correct direction. For example, when false a yaw of 1000 degrees and a dir of /// (0,0,-1) will return a -1000 degree yaw. When true, the same yaw and dir would give 80 degrees (1080 degrees faces /// the same way as (0,0,-1). /// The rotation won't flip upside down then roll instead of a 180 degree yaw. /// </summary> /// <param name="direction">...TODO...</param> /// <param name="shortest">If false, the full value of each angle is used. If true, the angles are normalised and the shortest rotation is found to face the correct direction.</param> /// <param name="setYaw">If true the angles are calculated. If false, the angle is set to 0. </param> /// <param name="setPitch">If true the angles are calculated. If false, the angle is set to 0. </param> public Euler GetRotationTo(Vector3 direction, Boolean setYaw, Boolean setPitch, Boolean shortest) { Euler t1 = Euler.IDENTITY; Euler t2; t1.SetDirection(direction, setYaw, setPitch); t2 = t1 - this; if(shortest && setYaw) { t2.Normalise(true, true, true); } return t2; } /// <summary> /// Clamp the yaw angle to a range of +/-limit. /// </summary> /// <param name="limit">Wanted co-domain for the Yaw angle.</param> public void LimitYaw(Radian limit) { if (mYaw > limit) { mYaw = limit; mChanged = true; } else if (mYaw < -limit) { mYaw = -limit; mChanged = true; } } /// <summary> /// Clamp the pitch angle to a range of +/-limit. /// </summary> /// <param name="limit">Wanted co-domain for the Pitch angle.</param> public void LimitPitch(Radian limit) { if (mPitch > limit) { mPitch = limit; mChanged = true; } else if (mPitch < -limit) { mPitch = -limit; mChanged = true; } } /// <summary> /// Clamp the roll angle to a range of +/-limit. /// </summary> /// <param name="limit">Wanted co-domain for the Roll angle.</param> public void LimitRoll(Radian limit) { if (mRoll > limit) { mRoll = limit; mChanged = true; } else if (mRoll < -limit) { mRoll = -limit; mChanged = true; } } /// <summary> /// Port of method <c>Matrix3.ToEulerAnglesYXZ()</c>, from MogreMatrix3.cpp as a workaround for a bug in the Math class. /// (The bug was fixed, but is not present in common used binary files.) /// </summary> /// <param name="matrix">Rotation matrix</param> /// <param name="isUnique">If false, the orientation can be described by different angle combinations. /// In this case the returned angle values can be different than expected.</param> public static void Matrix3ToEulerAnglesYXZ(Matrix3 matrix, out Radian rfYAngle, out Radian rfPAngle, out Radian rfRAngle, out Boolean isUnique) { rfPAngle = Mogre.Math.ASin(-matrix.m12); if (rfPAngle < new Radian(Math.HALF_PI)) { if (rfPAngle > new Radian(-Math.HALF_PI)) { rfYAngle = Math.ATan2(matrix.m02, matrix.m22); rfRAngle = Math.ATan2(matrix.m10, matrix.m11); isUnique = true; return; } else { // WARNING. Not a unique solution. Radian fRmY = Math.ATan2(-matrix.m01, matrix.m00); rfRAngle = new Radian(0f); // any angle works rfYAngle = rfRAngle - fRmY; isUnique = false; return; } } else { // WARNING. Not a unique solution. Radian fRpY = Math.ATan2(-matrix.m01, matrix.m00); rfRAngle = new Radian(0f); // any angle works rfYAngle = fRpY - rfRAngle; isUnique = false; return; } // "Original" CODE FROM CLASS Matrix3.ToEulerAnglesYXZ() //rfPAngle = Mogre::Math::ASin(-m12); //if ( rfPAngle < Radian(Math::HALF_PI) ) //{ // if ( rfPAngle > Radian(-Math::HALF_PI) ) // { // rfYAngle = System::Math::Atan2(m02,m22); // rfRAngle = System::Math::Atan2(m10,m11); // return true; // } // else // { // // WARNING. Not a unique solution. // Radian fRmY = System::Math::Atan2(-m01,m00); // rfRAngle = Radian(0.0); // any angle works // rfYAngle = rfRAngle - fRmY; // return false; // } //} //else //{ // // WARNING. Not a unique solution. // Radian fRpY = System::Math::Atan2(-m01,m00); // rfRAngle = Radian(0.0); // any angle works // rfYAngle = fRpY - rfRAngle; // return false; //} } // Matrix3ToEulerAnglesYXZ() /// <summary> /// Add two euler objects. /// </summary> /// <returns>Calculation result</returns> public static Euler operator +(Euler lhs, Euler rhs) { return new Euler(lhs.Yaw + rhs.Yaw, lhs.Pitch + rhs.Pitch, lhs.Roll + rhs.Roll); } /// <summary> /// Subtract two euler objects. This finds the difference as relative angles. /// </summary> /// <returns>Calculation result</returns> public static Euler operator-(Euler lhs, Euler rhs) { return new Euler(lhs.Yaw - rhs.Yaw, lhs.Pitch - rhs.Pitch, lhs.Roll - rhs.Roll); } /// <summary> /// Interpolate each euler angle by the given factor. /// (Each angle will be multiplied with the factor.) /// </summary> /// <returns>Calculation result</returns> public static Euler operator *(Euler lhs, Single factor) { return new Euler(lhs.Yaw * factor, lhs.Pitch * factor, lhs.Roll * factor); } /// <summary> /// Interpolate the euler angles by lhs. /// (Each angle will be multiplied with the factor.) /// </summary> /// <returns>Calculation result</returns> public static Euler operator *(Single factor, Euler rhs) { return new Euler(factor * rhs.Yaw, factor * rhs.Pitch, factor * rhs.Roll); } /// <summary> /// Apply the euler rotation to the vector rhs. /// The calculation is equal to: quaternion*vector /// </summary> /// <returns>Calculation result</returns> public static Vector3 operator *(Euler lhs, Vector3 rhs) { return lhs.ToQuaternion() * rhs; } /// <summary>Base settings (all angles are 0)</summary> public static Euler IDENTITY = new Euler(new Radian(0), new Radian(0), new Radian(0)); /// <summary>Rotation around the Y axis.</summary> private Radian mYaw; /// <summary>Rotation around the X axis.</summary> private Radian mPitch; /// <summary>Rotation around the Z axis.</summary> private Radian mRoll; /// <summary>Is the cached quaternion out of date?</summary> private bool mChanged; /// <summary>Cached quaternion equivalent of this euler object.</summary> private Quaternion mCachedQuaternion; } // struct Euler } // namespace

# Source code Visual Basic

Converted by C#<->VB Converter of developerfusion.com.

In a quick test the converter had no errors. If the VB code really works, wasn't tested yet. (Please give us a report after testing.)

Imports System.Collections.Generic Imports System.Text Imports System.Text.RegularExpressions Imports Mogre Imports Math = Mogre.Math Namespace MogreEulerAngles ' ABOUT THIS CODE: ' ' Developed by user Kojack in January 2012 ' Extended by Beauty and Tubulii ' ' Detailed information about usage: ' http://www.ogre3d.org/tikiwiki/Euler+Angle+Class ' ' "official place" of C# version: ' http://www.ogre3d.org/tikiwiki/Euler+Angle+Class+Mogre ' ' For questions and bug reports use this forum topic: ' viewtopic.php?f=8&t=29262 ' ''' <summary> ''' This struct manages rotations by use of Euler Angles. ''' The rotation will be applied in the order Yaw-Pitch-Roll, which are related to the axis order Y-X-Z. ''' It's fully compatible with Ogre::Matrix3::FromEulerAnglesYXZ and Ogre::Matrix3::ToEulerAnglesYXZ. ''' For the Yaw angle the standard anticlockwise right handed rotation is used (as common in Ogre). ''' The Yaw-Pitch-Roll ordering is most convenient for upright character controllers and cameras. ''' </summary> Public Structure Euler ''' <summary>Get or set the Yaw angle.</summary> Public Property Yaw() As Radian Get Return mYaw End Get Set mYaw = value mChanged = True End Set End Property ''' <summary>Get or set the Pitch angle.</summary> Public Property Pitch() As Radian Get Return mPitch End Get Set mPitch = value mChanged = True End Set End Property ''' <summary>Get or set the Roll angle.</summary> Public Property Roll() As Radian Get Return mRoll End Get Set mRoll = value mChanged = True End Set End Property ''' <summary> ''' Constructor to create a new Euler Angle struct from angle values. ''' The rotation will be applied in the order Yaw-Pitch- Roll, which are related to the axis order Y-X-Z. ''' </summary> Public Sub New(yaw As Radian, pitch As Radian, roll As Radian) mYaw = yaw mPitch = pitch mRoll = roll mChanged = True mCachedQuaternion = Quaternion.IDENTITY End Sub ''' <summary> ''' Constructor which calculates the Euler Angles from a quaternion. ''' </summary> Public Sub New(oriantation As Quaternion) Dim rotMat As Matrix3 rotMat = oriantation.ToRotationMatrix() ' BUGGY METHOD (NaN return in some cases) ' rotMat.ToEulerAnglesYXZ(out mYaw, out mPitch, out mRoll); ' WORKAROUND Dim isUnique As [Boolean] Matrix3ToEulerAnglesYXZ(rotMat, mYaw, mPitch, mRoll, isUnique) mChanged = True mCachedQuaternion = Quaternion.IDENTITY End Sub ''' <summary> ''' Apply a relative yaw. (Adds the angle to the current yaw value) ''' </summary> ''' <param name="yaw">Yaw value as radian</param> Public Sub AddYaw(yaw As Radian) mYaw += yaw mChanged = True End Sub ''' <summary> ''' Apply a relative pitch. (Adds the angle to the current pitch value) ''' </summary> ''' <param name="pitch">Pitch value as radian</param> Public Sub AddPitch(pitch As Radian) mPitch += pitch mChanged = True End Sub ''' <summary> ''' Apply a relative roll. (Adds the angle to the current roll value) ''' </summary> ''' <param name="roll">Roll value as radian</param> Public Sub AddRoll(roll As Radian) mRoll += roll mChanged = True End Sub ''' <summary> ''' Apply a relative yaw. (Adds the angle to the current yaw value) ''' </summary> ''' <param name="yaw">Yaw value as degree</param> Public Sub AddYaw(yaw As Degree) mYaw += yaw mChanged = True End Sub ''' <summary> ''' Apply a relative pitch. (Adds the angle to the current pitch value) ''' </summary> ''' <param name="pitch">Pitch value as degree</param> Public Sub AddPitch(pitch As Degree) mPitch += pitch mChanged = True End Sub ''' <summary> ''' Apply a relative roll. (Adds the angle to the current roll value) ''' </summary> ''' <param name="roll">Roll value as degree</param> Public Sub AddRoll(roll As Degree) mRoll += roll mChanged = True End Sub ''' <summary>Get a vector pointing forwards. </summary> Public ReadOnly Property Forward() As Vector3 Get Return ToQuaternion() * Vector3.NEGATIVE_UNIT_Z End Get End Property ''' <summary>Get a vector pointing to the right.</summary> Public ReadOnly Property Right() As Vector3 Get Return ToQuaternion() * Vector3.UNIT_X End Get End Property ''' <summary> Get a vector pointing up.</summary> Public ReadOnly Property Up() As Vector3 Get Return ToQuaternion() * Vector3.UNIT_Y End Get End Property ''' <summary> ''' Calculate the quaternion of the euler object. ''' The result is cached. It will only be recalculated when the component euler angles are changed. ''' </summary> Public Function ToQuaternion() As Quaternion If mChanged Then mCachedQuaternion = New Quaternion(mYaw, Vector3.UNIT_Y) * New Quaternion(mPitch, Vector3.UNIT_X) * New Quaternion(mRoll, Vector3.UNIT_Z) mChanged = False End If Return mCachedQuaternion End Function ''' <summary> ''' Return a String with degree values of the axis rotations (human readable style). ''' For example "X-axis: 0° Y-axis: 36° Z-axis: 90°" ''' </summary> Public Function ToAxisString() As [String] Return [String].Format("X: {0:00}° Y: {1:00}° Z: {2:00}°", Pitch.ValueDegrees, Yaw.ValueDegrees, Roll.ValueDegrees) End Function ''' <summary> ''' Return a String with degree values in the applied rotation order (human readable style). ''' For example "Yaw: 0° Pitch: 36° Roll: 90°" ''' </summary> Public Function ToYawPitchRollString() As [String] Return [String].Format("Yaw: {0:00}° Pitch: {1:00}° Roll: {2:00}°", Yaw.ValueDegrees, Pitch.ValueDegrees, Roll.ValueDegrees) End Function ''' <summary> ''' Try to parse 3 floating point values from a string, which are seperated by spaces or tabulators. ''' Input order for rotation values:: Yaw, Pitch, Roll (all in degree) ''' If success, an Euler struct will be returned. ''' If parsing failed, a FormatException will be thrown. ''' Example: "111 .99 -66" ''' </summary> ''' <param name="valueString">String which contains 3 floating point values</param> ''' <remarks> ''' Multiple and mixed usage of space/tabulator/commas are possible. ''' As decimal seperator a dot "." is expected. ''' </remarks> ''' <returns>Returns an Euler struct or a FormatException</returns> Public Shared Function ParseStringYawPitchRoll(valueString As [String]) As Euler Dim values As [Single]() = Parse3Params(valueString) If values Is Nothing Then Throw New FormatException([String].Format("Can't parse floating point values of string '{0}'", valueString)) End If Return New Euler(New Degree(values(0)), New Degree(values(1)), New Degree(values(2))) End Function ''' <summary> ''' Try to parse 3 floating point values from a string, which are seperated by spaces or tabulators or comma. ''' Input order for rotation values: X-axis, Y-axis, Z-axis (all in degree) ''' If success, an Euler struct will be returned. ''' If parsing failed, a FormatException will be thrown. ''' Example: "111 .99 -66" ''' </summary> ''' <param name="valueString">String which contains 3 floating point values</param> ''' <remarks> ''' Multiple and mixed usage of space/tabulator/commas are possible. ''' As decimal seperator a dot "." is expected. ''' </remarks> ''' <returns>Returns an Euler struct or a FormatException</returns> Public Shared Function ParseStringAxisXYZ(valueString As [String]) As Euler Dim values As [Single]() = Parse3Params(valueString) If values Is Nothing Then Throw New FormatException([String].Format("Can't parse floating point values of string '{0}'", valueString)) End If Return New Euler(New Degree(values(1)), New Degree(values(0)), New Degree(values(2))) End Function ''' <summary> ''' Try to parse 3 floating point values from a string, which are seperated by spaces or tabulators or comma. ''' If parsing failed, null will be returned. ''' Example: "111 .99 -66" ''' </summary> ''' <param name="valueString">String which contains 3 floating point values</param> ''' <remarks> ''' Multiple and mixed usage of space/tabulator/commas are possible. ''' As decimal seperator a dot "." is expected. ''' </remarks> ''' <returns>Returns 3 Single values or null</returns> Private Shared Function Parse3Params(valueString As [String]) As [Single]() ' Some Regex explanation: ' ' The "@" prefix in front of the String means: ' Backslash are processed as Text instead of special symbols. ' Advantage: Just write "\" instead of "\\" for each backslash ' ' "^" at first position means: No text is allowed before ' "$" at the end means: No text is allowed after that ' ' Floating point values are matched ' Expression: "-?\d*\.?\d+" ' Examples: "111", "0.111", ".99", "-66" ' ' Seperator can be tabs or spaces or commas (at least one symbol; mixing is possible) ' Expression: "[, \t]+" Dim val As [String] = "[-\d\.]+" ' simplified (faster) floating point pattern (exact pattern would be @"-?\d*\.?\d+" ) Dim sep As [String] = "[, \t]+" ' seperator pattern ' build target pattern Dim searchPattern As [String] = "^(" & val & ")" & sep & "(" & val & ")" & sep & "(" & val & ")$" Dim match As Match = Regex.Match(valueString, searchPattern) Try If match.Success Then ' Force to parse "." as decimal char. (Can be different with other culture settings. E.g. German culture expect "," instad of ".") Dim englishCulture As New System.Globalization.CultureInfo("en-US") Dim result As [Single]() = New [Single](2) {} result(0) = Convert.ToSingle(match.Groups(1).Value, englishCulture) result(1) = Convert.ToSingle(match.Groups(2).Value, englishCulture) result(2) = Convert.ToSingle(match.Groups(3).Value, englishCulture) Return result Else Return Nothing End If Catch generatedExceptionName As FormatException Return Nothing Catch generatedExceptionName As OverflowException Return Nothing End Try End Function ' Parse3Params() ''' <summary> ''' Return the Euler rotation state as quaternion. ''' </summary> ''' <param name="e">Euler Angle state</param> ''' <returns>Rotation state as Quaternion</returns> Public Shared Widening Operator CType(e As Euler) As Quaternion Return e.ToQuaternion() End Operator ''' <summary> ''' Set the yaw and pitch to face in the given direction. ''' The direction doesn't need to be normalised. ''' Roll is always unaffected. ''' </summary> ''' <param name="directionVector">Vector which points to the wanted direction</param> ''' <param name="setYaw">if false, the yaw isn't changed.</param> ''' <param name="setPitch">if false, the pitch isn't changed.</param> Public Sub SetDirection(directionVector As Vector3, setYaw As [Boolean], setPitch As [Boolean]) Dim d As Vector3 = directionVector.NormalisedCopy If setPitch Then mPitch = Math.ASin(d.y) End If If setYaw Then mYaw = Math.ATan2(-d.x, -d.z) End If '+Math.PI/2.0; mChanged = setYaw OrElse setPitch End Sub ''' <summary> ''' Normalise the selected rotations to be within the +/-180 degree range. ''' The normalise uses a wrap around, so for example a yaw of 360 degrees becomes 0 degrees, ''' and -190 degrees becomes 170. ''' By the parameters it's possible to choose which angles should be normalised. ''' </summary> ''' <param name="normYaw">If true, the angle will be normalised.</param> ''' <param name="normPitch">If true, the angle will be normalised.</param> ''' <param name="normRoll">If true, the angle will be normalised.</param> ''' <remarks></remarks> Public Sub Normalise(normYaw As [Boolean], normPitch As [Boolean], normRoll As [Boolean]) If normYaw Then Dim yaw As [Single] = mYaw.ValueRadians If yaw < -Math.PI Then yaw = CType(System.Math.IEEERemainder(yaw, Math.PI * 2.0), [Single]) If yaw < -Math.PI Then yaw += Math.PI * 2F End If mYaw = yaw mChanged = True ElseIf yaw > Math.PI Then yaw = CType(System.Math.IEEERemainder(yaw, Math.PI * 2F), [Single]) If yaw > Math.PI Then yaw -= Math.PI * 2F End If mYaw = yaw mChanged = True End If End If If normPitch Then Dim pitch As [Single] = mPitch.ValueRadians If pitch < -Math.PI Then pitch = CType(System.Math.IEEERemainder(pitch, Math.PI * 2F), [Single]) If pitch < -Math.PI Then pitch += Math.PI * 2F End If mPitch = pitch mChanged = True If [Single].IsNaN(mPitch.ValueDegrees) Then ' DEBUGGING ' add breakpoint here End If ElseIf pitch > Math.PI Then pitch = CType(System.Math.IEEERemainder(pitch, Math.PI * 2F), [Single]) If pitch > Math.PI Then pitch -= Math.PI * 2F End If mPitch = pitch mChanged = True If [Single].IsNaN(mPitch.ValueDegrees) Then ' DEBUGGING ' add breakpoint here End If End If End If If normRoll Then Dim roll As [Single] = mRoll.ValueRadians If roll < -Math.PI Then roll = CType(System.Math.IEEERemainder(roll, Math.PI * 2F), [Single]) If roll < -Math.PI Then roll += Math.PI * 2F End If mRoll = roll mChanged = True ElseIf roll > Math.PI Then roll = CType(System.Math.IEEERemainder(roll, Math.PI * 2F), [Single]) If roll > Math.PI Then roll -= Math.PI * 2F End If mRoll = roll mChanged = True End If End If End Sub ' Normalise() ''' <summary> ''' Return the relative euler angles required to rotate from the current forward direction to the specified direction vector. ''' The result euler can then be added to the current euler to immediately face dir. ''' Rotation is found to face the correct direction. For example, when false a yaw of 1000 degrees and a dir of ''' (0,0,-1) will return a -1000 degree yaw. When true, the same yaw and dir would give 80 degrees (1080 degrees faces ''' the same way as (0,0,-1). ''' The rotation won't flip upside down then roll instead of a 180 degree yaw. ''' </summary> ''' <param name="direction">...TODO...</param> ''' <param name="shortest">If false, the full value of each angle is used. If true, the angles are normalised and the shortest rotation is found to face the correct direction.</param> ''' <param name="setYaw">If true the angles are calculated. If false, the angle is set to 0. </param> ''' <param name="setPitch">If true the angles are calculated. If false, the angle is set to 0. </param> Public Function GetRotationTo(direction As Vector3, setYaw As [Boolean], setPitch As [Boolean], shortest As [Boolean]) As Euler Dim t1 As Euler = Euler.IDENTITY Dim t2 As Euler t1.SetDirection(direction, setYaw, setPitch) t2 = t1 - Me If shortest AndAlso setYaw Then t2.Normalise(True, True, True) End If Return t2 End Function ''' <summary> ''' Clamp the yaw angle to a range of +/-limit. ''' </summary> ''' <param name="limit">Wanted co-domain for the Yaw angle.</param> Public Sub LimitYaw(limit As Radian) If mYaw > limit Then mYaw = limit mChanged = True ElseIf mYaw < -limit Then mYaw = -limit mChanged = True End If End Sub ''' <summary> ''' Clamp the pitch angle to a range of +/-limit. ''' </summary> ''' <param name="limit">Wanted co-domain for the Pitch angle.</param> Public Sub LimitPitch(limit As Radian) If mPitch > limit Then mPitch = limit mChanged = True ElseIf mPitch < -limit Then mPitch = -limit mChanged = True End If End Sub ''' <summary> ''' Clamp the roll angle to a range of +/-limit. ''' </summary> ''' <param name="limit">Wanted co-domain for the Roll angle.</param> Public Sub LimitRoll(limit As Radian) If mRoll > limit Then mRoll = limit mChanged = True ElseIf mRoll < -limit Then mRoll = -limit mChanged = True End If End Sub ''' <summary> ''' Port of method <c>Matrix3.ToEulerAnglesYXZ()</c>, from MogreMatrix3.cpp as a workaround for a bug in the Math class. ''' (The bug was fixed, but is not present in common used binary files.) ''' </summary> ''' <param name="matrix">Rotation matrix</param> ''' <param name="isUnique">If false, the orientation can be described by different angle combinations. ''' In this case the returned angle values can be different than expected.</param> Public Shared Sub Matrix3ToEulerAnglesYXZ(matrix As Matrix3, ByRef rfYAngle As Radian, ByRef rfPAngle As Radian, ByRef rfRAngle As Radian, ByRef success As [Boolean]) rfPAngle = Mogre.Math.ASin(-matrix.m12) If rfPAngle < New Radian(Math.HALF_PI) Then If rfPAngle > New Radian(-Math.HALF_PI) Then rfYAngle = Math.ATan2(matrix.m02, matrix.m22) rfRAngle = Math.ATan2(matrix.m10, matrix.m11) success = True Return Else ' WARNING. Not a unique solution. Dim fRmY As Radian = Math.ATan2(-matrix.m01, matrix.m00) rfRAngle = New Radian(0F) ' any angle works rfYAngle = rfRAngle - fRmY success = False Return End If Else ' WARNING. Not a unique solution. Dim fRpY As Radian = Math.ATan2(-matrix.m01, matrix.m00) rfRAngle = New Radian(0F) ' any angle works rfYAngle = fRpY - rfRAngle success = False Return End If ' "Original" CODE FROM CLASS Matrix3.ToEulerAnglesYXZ() 'rfPAngle = Mogre::Math::ASin(-m12); 'if ( rfPAngle < Radian(Math::HALF_PI) ) '{ ' if ( rfPAngle > Radian(-Math::HALF_PI) ) ' { ' rfYAngle = System::Math::Atan2(m02,m22); ' rfRAngle = System::Math::Atan2(m10,m11); ' return true; ' } ' else ' { ' // WARNING. Not a unique solution. ' Radian fRmY = System::Math::Atan2(-m01,m00); ' rfRAngle = Radian(0.0); // any angle works ' rfYAngle = rfRAngle - fRmY; ' return false; ' } '} 'else '{ ' // WARNING. Not a unique solution. ' Radian fRpY = System::Math::Atan2(-m01,m00); ' rfRAngle = Radian(0.0); // any angle works ' rfYAngle = fRpY - rfRAngle; ' return false; '} End Sub ' Matrix3ToEulerAnglesYXZ() ''' <summary> ''' Add two euler objects. ''' </summary> ''' <returns>Calculation result</returns> Public Shared Operator +(lhs As Euler, rhs As Euler) As Euler Return New Euler(lhs.Yaw + rhs.Yaw, lhs.Pitch + rhs.Pitch, lhs.Roll + rhs.Roll) End Operator ''' <summary> ''' Subtract two euler objects. This finds the difference as relative angles. ''' </summary> ''' <returns>Calculation result</returns> Public Shared Operator -(lhs As Euler, rhs As Euler) As Euler Return New Euler(lhs.Yaw - rhs.Yaw, lhs.Pitch - rhs.Pitch, lhs.Roll - rhs.Roll) End Operator ''' <summary> ''' Interpolate each euler angle by the given factor. ''' (Each angle will be multiplied with the factor.) ''' </summary> ''' <returns>Calculation result</returns> Public Shared Operator *(lhs As Euler, factor As [Single]) As Euler Return New Euler(lhs.Yaw * factor, lhs.Pitch * factor, lhs.Roll * factor) End Operator ''' <summary> ''' Interpolate the euler angles by lhs. ''' (Each angle will be multiplied with the factor.) ''' </summary> ''' <returns>Calculation result</returns> Public Shared Operator *(factor As [Single], rhs As Euler) As Euler Return New Euler(factor * rhs.Yaw, factor * rhs.Pitch, factor * rhs.Roll) End Operator ''' <summary> ''' Apply the euler rotation to the vector rhs. ''' The calculation is equal to: quaternion*vector ''' </summary> ''' <returns>Calculation result</returns> Public Shared Operator *(lhs As Euler, rhs As Vector3) As Vector3 Return lhs.ToQuaternion() * rhs End Operator ''' <summary>Base settings (all angles are 0)</summary> Public Shared IDENTITY As New Euler(New Radian(0), New Radian(0), New Radian(0)) ''' <summary>Rotation around the Y axis.</summary> Private mYaw As Radian ''' <summary>Rotation around the X axis.</summary> Private mPitch As Radian ''' <summary>Rotation around the Z axis.</summary> Private mRoll As Radian ''' <summary>Is the cached quaternion out of date?</summary> Private mChanged As Boolean ''' <summary>Cached quaternion equivalent of this euler object.</summary> Private mCachedQuaternion As Quaternion End Structure ' struct Euler End Namespace ' namespace

# Examples

Making and using a euler object:

Euler euler = new Euler(new Radian(Math.PI), new Radian(0), new Radian(0)); sceneNode.Orientation = euler; // Euler auto converts to a quaternion if you use it where Mogre expects a quaternion.

Make a scene node face the camera (both yaw and pitch):

Euler euler = new Euler(); euler.SetDirection(camera.Position - sceneNode.Position, true, true); // SetDirection normalises the vector sceneNode.Orientation = euler;

Make a scene node face the camera using only the yaw (good for character controllers):

Euler euler = new Euler(); euler.SetDirection(camera.Position - sceneNode.Position, true, false); // the bools control whether the yaw or pitch are changed sceneNode.Orientation = euler;

Make a scene node face the camera with a limit of +/-45 degrees on the pitch and unlimited yaw:

Euler euler = new Euler(); euler.SetDirection(camera.Position - sceneNode.Position); euler.LimitPitch(new Degree(45)); sceneNode.Orientation = euler;

Perform a corkscrew motion (move vertical as the yaw increases:

sceneNode.Orientation = euler; Vector3 pos = sceneNode.Position; pos.y = euler.Yaw.ValueRadians; sceneNode.Position = pos;

Move forward using the euler direction:

sceneNode.Position = (sceneNode.Position + euler.Forward);

Cancel out excess rotations (return all angles to the +-180 degree range):

euler.Normalise();

Rotate the head of the Sinbad mesh to face the camera, with slow head turning and pitch/yaw constraints:

(This is using a modified version of the Sinbad mesh which is 2m tall and faces -Z. If your bone does not face -Z when it is not affected by any keyframes, this code might have problems. For example, if your bone faces +Z instead, you will need to negate the "lookat" vector before you use it in the "GetRotationTo" method)

//In the initialisation: Bone bone = entity.Skeleton.GetBone("Head"); bone.SetManuallyControlled(true); Euler headFacing = new Euler(new Radian(0), new Radian(0), new Radian(0)); // In the update logic: Vector3 lookat = sinbadNode.ConvertWorldToLocalPosition(camera.Position) - new Vector3(0.0f, 1.9f, 0.0f); Euler temp = headFacing.GetRotationTo(lookat, true, true, true); // temp is how much you need to rotate to get from the current orientation to the new orientation temp.LimitYaw(new Radian(deltaT*3.0f)); // limit the offset so the head turns at a maximum of 3.0 radians per second temp.LimitPitch(new Radian(deltaT*3.0f)); headFacing = headFacing + temp; // add the offset to the current orientation headFacing.LimitYaw(new Degree(60.0f)); // make sure the head doesn't turn too far. Clamp it to +/- 60 degrees of yaw and pitch. headFacing.LimitPitch(new Degree(60.0f)); bone.Orientation = headFacing;

Sinbad's current orientation is taken into account by the ConvertWorldToLocalPosition call, so you can rotate him in any direction and he will still try to turn his head to the camera (if the limits allow).

<HR>

THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.

BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.

## 1. Definitions

- "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
- "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
- "Licensor" means the individual or entity that offers the Work under the terms of this License.
- "Original Author" means the individual or entity who created the Work.
- "Work" means the copyrightable work of authorship offered under the terms of this License.
- "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
- "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.

## 2. Fair Use Rights

Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.

## 3. License Grant

Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:

- to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
- to create and reproduce Derivative Works;
- to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
- to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works.
- For the avoidance of doubt, where the work is a musical composition:
- Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work.
- Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights society or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).
- Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).

The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.

## 4. Restrictions

The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:

- You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(c), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by clause 4(c), as requested.
- You may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under the terms of this License, a later version of this License with the same License Elements as this License, or a Creative Commons iCommons license that contains the same License Elements as this License (e.g. Attribution-ShareAlike 2.5 Japan). You must include a copy of, or the Uniform Resource Identifier for, this License or other license specified in the previous sentence with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Derivative Works that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder, and You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Derivative Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of this License.
- If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.

## 5. Representations, Warranties and Disclaimer

UNLESS OTHERWISE AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE MATERIALS, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.

## 6. Limitation on Liability.

EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

## 7. Termination

- This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
- Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.

## 8. Miscellaneous

- Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
- Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
- If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
- No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
- This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.