Browse Source

Channels Dock WIP

CPKreuz 1 year ago
parent
commit
33d9771f4c

+ 10 - 0
src/PixiEditor.AvaloniaUI/Models/DocumentModels/ColorChannel.cs

@@ -0,0 +1,10 @@
+namespace PixiEditor.AvaloniaUI.Models.DocumentModels;
+
+public enum ColorChannel
+{
+    None = -1,
+    Red,
+    Green,
+    Blue,
+    Alpha
+}

+ 14 - 0
src/PixiEditor.AvaloniaUI/Models/DocumentModels/ColorChannelMode.cs

@@ -0,0 +1,14 @@
+using System.Diagnostics.Contracts;
+
+namespace PixiEditor.AvaloniaUI.Models.DocumentModels;
+
+internal record struct ColorChannelMode(bool IsVisible, bool IsSolo)
+{
+    [Pure]
+    public ColorChannelMode WithVisible(bool visible) => this with { IsVisible = visible };
+    
+    [Pure]
+    public ColorChannelMode WithSolo(bool solo) => this with { IsSolo = solo };
+
+    public static ColorChannelMode Default => new(true, false);
+}

+ 154 - 0
src/PixiEditor.AvaloniaUI/Models/DocumentModels/ViewportColorChannels.cs

@@ -0,0 +1,154 @@
+using System.ComponentModel;
+using System.Diagnostics.Contracts;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.AvaloniaUI.Models.DocumentModels;
+
+internal record struct ViewportColorChannels
+{
+    public ColorChannelMode Red { get; }
+
+    public ColorChannelMode Green { get; }
+
+    public ColorChannelMode Blue { get; }
+
+    public ColorChannelMode Alpha { get; }
+
+    public ViewportColorChannels(ColorChannelMode red, ColorChannelMode green, ColorChannelMode blue, ColorChannelMode alpha)
+    {
+        ReadOnlySpan<ColorChannelMode> modes = [red, green, blue, alpha];
+        int solos = 0;
+
+        for (int i = 0; i < modes.Length; i++)
+        {
+            if (!modes[i].IsSolo)
+            {
+                continue;
+            }
+
+            solos++;
+
+            if (solos > 1)
+            {
+                throw new ArgumentException("Can't have more than one channel solo");
+            }
+        }
+
+        Red = red;
+        Green = green;
+        Blue = blue;
+        Alpha = alpha;
+    }
+
+    public static ViewportColorChannels Default => new(ColorChannelMode.Default, ColorChannelMode.Default, ColorChannelMode.Default, ColorChannelMode.Default);
+
+    public override string ToString() => $"Red: {Red}; Green: {Green}; Blue: {Blue}; Alpha: {Alpha}";
+
+    public bool IsVisiblyVisible(ColorChannel channel) =>
+        GetModeForChannel(channel).IsVisible || GetModeForChannel(channel).IsSolo;
+
+    public bool IsSolo(ColorChannel channel) => GetModeForChannel(channel).IsSolo;
+
+    [Pure]
+    public ViewportColorChannels WithModeForChannel(ColorChannel channel, Func<ColorChannelMode, ColorChannelMode> mode, bool otherNonSolo)
+    {
+        switch (channel)
+        {
+            case ColorChannel.Red:
+                return new ViewportColorChannels(mode(Red), MON(Green), MON(Blue), MON(Alpha));
+            case ColorChannel.Green:
+                return new ViewportColorChannels(MON(Red), mode(Green), MON(Blue), MON(Alpha));
+            case ColorChannel.Blue:
+                return new ViewportColorChannels(MON(Red), MON(Green), mode(Blue), MON(Alpha));
+            case ColorChannel.Alpha:
+                return new ViewportColorChannels(MON(Red), MON(Green), MON(Blue), mode(Alpha));
+            case ColorChannel.None:
+                throw new InvalidEnumArgumentException(nameof(channel), (int)channel, typeof(ColorChannel));
+            default:
+                throw new ArgumentOutOfRangeException(nameof(channel), channel, null);
+        }
+
+        // Modify Other Node
+        ColorChannelMode MON(ColorChannelMode otherMode)
+        {
+            if (otherNonSolo && otherMode.IsSolo)
+            {
+                return otherMode.WithSolo(false);
+            }
+
+            return otherMode;
+        }
+    }
+    
+    public ColorChannelMode GetModeForChannel(ColorChannel channel) => channel switch
+    {
+        ColorChannel.Red => Red,
+        ColorChannel.Green => Green,
+        ColorChannel.Blue => Blue,
+        ColorChannel.Alpha => Alpha
+    };
+
+    public ColorMatrix GetColorMatrix()
+    {
+        var solo = GetSoloChannel();
+
+        var (otherToRed, redToRed) = GetTarget(Red, solo, ColorChannel.Red);
+        var (otherToGreen, greenToGreen) = GetTarget(Green, solo, ColorChannel.Green);
+        var (otherToBlue, blueToBlue) = GetTarget(Blue, solo, ColorChannel.Blue);
+        
+        var opaque = solo is not ColorChannel.None || !Alpha.IsVisible;
+
+        var alphaToOther = Alpha.IsSolo;
+        var alphaToAlpha = !alphaToOther && !opaque;
+
+        var o2r = otherToRed ? 1 : 0;
+        var r2r = redToRed ? 1 : 0;
+
+        var o2g = otherToGreen ? 1 : 0;
+        var g2g = greenToGreen ? 1 : 0;
+
+        var o2b = otherToBlue ? 1 : 0;
+        var b2b = blueToBlue ? 1 : 0;
+
+        var a2o = alphaToOther ? 1 : 0;
+        var a2a = alphaToAlpha ? 1 : 0;
+
+        var o = opaque ? 1 : 0;
+
+        return new ColorMatrix(
+            (r2r, o2g, o2b, a2o, 0),
+            (o2r, g2g, o2b, a2o, 0),
+            (o2r, o2g, b2b, a2o, 0),
+            (0, 0, 0, a2a, o)
+        );
+    }
+
+    private static (bool otherToRed, bool targetToTarget) GetTarget(ColorChannelMode mode, ColorChannel solo, ColorChannel target)
+    {
+        var otherToTarget = solo == target;
+        var targetToTarget = solo == target || (mode.IsVisible && solo == ColorChannel.None);
+
+        return (otherToTarget, targetToTarget);
+    }
+
+    public ColorChannel GetSoloChannel()
+    {
+        ReadOnlySpan<(ColorChannel channel, ColorChannelMode mode)> modes = [
+            (ColorChannel.Red, Red),
+            (ColorChannel.Green, Green),
+            (ColorChannel.Blue, Blue),
+            (ColorChannel.Alpha, Alpha)
+        ];
+    
+        for (int i = 0; i < modes.Length; i++)
+        {
+            var mode = modes[i];
+            if (modes[i].mode.IsSolo)
+            {
+                return mode.channel;
+            }
+        }
+    
+        return ColorChannel.None;
+    }
+}

