fabricjs / fabric.js

Javascript Canvas Library, SVG-to-Canvas (& canvas-to-SVG) Parser
http://fabricjs.com
Other
29.03k stars 3.51k forks source link

Pressure support for styluses? #4885

Closed AndrewJDR closed 4 years ago

AndrewJDR commented 6 years ago

Is there currently any support for (or are there any plans to add) pressure / tilt from pens (e.g. Wacom tablet, iPad pencil, etc)? Pressure is typically used to vary the brush width as you are using the brush, so this would be the natural usecase for it. I've seen this done in a number of places (e.g. photoshop and most standalone painting tools), but below are some web-based examples that do it. These use the Pointer Events API, which does provide pressure info, as far as I can see.

https://aggie.io (tested that brush thickness based on pressure works with ipad + pencil) https://github.com/kanreisa/reichat (Microsoft Edge/windows only it seems?) https://github.com/thenickdude/chickenpaint/ (couldn't find working online demo, but code is there) And I'm sure there are others...

There's also https://github.com/stuyam/pressure which is a popular abstraction for getting pressure info, though I'm not sure why it's necessary these days thanks to the Pointer Events API.

asturur commented 6 years ago

So as intention to support it, i bought a ms surface 4 pad, just for that. The time to dive into the topic is missing

AndrewJDR commented 6 years ago

Some day I may be able to take a look (though I'm short on time myself, these days).

One question -- does fabric already have the ability to represent lines/paint brushes that change in width along the length of the stroke? For example, a line may start out at a thinkness of 1.2, then increase to 1.4, then decrease down to 0.9, etc. Putting pressure support aside, can fabric represent this kind of stroke, currently?

asturur commented 6 years ago

no it does not, that would need a special class, derived from path probably.

Just to make it clear, brush are pieces of code ( in fabricJS ) that have a method to handle mouse down/move/up, they can draw custom stuff since they have a render method, but when they finish, the end result must be handled by a fabric.Object derived class.

So other than the pressure code, the brush code, you need the object code. This is a big feature. It could be also be handled with a series of path of different strokeWidths grouped togheter. in that case you would need just a particular brush code.

AndrewJDR commented 6 years ago

Okay, thanks. I better understand what's involved, at least :) I think the quantization would be too obvious looking with the multiple strokes method, so a custom object that represents a smooth interpolation between a set of thickness values would probably be needed. If I do get some time to work on it some day, I will post here.

AndrewJDR commented 6 years ago

For reference - https://web.archive.org/web/20180403121847/http://owl3d.com/svg/vsw/articles/vsw_article.html

arcatdmz commented 5 years ago

After almost a year since this issue was opened, I came up with the same motivation and has just started to implement a simple custom brush and path.

The pull request #5587 is the first step toward the goal, allowing the brush to access pressure values, as well as x and y coordinates through fabric.util.getPointer method.

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

arcatdmz commented 4 years ago

Since I contributed to #5587 and #5589, I have continued my effort to implement a pressure-sensitive brush, and it’s somehow working as intended. I’ve tested it on iPad (Safari and Chrome) and Surface Go (Edge). It’s not yet open-sourced, but you could get the idea below:

https://gs.archinc.jp/drawings/5e4fa91448c4290030d09661

screenshot

AndrewJDR commented 4 years ago

@arcatdmz That's awesome!! Curious, how is it represented in vector terms, a series of separate smaller segments of different widths, or something else?

arcatdmz commented 4 years ago

@AndrewJDR Thanks! Your guess is right, one stroke consists of multiple separate segments. To make it look smooth, a path simplification algorithm is applied to the raw stroke data at the end of each stroke (onMouseUp).

AndrewJDR commented 4 years ago

@arcatdmz That's very cool, do you plan to contribute that back to the fabric repo or somewhere else on github?

asturur commented 4 years ago

Hi @AndrewJDR , as i said before, if you want to contribute it, open a pr in early stage, so that we can start to look at code for integration before is done and maybe your time allotment for it is finished. Looks nice.

AndrewJDR commented 4 years ago

@asturur oh this is not my work, it’s @arcatdmz code... so I was wondering if they plan to open it :)

asturur commented 4 years ago

sorry i confused message order and nicknames

arcatdmz commented 4 years ago

Hi all, thanks for your positive comments. I do wish to open a PR but it would be in March since I'm a bit busy this week.

arcatdmz commented 4 years ago

Oh, by the way, our tech stack is completely based on TypeScript so it wouldn't be straightforward to open a PR on this main repo.

Probably we're going to publish it as an independent npm package as well as a js file hosted on CDN services such as jsDelivr and unpkg.

asturur commented 4 years ago

that is fine by me! We could find a way to link that clearly in examples/readme.

arcatdmz commented 4 years ago

I've noticed v4 is being developed and its beta version has been already released. Regarding the pressure-sensitive brush library, let me make sure that it can be used with v4.

asturur commented 4 years ago

Nothing should have changed in that case, the new item i think it allows you to use pressure events on controls, although i have no idea how you can use them.

arcatdmz commented 4 years ago

I still need to work on documentations, but the basic features are working properly with v3.

arcatdmz commented 4 years ago

@asturur @AndrewJDR I've completed the API document and other publication materials, I think.

@asturur Please let me know if there's anything I could do to include links to any of these in the Fabric.js official documents, as you've said so:

We could find a way to link that clearly in examples/readme.

asturur commented 4 years ago

