Browse Source

Merge branch 'master' into development

Krzysztof Krysiński 1 day ago
parent
commit
e639361987

+ 10 - 6
src/PixiEditor.ChangeableDocument/Changeables/Brushes/BrushEngine.cs

@@ -31,6 +31,7 @@ public class BrushEngine : IDisposable
     private int lastAppliedHistoryIndex = -1;
     private VecI lastCachedTexturePaintableSize = VecI.Zero;
     private TexturePaintable? lastCachedTexturePaintable = null;
+    private Matrix3X3 lastCachedTransform = Matrix3X3.Identity;
     private readonly List<RecordedPoint> pointsHistory = new();
 
     private bool drawnOnce = false;
@@ -217,6 +218,7 @@ public class BrushEngine : IDisposable
             lastPos = point;
             drawnOnce = true;
             target?.SetBlendMode(imageBlendMode);
+            brushNode.ResetContentTexture();
         }
 
         float strokeWidth = brushData.StrokeWidth;
@@ -333,10 +335,11 @@ public class BrushEngine : IDisposable
         bool snapToPixels = brushNode.SnapToPixels.Value;
         bool canReuseStamps = brushNode.CanReuseStamps.Value;
         Blender? stampBlender = brushNode.UseCustomStampBlender.Value ? brushNode.LastStampBlender : null;
+        Matrix3X3 transform = brushNode.Transform.Value;
         //Blender? imageBlender = brushNode.UseCustomImageBlender.Value ? brushNode.LastImageBlender : null;
 
         if (PaintBrush(target, autoPosition, vectorShape, rect, fitToStrokeSize, pressure, content, contentTexture,
-                stampBlender, brushNode.StampBlendMode.Value, antiAliasing, fill, stroke, snapToPixels, canReuseStamps))
+                stampBlender, brushNode.StampBlendMode.Value, antiAliasing, fill, stroke, snapToPixels, canReuseStamps, transform))
         {
             lastPos = point;
         }
