Stupid mouse move coordinates in dart - dart

I am learning Dart and I was trying to make a very simple drageable HTML element. I followed patterns I'm used to from javascript but it doesn't work as expected.
When making drageable object from scratch you usually do the following:
Listen on mouse down event on that object
Upon mouse down, remember mouse coordinates relative to the object's top-left corner
Listen for any mouse movement. For every move operation, move the object to cursor location minus the coordinates you remembered earlier.
Upon any mouse up event, stop following mouse movement.
So I produced this code:
class DrageableControl {
DivElement _elm;
DrageableControl(String txt, int x, int y) {
//Create element and set up styles
var elm = this.setupElement(x, y);
//Append element to document, add some text and start listeners
document.body.append(elm);
elm.text = txt;
setupEvents();
}
//This function creates all event necessary for drag operations
setupEvents() {
Point relativeMouseOffset = null;
_elm.onMouseDown.listen((MouseEvent e) {
Rectangle myPos = _elm.getBoundingClientRect();
relativeMouseOffset = new Point(e.offset.x-myPos.left, e.offset.y-myPos.top);
e.preventDefault();
return false;
});
//Of course this is completely wrong, the listener should only be added for the duration of dragging
document.onMouseMove.listen((MouseEvent e) {
if(relativeMouseOffset!=null) {
print("Clicked at: ${relativeMouseOffset}\nCurrent mouse position:${e.offset}");
_elm.style.top = "${(e.offset.y/*-relativeMouseOffset.y*/)}px";
_elm.style.left = "${(e.offset.x/*-relativeMouseOffset.x*/)}px";
}
});
document.onMouseUp.listen((MouseEvent e){
relativeMouseOffset = null;
});
}
setupElement(int x, int y) {
var elm = this._elm = new DivElement();
//TODO: Use css?
elm.style.position = "absolute";
elm.style.top = "${y}px";
elm.style.left = "${x}px";
elm.style.border = "1px solid red";
elm.style.backgroundColor = "#FFAAAA";
elm.style.cursor = "default";
//elm.style.transition = "top 1s";
return elm;
}
}
Problem is, that some coordinates delivered by MouseMove are complete nonsense. See the console:
Clicked at: Point(-76.0, -143.0)
Current mouse position:Point(1, 1)
Clicked at: Point(-76.0, -143.0)
Current mouse position:Point(374, 272)
Clicked at: Point(-76.0, -143.0)
Current mouse position:Point(1, 0)
Clicked at: Point(-76.0, -143.0)
Current mouse position:Point(376, 273)
Clicked at: Point(-76.0, -143.0)
Current mouse position:Point(0, 1)
As you can see every second mouse move event delivers broken data - coordinates right around [0, 0]. So how can I filter out this invalid data? Why does it happen?
So far I'm probably fixing this by adding:
if(e.offset.x+e.offset.y<5)
return;

Use e.client.x/e.client.y instead.

Related

Higcharts: Arrow segment drawn with single click, can not be selected nor dragged

I have my code in this JSFiddle. I can draw annotations with click-and-drag, using the plugins: user click and hold the mouse (drawing starts on mouse-down), start to drag around and drawing is completed when user releases the mouse (mouse-up event).
The issue here is: when we have a single-click in a certain point, an arrow symbol appears and this can not be selected nor draggable. In cases of single-click, will be ideal if mouse-up event to not complete the drawing and instead drawing to be completed on the next mouse-down event.
Here in this video we can see the behavior of how the single-click acts at the moment and also there I commented one condition to show how I expect this to behave. I am already successfully having the check if we have a single-click while drawing in the part with console.log("this is a single click");.
The two most important blocks of the code-snippet are given below, and what I don't understand is why this is not working as I described above, since I am already terminating the function in case single-click is detected (with return).
//on mouse-down event, if cursor is inside of the chart and if drawing tool is selected, set the
//flag "isAddingAnnotations" to true and store the coordinates from where the drawing started
navigation.eventsToUnbind.push(addEvent(chart.container, 'mousedown', function(e) {
if (!chart.cancelClick &&
chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop))
{
navigation.bindingsChartClick(chart, e);
if (!navigation.selectedButton) {
chart.isAddingAnnotations = false;
} else {
chart.isAddingAnnotations = true;
chart.addingAnnotationsX = e.clientX;
chart.addingAnnotationsY = e.clientY;
}
}
}));
//on mouse-up event, set the flag "isAddingAnnotations" to false (since drawing ended)
//additionally, if drawing was in progress and if ending coordinates are matching with
//starting coordinates, drawing should still be active
navigation.eventsToUnbind.push(addEvent(chart.container, 'mouseup', function(e) {
if (chart.isAddingAnnotations &&
chart.addingAnnotationsX == e.clientX &&
chart.addingAnnotationsY == e.clientY)
{
console.log("this is a single click");
return;
}
chart.pointer.normalize(e);
navigation.bindingsChartClick(chart, e);
chart.isAddingAnnotations = false;
}));

