How to change Qt3D OrbitCameraController's behavior, eg. left mouse to rotate camera - qt3d

The default Qt3D OrbitCameraController's behavior is:
- Left mouse to move camera.
- Right mouse to rotate camera.
I need opposite solution, to simply rotate show a product model.
C++ code or AxisActionHandler maybe work, but I don't known how to write it. Thanks for help.

After trying different solutions, I used MouseHandler to finish this task. The core codes is below:
Entity{
id: root
property Camera camera;
MouseDevice {
id: mouseDevice
}
MouseHandler {
property point _lastPt; // 鼠标最后的位置
property real _pan; // 相机沿y轴旋转角度
property real _tilt; // 相机沿x轴旋转角度
on_PanChanged: root.camera.panAboutViewCenter(_pan);
on_TiltChanged: root.camera.tiltAboutViewCenter(_tilt);
sourceDevice: mouseDevice
onPressed: {_lastPt = Qt.point(mouse.x, mouse.y);}
onPositionChanged: mouseMove(mouse);
...
function mouseMove(mouse){
if (mouse.buttons == 1){
_pan = -(mouse.x - _lastPt.x);
_tilt = (mouse.y - _lastPt.y);
_lastPt = Qt.point(mouse.x, mouse.y);
}
}
}
}

Related

Xamarin.Forms.Maps custom polyline renderer does not render when in view

I am trying to follow this guide in order to draw a polyline on a map in a Xamarin.Forms app. It should track the user's position in real-time and update the polyline when new position data comes in.
I wrote a custom map renderer that will render the polyline, but for some reason it does not update when the map is in view. I have to navigate back to the main launch page and navigate to the mapping page again for it to update.
I extracted the minimum code to reproduce the problem, but it is still too much to paste here, so I hosted it on GitHub:
https://github.com/Steztric/MapWithWaylineSample
Please could somebody let me know what I am doing wrong. You can demonstrate the problem by cloning the repo and running it.
You need to create renderer every time, so remove class variable polylineRenderer and use local one.
MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlayWrapper)
{
IMKOverlay overlay = Runtime.GetNSObject(overlayWrapper.Handle) as IMKOverlay;
if (overlay is MKPolyline)
{
var polylineRenderer = new MKPolylineRenderer(overlay as MKPolyline);
polylineRenderer.FillColor = UIColor.Blue;
polylineRenderer.StrokeColor = UIColor.Red;
polylineRenderer.LineWidth = 3;
polylineRenderer.Alpha = 0.4f;
return polylineRenderer;
}
else
{
return null;
}
}
Also you can simplify things a little
Define MKPolyline currentWayline; then
var wayline = MKPolyline.FromCoordinates(coords.ToArray());
//IMKOverlay overlay = Runtime.GetNSObject(wayline.Handle) as IMKOverlay;
nativeMap.AddOverlay(wayline);
currentWayline = wayline;

three.js and openlayers coordinates don't line up

I'm using three.js on a separate canvas on top of a openlayers map. the way I synchronize the view of the map and the three.js camera (which looks straight down onto the map) looks like this:
// calculate how far away the camera needs to be, to
// see this part of the map
function distanceFromExtentAndFOV(h, vFOV) {
return h / (2 * Math.tan(vFOV / 2));
}
// keep three.js camera in sync with visible part of openlayers map
function updateThreeCam(
group, // three.js group, containing camera
view, // ol view
map // ol map
) {
extent = getViewExtent(view, map);
const h = ol.extent.getHeight(extent);
mapCenter = ol.extent.getCenter(extent);
camDist = distanceFromExtentAndFOV(h, constants.CAM_V_FOV_RAD);
const pos = [...mapCenter, camDist];
group.position.set(...pos);
group.updateMatrixWorld();
}
// whenever view changes, update three.js camera
view.on('change:center', (event) => {
updateThreeCam(group, event.target, map);
});
view.on('change:resolution', (event) => {
updateThreeCam(group, event.target, map);
});
updateThreeCam(group, view, map);
I'm using three.js for custom animations of objects, that in the end land on the ground. however, when I turn the objects into openlayers features, the original three.js objects and the features don't line up perfectly (although their position coordinates are exactly the same). — also, when you pan, you can see that s.th. is slightly off.
// turn three.js planes into openlayers features:
const features = rects.map((rect) => {
const point = new ol.geom.Point([
rect.position.x,
tect.position.y
]);
return new ol.Feature(point);
});
vectorSource.addFeatures(features);
as I am pretty sure that my the code for synchronizing three.js and ol is correct, I am not really sure what what is going wrong. am I missing s.th. here?
It could be that you are using PerspectiveCamera and that will change the size/position based on depth. Try OrtographicCamera instead.

