-
-
Notifications
You must be signed in to change notification settings - Fork 99
Building a Tool
In this guide, we will be building a simple, single-pixel drawing tool, to show the process of creating tools.
There are 2 main different types of tools.
The BitmapOperationTool
and ReadonlyTool
.
BitmapOperationTool is a tool, that performs some kind of operation, which yields the result as change on canvas.
An example of such a tool is EarserTool
which "erases" pixels (changes the color to transparent).
It does two main things:
- Processes the input data
- Returns the changed pixels
Readonly tool, as the name suggests, does not return any data, it just processes the input.
An Example is SelectTool
which selects the area on the canvas. It doesn't affect the layers in any way.
We are starting by creating a SinglePixelTool.cs
file in Models/Tools/Tools
folder.
Our tool will draw the pixel, which means we should inherit from BitmapOperationTool
.
By now the script should look like this:
using PixiEditor.Models.DataHolders;
using PixiEditor.Models.Layers;
using PixiEditor.Models.Position;
using System;
using System.Windows.Media;
namespace PixiEditor.Models.Tools.Tools
{
public class SinglePixelTool : BitmapOperationTool
{
public override ToolType ToolType => throw new NotImplementedException();
public override LayerChange[] Use(Layer layer, Coordinates[] mouseMove, Color color)
{
throw new NotImplementedException();
}
}
}
As we can see, we have 2 required abstractions, ToolType
and Use
.
ToolType is an Enum, it is used to select tools easily, this approach might change in the future, but for now, it is that way.
We need to add our SinglePixelTool to the ToolType
. Open ToolType script and append SinglePixelPen
to the end.
The script should look like this:
namespace PixiEditor.Models.Tools
{
public enum ToolType
{
None,
Move,
Pen,
Select,
...
SinglePixel
}
}
Now we can set it in our SinglePixelTool.cs
public override ToolType ToolType => ToolType.SinglePixel;
Now we are ready to write actual tool code.
Use(Layer layer, Coordinates[] mouseMove, Color color)
is a method that is executed for every BitmapOperationTool
. All logic should be placed here.
This argument is a Layer that was selected as active while drawing. It should be used only to read values, not write them directly. Some tools might require a lot of processing power, in this case, you can Set pixels directly, but be aware, that you will need to write Undo logic yourself.
This argument contains an array of mouse positions since mouse click. So latest mouse positon is mouseMove[0]
and start (click) is mouseMove[mouseMove.Length - 1]
, or as I like it more mouseMove[^1]
.
This argument is a color that is used while drawing (Primary Color).
We want to draw a single pixel on a position where the user clicked (is holding mouse). So we need to take the recent mouse position and color and return the change there.
Our code will look like this:
using PixiEditor.Models.DataHolders;
using PixiEditor.Models.Layers;
using PixiEditor.Models.Position;
using System;
using System.Windows.Media;
namespace PixiEditor.Models.Tools.Tools
{
public class SinglePixelTool : BitmapOperationTool
{
public override ToolType ToolType => ToolType.SinglePixel;
public override LayerChange[] Use(Layer layer, Coordinates[] mouseMove, Color color)
{
Coordinates latestPosition = mouseMove[0];
return Only(GetPixelChange(latestPosition, color), layer);
}
public BitmapPixelChanges GetPixelChange(Coordinates mouseClick, Color color)
{
Coordinates[] changedPositions = new[] { mouseClick }; // We need to return array, but changed pixel is only the mouseClick.
return BitmapPixelChanges.FromSingleColoredArray(changedPositions, color);
}
}
}
Only
is a nice wrapper for LayerChange[], if we are only changing a single layer. It looks like this
return new[] { new LayerChange(changes, layerIndex) };
BitmapPixelChanges.FromSingleColoredArray
returns a BitmapPixelChanges that are single-colored. This class is used to store changed pixels.
Our tool is ready, now it's the time to add it to the editor.
Go to ViewModels/ViewModelMain
and in the class constructor, under ToolSet
add a new SinglePixelTool()
.
Now it should look like this:
ToolSet = new ObservableCollection<Tool>
{
new MoveTool(), new PenTool(), new SelectTool(), ... new SinglePixelTool()
};
Awesome, we are almost ready!
Everything is set up when you run the program the tool is will be there, but invisible! You can click under the last tool you can see, and you'll select the SinglePixelPen tool.
Adding a tool image is super easy. The only thing you need to do is to create an image with name format: {ToolTypeName}Image.png
For example PenImage.png
, so image for SinglePixelTool should be named SinglePixelImage.png
, copy it to Images\
and mark it as a resource in properties.
If you still don't see the image, go to Build -> Rebuild solution
Adding a tool requires a bit of work, but it's easy. There are a lot of cool features provided with Tools, I recommend checking out the source code and other tools for reference.
⚠️ PixiEditor wiki has been moved to pixieditor.net⚠️
We are continuously expanding docs, so expect more materials there, and of course, you are more than welcome to contribute!
If you have any questions, don't hesitate to ask us on Discord