hunkim98 / dotting

Dotting is a pixel art editor component library for react
https://hunkim98.github.io/dotting/
MIT License
43 stars 12 forks source link

useBrush to paint icons (SVG/PNG) to grid #113

Open chrisvasey opened 2 months ago

chrisvasey commented 2 months ago

Hello!

I have been testing out this library, and some incredible work has been done on this so far.

I have a use case where I need to be able to paint icons (SVG/PNG) to the grid rather than just colours; the outcome will look something like this: CleanShot 2024-07-23 at 14 09 40@2x Can this be achieved using the existing brush API?

If not, I would happily add this if you would welcome my PR.

hunkim98 commented 2 months ago

Thank you for your interest in this library. PRs are always welcome! Recently I have been refactoring the whole codebase to make it easier for other developers to easily understand the library and contribute to the project.

It seems that your desired feature is not impossible to achieve in the current library, but quite difficult to achieve. If you are interested in contributing yourself, I could help guide you achieve it.

First, I think there needs to be a new BrushTool to integrate your desired feature. The BrushTool is defined in src/components/Canvas/types

export enum BrushTool {
  DOT = "DOT",
  ERASER = "ERASER",
  PAINT_BUCKET = "PAINT_BUCKET",
  SELECT = "SELECT",
  NONE = "NONE",
}

Maybe something BrushTool.Icon would be great

Secondly, if you would like to define the action that should happen for the BrushTool.Icon, you might have to tweak the onMouseDown and onMouseMove function defined in src/components/Canvas/Editor. The brush tool drawing mode will be activated when this.mouseMode is MouseMode.DRAWING. The real drawing happens in the drawPixelInInteractionLayer, so you can refer to that function to create a custom drawing function or add the code to the existing function.

To first contributors, the concept of interaction layer and data layer might be quite confusing, so please allow me to give you a brief explanation on how Dotting works. Basically Dotting has 3 internal layers, which are grid layer, interaction layer, data layer. The grid layer is at the top, the interaction layer in the middle, and the data layer at the bottom.

While the user presses the mouse down (or touches) in the pixel grid, the user's trace is drawn in the interaction layer for optimization purposes. After the user finishes interacting (mouse is up), then the trace drawn in the interaction layer is passed down to the data layer and rendered with all other pixel drawn data. The interaction layer is cleared once all the user's interaction data is transferred to the data layer.

This is why there is a drawPixelInInteractionLayer function that does not draw in the data layer but in the interaction layer. If your requested feature is implemented, the icons (svg/png) will first be drawn in the interaction layer while the user's mouse is dragged, and then the icons should be drawn in the data layer. The magic of the interaction data being transferred to the data layer can be discovered in the relayInteractionDataToDataLayer function in the Editor.tsx file.

How the data layer renders the recorded data by the user can be understood through checking the render method defined in the Datalayer.tsx. Since your requested feature requires svg/png data. Maybe a different or creative method might be necessary in the render method in both the Datalayer.tsx and InteractionLayer.tsx

I think I have covered most of the part you might need to know when implementing the feature! Feel free to ask me any questions if you are struggling. I know that the codebase is quite complicated to understand and I am trying my best to improve the readability of the code for future maintenance. If your feature is not an urgent one, I think it would be great if you could wait a little bit and then try implementing the feature after I refactor the whole code! I am currently designing a flux pattern for Dotting and I believe it might be easier for you to understand and modify the code after the change. I assume the whole migration to the new pattern will be done by mid-August. If you are in need of the feature now, you are free to open a PR based on the current version!

chrisvasey commented 2 months ago

Thank you so much for your detailed response!

After reading your notes on implementing this, I would be more than happy to have a go at it myself. The way you have written the library seems intuitive enough.

I'm happy to branch off whenever you are up to with your rewrite if it isn't too disruptive for you to merge.

I plan to implement this feature for my application next month, so I will check in again before starting work.

All the best

chrisvasey commented 2 months ago

Hello!

I have some time next week to look at this. How has your rewrite gone?

I am happy to start with wherever the codebase is if it won't be too disruptive to merge.

hunkim98 commented 2 months ago

Sorry for the late reply. I was busy taking care of my personal work.

Currently I am not actively rewriting the code so please feel free to start with the current main branch as you will!