private void RotateWidgets(Vector2 pivotPoint, List<Widget> widgets, Vector2 curMousePos, Vector2 prevMousePos, bool snapped, List<Tuple<Widget, AccumulativeRotationHelper>> accumulativeRotationHelpers) { WidgetTransformsHelper.ApplyTransformationToWidgetsGroupObb( sv.Scene, widgets, widgets.Count <= 1 ? (Vector2?) null : pivotPoint, widgets.Count <= 1, curMousePos, prevMousePos, false, (originalVectorInObbSpace, deformedVectorInObbSpace) => { double rotation = 0; if (originalVectorInObbSpace.Length > Mathf.ZeroTolerance && deformedVectorInObbSpace.Length > Mathf.ZeroTolerance) { rotation = Mathd.Wrap180(deformedVectorInObbSpace.Atan2Deg - originalVectorInObbSpace.Atan2Deg); } if (snapped) { rotation = WidgetTransformsHelper.RoundTo(rotation, 15); } foreach (Tuple<Widget, AccumulativeRotationHelper> tuple in accumulativeRotationHelpers) { tuple.Item2.Rotate((float) rotation); } return new Transform2d(Vector2d.Zero, Vector2d.One, rotation); } ); foreach (Tuple<Widget, AccumulativeRotationHelper> tuple in accumulativeRotationHelpers) { SetAnimableProperty.Perform(tuple.Item1, nameof(Widget.Rotation), tuple.Item2.Rotation, CoreUserPreferences.Instance.AutoKeyframes); } }
private void ScaleKeyframes() { var boundaries = GridSelection.GetSelectionBoundaries(); if (boundaries.HasValue || Scale < Mathf.ZeroTolerance) { var saved = new List <IKeyframe>(); for (int i = boundaries.Value.Top; i <= boundaries.Value.Bottom; ++i) { if (!(Document.Current.Rows[i].Components.Get <NodeRow>()?.Node is IAnimationHost animable)) { continue; } foreach (var animator in animable.Animators.ToList()) { saved.Clear(); IEnumerable <IKeyframe> keys = animator.ReadonlyKeys.Where(k => k.Frame >= boundaries.Value.Left && k.Frame < boundaries.Value.Right ).ToList(); if (Scale < 1) { keys = keys.Reverse().ToList(); } foreach (var key in keys) { saved.Add(key); RemoveKeyframe.Perform(animator, key.Frame); } foreach (var key in saved) { // The formula should behave similiar to stretching animation with mouse int newFrame = (int)( boundaries.Value.Left + (key.Frame - boundaries.Value.Left) * (1 + (boundaries.Value.Left - boundaries.Value.Right) * Scale) / (1 + boundaries.Value.Left - boundaries.Value.Right) ); var newKey = key.Clone(); newKey.Frame = newFrame; SetAnimableProperty.Perform( animable, animator.TargetPropertyPath, newKey.Value, createAnimatorIfNeeded: true, createInitialKeyframeForNewAnimator: false, newKey.Frame ); SetKeyframe.Perform(animable, animator.TargetPropertyPath, Document.Current.AnimationId, newKey); } } } ClearGridSelection.Perform(); for (int i = boundaries.Value.Top; i <= boundaries.Value.Bottom; ++i) { SelectGridSpan.Perform(i, boundaries.Value.Left, (int)(boundaries.Value.Left + (boundaries.Value.Right - boundaries.Value.Left) * Scale)); } } else { Document.Current.History.RollbackTransaction(); } }
private void ScaleKeyframes() { if (GridSelection.GetSelectionBoundaries(out var boundaries) && Scale > Mathf.ZeroTolerance) { var processed = new HashSet <IAnimator>(); var saved = new List <IKeyframe>(); foreach (var animable in GridSelection.EnumerateAnimators(boundaries)) { foreach (var animator in animable.Animators) { if (animator.AnimationId != Document.Current.AnimationId || processed.Contains(animator)) { continue; } processed.Add(animator); saved.Clear(); IEnumerable <IKeyframe> keys = animator.ReadonlyKeys.Where(k => k.Frame >= boundaries.Left && k.Frame < boundaries.Right ).ToList(); if (Scale < 1) { keys = keys.Reverse().ToList(); } foreach (var key in keys) { saved.Add(key); RemoveKeyframe.Perform(animator, key.Frame); } foreach (var key in saved) { // The formula should behave similiar to stretching animation with mouse int newFrame = (int)( boundaries.Left + (key.Frame - boundaries.Left) * (1 + (boundaries.Left - boundaries.Right) * Scale) / (1 + boundaries.Left - boundaries.Right) ); var newKey = key.Clone(); newKey.Frame = newFrame; SetAnimableProperty.Perform( animable.Host, animator.TargetPropertyPath, newKey.Value, createAnimatorIfNeeded: true, createInitialKeyframeForNewAnimator: false, newKey.Frame ); SetKeyframe.Perform(animable.Host, animator.TargetPropertyPath, Document.Current.AnimationId, newKey); } } } ClearGridSelection.Perform(); for (int i = boundaries.Top; i <= boundaries.Bottom; ++i) { SelectGridSpan.Perform(i, boundaries.Left, (int)(boundaries.Left + (boundaries.Right - boundaries.Left) * Scale)); } }
private void RotateWidgets(Vector2 pivotPoint, List <Widget> widgets, Vector2 curMousePos, Vector2 prevMousePos, bool discret, ref float accumAngle, ref float prevAngle) { List <KeyValuePair <Widget, float> > wasRotations = widgets.Select(widget => new KeyValuePair <Widget, float>(widget, widget.Rotation)).ToList(); float rotationRes = prevAngle; Utils.ApplyTransformationToWidgetsGroupOobb( sv.Scene, widgets, pivotPoint, false, curMousePos, prevMousePos, (originalVectorInOobbSpace, deformedVectorInOobbSpace) => { float rotation = 0; if (originalVectorInOobbSpace.Length > Mathf.ZeroTolerance && deformedVectorInOobbSpace.Length > Mathf.ZeroTolerance) { rotation = Mathf.Wrap180(deformedVectorInOobbSpace.Atan2Deg - originalVectorInOobbSpace.Atan2Deg); } if (discret) { rotation = Utils.RoundTo(rotation, 15); } rotationRes = rotation; return(Matrix32.Rotation(rotation * Mathf.DegToRad)); } ); // accumulate rotation, each visual turn of widget will increase it's angle on 360, // without that code angle will be allways [-180; 180) rotationRes = rotationRes.NormalizeRotation(); float rotationDelta = (rotationRes - prevAngle).NormalizeRotation(); prevAngle = rotationRes; accumAngle += rotationDelta; foreach (KeyValuePair <Widget, float> wasRotation in wasRotations) { SetAnimableProperty.Perform(wasRotation.Key, nameof(Widget.Rotation), wasRotation.Value + accumAngle); } }
public static void SetAnimatorAndInitialKeyframeIfNeed(IAnimable animable, params string[] properties) { var frame = Document.Current.AnimationFrame; Document.Current.AnimationFrame = 0; IAnimator animator; foreach (var propName in properties) { if (!animable.Animators.TryFind(propName, out animator, Document.Current.AnimationId)) { var propValue = animable.GetType().GetProperty(propName).GetValue(animable); SetProperty.Perform(animable, propName, propValue); var type = animable.GetType().GetProperty(propName).PropertyType; animator = AnimatorRegistry.Instance.CreateAnimator(type); animator.TargetProperty = propName; animator.AnimationId = Document.Current.AnimationId; SetAnimator.Perform(animable, animator); SetAnimableProperty.Perform(animable, propName, propValue); } } Document.Current.AnimationFrame = frame; }
public static void ApplyTransformationToWidgetsGroupOobb(IEnumerable <Widget> widgetsInParentSpace, Widget parentWidget, Matrix32 oobbInParentSpace, Matrix32 oobbTransformation) { Matrix32 originalOobbToWorldSpace = oobbInParentSpace * parentWidget.LocalToWorldTransform; foreach (Widget widget in widgetsInParentSpace) { Matrix32 widgetToOriginalOobbSpace = widget.LocalToWorldTransform * originalOobbToWorldSpace.CalcInversed(); // calculate new oobb transformation in world space Matrix32 deformedOobbToWorldSpace = oobbTransformation * originalOobbToWorldSpace; Matrix32 deformedWidgetToWorldSpace = widgetToOriginalOobbSpace * deformedOobbToWorldSpace; Matrix32 deformedWidgetToParentSpace = deformedWidgetToWorldSpace * widget.ParentWidget.LocalToWorldTransform.CalcInversed(); Transform2 widgetResultTransform = widget.CalcApplicableTransfrom2(deformedWidgetToParentSpace); // correct rotation delta, to prevent wrong values if new angle 0 and previous is 359, // then rotationDelta must be 1 float rotationDelta = (widget.Rotation - widgetResultTransform.Rotation).NormalizeRotation(); if ((widget.Position - widgetResultTransform.Translation).Length > Mathf.ZeroTolerance) { SetAnimableProperty.Perform(widget, nameof(Widget.Position), widgetResultTransform.Translation); } if (Mathf.Abs(rotationDelta) > Mathf.ZeroTolerance) { SetAnimableProperty.Perform(widget, nameof(Widget.Rotation), widget.Rotation + rotationDelta); } if ((widget.Scale - widgetResultTransform.Scale).Length > Mathf.ZeroTolerance) { SetAnimableProperty.Perform(widget, nameof(Widget.Scale), widgetResultTransform.Scale); } } }
private static void SetKeyframes(Dictionary <Node, BoneAnimationData> keyframeDictionary) { foreach (var pair in keyframeDictionary) { if (pair.Value.NoParentKeyframes) { TransformPropertyAndKeyframes(pair.Key, nameof(Bone.Position), pair.Value.PositionTransformer); } else { SetProperty.Perform(pair.Key, nameof(Bone.Position), pair.Value.CurrentPosition); SetProperty.Perform(pair.Key, nameof(Bone.Rotation), pair.Value.CurrentRotation); foreach (var keyframe in pair.Value.PositionKeyframes) { SetKeyframe.Perform(pair.Key, nameof(Bone.Position), Document.Current.AnimationId, keyframe.Value); } foreach (var keyframe in pair.Value.RotationKeyframes) { SetKeyframe.Perform(pair.Key, nameof(Bone.Rotation), Document.Current.AnimationId, keyframe.Value); } SetAnimableProperty.Perform(pair.Key, nameof(Bone.BaseIndex), 0); } } }
private void Stretch(IntRectangle boundaries, DragSide side, int newPos, bool stretchMarkers) { int length; if (side == DragSide.Left) { length = boundaries.Right - newPos - 1; } else { length = newPos - boundaries.Left - 1; } int oldLength = boundaries.Right - boundaries.Left - 1; var processed = new HashSet <IAnimator>(); foreach (var animable in GridSelection.EnumerateAnimators(boundaries)) { foreach (var animator in animable.Animators) { if (animator.AnimationId != Document.Current.AnimationId || processed.Contains(animator) || !savedKeyframes.ContainsKey(animator)) { continue; } processed.Add(animator); IEnumerable <IKeyframe> saved = savedKeyframes[animator]; if ( side == DragSide.Left && length < oldLength || side == DragSide.Right && length > oldLength ) { saved = saved.Reverse(); } foreach (var key in saved) { RemoveKeyframe.Perform(animator, key.Frame); } foreach (var key in saved) { double relpos = savedPositions[key]; int newFrame; if (side == DragSide.Left) { newFrame = (int)Math.Round(newPos + relpos * length); } else { newFrame = (int)Math.Round(boundaries.Left + relpos * length); } var newKey = key.Clone(); newKey.Frame = newFrame; SetAnimableProperty.Perform( animable.Host, animator.TargetPropertyPath, newKey.Value, createAnimatorIfNeeded: true, createInitialKeyframeForNewAnimator: false, newKey.Frame ); SetKeyframe.Perform(animable.Host, animator.TargetPropertyPath, Document.Current.AnimationId, newKey); } } } if (stretchMarkers) { foreach (var marker in savedMarkers) { DeleteMarker.Perform(marker, removeDependencies: false); } foreach (var marker in savedMarkers) { double relpos = savedMarkerPositions[marker]; int newFrame; if (side == DragSide.Left) { newFrame = (int)Math.Round(newPos + relpos * length); } else { newFrame = (int)Math.Round(boundaries.Left + relpos * length); } var newMarker = marker.Clone(); newMarker.Frame = newFrame; SetMarker.Perform(newMarker, removeDependencies: false); } } }
public static void Perform(IntVector2 offset, bool removeOriginals) { var processedKeys = new HashSet <IKeyframe>(); var operations = new List <Action>(); foreach (var row in Document.Current.Rows) { var spans = row.Components.GetOrAdd <GridSpanListComponent>().Spans.GetNonOverlappedSpans(offset.X > 0); foreach (var span in spans) { var node = row.Components.Get <NodeRow>()?.Node ?? row.Components.Get <PropertyRow>()?.Node; if (node == null) { continue; } var property = row.Components.Get <PropertyRow>()?.Animator.TargetPropertyPath; foreach (var a in node.Animators.ToList()) { if (property != null && a.TargetPropertyPath != property) { continue; } IEnumerable <IKeyframe> keysEnumerable = a.Keys.Where(k => k.Frame >= span.A && k.Frame < span.B); if (offset.X > 0) { keysEnumerable = keysEnumerable.Reverse(); } foreach (var k in keysEnumerable) { if (processedKeys.Contains(k)) { continue; } processedKeys.Add(k); var destRow = row.Index + offset.Y; if (!CheckRowRange(destRow)) { continue; } var destRowComponents = Document.Current.Rows[destRow].Components; var destNode = destRowComponents.Get <NodeRow>()?.Node ?? destRowComponents.Get <PropertyRow>()?.Node; if (destNode == null || !ArePropertiesCompatible(node, destNode, a.TargetPropertyPath)) { continue; } if (k.Frame + offset.X >= 0) { var k1 = k.Clone(); k1.Frame += offset.X; // The same logic is used to create keyframes as everywhere, but extended by setting // all parameters from a particular keyframe. Yes, this creates some overhead. operations.Add(() => SetAnimableProperty.Perform(destNode, a.TargetPropertyPath, k1.Value, true, false, k1.Frame)); operations.Add(() => SetKeyframe.Perform(destNode, a.TargetPropertyPath, Document.Current.AnimationId, k1)); } // Order is importent. RemoveKeyframe must be after SetKeyframe, // to prevent animator clean up if all keys were removed. if (removeOriginals) { operations.Add(() => RemoveKeyframe.Perform(a, k.Frame)); } } } } } foreach (var o in operations) { o(); } }
public static void ApplyTransformationToWidgetsGroupObb(IEnumerable <Widget> widgetsInParentSpace, Matrix32d obbInParentSpace, Transform2d obbTransformation, bool convertScaleToSize) { Matrix32d originalObbToParentSpace = obbInParentSpace; if (Math.Abs(originalObbToParentSpace.CalcDeterminant()) < Mathf.ZeroTolerance) { return; } Matrix32d obbTransformationMatrix = obbTransformation.ToMatrix32(); foreach (Widget widget in widgetsInParentSpace) { WidgetZeroScalePreserver zeroScalePreserver = new WidgetZeroScalePreserver(widget); zeroScalePreserver.Store(); try { Matrix32d widgetToParentSpace = widget.CalcLocalToParentTransformDouble(); Matrix32d widgetToOriginalObbSpace = widgetToParentSpace * originalObbToParentSpace.CalcInversed(); // Calculate the new obb transformation in the parent space. Matrix32d deformedObbToParentSpace = obbTransformationMatrix * originalObbToParentSpace; Matrix32d deformedWidgetToParentSpace = widgetToOriginalObbSpace * deformedObbToParentSpace; Transform2d widgetResultTransform = widget.ExtractTransform2Double(deformedWidgetToParentSpace, widget.Rotation + obbTransformation.Rotation); // Correct a rotation delta, to prevent wrong values if a new angle 0 and previous is 359, // then rotationDelta must be 1. double rotationDelta = Mathd.Wrap180(widgetResultTransform.Rotation - widget.Rotation); // Reduce an influence of small transformations (Scale, Position, Rotation). bool needChangeScaleX = IsSignificantChangeOfValue(widget.Scale.X, widgetResultTransform.Scale.X); bool needChangeScaleY = IsSignificantChangeOfValue(widget.Scale.Y, widgetResultTransform.Scale.Y); if (needChangeScaleX || needChangeScaleY) { Vector2 useScale = new Vector2( (float)(!needChangeScaleX ? widget.Scale.X : widgetResultTransform.Scale.X), (float)(!needChangeScaleY ? widget.Scale.Y : widgetResultTransform.Scale.Y) ); useScale = zeroScalePreserver.AdjustToScale(useScale); zeroScalePreserver.Restore(); if (!convertScaleToSize) { SetAnimableProperty.Perform(widget, nameof(Widget.Scale), useScale, CoreUserPreferences.Instance.AutoKeyframes); } else { Vector2 useSize = new Vector2( Math.Abs(widget.Scale.X) < FloatSignificantDelta ? widget.Size.X : widget.Size.X * Math.Abs(useScale.X / widget.Scale.X), Math.Abs(widget.Scale.Y) < FloatSignificantDelta ? widget.Size.Y : widget.Size.Y * Math.Abs(useScale.Y / widget.Scale.Y) ); SetAnimableProperty.Perform(widget, nameof(Widget.Size), useSize, CoreUserPreferences.Instance.AutoKeyframes); } } bool needChangePositionX = IsSignificantChangeOfValue(widget.Position.X, widgetResultTransform.Translation.X); bool needChangePositionY = IsSignificantChangeOfValue(widget.Position.Y, widgetResultTransform.Translation.Y); if (needChangePositionX || needChangePositionY) { SetAnimableProperty.Perform(widget, nameof(Widget.Position), new Vector2( (float)(!needChangePositionX ? widget.Position.X : widgetResultTransform.Translation.X), (float)(!needChangePositionY ? widget.Position.Y : widgetResultTransform.Translation.Y) ), CoreUserPreferences.Instance.AutoKeyframes); } if (IsSignificantChangeByDelta(widget.Rotation, rotationDelta)) { SetAnimableProperty.Perform(widget, nameof(Widget.Rotation), (float)(widget.Rotation + rotationDelta), CoreUserPreferences.Instance.AutoKeyframes); } } finally { zeroScalePreserver.Restore(); } } }
protected override bool ProbeInternal(BoneRow node, Row row, RowLocation location) { if (!(location.ParentRow.Components.Contains <BoneRow>() || location.ParentRow.Components.Contains <FolderRow>())) { return(false); } var targetParent = location.ParentRow.Components.Get <BoneRow>()?.Bone; try { var bone = row.Components.Get <BoneRow>().Bone; // Check if bone target parent is bone descendant if (IsDescendant(bone, targetParent)) { return(false); } if (bone.BaseIndex == 0 && !location.ParentRow.Components.Contains <BoneRow>()) { MoveFolderItemTo(bone, location); } else if (!location.ParentRow.Components.Contains <BoneRow>()) { SetAnimableProperty.Perform( bone, nameof(Bone.Position), bone.Position * bone.CalcLocalToParentWidgetTransform(), CoreUserPreferences.Instance.AutoKeyframes); var boneEntry = bone.Parent.AsWidget.BoneArray[bone.Index]; float angle = (boneEntry.Tip - boneEntry.Joint).Atan2Deg; SetAnimableProperty.Perform( bone, nameof(Bone.Rotation), angle, CoreUserPreferences.Instance.AutoKeyframes); } else { SetAnimableProperty.Perform( bone, nameof(Bone.Position), Vector2.Zero, CoreUserPreferences.Instance.AutoKeyframes); var newParent = location.ParentRow.Components.Get <BoneRow>().Bone; var parentEntry = newParent.Parent.AsWidget.BoneArray[newParent.Index]; float parentAngle = (parentEntry.Tip - parentEntry.Joint).Atan2Deg; var boneEntry = bone.Parent.AsWidget.BoneArray[bone.Index]; float angle = (boneEntry.Tip - boneEntry.Joint).Atan2Deg; SetAnimableProperty.Perform( bone, nameof(Bone.Rotation), angle - parentAngle, CoreUserPreferences.Instance.AutoKeyframes); } SetProperty.Perform(bone, nameof(Bone.BaseIndex), targetParent?.Index ?? 0); SortBonesInChain.Perform(bone); var nodes = Document.Current.Container.Nodes; var parentBone = nodes.GetBone(bone.BaseIndex); while (parentBone != null && !parentBone.EditorState().ChildrenExpanded) { SetProperty.Perform(parentBone.EditorState(), nameof(NodeEditorState.ChildrenExpanded), true); bone = nodes.GetBone(bone.BaseIndex); } } catch (InvalidOperationException e) { AlertDialog.Show(e.Message); return(false); } return(true); }
private void Stretch(Boundaries boundaries, DragSide side, int newPos, bool stretchMarkers) { int length; if (side == DragSide.Left) { length = boundaries.Right - newPos - 1; } else { length = newPos - boundaries.Left - 1; } int oldLength = boundaries.Right - boundaries.Left - 1; for (int i = boundaries.Top; i <= boundaries.Bottom; ++i) { if (!(Document.Current.Rows[i].Components.Get <NodeRow>()?.Node is IAnimationHost animable)) { continue; } foreach (var animator in animable.Animators.ToList()) { IEnumerable <IKeyframe> saved = savedKeyframes[animator]; if ( side == DragSide.Left && length < oldLength || side == DragSide.Right && length > oldLength ) { saved = saved.Reverse(); } foreach (var key in saved) { RemoveKeyframe.Perform(animator, key.Frame); } foreach (var key in saved) { double relpos = savedPositions[key]; int newFrame; if (side == DragSide.Left) { newFrame = (int)Math.Round(newPos + relpos * length); } else { newFrame = (int)Math.Round(boundaries.Left + relpos * length); } var newKey = key.Clone(); newKey.Frame = newFrame; SetAnimableProperty.Perform( animable, animator.TargetPropertyPath, newKey.Value, createAnimatorIfNeeded: true, createInitialKeyframeForNewAnimator: false, newKey.Frame ); SetKeyframe.Perform(animable, animator.TargetPropertyPath, Document.Current.AnimationId, newKey); } } } if (stretchMarkers) { foreach (var marker in savedMarkers) { DeleteMarker.Perform(Document.Current.Container, marker, removeDependencies: false); } foreach (var marker in savedMarkers) { double relpos = savedMarkerPositions[marker]; int newFrame; if (side == DragSide.Left) { newFrame = (int)Math.Round(newPos + relpos * length); } else { newFrame = (int)Math.Round(boundaries.Left + relpos * length); } var newMarker = marker.Clone(); newMarker.Frame = newFrame; SetMarker.Perform(Document.Current.Container, newMarker, removeDependencies: false); } } }