How to implement sketch brush, like in deviantART muro? - javascript

deviantART muro has a set of brilliant tools of painting. And I'm very curious how to implement these brushes like Sketch and Paintbrush, arithmetically?
Using any normal programming language to explain is okay, though I prefer C++ or JavaScript. I think it's better than read their JS source code.

I'd say it works something like:
Track mouse movement
On captured mouse movement, draw your desired brush from saved "Old mouse position" to captured "New mouse position", iterating at a pixel's distance at a time
If you move the mouse too fast for the script to capture, it will just look like a computed long straight line (which is what it looks like Muro is doing). If you want to get real fancy you can calculate the trajectory from previous mouse positions and draw that instead for a "smoother" line.
Since you specified Javascript you'd probably want to draw it in a canvas object.
EDIT 1:
Sketch specifically seems to save mouse movements and then loop through, say the 20 latest mouse movements for each mouse movement and draw a bezier curve from that point to the current point.
So, something like (pseudo code)
Object mousemovements = [];
on.mousemove(event)
{
if (mousemovements.length > 20)
{
mousemovements.removeLast();
}
mousemovements.insertAtBeginning([ event.mouseX, event.mouseY ]);
for-each (movement in mousemovements)
{
drawBeziercurveFromTo(movement.mouseX, movement.mouseY,
event.mouseX, event.mouseY);
}
}
Jquery/Canvas DEMO based on the above pseudo code
EDIT 2:
I had a closer look at how "Sketch" worked and it seems that they update the mouse pointer positions, moving the older points closer to the newer points. Something like this:
This DEMO works pretty much like the sketch brush

Related

How can I replace my cursor with a circle instead of drawing it to canvas in p5.js?

The problem: I'm trying to create a simple drawing app using p5.js. Instead of the standard cursor image, I'd like to show a circle at my cursor location that represents the size of the drawing brush.
Potential solution 1: Replace the cursor using the cursor() function native to p5.
Why it doesn't work: The p5 cursor function only takes the following parameters:
ARROW, CROSS, HAND, MOVE, TEXT, or WAIT, or path for image
As such, there's no native way to replace the cursor using the ellipse class.
Potential solution 2: Use the noCursor() function and then draw the circle at the cursor location, while also drawing the background, as such:
var brushSize = 50;
function setup() {
createCanvas(1080,720);
noCursor();
}
function draw() {
background(100);
ellipse(mouseX,mouseY,brushSize);
}
Why it doesn't work: While this solution gets the desired effect i.e. replacing the cursor with a circle the size of the brush, the constantly updating background prevents me from actually drawing to the canvas with the brush when I want to.
Is there some way I can replace the cursor without actually drawing the ellipse to the canvas? Is there any way to save and then instantly reload a canvas in p5? I couldn't find such a method searching through the API docs. Any hints are appreciated.
According to the reference, you can pass a URL into the cursor() function to set an image.
If you want to use an image that you draw, you're going to have to draw them ahead of time and save them to files, and then use those files. Something like this:
cursor('images/ellipse-15.png');
Where ellipse-15.png is an image that you generated ahead of time, to match when brushSize is 15, for example.
Btw P5.js is just setting the cursor CSS property. You can read more about it here.
If you want to go with the noCursor() approach and draw the ellipse yourself, you could draw your drawing to a buffer (the createGraphics() function is your friend) and then draw the ellipse on top of that every frame. I'd still probably use a cross cursor just because there's going to be some annoying lag if you draw it yourself.
Create a circular DIV inside the canvas container and show it on top of the actual canvas.

Get started with animated typography/particles in javascript (mapping particles to a word)?

