Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pressure support for styluses? #4885

Closed
AndrewJDR opened this issue Apr 3, 2018 · 38 comments
Closed

Pressure support for styluses? #4885

AndrewJDR opened this issue Apr 3, 2018 · 38 comments
Labels
stale Issue marked as stale by the stale bot

Comments

@AndrewJDR
Copy link
Contributor

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
Copy link
Member

asturur commented Apr 3, 2018

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
Copy link
Contributor Author

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
Copy link
Member

asturur commented Apr 3, 2018

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
Copy link
Contributor Author

AndrewJDR commented Apr 3, 2018

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
Copy link
Contributor Author

@arcatdmz
Copy link
Contributor

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
Copy link

stale bot commented Jan 26, 2020

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.

@stale stale bot added the stale Issue marked as stale by the stale bot label Jan 26, 2020
@stale stale bot closed this as completed Feb 2, 2020
@arcatdmz
Copy link
Contributor

arcatdmz commented Feb 21, 2020

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
Copy link
Contributor Author

@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
Copy link
Contributor

@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
Copy link
Contributor Author

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

@asturur
Copy link
Member

asturur commented Feb 22, 2020

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
Copy link
Contributor Author

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

@asturur
Copy link
Member

asturur commented Feb 22, 2020

sorry i confused message order and nicknames

@arcatdmz
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Member

asturur commented Feb 23, 2020

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

@arcatdmz arcatdmz mentioned this issue Mar 6, 2020
10 tasks
@arcatdmz
Copy link
Contributor

arcatdmz commented Mar 6, 2020

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
Copy link
Member

asturur commented Mar 8, 2020

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
Copy link
Contributor

arcatdmz commented Mar 9, 2020

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

@arcatdmz
Copy link
Contributor

@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
Copy link
Member

asturur commented Mar 29, 2020

@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
Copy link

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
Copy link
Member

asturur commented May 24, 2020

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

@arcatdmz
Copy link
Contributor

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
Copy link
Contributor

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

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

@jiayihu
Copy link
Contributor

jiayihu commented Sep 17, 2023

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
Copy link
Member

asturur commented Sep 18, 2023

should include this integrstion in my brush docs

@ozitrance
Copy link

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
Copy link
Contributor

jiayihu commented Jan 30, 2024

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.

@ShaMan123
Copy link
Contributor

I am for making this supported

@ozitrance
Copy link

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
Copy link

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

@kasual1
Copy link

kasual1 commented Jul 6, 2024

Yep would also appreciate this :-)

@mspanish
Copy link

mspanish commented Jul 16, 2024

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
Copy link
Member

asturur commented Jul 19, 2024

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
Copy link

crystalthoughts commented Jul 20, 2024

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:

  • I attached a pointermove listener to the parent div of the canvas elements ( the pointerevents boolean on Canvas doesn't seem to do anything, and there aren't any listeners like 'pointer:move' for pointer events using Fabric's Canvas.on() . There should be a simpler way of hooking into the standard listeners rather than this custom string I think

  • I set the brush width according to pressure on mouse move.

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

@mspanish
Copy link

mspanish commented Jul 20, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stale Issue marked as stale by the stale bot
Projects
None yet
Development

No branches or pull requests

10 participants