@@ -345,7 +348,7 @@ public class BrushEngine : IDisposable
     public bool PaintBrush(ChunkyImage target, bool autoPosition, ShapeVectorData vectorShape,
         RectD rect, bool fitToStrokeSize, float pressure, Painter? content,
         Texture? contentTexture, Blender? blender, DrawingApiBlendMode blendMode, bool antiAliasing, Paintable fill, Paintable stroke,
-        bool snapToPixels, bool canReuseStamps)
+        bool snapToPixels, bool canReuseStamps, Matrix3X3 transform)
     {
         var path = vectorShape.ToPath(true);
         if (path == null)
@@ -411,11 +414,12 @@ public class BrushEngine : IDisposable
                 TexturePaintable brushPaintable;
                 if (canReuseStamps)
                 {
-                    if (lastCachedTexturePaintableSize != contentTexture.Size || lastCachedTexturePaintable == null)
+                    if (lastCachedTexturePaintableSize != contentTexture.Size || lastCachedTexturePaintable == null || lastCachedTransform != transform)
                     {
                         lastCachedTexturePaintable?.Dispose();
                         lastCachedTexturePaintable = new TexturePaintable(new Texture(contentTexture), false);
                         lastCachedTexturePaintableSize = contentTexture.Size;
+                        lastCachedTransform = transform;
                     }
 
                     brushPaintable = lastCachedTexturePaintable;
@@ -570,15 +574,15 @@ public class BrushEngine : IDisposable
     {
         if (fitToStrokeSize)
         {
-            VecD scale = new VecD(rect.Size.X / (float)path.TightBounds.Width,
-                rect.Size.Y / (float)path.TightBounds.Height);
+            VecD scale = new VecD(rect.Size.X / (float)vectorShape.GeometryAABB.Width,
+                rect.Size.Y / (float)vectorShape.GeometryAABB.Height);
             if (scale.IsNaNOrInfinity())
             {
                 scale = VecD.Zero;
             }
 
             VecD uniformScale = new VecD(Math.Min(scale.X, scale.Y));
-            VecD center = autoPosition ? rect.Center : vectorShape.TransformedAABB.Center;
+            VecD center = autoPosition ? rect.Center : vectorShape.GeometryAABB.Center;
 
             path.Transform(Matrix3X3.CreateScale((float)uniformScale.X, (float)uniformScale.Y, (float)center.X,
                 (float)center.Y));

+ 22 - 7
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Brushes/BrushOutputNode.cs

@@ -89,8 +89,12 @@ public class BrushOutputNode : Node
     public const string UseCustomStampBlenderProperty = "UseCustomStampBlender";
     public const string CustomStampBlenderCodeProperty = "CustomStampBlender";
     public const string StampBlendModeProperty = "StampBlendMode";
+    public const string ContentProperty = "Content";
+    public const string ContentTransformProperty = "Transform";
 
     private VectorPath? previewVectorPath;
+    private bool drawnContentTextureOnce = false;
+    private Matrix3X3 lastTranform = Matrix3X3.Identity;
 
     public BrushOutputNode()
     {
@@ -98,8 +102,8 @@ public class BrushOutputNode : Node
         VectorShape = CreateInput<ShapeVectorData>("VectorShape", "SHAPE", null);
         Stroke = CreateInput<Paintable>("Stroke", "STROKE", null);
         Fill = CreateInput<Paintable>("Fill", "FILL", null);
-        Content = CreateRenderInput("Content", "CONTENT");
-        Transform = CreateInput<Matrix3X3>("Transform", "TRANSFORM", Matrix3X3.Identity);
+        Content = CreateRenderInput(ContentProperty, "CONTENT");
+        Transform = CreateInput<Matrix3X3>(ContentTransformProperty, "CONTENT_TRANSFORM", Matrix3X3.Identity);
         ImageBlendMode = CreateInput<Drawie.Backend.Core.Surfaces.BlendMode>("BlendMode", "BLEND_MODE",
             Drawie.Backend.Core.Surfaces.BlendMode.SrcOver);
         StampBlendMode = CreateInput<Drawie.Backend.Core.Surfaces.BlendMode>(StampBlendModeProperty, "STAMP_BLEND_MODE",
@@ -146,11 +150,17 @@ public class BrushOutputNode : Node
         {
             if (context.RenderOutputSize.LongestAxis > 0)
             {
-                ContentTexture = cache.RequestTexture(0, context.RenderOutputSize, context.ProcessingColorSpace);
-                ContentTexture.DrawingSurface.Canvas.Save();
-                ContentTexture.DrawingSurface.Canvas.SetMatrix(Transform.Value);
-                Content.Value.Paint(context, ContentTexture.DrawingSurface.Canvas);
-                ContentTexture.DrawingSurface.Canvas.Restore();
+                if (!CanReuseStamps.Value || ContentTexture == null || ContentTexture.Size != context.RenderOutputSize ||
+                    !drawnContentTextureOnce || Transform.Value != lastTranform)
+                {
+                    ContentTexture = cache.RequestTexture(0, context.RenderOutputSize, context.ProcessingColorSpace);
+                    ContentTexture.DrawingSurface.Canvas.Save();
+                    ContentTexture.DrawingSurface.Canvas.SetMatrix(Transform.Value);
+                    Content.Value.Paint(context, ContentTexture.DrawingSurface.Canvas);
+                    ContentTexture.DrawingSurface.Canvas.Restore();
+                    drawnContentTextureOnce = true;
+                    lastTranform = Transform.Value;
+                }
             }
         }
 
@@ -179,6 +189,11 @@ public class BrushOutputNode : Node
         additionalData["PersistentId"] = PersistentId;
     }
 
+    public void ResetContentTexture()
+    {
+        drawnContentTextureOnce = false;
+    }
+
     internal override void DeserializeAdditionalData(IReadOnlyDocument target, IReadOnlyDictionary<string, object> data,
         List<IChangeInfo> infos)
     {

+ 23 - 2
src/PixiEditor.ChangeableDocument/Changes/NodeGraph/Blackboard/SetBlackboardVariable_Change.cs

@@ -6,7 +6,7 @@ using PixiEditor.ChangeableDocument.ChangeInfos.NodeGraph.Blackboard;
 
 namespace PixiEditor.ChangeableDocument.Changes.NodeGraph.Blackboard;
 
-internal class SetBlackboardVariable_Change : Change
+internal class SetBlackboardVariable_Change : InterruptableUpdateableChange
 {
     private string variable;
     private object value;
@@ -19,7 +19,7 @@ internal class SetBlackboardVariable_Change : Change
     private string unit;
     private bool isExposed;
 
-    [GenerateMakeChangeAction]
+    [GenerateUpdateableChangeActions]
     public SetBlackboardVariable_Change(string variable, object value, double min, double max, string unit, bool isExposed)
     {
         this.variable = variable;
@@ -30,6 +30,17 @@ internal class SetBlackboardVariable_Change : Change
         this.isExposed = isExposed;
     }
 
+    [UpdateChangeMethod]
+    public void Update(string variable, object value, double min, double max, string unit, bool isExposed)
+    {
+        this.variable = variable;
+        this.value = value;
+        this.min = min;
+        this.max = max;
+        this.unit = unit;
+        this.isExposed = isExposed;
+    }
+
     public override bool InitializeAndValidate(Document target)
     {
         if (variable == null)
@@ -80,6 +91,11 @@ internal class SetBlackboardVariable_Change : Change
         out bool ignoreInUndo)
     {
         ignoreInUndo = false;
+        return Apply(target);
+    }
+
+    private OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target)
+    {
         if (target.NodeGraph.Blackboard.GetVariable(variable) == null)
         {
             Type type = value.GetType();
@@ -95,6 +111,11 @@ internal class SetBlackboardVariable_Change : Change
         return new List<IChangeInfo>() { new BlackboardVariable_ChangeInfo(variable, oldVar.Type, value, oldVar.Min ?? double.MinValue, oldVar.Max ?? double.MaxValue, oldVar.Unit), new BlackboardVariableExposed_ChangeInfo(variable, isExposed) };
     }
 
+    public override OneOf<None, IChangeInfo, List<IChangeInfo>> ApplyTemporarily(Document target)
+    {
+        return Apply(target);
+    }
+
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
     {
         if (!existsInBlackboard)

+ 1 - 0
src/PixiEditor.UI.Common/Controls/SizeInput.axaml

@@ -40,6 +40,7 @@
                      FocusNext="{Binding FocusNext, ElementName=uc}"
                      SelectOnMouseClick="{Binding BehaveLikeSmallEmbeddedField, ElementName=uc}"
                      ConfirmOnEnter="{Binding BehaveLikeSmallEmbeddedField, ElementName=uc}"
+                     DraggingGrabber="{Binding DraggingGrabber, ElementName=uc, Mode=TwoWay}"
                      Width="53"/>
             <Grid Grid.Column="1" Background="{Binding BorderBrush, ElementName=border}"
                   d:Background="{DynamicResource ThemeAccentBrush}"/>

+ 8 - 0
src/PixiEditor.UI.Common/Controls/SizeInput.axaml.cs

@@ -9,6 +9,8 @@ namespace PixiEditor.UI.Common.Controls;
 /// </summary>
 public partial class SizeInput : UserControl
 {
+    public static readonly StyledProperty<bool> DraggingGrabberProperty = AvaloniaProperty.Register<SizeInput, bool>("DraggingGrabber");
+
     public static readonly StyledProperty<double> SizeProperty =
         AvaloniaProperty.Register<SizeInput, double>(nameof(Size), defaultValue: 1);
 
@@ -115,6 +117,12 @@ public partial class SizeInput : UserControl
         set => SetValue(UnitProperty, value);
     }
 
+    public bool DraggingGrabber
+    {
+        get { return (bool)GetValue(DraggingGrabberProperty); }
+        set { SetValue(DraggingGrabberProperty, value); }
+    }
+
     private void Border_MouseWheel(object? sender, PointerWheelEventArgs e)
     {
         int step = (int)e.Delta.Y / 100;

+ 2 - 1
src/PixiEditor/Data/Localization/Languages/en.json

@@ -1290,5 +1290,6 @@
   "VIEWPORT_INFO_NODE": "Viewport Info",
   "EQUALS_NODE": "Equals",
   "IS_LEFT_BUTTON_PRESSED": "Is Left Button Pressed",
-  "IS_RIGHT_BUTTON_PRESSED": "Is Right Button Pressed"
+  "IS_RIGHT_BUTTON_PRESSED": "Is Right Button Pressed",
+  "CONTENT_TRANSFORM": "Content Transform"
 }

+ 3 - 1
src/PixiEditor/Models/DocumentModels/ChangeExecutionController.cs

@@ -243,7 +243,9 @@ internal class ChangeExecutionController
         LeftMousePressed = true;
         RightMousePressed = args.Properties.IsRightButtonPressed;
 
-        lastPointerInfo = ConstructPointerInfo(args.Point.PositionOnCanvas, args);
+        // Some drivers do not report pressure for pointer down events. For example it happens on X11. We set pressure to 0, so Brush Engine does not
+        // draw a blob with 0.5 pressure on each start of stroke.
+        lastPointerInfo = ConstructPointerInfo(args.Point.PositionOnCanvas, args) with { Pressure = 0 }; 
         if (_queuedExecutor != null && currentSession == null)
         {
             StartExecutor(_queuedExecutor);

+ 2 - 2
src/PixiEditor/Properties/AssemblyInfo.cs

@@ -43,5 +43,5 @@ using System.Runtime.InteropServices;
 // You can specify all the values or you can default the Build and Revision Numbers
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("2.1.0.2")]
-[assembly: AssemblyFileVersion("2.1.0.2")]
+[assembly: AssemblyVersion("2.1.0.3")]
+[assembly: AssemblyFileVersion("2.1.0.3")]

+ 16 - 1
src/PixiEditor/ViewModels/Document/Blackboard/VariableViewModel.cs

@@ -84,8 +84,23 @@ internal class VariableViewModel : ViewModelBase, IVariableHandler
             if (suppressValueChange)
                 return;
 
+            if (SettingView.MergeChanges)
+            {
+                internals.ActionAccumulator.AddActions(
+                    new SetBlackboardVariable_Action(Name, AdjustValueForBlackboard(SettingView.Value), min, max, unit, IsExposedBindable));
+            }
+            else
+            {
+                internals.ActionAccumulator.AddFinishedActions(
+                    new SetBlackboardVariable_Action(Name, AdjustValueForBlackboard(SettingView.Value), min, max, unit, IsExposedBindable),
+                    new EndSetBlackboardVariable_Action());
+            }
+        };
+
+        SettingView.MergeChangesEnded += () =>
+        {
             internals.ActionAccumulator.AddFinishedActions(
-                new SetBlackboardVariable_Action(Name, AdjustValueForBlackboard(SettingView.Value), min, max, unit, IsExposedBindable));
+                new EndSetBlackboardVariable_Action());
         };
 
         RemoveCommand = new RelayCommand(() =>

+ 8 - 0
src/PixiEditor/ViewModels/Document/Nodes/Brushes/BrushOutputNodeViewModel.cs

@@ -15,6 +15,8 @@ internal class BrushOutputNodeViewModel : NodeViewModel<BrushOutputNode>
         InputPropertyMap[BrushOutputNode.BrushNameProperty].SocketEnabled = false;
         InputPropertyMap[BrushOutputNode.FitToStrokeSizeProperty].SocketEnabled = false;
         InputPropertyMap[BrushOutputNode.UseCustomStampBlenderProperty].ValueChanged += OnValueChanged;
+        InputPropertyMap[BrushOutputNode.ContentProperty].ConnectedOutputChanged += OnContentConnectionChanged;
+        InputPropertyMap[BrushOutputNode.ContentTransformProperty].IsVisible = InputPropertyMap[BrushOutputNode.ContentProperty].ConnectedOutput != null;
         if(InputPropertyMap[BrushOutputNode.CustomStampBlenderCodeProperty] is StringPropertyViewModel codeProperty)
         {
             codeProperty.IsVisible = (bool)InputPropertyMap[BrushOutputNode.UseCustomStampBlenderProperty].Value;
@@ -27,4 +29,10 @@ internal class BrushOutputNodeViewModel : NodeViewModel<BrushOutputNode>
         InputPropertyMap[BrushOutputNode.CustomStampBlenderCodeProperty].IsVisible = (bool)args.NewValue;
         InputPropertyMap[BrushOutputNode.StampBlendModeProperty].IsVisible = !(bool)args.NewValue;
     }
+
+    private void OnContentConnectionChanged(object? sender, EventArgs eventArgs)
+    {
+        var connection = InputPropertyMap[BrushOutputNode.ContentProperty].ConnectedOutput;
+        InputPropertyMap[BrushOutputNode.ContentTransformProperty].IsVisible = connection != null;
+    }
 }

+ 1 - 1
src/PixiEditor/ViewModels/SubViewModels/ToolsViewModel.cs

@@ -625,7 +625,7 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
             ViewModelMain.Current.DocumentManagerSubViewModel.ActiveDocument?.Operations.TryStopToolLinkedExecutor();
         }
 
-        ActiveTool.UseTool(canvasPos);
+        ActiveTool?.UseTool(canvasPos);
     }
 
     public void ConvertedKeyDownInlet(FilteredKeyEventArgs args)

+ 16 - 0
src/PixiEditor/ViewModels/Tools/ToolSettings/Settings/Setting.cs

@@ -58,6 +58,8 @@ internal abstract class Setting : ObservableObject
     protected bool hasOverwrittenValue;
     protected bool hasOverwrittenExposed;
 
+    private bool mergeChanges;
+
     protected Setting(string name)
     {
         Name = name;
@@ -126,6 +128,20 @@ internal abstract class Setting : ObservableObject
         set => toolsetValues[currentToolset] = value;
     }
 
+    public bool MergeChanges
+    {
+        get => mergeChanges;
+        set
+        {
+            if (SetProperty(ref mergeChanges, value) && !value)
+            {
+                MergeChangesEnded?.Invoke();
+            }
+        }
+    }
+
+    public event Action MergeChangesEnded;
+
     public abstract Type GetSettingType();
 
     public void SetOverwriteValue(object value)

+ 1 - 1
src/PixiEditor/Views/Blackboard/BlackboardView.axaml

@@ -33,7 +33,7 @@
                 Classes="pixi-icon"
                 Content="{DynamicResource icon-plus-square}"
                 Command="{Binding AddVariableCommand, RelativeSource={RelativeSource AncestorType=blackboard:BlackboardView, Mode=FindAncestor}}"
-                CommandParameter="{Binding SelectedItem, ElementName=typeComboBox}"/>
+                CommandParameter="{Binding SelectedVariableOption, RelativeSource={RelativeSource AncestorType=blackboard:BlackboardView, Mode=FindAncestor}}"/>
 
         <Separator Background="{DynamicResource ThemeControlHighBrush}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,5"/>
 

+ 1 - 0
src/PixiEditor/Views/Main/ViewportControls/Viewport.axaml

@@ -277,6 +277,7 @@
             BackgroundBitmap="{Binding BackgroundBitmap, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWay}"
             MaxBilinearSamplingSize="{Binding MaxBilinearSamplingSize, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWay}"
             PointerPressed="Scene_OnContextMenuOpening"
+            ContextRequested="Scene_OnContextRequested"
             ui1:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, RelativeSource={RelativeSource Self}}">
             <rendering:Scene.ContextFlyout>
                 <Flyout x:CompileBindings="True" x:DataType="subViewModels:ViewportWindowViewModel"

+ 6 - 0
src/PixiEditor/Views/Main/ViewportControls/Viewport.axaml.cs

@@ -434,6 +434,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         Scene.ScaleChanged += OnScaleChanged;
     }
 
+
     private void OnScaleChanged(double newScale)
     {
         SnappingViewModel.SnappingController.SnapDistance = SnappingController.DefaultSnapDistance / newScale;
@@ -776,4 +777,9 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
             }
         }
     }
