Draw a circle by pressing ctrl & dragging mouse in leaflet - javascript

I'm trying to develop a function with leaflet which make user be able to draw a circle by pressing ctrl & dragging mouse, as the following
let mouseDownPos = null
let mouseUpPos = null
L.Map.CircleSelector = L.Map.Drag.extend({
_onMouseDown: function(e) {
if (!e.ctrlKey)
return
let map = this._map
map.dragging.disable()
mouseDownPos = map.containerPointToLatLng(this._point)
},
_onMouseUp: function(e) {
if (!e.ctrlKey) {
this._map.dragging.enable()
return
}
let map = this._map
mouseUpPos = map.containerPointToLatLng(this._point)
let radius = map.distance(mouseDownPos, mouseUpPos)
L.circle(mouseDownPos, {radius: radius}).addTo(map)
map.dragging.enable()
}
})
L.Map.mergeOptions({circleSelector: true})
L.Map.addInitHook('addHandler', 'circleSelector', L.Map.CircleSelector)
When I press ctrl & drag mouse on the map, it still does not work.
I've tried to print text to console at the beginning in _onMouseDown(), it shows nothing.
It seems that the event doesn't trigger.
What should I need to modify? Thank you.

Finally I extend leaflet.draw to approach my goal.
Refer to the source code of L.Draw.Circle, I extend my selector from L.Draw.Circle. The mainly modified part is in _onMouseUp, as the following
L.Map.CircleSelector = L.Draw.SimpleShape.extend({
_onMouseUp: function (e) {
// TODO
// 1. Get the circle center & radius
// 2. Calculate distances between center & markers
// 3. If the distance in step 2 <= radius, it is in the circle
// 4. Anything you'd like to do......
}
})
The rest code of the event can be referred to L.Draw.Circle, e.g., addHooks, _onMouseMove......

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 find marker position in AFrame using JavaScript?

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):

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

Nokia / HERE maps goes back to level 0 zoom after map.zoomTo

My javascript code loops through some user data and adds each one as a marker to a container. It then adds that container to a nokia map and uses the Display zoomTo to zoom to the bounding box of the container holding all the markers.
However, right after that happens, the map just zooms itself all the way back out. The zoomTo call is the very last of my code that executes so it seems like something weird is going on.
this.finishMapping = function () {
map.objects.add(multiMapContainer);
var markerHopefully = multiMapContainer.objects.get(0);
$.ajax({
dataType: "jsonp",
url: "https://route.api.here.com/routing/7.2/calculateroute.json?app_id=WgevZ2m4AF8WHx1TY6GS&app_code=G11AO2dbvCRTdCjfTf-mUw&waypoint0=geo!" + markerHopefully.coordinate.latitude + "," + markerHopefully.coordinate.longitude + "&waypoint1=geo!" + markerHopefully.coordinate.latitude + "," + markerHopefully.coordinate.longitude + "&mode=fastest;car;",
success: function (data) {
onRouteCalculated(data)
},
jsonp: "jsoncallback"
});
function onRouteCalculated(data) {
if (data.response) {
var position = data.response.route[0].waypoint[0].mappedPosition;
var coordinate = new nokia.maps.map.StandardMarker([position.latitude, position.longitude]);
var tempContainer = new nokia.maps.map.Container();
tempContainer.objects.add(coordinate);
map.zoomTo(multiMapContainer.getBoundingBox().merge(tempContainer.getBoundingBox()), false);
}
}
}
I debugged through it in Chrome and I can see that zoomTo does actually zoom to the proper bounding box, but then right after I hit the 'continue' button it jumps back to the highest zoom level.
I recently came across a similar issue. The basic issue was that the containing <DIV> for the map was itself being initialized during the map initialization. My problem was compounded because map.zoomTo() doesn't work during map initialization anyway (since 2.5.3 map loading is always asynchronous)
The crux of the issue was that I was trying to use zoomTo() on a 0x0 pixel map since the <DIV> wasn't displayed yet - hence I ended up with a zoomLevel zero map.
The solution is to add listeners to the map as shown:
map.addListener("displayready", function () {
if(bbox){map.zoomTo(bbox, false);}
});
map.addListener("resize", function () {
if(bbox){map.zoomTo(bbox, false);}
});
And set up the bbox parameter as each coordinate is received as shown:
function onCoordinateReceived(coordinate){
if(bbox){
bbox = nokia.maps.geo.BoundingBox.coverAll([
bbox.topLeft, bbox.bottomRight, coordinate]);
} else {
bbox = nokia.maps.geo.BoundingBox.coverAll([coordinate]);
}
map.zoomTo(bbox, false);
}
So that:
If the map is already intialized and displayed the zoomTo() in the onCoordinateReceived() will fire
If the map completes intialization after onCoordinateReceived(), the zoomTo() in the displayready listener will fire.
If the <DIV> update occurs last, the zoomTo() in the resize listener will fire, which will alter from a zoomed Out map to the "right" zoom level.

rotate a globe with the mouse

I can determine where on a globe the user has clicked.
When the user first clicks the mouse button down, I can store where they have clicked; lets call it the pin.
Then, as they drag the mouse around, I want to put the pin under the mouse cursor.
Here is some code that seems to roughly keep the pin under the mouse pointer, but doesn't correctly maintain the globe; it flickers and spins randomly. Sometimes it flips the axis entirely so the globe is momentarily back-to-front.
function evtPos(evt) {
if(!scene.ortho) return null;
var x = lerp(scene.ortho[0],scene.ortho[1],evt.clientX/canvas.width),
y = lerp(scene.ortho[3],scene.ortho[2],evt.clientY/canvas.height), // flipped
sqrd = x*x+y*y;
return (sqrd > 1)?
null:
mat4_vec3_multiply(mat4_inverse(scene.mvMatrix),[x,y,Math.sqrt(1-sqrd)]);
}
function onMouseDown(evt) {
pin = evtPos(evt);
}
function onMouseMove(evt,keys,isMouseDown) {
if(!isMouseDown) return;
var pt = evtPos(evt);
if(pin == null) pin = pt;
if(pt == null) return;
var d = vec3_sub(pt,pin),
rotx = Math.atan2(d[1],d[2]),
roty = (d[2] >= 0)?
-Math.atan2(d[0] * Math.cos(rotx),d[2]):
Math.atan2(d[0] * Math.cos(rotx),-d[2]),
rotz = Math.atan2(Math.cos(rotx),Math.sin(rotx)*Math.sin(roty));
scene.mvMatrix = mat4_multiply(scene.mvMatrix,mat4_rotation(rotx,[1,0,0]));
scene.mvMatrix = mat4_multiply(scene.mvMatrix,mat4_rotation(roty,[0,1,0]));
scene.mvMatrix = mat4_multiply(scene.mvMatrix,mat4_rotation(rotz,[0,0,1]));
}
function onMouseUp(evt) {
pin = null;
}
Also, over time, an error seems to build up and the pin drifts further and further from the mouse pointer. I presume I should somehow compute the mvMatrix completely rather than by lots of samll increments each event?
I want the user to be able to drag the globe around to navigate naturally. All code to spin globes that I've found uses fixed speeds e.g. arrow keys, rather than 'pinning' the globe under a mouse pointer. Unity has a function Quaternion.FromToRotation(fromPos,toPos) which seems very promising but the source is not available.
One of the approaches for doing this is the arcBall algorithm. There are even JavaScript implementations available so you don't need to roll your own.

Categories

Resources