Paper.js change layer coordinate start point - javascript

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;
}
},
...
});

Related

PaperJS trouble creating circle on mouseDown

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);
}
}

How to make resizable Text on canvas using javascript

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'
});

Get rectangle width and height leaflet jquery

I have a map and an area select field:
// initialize map
var map = L.map('map').setView([38, 0], 2);
L.tileLayer('http://{s}.tile.cloudmade.com/70146506bc514228adc1756788c730d3/997/256/{z}/{x}/{y}.png', {attribution: 'Map data © OpenStreetMap contributors, Imagery © CloudMade', maxZoom: 18}).addTo(map);
var areaSelect = L.areaSelect({
width:100,
height:150,
keepAspectRatio:true
});
areaSelect.addTo(map);
areaSelect.setDimensions({width: 50, height: 50});
want to get the rectangle width and height.
var neLatCoord = $('#ne-lat-coordinate').val();
var neLonCoord = $('#ne-lon-coordinate').val();
var swLatCoord = $('#sw-lat-coordinate').val();
var swLonCoord = $('#sw-lon-coordinate').val();
var rectangle = L.rectangle([ [neLatCoord, neLonCoord], [swLatCoord, swLonCoord]]).addTo(map);
So now I want to get the rectangle width and height so I can set dimensions of the area select like this :
var rectangleWidth = rectangle.getBounds().getEast() - rectangle.getBounds().getWest();
var rectangleHeight = rectangle.getBounds().getNorth() - rectangle.getBounds().getSouth();
areaSelect.setDimensions({width: rectangleWidth, height: rectangleHeight});
but this gives another width and height? I don't know why?
Can someone help me please, cause I'm stuck on this?
Here's my JS FIDDLE
EDIT:
If I use rectangle instead of area select:
var rectangle = L.rectangle([ [21.616579, 29.487305], [7.798079, 20.522461]]);
map.addLayer(rectangle);
rectangle.editing.enable();
// Every time we move the selected rectangle, refresh data about the selected area.
rectangle.on('edit', function() {
selectedBounds = this.getBounds();
console.log("Area:", selectedBounds);
//some other code
});
$( ".select" ).click(function() {
rectangle.editing.disable();
map.removeLayer(rectangle);
rectangle = new L.rectangle([ [17.853290, 34.980469], [10.876465, 14.853516]]);
map.addLayer(rectangle);
rectangle.editing.enable();
});
When I do this reset on clicking, the event rectangle.on('edit', function() { ... is not working? I don't know why? Can you help me, please
Like the docs in leaflet-areaselect state, the method .setDimensions() needs arguments as Pixel and you give the arguments in geographical coordinates. With the leaflet map method .latLngToLayerPoint() you are able to convert geogr. coords to Pixel of your map extend.
Just change
var rectangleWidth = rectangle.getBounds().getEast() - rectangle.getBounds().getWest();
var rectangleHeight = rectangle.getBounds().getNorth() - rectangle.getBounds().getSouth();
into
var rectangleWidth = map.latLngToLayerPoint(rectangle.getBounds().getNorthEast()).x- map.latLngToLayerPoint(rectangle.getBounds().getSouthWest()).x
var rectangleHeight = map.latLngToLayerPoint(rectangle.getBounds().getNorthEast()).y- map.latLngToLayerPoint(rectangle.getBounds().getSouthWest()).y
Here is the working JS FIDDLE
I know this thread is about a year old, but I figured someone else might benefit from my trouble with the provided answer.
Leaflet actually provides 2 conversion methods:
latLngToLayerPoint()
latLngToContainerPoint()
The difference between the two has to do with how x,y positions are calculated.
LayerPoint finds x,y positions relative the top-left corner of the map when it is initialized!!! (Zooming resets this origin point, but panning does not!)
ContainerPoint finds x,y positions relative to the top-left corner of the map container itself.
Why does that matter?
If you initialize your map, pan it, and then get x,y coords using latLngToLayerPoint(), you can potentially get negative x/y values, which can lead to negative widths/heights if you don't actively compensate for them.
At least in my use-case, it was better to use latLngToContainerPoint() when determining boundary heights/widths in pixels.
Other than that, the above answer worked perfectly for me! :D

Paper.js Subraster Selecting Wrong Area

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>

give motion to grass field in canvas

i have created a grass field which is a combination of several small 60x36 images.a grass object is introduced and then drawn on the canvas.now i want to give it motion .the continuous scrolling effect .i made a code for it and it isn't working( the images (the grass field)are not scrolling along the width of the canvas which is the goal of this script).i haven't work much with oop in js. a little discussion on the mistakes i have done will be great
(the image i have used is added to the post)
<html>
<body>
<canvas id="mycanvas"></canvas>
<script>
function makeit(){
var canvas=document.getElementById("mycanvas");
var ctx=canvas.getContext('2d');
var height=500-36;
var xpos=[];
var img=new Image();
img.src="grass.jpg";
drawcanvas();
function drawcanvas(){
canvas.width=600;
canvas.height=500;
canvas.style.border="1px solid black";
}
for(i=0;i<10;i++){
xpos.push(i*60);
}
var grass=function(x,y){
this.x=x;
this.y=y;
this.img=img;
ctx.drawImage(this.img,this.x,this.y);
}
grass.prototype.motion=function(){
for(i=0;i<xpos.length;i++){
xpos[i]--;
if(xpos[i]<=-60){
xpos[i]=canvas.width;
}
ctx.drawImage(this.img,this.x,this.y);
}
}
for(i=0;i<xpos.length;i++){
var grass1=new grass(xpos[i],height);
}
var m=setTimeout(function(){
for(i=0;i<xpos.length;i++){
grass1.motion();
}
},1000);
}
window.onload=makeit;
</script>
</body>
</html>
actual canvas after drawing all the images
In essence, all you need is to create an image pattern then translate and draw it to screen.
An example assuming image has been loaded:
var ph = img.height; // pattern height
var w = canvas.width; // width of canvas/scoll area
var h = canvas.height; // used to calculate y pos.
var x = 0; // scroll position
ctx.fillStyle = ctx.createPattern(img, 'repeat-x'); // pattern
Then in the loop scrolling the grass:
function scroll() {
ctx.translate(x, h - ph); // translate to next position
ctx.fillRect(-x, 0, w, ph); // fill rectangle (fillstyle = pattern)
ctx.translate(-x, -(h -ph)); // translate back for other operations
x--; // scroll speed (here 1 pixel / frame)
requestAnimationFrame(scroll); // loop
}
FIDDLE
Pattern fills are anchored to the coordinate system which is why the translate is necessary. As we translate we also compensate for it using draw position in the opposite direction. This will make the pattern be filled into the same position but at a variable offset which creates the animation effect.
Just note that if you change fillStyle you need to store the pattern in a variable and reinitialize the fill style. If the loop is long-running also limit x so it doesn't overflow. This can be done using w as a condition (or modulo) to reset x to 0.

Categories

Resources