+ 137 - 0
src/PixiEditor.AvaloniaUI/ViewModels/Dock/ChannelsDockViewModel.cs

@@ -0,0 +1,137 @@
+using System.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using PixiEditor.AvaloniaUI.Models.DocumentModels;
+using PixiEditor.AvaloniaUI.ViewModels.SubViewModels;
+using PixiEditor.Extensions.Common.Localization;
+
+namespace PixiEditor.AvaloniaUI.ViewModels.Dock;
+
+internal class ChannelsDockViewModel : DockableViewModel
+{
+    public const string TabId = "ChannelsDock";
+
+    public override string Id => TabId;
+    public override string Title => new LocalizedString("CHANNELS_DOCK_TITLE");
+    public override bool CanFloat => true;
+    public override bool CanClose => true;
+
+    public WindowViewModel WindowViewModel { get; }
+
+    private ViewportWindowViewModel? _activeViewport;
+
+    public ViewportWindowViewModel? ActiveViewport
+    {
+        get => _activeViewport;
+        set => SetProperty(ref _activeViewport, value);
+    }
+
+    private ViewportColorChannels Channels
+    {
+        get => ActiveViewport.Channels;
+        set => ActiveViewport.Channels = value;
+    }
+
+    public ChannelsDockViewModel(WindowViewModel windowViewModel)
+    {
+        WindowViewModel = windowViewModel;
+        windowViewModel.ActiveViewportChanged += WindowViewModelOnActiveViewportChanged;
+    }
+
+    private void WindowViewModelOnActiveViewportChanged(object? sender, ViewportWindowViewModel e)
+    {
+        if (ActiveViewport != null)
+        {
+            ActiveViewport.PropertyChanged -= ActiveViewportOnPropertyChanged;
+        }
+
+        ActiveViewport = e;
+        ActiveViewport.PropertyChanged += ActiveViewportOnPropertyChanged;
+    }
+
+    private void ActiveViewportOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
+    {
+        if (e.PropertyName != nameof(ViewportWindowViewModel.Channels))
+        {
+            return;
+        }
+
+        OnPropertyChanged(nameof(IsRedVisible));
+        OnPropertyChanged(nameof(IsGreenVisible));
+        OnPropertyChanged(nameof(IsBlueVisible));
+        OnPropertyChanged(nameof(IsAlphaVisible));
+        
+        OnPropertyChanged(nameof(IsRedSolo));
+        OnPropertyChanged(nameof(IsGreenSolo));
+        OnPropertyChanged(nameof(IsBlueSolo));
+        OnPropertyChanged(nameof(IsAlphaSolo));
+    }
+
+    public bool IsRedVisible
+    {
+        get => Channels.IsVisiblyVisible(ColorChannel.Red);
+        set => SetVisible(ColorChannel.Red, value);
+    }
+
+    public bool IsRedSolo
+    {
+        get => Channels.IsSolo(ColorChannel.Red);
+        set => SetSolo(ColorChannel.Red, value);
+    }
+
+    public bool IsGreenVisible
+    {
+        get => Channels.IsVisiblyVisible(ColorChannel.Green);
+        set => SetVisible(ColorChannel.Green, value);
+    }
+
+    public bool IsGreenSolo
+    {
+        get => Channels.IsSolo(ColorChannel.Green);
+        set => SetSolo(ColorChannel.Green, value);
+    }
+
+    public bool IsBlueVisible
+    {
+        get => Channels.IsVisiblyVisible(ColorChannel.Blue);
+        set => SetVisible(ColorChannel.Blue, value);
+    }
+
+    public bool IsBlueSolo
+    {
+        get => Channels.IsSolo(ColorChannel.Blue);
+        set => SetSolo(ColorChannel.Blue, value);
+    }
+
+    public bool IsAlphaVisible
+    {
+        get => Channels.IsVisiblyVisible(ColorChannel.Alpha);
+        set => SetVisible(ColorChannel.Alpha, value);
+    }
+
+    public bool IsAlphaSolo
+    {
+        get => Channels.IsSolo(ColorChannel.Alpha);
+        set => SetSolo(ColorChannel.Alpha, value);
+    }
+
+    private void SetVisible(ColorChannel channel, bool value)
+    {
+        var mode = Channels.GetModeForChannel(channel);
+
+        if (mode.IsSolo && !value)
+        {
+            Channels = Channels.WithModeForChannel(channel, _ => new ColorChannelMode(), false);
+        }
+        else
+        {
+            Channels = Channels.WithModeForChannel(channel, x => x.WithVisible(value), value);
+        }
+    }
+
+    private void SetSolo(ColorChannel channel, bool value)
+    {
+        var mode = Channels.GetModeForChannel(channel);
+
+        Channels = Channels.WithModeForChannel(channel, x => x.WithSolo(value), value);
+    }
+}