@arcatdmz if you want to have a look at how i m building demo pages now: https://raw.githubusercontent.com/fabricjs/fabricjs.com/gh-pages/posts/demos/_posts/2020-2-15-custom-control-render.md and how it looks like: http://fabricjs.com/custom-control-render

you can build a demo based on that. As you see the embed prefill from codepen allow you to link external libraries easily and to have an editable demo. You can write a demo page for fabricjs.com, a simple one.

We can also have in the tutorial section how to build a custom brush

g-ravity commented 4 years ago

Hello, I am working on a drawing app similar to Chrome Canvas or the Google Keep Drawing Area. I want the free drawing brush width to be velocity sensitive (thus in a way simulating pressure) ie, when the mouse velocity increases, I want the brush stroke to get thicker. I stumbled on to this issue, and after some reading and research, I overrided the freedrawingBrush methods and broke down the path segments into smaller subsegments with width according to the speed.

But I'm having trouble regarding how to simplify or smooth out the different paths and their widths, so that it all looks like a single smooth curve.

@asturur @AndrewJDR @arcatdmz any suggestions or help?

asturur commented 4 years ago

Well how are you breaking up the segents? are those linear now? or still cubic curves?

arcatdmz commented 4 years ago

I guess my code helps here. If you go with the "smaller sub-segments" approach, I would suggest the following:

  1. When the user is drawing a path, show raw non-simplified path and record relevant data (in your case, probably the timestamp of each mousedown event.)
  2. When drawing finishes, simplify the path

To simplify the path, I rewrote a simplification library in TypeScript and extended it to take pressure values into consideration.

You can check the actual behavior in the demo site: https://arch-inc.github.io/fabricjs-psbrush/

arcatdmz commented 4 years ago

btw I've finally managed to start working on the demo page for fabricjs-psbrush.

https://github.com/arcatdmz/fabricjs.com

jiayihu commented 1 year ago

For anyone looking for a quick and well-made solution, I just pass the array of points (x, y, pressure) to https://github.com/steveruizok/perfect-freehand then render it as SVG path commands. The result is perfect and easy to implement, supporting stylus pressure and falling back to velocity (distance of points) estimation on mouse/touch.

Drawn with MBP trackpad:

Screenshot 2023-09-17 at 18 12 23
asturur commented 1 year ago

should include this integrstion in my brush docs

ozitrance commented 9 months ago

For anyone looking for a quick and well-made solution, I just pass the array of points (x, y, pressure) to https://github.com/steveruizok/perfect-freehand then render it as SVG path commands. The result is perfect and easy to implement, supporting stylus pressure and falling back to velocity (distance of points) estimation on mouse/touch.

Drawn with MBP trackpad: Screenshot 2023-09-17 at 18 12 23

Hey thanks for sharing this!

Do you have a quick example code on how to add it to a fabric canvas?

jiayihu commented 9 months ago

Do you have a quick example code on how to add it to a fabric canvas?

I don't have code that I can share unfortunately, but the rough idea is:

Have a look at PencilBrush, it's already very close to what you need to implement. The main difference is the PencilBrush will transform the points into a smoothed path only at the finalization stage (on mouseup), whereas you probably want to render using the perfect-freeform path both during drawing and on finalization.

ShaMan123 commented 9 months ago

I am for making this supported

ozitrance commented 9 months ago

Do you have a quick example code on how to add it to a fabric canvas?

I don't have code that I can share unfortunately, but the rough idea is:

  • Extend or hook into BaseBrush. You register the points and the pressure on onMouseMove

  • Override _render to create an SVG path from the points, using perfect-freehand

Have a look at PencilBrush, it's already very close to what you need to implement. The main difference is the PencilBrush will transform the points into a smoothed path only at the finalization stage (on mouseup), whereas you probably want to render using the perfect-freeform path both during drawing and on finalization.

Thank you for you help! I will give it a try

crystalthoughts commented 5 months ago

+1 on adding first party support for this feature :)

kasual1 commented 3 months ago

Yep would also appreciate this :-)

mspanish commented 3 months ago

This is really interesting, thanks a lot for sharing. The more I play with it the more I like it. Here is another one, although after playing with both extensively I think the perfect freehand works better across stroke widths.

atrament

Here is my pretty hacky attempt to integrate an override for the PencilBrush with the perfect freehand points. I used the simplify-js tool, and override _render and _finalizeAndAddPath. I'm sure it could be improved, and I don't add pressure to the Mouse event but I don't think it would be very hard. The Codepen is here

I'd be delighted to hear any suggestions.

asturur commented 3 months ago

Probably we could me a demo/tutorial on a custom brush. I absolutely have no pencil, otherwise i could test it. Does the trackpad support pressure by chance?

crystalthoughts commented 3 months ago

A tutorial would be great! I have a wacom so if a tutorial materialized I could test.

I just tried the simplest method I could think of in v5 which didn't work for me:

The whole stroke changes width and it causes bugs for example with eraser, so I guess I'm misunderstanding how everything fits together...

mspanish commented 3 months ago

MIne doesn't use the pressure from the mouse or touch event yet, but I'll work on that in the next couple of weeks. The drawing is beautiful. In the app I'm working on I built a settings control group where you can adjust the same things as the freehand demo - and I've compared them closely, and you can achieve very close to what he has in his demo. Not quite as buttery smooth, but I've tried using my Huion stylus which is high end (desktop), as well as an Android stylus on a Fire Max, as well as my finger and a kid type crayon stylus on my iPhone. I'm sure my implementation could be cleaned up but I'm really delighted with the results.