ILNumerics: Export part of a scene as image without affecting original scene

I have created a Scene with ILNumerics that consists of 3 PlotCubes and a Colorbar.
Screenshot of the ILPanel
I wanted to add a method that exports the scene as an image in two ways, the first being the screenshot you see above.
The second export is supposed to only show the centric cube.
I attempted to follow the guidelines of ILNumerics for scene management.
I wrote the following code:
public void ExportAsImage(int resolutionWidth, int resolutionHeight, string path, bool includeSubCubes)
{
using (ILScope.Enter())
{
ILGDIDriver backgroundDriver = new ILGDIDriver(resolutionWidth, resolutionHeight, ilPanel1.Scene);
if (includeSubCubes)
{
// code for standard export here
}
else
{
// setting left and top cube and color bar invisible and
// adjusting main cube size is affecting the ilPanel.Scene
backgroundDriver.Scene.First<ILColorbar>().Visible = false;
GetElementByTag<ILPlotCube>(backgroundDriver.Scene, _leftCubeTag).Visible = false;
GetElementByTag<ILPlotCube>(backgroundDriver.Scene, _topCubeTag).Visible = false;
GetElementByTag<ILPlotCube>(backgroundDriver.Scene, _mainCubeTag).ScreenRect = new RectangleF(0, 0, 1, 1);
GetElementByTag<ILPlotCube>(backgroundDriver.Scene, _mainCubeTag).DataScreenRect = new RectangleF.Empty;
backgroundDriver.Scene.Configure();
backgroundDriver.Render();
// save image
backgroundDriver.BackBuffer.Bitmap.Save(path,System.Drawing.Imaging.ImageFormat.Png);
// revert changes done to cubes and color bar
backgroundDriver.Scene.First<ILColorbar>().Visible = true;
GetElementByTag<ILPlotCube>(backgroundDriver.Scene, _leftCubeTag).Visible = true;
GetElementByTag<ILPlotCube>(backgroundDriver.Scene, _topCubeTag).Visible = true;
AdjustCubeSizes();
}
}
}
Note: "GetElementByTag" is an own implementation to retrieve objects in the ILNumerics Scene.
I first expected that the new driver basically creates a copy of the Scene I can work on, but like the code shows I have to revert all changed after the export or the displayed ilPanel only shows the scene the way I exported it.
Is it possible at all to do export to image without affecting the real Scene? Am I just missing some details?
Regards,
Florian S.
Florian, it does make a copy. But you need to add the interesting part to a new scene. The magic is happening in the Add() method:
var scene4render = new ILScene();
scene4render.Add(oldScene.First<ILPlotCube>(mytag));
// ... configure scene4render here, it will be detached from the original scene
// with the exception of shared buffers.
// ... proceed with rendering
In order to also include + render interactive state changes to the original plot cube (let's say rotations by the users mouse) you'd use something like that:
scene4render.Add(panel.SceneSyncRoot.First<ILPlotCube>(mytag));
Also, I wonder what GetElementByTag does better than ILGroup.First<T>(tag, predicate) or ILGroup.Find<T>(...)?
See also: http://ilnumerics.net/scene-management.html

Three.js Hemisphere Light

I`m examining a different types of light with dat.GUI and three.js (r72) and get stucked on dinamically turning on/off the HemisphereLight.
I have a one instance of each light being added to the scene:
var pointLight = new THREE.PointLight(this._whiteColor);
pointLight.visible = false;
var hemisphereLight = new THREE.HemisphereLight(this._whiteColor, this._whiteColor);
hemisphereLight.visible = false;
ect...
and a buttons for turning on/off with simple handler. Like this:
me._sceneObjects.hemisphereLight.visible = value;
So, before rendering all lights are present in the scene, but not visible.
When executing a handler for Hemisphere light - it stay invisible. After excluding hemisphereLight.visible = false; works fine.
Currently I`m disabling this light after rendering a 1st frame:
function render() {
some code ...
if (!sentenceExecuted && firstCall > 0) {
hemisphereLight.light.visible = false;
sentenceExecuted = true;
} else {
firstCall++;
}
};
I will be grateful for any suggestions to fix this without workaround.
Sorry for possible mistakes in English.
WestLangley, thank you for the link to Wiki article.
After reading and trying the examples I have found my mistake.
Originally I wanted to add "invisible" (using Object3D.visible) for render lights into a scene and then turn them on/off dynamically.
Some lights (in my case HemisphereLight and SpotLight) do not work this way because of internal three.js rendering algorythm (buffers for geometries and materials were built without considering my lights).
To achieve dynamic lightning an author of the article suggest to:
Add light with intensity = 0 and then increase it (such light is taken into account by render)
Force material to be updated (material.needsUpdate = true) for affected scene objects after setting light visibility to true.

Hammer.js breaks vertical scroll when horizontal pan

I'm using Hammer.js to look for horizontal pan gestures, I've devised a simple function to clicks a button when panned left or right. It works okay, except the vertical scroll doesn't do anything on a touch device, or it's really glitchy and weird.
Here's the function:
var panelSliderPan = function() {
// Pan options
myOptions = {
// possible option
};
var myElement = document.querySelector('.scroll__inner'),
mc = new Hammer.Manager(myElement);
mc.add(new Hammer.Pan(myOptions));
// Pan control
var panIt = function(e) {
// I'm checking the direction here, my common sense says it shouldn't
// affect the vertical gestures, but it blocks them somehow
// 2 means it's left pan
if (e.direction === 2) {
$('.controls__btn--next').click();
// 4 == right
} else if (e.direction === 4) {
$('.controls__btn--prev').click();
}
};
// Call it
mc.on("panstart", function(e) {
panIt(e);
});
};
I've tried to add a horizontal direction to the recognizer but it didn't really help (not sure if I did it even right):
mc = new Hammer.Manager(myElement, {
recognizers: [
[Hammer.Pan,{ direction: Hammer.DIRECTION_HORIZONTAL }],
]
});
Thanks!
Try setting the touch-action property to auto.
mc = new Hammer.Manager(myElement, {
touchAction: 'auto',
recognizers: [
[Hammer.Pan,{ direction: Hammer.DIRECTION_HORIZONTAL }],
]
});
From the hammer.js docs:
When you set the touchAction to auto it doesnt prevent any defaults, and Hammer would probably break. You have to call preventDefault manually to fix this. You should only use this if you know what you're doing.
User patforna is correct. You need to adjust the touch-action property. This will fix scrolling not working when you have hammer bound on a big element in mobile.
You create a Hammer instance like so
var h = new Hammer(options.contentEl, {
touchAction : 'auto'
});
I was working on a pull to refresh feature, so I need the pan event.
Add the recognizers.
h.get( 'pan' ).set({
direction : Hammer.DIRECTION_VERTICAL,
});
h.on('panstart pandown panup panend', eventHandler);
Inside the eventhandler, you'd look at the event that was triggered and manually call on event.preventDefault() when you require it. This is applicable for hammer 2.0.6.
For anyone who's looking the pull to refresh code was taken from - https://github.com/apeatling/web-pull-to-refresh
My problem was that vertical scroll was toggling a sidebar that was supposed to show/hide on horizontal pan/swipe. After looking at the event details, I realized that Hammer probably triggers panleft and panright event based on X delta and doesn't consider Y delta, so my quick solution was to check the pan direction in my handler:
this.$data.$hammer.on('panleft', (e) => {
if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) {
return;
}
this.isVisible = true;
});
I was stuck on this for several days. Hope this will fix your problem.
mc = new Hammer(myElement, {
inputClass: Hammer.SUPPORT_POINTER_EVENTS ? Hammer.PointerEventInput : Hammer.TouchInput,
touchAction: 'auto',
});
When the relevant gesture is triggered, we applied a css class to the element, that would set the touch-action to none.
mc.on('panmove panstart', event => {
mc.addClass('is-dragging');
}
);
.is-dragging {
touch-action: none !important;
}
Hammer 2.x does not support vertical swipe/pan. Documentation says:
Notes:
When calling Hammer() to create a simple instance, the pan and swipe recognizers are configured to only detect horizontal gestures
You can however use older 1.1.x version, which supports vertical gestures
——
Clarification: this refers to a ‘simple instance’ which is when you don’t pass in any recognizer configuration as the second parameter. In other words these are the defaults but can (and usually should) be overridden.

Resources