+ 2 - 0
src/PixiEditor.AvaloniaUI/ViewModels/Dock/LayoutManager.cs

@@ -40,6 +40,7 @@ internal class LayoutManager
         TimelineDockViewModel timelineDockViewModel = new(mainViewModel.DocumentManagerSubViewModel);
         
         NodeGraphDockViewModel nodeGraphDockViewModel = new(mainViewModel.DocumentManagerSubViewModel);
+        ChannelsDockViewModel channelsDockDockViewModel = new(mainViewModel.WindowSubViewModel);
 
         RegisterDockable(layersDockViewModel);
         RegisterDockable(colorPickerDockViewModel);
@@ -49,6 +50,7 @@ internal class LayoutManager
         RegisterDockable(paletteViewerDockViewModel);
         RegisterDockable(timelineDockViewModel);
         RegisterDockable(nodeGraphDockViewModel);
+        RegisterDockable(channelsDockDockViewModel);
         
         DefaultLayout = new LayoutTree
         {

+ 9 - 0
src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/ViewportWindowViewModel.cs

@@ -20,6 +20,7 @@ internal class ViewportWindowViewModel : SubViewModel<WindowViewModel>, IDockabl
     public ExecutionTrigger<VecI> CenterViewportTrigger { get; } = new ExecutionTrigger<VecI>();
     public ExecutionTrigger<double> ZoomViewportTrigger { get; } = new ExecutionTrigger<double>();
 
+    
     public string Index => _index;
 
     public string Id => id;
@@ -58,6 +59,14 @@ internal class ViewportWindowViewModel : SubViewModel<WindowViewModel>, IDockabl
         }
     }
 