Alright, so I have a good deal of experience with HTML and CSS, and some experience with Javascript (I can write basic functions and have coded in similar languages).
I'm looking to start some visual projects and am specifically interested in getting into particle systems. I have an idea for something similar to Codecademy's name generator here (https://www.codecademy.com/courses/animate-your-name/0/1) where particles are mapped to a word and move if hovered over. It seems as though alphabet.js is what's really behind Codecademy's demo however I can't understand exactly how they mapped the particles to a word, etc.
I've done some basic tutorials just creating rudimentary particles in a canvas but I'm not sure a canvas is the best way to go - demos that utilize one of the many libraries available (such as http://soulwire.github.io/sketch.js/examples/particles.html) don't use a canvas.
So my question is - what is the best way for a beginner/intermediate in Javascript to start with particle systems? Specifically to accomplish the Codecademy name effect or similar? Should I try to use canvas or which library would be best to start with and how would you recommend starting?
The code for this project is achievable for your intermediate JS programmer status.
How the CodeAcademy project works ...
Start by building each letter out of circles and saving each circle's centerpoint in an array. The alphabet.js script holds that array of circle centerpoints.
On mousemove events, test which circles are within a specified radius of the mouse position. Then animate each of those discovered circles radially outward from the mouse position using simple trigonometry.
When the mouse moves again, test which circles are no longer within the specified radius of the current mouse position. Then animate each of those "outside" circles back towards their original positions.
You can also use native html5 canvas without any libraries...
Another approach allowing any text to be "dissolved" and reassembled
Start by drawing the text on the canvas. BTW, this approach will "dissolve" any drawing, not just text.
Use context.getImageData to fetch the opacity value of every pixel on the canvas. Determine which pixels on the canvas contain parts of the text. You can tell if a pixel is part of the text because it will be opaque rather than transparent.
Now do the same procedure that CodeAcademy did with their circles -- but use your pixels:
On mousemove events, test which pixels are within a specified radius of the mouse position. Then animate each of those discovered pixels radially outward from the mouse position using simple trigonometry.
When the mouse moves again, test which pixels are no longer within the specified radius of the current mouse position. Then animate each of those "outside" pixels back towards their original positions.
[Addition: mousemove event to test if circles are within mouse distance]
Note: You probably want to keep an animation frame running that moves circles closer or further from their original positions based on a flag (isInside) for each circle.
function handleMouseMove(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calc the current mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// test each circle to see if it's inside or outside
// radius of 40px to current mouse position
// circles[] is an array of circle objects shaped like this
// {x:,y:,r:,originalX:,originalY:,isInside:}
var radius=40;
for(var i=0;i<circles.length;i++){
var c=circles[i];
var dx=c.x-mouseX;
var dy=c.y-mouseY;
if(dx*dx+dy*dy<radius*radius){
c.isInside=true;
// move c.x & c.y away from its originalX & originalY
}else{
c.isInside=false;
// if the circle is not already back at it's originalX, originalY
// then move c.x & c.y back towards its originalX, originalY
}
}
}

Drag and mouseover in snap.svg

I'm relatively new to javascript, and am learning about drag and drop using snap.svg. My problem is in the drop. I can't tell if the dragged element is over the drop target. In this code, I want to drag the circle over the square, and thought I could use mouseover. My (distilled) example may also be a simpler version of this post.
var paper = Snap(300, 300);
var square = paper.rect(100, 100, 40, 40).attr({fill:"blue"});
var circle = paper.circle(50, 50, 20).attr({fill:"red"});
circle.drag();
square.mouseover(
function() {
console.log("Over the square");
}
);
As written, the mouseover will fire when you move the pointer over the blue square, but not when you drag the red circle over the blue square. If you reverse the creation of the square and circle, the mouseover fires either way, but of course the circle is behind the square.
Evidently the event gets caught in the view hierarchy (or something) and doesn't propagate. There must be an easy way around this. Any help?
(And if the best answer is, "use jQuery," fine, but I'd love to learn how to make this work directly, since snap.svg makes dragging so easy.)
Addition: The direction I'm hoping for: the snap.svg documentation for Element.drag() says, in part, "When Element is dragged over another element, drag.over.<id> fires as well." A fine, event-based direction, which would let me (for example) highlight the drop target without a lot of fuss.
But I haven't figured out how to listen for that event! Any help or advice?
Only quick way without collision or element detection from points that I can think of, is to place an almost invisible clone in front of the object, later in the DOM that you can't really see, eg ...
paper.append( square.clone().attr({ opacity: 0.000001 }) )
jsfiddle
Depends how complex your svgs are going to be as to whether this would work I guess, you also have a slight issue if you drop the element over it, your redrag start won't get picked up, so you would need to code around that as well. I think some testing is probably going to be the most bug free solution (there are a few solutions on S.O for getElementFromPoint or hit detection type solutions).
jsfiddle workaround for point above

Making Javascript TileEngine Scrollable in Canvas

The basis for the code is from John E. Graham's blog http://johnegraham2.com/blog/2010/09/25/project-javascript-2d-tile-engine-with-html5-canvas-part-4-using-zones-for-further-optimization/
It works perfectly for drawing a screen's worth of tiles, but I cannot for the life of me figure out how to adjust it 1 row/column at a time based on pressing up, down, left, or right keys.
Here is an example with the transparency to help visualize the zones http://simplehotkey.com/Javascript/canvas.html (loading positions of 1,188 tiles but only draws a couple hundred to fill the screen) I had it loading an array with 70,000 entries and it was still quick because it's only drawing whats on the screen, but cannot figure out how to slide everything based on input...
I've come up with a couple ideas and am not sure what's the best way.
One screen worth of tiles is shown here:
tilesArray = [
0,0,0,0,0,0,0,1,2,9,6,0,0,7,0,0,1,0,
0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0,
0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0,9,0,
0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0,9,0,
0,9,9,9,9,9,9,0,7,2,0,0,0,0,0,1,2,0,
0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0,
0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0,
0,9,0,7,2,9,9,9,9,9,9,9,9,9,9,9,9,0,
0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0,
0,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
];
Where 0 is a wall tile (around perimeter), 9 a floor tile, 7 a door and a couple other random tiles.
That is exactly what is loaded to the screen, but I cannot figure out how to shift everything 1 tile in either direction based on input, up, down, left, right.
The one idea I'm leaning towards now, is just to use that array above as the basis for rendering, and somehow feeding the new values into it based on keyboard input, from another array. Maybe slicing from another, much larger array (holding all the tiles for the entire level) and using that slice to populate the array that's actually rendered???
That's replacing every tile every frame though...
for getting player input I was using:
//Key listener
document.onkeydown = function(e){
e = e?e:window.event;
console.log(e.keyCode + "Down");
switch (e.keyCode){
case 38:
//UP KEY
Game.inputReaction('up');
//Game.moveDir('up');
break;
case 40:
//DOWN KEY
//Game.inputReaction(40);
//Game.moveDir('down');
break;
case 37:
//Left Key
//Game.inputReaction(37);
break;
}
}
The other alternative is to try to adjust the tiles already on the screen and add new tiles but this engine isn't using global variables so I'm not sure how to affect the tile engine programatically based on input....like I can add another method (inputReaction(num)) and trigger some actions from my keyboard input (console.log()) but I can't access the other methods actually drawing the tiles. Or maybe I have to make a copy of the object, change it and return it? but it's pretty complex.
I think it might be easier to adjust the array values that are being fed into the "engine" (array above) rather than changing around how the engine is calculating what's being drawn. Can you confirm this?
Add a camera abstraction that you can move around on the map, then shift the drawing positions according to the camera position. When the camera moves south 10px, all tiles move north 10px, same with east and west. Since you only draw the tiles that are visible, there won't be much of a performance loss.
The renderer looks at the camera to figure out what needs to be drawn and you can expose the camera object to the outside to manipulate it. That way you only need to change the camera position to change what is shown on the screen.
I did this in a proof of concept tiling engine a year ago and I was able to smoothly scroll and scale huge tilemaps.
If you start changing the array itself, your performance will suffer and you won't be able to scroll smoothly since you can only go in steps of one tile and not one pixel.

Get Line co-ordinates in Javascript

I am drawing lines using Canvas (HTML 5), since lines/shapes are not stored as objects in Canvas, I cannot attach unique events to it (eg onmouseclick)
I wish to attach a onmouseover event to a line, is it possible by getting to know if the mouse if over a particular line (using its 2 X and 2 Y co-ordinates) in Canvas using Javascript. Would this work for different line widths (eg: 2,5 pixels)
Want to avoid using SVG as the entire project is built on Canvas
Please advise.
You would need to use math formulas to calculate the area of the line and whether a certain point intersects with it.
Here's a basic example:
Find mouse coordinates relative to position of the canvas (How to find mouse pos on element)
Calculate whether mouse x/y is inside some rectangle (Point in rectangle formula)
Done.
There is a function isPointInPath(x,y). It will return true if a point is on the current path.
You will have to call that for every line you want to check and the best way to do that is at the same time as you draw.
The best way is using some canvas frameworks. Look at "LibCanvas :: Creating Lines" (dont forget to dblClick at canvas)

Categories