getClosestFeatureToCoordinate does not work for Points

I made a function to get the closer feature to the one clicked. I use Openlayers 3.9.0 and the getClosestFeatureToCoordinate method.
var select = new ol.interaction.Select();//simple click interaction
map.addInteraction(select);//add it to the map
select.on('select', function(e) {
//get the extent of the first selected feature
var aa = e.selected[0].getGeometry().getExtent();
//in case of line or polygon get the center of that extent
var oo = ol.extent.getCenter(aa);
//use it to get the name of the closest feature
console.log((sourceVector.getClosestFeatureToCoordinate(oo)).get("mylayer_name")) ;
});
But in a case like the following
if I click the "u" Polygon (bottom down) I get "u" instead of , say, "e"
if I click any point I get its name , instead of the closest feature's name. I click "testpoint9" and I get "testpoint9" instead of "u" or "e". I click "h" and I get "h" instead of "p" or "k".
So maybe has to do with points, so I changed the select.on event function to
select.on('select', function(e) {
var closestType = e.selected[0].getGeometry().getType();
var oo;
if (closestType === 'Point'){
oo = e.selected[0].getGeometry().getCoordinates();
}
else{
var aa = e.selected[0].getGeometry().getExtent();
oo = ol.extent.getCenter(aa);
}
console.log("---------------------------------------------------");
console.log("Name: "+sourceVector.getClosestFeatureToCoordinate(oo).get('mylayer_name'));
})
and still nothing. So, how I fix this?
Thanks
When You click inside the "u" polygon - the distance to it is 0.
When you click on something - that thing will always be the closest (to itself).
What you can do there, is removing the clicked element from the layer, run the algorithm (without clicked point) and put the point back on the layer.
If You are afraid that point could be invisible for too long (but it shouldn't), place it in another layer for the time of algorithm.
select.on('select', function(e) {
var clicked = e.selected[0];
sourceVector.removeFeature(clicked);
var closest = sourceVector.getClosestFeatureToCoordinate(e.coordinate);
sourceVector.addFeature(clicked);
var closestType = closest.getType();
if (closestType === 'Point'){
oo = e.selected[0].getGeometry().getCoordinates();
}
else{
var aa = e.selected[0].getGeometry().getExtent();
oo = ol.extent.getCenter(aa);
}
})

Very slow hover interactions in OpenLayers 3 with any browser except Chrome

I have two styles of interactions, one highlights the feature, the second places a tooltop with the feature name. Commenting both out, they're very fast, leave either in, the map application slows in IE and Firefox (but not Chrome).
map.addInteraction(new ol.interaction.Select({
condition: ol.events.condition.pointerMove,
layers: [stationLayer],
style: null // this is actually a style function but even as null it slows
}));
$(map.getViewport()).on('mousemove', function(evt) {
if(!dragging) {
var pixel = map.getEventPixel(evt.originalEvent);
var feature = null;
// this block directly below is the offending function, comment it out and it works fine
map.forEachFeatureAtPixel(pixel, function(f, l) {
if(f.get("type") === "station") {
feature = f;
}
});
// commenting out just below (getting the feature but doing nothing with it, still slow
if(feature) {
target.css("cursor", "pointer");
$("#FeatureTooltip").html(feature.get("name"))
.css({
top: pixel[1]-10,
left: pixel[0]+15
}).show();
} else {
target.css("cursor", "");
$("#FeatureTooltip").hide();
}
}
});
I mean this seems like an issue with OpenLayers-3 but I just wanted to be sure I wasn't overlooking something else here.
Oh yeah, there's roughly 600+ points. Which is a lot, but not unreasonably so I would think. Zooming-in to limit the features in view definitely helps. So I guess this is a # of features issue.
This is a known bug and needs more investigation. You can track progress here: https://github.com/openlayers/ol3/issues/4232.
However, there is one thing you can do to make things faster: return a truthy value from map.forEachFeatureAtPixel to stop checking for features once one was found:
var feature = map.forEachFeatureAtPixel(pixel, function(f) {
if (f.get('type') == 'station') {
return feature;
}
});
i had same issue, solved a problem by setInterval, about this later
1) every mouse move to 1 pixel fires event, and you will have a quee of event till you stop moving, and the quee will run in calback function, and freezes
2) if you have an objects with difficult styles, all element shown in canvas will take time to calculate for if they hit the cursor
resolve:
1. use setInterval
2. check for pixels moved size from preview, if less than N, return
3. for layers where multiple styles, try to simplify them by dividing into multiple ones, and let only one layer by interactive for cursor move
function mouseMove(evt) {
clearTimeout(mm.sheduled);
function squareDist(coord1, coord2) {
var dx = coord1[0] - coord2[0];
var dy = coord1[1] - coord2[1];
return dx * dx + dy * dy;
}
if (mm.isActive === false) {
map.unByKey(mm.listener);
return;
}
//shedules FIFO, last pixel processed after 200msec last process
const elapsed = (performance.now() - mm.finishTime);
const pixel = evt.pixel;
const distance = squareDist(mm.lastP, pixel);
if (distance > 0) {
mm.lastP = pixel;
mm.finishTime = performance.now();
mm.sheduled = setTimeout(function () {
mouseMove(evt);
}, MIN_ELAPSE_MSEC);
return;
} else if (elapsed < MIN_ELAPSE_MSEC || mm.working === true) {
// console.log(`distance = ${distance} and elapsed = ${elapsed} mesc , it never should happen`);
mm.sheduled = setTimeout(function () {
mouseMove(evt);
}, MIN_ELAPSE_MSEC);
return;
}
//while multithreading is not working on browsers, this flag is unusable
mm.working = true;
let t = performance.now();
//region drag map
const vStyle = map.getViewport().style;
vStyle.cursor = 'default';
if (evt.dragging) {
vStyle.cursor = 'grabbing';
}//endregion
else {
//todo replace calback with cursor=wait,cursor=busy
UtGeo.doInCallback(function () {
checkPixel(pixel);
});
}
mm.finishTime = performance.now();
mm.working = false;
console.log('mm finished', performance.now() - t);
}
In addition to #ahocevar's answer, a possible optimization for you is to utilize the select interaction's select event.
It appears that both the select interaction and your mousemove listener are both checking for hits on the same layers, doing double work. The select interaction will trigger select events whenever the set of selected features changes. You could listen to it, and show the popup whenever some feature is selected and hide it when not.
This should reduce the work by half, assuming that forEachFeatureAtPixel is what's hogging the system.