+    private ViewportColorChannels _channels = ViewportColorChannels.Default;
+    
+    public ViewportColorChannels Channels
+    {
+        get => _channels;
+        set => SetProperty(ref _channels, value);
+    }
+
     public void IndexChanged()
     {
         _index = Owner.CalculateViewportIndex(this) ?? "";

+ 47 - 0
src/PixiEditor.AvaloniaUI/Views/Dock/ChannelsDockView.axaml

@@ -0,0 +1,47 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             xmlns:dock="clr-namespace:PixiEditor.AvaloniaUI.ViewModels.Dock"
+             xmlns:ui="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
+             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+             x:Class="PixiEditor.AvaloniaUI.Views.Dock.ChannelsDockView"
+             x:DataType="dock:ChannelsDockViewModel">
+    <Design.DataContext>
+        <dock:ChannelsDockViewModel/>
+    </Design.DataContext>
+    
+    <StackPanel>
+        <Grid ColumnDefinitions="*,Auto">
+            <TextBlock ui:Translator.Key="RED" />
+            <StackPanel Grid.Column="1" Orientation="Horizontal">
+                <ToggleButton Classes="pixi-icon" Content="{DynamicResource icon-eye}" IsChecked="{Binding IsRedVisible}" />
+                <ToggleButton Classes="pixi-icon" Content="{DynamicResource icon-trash}" IsChecked="{Binding IsRedSolo}" />
+            </StackPanel>
+        </Grid>
+        
+        <Grid ColumnDefinitions="*,Auto">
+            <TextBlock ui:Translator.Key="GREEN" />
+            <StackPanel Grid.Column="1" Orientation="Horizontal">
+                <ToggleButton Classes="pixi-icon" Content="{DynamicResource icon-eye}" IsChecked="{Binding IsGreenVisible}" />
+                <ToggleButton Classes="pixi-icon" Content="{DynamicResource icon-trash}" IsChecked="{Binding IsGreenSolo}" />
+            </StackPanel>
+        </Grid>
+
+        <Grid ColumnDefinitions="*,Auto">
+            <TextBlock ui:Translator.Key="BLUE" />
+            <StackPanel Grid.Column="1" Orientation="Horizontal">
+                <ToggleButton Classes="pixi-icon" Content="{DynamicResource icon-eye}" IsChecked="{Binding IsBlueVisible}" />
+                <ToggleButton Classes="pixi-icon" Content="{DynamicResource icon-trash}" IsChecked="{Binding IsBlueSolo}" />
+            </StackPanel>
+        </Grid>
+
+        <Grid ColumnDefinitions="*,Auto">
+            <TextBlock ui:Translator.Key="ALPHA" />
+            <StackPanel Grid.Column="1" Orientation="Horizontal">
+                <ToggleButton Classes="pixi-icon" Content="{DynamicResource icon-eye}" IsChecked="{Binding IsAlphaVisible}" />
+                <ToggleButton Classes="pixi-icon" Content="{DynamicResource icon-trash}" IsChecked="{Binding IsAlphaSolo}" />
+            </StackPanel>
+        </Grid>
+    </StackPanel>
+</UserControl>

+ 14 - 0
src/PixiEditor.AvaloniaUI/Views/Dock/ChannelsDockView.axaml.cs

@@ -0,0 +1,14 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace PixiEditor.AvaloniaUI.Views.Dock;
+
+public partial class ChannelsDockView : UserControl
+{
+    public ChannelsDockView()
+    {
+        InitializeComponent();
+    }
+}
+

+ 1 - 0
src/PixiEditor.AvaloniaUI/Views/Dock/DocumentTemplate.axaml

@@ -33,6 +33,7 @@
         UseTouchGestures="{Binding StylusSubViewModel.UseTouchGestures, Source={viewModels1:MainVM}}"
         FlipX="{Binding FlipX, Mode=TwoWay}"
         FlipY="{Binding FlipY, Mode=TwoWay}"
+        Channels="{Binding Channels, Mode=TwoWay}"
         ContextRequested="Viewport_OnContextMenuOpening"
         Document="{Binding Document}">
         <viewportControls:Viewport.ContextFlyout>

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

@@ -112,6 +112,7 @@
             ZoomOutOnClick="{Binding ZoomOutOnClick, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
             FlipX="{Binding FlipX, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
             FlipY="{Binding FlipY, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
+            Channels="{Binding Channels, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
             AllOverlays="{Binding ElementName=vpUc, Path=ActiveOverlays}"
             FadeOut="{Binding Source={viewModels:ToolVM ColorPickerToolViewModel}, Path=PickOnlyFromReferenceLayer, Mode=OneWay}"
             DefaultCursor="{Binding Source={viewModels:MainVM}, Path=ToolsSubViewModel.ToolCursor, Mode=OneWay}"

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

@@ -84,6 +84,9 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
     public static readonly StyledProperty<ICommand> MiddleMouseClickedCommandProperty =
         AvaloniaProperty.Register<Viewport, ICommand>(nameof(MiddleMouseClickedCommand), null);
 
+    public static readonly StyledProperty<ViewportColorChannels> ChannelsProperty = AvaloniaProperty.Register<Viewport, ViewportColorChannels>(
+        nameof(Channels));
+
     public ICommand? MiddleMouseClickedCommand
     {
         get => (ICommand?)GetValue(MiddleMouseClickedCommandProperty);
@@ -193,6 +196,12 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         set => SetValue(FlipYProperty, value);
     }
 
+    public ViewportColorChannels Channels
+    {
+        get => GetValue(ChannelsProperty);
+        set => SetValue(ChannelsProperty, value);
+    }
+
     private double angleRadians = 0;
 
     public double AngleRadians

+ 26 - 2
src/PixiEditor.AvaloniaUI/Views/Rendering/Scene.cs

@@ -14,6 +14,7 @@ using ChunkyImageLib;
 using ChunkyImageLib.DataHolders;
 using PixiEditor.AvaloniaUI.Helpers;
 using PixiEditor.AvaloniaUI.Helpers.Converters;
+using PixiEditor.AvaloniaUI.Models.DocumentModels;
 using PixiEditor.AvaloniaUI.ViewModels.Document;
 using PixiEditor.AvaloniaUI.Views.Overlays;
 using PixiEditor.AvaloniaUI.Views.Overlays.Pointers;
@@ -50,6 +51,9 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
     public static readonly StyledProperty<Cursor> DefaultCursorProperty = AvaloniaProperty.Register<Scene, Cursor>(
         nameof(DefaultCursor));
 
+    public static readonly StyledProperty<ViewportColorChannels> ChannelsProperty = AvaloniaProperty.Register<Scene, ViewportColorChannels>(
+        nameof(Channels));
+
     public Cursor DefaultCursor
     {
         get => GetValue(DefaultCursorProperty);
@@ -86,6 +90,12 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
         set => SetValue(SurfaceProperty, value);
     }
 
+    public ViewportColorChannels Channels
+    {
+        get => GetValue(ChannelsProperty);
+        set => SetValue(ChannelsProperty, value);
+    }
+
     private Bitmap? checkerBitmap;
 
     private Overlay? capturedOverlay;
@@ -104,6 +114,12 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
         CheckerImagePathProperty.Changed.AddClassHandler<Scene>(CheckerImagePathChanged);
         AllOverlaysProperty.Changed.AddClassHandler<Scene>(ActiveOverlaysChanged);
         DefaultCursorProperty.Changed.AddClassHandler<Scene>(DefaultCursorChanged);
+        ChannelsProperty.Changed.AddClassHandler<Scene>(ChannelsChanged);
+    }
+
+    private static void ChannelsChanged(Scene scene, AvaloniaPropertyChangedEventArgs args)
+    {
+        scene.InvalidateVisual();
     }
 
     public Scene()