+
+    private void Scene_OnContextRequested(object? sender, ContextRequestedEventArgs e)
+    {
+        e.Handled = true;
+    }
 }

+ 1 - 0
src/PixiEditor/Views/Tools/ToolSettings/Settings/FloatSettingView.axaml

@@ -14,6 +14,7 @@
                        Value="{Binding Value, Mode=TwoWay}"
                        Min="{Binding Min}"
                        Max="{Binding Max}"
+                       DraggingGrabber="{Binding MergeChanges, Mode=OneWayToSource}"
                        Decimals="{Binding DecimalPlaces}"
                        Margin="0,0,0,0" 
                        FocusNext="False"

+ 1 - 0
src/PixiEditor/Views/Tools/ToolSettings/Settings/PercentSettingView.axaml

@@ -15,6 +15,7 @@
                        Size="{Binding Value, Mode=TwoWay, Converter={converters:IntPercentConverter}}"
                        MinSize="{Binding Min, Converter={converters:IntPercentConverter}}"
                        MaxSize="{Binding Max, Converter={converters:IntPercentConverter}}"
+                       DraggingGrabber="{Binding MergeChanges, Mode=OneWayToSource}"
                        Unit="%"
                        Margin="0,0,0,0" 
                         />

+ 1 - 0
src/PixiEditor/Views/Tools/ToolSettings/Settings/SizeSettingView.axaml

@@ -17,5 +17,6 @@
                      Decimals="{Binding DecimalPlaces}"
                      Unit="{Binding Unit}"
                      IsEnabled="{Binding IsEnabled}" FocusNext="False"
+                     DraggingGrabber="{Binding MergeChanges, Mode=OneWayToSource}"
                      Size="{Binding Value, Mode=TwoWay}"/>
 </UserControl>