ray doesn't reach JSON

I'm trying to catch a JSON object with a mouse click event. I use ray to identify the object, but for some reason, the objects are not always identified. I suspect that it is related to the fact that I move the camera, because when I click nearby the object, i is identified.
Can you help me figure out how to set the ray correctly, in accordance with the camera move?
Here is the code :
this is the part of the mouse down event *
document.addEventListener("mousemove", onDocumentMouseMove, false);
document.addEventListener("mouseup", onDocumentMouseUp, false);
document.addEventListener("mouseout", onDocumentMouseOut, false);
mouseXOnMouseDown = event.clientX - windowHalfX;
targetRotationOnMouseDown = targetRotation;
var ray, intersections;
_vector.set((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1, 0);
projector.unprojectVector(_vector, camera);
ray = new THREE.Ray(camera.position, _vector.subSelf(camera.position).normalize());
intersections = ray.intersectObjects(furniture);
if (intersections.length > 0) {
selected_block = intersections[0].object;
_vector.set(0, 0, 0);
selected_block.setAngularFactor(_vector);
selected_block.setAngularVelocity(_vector);
selected_block.setLinearFactor(_vector);
selected_block.setLinearVelocity(_vector);
mouse_position.copy(intersections[0].point);
block_offset.sub(selected_block.position, mouse_position);
intersect_plane.position.y = mouse_position.y;
}
}
this is the part of the camera move *
camera.position.x = (Math.cos(timer) * 10);
camera.position.z = (Math.sin(timer) * 10);
camera.lookAt(scene.position);
Hmmm, It is hard to say what your problem might be without seeing some kind of demonstration of how your program is actually acting. I would suggest looking at my demo that I have been working on today. I handle my camera, controls, and rays. I am using a JSON as well.
First you can view my demo: here to get an idea of what it is doing, what your describing sounds similar. You should be able to adapt my code if you can understand it.
--If you would like a direct link to the source code: main.js
I also have another you might find useful where I use rays and mouse collisions to spin a cube. --Source code: main.js
Finally I'll post the guts of my mouse events and how I handle it with the trackball camera in the first demo, hopefully some of this will lead you to a solution:
/** Event fired when the mouse button is pressed down */
function onDocumentMouseDown(event) {
event.preventDefault();
/** Calculate mouse position and project vector through camera and mouse3D */
mouse3D.x = mouse2D.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse3D.y = mouse2D.y = -(event.clientY / window.innerHeight) * 2 + 1;
mouse3D.z = 0.5;
projector.unprojectVector(mouse3D, camera);
var ray = new THREE.Ray(camera.position, mouse3D.subSelf(camera.position).normalize());
var intersects = ray.intersectObject(maskMesh);
if (intersects.length > 0) {
SELECTED = intersects[0].object;
var intersects = ray.intersectObject(plane);
offset.copy(intersects[0].point).subSelf(plane.position);
killControls = true;
}
else if (controls.enabled == false)
controls.enabled = true;
}
/** This event handler is only fired after the mouse down event and
before the mouse up event and only when the mouse moves */
function onDocumentMouseMove(event) {
event.preventDefault();
/** Calculate mouse position and project through camera and mouse3D */
mouse3D.x = mouse2D.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse3D.y = mouse2D.y = -(event.clientY / window.innerHeight) * 2 + 1;
mouse3D.z = 0.5;
projector.unprojectVector(mouse3D, camera);
var ray = new THREE.Ray(camera.position, mouse3D.subSelf(camera.position).normalize());
if (SELECTED) {
var intersects = ray.intersectObject(plane);
SELECTED.position.copy(intersects[0].point.subSelf(offset));
killControls = true;
return;
}
var intersects = ray.intersectObject(maskMesh);
if (intersects.length > 0) {
if (INTERSECTED != intersects[0].object) {
INTERSECTED = intersects[0].object;
INTERSECTED.currentHex = INTERSECTED.material.color.getHex();
plane.position.copy(INTERSECTED.position);
}
}
else {
INTERSECTED = null;
}
}
/** Removes event listeners when the mouse button is let go */
function onDocumentMouseUp(event) {
event.preventDefault();
if (INTERSECTED) {
plane.position.copy(INTERSECTED.position);
SELECTED = null;
killControls = false;
}
}
/** Removes event listeners if the mouse runs off the renderer */
function onDocumentMouseOut(event) {
event.preventDefault();
if (INTERSECTED) {
plane.position.copy(INTERSECTED.position);
SELECTED = null;
}
}
And in order to get the desired effect shown in my first demo that I wanted, I had to add this to my animation loop in order to use the killControls flag to selectively turn on and off the trackball camera controls based on the mouse collisions:
if (!killControls) controls.update(delta);
else controls.enabled = false;

