I'm trying to replicate a potter's wheel effect, where a user can click on a piece of the wheel, hold down the mouse, and a circle will be created with respect to the center of the wheel.
Like in this persons demo: https://balazsdavid987.github.io/Pottery-Wheel/
But what's happening for me can be seen here:
http://p2-paperjs-dpayne5-dpayne589733.codeanyapp.com:3000/coloring/
The relevant pieces of code are the following:
var tool = new paper.Tool();
//array to hold all curves drawn from mousedrags
var allPaths = [];
var currPath;
var rotationPath;
//wheel center point, #center of canvas
var wheelCenter = new paper.Point(350,350);
//create the potters wheel
var wheel = new paper.Path.Circle({
center: wheelCenter,
radius: 300,
strokeColor: 'black',
strokeWidth: 5
});
//hold down to create a continous curve with respect to wheelCenter
tool.onMouseDown = function(event) {
currPath = new paper.Path();
currPath.strokeColor = cp.history[cp.history.length-1];
currPath.strokeWidth = 10;
currPath.add(event.point);
}
//creates a curve from the last position to the new position of mouse
tool.onMouseDrag = function(event) {
currPath.add(currPath.rotate(4, wheelCenter));
}
//add the curve to allPaths, which then gets animated in onFrame
tool.onMouseUp = function(event) {
allPaths.push(currPath);
}
paper.view.onFrame = function(event) {
for (var i = 0; i < allPaths.length; i++) {
allPaths[i].rotate(4, wheelCenter);
}
//testPath.rotate(3, wheelCenter);
}
paper.view.draw();
I'm not understanding why the mouseDrag would make a circle way father out from where my mouse has clicked, and I've been stuck on this for awhile.
Any help would be greatly appreciated, thanks!
Apart from your technical difficulty with the onMouseDrag method, I think that you should change your approach to the problem.
The thing is that if you rely on mouse drag event (which is only triggered when the mouse move), you won't be able to paint on the wheel by keeping your mouse static (as shown in your reference demo).
So you would better keep track of the mouse position (by listening to a mouse move event), and draw on each frame, adding the last mouse position to the current path (only when drawing of course).
Better than a thousand words, here is a sketch demonstrating how this can be achieved.
// Create the wheel.
const wheel = new Path.Circle({
center: view.center,
radius: 300,
strokeColor: 'black',
strokeWidth: 3
});
// Create a group that will contain all the user drawn path.
// This will allow us to more easily rotate them together.
const paths = new Group();
// Init state variables.
let currentPath;
let drawing = false;
let lastMousePosition;
// On mouse down...
function onMouseDown(event) {
// ...start a new path.
currentPath = new Path({
segments: [event.point],
strokeColor: 'red',
strokeWidth: 2
});
// Add it to the paths group.
paths.addChild(currentPath);
// Mark state as drawing.
drawing = true;
}
// On mouse move...
function onMouseMove(event) {
// ...keep track of the mouse position, this will be used to add points to
// the current path on each frame.
lastMousePosition = event.point;
}
// On mouse up...
function onMouseUp(event) {
// ...improve performances by simplifying the path.
currentPath.simplify();
// Mark state as not drawing.
drawing = false;
}
// On each frame...
function onFrame(event) {
// ...rotate paths around the wheel center.
paths.rotate(4, wheel.position);
// If we are currently drawing...
if (drawing) {
// ...add the last mouse position to the current path.
currentPath.add(lastMousePosition);
}
}
Related
I'm using a camera and barcode markers. In order for my cannon to shoot I need to spawn a sphere when a clicked. I tried to get the marker position by using .getAttribute('position') but I'm getting disappointing results e.g. null and [object, object]. Is there a real way to to access the coordinates of a marker in AFrame? So far it creates a sphere but right in the camera because its unable to find the location of the marker.
Javascript
var sceneEl = document.querySelector('a-scene'); //select scene
var markerEl = document.querySelector('#cannonMarker');
// trigger event when button is pressed.
document.addEventListener("click", function (e) {
var pos = markerEl.getAttribute('position');
var Sphere = document.createElement('a-sphere');
Sphere.setAttribute('dynamic-body', {
shape: 'sphere',
mass: 4
});
Sphere.setAttribute('position', pos);
sceneEl.appendChild(Sphere);
console.log('Sphere coordinates:' + pos);
});
Provided the marker is being recognized and the references are correct
markerEl.getAttribute('position')
should return the current marker position.
If your script is in the <head> element, the marker may not yet exist when the code is executed.
It's a good idea to throw your code into an a-frame component:
HTML:
<a-scene ball-spawner></a-scene>
js:
AFRAME.registerComponent('ball-spawner', {
init: function() {
// your code here - the scene should be ready
}
})
I've made a slight modification to your code (working glitch here):
var sceneEl = document.querySelector('a-scene'); //select scene
var markerEl = document.querySelector('a-marker');
// trigger event when button is pressed.
sceneEl.addEventListener("click", function (e) {
if (markerEl.object3D.visible === false) {
return;
}
var pos = markerEl.getAttribute('position');
var Sphere = document.createElement('a-sphere');
Sphere.setAttribute('radius', 0.5)
Sphere.setAttribute('dynamic-body', {
shape: 'sphere',
mass: 4
});
Sphere.setAttribute('position', pos);
sceneEl.appendChild(Sphere);
});
}
When the marker gets out of vision, it's position is 'last remembered'. So it's the same place on the screen. That's why there's a return if the marker element is not visible.
It seems to be working as you want since the ball is falling out of a transparent box on the marker (the box is a nice way to be sure the marker is recognized):
I'm pretty much new to canvas. What I'm trying to make is that I can write text in canvas using input and can be able to resize it by dragging it's corners. Also I should be able to drag text position within the canvas.
Following is the screen shot of what I want!
Canvas is raster, not vector. By simply drawing and resizing text you would expect it to get blurry or pixelated. And redrawing the whole canvas each time user moves the cursor while resizing will not result in the best performance. Consider using svg instead. In case you do need canvas and don't want to implement all the functions yourself, you can use the paperjs library.
http://paperjs.org/reference/pointtext/
As #hirasawa-yui mentioned, you can use Paper.js to greatly facilitate the implementation of what you want in a canvas.
Here is a simplified sketch showing a possible implementation of dragging/resizing interactions.
// create item
var item = new PointText({
content: 'Custom text content',
point: view.center,
justification: 'center',
fontSize: 30,
selected: true
});
// init variables so they can be shared by event handlers
var resizeVector;
var moving;
// on mouse down...
function onMouseDown(event) {
// ...do a hit test on item bounds with a small tolerance for better UX
var cornerHit = item.hitTest(event.point, {
bounds: true,
tolerance: 5
});
// if a hit is detected on one of the corners...
if (cornerHit && ['top-left', 'top-right', 'bottom-left', 'bottom-right'].indexOf(cornerHit.name) >= 0) {
// ...store current vector from item center to point
resizeVector = event.point - item.bounds.center;
// ...else if hit is detected inside item...
} else if (item.hitTest(event.point, { fill: true })) {
// ...store moving state
moving = true;
}
}
// on mouse drag...
function onMouseDrag(event) {
// ...if a corner was previously hit...
if (resizeVector) {
// ...calculate new vector from item center to point
var newVector = event.point - item.bounds.center;
// scale item so current mouse position is corner position
item.scale(newVector / resizeVector);
// store vector for next event
resizeVector = newVector;
// ...if item fill was previously hit...
} else {
// ...move item
item.position += event.delta;
}
}
// on mouse up...
function onMouseUp(event) {
// ... reset state
resizeVector = null;
moving = null;
}
// draw instructions
new PointText({
content: 'Drag rectangle to move, drag corners to resize.',
point: view.center + [0, -50],
justification: 'center'
});
I'm working in a Paper.js project where we're essentially doing image editing. There is one large Raster. I'm attempting to use the getSubRaster method to copy a section of the image (raster) that the user can then move around.
After the raster to edit is loaded, selectArea is called to register these listeners:
var selectArea = function() {
if(paper.project != null) {
var startDragPoint;
paper.project.layers[0].on('mousedown', function(event) { // TODO should be layer 0 in long run? // Capture start of drag selection
if(event.event.ctrlKey && event.event.altKey) {
startDragPoint = new paper.Point(event.point.x + imageWidth/2, (event.point.y + imageHeight/2));
//topLeftPointOfSelectionRectangleCanvasCoordinates = new paper.Point(event.point.x, event.point.y);
}
});
paper.project.layers[0].on('mouseup', function(event) { // TODO should be layer 0 in long run? // Capture end of drag selection
if(event.event.ctrlKey && event.event.altKey) {
var endDragPoint = new paper.Point(event.point.x + imageWidth/2, event.point.y + imageHeight/2);
// Don't know which corner user started dragging from, aggregate the data we have into the leftmost and topmost points for constructing a rectangle
var leftmostX;
if(startDragPoint.x < endDragPoint.x) {
leftmostX = startDragPoint.x;
} else {
leftmostX = endDragPoint.x;
}
var width = Math.abs(startDragPoint.x - endDragPoint.x);
var topmostY;
if(startDragPoint.y < endDragPoint.y) {
topmostY = startDragPoint.y;
} else {
topmostY = endDragPoint.y;
}
var height = Math.abs(startDragPoint.y - endDragPoint.y);
var boundingRectangle = new paper.Rectangle(leftmostX, topmostY, width, height);
console.log(boundingRectangle);
console.log(paper.view.center);
var selectedArea = raster.getSubRaster(boundingRectangle);
var selectedAreaAsDataUrl = selectedArea.toDataURL();
var subImage = new Image(width, height);
subImage.src = selectedAreaAsDataUrl;
subImage.onload = function(event) {
var subRaster = new paper.Raster(subImage);
// Make movable
subRaster.onMouseEnter = movableEvents.showSelected;
subRaster.onMouseDrag = movableEvents.dragItem;
subRaster.onMouseLeave = movableEvents.hideSelected;
};
}
});
}
};
The methods are triggered at the right time and the selection box seems to be the right size. It does indeed render a new raster for me that I can move around, but the contents of the raster are not what I selected. They are close to what I selected but not what I selected. Selecting different areas does not seem to yield different results. The content of the generated subraster always seems to be down and to the right of the actual selection.
Note that as I build the points for the bounding selection rectangle I do some translations. This is because of differences in coordinate systems. The coordinate system where I've drawn the rectangle selection has (0,0) in the center of the image and x increases rightward and y increases downward. But for getSubRaster, we are required to provide the pixel coordinates, per the documentation, which start at (0,0) at the top left of the image and increase going rightward and downward. Consequently, as I build the points, I translate the points to the raster/pixel coordinates by adding imageWidth/2 and imageHeight/2`.
So why does this code select the wrong area? Thanks in advance.
EDIT:
Unfortunately I can't share the image I'm working with because it is sensitive company data. But here is some metadata:
Image Width: 4250 pixels
Image Height: 5500 pixels
Canvas Width: 591 pixels
Canvas Height: 766 pixels
My canvas size varies by the size of the browser window, but those are the parameters I've been testing in. I don't think the canvas dimensions are particularly relevant because I'm doing everything in terms of image pixels. When I capture the event.point.x and event.point.y to the best of my knowledge these are image scaled coordinates, but from a different origin - the center rather than the top left. Unfortunately I can't find any documentation on this. Observe how the coordinates work in this sketch.
I've also been working on a sketch to illustrate the problem of this question. To use it, hold Ctrl + Alt and drag a box on the image. This should trigger some logging data and attempt to get a subraster, but I get an operation insecure error, which I think is because of security settings in the image request header. Using the base 64 string instead of the URL doesn't give the security error, but doesn't do anything. Using that string in the sketch produces a super long URL I can't paste here. But to get that you can download the image (or any image) and convert it here, and put that as the img.src.
The problem is that the mouse events all return points relative to 0, 0 of the canvas. And getSubRaster expects the coordinates to be relative to the 0, 0 of the raster item it is extracting from.
The adjustment needs to be eventpoint - raster.bounds.topLeft. It doesn't really have anything to do with the image width or height. You want to adjust the event points so they are relative to 0, 0 of the raster, and 0, 0 is raster.bounds.topLeft.
When you adjust the event points by 1/2 the image size that causes event points to be offset incorrectly. For the Mona Lisa example, the raster size (image size) is w: 320, h: 491; divided by two they are w: 160, h: 245.5. But bounds.topLeft of the image (when I ran my sketch) was x: 252.5, y: 155.5.
Here's a sketch that shows it working. I've added a little red square highlighting the selected area just to make it easier to compare when it's done. I also didn't include the toDataURL logic as that creates the security issues you mentioned.
Here you go: Sketch
Here's code I put into an HTML file; I noticed that the sketch I put together links to a previous version of the code that doesn't completely work.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Rasters</title>
<script src="./vendor/jquery-2.1.3.js"></script>
<script src="./vendor/paper-0.9.25.js"></script>
</head>
<body>
<main>
<h3>Raster Bug</h3>
<div>
<canvas id="canvas"></canvas>
</div>
<div id="position">
</div>
</main>
<script>
// initialization code
$(function() {
// setup paper
$("#canvas").attr({width: 600, height: 600});
var canvas = document.getElementById("canvas");
paper.setup(canvas);
// show a border to make range of canvas clear
var border = new paper.Path.Rectangle({
rectangle: {point: [0, 0], size: paper.view.size},
strokeColor: 'black',
strokeWidth: 2
});
var tool = new paper.Tool();
// setup mouse position tracking
tool.on('mousemove', function(e) {
$("#position").text("mouse: " + e.point);
});
// load the image from a dataURL to avoid CORS issues
var raster = new paper.Raster(dataURL);
raster.position = paper.view.center;
var lt = raster.bounds.topLeft;
var startDrag, endDrag;
console.log('rb', raster.bounds);
console.log('lt', lt);
// setup mouse handling
tool.on('mousedown', function(e) {
startDrag = new paper.Point(e.point);
console.log('sd', startDrag);
});
tool.on('mousedrag', function(e) {
var show = new paper.Path.Rectangle({
from: startDrag,
to: e.point,
strokeColor: 'red',
strokeWidth: 1
});
show.removeOn({
drag: true,
up: true
});
});
tool.on('mouseup', function(e) {
endDrag = new paper.Point(e.point);
console.log('ed', endDrag);
var bounds = new paper.Rectangle({
from: startDrag.subtract(lt),
to: endDrag.subtract(lt)
});
console.log('bounds', bounds);
var sub = raster.getSubRaster(bounds);
sub.bringToFront();
var subData = sub.toDataURL();
sub.remove();
var subRaster = new paper.Raster(subData);
subRaster.position = paper.view.center;
});
});
var dataURL = ; // insert data or real URL here
</script>
</body>
</html>
in a map I want when the user clicks inside the map to be able to draw with his mouse a scheme. For example it clicks and then circles an area just by moving his mouse. The problem is that when he circles the area, continues to draw at the current mouse position.
In other words I want the user to click for start drawing and click again to stop it.
I am using addEventListener and removeEventListener
I am trying to remove the events from the map but it does not work.
Any ideas?
Code:
map.addEventListener('click', goInter, false);
function goInter() {
map.addEventListener('mousemove', move, false);
map.addEventListener('click', close, false);
var pointList = [];
var first_element;
var trig = 0;
function move(e) {
map.dragging.disable();
pointList[pointList.length] = new L.LatLng(e.latlng.lat, e.latlng.lng);
first_element = pointList[0];
//ama to valw den doulebei tipota!!
//document.getElementById("elements").innerHTML = pointList[ii++];
if (pointList.length > 400){
pointList = [];
}
var line = new L.Polyline(pointList, {
color: 'red',
weight: 2,
opacity: 0.5,
});
map.addLayer(line);
if (trig == 1){
map.preventDefault();
alert("den einai 1");
return false;
}
};
function close(){
//map.removeEventListener ('mousemove', move, false);
var last_points = [first_element, pointList[pointList.length-1]];
var last_connection = new L.Polyline(last_points, {
color: 'blue',
weight: 1,
opacity: 0.9
});
map.addLayer(last_connection);
trig = 1;
map.preventDefault();
//map.removeLayer(line);
//map.fitBounds(line.getBounds());
};
};
I have a progress -> i changed the two click events to -> mouseup and mousedown respectively. That gives me a better interaction. when the user releases the button it stops drawing. However if you press it again it draws. ALSO uncomment first line of close() function
everyone.
It is possible in paper.js change layer start coordinate position from top-left corner?
Use case: I put image into bottom layer and draw some stuff on top layer. Image can be scalled and moved. I need to get my drawing path points coord in image coordinate system. And I want to set my drawing layer coordinate start to image top left point and move it then image scale/move.
I try do this:
var layer = new Layer();
project.activeLayer.setName("DrawingStuff");
project.activeLayer.setPosition(paperjs.project.view.center);
project.activeLayer.bounds.x = 300;
project.activeLayer.bounds.y = 300;
project.activeLayer.bounds.width = raster.width;
project.activeLayer.bounds.height = raster.height;
But it is don't work. Name is setted, but bounds and position still empty.
Will be very thankfull for any advise.
P.S. I know that I can just recalculate path point from canvas coords system to image coords system, but I want to try change layer coords start point
It would be easier to use the current coordinate system and change the zoom and center properties of the project's view. Take a look at the code behind the zoom tool in main.js at http://sketch.paperjs.org:
var lastPoint;
var body = $('body');
zoomTool = new Tool({
buttonClass: 'icon-zoom'
}).on({
mousedown: function(event) {
if (event.modifiers.space) {
lastPoint = paper.view.projectToView(event.point);
return;
}
var factor = 1.25;
if (event.modifiers.option)
factor = 1 / factor;
paper.view.zoom *= factor;
paper.view.center = event.point;
},
...
mousedrag: function(event) {
if (event.modifiers.space) {
body.addClass('zoom-grab');
// In order to have coordinate changes not mess up the
// dragging, we need to convert coordinates to view space,
// and then back to project space after the view space has
// changed.
var point = paper.view.projectToView(event.point),
last = paper.view.viewToProject(lastPoint);
paper.view.scrollBy(last.subtract(event.point));
lastPoint = point;
}
},
...
});