@@ -130,7 +146,8 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
         using var operation = new DrawSceneOperation(Surface, Document, CanvasPos, Scale * resolutionScale, angle, FlipX, FlipY,
             dirtyRect,
             Bounds,
-            sceneOpacity);
+            sceneOpacity,
+            Channels.GetColorMatrix());
 
         var matrix = CalculateTransformMatrix();
         context.PushTransform(matrix);
@@ -454,13 +471,14 @@ internal class DrawSceneOperation : SkiaDrawOperation
     public bool FlipX { get; set; }
     public bool FlipY { get; set; }
     public Rect ViewportBounds { get; }
+    public ColorMatrix ColorMatrix { get; }
 
     public RectI SurfaceRectToRender { get; }
 
     private SKPaint _paint = new SKPaint();
 
     public DrawSceneOperation(Surface surface, DocumentViewModel document, VecD contentPosition, double scale,
-        double angle, bool flipX, bool flipY, Rect dirtyBounds, Rect viewportBounds, double opacity) : base(dirtyBounds)
+        double angle, bool flipX, bool flipY, Rect dirtyBounds, Rect viewportBounds, double opacity, ColorMatrix colorMatrix) : base(dirtyBounds)
     {
         Surface = surface;
         Document = document;
@@ -469,6 +487,7 @@ internal class DrawSceneOperation : SkiaDrawOperation
         Angle = angle;
         FlipX = flipX;
         FlipY = flipY;
+        ColorMatrix = colorMatrix;
         ViewportBounds = viewportBounds;
         _paint.Color = _paint.Color.WithAlpha((byte)(opacity * 255));
         SurfaceRectToRender = FindRectToRender((float)scale);
@@ -489,6 +508,11 @@ internal class DrawSceneOperation : SkiaDrawOperation
         }
         
         using Image snapshot = Surface.DrawingSurface.Snapshot(SurfaceRectToRender);
+
+        var matrixValues = new float[ColorMatrix.Width * ColorMatrix.Height];
+        ColorMatrix.TryGetMembers(matrixValues);
+        
+        _paint.ColorFilter = SKColorFilter.CreateColorMatrix(matrixValues);
         canvas.DrawImage((SKImage)snapshot.Native, SurfaceRectToRender.X, SurfaceRectToRender.Y, _paint);
 
         canvas.Restore();