Actionscript 3, rotate object on drag

I have an object I need to rotate by clicking and dragging. Following some AS2 code I got the object to rotate a bit every time the mouse is clicked, but can't get it to work with drag.
needle.addEventListener(MouseEvent.MOUSE_DOWN, fl_ClickToDrag_2);
stage.addEventListener(MouseEvent.MOUSE_UP, fl_ReleaseToDrop_2);
function fl_ClickToDrag_2(event:MouseEvent):void
{
var angle = Math.atan2(mouseY-needle.y,mouseX-needle.x);
// apply rotation to handle by converting angle into degrees
needle.rotation = angle*180/Math.PI;
// rotate the grip opposite the handle so it won't rotate along with it
//this.grip._rotation = -this._rotation;
}
function fl_ReleaseToDrop_2(event:MouseEvent):void
{
needle.stopDrag();
}
Well the problem I see is that the MOUSE_DOWN event only fires once per click, so you only run the code in the handler once.
There could be a better way than this but this is how I'd consider doing it:
EDITED FOR DETAIL:
public class Test extends MovieClip {
private var n:Needle;
public function Test() {
// constructor code
n = new Needle();
stage.addEventListener(MouseEvent.MOUSE_DOWN,mouseDownF,false,0,true);
stage.addEventListener(MouseEvent.MOUSE_UP,mouseUpF,false,0,true);
n.x = stage.stageWidth/2; //center needle on stage
n.y = stage.stageHeight/2;
addChild(n); //add needle to stage
}
public function mouseDownF(e:MouseEvent):void {
stage.addEventListener(MouseEvent.MOUSE_MOVE,rotate,false,0,true);
}
public function rotate(e:MouseEvent):void {
var angle:Number = Math.atan2(mouseY - n.y,mouseX - n.x); //get angle in radians (pythagoras)
angle = angle * 180/Math.PI -90; //convert to degrees , the 90 is to have it point to the mouse
n.rotation = angle; //rotate
}
public function mouseUpF(e:MouseEvent):void {
stage.removeEventListener(MouseEvent.MOUSE_MOVE,rotate);
}
}
So when the user clicks down (mouseDown) it activates an event listener that fires the rotate handler every time the mouse moves. When the user lets go of the click the event listener is destroyed. The false,0,true); when adding the event listener is to make it a weakly referenced listener so that it gets collected by the garbage collector and doesn't just sit in memory taking up space forever.

Resources