| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810 |
- //-----------------------------------------------------------------------------
- // EditCurve.cs
- //
- // Microsoft XNA Community Game Platform
- // Copyright (C) Microsoft Corporation. All rights reserved.
- //-----------------------------------------------------------------------------
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Windows.Forms;
- using System.IO;
- using System.Xml;
- using System.Drawing;
- using Microsoft.Xna.Framework;
- using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate;
- namespace Xna.Tools
- {
- /// <summary>
- /// This class contains curve editting related information.
- /// </summary>
- public class EditCurve
- {
- #region Properties
- /// <summary>
- /// Sets/Gets Control that owns this EditCurve.
- /// </summary>
- public Control Owner { get { return owner; } set { owner = value; } }
- /// <summary>
- /// Sets/Gets Id of this EditCurve.
- /// </summary>
- public long Id { get { return id; } set { id = value; } }
- /// <summary>
- /// Sets/Gets Name of this EditCurve.
- /// </summary>
- public string Name
- {
- get { return state.Name; }
- set { state.Name = value; FireStateChangeEvent(); }
- }
- /// <summary>
- /// Sets/Gets Color of this EditCurve.
- /// </summary>
- public System.Drawing.Color Color
- {
- get { return color; }
- set { color = value; FireStateChangeEvent(); }
- }
- /// <summary>
- /// Gets EditCurveKeyCollection.
- /// </summary>
- public EditCurveKeyCollection Keys { get { return keys; } }
- /// <summary>
- /// Sets/Gets PreLoop
- /// </summary>
- public CurveLoopType PreLoop
- {
- get { return OriginalCurve.PreLoop; }
- set
- {
- state.PreLoop = OriginalCurve.PreLoop = value;
- FireStateChangeEvent();
- }
- }
- /// <summary>
- /// Sets/Gets PostLoop
- /// </summary>
- public CurveLoopType PostLoop
- {
- get { return OriginalCurve.PostLoop; }
- set {
- state.PostLoop = OriginalCurve.PostLoop = value;
- FireStateChangeEvent();
- }
- }
- /// <summary>
- /// Gets original curve object.
- /// </summary>
- public Curve OriginalCurve { get { return originalCurve; } }
- /// <summary>
- /// Sets/Gets Visible that represents this EditCurve rendered or not
- /// in CurveControl.
- /// </summary>
- public bool Visible { get { return visible; } set { visible = value; } }
- /// <summary>
- /// Sets/Gets Editable that represents this EditCurve could update states.
- /// </summary>
- public bool Editable { get { return editable; } set { editable = value; } }
- /// <summary>
- /// Sets/Gets Dirty flag for file saving.
- /// </summary>
- public bool Dirty { get { return dirty; } set { dirty = value; } }
- /// <summary>
- /// Get selections.
- /// </summary>
- public EditCurveKeySelection Selection { get { return selection; } }
- /// <summary>
- /// Occures after EditCurve state (Name, Visible, Editable, PreLoop,
- /// and PostLoop) changed.
- /// </summary>
- public event EventHandler StateChanged;
- #endregion
- #region Constructors
- public EditCurve(string name, System.Drawing.Color curveColor, CommandHistory commandHistory)
- {
- originalCurve = new Curve();
- this.color = curveColor;
- keys = new EditCurveKeyCollection(this);
- state.Name = name;
- state.PreLoop = OriginalCurve.PreLoop;
- state.PostLoop = OriginalCurve.PostLoop;
- this.commandHistory = commandHistory;
- }
- public EditCurve(string name, System.Drawing.Color curveColor, Curve curve,
- CommandHistory commandHistory)
- {
- originalCurve = curve;
- this.color = curveColor;
- keys = new EditCurveKeyCollection(this);
- state.Name = name;
- state.PreLoop = OriginalCurve.PreLoop;
- state.PostLoop = OriginalCurve.PostLoop;
- this.commandHistory = commandHistory;
- }
- #endregion
- public override string ToString()
- {
- return state.Name;
- }
- #region Public Methods
- /// <summary>
- /// Evaluate this curve at given position.
- /// </summary>
- /// <param name="position"></param>
- /// <returns></returns>
- public float Evaluate(float position)
- {
- return OriginalCurve.Evaluate(position);
- }
- /// <summary>
- /// Begin update curve parameters.
- /// </summary>
- /// <remarks>It records curve satte and key values modification between
- /// BeginUpdate and EndUpdate method.</remarks>
- public void BeginUpdate()
- {
- if (inUpdating)
- throw new InvalidOperationException("BeginUpdate called twice.");
- modifiedKeys = new Dictionary<long, EditCurveKey>();
- savedState = (EditCurveState)state.Clone();
- inUpdating = true;
- }
- /// <summary>
- /// EndUpdate and generate Undo/Redo command buffer if there is any
- /// modification happend since BeginUpdate called.
- /// </summary>
- public void EndUpdate()
- {
- if (!inUpdating)
- throw new InvalidOperationException(
- "You must call BeginUpdate before call EndUpdate.");
- // Compare modified key values.
- if (modifiedKeys != null && modifiedKeys.Count > 0)
- {
- List<EditCurveKey> oldKeyValues =
- new List<EditCurveKey>(modifiedKeys.Count);
- List<EditCurveKey> newKeyValues =
- new List<EditCurveKey>(modifiedKeys.Count);
- foreach (EditCurveKey savedKey in modifiedKeys.Values)
- {
- EditCurveKey curKey;
- if (keys.TryGetValue(savedKey.Id, out curKey))
- {
- if (!curKey.Equals(savedKey))
- {
- // Saved value is already cloned.
- oldKeyValues.Add(savedKey);
- newKeyValues.Add(curKey.Clone());
- }
- }
- }
- if (newKeyValues.Count != 0)
- {
- dirty = true;
- if (commandHistory != null)
- commandHistory.Add(new EditCurveKeyUpdateCommand(
- this, oldKeyValues, newKeyValues));
- }
- }
- modifiedKeys = null;
- // Compare states
- bool stateChanged = state != savedState;
- if (commandHistory != null && stateChanged)
- commandHistory.Add(new EditCurveStateChangeCommand(
- this, savedState, state));
- savedState = null;
- inUpdating = false;
- if (stateChanged) FireStateChangeEvent();
- }
- /// <summary>
- /// Select keys and key tangents.
- /// </summary>
- /// <param name="selectRegion">Selection region in unit coordinate.</param>
- /// <param name="tangentScale">Tangent scale in unit coordinate.</param>
- /// <param name="keyView"></param>
- /// <param name="tangentView"></param>
- /// <param name="toggleSelection"></param>
- /// <param name="singleSelection"></param>
- public void Select(BoundingBox selectRegion, Vector2 tangentScale,
- EditCurveView keyView, EditCurveView tangentView,
- bool toggleSelection, bool singleSelect)
- {
- if (!Editable) return;
- EditCurveKeySelection newSelection = new EditCurveKeySelection();
- // Check Intersection of Keys and Tangents.
- if (keyView != EditCurveView.Never)
- {
- ICollection<EditCurveKey> targetKeys =
- (keyView == EditCurveView.Always) ?
- (ICollection<EditCurveKey>)keys :
- (ICollection<EditCurveKey>)selectedKeys.Values;
- newSelection.SelectKeys(targetKeys, selectRegion, singleSelect);
- }
- // Check Tangents if any keys are not selected.
- if (newSelection.Count == 0 && tangentView != EditCurveView.Never)
- {
- ICollection<EditCurveKey> targetKeys
- = (tangentView == EditCurveView.Always) ?
- (ICollection<EditCurveKey>)keys :
- (ICollection<EditCurveKey>)selectedKeys.Values;
- newSelection.SelectTangents(targetKeys, selectRegion, tangentScale,
- singleSelect);
- }
- if (toggleSelection)
- newSelection = EditCurveKeySelection.ToggleSelection(
- selection, newSelection);
- ApplySelection(newSelection, true);
- }
- /// <summary>
- /// Clear selection.
- /// </summary>
- public void ClearSelection()
- {
- ApplySelection(new EditCurveKeySelection(), true);
- }
- /// <summary>
- /// Apply key selection.
- /// </summary>
- /// <param name="newSelection"></param>
- /// <param name="generateCommand"></param>
- public void ApplySelection(EditCurveKeySelection newSelection,
- bool generateCommand)
- {
- // Re-create selected keys and store selection information from
- // new selection.
- selectedKeys.Clear();;
- foreach (long id in newSelection.Keys)
- {
- EditCurveKey key = keys.GetValue(id);
- key.Selection = newSelection[id];
- selectedKeys.Add(key.Id, key);
- }
- // Clear de-selected keys selection information.
- foreach (long id in selection.Keys)
- {
- if (!newSelection.ContainsKey(id))
- {
- EditCurveKey key;
- if ( keys.TryGetValue(id, out key))
- key.Selection = EditCurveSelections.None;
- }
- }
- // Invoke selection change event.
- if ( generateCommand == true && !newSelection.Equals(selection) &&
- commandHistory != null)
- commandHistory.Add(new SelectCommand(this, newSelection, selection));
- // Update selection.
- selection = newSelection;
- }
- /// <summary>
- /// Move selected keys or tangents.
- /// </summary>
- /// <param name="newPos"></param>
- /// <param name="prevPos"></param>
- public void Move(Vector2 newPos, Vector2 prevPos)
- {
- if (!Editable || !Visible) return;
- Vector2 delta = newPos - prevPos;
- foreach (EditCurveKey key in selectedKeys.Values)
- {
- MarkModify(key);
- int oldIdx = keys.IndexOf(key);
- // Move tangents.
- if ((key.Selection & (EditCurveSelections.TangentIn |
- EditCurveSelections.TangentOut)) != 0)
- {
- // Compute delta angle.
- Vector2 pos = new Vector2(key.Position, key.Value);
- float minAngle = MathHelper.ToRadians(MinTangentAngle);
- float maxAngle = MathHelper.ToRadians(MaxTangentAngle);
- const float epsilon = 1e-5f;
- if ((key.Selection & EditCurveSelections.TangentIn) != 0)
- {
- // Compute delta angle.
- double prevAngle =
- Math.Atan2(prevPos.Y - pos.Y, pos.X - prevPos.X);
- double newAngle =
- Math.Atan2(newPos.Y - pos.Y, pos.X - newPos.X);
- double da = prevAngle - newAngle;
- float d = GetDistanceOfKeys(oldIdx, 0);
- if (Math.Abs(d) > epsilon)
- {
- float tn = key.TangentIn / d;
- key.TangentIn = (float)Math.Tan(MathHelper.Clamp(
- (float)(Math.Atan(tn) + da), minAngle, maxAngle)) * d;
- if (Single.IsNaN(key.TangentIn))
- key.TangentIn = key.TangentIn;
- key.TangentInType = EditCurveTangent.Fixed;
- }
- }
- if ((key.Selection & EditCurveSelections.TangentOut) != 0)
- {
- // Compute delta angle.
- double prevAngle =
- Math.Atan2(prevPos.Y - pos.Y, prevPos.X - pos.X);
- double newAngle =
- Math.Atan2(newPos.Y - pos.Y, newPos.X - pos.X);
- double da = newAngle - prevAngle;
- float d = GetDistanceOfKeys(oldIdx, 1);
- if (Math.Abs(d) > epsilon)
- {
- float tn = key.TangentOut / d;
- key.TangentOut = (float)Math.Tan(MathHelper.Clamp(
- (float)(Math.Atan(tn) + da), minAngle, maxAngle)) * d;
- key.TangentOutType = EditCurveTangent.Fixed;
- }
- }
- }
- // Move key position.
- keys.RemoveAt(oldIdx); // remove key from curve once.
- if ((key.Selection & EditCurveSelections.Key) != 0)
- {
- key.OriginalKey = new CurveKey(
- key.Position + delta.X, key.Value + delta.Y,
- key.TangentIn, key.TangentOut, key.Continuity);
- }
- // Then store updated node back to the curve.
- keys.Add(key);
- // Compute auto-generated tangents.
- int newIdx = keys.IndexOf(key);
- ComputeTangents(newIdx);
- if (newIdx != oldIdx)
- ComputeTangents(oldIdx);
- }
- }
- /// <summary>
- /// Updated specified key values.
- /// </summary>
- public void UpdateKey( long keyId, float newPosition, float newValue)
- {
- if (!Editable || !Visible) return;
- EditCurveKey key;
- keys.TryGetValue(keyId, out key);
- MarkModify(key);
- int oldIdx = keys.IndexOf(key);
- // Move key position.
- keys.RemoveAt(oldIdx); // remove key from curve once.
- key.OriginalKey = new CurveKey( newPosition, newValue,
- key.TangentIn, key.TangentOut, key.Continuity);
- // Then store updated node back to the curve.
- keys.Add(key);
- // Compute auto-generated tangents.
- int newIdx = keys.IndexOf(key);
- ComputeTangents(newIdx);
- if (newIdx != oldIdx)
- ComputeTangents(oldIdx);
- dirty = true;
- }
- /// <summary>
- /// Add new key at given position.
- /// </summary>
- /// <param name="pos"></param>
- public void AddKey(Vector2 pos)
- {
- EnsureUpdating("AddKey");
- // Create new key.
- EditCurveKey key = new EditCurveKey(EditCurveKey.GenerateUniqueId(),
- new CurveKey(pos.X, pos.Y));
- key.Selection = EditCurveSelections.Key;
- // Generate add key command and execute it.
- EditCurveKeyAddRemoveCommand command =
- new EditCurveKeyAddRemoveCommand(this, key, selection);
- command.Execute();
- if (commandHistory != null) commandHistory.Add(command);
- }
- /// <summary>
- /// Remove selected keys.
- /// </summary>
- public void RemoveKeys()
- {
- if (!Editable) return;
- if (selectedKeys.Count != 0)
- {
- // Generate Remove keys command and execute it.
- EditCurveKeyAddRemoveCommand command =
- new EditCurveKeyAddRemoveCommand(this, selectedKeys.Values);
- command.Execute();
- if (commandHistory != null) commandHistory.Add(command);
- // Clear selection
- selection.Clear();
- selectedKeys.Clear();
- dirty = true;
- }
- }
- /// <summary>
- /// Apply new EditCurveState.
- /// </summary>
- /// <param name="newState"></param>
- public void ApplyState(EditCurveState newState)
- {
- if (newState == null) throw new ArgumentNullException("newState");
- inUpdating = true;
- Name = newState.Name;
- PreLoop = newState.PreLoop;
- PostLoop = newState.PostLoop;
- inUpdating = false;
- FireStateChangeEvent();
- }
- /// <summary>
- /// Apply given key values.
- /// </summary>
- /// <param name="newKeyValues"></param>
- public void ApplyKeyValues(ICollection<EditCurveKey> newKeyValues)
- {
- foreach (EditCurveKey newKeyValue in newKeyValues)
- {
- EditCurveKey key = newKeyValue.Clone();
- // Update key value.
- keys.Remove(keys.GetValue(key.Id));
- keys.Add(key);
- // Also, update key values if that key is selected.
- if (selectedKeys.ContainsKey(key.Id))
- {
- selectedKeys.Remove(key.Id);
- selectedKeys.Add(key.Id, key);
- }
- dirty = true;
- }
- }
- /// <summary>
- /// Set specfied tangent type to selected tangents.
- /// </summary>
- /// <param name="targetTangent">target tangent (In/Out)</param>
- /// <param name="tangentType"></param>
- public void SetTangents(EditCurveSelections targetTangent,
- EditCurveTangent tangentType)
- {
- if (!Editable) return;
- EnsureUpdating("SetTangents");
- // Change tangent type of selcted nodes.
- foreach (EditCurveKey key in selectedKeys.Values)
- {
- MarkModify(key);
- if (tangentType == EditCurveTangent.Stepped)
- {
- SetKeyContinuity(key, CurveContinuity.Step);
- }
- else
- {
- SetKeyContinuity(key, CurveContinuity.Smooth);
- if ((targetTangent & EditCurveSelections.TangentIn) != 0)
- key.TangentInType = tangentType;
- if ((targetTangent & EditCurveSelections.TangentOut) != 0)
- key.TangentOutType = tangentType;
- }
- }
- // Then, compute tangents.
- foreach (EditCurveKey key in selectedKeys.Values)
- ComputeTangents(keys.IndexOf(key));
- }
- /// <summary>
- /// Compute specfied index key tangents.
- /// </summary>
- /// <param name="idx"></param>
- public void ComputeTangents(int keyIndex)
- {
- if (keyIndex < 0 || keyIndex >keys.Count ||keyIndex > Int32.MaxValue - 2)
- throw new ArgumentOutOfRangeException("keyIndex");
- // Compute neighbors tangents too.
- for (int i = keyIndex - 1; i < keyIndex + 2; ++i)
- {
- if (i >= 0 && i < keys.Count)
- {
- EditCurveKey key = keys[i];
- MarkModify(key);
- float tangentInValue = key.TangentIn;
- float tangentOutValue = key.TangentOut;
- CurveTangent tangentIn = Convert(key.TangentInType);
- CurveTangent tangentOut = Convert(key.TangentOutType);
- OriginalCurve.ComputeTangent(i, tangentIn, tangentOut);
- if (Single.IsNaN(key.TangentIn)) key.TangentIn = 0.0f;
- if (Single.IsNaN(key.TangentOut)) key.TangentOut = 0.0f;
- // Restore original value if EditCurveTanget is fixed.
- if (key.TangentInType == EditCurveTangent.Fixed)
- key.TangentIn = tangentInValue;
- if (key.TangentOutType == EditCurveTangent.Fixed)
- key.TangentOut = tangentOutValue;
- }
- }
- }
- /// <summary>
- /// Get distance between given index key position and previous/next key.
- /// </summary>
- /// <param name="index"></param>
- /// <param name="direction">0:previeous key, 1:next key</param>
- /// <returns></returns>
- public float GetDistanceOfKeys(int index, int direction)
- {
- float result = 1.0f;
- if (direction == 0)
- {
- // From previous key.
- if (index > 0)
- {
- result = keys[index].Position - keys[index - 1].Position;
- }
- else if (OriginalCurve.PreLoop == CurveLoopType.Oscillate &&
- keys.Count > 1)
- {
- result = keys[1].Position - keys[0].Position;
- }
- }
- else
- {
- // From next key.
- if (index < keys.Count - 1)
- {
- result = keys[index + 1].Position - keys[index].Position;
- }
- else if (OriginalCurve.PostLoop == CurveLoopType.Oscillate &&
- keys.Count > 1)
- {
- result = keys[index].Position - keys[index - 1].Position;
- }
- }
- return result;
- }
- /// <summary>
- /// Returns selected key list.
- /// </summary>
- /// <returns></returns>
- public EditCurveKey[] GetSelectedKeys()
- {
- EditCurveKey[] keys = new EditCurveKey[selectedKeys.Count];
- int idx = 0;
- foreach (EditCurveKey key in selectedKeys.Values)
- keys[idx++] = key;
- return keys;
- }
- /// <summary>
- /// Load a Curve from given filename.
- /// </summary>
- /// <param name="filename"></param>
- /// <param name="name"></param>
- /// <param name="color"></param>
- /// <param name="commandHistory"></param>
- /// <returns></returns>
- public static EditCurve LoadFromFile(string filename, string name,
- System.Drawing.Color color, CommandHistory commandHistory)
- {
- EditCurve editCurve = null;
- using (XmlReader xr = XmlReader.Create(filename))
- {
- Curve curve = IntermediateSerializer.Deserialize<Curve>(xr,
- Path.GetDirectoryName(filename));
- editCurve = new EditCurve(name, color, curve, commandHistory);
- }
- return editCurve;
- }
- /// <summary>
- /// Save this curve to given filename.
- /// </summary>
- /// <param name="filename"></param>
- public void Save(string filename)
- {
- using (XmlWriter xw = XmlWriter.Create(filename))
- {
- IntermediateSerializer.Serialize(xw, originalCurve,
- Path.GetDirectoryName(filename));
- dirty = false;
- }
- }
- #endregion
- #region Private methods
- /// <summary>
- /// Mark modified key.
- /// </summary>
- private void MarkModify(EditCurveKey key)
- {
- // Clone and save current EditCurveKey to modified keys.
- if (modifiedKeys != null && !modifiedKeys.ContainsKey(key.Id))
- {
- modifiedKeys.Add(key.Id, key.Clone());
- dirty = true;
- }
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="key"></param>
- /// <param name="continuity"></param>
- private void SetKeyContinuity(EditCurveKey key, CurveContinuity continuity)
- {
- if (key.Continuity != continuity)
- {
- MarkModify(key);
- key.Continuity = continuity;
- if (continuity == CurveContinuity.Step)
- {
- key.TangentIn = key.TangentOut = 0;
- key.TangentInType = key.TangentInType = EditCurveTangent.Flat;
- }
- }
- }
- /// <summary>
- /// Convert EditCurveTanget to CurveTangent.
- /// </summary>
- /// <param name="tangent"></param>
- /// <returns></returns>
- private static CurveTangent Convert(EditCurveTangent tangent)
- {
- return (tangent == EditCurveTangent.Fixed) ?
- CurveTangent.Flat : (CurveTangent)tangent;
- }
- private void FireStateChangeEvent()
- {
- dirty = true;
- if (inUpdating) return;
- if (StateChanged != null) StateChanged(this, EventArgs.Empty);
- if (Owner != null)
- Owner.Invalidate();
- }
- private void EnsureUpdating(string operationName)
- {
- if (!inUpdating)
- throw new InvalidOperationException(String.Format(
- "You have to call BeginUpdate before call {0}", operationName));
- }
- #endregion
- #region Private Constants
- const float MinTangentAngle = -89.99999f;
- const float MaxTangentAngle = +89.99999f;
- #endregion
- #region Properties wrap members
- private Control owner;
- private long id;
- private System.Drawing.Color color = System.Drawing.Color.Red;
- private Curve originalCurve;
- private EditCurveKeyCollection keys;
- private bool editable = true;
- private bool visible = true;
- private bool dirty = false;
- #endregion
- #region Private members
- private CommandHistory commandHistory;
- private Dictionary<long, EditCurveKey> selectedKeys =
- new Dictionary<long, EditCurveKey>();
- private EditCurveKeySelection selection = new EditCurveKeySelection();
- private Dictionary<long, EditCurveKey> modifiedKeys;
- private EditCurveState state = new EditCurveState();
- private EditCurveState savedState;
- private bool inUpdating;
- #endregion
- }
- }
|